blob: d3c99d614e6f42d3ad0930c6a4073dde97ab071d [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% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 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*/
44#include "magick/studio.h"
glennrp5c7cf4e2010-12-24 00:30:00 +000045#include "magick/artifact.h"
cristy5a2ca482009-10-14 18:24:56 +000046#include "magick/attribute.h"
cristy3ed852e2009-09-05 21:47:34 +000047#include "magick/blob.h"
48#include "magick/blob-private.h"
49#include "magick/cache.h"
50#include "magick/color.h"
51#include "magick/color-private.h"
cristy4ccd4c02010-04-25 00:43:15 +000052#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000053#include "magick/colorspace.h"
54#include "magick/constitute.h"
55#include "magick/enhance.h"
56#include "magick/exception.h"
57#include "magick/exception-private.h"
58#include "magick/geometry.h"
cristyf2e11662009-10-14 01:24:43 +000059#include "magick/histogram.h"
cristy3ed852e2009-09-05 21:47:34 +000060#include "magick/image.h"
61#include "magick/image-private.h"
62#include "magick/layer.h"
63#include "magick/list.h"
64#include "magick/log.h"
65#include "magick/magick.h"
66#include "magick/memory_.h"
67#include "magick/module.h"
68#include "magick/monitor.h"
69#include "magick/monitor-private.h"
70#include "magick/option.h"
71#include "magick/quantum-private.h"
72#include "magick/profile.h"
73#include "magick/property.h"
cristy3ed852e2009-09-05 21:47:34 +000074#include "magick/resource_.h"
75#include "magick/semaphore.h"
76#include "magick/quantum-private.h"
77#include "magick/static.h"
78#include "magick/statistic.h"
79#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000080#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000081#include "magick/transform.h"
82#include "magick/utility.h"
83#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000084
glennrp7ef138c2009-11-10 13:50:20 +000085/* Suppress libpng pedantic warnings that were added in
86 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000087 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000088 * fix any code that generates warnings.
89 */
glennrp991e92a2010-01-28 03:09:00 +000090/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000091/* #define PNG_USE_RESULT The result of this function must be checked */
92/* #define PNG_NORETURN This function does not return */
93/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000094/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000095
96/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +000097#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +000098
cristy3ed852e2009-09-05 21:47:34 +000099#include "png.h"
100#include "zlib.h"
101
102/* ImageMagick differences */
103#define first_scene scene
104
glennrpd5045b42010-03-24 12:40:35 +0000105#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000106/*
107 Optional declarations. Define or undefine them as you like.
108*/
109/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
110
111/*
112 Features under construction. Define these to work on them.
113*/
114#undef MNG_OBJECT_BUFFERS
115#undef MNG_BASI_SUPPORTED
116#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
117#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000118#if defined(MAGICKCORE_JPEG_DELEGATE)
119# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
120#endif
121#if !defined(RGBColorMatchExact)
122#define IsPNGColorEqual(color,target) \
123 (((color).red == (target).red) && \
124 ((color).green == (target).green) && \
125 ((color).blue == (target).blue))
126#endif
127
128/*
129 Establish thread safety.
130 setjmp/longjmp is claimed to be safe on these platforms:
131 setjmp/longjmp is alleged to be unsafe on these platforms:
132*/
133#ifndef SETJMP_IS_THREAD_SAFE
134#define PNG_SETJMP_NOT_THREAD_SAFE
135#endif
136
137#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
138static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000139 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000140#endif
141
142/*
143 This temporary until I set up malloc'ed object attributes array.
144 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
145 waste more memory.
146*/
147#define MNG_MAX_OBJECTS 256
148
149/*
150 If this not defined, spec is interpreted strictly. If it is
151 defined, an attempt will be made to recover from some errors,
152 including
153 o global PLTE too short
154*/
155#undef MNG_LOOSE
156
157/*
158 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
159 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
160 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
161 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
162 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
163 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
164 will be enabled by default in libpng-1.2.0.
165*/
cristy3ed852e2009-09-05 21:47:34 +0000166#ifdef PNG_MNG_FEATURES_SUPPORTED
167# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
168# define PNG_READ_EMPTY_PLTE_SUPPORTED
169# endif
170# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
171# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
172# endif
173#endif
174
175/*
cristybb503372010-05-27 20:51:26 +0000176 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000177 This macro is only defined in libpng-1.0.3 and later.
178 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
179*/
180#ifndef PNG_UINT_31_MAX
181#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
182#endif
183
184/*
185 Constant strings for known chunk types. If you need to add a chunk,
186 add a string holding the name here. To make the code more
187 portable, we use ASCII numbers like this, not characters.
188*/
189
190static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
191static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
192static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
193static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
194static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
195static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
196static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
197static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
198static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
199static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
200static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
201static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
202static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
203static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
204static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
205static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
206static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
207static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
208static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
209static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
210static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
211static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
212static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
213static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
214static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
215static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
216static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
217static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
218static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
219static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
220static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
221static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
222static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
223static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
224
225#if defined(JNG_SUPPORTED)
226static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
227static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
228static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
229static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
230static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
231static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
232#endif
233
234/*
235Other known chunks that are not yet supported by ImageMagick:
236static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
237static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
238static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
239static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
240static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
241static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
242static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
243static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
244*/
245
246typedef struct _MngBox
247{
cristy8182b072010-05-30 20:10:53 +0000248 long
cristy3ed852e2009-09-05 21:47:34 +0000249 left,
250 right,
251 top,
252 bottom;
253} MngBox;
254
255typedef struct _MngPair
256{
cristy8182b072010-05-30 20:10:53 +0000257 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000258 a,
259 b;
260} MngPair;
261
262#ifdef MNG_OBJECT_BUFFERS
263typedef struct _MngBuffer
264{
265
cristybb503372010-05-27 20:51:26 +0000266 size_t
cristy3ed852e2009-09-05 21:47:34 +0000267 height,
268 width;
269
270 Image
271 *image;
272
273 png_color
274 plte[256];
275
276 int
277 reference_count;
278
279 unsigned char
280 alpha_sample_depth,
281 compression_method,
282 color_type,
283 concrete,
284 filter_method,
285 frozen,
286 image_type,
287 interlace_method,
288 pixel_sample_depth,
289 plte_length,
290 sample_depth,
291 viewable;
292} MngBuffer;
293#endif
294
295typedef struct _MngInfo
296{
297
298#ifdef MNG_OBJECT_BUFFERS
299 MngBuffer
300 *ob[MNG_MAX_OBJECTS];
301#endif
302
303 Image *
304 image;
305
306 RectangleInfo
307 page;
308
309 int
310 adjoin,
311#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
312 bytes_in_read_buffer,
313 found_empty_plte,
314#endif
315 equal_backgrounds,
316 equal_chrms,
317 equal_gammas,
318#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
319 defined(PNG_MNG_FEATURES_SUPPORTED)
320 equal_palettes,
321#endif
322 equal_physs,
323 equal_srgbs,
324 framing_mode,
325 have_global_bkgd,
326 have_global_chrm,
327 have_global_gama,
328 have_global_phys,
329 have_global_sbit,
330 have_global_srgb,
331 have_saved_bkgd_index,
332 have_write_global_chrm,
333 have_write_global_gama,
334 have_write_global_plte,
335 have_write_global_srgb,
336 need_fram,
337 object_id,
338 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000339 saved_bkgd_index;
340
341 int
342 new_number_colors;
343
cristybb503372010-05-27 20:51:26 +0000344 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000345 image_found,
346 loop_count[256],
347 loop_iteration[256],
348 scenes_found,
349 x_off[MNG_MAX_OBJECTS],
350 y_off[MNG_MAX_OBJECTS];
351
352 MngBox
353 clip,
354 frame,
355 image_box,
356 object_clip[MNG_MAX_OBJECTS];
357
358 unsigned char
359 /* These flags could be combined into one byte */
360 exists[MNG_MAX_OBJECTS],
361 frozen[MNG_MAX_OBJECTS],
362 loop_active[256],
363 invisible[MNG_MAX_OBJECTS],
364 viewable[MNG_MAX_OBJECTS];
365
366 MagickOffsetType
367 loop_jump[256];
368
369 png_colorp
370 global_plte;
371
372 png_color_8
373 global_sbit;
374
375 png_byte
376#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
377 read_buffer[8],
378#endif
379 global_trns[256];
380
381 float
382 global_gamma;
383
384 ChromaticityInfo
385 global_chrm;
386
387 RenderingIntent
388 global_srgb_intent;
389
cristy35ef8242010-06-03 16:24:13 +0000390 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000391 delay,
392 global_plte_length,
393 global_trns_length,
394 global_x_pixels_per_unit,
395 global_y_pixels_per_unit,
396 mng_width,
397 mng_height,
398 ticks_per_second;
399
glennrpb9cfe272010-12-21 15:08:06 +0000400 MagickBooleanType
401 need_blob;
402
cristy3ed852e2009-09-05 21:47:34 +0000403 unsigned int
404 IsPalette,
405 global_phys_unit_type,
406 basi_warning,
407 clon_warning,
408 dhdr_warning,
409 jhdr_warning,
410 magn_warning,
411 past_warning,
412 phyg_warning,
413 phys_warning,
414 sbit_warning,
415 show_warning,
416 mng_type,
417 write_mng,
418 write_png_colortype,
419 write_png_depth,
420 write_png8,
421 write_png24,
422 write_png32;
423
424#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000425 size_t
cristy3ed852e2009-09-05 21:47:34 +0000426 basi_width,
427 basi_height;
428
429 unsigned int
430 basi_depth,
431 basi_color_type,
432 basi_compression_method,
433 basi_filter_type,
434 basi_interlace_method,
435 basi_red,
436 basi_green,
437 basi_blue,
438 basi_alpha,
439 basi_viewable;
440#endif
441
442 png_uint_16
443 magn_first,
444 magn_last,
445 magn_mb,
446 magn_ml,
447 magn_mr,
448 magn_mt,
449 magn_mx,
450 magn_my,
451 magn_methx,
452 magn_methy;
453
454 PixelPacket
455 mng_global_bkgd;
456
glennrp26f37912010-12-23 16:22:42 +0000457 /* Added at version 6.6.6-7 */
458 MagickBooleanType
459 ping_exclude_bKGD,
460 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000461 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000462 ping_exclude_EXIF,
463 ping_exclude_gAMA,
464 ping_exclude_iCCP,
465 /* ping_exclude_iTXt, */
466 ping_exclude_oFFs,
467 ping_exclude_pHYs,
468 ping_exclude_sRGB,
469 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000470 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000471 ping_exclude_vpAg,
472 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000473 ping_exclude_zTXt,
474 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000475
cristy3ed852e2009-09-05 21:47:34 +0000476} MngInfo;
477#endif /* VER */
478
479/*
480 Forward declarations.
481*/
482static MagickBooleanType
483 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000484
cristy3ed852e2009-09-05 21:47:34 +0000485static MagickBooleanType
486 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000487
cristy3ed852e2009-09-05 21:47:34 +0000488#if defined(JNG_SUPPORTED)
489static MagickBooleanType
490 WriteJNGImage(const ImageInfo *,Image *);
491#endif
492
glennrp0c3e06b2010-11-19 13:45:02 +0000493#if PNG_LIBPNG_VER > 10011
494
glennrpfd05d622011-02-25 04:10:33 +0000495
glennrp0c3e06b2010-11-19 13:45:02 +0000496#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
497static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000498LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000499{
glennrp67b9c1a2011-04-22 18:47:36 +0000500 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
501 *
502 * This is true if the high byte and the next highest byte of
503 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000504 * are equal to each other. We check this by seeing if the samples
505 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000506 *
507 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000508 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000509 */
510
glennrp3faa9a32011-04-23 14:00:25 +0000511#define QuantumToCharToQuantumEqQuantum(quantum) \
512 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
513
glennrp0c3e06b2010-11-19 13:45:02 +0000514 MagickBooleanType
515 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000516
glennrp03e11f62011-04-22 13:30:16 +0000517 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000518 {
519
520 const PixelPacket
521 *p;
522
523 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000524 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
525 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
526 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
527 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000528
529 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
530 {
531 int indx;
532
533 for (indx=0; indx < (ssize_t) image->colors; indx++)
534 {
glennrp3faa9a32011-04-23 14:00:25 +0000535 ok_to_reduce=(
536 QuantumToCharToQuantumEqQuantum(
537 image->colormap[indx].red) &&
538 QuantumToCharToQuantumEqQuantum(
539 image->colormap[indx].green) &&
540 QuantumToCharToQuantumEqQuantum(
541 image->colormap[indx].blue)) ?
542 MagickTrue : MagickFalse;
543
glennrp0c3e06b2010-11-19 13:45:02 +0000544 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000545 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000546 }
547 }
548
549 if ((ok_to_reduce != MagickFalse) &&
550 (image->storage_class != PseudoClass))
551 {
552 ssize_t
553 y;
554
555 register ssize_t
556 x;
557
558 for (y=0; y < (ssize_t) image->rows; y++)
559 {
560 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
561
562 if (p == (const PixelPacket *) NULL)
563 {
564 ok_to_reduce = MagickFalse;
565 break;
566 }
567
568 for (x=(ssize_t) image->columns-1; x >= 0; x--)
569 {
glennrp3faa9a32011-04-23 14:00:25 +0000570 ok_to_reduce=
571 QuantumToCharToQuantumEqQuantum(GetRedPixelComponent(p)) &&
572 QuantumToCharToQuantumEqQuantum(GetGreenPixelComponent(p)) &&
573 QuantumToCharToQuantumEqQuantum(GetBluePixelComponent(p)) ?
574 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000575
576 if (ok_to_reduce == MagickFalse)
577 break;
578
579 p++;
580 }
glennrp8640fb52010-11-23 15:48:26 +0000581 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +0000582 break;
583 }
584 }
585
586 if (ok_to_reduce != MagickFalse)
587 {
glennrp0c3e06b2010-11-19 13:45:02 +0000588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000589 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +0000590 }
glennrpa6a06632011-01-19 15:15:34 +0000591 else
592 {
593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000594 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +0000595 }
glennrp0c3e06b2010-11-19 13:45:02 +0000596 }
597
598 return ok_to_reduce;
599}
600#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
601
glennrpe610a072010-08-05 17:08:46 +0000602static int
glennrpcf002022011-01-30 02:38:15 +0000603Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +0000604{
glennrpe610a072010-08-05 17:08:46 +0000605 switch (intent)
606 {
607 case PerceptualIntent:
608 return 0;
glennrp0fe50b42010-11-16 03:52:51 +0000609
glennrpe610a072010-08-05 17:08:46 +0000610 case RelativeIntent:
611 return 1;
glennrp0fe50b42010-11-16 03:52:51 +0000612
glennrpe610a072010-08-05 17:08:46 +0000613 case SaturationIntent:
614 return 2;
glennrp0fe50b42010-11-16 03:52:51 +0000615
glennrpe610a072010-08-05 17:08:46 +0000616 case AbsoluteIntent:
617 return 3;
glennrp0fe50b42010-11-16 03:52:51 +0000618
glennrpe610a072010-08-05 17:08:46 +0000619 default:
620 return -1;
621 }
622}
623
624static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +0000625Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +0000626{
glennrpcf002022011-01-30 02:38:15 +0000627 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +0000628 {
629 case 0:
630 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000631
glennrpe610a072010-08-05 17:08:46 +0000632 case 1:
633 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000634
glennrpe610a072010-08-05 17:08:46 +0000635 case 2:
636 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000637
glennrpe610a072010-08-05 17:08:46 +0000638 case 3:
639 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000640
glennrpe610a072010-08-05 17:08:46 +0000641 default:
642 return UndefinedIntent;
643 }
644}
645
cristybb503372010-05-27 20:51:26 +0000646static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000647{
648 if (x > y)
649 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000650
cristy3ed852e2009-09-05 21:47:34 +0000651 return(y);
652}
glennrp0c3e06b2010-11-19 13:45:02 +0000653
cristybb503372010-05-27 20:51:26 +0000654static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000655{
656 if (x < y)
657 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000658
cristy3ed852e2009-09-05 21:47:34 +0000659 return(y);
660}
glennrp0c3e06b2010-11-19 13:45:02 +0000661
cristy3ed852e2009-09-05 21:47:34 +0000662
663/*
664%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
665% %
666% %
667% %
668% I m a g e I s G r a y %
669% %
670% %
671% %
672%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
673% %
674% Like IsGrayImage except does not change DirectClass to PseudoClass %
675% %
676%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
677*/
678static MagickBooleanType ImageIsGray(Image *image)
679{
680 register const PixelPacket
681 *p;
682
cristybb503372010-05-27 20:51:26 +0000683 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000684 i,
685 x,
686 y;
687
688 assert(image != (Image *) NULL);
689 assert(image->signature == MagickSignature);
690 if (image->debug != MagickFalse)
691 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
692
693 if (image->storage_class == PseudoClass)
694 {
cristybb503372010-05-27 20:51:26 +0000695 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000696 if (IsGray(image->colormap+i) == MagickFalse)
697 return(MagickFalse);
698 return(MagickTrue);
699 }
cristybb503372010-05-27 20:51:26 +0000700 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000701 {
702 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
703 if (p == (const PixelPacket *) NULL)
704 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000705 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +0000706 {
707 if (IsGray(p) == MagickFalse)
708 return(MagickFalse);
709 p++;
710 }
711 }
712 return(MagickTrue);
713}
glennrpd5045b42010-03-24 12:40:35 +0000714#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000715#endif /* MAGICKCORE_PNG_DELEGATE */
716
717/*
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719% %
720% %
721% %
722% I s M N G %
723% %
724% %
725% %
726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727%
728% IsMNG() returns MagickTrue if the image format type, identified by the
729% magick string, is MNG.
730%
731% The format of the IsMNG method is:
732%
733% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
734%
735% A description of each parameter follows:
736%
737% o magick: compare image format pattern against these bytes.
738%
739% o length: Specifies the length of the magick string.
740%
741%
742*/
743static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
744{
745 if (length < 8)
746 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000747
cristy3ed852e2009-09-05 21:47:34 +0000748 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
749 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000750
cristy3ed852e2009-09-05 21:47:34 +0000751 return(MagickFalse);
752}
753
754/*
755%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756% %
757% %
758% %
759% I s J N G %
760% %
761% %
762% %
763%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
764%
765% IsJNG() returns MagickTrue if the image format type, identified by the
766% magick string, is JNG.
767%
768% The format of the IsJNG method is:
769%
770% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
771%
772% A description of each parameter follows:
773%
774% o magick: compare image format pattern against these bytes.
775%
776% o length: Specifies the length of the magick string.
777%
778%
779*/
780static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
781{
782 if (length < 8)
783 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000784
cristy3ed852e2009-09-05 21:47:34 +0000785 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
786 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000787
cristy3ed852e2009-09-05 21:47:34 +0000788 return(MagickFalse);
789}
790
791/*
792%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
793% %
794% %
795% %
796% I s P N G %
797% %
798% %
799% %
800%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
801%
802% IsPNG() returns MagickTrue if the image format type, identified by the
803% magick string, is PNG.
804%
805% The format of the IsPNG method is:
806%
807% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
808%
809% A description of each parameter follows:
810%
811% o magick: compare image format pattern against these bytes.
812%
813% o length: Specifies the length of the magick string.
814%
815*/
816static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
817{
818 if (length < 8)
819 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000820
cristy3ed852e2009-09-05 21:47:34 +0000821 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
822 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000823
cristy3ed852e2009-09-05 21:47:34 +0000824 return(MagickFalse);
825}
826
827#if defined(MAGICKCORE_PNG_DELEGATE)
828#if defined(__cplusplus) || defined(c_plusplus)
829extern "C" {
830#endif
831
glennrpd5045b42010-03-24 12:40:35 +0000832#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +0000833static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +0000834{
835 unsigned char
836 buffer[4];
837
838 assert(image != (Image *) NULL);
839 assert(image->signature == MagickSignature);
840 buffer[0]=(unsigned char) (value >> 24);
841 buffer[1]=(unsigned char) (value >> 16);
842 buffer[2]=(unsigned char) (value >> 8);
843 buffer[3]=(unsigned char) value;
844 return((size_t) WriteBlob(image,4,buffer));
845}
846
847static void PNGLong(png_bytep p,png_uint_32 value)
848{
849 *p++=(png_byte) ((value >> 24) & 0xff);
850 *p++=(png_byte) ((value >> 16) & 0xff);
851 *p++=(png_byte) ((value >> 8) & 0xff);
852 *p++=(png_byte) (value & 0xff);
853}
854
glennrpa521b2f2010-10-29 04:11:03 +0000855#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +0000856static void PNGsLong(png_bytep p,png_int_32 value)
857{
858 *p++=(png_byte) ((value >> 24) & 0xff);
859 *p++=(png_byte) ((value >> 16) & 0xff);
860 *p++=(png_byte) ((value >> 8) & 0xff);
861 *p++=(png_byte) (value & 0xff);
862}
glennrpa521b2f2010-10-29 04:11:03 +0000863#endif
cristy3ed852e2009-09-05 21:47:34 +0000864
865static void PNGShort(png_bytep p,png_uint_16 value)
866{
867 *p++=(png_byte) ((value >> 8) & 0xff);
868 *p++=(png_byte) (value & 0xff);
869}
870
871static void PNGType(png_bytep p,png_bytep type)
872{
873 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
874}
875
glennrp03812ae2010-12-24 01:31:34 +0000876static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
877 size_t length)
cristy3ed852e2009-09-05 21:47:34 +0000878{
879 if (logging != MagickFalse)
880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000881 " Writing %c%c%c%c chunk, length: %.20g",
882 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000883}
glennrpd5045b42010-03-24 12:40:35 +0000884#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000885
886#if defined(__cplusplus) || defined(c_plusplus)
887}
888#endif
889
glennrpd5045b42010-03-24 12:40:35 +0000890#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000891/*
892%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
893% %
894% %
895% %
896% R e a d P N G I m a g e %
897% %
898% %
899% %
900%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
901%
902% ReadPNGImage() reads a Portable Network Graphics (PNG) or
903% Multiple-image Network Graphics (MNG) image file and returns it. It
904% allocates the memory necessary for the new Image structure and returns a
905% pointer to the new image or set of images.
906%
907% MNG support written by Glenn Randers-Pehrson, glennrp@image...
908%
909% The format of the ReadPNGImage method is:
910%
911% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
912%
913% A description of each parameter follows:
914%
915% o image_info: the image info.
916%
917% o exception: return any errors or warnings in this structure.
918%
919% To do, more or less in chronological order (as of version 5.5.2,
920% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
921%
922% Get 16-bit cheap transparency working.
923%
924% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
925%
926% Preserve all unknown and not-yet-handled known chunks found in input
927% PNG file and copy them into output PNG files according to the PNG
928% copying rules.
929%
930% (At this point, PNG encoding should be in full MNG compliance)
931%
932% Provide options for choice of background to use when the MNG BACK
933% chunk is not present or is not mandatory (i.e., leave transparent,
934% user specified, MNG BACK, PNG bKGD)
935%
936% Implement LOOP/ENDL [done, but could do discretionary loops more
937% efficiently by linking in the duplicate frames.].
938%
939% Decode and act on the MHDR simplicity profile (offer option to reject
940% files or attempt to process them anyway when the profile isn't LC or VLC).
941%
942% Upgrade to full MNG without Delta-PNG.
943%
944% o BACK [done a while ago except for background image ID]
945% o MOVE [done 15 May 1999]
946% o CLIP [done 15 May 1999]
947% o DISC [done 19 May 1999]
948% o SAVE [partially done 19 May 1999 (marks objects frozen)]
949% o SEEK [partially done 19 May 1999 (discard function only)]
950% o SHOW
951% o PAST
952% o BASI
953% o MNG-level tEXt/iTXt/zTXt
954% o pHYg
955% o pHYs
956% o sBIT
957% o bKGD
958% o iTXt (wait for libpng implementation).
959%
960% Use the scene signature to discover when an identical scene is
961% being reused, and just point to the original image->exception instead
962% of storing another set of pixels. This not specific to MNG
963% but could be applied generally.
964%
965% Upgrade to full MNG with Delta-PNG.
966%
967% JNG tEXt/iTXt/zTXt
968%
969% We will not attempt to read files containing the CgBI chunk.
970% They are really Xcode files meant for display on the iPhone.
971% These are not valid PNG files and it is impossible to recover
972% the orginal PNG from files that have been converted to Xcode-PNG,
973% since irretrievable loss of color data has occurred due to the
974% use of premultiplied alpha.
975*/
976
977#if defined(__cplusplus) || defined(c_plusplus)
978extern "C" {
979#endif
980
981/*
982 This the function that does the actual reading of data. It is
983 the same as the one supplied in libpng, except that it receives the
984 datastream from the ReadBlob() function instead of standard input.
985*/
986static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
987{
988 Image
989 *image;
990
991 image=(Image *) png_get_io_ptr(png_ptr);
992 if (length)
993 {
994 png_size_t
995 check;
996
997 check=(png_size_t) ReadBlob(image,(size_t) length,data);
998 if (check != length)
999 {
1000 char
1001 msg[MaxTextExtent];
1002
1003 (void) FormatMagickString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001004 "Expected %.20g bytes; found %.20g bytes",(double) length,
1005 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001006 png_warning(png_ptr,msg);
1007 png_error(png_ptr,"Read Exception");
1008 }
1009 }
1010}
1011
1012#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1013 !defined(PNG_MNG_FEATURES_SUPPORTED)
1014/* We use mng_get_data() instead of png_get_data() if we have a libpng
1015 * older than libpng-1.0.3a, which was the first to allow the empty
1016 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1017 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1018 * encountered after an empty PLTE, so we have to look ahead for bKGD
1019 * chunks and remove them from the datastream that is passed to libpng,
1020 * and store their contents for later use.
1021 */
1022static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1023{
1024 MngInfo
1025 *mng_info;
1026
1027 Image
1028 *image;
1029
1030 png_size_t
1031 check;
1032
cristybb503372010-05-27 20:51:26 +00001033 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001034 i;
1035
1036 i=0;
1037 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1038 image=(Image *) mng_info->image;
1039 while (mng_info->bytes_in_read_buffer && length)
1040 {
1041 data[i]=mng_info->read_buffer[i];
1042 mng_info->bytes_in_read_buffer--;
1043 length--;
1044 i++;
1045 }
1046 if (length)
1047 {
1048 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001049
cristy3ed852e2009-09-05 21:47:34 +00001050 if (check != length)
1051 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001052
cristy3ed852e2009-09-05 21:47:34 +00001053 if (length == 4)
1054 {
1055 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1056 (data[3] == 0))
1057 {
1058 check=(png_size_t) ReadBlob(image,(size_t) length,
1059 (char *) mng_info->read_buffer);
1060 mng_info->read_buffer[4]=0;
1061 mng_info->bytes_in_read_buffer=4;
1062 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1063 mng_info->found_empty_plte=MagickTrue;
1064 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1065 {
1066 mng_info->found_empty_plte=MagickFalse;
1067 mng_info->have_saved_bkgd_index=MagickFalse;
1068 }
1069 }
glennrp0fe50b42010-11-16 03:52:51 +00001070
cristy3ed852e2009-09-05 21:47:34 +00001071 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1072 (data[3] == 1))
1073 {
1074 check=(png_size_t) ReadBlob(image,(size_t) length,
1075 (char *) mng_info->read_buffer);
1076 mng_info->read_buffer[4]=0;
1077 mng_info->bytes_in_read_buffer=4;
1078 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1079 if (mng_info->found_empty_plte)
1080 {
1081 /*
1082 Skip the bKGD data byte and CRC.
1083 */
1084 check=(png_size_t)
1085 ReadBlob(image,5,(char *) mng_info->read_buffer);
1086 check=(png_size_t) ReadBlob(image,(size_t) length,
1087 (char *) mng_info->read_buffer);
1088 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1089 mng_info->have_saved_bkgd_index=MagickTrue;
1090 mng_info->bytes_in_read_buffer=0;
1091 }
1092 }
1093 }
1094 }
1095}
1096#endif
1097
1098static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1099{
1100 Image
1101 *image;
1102
1103 image=(Image *) png_get_io_ptr(png_ptr);
1104 if (length)
1105 {
1106 png_size_t
1107 check;
1108
cristybb503372010-05-27 20:51:26 +00001109 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 if (check != length)
1112 png_error(png_ptr,"WriteBlob Failed");
1113 }
1114}
1115
1116static void png_flush_data(png_structp png_ptr)
1117{
1118 (void) png_ptr;
1119}
1120
1121#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1122static int PalettesAreEqual(Image *a,Image *b)
1123{
cristybb503372010-05-27 20:51:26 +00001124 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001125 i;
1126
1127 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1128 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001129
cristy3ed852e2009-09-05 21:47:34 +00001130 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1131 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001132
cristy3ed852e2009-09-05 21:47:34 +00001133 if (a->colors != b->colors)
1134 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001135
cristybb503372010-05-27 20:51:26 +00001136 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001137 {
1138 if ((a->colormap[i].red != b->colormap[i].red) ||
1139 (a->colormap[i].green != b->colormap[i].green) ||
1140 (a->colormap[i].blue != b->colormap[i].blue))
1141 return((int) MagickFalse);
1142 }
glennrp0fe50b42010-11-16 03:52:51 +00001143
cristy3ed852e2009-09-05 21:47:34 +00001144 return((int) MagickTrue);
1145}
1146#endif
1147
1148static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1149{
1150 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1151 mng_info->exists[i] && !mng_info->frozen[i])
1152 {
1153#ifdef MNG_OBJECT_BUFFERS
1154 if (mng_info->ob[i] != (MngBuffer *) NULL)
1155 {
1156 if (mng_info->ob[i]->reference_count > 0)
1157 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001158
cristy3ed852e2009-09-05 21:47:34 +00001159 if (mng_info->ob[i]->reference_count == 0)
1160 {
1161 if (mng_info->ob[i]->image != (Image *) NULL)
1162 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001163
cristy3ed852e2009-09-05 21:47:34 +00001164 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1165 }
1166 }
1167 mng_info->ob[i]=(MngBuffer *) NULL;
1168#endif
1169 mng_info->exists[i]=MagickFalse;
1170 mng_info->invisible[i]=MagickFalse;
1171 mng_info->viewable[i]=MagickFalse;
1172 mng_info->frozen[i]=MagickFalse;
1173 mng_info->x_off[i]=0;
1174 mng_info->y_off[i]=0;
1175 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001176 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001177 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001178 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001179 }
1180}
1181
glennrp21f0e622011-01-07 16:20:57 +00001182static void MngInfoFreeStruct(MngInfo *mng_info,
1183 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001184{
glennrp21f0e622011-01-07 16:20:57 +00001185 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001186 {
cristybb503372010-05-27 20:51:26 +00001187 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001188 i;
1189
1190 for (i=1; i < MNG_MAX_OBJECTS; i++)
1191 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001192
cristy3ed852e2009-09-05 21:47:34 +00001193 if (mng_info->global_plte != (png_colorp) NULL)
1194 mng_info->global_plte=(png_colorp)
1195 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001196
cristy3ed852e2009-09-05 21:47:34 +00001197 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1198 *have_mng_structure=MagickFalse;
1199 }
1200}
1201
1202static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1203{
1204 MngBox
1205 box;
1206
1207 box=box1;
1208 if (box.left < box2.left)
1209 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001210
cristy3ed852e2009-09-05 21:47:34 +00001211 if (box.top < box2.top)
1212 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001213
cristy3ed852e2009-09-05 21:47:34 +00001214 if (box.right > box2.right)
1215 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001216
cristy3ed852e2009-09-05 21:47:34 +00001217 if (box.bottom > box2.bottom)
1218 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001219
cristy3ed852e2009-09-05 21:47:34 +00001220 return box;
1221}
1222
1223static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1224{
1225 MngBox
1226 box;
1227
1228 /*
1229 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1230 */
cristybb503372010-05-27 20:51:26 +00001231 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1232 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1233 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1234 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001235 if (delta_type != 0)
1236 {
1237 box.left+=previous_box.left;
1238 box.right+=previous_box.right;
1239 box.top+=previous_box.top;
1240 box.bottom+=previous_box.bottom;
1241 }
glennrp0fe50b42010-11-16 03:52:51 +00001242
cristy3ed852e2009-09-05 21:47:34 +00001243 return(box);
1244}
1245
1246static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1247 unsigned char *p)
1248{
1249 MngPair
1250 pair;
1251 /*
cristybb503372010-05-27 20:51:26 +00001252 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001253 */
cristy8182b072010-05-30 20:10:53 +00001254 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1255 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001256
cristy3ed852e2009-09-05 21:47:34 +00001257 if (delta_type != 0)
1258 {
1259 pair.a+=previous_pair.a;
1260 pair.b+=previous_pair.b;
1261 }
glennrp0fe50b42010-11-16 03:52:51 +00001262
cristy3ed852e2009-09-05 21:47:34 +00001263 return(pair);
1264}
1265
cristy8182b072010-05-30 20:10:53 +00001266static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001267{
cristy8182b072010-05-30 20:10:53 +00001268 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001269}
1270
glennrpcf002022011-01-30 02:38:15 +00001271static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001272{
1273 Image
1274 *image;
1275
1276 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001277
cristy3ed852e2009-09-05 21:47:34 +00001278 if (image->debug != MagickFalse)
1279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1280 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001281
cristy3ed852e2009-09-05 21:47:34 +00001282 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1283 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001284
glennrpe4017e32011-01-08 17:16:09 +00001285#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001286 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1287 * are building with libpng-1.4.x and can be ignored.
1288 */
cristy3ed852e2009-09-05 21:47:34 +00001289 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001290#else
1291 png_longjmp(ping,1);
1292#endif
cristy3ed852e2009-09-05 21:47:34 +00001293}
1294
glennrpcf002022011-01-30 02:38:15 +00001295static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001296{
1297 Image
1298 *image;
1299
1300 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1301 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001302
cristy3ed852e2009-09-05 21:47:34 +00001303 image=(Image *) png_get_error_ptr(ping);
1304 if (image->debug != MagickFalse)
1305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001306 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001307
cristy3ed852e2009-09-05 21:47:34 +00001308 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1309 message,"`%s'",image->filename);
1310}
1311
1312#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001313static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001314{
1315#if (PNG_LIBPNG_VER < 10011)
1316 png_voidp
1317 ret;
1318
1319 png_ptr=png_ptr;
1320 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001321
cristy3ed852e2009-09-05 21:47:34 +00001322 if (ret == NULL)
1323 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001324
cristy3ed852e2009-09-05 21:47:34 +00001325 return(ret);
1326#else
1327 png_ptr=png_ptr;
1328 return((png_voidp) AcquireMagickMemory((size_t) size));
1329#endif
1330}
1331
1332/*
1333 Free a pointer. It is removed from the list at the same time.
1334*/
glennrpcf002022011-01-30 02:38:15 +00001335static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001336{
1337 png_ptr=png_ptr;
1338 ptr=RelinquishMagickMemory(ptr);
1339 return((png_free_ptr) NULL);
1340}
1341#endif
1342
1343#if defined(__cplusplus) || defined(c_plusplus)
1344}
1345#endif
1346
1347static int
glennrpcf002022011-01-30 02:38:15 +00001348Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001349 png_textp text,int ii)
1350{
cristybb503372010-05-27 20:51:26 +00001351 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001352 i;
1353
1354 register unsigned char
1355 *dp;
1356
1357 register png_charp
1358 sp;
1359
1360 png_uint_32
1361 length,
1362 nibbles;
1363
1364 StringInfo
1365 *profile;
1366
glennrp0c3e06b2010-11-19 13:45:02 +00001367 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001368 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1369 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1370 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1371 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1372 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1373 13,14,15};
1374
1375 sp=text[ii].text+1;
1376 /* look for newline */
1377 while (*sp != '\n')
1378 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001379
cristy3ed852e2009-09-05 21:47:34 +00001380 /* look for length */
1381 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1382 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001383
cristyf2f27272009-12-17 14:48:46 +00001384 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001385
glennrp97f90e22011-02-22 05:47:58 +00001386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1387 " length: %lu",(unsigned long) length);
1388
cristy3ed852e2009-09-05 21:47:34 +00001389 while (*sp != ' ' && *sp != '\n')
1390 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001391
cristy3ed852e2009-09-05 21:47:34 +00001392 /* allocate space */
1393 if (length == 0)
1394 {
1395 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1396 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1397 return(MagickFalse);
1398 }
glennrp0fe50b42010-11-16 03:52:51 +00001399
cristy3ed852e2009-09-05 21:47:34 +00001400 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001401
cristy3ed852e2009-09-05 21:47:34 +00001402 if (profile == (StringInfo *) NULL)
1403 {
1404 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1405 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1406 "unable to copy profile");
1407 return(MagickFalse);
1408 }
glennrp0fe50b42010-11-16 03:52:51 +00001409
cristy3ed852e2009-09-05 21:47:34 +00001410 /* copy profile, skipping white space and column 1 "=" signs */
1411 dp=GetStringInfoDatum(profile);
1412 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001413
cristybb503372010-05-27 20:51:26 +00001414 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001415 {
1416 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1417 {
1418 if (*sp == '\0')
1419 {
1420 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1421 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1422 profile=DestroyStringInfo(profile);
1423 return(MagickFalse);
1424 }
1425 sp++;
1426 }
glennrp0fe50b42010-11-16 03:52:51 +00001427
cristy3ed852e2009-09-05 21:47:34 +00001428 if (i%2 == 0)
1429 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001430
cristy3ed852e2009-09-05 21:47:34 +00001431 else
1432 (*dp++)+=unhex[(int) *sp++];
1433 }
1434 /*
1435 We have already read "Raw profile type.
1436 */
1437 (void) SetImageProfile(image,&text[ii].key[17],profile);
1438 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001439
cristy3ed852e2009-09-05 21:47:34 +00001440 if (image_info->verbose)
1441 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001442
cristy3ed852e2009-09-05 21:47:34 +00001443 return MagickTrue;
1444}
1445
1446#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1447static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1448{
1449 Image
1450 *image;
1451
1452
1453 /* The unknown chunk structure contains the chunk data:
1454 png_byte name[5];
1455 png_byte *data;
1456 png_size_t size;
1457
1458 Note that libpng has already taken care of the CRC handling.
1459 */
1460
1461
1462 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1463 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1464 return(0); /* Did not recognize */
1465
1466 /* recognized vpAg */
1467
1468 if (chunk->size != 9)
1469 return(-1); /* Error return */
1470
1471 if (chunk->data[8] != 0)
1472 return(0); /* ImageMagick requires pixel units */
1473
1474 image=(Image *) png_get_user_chunk_ptr(ping);
1475
cristybb503372010-05-27 20:51:26 +00001476 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001477 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001478
cristybb503372010-05-27 20:51:26 +00001479 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001480 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1481
1482 /* Return one of the following: */
1483 /* return(-n); chunk had an error */
1484 /* return(0); did not recognize */
1485 /* return(n); success */
1486
1487 return(1);
1488
1489}
1490#endif
1491
1492/*
1493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1494% %
1495% %
1496% %
1497% R e a d O n e P N G I m a g e %
1498% %
1499% %
1500% %
1501%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1502%
1503% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1504% (minus the 8-byte signature) and returns it. It allocates the memory
1505% necessary for the new Image structure and returns a pointer to the new
1506% image.
1507%
1508% The format of the ReadOnePNGImage method is:
1509%
1510% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1511% ExceptionInfo *exception)
1512%
1513% A description of each parameter follows:
1514%
1515% o mng_info: Specifies a pointer to a MngInfo structure.
1516%
1517% o image_info: the image info.
1518%
1519% o exception: return any errors or warnings in this structure.
1520%
1521*/
1522static Image *ReadOnePNGImage(MngInfo *mng_info,
1523 const ImageInfo *image_info, ExceptionInfo *exception)
1524{
1525 /* Read one PNG image */
1526
glennrpcc95c3f2011-04-18 16:46:48 +00001527 /* To do: Read the tIME chunk into the date:modify property */
1528 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1529
cristy3ed852e2009-09-05 21:47:34 +00001530 Image
1531 *image;
1532
1533 int
glennrp4eb39312011-03-30 21:34:55 +00001534 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001535 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001536 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001537 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001538 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001539 pass,
1540 ping_bit_depth,
1541 ping_color_type,
1542 ping_interlace_method,
1543 ping_compression_method,
1544 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001545 ping_num_trans,
1546 unit_type;
1547
1548 double
1549 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001550
glennrpa6a06632011-01-19 15:15:34 +00001551 LongPixelPacket
1552 transparent_color;
1553
cristy3ed852e2009-09-05 21:47:34 +00001554 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00001555 logging,
cristy3ed852e2009-09-05 21:47:34 +00001556 status;
1557
glennrpfaa852b2010-03-30 12:17:00 +00001558 png_bytep
1559 ping_trans_alpha;
1560
1561 png_color_16p
1562 ping_background,
1563 ping_trans_color;
1564
cristy3ed852e2009-09-05 21:47:34 +00001565 png_info
1566 *end_info,
1567 *ping_info;
1568
1569 png_struct
1570 *ping;
1571
1572 png_textp
1573 text;
1574
glennrpfaa852b2010-03-30 12:17:00 +00001575 png_uint_32
1576 ping_height,
1577 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00001578 ping_rowbytes,
1579 x_resolution,
1580 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 QuantumInfo
1583 *quantum_info;
1584
1585 unsigned char
glennrpcf002022011-01-30 02:38:15 +00001586 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001587
cristybb503372010-05-27 20:51:26 +00001588 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001589 y;
1590
1591 register unsigned char
1592 *p;
1593
1594 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00001595 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00001596
cristybb503372010-05-27 20:51:26 +00001597 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001598 i,
1599 x;
1600
1601 register PixelPacket
1602 *q;
1603
1604 size_t
glennrp39992b42010-11-14 00:03:43 +00001605 length,
cristy3ed852e2009-09-05 21:47:34 +00001606 row_offset;
1607
cristyeb3b22a2011-03-31 20:16:11 +00001608 ssize_t
1609 j;
1610
cristy3ed852e2009-09-05 21:47:34 +00001611#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1612 png_byte unused_chunks[]=
1613 {
1614 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1615 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1616 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1617 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1618 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1619 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1620 };
1621#endif
1622
1623 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001624 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00001625
1626#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001627 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001628#endif
1629
glennrp25c1e2b2010-03-25 01:39:56 +00001630#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00001631 if (image_info->verbose)
1632 printf("Your PNG library (libpng-%s) is rather old.\n",
1633 PNG_LIBPNG_VER_STRING);
1634#endif
1635
glennrp61b4c952009-11-10 20:40:41 +00001636#if (PNG_LIBPNG_VER >= 10400)
1637# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1638 if (image_info->verbose)
1639 {
1640 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1641 PNG_LIBPNG_VER_STRING);
1642 printf("Please update it.\n");
1643 }
1644# endif
1645#endif
1646
1647
cristyed552522009-10-16 14:04:35 +00001648 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00001649 image=mng_info->image;
1650
glennrpa6a06632011-01-19 15:15:34 +00001651 if (logging != MagickFalse)
1652 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1653 " image->matte=%d",(int) image->matte);
1654
glennrp0e319732011-01-25 21:53:13 +00001655 /* Set to an out-of-range color unless tRNS chunk is present */
1656 transparent_color.red=65537;
1657 transparent_color.green=65537;
1658 transparent_color.blue=65537;
1659 transparent_color.opacity=65537;
1660
glennrpcb395ac2011-03-30 19:50:23 +00001661 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00001662 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00001663 num_raw_profiles = 0;
1664
cristy3ed852e2009-09-05 21:47:34 +00001665 /*
1666 Allocate the PNG structures
1667 */
1668#ifdef PNG_USER_MEM_SUPPORTED
1669 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00001670 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1671 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00001672#else
1673 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00001674 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00001675#endif
1676 if (ping == (png_struct *) NULL)
1677 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00001678
cristy3ed852e2009-09-05 21:47:34 +00001679 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001680
cristy3ed852e2009-09-05 21:47:34 +00001681 if (ping_info == (png_info *) NULL)
1682 {
1683 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1684 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1685 }
glennrp0fe50b42010-11-16 03:52:51 +00001686
cristy3ed852e2009-09-05 21:47:34 +00001687 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001688
cristy3ed852e2009-09-05 21:47:34 +00001689 if (end_info == (png_info *) NULL)
1690 {
1691 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1692 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
glennrpcf002022011-01-30 02:38:15 +00001695 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00001696
glennrpfaa852b2010-03-30 12:17:00 +00001697 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00001698 {
1699 /*
1700 PNG image is corrupt.
1701 */
1702 png_destroy_read_struct(&ping,&ping_info,&end_info);
1703#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001704 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001705#endif
1706 if (logging != MagickFalse)
1707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1708 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00001709
cristy3ed852e2009-09-05 21:47:34 +00001710 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00001711 {
1712 InheritException(exception,&image->exception);
1713 image->columns=0;
1714 }
glennrp0fe50b42010-11-16 03:52:51 +00001715
cristy3ed852e2009-09-05 21:47:34 +00001716 return(GetFirstImageInList(image));
1717 }
1718 /*
1719 Prepare PNG for reading.
1720 */
glennrpfaa852b2010-03-30 12:17:00 +00001721
cristy3ed852e2009-09-05 21:47:34 +00001722 mng_info->image_found++;
1723 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00001724
cristy3ed852e2009-09-05 21:47:34 +00001725 if (LocaleCompare(image_info->magick,"MNG") == 0)
1726 {
1727#if defined(PNG_MNG_FEATURES_SUPPORTED)
1728 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1729 png_set_read_fn(ping,image,png_get_data);
1730#else
1731#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1732 png_permit_empty_plte(ping,MagickTrue);
1733 png_set_read_fn(ping,image,png_get_data);
1734#else
1735 mng_info->image=image;
1736 mng_info->bytes_in_read_buffer=0;
1737 mng_info->found_empty_plte=MagickFalse;
1738 mng_info->have_saved_bkgd_index=MagickFalse;
1739 png_set_read_fn(ping,mng_info,mng_get_data);
1740#endif
1741#endif
1742 }
glennrp0fe50b42010-11-16 03:52:51 +00001743
cristy3ed852e2009-09-05 21:47:34 +00001744 else
1745 png_set_read_fn(ping,image,png_get_data);
1746
1747#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1748 /* Ignore unused chunks and all unknown chunks except for vpAg */
1749 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1750 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1751 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1752 (int)sizeof(unused_chunks)/5);
1753 /* Callback for other unknown chunks */
1754 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1755#endif
1756
glennrp991e92a2010-01-28 03:09:00 +00001757#if (PNG_LIBPNG_VER < 10400)
1758# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1759 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00001760 /* Disable thread-unsafe features of pnggccrd */
1761 if (png_access_version_number() >= 10200)
1762 {
1763 png_uint_32 mmx_disable_mask=0;
1764 png_uint_32 asm_flags;
1765
1766 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1767 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1768 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1769 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1770 asm_flags=png_get_asm_flags(ping);
1771 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1772 }
glennrp991e92a2010-01-28 03:09:00 +00001773# endif
cristy3ed852e2009-09-05 21:47:34 +00001774#endif
1775
1776 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00001777
1778 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1779 &ping_bit_depth,&ping_color_type,
1780 &ping_interlace_method,&ping_compression_method,
1781 &ping_filter_method);
1782
1783 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1784 &ping_trans_color);
1785
1786 (void) png_get_bKGD(ping, ping_info, &ping_background);
1787
1788 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00001789 {
glennrpfaa852b2010-03-30 12:17:00 +00001790 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1791 {
1792 png_set_packing(ping);
1793 ping_bit_depth = 8;
1794 }
cristy3ed852e2009-09-05 21:47:34 +00001795 }
glennrpfaa852b2010-03-30 12:17:00 +00001796
1797 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00001798 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00001799 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00001800 if (logging != MagickFalse)
1801 {
1802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001803 " PNG width: %.20g, height: %.20g",
1804 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00001805
cristy3ed852e2009-09-05 21:47:34 +00001806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1807 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001808 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00001809
cristy3ed852e2009-09-05 21:47:34 +00001810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1811 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001812 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00001813
cristy3ed852e2009-09-05 21:47:34 +00001814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1815 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001816 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00001817 }
1818
glennrpfaa852b2010-03-30 12:17:00 +00001819#ifdef PNG_READ_iCCP_SUPPORTED
1820 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00001821 {
1822 int
1823 compression;
1824
glennrpe4017e32011-01-08 17:16:09 +00001825#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00001826 png_charp
glennrpe4017e32011-01-08 17:16:09 +00001827 info;
1828#else
1829 png_bytep
1830 info;
1831#endif
1832
1833 png_charp
cristy3ed852e2009-09-05 21:47:34 +00001834 name;
1835
1836 png_uint_32
1837 profile_length;
1838
1839 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1840 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00001841
cristy3ed852e2009-09-05 21:47:34 +00001842 if (profile_length != 0)
1843 {
1844 StringInfo
1845 *profile;
1846
1847 if (logging != MagickFalse)
1848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1849 " Reading PNG iCCP chunk.");
1850 profile=AcquireStringInfo(profile_length);
1851 SetStringInfoDatum(profile,(const unsigned char *) info);
1852 (void) SetImageProfile(image,"icc",profile);
1853 profile=DestroyStringInfo(profile);
1854 }
1855 }
1856#endif
1857#if defined(PNG_READ_sRGB_SUPPORTED)
1858 {
cristy3ed852e2009-09-05 21:47:34 +00001859 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00001860 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1861 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00001862
cristy3ed852e2009-09-05 21:47:34 +00001863 if (png_get_sRGB(ping,ping_info,&intent))
1864 {
glennrpcf002022011-01-30 02:38:15 +00001865 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1866 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00001867
cristy3ed852e2009-09-05 21:47:34 +00001868 if (logging != MagickFalse)
1869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00001870 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00001871 }
1872 }
1873#endif
1874 {
glennrpfaa852b2010-03-30 12:17:00 +00001875 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1876 if (mng_info->have_global_gama)
1877 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00001878
cristy3ed852e2009-09-05 21:47:34 +00001879 if (png_get_gAMA(ping,ping_info,&file_gamma))
1880 {
1881 image->gamma=(float) file_gamma;
1882 if (logging != MagickFalse)
1883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1884 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1885 }
1886 }
glennrpfaa852b2010-03-30 12:17:00 +00001887 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1888 {
1889 if (mng_info->have_global_chrm != MagickFalse)
1890 {
1891 (void) png_set_cHRM(ping,ping_info,
1892 mng_info->global_chrm.white_point.x,
1893 mng_info->global_chrm.white_point.y,
1894 mng_info->global_chrm.red_primary.x,
1895 mng_info->global_chrm.red_primary.y,
1896 mng_info->global_chrm.green_primary.x,
1897 mng_info->global_chrm.green_primary.y,
1898 mng_info->global_chrm.blue_primary.x,
1899 mng_info->global_chrm.blue_primary.y);
1900 }
1901 }
glennrp0fe50b42010-11-16 03:52:51 +00001902
glennrpfaa852b2010-03-30 12:17:00 +00001903 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00001904 {
1905 (void) png_get_cHRM(ping,ping_info,
1906 &image->chromaticity.white_point.x,
1907 &image->chromaticity.white_point.y,
1908 &image->chromaticity.red_primary.x,
1909 &image->chromaticity.red_primary.y,
1910 &image->chromaticity.green_primary.x,
1911 &image->chromaticity.green_primary.y,
1912 &image->chromaticity.blue_primary.x,
1913 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy3ed852e2009-09-05 21:47:34 +00001915 if (logging != MagickFalse)
1916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1917 " Reading PNG cHRM chunk.");
1918 }
glennrp0fe50b42010-11-16 03:52:51 +00001919
glennrpe610a072010-08-05 17:08:46 +00001920 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00001921 {
glennrpe610a072010-08-05 17:08:46 +00001922 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00001923 Magick_RenderingIntent_to_PNG_RenderingIntent
1924 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00001925 png_set_gAMA(ping,ping_info,0.45455f);
1926 png_set_cHRM(ping,ping_info,
1927 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1928 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00001929 }
cristy3ed852e2009-09-05 21:47:34 +00001930#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001931 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00001932 {
cristy905ef802011-02-23 00:29:18 +00001933 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1934 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00001935
cristy3ed852e2009-09-05 21:47:34 +00001936 if (logging != MagickFalse)
1937 if (image->page.x || image->page.y)
1938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001939 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1940 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001941 }
1942#endif
1943#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001944 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1945 {
1946 if (mng_info->have_global_phys)
1947 {
1948 png_set_pHYs(ping,ping_info,
1949 mng_info->global_x_pixels_per_unit,
1950 mng_info->global_y_pixels_per_unit,
1951 mng_info->global_phys_unit_type);
1952 }
1953 }
1954
1955 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00001956 {
cristy3ed852e2009-09-05 21:47:34 +00001957 /*
1958 Set image resolution.
1959 */
1960 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00001961 &unit_type);
1962 image->x_resolution=(double) x_resolution;
1963 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00001964
cristy3ed852e2009-09-05 21:47:34 +00001965 if (unit_type == PNG_RESOLUTION_METER)
1966 {
1967 image->units=PixelsPerCentimeterResolution;
1968 image->x_resolution=(double) x_resolution/100.0;
1969 image->y_resolution=(double) y_resolution/100.0;
1970 }
glennrp0fe50b42010-11-16 03:52:51 +00001971
cristy3ed852e2009-09-05 21:47:34 +00001972 if (logging != MagickFalse)
1973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001974 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1975 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00001976 }
cristy3ed852e2009-09-05 21:47:34 +00001977#endif
glennrp823b55c2011-03-14 18:46:46 +00001978
glennrpfaa852b2010-03-30 12:17:00 +00001979 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00001980 {
1981 int
1982 number_colors;
1983
1984 png_colorp
1985 palette;
1986
1987 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00001988
cristy3ed852e2009-09-05 21:47:34 +00001989 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00001990 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00001991 {
1992 if (mng_info->global_plte_length)
1993 {
1994 png_set_PLTE(ping,ping_info,mng_info->global_plte,
1995 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00001996
glennrpfaa852b2010-03-30 12:17:00 +00001997 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00001998 if (mng_info->global_trns_length)
1999 {
2000 if (mng_info->global_trns_length >
2001 mng_info->global_plte_length)
2002 (void) ThrowMagickException(&image->exception,
2003 GetMagickModule(),CoderError,
2004 "global tRNS has more entries than global PLTE",
2005 "`%s'",image_info->filename);
2006 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2007 (int) mng_info->global_trns_length,NULL);
2008 }
glennrpbfd9e612011-04-22 14:02:20 +00002009#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002010 if (
2011#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2012 mng_info->have_saved_bkgd_index ||
2013#endif
glennrpfaa852b2010-03-30 12:17:00 +00002014 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002015 {
2016 png_color_16
2017 background;
2018
2019#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2020 if (mng_info->have_saved_bkgd_index)
2021 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002022#endif
glennrpfaa852b2010-03-30 12:17:00 +00002023 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2024 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002025
cristy3ed852e2009-09-05 21:47:34 +00002026 background.red=(png_uint_16)
2027 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002028
cristy3ed852e2009-09-05 21:47:34 +00002029 background.green=(png_uint_16)
2030 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002031
cristy3ed852e2009-09-05 21:47:34 +00002032 background.blue=(png_uint_16)
2033 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002034
cristy3ed852e2009-09-05 21:47:34 +00002035 png_set_bKGD(ping,ping_info,&background);
2036 }
2037#endif
2038 }
2039 else
2040 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2041 CoderError,"No global PLTE in file","`%s'",
2042 image_info->filename);
2043 }
2044 }
2045
glennrpbfd9e612011-04-22 14:02:20 +00002046#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002047 if (mng_info->have_global_bkgd &&
2048 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002049 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002050
glennrpfaa852b2010-03-30 12:17:00 +00002051 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002052 {
glennrpbfd9e612011-04-22 14:02:20 +00002053 unsigned int
2054 bkgd_scale;
2055
cristy3ed852e2009-09-05 21:47:34 +00002056 /*
2057 Set image background color.
2058 */
2059 if (logging != MagickFalse)
2060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2061 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002062
glennrpbfd9e612011-04-22 14:02:20 +00002063 /* Scale background components to 16-bit, then scale
2064 * to quantum depth
2065 */
2066 if (logging != MagickFalse)
2067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2068 " raw ping_background=(%d,%d,%d).",ping_background->red,
2069 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002070
glennrpbfd9e612011-04-22 14:02:20 +00002071 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002072
glennrpbfd9e612011-04-22 14:02:20 +00002073 if (ping_bit_depth == 1)
2074 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002075
glennrpbfd9e612011-04-22 14:02:20 +00002076 else if (ping_bit_depth == 2)
2077 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002078
glennrpbfd9e612011-04-22 14:02:20 +00002079 else if (ping_bit_depth == 4)
2080 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002081
glennrpbfd9e612011-04-22 14:02:20 +00002082 if (ping_bit_depth <= 8)
2083 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002084
glennrpbfd9e612011-04-22 14:02:20 +00002085 ping_background->red *= bkgd_scale;
2086 ping_background->green *= bkgd_scale;
2087 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002088
glennrpbfd9e612011-04-22 14:02:20 +00002089 if (logging != MagickFalse)
2090 {
glennrp2cbb4482010-06-02 04:37:24 +00002091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2092 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002093
glennrp2cbb4482010-06-02 04:37:24 +00002094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2095 " ping_background=(%d,%d,%d).",ping_background->red,
2096 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002097 }
glennrp2cbb4482010-06-02 04:37:24 +00002098
glennrpbfd9e612011-04-22 14:02:20 +00002099 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002100 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002101
glennrpbfd9e612011-04-22 14:02:20 +00002102 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002103 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002104
glennrpbfd9e612011-04-22 14:02:20 +00002105 image->background_color.blue=
2106 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002107
glennrpbfd9e612011-04-22 14:02:20 +00002108 image->background_color.opacity=OpaqueOpacity;
glennrp2cbb4482010-06-02 04:37:24 +00002109
glennrpbfd9e612011-04-22 14:02:20 +00002110 if (logging != MagickFalse)
2111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2112 " image->background_color=(%.20g,%.20g,%.20g).",
2113 (double) image->background_color.red,
2114 (double) image->background_color.green,
2115 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002116 }
glennrpbfd9e612011-04-22 14:02:20 +00002117#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002118
glennrpfaa852b2010-03-30 12:17:00 +00002119 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002120 {
2121 /*
glennrpa6a06632011-01-19 15:15:34 +00002122 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002123 */
2124 int
2125 max_sample;
2126
cristy35ef8242010-06-03 16:24:13 +00002127 size_t
2128 one=1;
2129
cristy3ed852e2009-09-05 21:47:34 +00002130 if (logging != MagickFalse)
2131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2132 " Reading PNG tRNS chunk.");
2133
cristyf9cca6a2010-06-04 23:49:28 +00002134 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002135
glennrpfaa852b2010-03-30 12:17:00 +00002136 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2137 (int)ping_trans_color->gray > max_sample) ||
2138 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2139 ((int)ping_trans_color->red > max_sample ||
2140 (int)ping_trans_color->green > max_sample ||
2141 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002142 {
2143 if (logging != MagickFalse)
2144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2145 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002146 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002147 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002148 image->matte=MagickFalse;
2149 }
2150 else
2151 {
glennrpa6a06632011-01-19 15:15:34 +00002152 int
2153 scale_to_short;
2154
2155 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2156
2157 /* Scale transparent_color to short */
2158 transparent_color.red= scale_to_short*ping_trans_color->red;
2159 transparent_color.green= scale_to_short*ping_trans_color->green;
2160 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2161 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002162
glennrpfaa852b2010-03-30 12:17:00 +00002163 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002164 {
glennrp0f111982010-07-07 20:18:33 +00002165 if (logging != MagickFalse)
2166 {
2167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2168 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002169
glennrp0f111982010-07-07 20:18:33 +00002170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2171 " scaled graylevel is %d.",transparent_color.opacity);
2172 }
cristy3ed852e2009-09-05 21:47:34 +00002173 transparent_color.red=transparent_color.opacity;
2174 transparent_color.green=transparent_color.opacity;
2175 transparent_color.blue=transparent_color.opacity;
2176 }
2177 }
2178 }
2179#if defined(PNG_READ_sBIT_SUPPORTED)
2180 if (mng_info->have_global_sbit)
2181 {
glennrpfaa852b2010-03-30 12:17:00 +00002182 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002183 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2184 }
2185#endif
2186 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002187
cristy3ed852e2009-09-05 21:47:34 +00002188 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002189
2190 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2191
cristy3ed852e2009-09-05 21:47:34 +00002192 /*
2193 Initialize image structure.
2194 */
2195 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002196 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002197 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002198 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002199 if (mng_info->mng_type == 0)
2200 {
glennrpfaa852b2010-03-30 12:17:00 +00002201 mng_info->mng_width=ping_width;
2202 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002203 mng_info->frame=mng_info->image_box;
2204 mng_info->clip=mng_info->image_box;
2205 }
glennrp0fe50b42010-11-16 03:52:51 +00002206
cristy3ed852e2009-09-05 21:47:34 +00002207 else
2208 {
2209 image->page.y=mng_info->y_off[mng_info->object_id];
2210 }
glennrp0fe50b42010-11-16 03:52:51 +00002211
cristy3ed852e2009-09-05 21:47:34 +00002212 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002213 image->columns=ping_width;
2214 image->rows=ping_height;
2215 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002216 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002217 {
cristybefe4d22010-06-07 01:18:58 +00002218 size_t
2219 one;
2220
cristy3ed852e2009-09-05 21:47:34 +00002221 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002222 one=1;
2223 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002224#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2225 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002226 image->colors=256;
2227#else
2228 if (image->colors > 65536L)
2229 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002230#endif
glennrpfaa852b2010-03-30 12:17:00 +00002231 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002232 {
2233 int
2234 number_colors;
2235
2236 png_colorp
2237 palette;
2238
2239 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002240 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002241
cristy3ed852e2009-09-05 21:47:34 +00002242 if (logging != MagickFalse)
2243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2244 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2245 }
2246 }
2247
2248 if (image->storage_class == PseudoClass)
2249 {
2250 /*
2251 Initialize image colormap.
2252 */
2253 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2254 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002255
glennrpfaa852b2010-03-30 12:17:00 +00002256 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002257 {
2258 int
2259 number_colors;
2260
2261 png_colorp
2262 palette;
2263
2264 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002265
glennrp6af6cf12011-04-22 13:05:16 +00002266 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002267 {
2268 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2269 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2270 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2271 }
glennrp6af6cf12011-04-22 13:05:16 +00002272
glennrp67b9c1a2011-04-22 18:47:36 +00002273 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002274 {
2275 image->colormap[i].red=0;
2276 image->colormap[i].green=0;
2277 image->colormap[i].blue=0;
2278 }
cristy3ed852e2009-09-05 21:47:34 +00002279 }
glennrp0fe50b42010-11-16 03:52:51 +00002280
cristy3ed852e2009-09-05 21:47:34 +00002281 else
2282 {
cristybb503372010-05-27 20:51:26 +00002283 size_t
cristy3ed852e2009-09-05 21:47:34 +00002284 scale;
2285
glennrpfaa852b2010-03-30 12:17:00 +00002286 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002287
cristy3ed852e2009-09-05 21:47:34 +00002288 if (scale < 1)
2289 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristybb503372010-05-27 20:51:26 +00002291 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002292 {
2293 image->colormap[i].red=(Quantum) (i*scale);
2294 image->colormap[i].green=(Quantum) (i*scale);
2295 image->colormap[i].blue=(Quantum) (i*scale);
2296 }
2297 }
2298 }
glennrp147bc912011-03-30 18:47:21 +00002299
glennrpcb395ac2011-03-30 19:50:23 +00002300 /* Set some properties for reporting by "identify" */
2301 {
glennrp147bc912011-03-30 18:47:21 +00002302 char
2303 msg[MaxTextExtent];
2304
2305 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2306 ping_interlace_method in value */
2307
glennrp7cdb11c2011-03-31 18:17:25 +00002308 (void) FormatMagickString(msg,MaxTextExtent,
2309 "%d, %d",(int) ping_width, (int) ping_height);
2310 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002311
2312 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2313 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2314
2315 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2316 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2317
2318 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2319 (int) ping_interlace_method);
2320 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002321 }
glennrp147bc912011-03-30 18:47:21 +00002322
cristy3ed852e2009-09-05 21:47:34 +00002323 /*
2324 Read image scanlines.
2325 */
2326 if (image->delay != 0)
2327 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002328
glennrp0ca69b12010-07-26 01:57:52 +00002329 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002330 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2331 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002332 {
2333 if (logging != MagickFalse)
2334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002335 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002336 mng_info->scenes_found-1);
2337 png_destroy_read_struct(&ping,&ping_info,&end_info);
2338#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002339 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002340#endif
2341 if (logging != MagickFalse)
2342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2343 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002344
cristy3ed852e2009-09-05 21:47:34 +00002345 return(image);
2346 }
glennrp0fe50b42010-11-16 03:52:51 +00002347
cristy3ed852e2009-09-05 21:47:34 +00002348 if (logging != MagickFalse)
2349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2350 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002353 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2354 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002355
cristy3ed852e2009-09-05 21:47:34 +00002356 else
glennrpcf002022011-01-30 02:38:15 +00002357 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2358 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002359
glennrpcf002022011-01-30 02:38:15 +00002360 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002361 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002362
cristy3ed852e2009-09-05 21:47:34 +00002363 if (logging != MagickFalse)
2364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2365 " Converting PNG pixels to pixel packets");
2366 /*
2367 Convert PNG pixels to pixel packets.
2368 */
glennrpfaa852b2010-03-30 12:17:00 +00002369 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002370 {
2371 /*
2372 PNG image is corrupt.
2373 */
2374 png_destroy_read_struct(&ping,&ping_info,&end_info);
2375#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002376 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002377#endif
2378 if (quantum_info != (QuantumInfo *) NULL)
2379 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002380
glennrpcf002022011-01-30 02:38:15 +00002381 if (ping_pixels != (unsigned char *) NULL)
2382 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002383
cristy3ed852e2009-09-05 21:47:34 +00002384 if (logging != MagickFalse)
2385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2386 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002387
cristy3ed852e2009-09-05 21:47:34 +00002388 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002389 {
2390 InheritException(exception,&image->exception);
2391 image->columns=0;
2392 }
glennrp0fe50b42010-11-16 03:52:51 +00002393
cristy3ed852e2009-09-05 21:47:34 +00002394 return(GetFirstImageInList(image));
2395 }
glennrp0fe50b42010-11-16 03:52:51 +00002396
cristyed552522009-10-16 14:04:35 +00002397 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002398
cristyed552522009-10-16 14:04:35 +00002399 if (quantum_info == (QuantumInfo *) NULL)
2400 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002401
glennrpc8cbc5d2011-01-01 00:12:34 +00002402 {
2403
2404 MagickBooleanType
2405 found_transparent_pixel;
2406
2407 found_transparent_pixel=MagickFalse;
2408
cristy3ed852e2009-09-05 21:47:34 +00002409 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002410 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002411 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002412 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002413 /*
2414 Convert image to DirectClass pixel packets.
2415 */
glennrp67b9c1a2011-04-22 18:47:36 +00002416#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2417 int
2418 depth;
2419
2420 depth=(ssize_t) ping_bit_depth;
2421#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002422 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2423 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2424 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2425 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002426
glennrpc8cbc5d2011-01-01 00:12:34 +00002427 for (y=0; y < (ssize_t) image->rows; y++)
2428 {
2429 if (num_passes > 1)
2430 row_offset=ping_rowbytes*y;
2431
2432 else
2433 row_offset=0;
2434
glennrpcf002022011-01-30 02:38:15 +00002435 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002436 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2437
2438 if (q == (PixelPacket *) NULL)
2439 break;
2440
glennrpc8cbc5d2011-01-01 00:12:34 +00002441 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2442 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002443 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002444
2445 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2446 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002447 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002448
2449 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2450 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002451 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002452
2453 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2454 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002455 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002456
2457 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2458 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002459 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002460
glennrpc8cbc5d2011-01-01 00:12:34 +00002461 if (found_transparent_pixel == MagickFalse)
2462 {
2463 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002464 if (y== 0 && logging != MagickFalse)
2465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2466 " Looking for cheap transparent pixel");
2467
glennrpc8cbc5d2011-01-01 00:12:34 +00002468 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2469 {
glennrp5aa37f62011-01-02 03:07:57 +00002470 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2471 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
glennrp8b698592011-04-26 03:38:21 +00002472 (GetOpacityPixelComponent(q) != OpaqueOpacity))
glennrpc8cbc5d2011-01-01 00:12:34 +00002473 {
glennrpa6a06632011-01-19 15:15:34 +00002474 if (logging != MagickFalse)
2475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2476 " ...got one.");
2477
glennrpc8cbc5d2011-01-01 00:12:34 +00002478 found_transparent_pixel = MagickTrue;
2479 break;
2480 }
glennrp4f25bd02011-01-01 18:51:28 +00002481 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2482 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp8b698592011-04-26 03:38:21 +00002483 (ScaleQuantumToShort(GetRedPixelComponent(q))
2484 == transparent_color.red &&
2485 ScaleQuantumToShort(GetGreenPixelComponent(q))
2486 == transparent_color.green &&
2487 ScaleQuantumToShort(GetBluePixelComponent(q))
2488 == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002489 {
glennrpa6a06632011-01-19 15:15:34 +00002490 if (logging != MagickFalse)
2491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2492 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002493 found_transparent_pixel = MagickTrue;
2494 break;
2495 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002496 q++;
2497 }
2498 }
2499
2500 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2501 {
2502 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2503 image->rows);
2504
2505 if (status == MagickFalse)
2506 break;
2507 }
2508 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2509 break;
2510 }
2511
2512 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2513 {
2514 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002515 if (status == MagickFalse)
2516 break;
2517 }
cristy3ed852e2009-09-05 21:47:34 +00002518 }
cristy3ed852e2009-09-05 21:47:34 +00002519 }
glennrp0fe50b42010-11-16 03:52:51 +00002520
cristy3ed852e2009-09-05 21:47:34 +00002521 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002522
cristy3ed852e2009-09-05 21:47:34 +00002523 for (pass=0; pass < num_passes; pass++)
2524 {
2525 Quantum
2526 *quantum_scanline;
2527
2528 register Quantum
2529 *r;
2530
2531 /*
2532 Convert grayscale image to PseudoClass pixel packets.
2533 */
glennrpfaa852b2010-03-30 12:17:00 +00002534 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002535 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002536
cristy3ed852e2009-09-05 21:47:34 +00002537 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2538 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002539
cristy3ed852e2009-09-05 21:47:34 +00002540 if (quantum_scanline == (Quantum *) NULL)
2541 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002542
cristybb503372010-05-27 20:51:26 +00002543 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002544 {
2545 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002546 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002547
cristy3ed852e2009-09-05 21:47:34 +00002548 else
2549 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00002550
glennrpcf002022011-01-30 02:38:15 +00002551 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002552 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00002553
cristy3ed852e2009-09-05 21:47:34 +00002554 if (q == (PixelPacket *) NULL)
2555 break;
glennrp0fe50b42010-11-16 03:52:51 +00002556
cristy5c6f7892010-05-05 22:53:29 +00002557 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00002558 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00002559 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00002560
glennrpfaa852b2010-03-30 12:17:00 +00002561 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00002562 {
2563 case 1:
2564 {
cristybb503372010-05-27 20:51:26 +00002565 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002566 bit;
2567
cristybb503372010-05-27 20:51:26 +00002568 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00002569 {
2570 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002571 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002572 p++;
2573 }
glennrp0fe50b42010-11-16 03:52:51 +00002574
cristy3ed852e2009-09-05 21:47:34 +00002575 if ((image->columns % 8) != 0)
2576 {
cristybb503372010-05-27 20:51:26 +00002577 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002578 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002579 }
glennrp0fe50b42010-11-16 03:52:51 +00002580
cristy3ed852e2009-09-05 21:47:34 +00002581 break;
2582 }
glennrp47b9dd52010-11-24 18:12:06 +00002583
cristy3ed852e2009-09-05 21:47:34 +00002584 case 2:
2585 {
cristybb503372010-05-27 20:51:26 +00002586 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002587 {
glennrpa18d5bc2011-04-23 14:51:34 +00002588 *r++=(*p >> 6) & 0x03;
2589 *r++=(*p >> 4) & 0x03;
2590 *r++=(*p >> 2) & 0x03;
2591 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00002592 }
glennrp0fe50b42010-11-16 03:52:51 +00002593
cristy3ed852e2009-09-05 21:47:34 +00002594 if ((image->columns % 4) != 0)
2595 {
cristybb503372010-05-27 20:51:26 +00002596 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00002597 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00002598 }
glennrp0fe50b42010-11-16 03:52:51 +00002599
cristy3ed852e2009-09-05 21:47:34 +00002600 break;
2601 }
glennrp47b9dd52010-11-24 18:12:06 +00002602
cristy3ed852e2009-09-05 21:47:34 +00002603 case 4:
2604 {
cristybb503372010-05-27 20:51:26 +00002605 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00002606 {
glennrpa18d5bc2011-04-23 14:51:34 +00002607 *r++=(*p >> 4) & 0x0f;
2608 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00002609 }
glennrp0fe50b42010-11-16 03:52:51 +00002610
cristy3ed852e2009-09-05 21:47:34 +00002611 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00002612 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00002613
cristy3ed852e2009-09-05 21:47:34 +00002614 break;
2615 }
glennrp47b9dd52010-11-24 18:12:06 +00002616
cristy3ed852e2009-09-05 21:47:34 +00002617 case 8:
2618 {
glennrpfaa852b2010-03-30 12:17:00 +00002619 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00002620 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002621 {
glennrpa18d5bc2011-04-23 14:51:34 +00002622 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00002623 /* In image.h, OpaqueOpacity is 0
2624 * TransparentOpacity is QuantumRange
2625 * In a PNG datastream, Opaque is QuantumRange
2626 * and Transparent is 0.
2627 */
glennrp8b698592011-04-26 03:38:21 +00002628 SetOpacityPixelComponent(q,
2629 ScaleCharToQuantum((unsigned char) (255-(*p++))));
2630 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00002631 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002632 q++;
2633 }
glennrp0fe50b42010-11-16 03:52:51 +00002634
cristy3ed852e2009-09-05 21:47:34 +00002635 else
cristybb503372010-05-27 20:51:26 +00002636 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002637 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00002638
cristy3ed852e2009-09-05 21:47:34 +00002639 break;
2640 }
glennrp47b9dd52010-11-24 18:12:06 +00002641
cristy3ed852e2009-09-05 21:47:34 +00002642 case 16:
2643 {
cristybb503372010-05-27 20:51:26 +00002644 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002645 {
glennrp58f77c72011-04-23 14:09:09 +00002646#if (MAGICKCORE_QUANTUM_DEPTH == 16)
2647 size_t
2648 quantum;
2649
2650 if (image->colors > 256)
2651 *r=((*p++) << 8);
2652
2653 else
2654 *r=0;
2655
2656 quantum=(*r);
2657 quantum|=(*p++);
2658 *r=(Quantum) quantum;
2659 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00002660
2661 if (ping_color_type == 4)
2662 {
glennrp58f77c72011-04-23 14:09:09 +00002663 quantum=((*p++) << 8);
2664 quantum|=(*p++);
glennrp8b698592011-04-26 03:38:21 +00002665 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-quantum));
2666 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002667 found_transparent_pixel = MagickTrue;
2668 q++;
2669 }
2670#else
2671#if (MAGICKCORE_QUANTUM_DEPTH == 32)
2672 size_t
2673 quantum;
2674
2675 if (image->colors > 256)
2676 *r=((*p++) << 8);
2677
2678 else
2679 *r=0;
2680
2681 quantum=(*r);
2682 quantum|=(*p++);
2683 *r=quantum;
2684 r++;
2685
2686 if (ping_color_type == 4)
2687 {
glennrp8b698592011-04-26 03:38:21 +00002688 quantum=(*p << 8) | *(p+1);
2689 quantum*=65537L;
2690 SetOpacityPixelComponent(q,
2691 (Quantum) GetAlphaPixelComponent(q));
2692 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002693 found_transparent_pixel = MagickTrue;
2694 p+=2;
2695 q++;
2696 }
2697
2698#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2699 *r++=(*p++);
2700 p++; /* strip low byte */
2701
2702 if (ping_color_type == 4)
2703 {
glennrp8b698592011-04-26 03:38:21 +00002704 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-(*p++)));
2705 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp9d0ea4d2011-04-22 18:35:57 +00002706 found_transparent_pixel = MagickTrue;
2707 p++;
2708 q++;
2709 }
cristy3ed852e2009-09-05 21:47:34 +00002710#endif
glennrp58f77c72011-04-23 14:09:09 +00002711#endif
glennrpa18d5bc2011-04-23 14:51:34 +00002712 }
glennrp47b9dd52010-11-24 18:12:06 +00002713
cristy3ed852e2009-09-05 21:47:34 +00002714 break;
2715 }
glennrp47b9dd52010-11-24 18:12:06 +00002716
cristy3ed852e2009-09-05 21:47:34 +00002717 default:
2718 break;
2719 }
glennrp3faa9a32011-04-23 14:00:25 +00002720
cristy3ed852e2009-09-05 21:47:34 +00002721 /*
2722 Transfer image scanline.
2723 */
2724 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00002725
cristybb503372010-05-27 20:51:26 +00002726 for (x=0; x < (ssize_t) image->columns; x++)
cristy80ac8b92010-05-08 13:36:57 +00002727 indexes[x]=(IndexPacket) (*r++);
glennrp0fe50b42010-11-16 03:52:51 +00002728
cristy3ed852e2009-09-05 21:47:34 +00002729 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2730 break;
glennrp0fe50b42010-11-16 03:52:51 +00002731
cristy7a287bf2010-02-14 02:18:09 +00002732 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2733 {
cristycee97112010-05-28 00:44:52 +00002734 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2735 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00002736
cristy7a287bf2010-02-14 02:18:09 +00002737 if (status == MagickFalse)
2738 break;
2739 }
cristy3ed852e2009-09-05 21:47:34 +00002740 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002741
cristy7a287bf2010-02-14 02:18:09 +00002742 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002743 {
2744 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00002745
cristy3ed852e2009-09-05 21:47:34 +00002746 if (status == MagickFalse)
2747 break;
2748 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002749
cristy3ed852e2009-09-05 21:47:34 +00002750 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2751 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002752
2753 image->matte=found_transparent_pixel;
2754
2755 if (logging != MagickFalse)
2756 {
2757 if (found_transparent_pixel != MagickFalse)
2758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2759 " Found transparent pixel");
2760 else
glennrp5aa37f62011-01-02 03:07:57 +00002761 {
2762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2763 " No transparent pixel was found");
glennrpa6a06632011-01-19 15:15:34 +00002764
glennrp5aa37f62011-01-02 03:07:57 +00002765 ping_color_type&=0x03;
2766 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002767 }
2768 }
2769
cristyb32b90a2009-09-07 21:45:48 +00002770 if (quantum_info != (QuantumInfo *) NULL)
2771 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002772
cristy5c6f7892010-05-05 22:53:29 +00002773 if (image->storage_class == PseudoClass)
2774 {
cristyaeb2cbc2010-05-07 13:28:58 +00002775 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002776 matte;
2777
2778 matte=image->matte;
2779 image->matte=MagickFalse;
2780 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002781 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002782 }
glennrp47b9dd52010-11-24 18:12:06 +00002783
glennrp4eb39312011-03-30 21:34:55 +00002784 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00002785
2786 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002787 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002788 {
2789 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00002790 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002791 image->colors=2;
2792 (void) SetImageBackgroundColor(image);
2793#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002794 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002795#endif
2796 if (logging != MagickFalse)
2797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2798 " exit ReadOnePNGImage() early.");
2799 return(image);
2800 }
glennrp47b9dd52010-11-24 18:12:06 +00002801
glennrpfaa852b2010-03-30 12:17:00 +00002802 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002803 {
2804 ClassType
2805 storage_class;
2806
2807 /*
2808 Image has a transparent background.
2809 */
2810 storage_class=image->storage_class;
2811 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002812
glennrp3c218112010-11-27 15:31:26 +00002813/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00002814
glennrp0fe50b42010-11-16 03:52:51 +00002815 if (storage_class == PseudoClass)
2816 {
2817 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002818 {
glennrp0fe50b42010-11-16 03:52:51 +00002819 for (x=0; x < ping_num_trans; x++)
2820 {
2821 image->colormap[x].opacity =
2822 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2823 }
glennrpc11cf6a2010-03-20 16:46:19 +00002824 }
glennrp47b9dd52010-11-24 18:12:06 +00002825
glennrp0fe50b42010-11-16 03:52:51 +00002826 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2827 {
2828 for (x=0; x < (int) image->colors; x++)
2829 {
2830 if (ScaleQuantumToShort(image->colormap[x].red) ==
2831 transparent_color.opacity)
2832 {
2833 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2834 }
2835 }
2836 }
2837 (void) SyncImage(image);
2838 }
glennrp47b9dd52010-11-24 18:12:06 +00002839
glennrpa6a06632011-01-19 15:15:34 +00002840#if 1 /* Should have already been done above, but glennrp problem P10
2841 * needs this.
2842 */
glennrp0fe50b42010-11-16 03:52:51 +00002843 else
2844 {
2845 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002846 {
glennrp0fe50b42010-11-16 03:52:51 +00002847 image->storage_class=storage_class;
2848 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2849
2850 if (q == (PixelPacket *) NULL)
2851 break;
2852
2853 indexes=GetAuthenticIndexQueue(image);
2854
glennrpa6a06632011-01-19 15:15:34 +00002855 /* Caution: on a Q8 build, this does not distinguish between
2856 * 16-bit colors that differ only in the low byte
2857 */
glennrp0fe50b42010-11-16 03:52:51 +00002858 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2859 {
glennrp8b698592011-04-26 03:38:21 +00002860 if (ScaleQuantumToShort(GetRedPixelComponent(q))
2861 == transparent_color.red &&
2862 ScaleQuantumToShort(GetGreenPixelComponent(q))
2863 == transparent_color.green &&
2864 ScaleQuantumToShort(GetBluePixelComponent(q))
2865 == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00002866 {
glennrp8b698592011-04-26 03:38:21 +00002867 SetOpacityPixelComponent(q,TransparentOpacity);
glennrp4f25bd02011-01-01 18:51:28 +00002868 }
glennrp0fe50b42010-11-16 03:52:51 +00002869
glennrp67b9c1a2011-04-22 18:47:36 +00002870#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00002871 else
glennrp4f25bd02011-01-01 18:51:28 +00002872 {
2873 q->opacity=(Quantum) OpaqueOpacity;
2874 }
glennrpa6a06632011-01-19 15:15:34 +00002875#endif
glennrp0fe50b42010-11-16 03:52:51 +00002876
2877 q++;
2878 }
2879
2880 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2881 break;
glennrpc11cf6a2010-03-20 16:46:19 +00002882 }
glennrp0fe50b42010-11-16 03:52:51 +00002883 }
glennrpa6a06632011-01-19 15:15:34 +00002884#endif
glennrpc11cf6a2010-03-20 16:46:19 +00002885
cristy3ed852e2009-09-05 21:47:34 +00002886 image->storage_class=DirectClass;
2887 }
glennrp3c218112010-11-27 15:31:26 +00002888
cristyb40fc462010-08-08 00:49:49 +00002889 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2890 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2891 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00002892
cristyeb3b22a2011-03-31 20:16:11 +00002893 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00002894 {
2895 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00002896 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2897 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00002898 else
glennrpa0ed0092011-04-18 16:36:29 +00002899 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2900 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002901
glennrp4eb39312011-03-30 21:34:55 +00002902 if (status != MagickFalse)
2903 for (i=0; i < (ssize_t) num_text; i++)
2904 {
2905 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00002906
glennrp4eb39312011-03-30 21:34:55 +00002907 if (logging != MagickFalse)
2908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2909 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00002910
glennrp4eb39312011-03-30 21:34:55 +00002911 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00002912 {
glennrp4eb39312011-03-30 21:34:55 +00002913 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2914 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00002915 }
glennrp0fe50b42010-11-16 03:52:51 +00002916
glennrp4eb39312011-03-30 21:34:55 +00002917 else
2918 {
2919 char
2920 *value;
2921
2922 length=text[i].text_length;
2923 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2924 sizeof(*value));
2925 if (value == (char *) NULL)
2926 {
2927 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2928 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2929 image->filename);
2930 break;
2931 }
2932 *value='\0';
2933 (void) ConcatenateMagickString(value,text[i].text,length+2);
2934
2935 /* Don't save "density" or "units" property if we have a pHYs
2936 * chunk
2937 */
2938 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2939 (LocaleCompare(text[i].key,"density") != 0 &&
2940 LocaleCompare(text[i].key,"units") != 0))
2941 (void) SetImageProperty(image,text[i].key,value);
2942
2943 if (logging != MagickFalse)
2944 {
2945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2946 " length: %lu",(unsigned long) length);
2947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2948 " Keyword: %s",text[i].key);
2949 }
2950
2951 value=DestroyString(value);
2952 }
2953 }
2954 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00002955 }
glennrp3c218112010-11-27 15:31:26 +00002956
cristy3ed852e2009-09-05 21:47:34 +00002957#ifdef MNG_OBJECT_BUFFERS
2958 /*
2959 Store the object if necessary.
2960 */
2961 if (object_id && !mng_info->frozen[object_id])
2962 {
2963 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2964 {
2965 /*
2966 create a new object buffer.
2967 */
2968 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00002969 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00002970
cristy3ed852e2009-09-05 21:47:34 +00002971 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2972 {
2973 mng_info->ob[object_id]->image=(Image *) NULL;
2974 mng_info->ob[object_id]->reference_count=1;
2975 }
2976 }
glennrp47b9dd52010-11-24 18:12:06 +00002977
cristy3ed852e2009-09-05 21:47:34 +00002978 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2979 mng_info->ob[object_id]->frozen)
2980 {
2981 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2982 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2983 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2984 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00002985
cristy3ed852e2009-09-05 21:47:34 +00002986 if (mng_info->ob[object_id]->frozen)
2987 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2988 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2989 "`%s'",image->filename);
2990 }
glennrp0fe50b42010-11-16 03:52:51 +00002991
cristy3ed852e2009-09-05 21:47:34 +00002992 else
2993 {
cristy3ed852e2009-09-05 21:47:34 +00002994
2995 if (mng_info->ob[object_id]->image != (Image *) NULL)
2996 mng_info->ob[object_id]->image=DestroyImage
2997 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00002998
cristy3ed852e2009-09-05 21:47:34 +00002999 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3000 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003001
cristy3ed852e2009-09-05 21:47:34 +00003002 if (mng_info->ob[object_id]->image != (Image *) NULL)
3003 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003004
cristy3ed852e2009-09-05 21:47:34 +00003005 else
3006 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3007 ResourceLimitError,"Cloning image for object buffer failed",
3008 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003009
glennrpfaa852b2010-03-30 12:17:00 +00003010 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003011 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003012
glennrpfaa852b2010-03-30 12:17:00 +00003013 mng_info->ob[object_id]->width=ping_width;
3014 mng_info->ob[object_id]->height=ping_height;
3015 mng_info->ob[object_id]->color_type=ping_color_type;
3016 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3017 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3018 mng_info->ob[object_id]->compression_method=
3019 ping_compression_method;
3020 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003021
glennrpfaa852b2010-03-30 12:17:00 +00003022 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003023 {
3024 int
3025 number_colors;
3026
3027 png_colorp
3028 plte;
3029
3030 /*
3031 Copy the PLTE to the object buffer.
3032 */
3033 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3034 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003035
cristy3ed852e2009-09-05 21:47:34 +00003036 for (i=0; i < number_colors; i++)
3037 {
3038 mng_info->ob[object_id]->plte[i]=plte[i];
3039 }
3040 }
glennrp47b9dd52010-11-24 18:12:06 +00003041
cristy3ed852e2009-09-05 21:47:34 +00003042 else
3043 mng_info->ob[object_id]->plte_length=0;
3044 }
3045 }
3046#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003047
3048 /* Set image->matte to MagickTrue if the input colortype supports
3049 * alpha or if a valid tRNS chunk is present, no matter whether there
3050 * is actual transparency present.
3051 */
3052 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3053 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3054 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3055 MagickTrue : MagickFalse;
3056
glennrpcb395ac2011-03-30 19:50:23 +00003057 /* Set more properties for identify to retrieve */
3058 {
3059 char
3060 msg[MaxTextExtent];
3061
glennrp4eb39312011-03-30 21:34:55 +00003062 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003063 {
3064 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3065 (void) FormatMagickString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003066 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003067 (void) SetImageProperty(image,"PNG:text ",msg);
3068 }
3069
3070 if (num_raw_profiles != 0)
3071 {
3072 (void) FormatMagickString(msg,MaxTextExtent,
3073 "%d were found", num_raw_profiles);
3074 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3075 }
3076
glennrpcb395ac2011-03-30 19:50:23 +00003077 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003078 {
3079 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3080 "chunk was found (see Chromaticity, above)");
3081 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3082 }
glennrpcb395ac2011-03-30 19:50:23 +00003083
3084 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003085 {
3086 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3087 "chunk was found (see Background color, above)");
3088 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3089 }
3090
3091 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3092 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003093
3094 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3095 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3096
glennrpcb395ac2011-03-30 19:50:23 +00003097 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3098 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003099
3100#if defined(PNG_sRGB_SUPPORTED)
3101 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3102 {
glennrp07523c72011-03-31 18:12:10 +00003103 (void) FormatMagickString(msg,MaxTextExtent,
3104 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003105 (int) intent);
3106 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3107 }
3108#endif
3109
3110 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3111 {
glennrp07523c72011-03-31 18:12:10 +00003112 (void) FormatMagickString(msg,MaxTextExtent,
3113 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003114 file_gamma);
3115 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3116 }
3117
3118#if defined(PNG_pHYs_SUPPORTED)
3119 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3120 {
glennrp07523c72011-03-31 18:12:10 +00003121 (void) FormatMagickString(msg,MaxTextExtent,
3122 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003123 (double) x_resolution,(double) y_resolution, unit_type);
3124 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3125 }
3126#endif
3127
3128#if defined(PNG_oFFs_SUPPORTED)
3129 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3130 {
3131 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3132 (double) image->page.x,(double) image->page.y);
3133 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3134 }
3135#endif
3136
glennrp07523c72011-03-31 18:12:10 +00003137 if ((image->page.width != 0 && image->page.width != image->columns) ||
3138 (image->page.height != 0 && image->page.height != image->rows))
3139 {
3140 (void) FormatMagickString(msg,MaxTextExtent,
3141 "width=%.20g, height=%.20g",
3142 (double) image->page.width,(double) image->page.height);
3143 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3144 }
glennrpcb395ac2011-03-30 19:50:23 +00003145 }
3146
cristy3ed852e2009-09-05 21:47:34 +00003147 /*
3148 Relinquish resources.
3149 */
3150 png_destroy_read_struct(&ping,&ping_info,&end_info);
3151
glennrpcf002022011-01-30 02:38:15 +00003152 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003153#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003154 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003155#endif
3156
3157 if (logging != MagickFalse)
3158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3159 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003160
cristy3ed852e2009-09-05 21:47:34 +00003161 return(image);
3162
3163/* end of reading one PNG image */
3164}
3165
3166static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3167{
3168 Image
3169 *image,
3170 *previous;
3171
3172 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003173 have_mng_structure,
3174 logging,
cristy3ed852e2009-09-05 21:47:34 +00003175 status;
3176
3177 MngInfo
3178 *mng_info;
3179
3180 char
3181 magic_number[MaxTextExtent];
3182
cristy3ed852e2009-09-05 21:47:34 +00003183 ssize_t
3184 count;
3185
3186 /*
3187 Open image file.
3188 */
3189 assert(image_info != (const ImageInfo *) NULL);
3190 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003191
cristy3ed852e2009-09-05 21:47:34 +00003192 if (image_info->debug != MagickFalse)
3193 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3194 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003195
cristy3ed852e2009-09-05 21:47:34 +00003196 assert(exception != (ExceptionInfo *) NULL);
3197 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003198 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003199 image=AcquireImage(image_info);
3200 mng_info=(MngInfo *) NULL;
3201 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003202
cristy3ed852e2009-09-05 21:47:34 +00003203 if (status == MagickFalse)
3204 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003205
cristy3ed852e2009-09-05 21:47:34 +00003206 /*
3207 Verify PNG signature.
3208 */
3209 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003210
glennrpdde35db2011-02-21 12:06:32 +00003211 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003212 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003213
cristy3ed852e2009-09-05 21:47:34 +00003214 /*
3215 Allocate a MngInfo structure.
3216 */
3217 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003218 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003219
cristy3ed852e2009-09-05 21:47:34 +00003220 if (mng_info == (MngInfo *) NULL)
3221 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003222
cristy3ed852e2009-09-05 21:47:34 +00003223 /*
3224 Initialize members of the MngInfo structure.
3225 */
3226 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3227 mng_info->image=image;
3228 have_mng_structure=MagickTrue;
3229
3230 previous=image;
3231 image=ReadOnePNGImage(mng_info,image_info,exception);
3232 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003233
cristy3ed852e2009-09-05 21:47:34 +00003234 if (image == (Image *) NULL)
3235 {
3236 if (previous != (Image *) NULL)
3237 {
3238 if (previous->signature != MagickSignature)
3239 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003240
cristy3ed852e2009-09-05 21:47:34 +00003241 (void) CloseBlob(previous);
3242 (void) DestroyImageList(previous);
3243 }
glennrp0fe50b42010-11-16 03:52:51 +00003244
cristy3ed852e2009-09-05 21:47:34 +00003245 if (logging != MagickFalse)
3246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3247 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003248
cristy3ed852e2009-09-05 21:47:34 +00003249 return((Image *) NULL);
3250 }
glennrp47b9dd52010-11-24 18:12:06 +00003251
cristy3ed852e2009-09-05 21:47:34 +00003252 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003253
cristy3ed852e2009-09-05 21:47:34 +00003254 if ((image->columns == 0) || (image->rows == 0))
3255 {
3256 if (logging != MagickFalse)
3257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3258 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003259
cristy3ed852e2009-09-05 21:47:34 +00003260 ThrowReaderException(CorruptImageError,"CorruptImage");
3261 }
glennrp47b9dd52010-11-24 18:12:06 +00003262
glennrp3faa9a32011-04-23 14:00:25 +00003263#if 0 /* This is probably redundant now */
cristy3ed852e2009-09-05 21:47:34 +00003264 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3265 {
3266 (void) SetImageType(image,PaletteType);
glennrp0fe50b42010-11-16 03:52:51 +00003267
cristy3ed852e2009-09-05 21:47:34 +00003268 if (image->matte != MagickFalse)
3269 {
3270 /* To do: Reduce to binary transparency */
3271 }
3272 }
glennrp3faa9a32011-04-23 14:00:25 +00003273#endif
glennrp47b9dd52010-11-24 18:12:06 +00003274
cristy3ed852e2009-09-05 21:47:34 +00003275 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3276 {
3277 (void) SetImageType(image,TrueColorType);
3278 image->matte=MagickFalse;
3279 }
glennrp0fe50b42010-11-16 03:52:51 +00003280
cristy3ed852e2009-09-05 21:47:34 +00003281 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3282 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003283
cristy3ed852e2009-09-05 21:47:34 +00003284 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3286 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3287 (double) image->page.width,(double) image->page.height,
3288 (double) image->page.x,(double) image->page.y);
3289
3290 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003292
cristy3ed852e2009-09-05 21:47:34 +00003293 return(image);
3294}
3295
3296
3297
3298#if defined(JNG_SUPPORTED)
3299/*
3300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3301% %
3302% %
3303% %
3304% R e a d O n e J N G I m a g e %
3305% %
3306% %
3307% %
3308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3309%
3310% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3311% (minus the 8-byte signature) and returns it. It allocates the memory
3312% necessary for the new Image structure and returns a pointer to the new
3313% image.
3314%
3315% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3316%
3317% The format of the ReadOneJNGImage method is:
3318%
3319% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3320% ExceptionInfo *exception)
3321%
3322% A description of each parameter follows:
3323%
3324% o mng_info: Specifies a pointer to a MngInfo structure.
3325%
3326% o image_info: the image info.
3327%
3328% o exception: return any errors or warnings in this structure.
3329%
3330*/
3331static Image *ReadOneJNGImage(MngInfo *mng_info,
3332 const ImageInfo *image_info, ExceptionInfo *exception)
3333{
3334 Image
3335 *alpha_image,
3336 *color_image,
3337 *image,
3338 *jng_image;
3339
3340 ImageInfo
3341 *alpha_image_info,
3342 *color_image_info;
3343
cristy4383ec82011-01-05 15:42:32 +00003344 MagickBooleanType
3345 logging;
3346
cristybb503372010-05-27 20:51:26 +00003347 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003348 y;
3349
3350 MagickBooleanType
3351 status;
3352
3353 png_uint_32
3354 jng_height,
3355 jng_width;
3356
3357 png_byte
3358 jng_color_type,
3359 jng_image_sample_depth,
3360 jng_image_compression_method,
3361 jng_image_interlace_method,
3362 jng_alpha_sample_depth,
3363 jng_alpha_compression_method,
3364 jng_alpha_filter_method,
3365 jng_alpha_interlace_method;
3366
3367 register const PixelPacket
3368 *s;
3369
cristybb503372010-05-27 20:51:26 +00003370 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003371 i,
3372 x;
3373
3374 register PixelPacket
3375 *q;
3376
3377 register unsigned char
3378 *p;
3379
3380 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003381 read_JSEP,
3382 reading_idat,
3383 skip_to_iend;
3384
cristybb503372010-05-27 20:51:26 +00003385 size_t
cristy3ed852e2009-09-05 21:47:34 +00003386 length;
3387
3388 jng_alpha_compression_method=0;
3389 jng_alpha_sample_depth=8;
3390 jng_color_type=0;
3391 jng_height=0;
3392 jng_width=0;
3393 alpha_image=(Image *) NULL;
3394 color_image=(Image *) NULL;
3395 alpha_image_info=(ImageInfo *) NULL;
3396 color_image_info=(ImageInfo *) NULL;
3397
3398 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003399 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003400
3401 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003402
cristy3ed852e2009-09-05 21:47:34 +00003403 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3404 {
3405 /*
3406 Allocate next image structure.
3407 */
3408 if (logging != MagickFalse)
3409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3410 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003411
cristy3ed852e2009-09-05 21:47:34 +00003412 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003413
cristy3ed852e2009-09-05 21:47:34 +00003414 if (GetNextImageInList(image) == (Image *) NULL)
3415 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003416
cristy3ed852e2009-09-05 21:47:34 +00003417 image=SyncNextImageInList(image);
3418 }
3419 mng_info->image=image;
3420
3421 /*
3422 Signature bytes have already been read.
3423 */
3424
3425 read_JSEP=MagickFalse;
3426 reading_idat=MagickFalse;
3427 skip_to_iend=MagickFalse;
3428 for (;;)
3429 {
3430 char
3431 type[MaxTextExtent];
3432
3433 unsigned char
3434 *chunk;
3435
3436 unsigned int
3437 count;
3438
3439 /*
3440 Read a new JNG chunk.
3441 */
3442 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3443 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003444
cristy3ed852e2009-09-05 21:47:34 +00003445 if (status == MagickFalse)
3446 break;
glennrp0fe50b42010-11-16 03:52:51 +00003447
cristy3ed852e2009-09-05 21:47:34 +00003448 type[0]='\0';
3449 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3450 length=ReadBlobMSBLong(image);
3451 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3452
3453 if (logging != MagickFalse)
3454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003455 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3456 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003457
3458 if (length > PNG_UINT_31_MAX || count == 0)
3459 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003460
cristy3ed852e2009-09-05 21:47:34 +00003461 p=NULL;
3462 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003463
cristy3ed852e2009-09-05 21:47:34 +00003464 if (length)
3465 {
3466 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003467
cristy3ed852e2009-09-05 21:47:34 +00003468 if (chunk == (unsigned char *) NULL)
3469 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003470
cristybb503372010-05-27 20:51:26 +00003471 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003472 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003473
cristy3ed852e2009-09-05 21:47:34 +00003474 p=chunk;
3475 }
glennrp47b9dd52010-11-24 18:12:06 +00003476
cristy3ed852e2009-09-05 21:47:34 +00003477 (void) ReadBlobMSBLong(image); /* read crc word */
3478
3479 if (skip_to_iend)
3480 {
3481 if (length)
3482 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003483
cristy3ed852e2009-09-05 21:47:34 +00003484 continue;
3485 }
3486
3487 if (memcmp(type,mng_JHDR,4) == 0)
3488 {
3489 if (length == 16)
3490 {
cristybb503372010-05-27 20:51:26 +00003491 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003492 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003493 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003494 (p[6] << 8) | p[7]);
3495 jng_color_type=p[8];
3496 jng_image_sample_depth=p[9];
3497 jng_image_compression_method=p[10];
3498 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003499
cristy3ed852e2009-09-05 21:47:34 +00003500 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3501 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003502
cristy3ed852e2009-09-05 21:47:34 +00003503 jng_alpha_sample_depth=p[12];
3504 jng_alpha_compression_method=p[13];
3505 jng_alpha_filter_method=p[14];
3506 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003507
cristy3ed852e2009-09-05 21:47:34 +00003508 if (logging != MagickFalse)
3509 {
3510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003511 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003512
cristy3ed852e2009-09-05 21:47:34 +00003513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003514 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003515
cristy3ed852e2009-09-05 21:47:34 +00003516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3517 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003518
cristy3ed852e2009-09-05 21:47:34 +00003519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3520 " jng_image_sample_depth: %3d",
3521 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003522
cristy3ed852e2009-09-05 21:47:34 +00003523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3524 " jng_image_compression_method:%3d",
3525 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003526
cristy3ed852e2009-09-05 21:47:34 +00003527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3528 " jng_image_interlace_method: %3d",
3529 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003530
cristy3ed852e2009-09-05 21:47:34 +00003531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3532 " jng_alpha_sample_depth: %3d",
3533 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003534
cristy3ed852e2009-09-05 21:47:34 +00003535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3536 " jng_alpha_compression_method:%3d",
3537 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003538
cristy3ed852e2009-09-05 21:47:34 +00003539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3540 " jng_alpha_filter_method: %3d",
3541 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003542
cristy3ed852e2009-09-05 21:47:34 +00003543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3544 " jng_alpha_interlace_method: %3d",
3545 jng_alpha_interlace_method);
3546 }
3547 }
glennrp47b9dd52010-11-24 18:12:06 +00003548
cristy3ed852e2009-09-05 21:47:34 +00003549 if (length)
3550 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003551
cristy3ed852e2009-09-05 21:47:34 +00003552 continue;
3553 }
3554
3555
3556 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3557 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3558 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3559 {
3560 /*
3561 o create color_image
3562 o open color_blob, attached to color_image
3563 o if (color type has alpha)
3564 open alpha_blob, attached to alpha_image
3565 */
3566
cristy73bd4a52010-10-05 11:24:23 +00003567 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003568
cristy3ed852e2009-09-05 21:47:34 +00003569 if (color_image_info == (ImageInfo *) NULL)
3570 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003571
cristy3ed852e2009-09-05 21:47:34 +00003572 GetImageInfo(color_image_info);
3573 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003574
cristy3ed852e2009-09-05 21:47:34 +00003575 if (color_image == (Image *) NULL)
3576 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3577
3578 if (logging != MagickFalse)
3579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3580 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003581
cristy3ed852e2009-09-05 21:47:34 +00003582 (void) AcquireUniqueFilename(color_image->filename);
3583 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3584 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003585
cristy3ed852e2009-09-05 21:47:34 +00003586 if (status == MagickFalse)
3587 return((Image *) NULL);
3588
3589 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3590 {
3591 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00003592 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00003593
cristy3ed852e2009-09-05 21:47:34 +00003594 if (alpha_image_info == (ImageInfo *) NULL)
3595 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003596
cristy3ed852e2009-09-05 21:47:34 +00003597 GetImageInfo(alpha_image_info);
3598 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003599
cristy3ed852e2009-09-05 21:47:34 +00003600 if (alpha_image == (Image *) NULL)
3601 {
3602 alpha_image=DestroyImage(alpha_image);
3603 ThrowReaderException(ResourceLimitError,
3604 "MemoryAllocationFailed");
3605 }
glennrp0fe50b42010-11-16 03:52:51 +00003606
cristy3ed852e2009-09-05 21:47:34 +00003607 if (logging != MagickFalse)
3608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3609 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003610
cristy3ed852e2009-09-05 21:47:34 +00003611 (void) AcquireUniqueFilename(alpha_image->filename);
3612 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3613 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003614
cristy3ed852e2009-09-05 21:47:34 +00003615 if (status == MagickFalse)
3616 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003617
cristy3ed852e2009-09-05 21:47:34 +00003618 if (jng_alpha_compression_method == 0)
3619 {
3620 unsigned char
3621 data[18];
3622
3623 if (logging != MagickFalse)
3624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3625 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003626
cristy3ed852e2009-09-05 21:47:34 +00003627 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3628 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00003629
cristy3ed852e2009-09-05 21:47:34 +00003630 (void) WriteBlobMSBULong(alpha_image,13L);
3631 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00003632 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00003633 PNGLong(data+4,jng_width);
3634 PNGLong(data+8,jng_height);
3635 data[12]=jng_alpha_sample_depth;
3636 data[13]=0; /* color_type gray */
3637 data[14]=0; /* compression method 0 */
3638 data[15]=0; /* filter_method 0 */
3639 data[16]=0; /* interlace_method 0 */
3640 (void) WriteBlob(alpha_image,17,data);
3641 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3642 }
3643 }
3644 reading_idat=MagickTrue;
3645 }
3646
3647 if (memcmp(type,mng_JDAT,4) == 0)
3648 {
glennrp47b9dd52010-11-24 18:12:06 +00003649 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003650
3651 if (logging != MagickFalse)
3652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3653 " Copying JDAT chunk data to color_blob.");
3654
3655 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003656
cristy3ed852e2009-09-05 21:47:34 +00003657 if (length)
3658 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003659
cristy3ed852e2009-09-05 21:47:34 +00003660 continue;
3661 }
3662
3663 if (memcmp(type,mng_IDAT,4) == 0)
3664 {
3665 png_byte
3666 data[5];
3667
glennrp47b9dd52010-11-24 18:12:06 +00003668 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003669
3670 if (image_info->ping == MagickFalse)
3671 {
3672 if (logging != MagickFalse)
3673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3674 " Copying IDAT chunk data to alpha_blob.");
3675
cristybb503372010-05-27 20:51:26 +00003676 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003677 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00003678 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00003679 (void) WriteBlob(alpha_image,4,data);
3680 (void) WriteBlob(alpha_image,length,chunk);
3681 (void) WriteBlobMSBULong(alpha_image,
3682 crc32(crc32(0,data,4),chunk,(uInt) length));
3683 }
glennrp0fe50b42010-11-16 03:52:51 +00003684
cristy3ed852e2009-09-05 21:47:34 +00003685 if (length)
3686 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688 continue;
3689 }
3690
3691 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3692 {
glennrp47b9dd52010-11-24 18:12:06 +00003693 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003694
3695 if (image_info->ping == MagickFalse)
3696 {
3697 if (logging != MagickFalse)
3698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3699 " Copying JDAA chunk data to alpha_blob.");
3700
3701 (void) WriteBlob(alpha_image,length,chunk);
3702 }
glennrp0fe50b42010-11-16 03:52:51 +00003703
cristy3ed852e2009-09-05 21:47:34 +00003704 if (length)
3705 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003706
cristy3ed852e2009-09-05 21:47:34 +00003707 continue;
3708 }
3709
3710 if (memcmp(type,mng_JSEP,4) == 0)
3711 {
3712 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00003713
cristy3ed852e2009-09-05 21:47:34 +00003714 if (length)
3715 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003716
cristy3ed852e2009-09-05 21:47:34 +00003717 continue;
3718 }
3719
3720 if (memcmp(type,mng_bKGD,4) == 0)
3721 {
3722 if (length == 2)
3723 {
3724 image->background_color.red=ScaleCharToQuantum(p[1]);
3725 image->background_color.green=image->background_color.red;
3726 image->background_color.blue=image->background_color.red;
3727 }
glennrp0fe50b42010-11-16 03:52:51 +00003728
cristy3ed852e2009-09-05 21:47:34 +00003729 if (length == 6)
3730 {
3731 image->background_color.red=ScaleCharToQuantum(p[1]);
3732 image->background_color.green=ScaleCharToQuantum(p[3]);
3733 image->background_color.blue=ScaleCharToQuantum(p[5]);
3734 }
glennrp0fe50b42010-11-16 03:52:51 +00003735
cristy3ed852e2009-09-05 21:47:34 +00003736 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3737 continue;
3738 }
3739
3740 if (memcmp(type,mng_gAMA,4) == 0)
3741 {
3742 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00003743 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00003744
cristy3ed852e2009-09-05 21:47:34 +00003745 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3746 continue;
3747 }
3748
3749 if (memcmp(type,mng_cHRM,4) == 0)
3750 {
3751 if (length == 32)
3752 {
cristy8182b072010-05-30 20:10:53 +00003753 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3754 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3755 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3756 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3757 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3758 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3759 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3760 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003761 }
glennrp47b9dd52010-11-24 18:12:06 +00003762
cristy3ed852e2009-09-05 21:47:34 +00003763 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3764 continue;
3765 }
3766
3767 if (memcmp(type,mng_sRGB,4) == 0)
3768 {
3769 if (length == 1)
3770 {
glennrpe610a072010-08-05 17:08:46 +00003771 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00003772 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00003773 image->gamma=0.45455f;
3774 image->chromaticity.red_primary.x=0.6400f;
3775 image->chromaticity.red_primary.y=0.3300f;
3776 image->chromaticity.green_primary.x=0.3000f;
3777 image->chromaticity.green_primary.y=0.6000f;
3778 image->chromaticity.blue_primary.x=0.1500f;
3779 image->chromaticity.blue_primary.y=0.0600f;
3780 image->chromaticity.white_point.x=0.3127f;
3781 image->chromaticity.white_point.y=0.3290f;
3782 }
glennrp47b9dd52010-11-24 18:12:06 +00003783
cristy3ed852e2009-09-05 21:47:34 +00003784 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3785 continue;
3786 }
3787
3788 if (memcmp(type,mng_oFFs,4) == 0)
3789 {
3790 if (length > 8)
3791 {
glennrp5eae7602011-02-22 15:21:32 +00003792 image->page.x=(ssize_t) mng_get_long(p);
3793 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00003794
cristy3ed852e2009-09-05 21:47:34 +00003795 if ((int) p[8] != 0)
3796 {
3797 image->page.x/=10000;
3798 image->page.y/=10000;
3799 }
3800 }
glennrp47b9dd52010-11-24 18:12:06 +00003801
cristy3ed852e2009-09-05 21:47:34 +00003802 if (length)
3803 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003804
cristy3ed852e2009-09-05 21:47:34 +00003805 continue;
3806 }
3807
3808 if (memcmp(type,mng_pHYs,4) == 0)
3809 {
3810 if (length > 8)
3811 {
cristy8182b072010-05-30 20:10:53 +00003812 image->x_resolution=(double) mng_get_long(p);
3813 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003814 if ((int) p[8] == PNG_RESOLUTION_METER)
3815 {
3816 image->units=PixelsPerCentimeterResolution;
3817 image->x_resolution=image->x_resolution/100.0f;
3818 image->y_resolution=image->y_resolution/100.0f;
3819 }
3820 }
glennrp0fe50b42010-11-16 03:52:51 +00003821
cristy3ed852e2009-09-05 21:47:34 +00003822 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3823 continue;
3824 }
3825
3826#if 0
3827 if (memcmp(type,mng_iCCP,4) == 0)
3828 {
glennrpfd05d622011-02-25 04:10:33 +00003829 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00003830 if (length)
3831 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003832
cristy3ed852e2009-09-05 21:47:34 +00003833 continue;
3834 }
3835#endif
3836
3837 if (length)
3838 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3839
3840 if (memcmp(type,mng_IEND,4))
3841 continue;
glennrp0fe50b42010-11-16 03:52:51 +00003842
cristy3ed852e2009-09-05 21:47:34 +00003843 break;
3844 }
3845
3846
3847 /* IEND found */
3848
3849 /*
3850 Finish up reading image data:
3851
3852 o read main image from color_blob.
3853
3854 o close color_blob.
3855
3856 o if (color_type has alpha)
3857 if alpha_encoding is PNG
3858 read secondary image from alpha_blob via ReadPNG
3859 if alpha_encoding is JPEG
3860 read secondary image from alpha_blob via ReadJPEG
3861
3862 o close alpha_blob.
3863
3864 o copy intensity of secondary image into
3865 opacity samples of main image.
3866
3867 o destroy the secondary image.
3868 */
3869
3870 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00003871
cristy3ed852e2009-09-05 21:47:34 +00003872 if (logging != MagickFalse)
3873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3874 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003875
cristy3ed852e2009-09-05 21:47:34 +00003876 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3877 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003878
cristy3ed852e2009-09-05 21:47:34 +00003879 color_image_info->ping=MagickFalse; /* To do: avoid this */
3880 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 if (jng_image == (Image *) NULL)
3883 return((Image *) NULL);
3884
3885 (void) RelinquishUniqueFileResource(color_image->filename);
3886 color_image=DestroyImage(color_image);
3887 color_image_info=DestroyImageInfo(color_image_info);
3888
3889 if (jng_image == (Image *) NULL)
3890 return((Image *) NULL);
3891
3892 if (logging != MagickFalse)
3893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3894 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00003895
cristy3ed852e2009-09-05 21:47:34 +00003896 image->rows=jng_height;
3897 image->columns=jng_width;
3898 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00003899
cristybb503372010-05-27 20:51:26 +00003900 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003901 {
3902 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3903 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3904 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00003905
cristy3ed852e2009-09-05 21:47:34 +00003906 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3907 break;
3908 }
glennrp0fe50b42010-11-16 03:52:51 +00003909
cristy3ed852e2009-09-05 21:47:34 +00003910 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00003911
cristy3ed852e2009-09-05 21:47:34 +00003912 if (image_info->ping == MagickFalse)
3913 {
3914 if (jng_color_type >= 12)
3915 {
3916 if (jng_alpha_compression_method == 0)
3917 {
3918 png_byte
3919 data[5];
3920 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3921 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00003922 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00003923 (void) WriteBlob(alpha_image,4,data);
3924 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3925 }
glennrp0fe50b42010-11-16 03:52:51 +00003926
cristy3ed852e2009-09-05 21:47:34 +00003927 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00003928
cristy3ed852e2009-09-05 21:47:34 +00003929 if (logging != MagickFalse)
3930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3931 " Reading opacity from alpha_blob.");
3932
3933 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3934 "%s",alpha_image->filename);
3935
3936 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003939 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003940 {
3941 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3942 &image->exception);
3943 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003944
cristy3ed852e2009-09-05 21:47:34 +00003945 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003946 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
glennrp7c7b3152011-04-26 04:01:27 +00003947 q->opacity=(Quantum) QuantumRange-
3948 GetRedPixelComponent(s);
glennrp0fe50b42010-11-16 03:52:51 +00003949
cristy3ed852e2009-09-05 21:47:34 +00003950 else
cristybb503372010-05-27 20:51:26 +00003951 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003952 {
glennrp7c7b3152011-04-26 04:01:27 +00003953 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
3954 GetRedPixelComponent(s));
3955 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +00003956 image->matte=MagickTrue;
3957 }
glennrp0fe50b42010-11-16 03:52:51 +00003958
cristy3ed852e2009-09-05 21:47:34 +00003959 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3960 break;
3961 }
3962 (void) RelinquishUniqueFileResource(alpha_image->filename);
3963 alpha_image=DestroyImage(alpha_image);
3964 alpha_image_info=DestroyImageInfo(alpha_image_info);
3965 if (jng_image != (Image *) NULL)
3966 jng_image=DestroyImage(jng_image);
3967 }
3968 }
3969
glennrp47b9dd52010-11-24 18:12:06 +00003970 /* Read the JNG image. */
3971
cristy3ed852e2009-09-05 21:47:34 +00003972 if (mng_info->mng_type == 0)
3973 {
3974 mng_info->mng_width=jng_width;
3975 mng_info->mng_height=jng_height;
3976 }
glennrp0fe50b42010-11-16 03:52:51 +00003977
cristy3ed852e2009-09-05 21:47:34 +00003978 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003979 {
3980 image->page.width=jng_width;
3981 image->page.height=jng_height;
3982 }
3983
cristy3ed852e2009-09-05 21:47:34 +00003984 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003985 {
3986 image->page.x=mng_info->x_off[mng_info->object_id];
3987 image->page.y=mng_info->y_off[mng_info->object_id];
3988 }
3989
cristy3ed852e2009-09-05 21:47:34 +00003990 else
glennrp0fe50b42010-11-16 03:52:51 +00003991 {
3992 image->page.y=mng_info->y_off[mng_info->object_id];
3993 }
3994
cristy3ed852e2009-09-05 21:47:34 +00003995 mng_info->image_found++;
3996 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3997 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003998
cristy3ed852e2009-09-05 21:47:34 +00003999 if (logging != MagickFalse)
4000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4001 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004002
cristy3ed852e2009-09-05 21:47:34 +00004003 return(image);
4004}
4005
4006/*
4007%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4008% %
4009% %
4010% %
4011% R e a d J N G I m a g e %
4012% %
4013% %
4014% %
4015%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4016%
4017% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4018% (including the 8-byte signature) and returns it. It allocates the memory
4019% necessary for the new Image structure and returns a pointer to the new
4020% image.
4021%
4022% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4023%
4024% The format of the ReadJNGImage method is:
4025%
4026% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4027% *exception)
4028%
4029% A description of each parameter follows:
4030%
4031% o image_info: the image info.
4032%
4033% o exception: return any errors or warnings in this structure.
4034%
4035*/
4036
4037static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4038{
4039 Image
4040 *image,
4041 *previous;
4042
4043 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004044 have_mng_structure,
4045 logging,
cristy3ed852e2009-09-05 21:47:34 +00004046 status;
4047
4048 MngInfo
4049 *mng_info;
4050
4051 char
4052 magic_number[MaxTextExtent];
4053
cristy3ed852e2009-09-05 21:47:34 +00004054 size_t
4055 count;
4056
4057 /*
4058 Open image file.
4059 */
4060 assert(image_info != (const ImageInfo *) NULL);
4061 assert(image_info->signature == MagickSignature);
4062 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4063 assert(exception != (ExceptionInfo *) NULL);
4064 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004065 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004066 image=AcquireImage(image_info);
4067 mng_info=(MngInfo *) NULL;
4068 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004069
cristy3ed852e2009-09-05 21:47:34 +00004070 if (status == MagickFalse)
4071 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004072
cristy3ed852e2009-09-05 21:47:34 +00004073 if (LocaleCompare(image_info->magick,"JNG") != 0)
4074 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004075
glennrp47b9dd52010-11-24 18:12:06 +00004076 /* Verify JNG signature. */
4077
cristy3ed852e2009-09-05 21:47:34 +00004078 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004079
glennrp3b8763e2011-02-21 12:08:18 +00004080 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004081 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004082
glennrp47b9dd52010-11-24 18:12:06 +00004083 /* Allocate a MngInfo structure. */
4084
cristy3ed852e2009-09-05 21:47:34 +00004085 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004086 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004087
cristy3ed852e2009-09-05 21:47:34 +00004088 if (mng_info == (MngInfo *) NULL)
4089 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004090
glennrp47b9dd52010-11-24 18:12:06 +00004091 /* Initialize members of the MngInfo structure. */
4092
cristy3ed852e2009-09-05 21:47:34 +00004093 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4094 have_mng_structure=MagickTrue;
4095
4096 mng_info->image=image;
4097 previous=image;
4098 image=ReadOneJNGImage(mng_info,image_info,exception);
4099 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004100
cristy3ed852e2009-09-05 21:47:34 +00004101 if (image == (Image *) NULL)
4102 {
4103 if (IsImageObject(previous) != MagickFalse)
4104 {
4105 (void) CloseBlob(previous);
4106 (void) DestroyImageList(previous);
4107 }
glennrp0fe50b42010-11-16 03:52:51 +00004108
cristy3ed852e2009-09-05 21:47:34 +00004109 if (logging != MagickFalse)
4110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4111 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004112
cristy3ed852e2009-09-05 21:47:34 +00004113 return((Image *) NULL);
4114 }
4115 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004116
cristy3ed852e2009-09-05 21:47:34 +00004117 if (image->columns == 0 || image->rows == 0)
4118 {
4119 if (logging != MagickFalse)
4120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4121 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004122
cristy3ed852e2009-09-05 21:47:34 +00004123 ThrowReaderException(CorruptImageError,"CorruptImage");
4124 }
glennrp0fe50b42010-11-16 03:52:51 +00004125
cristy3ed852e2009-09-05 21:47:34 +00004126 if (logging != MagickFalse)
4127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004128
cristy3ed852e2009-09-05 21:47:34 +00004129 return(image);
4130}
4131#endif
4132
4133static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4134{
4135 char
4136 page_geometry[MaxTextExtent];
4137
4138 Image
4139 *image,
4140 *previous;
4141
cristy4383ec82011-01-05 15:42:32 +00004142 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004143 logging,
4144 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004145
cristy3ed852e2009-09-05 21:47:34 +00004146 volatile int
4147 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004148 object_id,
4149 term_chunk_found,
4150 skip_to_iend;
4151
cristybb503372010-05-27 20:51:26 +00004152 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004153 image_count=0;
4154
4155 MagickBooleanType
4156 status;
4157
4158 MagickOffsetType
4159 offset;
4160
4161 MngInfo
4162 *mng_info;
4163
4164 MngBox
4165 default_fb,
4166 fb,
4167 previous_fb;
4168
4169#if defined(MNG_INSERT_LAYERS)
4170 PixelPacket
4171 mng_background_color;
4172#endif
4173
4174 register unsigned char
4175 *p;
4176
cristybb503372010-05-27 20:51:26 +00004177 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004178 i;
4179
4180 size_t
4181 count;
4182
cristybb503372010-05-27 20:51:26 +00004183 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004184 loop_level;
4185
4186 volatile short
4187 skipping_loop;
4188
4189#if defined(MNG_INSERT_LAYERS)
4190 unsigned int
4191 mandatory_back=0;
4192#endif
4193
4194 volatile unsigned int
4195#ifdef MNG_OBJECT_BUFFERS
4196 mng_background_object=0,
4197#endif
4198 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4199
cristybb503372010-05-27 20:51:26 +00004200 size_t
cristy3ed852e2009-09-05 21:47:34 +00004201 default_frame_timeout,
4202 frame_timeout,
4203#if defined(MNG_INSERT_LAYERS)
4204 image_height,
4205 image_width,
4206#endif
4207 length;
4208
glennrp38ea0832010-06-02 18:50:28 +00004209 /* These delays are all measured in image ticks_per_second,
4210 * not in MNG ticks_per_second
4211 */
cristybb503372010-05-27 20:51:26 +00004212 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004213 default_frame_delay,
4214 final_delay,
4215 final_image_delay,
4216 frame_delay,
4217#if defined(MNG_INSERT_LAYERS)
4218 insert_layers,
4219#endif
4220 mng_iterations=1,
4221 simplicity=0,
4222 subframe_height=0,
4223 subframe_width=0;
4224
4225 previous_fb.top=0;
4226 previous_fb.bottom=0;
4227 previous_fb.left=0;
4228 previous_fb.right=0;
4229 default_fb.top=0;
4230 default_fb.bottom=0;
4231 default_fb.left=0;
4232 default_fb.right=0;
4233
glennrp47b9dd52010-11-24 18:12:06 +00004234 /* Open image file. */
4235
cristy3ed852e2009-09-05 21:47:34 +00004236 assert(image_info != (const ImageInfo *) NULL);
4237 assert(image_info->signature == MagickSignature);
4238 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4239 assert(exception != (ExceptionInfo *) NULL);
4240 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004241 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004242 image=AcquireImage(image_info);
4243 mng_info=(MngInfo *) NULL;
4244 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004245
cristy3ed852e2009-09-05 21:47:34 +00004246 if (status == MagickFalse)
4247 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004248
cristy3ed852e2009-09-05 21:47:34 +00004249 first_mng_object=MagickFalse;
4250 skipping_loop=(-1);
4251 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004252
4253 /* Allocate a MngInfo structure. */
4254
cristy73bd4a52010-10-05 11:24:23 +00004255 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004256
cristy3ed852e2009-09-05 21:47:34 +00004257 if (mng_info == (MngInfo *) NULL)
4258 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004259
glennrp47b9dd52010-11-24 18:12:06 +00004260 /* Initialize members of the MngInfo structure. */
4261
cristy3ed852e2009-09-05 21:47:34 +00004262 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4263 mng_info->image=image;
4264 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004265
4266 if (LocaleCompare(image_info->magick,"MNG") == 0)
4267 {
4268 char
4269 magic_number[MaxTextExtent];
4270
glennrp47b9dd52010-11-24 18:12:06 +00004271 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004272 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4273 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4274 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004275
4276 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004277 for (i=0; i < MNG_MAX_OBJECTS; i++)
4278 {
cristybb503372010-05-27 20:51:26 +00004279 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4280 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004281 }
4282 mng_info->exists[0]=MagickTrue;
4283 }
glennrp47b9dd52010-11-24 18:12:06 +00004284
cristy3ed852e2009-09-05 21:47:34 +00004285 first_mng_object=MagickTrue;
4286 mng_type=0;
4287#if defined(MNG_INSERT_LAYERS)
4288 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4289#endif
4290 default_frame_delay=0;
4291 default_frame_timeout=0;
4292 frame_delay=0;
4293 final_delay=1;
4294 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4295 object_id=0;
4296 skip_to_iend=MagickFalse;
4297 term_chunk_found=MagickFalse;
4298 mng_info->framing_mode=1;
4299#if defined(MNG_INSERT_LAYERS)
4300 mandatory_back=MagickFalse;
4301#endif
4302#if defined(MNG_INSERT_LAYERS)
4303 mng_background_color=image->background_color;
4304#endif
4305 default_fb=mng_info->frame;
4306 previous_fb=mng_info->frame;
4307 do
4308 {
4309 char
4310 type[MaxTextExtent];
4311
4312 if (LocaleCompare(image_info->magick,"MNG") == 0)
4313 {
4314 unsigned char
4315 *chunk;
4316
4317 /*
4318 Read a new chunk.
4319 */
4320 type[0]='\0';
4321 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4322 length=ReadBlobMSBLong(image);
4323 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4324
4325 if (logging != MagickFalse)
4326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004327 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4328 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004329
4330 if (length > PNG_UINT_31_MAX)
4331 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004332
cristy3ed852e2009-09-05 21:47:34 +00004333 if (count == 0)
4334 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004335
cristy3ed852e2009-09-05 21:47:34 +00004336 p=NULL;
4337 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004338
cristy3ed852e2009-09-05 21:47:34 +00004339 if (length)
4340 {
4341 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004342
cristy3ed852e2009-09-05 21:47:34 +00004343 if (chunk == (unsigned char *) NULL)
4344 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004345
cristybb503372010-05-27 20:51:26 +00004346 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004347 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004348
cristy3ed852e2009-09-05 21:47:34 +00004349 p=chunk;
4350 }
glennrp0fe50b42010-11-16 03:52:51 +00004351
cristy3ed852e2009-09-05 21:47:34 +00004352 (void) ReadBlobMSBLong(image); /* read crc word */
4353
4354#if !defined(JNG_SUPPORTED)
4355 if (memcmp(type,mng_JHDR,4) == 0)
4356 {
4357 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004358
cristy3ed852e2009-09-05 21:47:34 +00004359 if (mng_info->jhdr_warning == 0)
4360 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4361 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004362
cristy3ed852e2009-09-05 21:47:34 +00004363 mng_info->jhdr_warning++;
4364 }
4365#endif
4366 if (memcmp(type,mng_DHDR,4) == 0)
4367 {
4368 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004369
cristy3ed852e2009-09-05 21:47:34 +00004370 if (mng_info->dhdr_warning == 0)
4371 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4372 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004373
cristy3ed852e2009-09-05 21:47:34 +00004374 mng_info->dhdr_warning++;
4375 }
4376 if (memcmp(type,mng_MEND,4) == 0)
4377 break;
glennrp47b9dd52010-11-24 18:12:06 +00004378
cristy3ed852e2009-09-05 21:47:34 +00004379 if (skip_to_iend)
4380 {
4381 if (memcmp(type,mng_IEND,4) == 0)
4382 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004383
cristy3ed852e2009-09-05 21:47:34 +00004384 if (length)
4385 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004386
cristy3ed852e2009-09-05 21:47:34 +00004387 if (logging != MagickFalse)
4388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4389 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004390
cristy3ed852e2009-09-05 21:47:34 +00004391 continue;
4392 }
glennrp0fe50b42010-11-16 03:52:51 +00004393
cristy3ed852e2009-09-05 21:47:34 +00004394 if (memcmp(type,mng_MHDR,4) == 0)
4395 {
cristybb503372010-05-27 20:51:26 +00004396 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004397 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004398
cristybb503372010-05-27 20:51:26 +00004399 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004400 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004401
cristy3ed852e2009-09-05 21:47:34 +00004402 if (logging != MagickFalse)
4403 {
4404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004405 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004407 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004408 }
glennrp0fe50b42010-11-16 03:52:51 +00004409
cristy3ed852e2009-09-05 21:47:34 +00004410 p+=8;
cristy8182b072010-05-30 20:10:53 +00004411 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004412
cristy3ed852e2009-09-05 21:47:34 +00004413 if (mng_info->ticks_per_second == 0)
4414 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004415
cristy3ed852e2009-09-05 21:47:34 +00004416 else
4417 default_frame_delay=1UL*image->ticks_per_second/
4418 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004419
cristy3ed852e2009-09-05 21:47:34 +00004420 frame_delay=default_frame_delay;
4421 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004422
cristy3ed852e2009-09-05 21:47:34 +00004423 if (length > 16)
4424 {
4425 p+=16;
cristy8182b072010-05-30 20:10:53 +00004426 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004427 }
glennrp0fe50b42010-11-16 03:52:51 +00004428
cristy3ed852e2009-09-05 21:47:34 +00004429 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004430
cristy3ed852e2009-09-05 21:47:34 +00004431 if ((simplicity != 0) && ((simplicity | 11) == 11))
4432 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004433
cristy3ed852e2009-09-05 21:47:34 +00004434 if ((simplicity != 0) && ((simplicity | 9) == 9))
4435 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004436
cristy3ed852e2009-09-05 21:47:34 +00004437#if defined(MNG_INSERT_LAYERS)
4438 if (mng_type != 3)
4439 insert_layers=MagickTrue;
4440#endif
4441 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4442 {
glennrp47b9dd52010-11-24 18:12:06 +00004443 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004444 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004445
cristy3ed852e2009-09-05 21:47:34 +00004446 if (GetNextImageInList(image) == (Image *) NULL)
4447 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004448
cristy3ed852e2009-09-05 21:47:34 +00004449 image=SyncNextImageInList(image);
4450 mng_info->image=image;
4451 }
4452
4453 if ((mng_info->mng_width > 65535L) ||
4454 (mng_info->mng_height > 65535L))
4455 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004456
cristye8c25f92010-06-03 00:53:06 +00004457 (void) FormatMagickString(page_geometry,MaxTextExtent,
4458 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004459 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004460
cristy3ed852e2009-09-05 21:47:34 +00004461 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004462 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004463 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004464 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004465 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004466
cristy3ed852e2009-09-05 21:47:34 +00004467 for (i=0; i < MNG_MAX_OBJECTS; i++)
4468 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004469
cristy3ed852e2009-09-05 21:47:34 +00004470 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4471 continue;
4472 }
4473
4474 if (memcmp(type,mng_TERM,4) == 0)
4475 {
4476 int
4477 repeat=0;
4478
4479
4480 if (length)
4481 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004482
cristy3ed852e2009-09-05 21:47:34 +00004483 if (repeat == 3)
4484 {
cristy8182b072010-05-30 20:10:53 +00004485 final_delay=(png_uint_32) mng_get_long(&p[2]);
4486 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004487
cristy3ed852e2009-09-05 21:47:34 +00004488 if (mng_iterations == PNG_UINT_31_MAX)
4489 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004490
cristy3ed852e2009-09-05 21:47:34 +00004491 image->iterations=mng_iterations;
4492 term_chunk_found=MagickTrue;
4493 }
glennrp0fe50b42010-11-16 03:52:51 +00004494
cristy3ed852e2009-09-05 21:47:34 +00004495 if (logging != MagickFalse)
4496 {
4497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4498 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004499
cristy3ed852e2009-09-05 21:47:34 +00004500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004501 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004502
cristy3ed852e2009-09-05 21:47:34 +00004503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004504 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004505 }
glennrp0fe50b42010-11-16 03:52:51 +00004506
cristy3ed852e2009-09-05 21:47:34 +00004507 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4508 continue;
4509 }
4510 if (memcmp(type,mng_DEFI,4) == 0)
4511 {
4512 if (mng_type == 3)
4513 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4514 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4515 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004516
cristy3ed852e2009-09-05 21:47:34 +00004517 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004518
cristy3ed852e2009-09-05 21:47:34 +00004519 if (mng_type == 2 && object_id != 0)
4520 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4521 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4522 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004523
cristy3ed852e2009-09-05 21:47:34 +00004524 if (object_id > MNG_MAX_OBJECTS)
4525 {
4526 /*
4527 Instead ofsuing a warning we should allocate a larger
4528 MngInfo structure and continue.
4529 */
4530 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4531 CoderError,"object id too large","`%s'",image->filename);
4532 object_id=MNG_MAX_OBJECTS;
4533 }
glennrp0fe50b42010-11-16 03:52:51 +00004534
cristy3ed852e2009-09-05 21:47:34 +00004535 if (mng_info->exists[object_id])
4536 if (mng_info->frozen[object_id])
4537 {
4538 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4539 (void) ThrowMagickException(&image->exception,
4540 GetMagickModule(),CoderError,
4541 "DEFI cannot redefine a frozen MNG object","`%s'",
4542 image->filename);
4543 continue;
4544 }
glennrp0fe50b42010-11-16 03:52:51 +00004545
cristy3ed852e2009-09-05 21:47:34 +00004546 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 if (length > 2)
4549 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004550
cristy3ed852e2009-09-05 21:47:34 +00004551 /*
4552 Extract object offset info.
4553 */
4554 if (length > 11)
4555 {
glennrp0fe50b42010-11-16 03:52:51 +00004556 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4557 (p[5] << 16) | (p[6] << 8) | p[7]);
4558
4559 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4560 (p[9] << 16) | (p[10] << 8) | p[11]);
4561
cristy3ed852e2009-09-05 21:47:34 +00004562 if (logging != MagickFalse)
4563 {
4564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004565 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004566 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004567
cristy3ed852e2009-09-05 21:47:34 +00004568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004569 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004570 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004571 }
4572 }
glennrp0fe50b42010-11-16 03:52:51 +00004573
cristy3ed852e2009-09-05 21:47:34 +00004574 /*
4575 Extract object clipping info.
4576 */
4577 if (length > 27)
4578 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4579 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4582 continue;
4583 }
4584 if (memcmp(type,mng_bKGD,4) == 0)
4585 {
4586 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004587
cristy3ed852e2009-09-05 21:47:34 +00004588 if (length > 5)
4589 {
4590 mng_info->mng_global_bkgd.red=
4591 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004592
cristy3ed852e2009-09-05 21:47:34 +00004593 mng_info->mng_global_bkgd.green=
4594 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004595
cristy3ed852e2009-09-05 21:47:34 +00004596 mng_info->mng_global_bkgd.blue=
4597 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004598
cristy3ed852e2009-09-05 21:47:34 +00004599 mng_info->have_global_bkgd=MagickTrue;
4600 }
glennrp0fe50b42010-11-16 03:52:51 +00004601
cristy3ed852e2009-09-05 21:47:34 +00004602 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4603 continue;
4604 }
4605 if (memcmp(type,mng_BACK,4) == 0)
4606 {
4607#if defined(MNG_INSERT_LAYERS)
4608 if (length > 6)
4609 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00004610
cristy3ed852e2009-09-05 21:47:34 +00004611 else
4612 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00004613
cristy3ed852e2009-09-05 21:47:34 +00004614 if (mandatory_back && length > 5)
4615 {
4616 mng_background_color.red=
4617 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004618
cristy3ed852e2009-09-05 21:47:34 +00004619 mng_background_color.green=
4620 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004621
cristy3ed852e2009-09-05 21:47:34 +00004622 mng_background_color.blue=
4623 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004624
cristy3ed852e2009-09-05 21:47:34 +00004625 mng_background_color.opacity=OpaqueOpacity;
4626 }
glennrp0fe50b42010-11-16 03:52:51 +00004627
cristy3ed852e2009-09-05 21:47:34 +00004628#ifdef MNG_OBJECT_BUFFERS
4629 if (length > 8)
4630 mng_background_object=(p[7] << 8) | p[8];
4631#endif
4632#endif
4633 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4634 continue;
4635 }
glennrp47b9dd52010-11-24 18:12:06 +00004636
cristy3ed852e2009-09-05 21:47:34 +00004637 if (memcmp(type,mng_PLTE,4) == 0)
4638 {
glennrp47b9dd52010-11-24 18:12:06 +00004639 /* Read global PLTE. */
4640
cristy3ed852e2009-09-05 21:47:34 +00004641 if (length && (length < 769))
4642 {
4643 if (mng_info->global_plte == (png_colorp) NULL)
4644 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4645 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00004646
cristybb503372010-05-27 20:51:26 +00004647 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004648 {
4649 mng_info->global_plte[i].red=p[3*i];
4650 mng_info->global_plte[i].green=p[3*i+1];
4651 mng_info->global_plte[i].blue=p[3*i+2];
4652 }
glennrp0fe50b42010-11-16 03:52:51 +00004653
cristy35ef8242010-06-03 16:24:13 +00004654 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00004655 }
4656#ifdef MNG_LOOSE
4657 for ( ; i < 256; i++)
4658 {
4659 mng_info->global_plte[i].red=i;
4660 mng_info->global_plte[i].green=i;
4661 mng_info->global_plte[i].blue=i;
4662 }
glennrp0fe50b42010-11-16 03:52:51 +00004663
cristy3ed852e2009-09-05 21:47:34 +00004664 if (length)
4665 mng_info->global_plte_length=256;
4666#endif
4667 else
4668 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00004669
cristy3ed852e2009-09-05 21:47:34 +00004670 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4671 continue;
4672 }
glennrp47b9dd52010-11-24 18:12:06 +00004673
cristy3ed852e2009-09-05 21:47:34 +00004674 if (memcmp(type,mng_tRNS,4) == 0)
4675 {
4676 /* read global tRNS */
4677
4678 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004679 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004680 mng_info->global_trns[i]=p[i];
4681
4682#ifdef MNG_LOOSE
4683 for ( ; i < 256; i++)
4684 mng_info->global_trns[i]=255;
4685#endif
cristy12560f32010-06-03 16:51:08 +00004686 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00004687 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4688 continue;
4689 }
4690 if (memcmp(type,mng_gAMA,4) == 0)
4691 {
4692 if (length == 4)
4693 {
cristybb503372010-05-27 20:51:26 +00004694 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004695 igamma;
4696
cristy8182b072010-05-30 20:10:53 +00004697 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004698 mng_info->global_gamma=((float) igamma)*0.00001;
4699 mng_info->have_global_gama=MagickTrue;
4700 }
glennrp0fe50b42010-11-16 03:52:51 +00004701
cristy3ed852e2009-09-05 21:47:34 +00004702 else
4703 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004704
cristy3ed852e2009-09-05 21:47:34 +00004705 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4706 continue;
4707 }
4708
4709 if (memcmp(type,mng_cHRM,4) == 0)
4710 {
glennrp47b9dd52010-11-24 18:12:06 +00004711 /* Read global cHRM */
4712
cristy3ed852e2009-09-05 21:47:34 +00004713 if (length == 32)
4714 {
cristy8182b072010-05-30 20:10:53 +00004715 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4716 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4717 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004718 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004719 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004720 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004721 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004722 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004723 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004724 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004725 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004726 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004727 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004728 mng_info->have_global_chrm=MagickTrue;
4729 }
4730 else
4731 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004732
cristy3ed852e2009-09-05 21:47:34 +00004733 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4734 continue;
4735 }
glennrp47b9dd52010-11-24 18:12:06 +00004736
cristy3ed852e2009-09-05 21:47:34 +00004737 if (memcmp(type,mng_sRGB,4) == 0)
4738 {
4739 /*
4740 Read global sRGB.
4741 */
4742 if (length)
4743 {
glennrpe610a072010-08-05 17:08:46 +00004744 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00004745 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004746 mng_info->have_global_srgb=MagickTrue;
4747 }
4748 else
4749 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004750
cristy3ed852e2009-09-05 21:47:34 +00004751 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4752 continue;
4753 }
glennrp47b9dd52010-11-24 18:12:06 +00004754
cristy3ed852e2009-09-05 21:47:34 +00004755 if (memcmp(type,mng_iCCP,4) == 0)
4756 {
glennrpfd05d622011-02-25 04:10:33 +00004757 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004758
4759 /*
4760 Read global iCCP.
4761 */
4762 if (length)
4763 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004764
cristy3ed852e2009-09-05 21:47:34 +00004765 continue;
4766 }
glennrp47b9dd52010-11-24 18:12:06 +00004767
cristy3ed852e2009-09-05 21:47:34 +00004768 if (memcmp(type,mng_FRAM,4) == 0)
4769 {
4770 if (mng_type == 3)
4771 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4772 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4773 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004774
cristy3ed852e2009-09-05 21:47:34 +00004775 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4776 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004777
cristy3ed852e2009-09-05 21:47:34 +00004778 frame_delay=default_frame_delay;
4779 frame_timeout=default_frame_timeout;
4780 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00004781
cristy3ed852e2009-09-05 21:47:34 +00004782 if (length)
4783 if (p[0])
4784 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004785
cristy3ed852e2009-09-05 21:47:34 +00004786 if (logging != MagickFalse)
4787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4788 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00004789
cristy3ed852e2009-09-05 21:47:34 +00004790 if (length > 6)
4791 {
glennrp47b9dd52010-11-24 18:12:06 +00004792 /* Note the delay and frame clipping boundaries. */
4793
cristy3ed852e2009-09-05 21:47:34 +00004794 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00004795
cristybb503372010-05-27 20:51:26 +00004796 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004797 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00004798
cristy3ed852e2009-09-05 21:47:34 +00004799 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00004800
cristybb503372010-05-27 20:51:26 +00004801 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004802 {
4803 int
4804 change_delay,
4805 change_timeout,
4806 change_clipping;
4807
4808 change_delay=(*p++);
4809 change_timeout=(*p++);
4810 change_clipping=(*p++);
4811 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00004812
cristy3ed852e2009-09-05 21:47:34 +00004813 if (change_delay)
4814 {
cristy8182b072010-05-30 20:10:53 +00004815 frame_delay=1UL*image->ticks_per_second*
4816 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004817
cristy8182b072010-05-30 20:10:53 +00004818 if (mng_info->ticks_per_second != 0)
4819 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004820
glennrpbb010dd2010-06-01 13:07:15 +00004821 else
4822 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004823
cristy3ed852e2009-09-05 21:47:34 +00004824 if (change_delay == 2)
4825 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 if (logging != MagickFalse)
4830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004831 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004832 }
glennrp47b9dd52010-11-24 18:12:06 +00004833
cristy3ed852e2009-09-05 21:47:34 +00004834 if (change_timeout)
4835 {
glennrpbb010dd2010-06-01 13:07:15 +00004836 frame_timeout=1UL*image->ticks_per_second*
4837 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004838
glennrpbb010dd2010-06-01 13:07:15 +00004839 if (mng_info->ticks_per_second != 0)
4840 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004841
glennrpbb010dd2010-06-01 13:07:15 +00004842 else
4843 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 if (change_delay == 2)
4846 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004849
cristy3ed852e2009-09-05 21:47:34 +00004850 if (logging != MagickFalse)
4851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004852 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004853 }
glennrp47b9dd52010-11-24 18:12:06 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 if (change_clipping)
4856 {
4857 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4858 p+=17;
4859 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00004860
cristy3ed852e2009-09-05 21:47:34 +00004861 if (logging != MagickFalse)
4862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004863 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004864 (double) fb.left,(double) fb.right,(double) fb.top,
4865 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00004866
cristy3ed852e2009-09-05 21:47:34 +00004867 if (change_clipping == 2)
4868 default_fb=fb;
4869 }
4870 }
4871 }
4872 mng_info->clip=fb;
4873 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00004874
cristybb503372010-05-27 20:51:26 +00004875 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004876 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristybb503372010-05-27 20:51:26 +00004878 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004879 -mng_info->clip.top);
4880 /*
4881 Insert a background layer behind the frame if framing_mode is 4.
4882 */
4883#if defined(MNG_INSERT_LAYERS)
4884 if (logging != MagickFalse)
4885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004886 " subframe_width=%.20g, subframe_height=%.20g",(double)
4887 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00004888
cristy3ed852e2009-09-05 21:47:34 +00004889 if (insert_layers && (mng_info->framing_mode == 4) &&
4890 (subframe_width) && (subframe_height))
4891 {
glennrp47b9dd52010-11-24 18:12:06 +00004892 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004893 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4894 {
4895 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 if (GetNextImageInList(image) == (Image *) NULL)
4898 {
4899 image=DestroyImageList(image);
4900 MngInfoFreeStruct(mng_info,&have_mng_structure);
4901 return((Image *) NULL);
4902 }
glennrp47b9dd52010-11-24 18:12:06 +00004903
cristy3ed852e2009-09-05 21:47:34 +00004904 image=SyncNextImageInList(image);
4905 }
glennrp0fe50b42010-11-16 03:52:51 +00004906
cristy3ed852e2009-09-05 21:47:34 +00004907 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00004908
cristy3ed852e2009-09-05 21:47:34 +00004909 if (term_chunk_found)
4910 {
4911 image->start_loop=MagickTrue;
4912 image->iterations=mng_iterations;
4913 term_chunk_found=MagickFalse;
4914 }
glennrp0fe50b42010-11-16 03:52:51 +00004915
cristy3ed852e2009-09-05 21:47:34 +00004916 else
4917 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004918
cristy3ed852e2009-09-05 21:47:34 +00004919 image->columns=subframe_width;
4920 image->rows=subframe_height;
4921 image->page.width=subframe_width;
4922 image->page.height=subframe_height;
4923 image->page.x=mng_info->clip.left;
4924 image->page.y=mng_info->clip.top;
4925 image->background_color=mng_background_color;
4926 image->matte=MagickFalse;
4927 image->delay=0;
4928 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00004929
cristy3ed852e2009-09-05 21:47:34 +00004930 if (logging != MagickFalse)
4931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004932 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004933 (double) mng_info->clip.left,(double) mng_info->clip.right,
4934 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004935 }
4936#endif
4937 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4938 continue;
4939 }
4940 if (memcmp(type,mng_CLIP,4) == 0)
4941 {
4942 unsigned int
4943 first_object,
4944 last_object;
4945
4946 /*
4947 Read CLIP.
4948 */
4949 first_object=(p[0] << 8) | p[1];
4950 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00004951
cristy3ed852e2009-09-05 21:47:34 +00004952 for (i=(int) first_object; i <= (int) last_object; i++)
4953 {
4954 if (mng_info->exists[i] && !mng_info->frozen[i])
4955 {
4956 MngBox
4957 box;
4958
4959 box=mng_info->object_clip[i];
4960 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4961 }
4962 }
glennrp47b9dd52010-11-24 18:12:06 +00004963
cristy3ed852e2009-09-05 21:47:34 +00004964 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4965 continue;
4966 }
4967 if (memcmp(type,mng_SAVE,4) == 0)
4968 {
4969 for (i=1; i < MNG_MAX_OBJECTS; i++)
4970 if (mng_info->exists[i])
4971 {
4972 mng_info->frozen[i]=MagickTrue;
4973#ifdef MNG_OBJECT_BUFFERS
4974 if (mng_info->ob[i] != (MngBuffer *) NULL)
4975 mng_info->ob[i]->frozen=MagickTrue;
4976#endif
4977 }
glennrp0fe50b42010-11-16 03:52:51 +00004978
cristy3ed852e2009-09-05 21:47:34 +00004979 if (length)
4980 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 continue;
4983 }
4984
4985 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4986 {
glennrp47b9dd52010-11-24 18:12:06 +00004987 /* Read DISC or SEEK. */
4988
cristy3ed852e2009-09-05 21:47:34 +00004989 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4990 {
4991 for (i=1; i < MNG_MAX_OBJECTS; i++)
4992 MngInfoDiscardObject(mng_info,i);
4993 }
glennrp0fe50b42010-11-16 03:52:51 +00004994
cristy3ed852e2009-09-05 21:47:34 +00004995 else
4996 {
cristybb503372010-05-27 20:51:26 +00004997 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004998 j;
4999
cristybb503372010-05-27 20:51:26 +00005000 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005001 {
5002 i=p[j] << 8 | p[j+1];
5003 MngInfoDiscardObject(mng_info,i);
5004 }
5005 }
glennrp0fe50b42010-11-16 03:52:51 +00005006
cristy3ed852e2009-09-05 21:47:34 +00005007 if (length)
5008 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005009
cristy3ed852e2009-09-05 21:47:34 +00005010 continue;
5011 }
glennrp47b9dd52010-11-24 18:12:06 +00005012
cristy3ed852e2009-09-05 21:47:34 +00005013 if (memcmp(type,mng_MOVE,4) == 0)
5014 {
cristybb503372010-05-27 20:51:26 +00005015 size_t
cristy3ed852e2009-09-05 21:47:34 +00005016 first_object,
5017 last_object;
5018
glennrp47b9dd52010-11-24 18:12:06 +00005019 /* read MOVE */
5020
cristy3ed852e2009-09-05 21:47:34 +00005021 first_object=(p[0] << 8) | p[1];
5022 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005023 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005024 {
5025 if (mng_info->exists[i] && !mng_info->frozen[i])
5026 {
5027 MngPair
5028 new_pair;
5029
5030 MngPair
5031 old_pair;
5032
5033 old_pair.a=mng_info->x_off[i];
5034 old_pair.b=mng_info->y_off[i];
5035 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5036 mng_info->x_off[i]=new_pair.a;
5037 mng_info->y_off[i]=new_pair.b;
5038 }
5039 }
glennrp47b9dd52010-11-24 18:12:06 +00005040
cristy3ed852e2009-09-05 21:47:34 +00005041 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5042 continue;
5043 }
5044
5045 if (memcmp(type,mng_LOOP,4) == 0)
5046 {
cristybb503372010-05-27 20:51:26 +00005047 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005048 loop_level=chunk[0];
5049 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005050
5051 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005052 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005053
cristy3ed852e2009-09-05 21:47:34 +00005054 if (logging != MagickFalse)
5055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005056 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5057 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005058
cristy3ed852e2009-09-05 21:47:34 +00005059 if (loop_iters == 0)
5060 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005061
cristy3ed852e2009-09-05 21:47:34 +00005062 else
5063 {
5064 mng_info->loop_jump[loop_level]=TellBlob(image);
5065 mng_info->loop_count[loop_level]=loop_iters;
5066 }
glennrp0fe50b42010-11-16 03:52:51 +00005067
cristy3ed852e2009-09-05 21:47:34 +00005068 mng_info->loop_iteration[loop_level]=0;
5069 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5070 continue;
5071 }
glennrp47b9dd52010-11-24 18:12:06 +00005072
cristy3ed852e2009-09-05 21:47:34 +00005073 if (memcmp(type,mng_ENDL,4) == 0)
5074 {
5075 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005076
cristy3ed852e2009-09-05 21:47:34 +00005077 if (skipping_loop > 0)
5078 {
5079 if (skipping_loop == loop_level)
5080 {
5081 /*
5082 Found end of zero-iteration loop.
5083 */
5084 skipping_loop=(-1);
5085 mng_info->loop_active[loop_level]=0;
5086 }
5087 }
glennrp47b9dd52010-11-24 18:12:06 +00005088
cristy3ed852e2009-09-05 21:47:34 +00005089 else
5090 {
5091 if (mng_info->loop_active[loop_level] == 1)
5092 {
5093 mng_info->loop_count[loop_level]--;
5094 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005095
cristy3ed852e2009-09-05 21:47:34 +00005096 if (logging != MagickFalse)
5097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005098 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005099 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005100 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005101
cristy3ed852e2009-09-05 21:47:34 +00005102 if (mng_info->loop_count[loop_level] != 0)
5103 {
5104 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5105 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005106
cristy3ed852e2009-09-05 21:47:34 +00005107 if (offset < 0)
5108 ThrowReaderException(CorruptImageError,
5109 "ImproperImageHeader");
5110 }
glennrp47b9dd52010-11-24 18:12:06 +00005111
cristy3ed852e2009-09-05 21:47:34 +00005112 else
5113 {
5114 short
5115 last_level;
5116
5117 /*
5118 Finished loop.
5119 */
5120 mng_info->loop_active[loop_level]=0;
5121 last_level=(-1);
5122 for (i=0; i < loop_level; i++)
5123 if (mng_info->loop_active[i] == 1)
5124 last_level=(short) i;
5125 loop_level=last_level;
5126 }
5127 }
5128 }
glennrp47b9dd52010-11-24 18:12:06 +00005129
cristy3ed852e2009-09-05 21:47:34 +00005130 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5131 continue;
5132 }
glennrp47b9dd52010-11-24 18:12:06 +00005133
cristy3ed852e2009-09-05 21:47:34 +00005134 if (memcmp(type,mng_CLON,4) == 0)
5135 {
5136 if (mng_info->clon_warning == 0)
5137 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5138 CoderError,"CLON is not implemented yet","`%s'",
5139 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005140
cristy3ed852e2009-09-05 21:47:34 +00005141 mng_info->clon_warning++;
5142 }
glennrp47b9dd52010-11-24 18:12:06 +00005143
cristy3ed852e2009-09-05 21:47:34 +00005144 if (memcmp(type,mng_MAGN,4) == 0)
5145 {
5146 png_uint_16
5147 magn_first,
5148 magn_last,
5149 magn_mb,
5150 magn_ml,
5151 magn_mr,
5152 magn_mt,
5153 magn_mx,
5154 magn_my,
5155 magn_methx,
5156 magn_methy;
5157
5158 if (length > 1)
5159 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005160
cristy3ed852e2009-09-05 21:47:34 +00005161 else
5162 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005163
cristy3ed852e2009-09-05 21:47:34 +00005164 if (length > 3)
5165 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005166
cristy3ed852e2009-09-05 21:47:34 +00005167 else
5168 magn_last=magn_first;
5169#ifndef MNG_OBJECT_BUFFERS
5170 if (magn_first || magn_last)
5171 if (mng_info->magn_warning == 0)
5172 {
5173 (void) ThrowMagickException(&image->exception,
5174 GetMagickModule(),CoderError,
5175 "MAGN is not implemented yet for nonzero objects",
5176 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005177
cristy3ed852e2009-09-05 21:47:34 +00005178 mng_info->magn_warning++;
5179 }
5180#endif
5181 if (length > 4)
5182 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005183
cristy3ed852e2009-09-05 21:47:34 +00005184 else
5185 magn_methx=0;
5186
5187 if (length > 6)
5188 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005189
cristy3ed852e2009-09-05 21:47:34 +00005190 else
5191 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005192
cristy3ed852e2009-09-05 21:47:34 +00005193 if (magn_mx == 0)
5194 magn_mx=1;
5195
5196 if (length > 8)
5197 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005198
cristy3ed852e2009-09-05 21:47:34 +00005199 else
5200 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005201
cristy3ed852e2009-09-05 21:47:34 +00005202 if (magn_my == 0)
5203 magn_my=1;
5204
5205 if (length > 10)
5206 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005207
cristy3ed852e2009-09-05 21:47:34 +00005208 else
5209 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005210
cristy3ed852e2009-09-05 21:47:34 +00005211 if (magn_ml == 0)
5212 magn_ml=1;
5213
5214 if (length > 12)
5215 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005216
cristy3ed852e2009-09-05 21:47:34 +00005217 else
5218 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005219
cristy3ed852e2009-09-05 21:47:34 +00005220 if (magn_mr == 0)
5221 magn_mr=1;
5222
5223 if (length > 14)
5224 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005225
cristy3ed852e2009-09-05 21:47:34 +00005226 else
5227 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005228
cristy3ed852e2009-09-05 21:47:34 +00005229 if (magn_mt == 0)
5230 magn_mt=1;
5231
5232 if (length > 16)
5233 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005234
cristy3ed852e2009-09-05 21:47:34 +00005235 else
5236 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005237
cristy3ed852e2009-09-05 21:47:34 +00005238 if (magn_mb == 0)
5239 magn_mb=1;
5240
5241 if (length > 17)
5242 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005243
cristy3ed852e2009-09-05 21:47:34 +00005244 else
5245 magn_methy=magn_methx;
5246
glennrp47b9dd52010-11-24 18:12:06 +00005247
cristy3ed852e2009-09-05 21:47:34 +00005248 if (magn_methx > 5 || magn_methy > 5)
5249 if (mng_info->magn_warning == 0)
5250 {
5251 (void) ThrowMagickException(&image->exception,
5252 GetMagickModule(),CoderError,
5253 "Unknown MAGN method in MNG datastream","`%s'",
5254 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005255
cristy3ed852e2009-09-05 21:47:34 +00005256 mng_info->magn_warning++;
5257 }
5258#ifdef MNG_OBJECT_BUFFERS
5259 /* Magnify existing objects in the range magn_first to magn_last */
5260#endif
5261 if (magn_first == 0 || magn_last == 0)
5262 {
5263 /* Save the magnification factors for object 0 */
5264 mng_info->magn_mb=magn_mb;
5265 mng_info->magn_ml=magn_ml;
5266 mng_info->magn_mr=magn_mr;
5267 mng_info->magn_mt=magn_mt;
5268 mng_info->magn_mx=magn_mx;
5269 mng_info->magn_my=magn_my;
5270 mng_info->magn_methx=magn_methx;
5271 mng_info->magn_methy=magn_methy;
5272 }
5273 }
glennrp47b9dd52010-11-24 18:12:06 +00005274
cristy3ed852e2009-09-05 21:47:34 +00005275 if (memcmp(type,mng_PAST,4) == 0)
5276 {
5277 if (mng_info->past_warning == 0)
5278 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5279 CoderError,"PAST is not implemented yet","`%s'",
5280 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005281
cristy3ed852e2009-09-05 21:47:34 +00005282 mng_info->past_warning++;
5283 }
glennrp47b9dd52010-11-24 18:12:06 +00005284
cristy3ed852e2009-09-05 21:47:34 +00005285 if (memcmp(type,mng_SHOW,4) == 0)
5286 {
5287 if (mng_info->show_warning == 0)
5288 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5289 CoderError,"SHOW is not implemented yet","`%s'",
5290 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005291
cristy3ed852e2009-09-05 21:47:34 +00005292 mng_info->show_warning++;
5293 }
glennrp47b9dd52010-11-24 18:12:06 +00005294
cristy3ed852e2009-09-05 21:47:34 +00005295 if (memcmp(type,mng_sBIT,4) == 0)
5296 {
5297 if (length < 4)
5298 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005299
cristy3ed852e2009-09-05 21:47:34 +00005300 else
5301 {
5302 mng_info->global_sbit.gray=p[0];
5303 mng_info->global_sbit.red=p[0];
5304 mng_info->global_sbit.green=p[1];
5305 mng_info->global_sbit.blue=p[2];
5306 mng_info->global_sbit.alpha=p[3];
5307 mng_info->have_global_sbit=MagickTrue;
5308 }
5309 }
5310 if (memcmp(type,mng_pHYs,4) == 0)
5311 {
5312 if (length > 8)
5313 {
5314 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005315 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005316 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005317 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005318 mng_info->global_phys_unit_type=p[8];
5319 mng_info->have_global_phys=MagickTrue;
5320 }
glennrp47b9dd52010-11-24 18:12:06 +00005321
cristy3ed852e2009-09-05 21:47:34 +00005322 else
5323 mng_info->have_global_phys=MagickFalse;
5324 }
5325 if (memcmp(type,mng_pHYg,4) == 0)
5326 {
5327 if (mng_info->phyg_warning == 0)
5328 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5329 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005330
cristy3ed852e2009-09-05 21:47:34 +00005331 mng_info->phyg_warning++;
5332 }
5333 if (memcmp(type,mng_BASI,4) == 0)
5334 {
5335 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005336
cristy3ed852e2009-09-05 21:47:34 +00005337 if (mng_info->basi_warning == 0)
5338 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5339 CoderError,"BASI is not implemented yet","`%s'",
5340 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 mng_info->basi_warning++;
5343#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005344 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005345 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005346 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005347 (p[6] << 8) | p[7]);
5348 basi_color_type=p[8];
5349 basi_compression_method=p[9];
5350 basi_filter_type=p[10];
5351 basi_interlace_method=p[11];
5352 if (length > 11)
5353 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005354
cristy3ed852e2009-09-05 21:47:34 +00005355 else
5356 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005357
cristy3ed852e2009-09-05 21:47:34 +00005358 if (length > 13)
5359 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005360
cristy3ed852e2009-09-05 21:47:34 +00005361 else
5362 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 if (length > 15)
5365 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005366
cristy3ed852e2009-09-05 21:47:34 +00005367 else
5368 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005369
cristy3ed852e2009-09-05 21:47:34 +00005370 if (length > 17)
5371 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005372
cristy3ed852e2009-09-05 21:47:34 +00005373 else
5374 {
5375 if (basi_sample_depth == 16)
5376 basi_alpha=65535L;
5377 else
5378 basi_alpha=255;
5379 }
glennrp47b9dd52010-11-24 18:12:06 +00005380
cristy3ed852e2009-09-05 21:47:34 +00005381 if (length > 19)
5382 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 else
5385 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387#endif
5388 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5389 continue;
5390 }
glennrp47b9dd52010-11-24 18:12:06 +00005391
cristy3ed852e2009-09-05 21:47:34 +00005392 if (memcmp(type,mng_IHDR,4)
5393#if defined(JNG_SUPPORTED)
5394 && memcmp(type,mng_JHDR,4)
5395#endif
5396 )
5397 {
5398 /* Not an IHDR or JHDR chunk */
5399 if (length)
5400 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005401
cristy3ed852e2009-09-05 21:47:34 +00005402 continue;
5403 }
5404/* Process IHDR */
5405 if (logging != MagickFalse)
5406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5407 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005408
cristy3ed852e2009-09-05 21:47:34 +00005409 mng_info->exists[object_id]=MagickTrue;
5410 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005411
cristy3ed852e2009-09-05 21:47:34 +00005412 if (mng_info->invisible[object_id])
5413 {
5414 if (logging != MagickFalse)
5415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5416 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005417
cristy3ed852e2009-09-05 21:47:34 +00005418 skip_to_iend=MagickTrue;
5419 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5420 continue;
5421 }
5422#if defined(MNG_INSERT_LAYERS)
5423 if (length < 8)
5424 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005425
cristy8182b072010-05-30 20:10:53 +00005426 image_width=(size_t) mng_get_long(p);
5427 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005428#endif
5429 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5430
5431 /*
5432 Insert a transparent background layer behind the entire animation
5433 if it is not full screen.
5434 */
5435#if defined(MNG_INSERT_LAYERS)
5436 if (insert_layers && mng_type && first_mng_object)
5437 {
5438 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5439 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005440 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005441 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005442 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005443 {
5444 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5445 {
5446 /*
5447 Allocate next image structure.
5448 */
5449 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005450
cristy3ed852e2009-09-05 21:47:34 +00005451 if (GetNextImageInList(image) == (Image *) NULL)
5452 {
5453 image=DestroyImageList(image);
5454 MngInfoFreeStruct(mng_info,&have_mng_structure);
5455 return((Image *) NULL);
5456 }
glennrp47b9dd52010-11-24 18:12:06 +00005457
cristy3ed852e2009-09-05 21:47:34 +00005458 image=SyncNextImageInList(image);
5459 }
5460 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005461
cristy3ed852e2009-09-05 21:47:34 +00005462 if (term_chunk_found)
5463 {
5464 image->start_loop=MagickTrue;
5465 image->iterations=mng_iterations;
5466 term_chunk_found=MagickFalse;
5467 }
glennrp47b9dd52010-11-24 18:12:06 +00005468
cristy3ed852e2009-09-05 21:47:34 +00005469 else
5470 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005471
5472 /* Make a background rectangle. */
5473
cristy3ed852e2009-09-05 21:47:34 +00005474 image->delay=0;
5475 image->columns=mng_info->mng_width;
5476 image->rows=mng_info->mng_height;
5477 image->page.width=mng_info->mng_width;
5478 image->page.height=mng_info->mng_height;
5479 image->page.x=0;
5480 image->page.y=0;
5481 image->background_color=mng_background_color;
5482 (void) SetImageBackgroundColor(image);
5483 if (logging != MagickFalse)
5484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005485 " Inserted transparent background layer, W=%.20g, H=%.20g",
5486 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005487 }
5488 }
5489 /*
5490 Insert a background layer behind the upcoming image if
5491 framing_mode is 3, and we haven't already inserted one.
5492 */
5493 if (insert_layers && (mng_info->framing_mode == 3) &&
5494 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5495 (simplicity & 0x08)))
5496 {
5497 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5498 {
5499 /*
5500 Allocate next image structure.
5501 */
5502 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005503
cristy3ed852e2009-09-05 21:47:34 +00005504 if (GetNextImageInList(image) == (Image *) NULL)
5505 {
5506 image=DestroyImageList(image);
5507 MngInfoFreeStruct(mng_info,&have_mng_structure);
5508 return((Image *) NULL);
5509 }
glennrp47b9dd52010-11-24 18:12:06 +00005510
cristy3ed852e2009-09-05 21:47:34 +00005511 image=SyncNextImageInList(image);
5512 }
glennrp0fe50b42010-11-16 03:52:51 +00005513
cristy3ed852e2009-09-05 21:47:34 +00005514 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005515
cristy3ed852e2009-09-05 21:47:34 +00005516 if (term_chunk_found)
5517 {
5518 image->start_loop=MagickTrue;
5519 image->iterations=mng_iterations;
5520 term_chunk_found=MagickFalse;
5521 }
glennrp0fe50b42010-11-16 03:52:51 +00005522
cristy3ed852e2009-09-05 21:47:34 +00005523 else
5524 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005525
cristy3ed852e2009-09-05 21:47:34 +00005526 image->delay=0;
5527 image->columns=subframe_width;
5528 image->rows=subframe_height;
5529 image->page.width=subframe_width;
5530 image->page.height=subframe_height;
5531 image->page.x=mng_info->clip.left;
5532 image->page.y=mng_info->clip.top;
5533 image->background_color=mng_background_color;
5534 image->matte=MagickFalse;
5535 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 if (logging != MagickFalse)
5538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005539 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005540 (double) mng_info->clip.left,(double) mng_info->clip.right,
5541 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005542 }
5543#endif /* MNG_INSERT_LAYERS */
5544 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005545
cristy3ed852e2009-09-05 21:47:34 +00005546 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5547 {
5548 /*
5549 Allocate next image structure.
5550 */
5551 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005552
cristy3ed852e2009-09-05 21:47:34 +00005553 if (GetNextImageInList(image) == (Image *) NULL)
5554 {
5555 image=DestroyImageList(image);
5556 MngInfoFreeStruct(mng_info,&have_mng_structure);
5557 return((Image *) NULL);
5558 }
glennrp47b9dd52010-11-24 18:12:06 +00005559
cristy3ed852e2009-09-05 21:47:34 +00005560 image=SyncNextImageInList(image);
5561 }
5562 mng_info->image=image;
5563 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5564 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005565
cristy3ed852e2009-09-05 21:47:34 +00005566 if (status == MagickFalse)
5567 break;
glennrp0fe50b42010-11-16 03:52:51 +00005568
cristy3ed852e2009-09-05 21:47:34 +00005569 if (term_chunk_found)
5570 {
5571 image->start_loop=MagickTrue;
5572 term_chunk_found=MagickFalse;
5573 }
glennrp0fe50b42010-11-16 03:52:51 +00005574
cristy3ed852e2009-09-05 21:47:34 +00005575 else
5576 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005577
cristy3ed852e2009-09-05 21:47:34 +00005578 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5579 {
5580 image->delay=frame_delay;
5581 frame_delay=default_frame_delay;
5582 }
glennrp0fe50b42010-11-16 03:52:51 +00005583
cristy3ed852e2009-09-05 21:47:34 +00005584 else
5585 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 image->page.width=mng_info->mng_width;
5588 image->page.height=mng_info->mng_height;
5589 image->page.x=mng_info->x_off[object_id];
5590 image->page.y=mng_info->y_off[object_id];
5591 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00005592
cristy3ed852e2009-09-05 21:47:34 +00005593 /*
5594 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5595 */
glennrp47b9dd52010-11-24 18:12:06 +00005596
cristy3ed852e2009-09-05 21:47:34 +00005597 if (logging != MagickFalse)
5598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5599 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5600 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005601
cristybb503372010-05-27 20:51:26 +00005602 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00005603
cristy3ed852e2009-09-05 21:47:34 +00005604 if (offset < 0)
5605 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5606 }
5607
5608 previous=image;
5609 mng_info->image=image;
5610 mng_info->mng_type=mng_type;
5611 mng_info->object_id=object_id;
5612
5613 if (memcmp(type,mng_IHDR,4) == 0)
5614 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005615
cristy3ed852e2009-09-05 21:47:34 +00005616#if defined(JNG_SUPPORTED)
5617 else
5618 image=ReadOneJNGImage(mng_info,image_info,exception);
5619#endif
5620
5621 if (image == (Image *) NULL)
5622 {
5623 if (IsImageObject(previous) != MagickFalse)
5624 {
5625 (void) DestroyImageList(previous);
5626 (void) CloseBlob(previous);
5627 }
glennrp47b9dd52010-11-24 18:12:06 +00005628
cristy3ed852e2009-09-05 21:47:34 +00005629 MngInfoFreeStruct(mng_info,&have_mng_structure);
5630 return((Image *) NULL);
5631 }
glennrp0fe50b42010-11-16 03:52:51 +00005632
cristy3ed852e2009-09-05 21:47:34 +00005633 if (image->columns == 0 || image->rows == 0)
5634 {
5635 (void) CloseBlob(image);
5636 image=DestroyImageList(image);
5637 MngInfoFreeStruct(mng_info,&have_mng_structure);
5638 return((Image *) NULL);
5639 }
glennrp0fe50b42010-11-16 03:52:51 +00005640
cristy3ed852e2009-09-05 21:47:34 +00005641 mng_info->image=image;
5642
5643 if (mng_type)
5644 {
5645 MngBox
5646 crop_box;
5647
5648 if (mng_info->magn_methx || mng_info->magn_methy)
5649 {
5650 png_uint_32
5651 magnified_height,
5652 magnified_width;
5653
5654 if (logging != MagickFalse)
5655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5656 " Processing MNG MAGN chunk");
5657
5658 if (mng_info->magn_methx == 1)
5659 {
5660 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005661
cristy3ed852e2009-09-05 21:47:34 +00005662 if (image->columns > 1)
5663 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005666 magnified_width += (png_uint_32)
5667 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00005668 }
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 else
5671 {
cristy4e5bc842010-06-09 13:56:01 +00005672 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00005673
cristy3ed852e2009-09-05 21:47:34 +00005674 if (image->columns > 1)
5675 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00005676
cristy3ed852e2009-09-05 21:47:34 +00005677 if (image->columns > 2)
5678 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00005679
cristy3ed852e2009-09-05 21:47:34 +00005680 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005681 magnified_width += (png_uint_32)
5682 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00005683 }
glennrp47b9dd52010-11-24 18:12:06 +00005684
cristy3ed852e2009-09-05 21:47:34 +00005685 if (mng_info->magn_methy == 1)
5686 {
5687 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005688
cristy3ed852e2009-09-05 21:47:34 +00005689 if (image->rows > 1)
5690 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005693 magnified_height += (png_uint_32)
5694 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00005695 }
glennrp47b9dd52010-11-24 18:12:06 +00005696
cristy3ed852e2009-09-05 21:47:34 +00005697 else
5698 {
cristy4e5bc842010-06-09 13:56:01 +00005699 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00005700
cristy3ed852e2009-09-05 21:47:34 +00005701 if (image->rows > 1)
5702 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00005703
cristy3ed852e2009-09-05 21:47:34 +00005704 if (image->rows > 2)
5705 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00005706
cristy3ed852e2009-09-05 21:47:34 +00005707 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005708 magnified_height += (png_uint_32)
5709 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00005710 }
glennrp47b9dd52010-11-24 18:12:06 +00005711
cristy3ed852e2009-09-05 21:47:34 +00005712 if (magnified_height > image->rows ||
5713 magnified_width > image->columns)
5714 {
5715 Image
5716 *large_image;
5717
5718 int
5719 yy;
5720
cristybb503372010-05-27 20:51:26 +00005721 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005722 m,
5723 y;
5724
cristybb503372010-05-27 20:51:26 +00005725 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005726 x;
5727
5728 register PixelPacket
5729 *n,
5730 *q;
5731
5732 PixelPacket
5733 *next,
5734 *prev;
5735
5736 png_uint_16
5737 magn_methx,
5738 magn_methy;
5739
glennrp47b9dd52010-11-24 18:12:06 +00005740 /* Allocate next image structure. */
5741
cristy3ed852e2009-09-05 21:47:34 +00005742 if (logging != MagickFalse)
5743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5744 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00005745
cristy3ed852e2009-09-05 21:47:34 +00005746 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005747
cristy3ed852e2009-09-05 21:47:34 +00005748 if (GetNextImageInList(image) == (Image *) NULL)
5749 {
5750 image=DestroyImageList(image);
5751 MngInfoFreeStruct(mng_info,&have_mng_structure);
5752 return((Image *) NULL);
5753 }
5754
5755 large_image=SyncNextImageInList(image);
5756
5757 large_image->columns=magnified_width;
5758 large_image->rows=magnified_height;
5759
5760 magn_methx=mng_info->magn_methx;
5761 magn_methy=mng_info->magn_methy;
5762
glennrp3faa9a32011-04-23 14:00:25 +00005763#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00005764#define QM unsigned short
5765 if (magn_methx != 1 || magn_methy != 1)
5766 {
5767 /*
5768 Scale pixels to unsigned shorts to prevent
5769 overflow of intermediate values of interpolations
5770 */
cristybb503372010-05-27 20:51:26 +00005771 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005772 {
5773 q=GetAuthenticPixels(image,0,y,image->columns,1,
5774 exception);
glennrp47b9dd52010-11-24 18:12:06 +00005775
cristybb503372010-05-27 20:51:26 +00005776 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005777 {
glennrp7c7b3152011-04-26 04:01:27 +00005778 SetRedPixelComponent(q,ScaleQuantumToShort(
5779 GetRedPixelComponent(q));
5780 SetGreenPixelComponent(q,ScaleQuantumToShort(
5781 GetGreenPixelComponent(q));
5782 SetBluePixelComponent(q,ScaleQuantumToShort(
5783 GetBluePixelComponent(q));
5784 SetOpacityPixelComponent(q,ScaleQuantumToShort(
5785 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00005786 q++;
5787 }
glennrp47b9dd52010-11-24 18:12:06 +00005788
cristy3ed852e2009-09-05 21:47:34 +00005789 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5790 break;
5791 }
5792 }
5793#else
5794#define QM Quantum
5795#endif
5796
5797 if (image->matte != MagickFalse)
5798 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005799
cristy3ed852e2009-09-05 21:47:34 +00005800 else
5801 {
5802 large_image->background_color.opacity=OpaqueOpacity;
5803 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 if (magn_methx == 4)
5806 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 if (magn_methx == 5)
5809 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00005810
cristy3ed852e2009-09-05 21:47:34 +00005811 if (magn_methy == 4)
5812 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00005813
cristy3ed852e2009-09-05 21:47:34 +00005814 if (magn_methy == 5)
5815 magn_methy=3;
5816 }
5817
5818 /* magnify the rows into the right side of the large image */
5819
5820 if (logging != MagickFalse)
5821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005822 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00005823 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005824 yy=0;
5825 length=(size_t) image->columns;
5826 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5827 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00005828
cristy3ed852e2009-09-05 21:47:34 +00005829 if ((prev == (PixelPacket *) NULL) ||
5830 (next == (PixelPacket *) NULL))
5831 {
5832 image=DestroyImageList(image);
5833 MngInfoFreeStruct(mng_info,&have_mng_structure);
5834 ThrowReaderException(ResourceLimitError,
5835 "MemoryAllocationFailed");
5836 }
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5839 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristybb503372010-05-27 20:51:26 +00005841 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005842 {
5843 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005844 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005845
cristybb503372010-05-27 20:51:26 +00005846 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5847 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005848
cristybb503372010-05-27 20:51:26 +00005849 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5850 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005851
cristybb503372010-05-27 20:51:26 +00005852 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005853 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005854
cristy3ed852e2009-09-05 21:47:34 +00005855 else
cristybb503372010-05-27 20:51:26 +00005856 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005857
cristy3ed852e2009-09-05 21:47:34 +00005858 n=prev;
5859 prev=next;
5860 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00005861
cristybb503372010-05-27 20:51:26 +00005862 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005863 {
5864 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5865 exception);
5866 (void) CopyMagickMemory(next,n,length);
5867 }
glennrp47b9dd52010-11-24 18:12:06 +00005868
cristy3ed852e2009-09-05 21:47:34 +00005869 for (i=0; i < m; i++, yy++)
5870 {
glennrp7c7b3152011-04-26 04:01:27 +00005871 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00005872 register PixelPacket
5873 *pixels;
5874
cristybb503372010-05-27 20:51:26 +00005875 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005876 pixels=prev;
5877 n=next;
5878 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5879 1,exception);
5880 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00005881
cristybb503372010-05-27 20:51:26 +00005882 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005883 {
glennrpfd05d622011-02-25 04:10:33 +00005884 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005885 /*
5886 if (image->storage_class == PseudoClass)
5887 {
5888 }
5889 */
5890
5891 if (magn_methy <= 1)
5892 {
5893 *q=(*pixels); /* replicate previous */
5894 }
glennrp47b9dd52010-11-24 18:12:06 +00005895
cristy3ed852e2009-09-05 21:47:34 +00005896 else if (magn_methy == 2 || magn_methy == 4)
5897 {
5898 if (i == 0)
5899 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005900
cristy3ed852e2009-09-05 21:47:34 +00005901 else
5902 {
5903 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005904 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5905 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005906 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005907 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5908 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005909 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005910 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5911 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005912 +(*pixels).blue);
glennrp47b9dd52010-11-24 18:12:06 +00005913
cristy3ed852e2009-09-05 21:47:34 +00005914 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005915 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005916 (2*i*((*n).opacity
5917 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005918 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005919 }
glennrp47b9dd52010-11-24 18:12:06 +00005920
cristy3ed852e2009-09-05 21:47:34 +00005921 if (magn_methy == 4)
5922 {
5923 /* Replicate nearest */
5924 if (i <= ((m+1) << 1))
5925 (*q).opacity=(*pixels).opacity+0;
5926 else
5927 (*q).opacity=(*n).opacity+0;
5928 }
5929 }
glennrp47b9dd52010-11-24 18:12:06 +00005930
cristy3ed852e2009-09-05 21:47:34 +00005931 else /* if (magn_methy == 3 || magn_methy == 5) */
5932 {
5933 /* Replicate nearest */
5934 if (i <= ((m+1) << 1))
5935 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005936
cristy3ed852e2009-09-05 21:47:34 +00005937 else
5938 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00005939
cristy3ed852e2009-09-05 21:47:34 +00005940 if (magn_methy == 5)
5941 {
cristybb503372010-05-27 20:51:26 +00005942 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5943 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005944 +(*pixels).opacity);
5945 }
5946 }
5947 n++;
5948 q++;
5949 pixels++;
5950 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00005951
cristy3ed852e2009-09-05 21:47:34 +00005952 if (SyncAuthenticPixels(large_image,exception) == 0)
5953 break;
glennrp47b9dd52010-11-24 18:12:06 +00005954
cristy3ed852e2009-09-05 21:47:34 +00005955 } /* i */
5956 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00005957
cristy3ed852e2009-09-05 21:47:34 +00005958 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5959 next=(PixelPacket *) RelinquishMagickMemory(next);
5960
5961 length=image->columns;
5962
5963 if (logging != MagickFalse)
5964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5965 " Delete original image");
5966
5967 DeleteImageFromList(&image);
5968
5969 image=large_image;
5970
5971 mng_info->image=image;
5972
5973 /* magnify the columns */
5974 if (logging != MagickFalse)
5975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005976 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00005977
cristybb503372010-05-27 20:51:26 +00005978 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005979 {
5980 register PixelPacket
5981 *pixels;
5982
5983 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5984 pixels=q+(image->columns-length);
5985 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00005986
cristybb503372010-05-27 20:51:26 +00005987 for (x=(ssize_t) (image->columns-length);
5988 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005989 {
glennrp7c7b3152011-04-26 04:01:27 +00005990 /* To do: Rewrite using Get/Set***PixelComponent() */
5991
cristybb503372010-05-27 20:51:26 +00005992 if (x == (ssize_t) (image->columns-length))
5993 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005994
cristybb503372010-05-27 20:51:26 +00005995 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5996 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005997
cristybb503372010-05-27 20:51:26 +00005998 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5999 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006000
cristybb503372010-05-27 20:51:26 +00006001 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006002 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006003
cristy3ed852e2009-09-05 21:47:34 +00006004 else
cristybb503372010-05-27 20:51:26 +00006005 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006006
cristy3ed852e2009-09-05 21:47:34 +00006007 for (i=0; i < m; i++)
6008 {
6009 if (magn_methx <= 1)
6010 {
6011 /* replicate previous */
6012 *q=(*pixels);
6013 }
glennrp47b9dd52010-11-24 18:12:06 +00006014
cristy3ed852e2009-09-05 21:47:34 +00006015 else if (magn_methx == 2 || magn_methx == 4)
6016 {
6017 if (i == 0)
6018 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006019
cristy3ed852e2009-09-05 21:47:34 +00006020 else
6021 {
6022 /* Interpolate */
6023 (*q).red=(QM) ((2*i*((*n).red
6024 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00006025 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00006026 (*q).green=(QM) ((2*i*((*n).green
6027 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00006028 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00006029 (*q).blue=(QM) ((2*i*((*n).blue
6030 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00006031 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00006032 if (image->matte != MagickFalse)
6033 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006034 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006035 +(*pixels).opacity);
6036 }
glennrp47b9dd52010-11-24 18:12:06 +00006037
cristy3ed852e2009-09-05 21:47:34 +00006038 if (magn_methx == 4)
6039 {
6040 /* Replicate nearest */
6041 if (i <= ((m+1) << 1))
6042 (*q).opacity=(*pixels).opacity+0;
6043 else
6044 (*q).opacity=(*n).opacity+0;
6045 }
6046 }
glennrp47b9dd52010-11-24 18:12:06 +00006047
cristy3ed852e2009-09-05 21:47:34 +00006048 else /* if (magn_methx == 3 || magn_methx == 5) */
6049 {
6050 /* Replicate nearest */
6051 if (i <= ((m+1) << 1))
6052 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006053
cristy3ed852e2009-09-05 21:47:34 +00006054 else
6055 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00006056
cristy3ed852e2009-09-05 21:47:34 +00006057 if (magn_methx == 5)
6058 {
6059 /* Interpolate */
6060 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006061 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006062 +(*pixels).opacity);
6063 }
6064 }
6065 q++;
6066 }
6067 n++;
6068 p++;
6069 }
glennrp47b9dd52010-11-24 18:12:06 +00006070
cristy3ed852e2009-09-05 21:47:34 +00006071 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6072 break;
6073 }
glennrp3faa9a32011-04-23 14:00:25 +00006074#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006075 if (magn_methx != 1 || magn_methy != 1)
6076 {
6077 /*
6078 Rescale pixels to Quantum
6079 */
cristybb503372010-05-27 20:51:26 +00006080 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006081 {
6082 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006083
cristybb503372010-05-27 20:51:26 +00006084 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006085 {
glennrp7c7b3152011-04-26 04:01:27 +00006086 SetRedPixelComponent(q,ScaleShortToQuantum(
6087 GetRedPixelComponent(q));
6088 SetGreenPixelComponent(q,ScaleShortToQuantum(
6089 GetGreenPixelComponent(q));
6090 SetBluePixelComponent(q,ScaleShortToQuantum(
6091 GetBluePixelComponent(q));
6092 SetOpacityPixelComponent(q,ScaleShortToQuantum(
6093 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00006094 q++;
6095 }
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6098 break;
6099 }
6100 }
6101#endif
6102 if (logging != MagickFalse)
6103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6104 " Finished MAGN processing");
6105 }
6106 }
6107
6108 /*
6109 Crop_box is with respect to the upper left corner of the MNG.
6110 */
6111 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6112 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6113 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6114 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6115 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6116 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6117 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6118 if ((crop_box.left != (mng_info->image_box.left
6119 +mng_info->x_off[object_id])) ||
6120 (crop_box.right != (mng_info->image_box.right
6121 +mng_info->x_off[object_id])) ||
6122 (crop_box.top != (mng_info->image_box.top
6123 +mng_info->y_off[object_id])) ||
6124 (crop_box.bottom != (mng_info->image_box.bottom
6125 +mng_info->y_off[object_id])))
6126 {
6127 if (logging != MagickFalse)
6128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6129 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006130
cristy3ed852e2009-09-05 21:47:34 +00006131 if ((crop_box.left < crop_box.right) &&
6132 (crop_box.top < crop_box.bottom))
6133 {
6134 Image
6135 *im;
6136
6137 RectangleInfo
6138 crop_info;
6139
6140 /*
6141 Crop_info is with respect to the upper left corner of
6142 the image.
6143 */
6144 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6145 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006146 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6147 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006148 image->page.width=image->columns;
6149 image->page.height=image->rows;
6150 image->page.x=0;
6151 image->page.y=0;
6152 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006153
cristy3ed852e2009-09-05 21:47:34 +00006154 if (im != (Image *) NULL)
6155 {
6156 image->columns=im->columns;
6157 image->rows=im->rows;
6158 im=DestroyImage(im);
6159 image->page.width=image->columns;
6160 image->page.height=image->rows;
6161 image->page.x=crop_box.left;
6162 image->page.y=crop_box.top;
6163 }
6164 }
glennrp47b9dd52010-11-24 18:12:06 +00006165
cristy3ed852e2009-09-05 21:47:34 +00006166 else
6167 {
6168 /*
6169 No pixels in crop area. The MNG spec still requires
6170 a layer, though, so make a single transparent pixel in
6171 the top left corner.
6172 */
6173 image->columns=1;
6174 image->rows=1;
6175 image->colors=2;
6176 (void) SetImageBackgroundColor(image);
6177 image->page.width=1;
6178 image->page.height=1;
6179 image->page.x=0;
6180 image->page.y=0;
6181 }
6182 }
6183#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6184 image=mng_info->image;
6185#endif
6186 }
6187
glennrp2b013e42010-11-24 16:55:50 +00006188#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6189 /* PNG does not handle depths greater than 16 so reduce it even
6190 * if lossy
6191 */
6192 if (image->depth > 16)
6193 image->depth=16;
6194#endif
6195
glennrp3faa9a32011-04-23 14:00:25 +00006196#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006197 if (LosslessReduceDepthOK(image) != MagickFalse)
6198 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006199#endif
glennrpd6afd542010-11-19 01:53:05 +00006200
cristy3ed852e2009-09-05 21:47:34 +00006201 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006202
cristy3ed852e2009-09-05 21:47:34 +00006203 if (image_info->number_scenes != 0)
6204 {
6205 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006206 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006207 break;
6208 }
glennrpd6afd542010-11-19 01:53:05 +00006209
cristy3ed852e2009-09-05 21:47:34 +00006210 if (logging != MagickFalse)
6211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6212 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006213
cristy3ed852e2009-09-05 21:47:34 +00006214 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006215
cristy3ed852e2009-09-05 21:47:34 +00006216 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006217
cristy3ed852e2009-09-05 21:47:34 +00006218 if (logging != MagickFalse)
6219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6220 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006221
cristy3ed852e2009-09-05 21:47:34 +00006222#if defined(MNG_INSERT_LAYERS)
6223 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6224 (mng_info->mng_height))
6225 {
6226 /*
6227 Insert a background layer if nothing else was found.
6228 */
6229 if (logging != MagickFalse)
6230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6231 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006232
cristy3ed852e2009-09-05 21:47:34 +00006233 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6234 {
6235 /*
6236 Allocate next image structure.
6237 */
6238 AcquireNextImage(image_info,image);
6239 if (GetNextImageInList(image) == (Image *) NULL)
6240 {
6241 image=DestroyImageList(image);
6242 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006243
cristy3ed852e2009-09-05 21:47:34 +00006244 if (logging != MagickFalse)
6245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6246 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006247
cristy3ed852e2009-09-05 21:47:34 +00006248 return((Image *) NULL);
6249 }
6250 image=SyncNextImageInList(image);
6251 }
6252 image->columns=mng_info->mng_width;
6253 image->rows=mng_info->mng_height;
6254 image->page.width=mng_info->mng_width;
6255 image->page.height=mng_info->mng_height;
6256 image->page.x=0;
6257 image->page.y=0;
6258 image->background_color=mng_background_color;
6259 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006260
cristy3ed852e2009-09-05 21:47:34 +00006261 if (image_info->ping == MagickFalse)
6262 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006263
cristy3ed852e2009-09-05 21:47:34 +00006264 mng_info->image_found++;
6265 }
6266#endif
6267 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006268
cristy3ed852e2009-09-05 21:47:34 +00006269 if (mng_iterations == 1)
6270 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006271
cristy3ed852e2009-09-05 21:47:34 +00006272 while (GetPreviousImageInList(image) != (Image *) NULL)
6273 {
6274 image_count++;
6275 if (image_count > 10*mng_info->image_found)
6276 {
6277 if (logging != MagickFalse)
6278 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006279
cristy3ed852e2009-09-05 21:47:34 +00006280 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6281 CoderError,"Linked list is corrupted, beginning of list not found",
6282 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristy3ed852e2009-09-05 21:47:34 +00006284 return((Image *) NULL);
6285 }
glennrp0fe50b42010-11-16 03:52:51 +00006286
cristy3ed852e2009-09-05 21:47:34 +00006287 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006288
cristy3ed852e2009-09-05 21:47:34 +00006289 if (GetNextImageInList(image) == (Image *) NULL)
6290 {
6291 if (logging != MagickFalse)
6292 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006293
cristy3ed852e2009-09-05 21:47:34 +00006294 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6295 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6296 image_info->filename);
6297 }
6298 }
glennrp47b9dd52010-11-24 18:12:06 +00006299
cristy3ed852e2009-09-05 21:47:34 +00006300 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6301 GetNextImageInList(image) ==
6302 (Image *) NULL)
6303 {
6304 if (logging != MagickFalse)
6305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6306 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006307
cristy3ed852e2009-09-05 21:47:34 +00006308 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6309 CoderError,"image->next for first image is NULL but shouldn't be.",
6310 "`%s'",image_info->filename);
6311 }
glennrp47b9dd52010-11-24 18:12:06 +00006312
cristy3ed852e2009-09-05 21:47:34 +00006313 if (mng_info->image_found == 0)
6314 {
6315 if (logging != MagickFalse)
6316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6317 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006318
cristy3ed852e2009-09-05 21:47:34 +00006319 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6320 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006321
cristy3ed852e2009-09-05 21:47:34 +00006322 if (image != (Image *) NULL)
6323 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006324
cristy3ed852e2009-09-05 21:47:34 +00006325 MngInfoFreeStruct(mng_info,&have_mng_structure);
6326 return((Image *) NULL);
6327 }
6328
6329 if (mng_info->ticks_per_second)
6330 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6331 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006332
cristy3ed852e2009-09-05 21:47:34 +00006333 else
6334 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006335
cristy3ed852e2009-09-05 21:47:34 +00006336 /* Find final nonzero image delay */
6337 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006338
cristy3ed852e2009-09-05 21:47:34 +00006339 while (GetNextImageInList(image) != (Image *) NULL)
6340 {
6341 if (image->delay)
6342 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006343
cristy3ed852e2009-09-05 21:47:34 +00006344 image=GetNextImageInList(image);
6345 }
glennrp0fe50b42010-11-16 03:52:51 +00006346
cristy3ed852e2009-09-05 21:47:34 +00006347 if (final_delay < final_image_delay)
6348 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006349
cristy3ed852e2009-09-05 21:47:34 +00006350 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006351
cristy3ed852e2009-09-05 21:47:34 +00006352 if (logging != MagickFalse)
6353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006354 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6355 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006356
cristy3ed852e2009-09-05 21:47:34 +00006357 if (logging != MagickFalse)
6358 {
6359 int
6360 scene;
6361
6362 scene=0;
6363 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006364
cristy3ed852e2009-09-05 21:47:34 +00006365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6366 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006367
cristy3ed852e2009-09-05 21:47:34 +00006368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006369 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006370
cristy3ed852e2009-09-05 21:47:34 +00006371 while (GetNextImageInList(image) != (Image *) NULL)
6372 {
6373 image=GetNextImageInList(image);
6374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006375 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006376 }
6377 }
6378
6379 image=GetFirstImageInList(image);
6380#ifdef MNG_COALESCE_LAYERS
6381 if (insert_layers)
6382 {
6383 Image
6384 *next_image,
6385 *next;
6386
cristybb503372010-05-27 20:51:26 +00006387 size_t
cristy3ed852e2009-09-05 21:47:34 +00006388 scene;
6389
6390 if (logging != MagickFalse)
6391 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006392
cristy3ed852e2009-09-05 21:47:34 +00006393 scene=image->scene;
6394 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006395
cristy3ed852e2009-09-05 21:47:34 +00006396 if (next_image == (Image *) NULL)
6397 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006398
cristy3ed852e2009-09-05 21:47:34 +00006399 image=DestroyImageList(image);
6400 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006401
cristy3ed852e2009-09-05 21:47:34 +00006402 for (next=image; next != (Image *) NULL; next=next_image)
6403 {
6404 next->page.width=mng_info->mng_width;
6405 next->page.height=mng_info->mng_height;
6406 next->page.x=0;
6407 next->page.y=0;
6408 next->scene=scene++;
6409 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006410
cristy3ed852e2009-09-05 21:47:34 +00006411 if (next_image == (Image *) NULL)
6412 break;
glennrp47b9dd52010-11-24 18:12:06 +00006413
cristy3ed852e2009-09-05 21:47:34 +00006414 if (next->delay == 0)
6415 {
6416 scene--;
6417 next_image->previous=GetPreviousImageInList(next);
6418 if (GetPreviousImageInList(next) == (Image *) NULL)
6419 image=next_image;
6420 else
6421 next->previous->next=next_image;
6422 next=DestroyImage(next);
6423 }
6424 }
6425 }
6426#endif
6427
6428 while (GetNextImageInList(image) != (Image *) NULL)
6429 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristy3ed852e2009-09-05 21:47:34 +00006431 image->dispose=BackgroundDispose;
6432
6433 if (logging != MagickFalse)
6434 {
6435 int
6436 scene;
6437
6438 scene=0;
6439 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006440
cristy3ed852e2009-09-05 21:47:34 +00006441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6442 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006443
cristy3ed852e2009-09-05 21:47:34 +00006444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006445 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6446 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006447
cristy3ed852e2009-09-05 21:47:34 +00006448 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006449 {
6450 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006451
cristyf2faecf2010-05-28 19:19:36 +00006452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006453 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6454 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006455 }
6456 }
glennrp47b9dd52010-11-24 18:12:06 +00006457
cristy3ed852e2009-09-05 21:47:34 +00006458 image=GetFirstImageInList(image);
6459 MngInfoFreeStruct(mng_info,&have_mng_structure);
6460 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006461
cristy3ed852e2009-09-05 21:47:34 +00006462 if (logging != MagickFalse)
6463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006464
cristy3ed852e2009-09-05 21:47:34 +00006465 return(GetFirstImageInList(image));
6466}
glennrp25c1e2b2010-03-25 01:39:56 +00006467#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006468static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6469{
6470 printf("Your PNG library is too old: You have libpng-%s\n",
6471 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006472
cristy3ed852e2009-09-05 21:47:34 +00006473 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6474 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristy3ed852e2009-09-05 21:47:34 +00006476 return(Image *) NULL;
6477}
glennrp47b9dd52010-11-24 18:12:06 +00006478
cristy3ed852e2009-09-05 21:47:34 +00006479static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6480{
6481 return(ReadPNGImage(image_info,exception));
6482}
glennrp25c1e2b2010-03-25 01:39:56 +00006483#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006484#endif
6485
6486/*
6487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6488% %
6489% %
6490% %
6491% R e g i s t e r P N G I m a g e %
6492% %
6493% %
6494% %
6495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6496%
6497% RegisterPNGImage() adds properties for the PNG image format to
6498% the list of supported formats. The properties include the image format
6499% tag, a method to read and/or write the format, whether the format
6500% supports the saving of more than one frame to the same file or blob,
6501% whether the format supports native in-memory I/O, and a brief
6502% description of the format.
6503%
6504% The format of the RegisterPNGImage method is:
6505%
cristybb503372010-05-27 20:51:26 +00006506% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006507%
6508*/
cristybb503372010-05-27 20:51:26 +00006509ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006510{
6511 char
6512 version[MaxTextExtent];
6513
6514 MagickInfo
6515 *entry;
6516
6517 static const char
6518 *PNGNote=
6519 {
6520 "See http://www.libpng.org/ for details about the PNG format."
6521 },
glennrp47b9dd52010-11-24 18:12:06 +00006522
cristy3ed852e2009-09-05 21:47:34 +00006523 *JNGNote=
6524 {
6525 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6526 "format."
6527 },
glennrp47b9dd52010-11-24 18:12:06 +00006528
cristy3ed852e2009-09-05 21:47:34 +00006529 *MNGNote=
6530 {
6531 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6532 "format."
6533 };
6534
6535 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006536
cristy3ed852e2009-09-05 21:47:34 +00006537#if defined(PNG_LIBPNG_VER_STRING)
6538 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6539 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006540
cristy3ed852e2009-09-05 21:47:34 +00006541 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6542 {
6543 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6544 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6545 MaxTextExtent);
6546 }
6547#endif
glennrp47b9dd52010-11-24 18:12:06 +00006548
cristy3ed852e2009-09-05 21:47:34 +00006549 entry=SetMagickInfo("MNG");
6550 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00006551
cristy3ed852e2009-09-05 21:47:34 +00006552#if defined(MAGICKCORE_PNG_DELEGATE)
6553 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6554 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6555#endif
glennrp47b9dd52010-11-24 18:12:06 +00006556
cristy3ed852e2009-09-05 21:47:34 +00006557 entry->magick=(IsImageFormatHandler *) IsMNG;
6558 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00006559
cristy3ed852e2009-09-05 21:47:34 +00006560 if (*version != '\0')
6561 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563 entry->module=ConstantString("PNG");
6564 entry->note=ConstantString(MNGNote);
6565 (void) RegisterMagickInfo(entry);
6566
6567 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristy3ed852e2009-09-05 21:47:34 +00006569#if defined(MAGICKCORE_PNG_DELEGATE)
6570 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6571 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6572#endif
glennrp47b9dd52010-11-24 18:12:06 +00006573
cristy3ed852e2009-09-05 21:47:34 +00006574 entry->magick=(IsImageFormatHandler *) IsPNG;
6575 entry->adjoin=MagickFalse;
6576 entry->description=ConstantString("Portable Network Graphics");
6577 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006578
cristy3ed852e2009-09-05 21:47:34 +00006579 if (*version != '\0')
6580 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006581
cristy3ed852e2009-09-05 21:47:34 +00006582 entry->note=ConstantString(PNGNote);
6583 (void) RegisterMagickInfo(entry);
6584
6585 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00006586
cristy3ed852e2009-09-05 21:47:34 +00006587#if defined(MAGICKCORE_PNG_DELEGATE)
6588 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6589 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6590#endif
glennrp47b9dd52010-11-24 18:12:06 +00006591
cristy3ed852e2009-09-05 21:47:34 +00006592 entry->magick=(IsImageFormatHandler *) IsPNG;
6593 entry->adjoin=MagickFalse;
6594 entry->description=ConstantString(
6595 "8-bit indexed with optional binary transparency");
6596 entry->module=ConstantString("PNG");
6597 (void) RegisterMagickInfo(entry);
6598
6599 entry=SetMagickInfo("PNG24");
6600 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006601
cristy3ed852e2009-09-05 21:47:34 +00006602#if defined(ZLIB_VERSION)
6603 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6604 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006605
cristy3ed852e2009-09-05 21:47:34 +00006606 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6607 {
6608 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6609 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6610 }
6611#endif
glennrp47b9dd52010-11-24 18:12:06 +00006612
cristy3ed852e2009-09-05 21:47:34 +00006613 if (*version != '\0')
6614 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006615
cristy3ed852e2009-09-05 21:47:34 +00006616#if defined(MAGICKCORE_PNG_DELEGATE)
6617 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6618 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6619#endif
glennrp47b9dd52010-11-24 18:12:06 +00006620
cristy3ed852e2009-09-05 21:47:34 +00006621 entry->magick=(IsImageFormatHandler *) IsPNG;
6622 entry->adjoin=MagickFalse;
6623 entry->description=ConstantString("opaque 24-bit RGB");
6624 entry->module=ConstantString("PNG");
6625 (void) RegisterMagickInfo(entry);
6626
6627 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00006628
cristy3ed852e2009-09-05 21:47:34 +00006629#if defined(MAGICKCORE_PNG_DELEGATE)
6630 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6631 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6632#endif
glennrp47b9dd52010-11-24 18:12:06 +00006633
cristy3ed852e2009-09-05 21:47:34 +00006634 entry->magick=(IsImageFormatHandler *) IsPNG;
6635 entry->adjoin=MagickFalse;
6636 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6637 entry->module=ConstantString("PNG");
6638 (void) RegisterMagickInfo(entry);
6639
6640 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006641
cristy3ed852e2009-09-05 21:47:34 +00006642#if defined(JNG_SUPPORTED)
6643#if defined(MAGICKCORE_PNG_DELEGATE)
6644 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6645 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6646#endif
6647#endif
glennrp47b9dd52010-11-24 18:12:06 +00006648
cristy3ed852e2009-09-05 21:47:34 +00006649 entry->magick=(IsImageFormatHandler *) IsJNG;
6650 entry->adjoin=MagickFalse;
6651 entry->description=ConstantString("JPEG Network Graphics");
6652 entry->module=ConstantString("PNG");
6653 entry->note=ConstantString(JNGNote);
6654 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00006655
cristy18b17442009-10-25 18:36:48 +00006656#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006657 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00006658#endif
glennrp47b9dd52010-11-24 18:12:06 +00006659
cristy3ed852e2009-09-05 21:47:34 +00006660 return(MagickImageCoderSignature);
6661}
6662
6663/*
6664%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6665% %
6666% %
6667% %
6668% U n r e g i s t e r P N G I m a g e %
6669% %
6670% %
6671% %
6672%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6673%
6674% UnregisterPNGImage() removes format registrations made by the
6675% PNG module from the list of supported formats.
6676%
6677% The format of the UnregisterPNGImage method is:
6678%
6679% UnregisterPNGImage(void)
6680%
6681*/
6682ModuleExport void UnregisterPNGImage(void)
6683{
6684 (void) UnregisterMagickInfo("MNG");
6685 (void) UnregisterMagickInfo("PNG");
6686 (void) UnregisterMagickInfo("PNG8");
6687 (void) UnregisterMagickInfo("PNG24");
6688 (void) UnregisterMagickInfo("PNG32");
6689 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006690
cristy3ed852e2009-09-05 21:47:34 +00006691#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006692 if (ping_semaphore != (SemaphoreInfo *) NULL)
6693 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006694#endif
6695}
6696
6697#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006698#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006699/*
6700%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6701% %
6702% %
6703% %
6704% W r i t e M N G I m a g e %
6705% %
6706% %
6707% %
6708%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6709%
6710% WriteMNGImage() writes an image in the Portable Network Graphics
6711% Group's "Multiple-image Network Graphics" encoded image format.
6712%
6713% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6714%
6715% The format of the WriteMNGImage method is:
6716%
6717% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6718%
6719% A description of each parameter follows.
6720%
6721% o image_info: the image info.
6722%
6723% o image: The image.
6724%
6725%
6726% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6727% "To do" under ReadPNGImage):
6728%
cristy3ed852e2009-09-05 21:47:34 +00006729% Preserve all unknown and not-yet-handled known chunks found in input
6730% PNG file and copy them into output PNG files according to the PNG
6731% copying rules.
6732%
6733% Write the iCCP chunk at MNG level when (icc profile length > 0)
6734%
6735% Improve selection of color type (use indexed-colour or indexed-colour
6736% with tRNS when 256 or fewer unique RGBA values are present).
6737%
6738% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6739% This will be complicated if we limit ourselves to generating MNG-LC
6740% files. For now we ignore disposal method 3 and simply overlay the next
6741% image on it.
6742%
6743% Check for identical PLTE's or PLTE/tRNS combinations and use a
6744% global MNG PLTE or PLTE/tRNS combination when appropriate.
6745% [mostly done 15 June 1999 but still need to take care of tRNS]
6746%
6747% Check for identical sRGB and replace with a global sRGB (and remove
6748% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6749% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6750% local gAMA/cHRM with local sRGB if appropriate).
6751%
6752% Check for identical sBIT chunks and write global ones.
6753%
6754% Provide option to skip writing the signature tEXt chunks.
6755%
6756% Use signatures to detect identical objects and reuse the first
6757% instance of such objects instead of writing duplicate objects.
6758%
6759% Use a smaller-than-32k value of compression window size when
6760% appropriate.
6761%
6762% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6763% ancillary text chunks and save profiles.
6764%
6765% Provide an option to force LC files (to ensure exact framing rate)
6766% instead of VLC.
6767%
6768% Provide an option to force VLC files instead of LC, even when offsets
6769% are present. This will involve expanding the embedded images with a
6770% transparent region at the top and/or left.
6771*/
6772
cristy3ed852e2009-09-05 21:47:34 +00006773static void
glennrpcf002022011-01-30 02:38:15 +00006774Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00006775 png_info *ping_info, unsigned char *profile_type, unsigned char
6776 *profile_description, unsigned char *profile_data, png_uint_32 length)
6777{
cristy3ed852e2009-09-05 21:47:34 +00006778 png_textp
6779 text;
6780
cristybb503372010-05-27 20:51:26 +00006781 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006782 i;
6783
6784 unsigned char
6785 *sp;
6786
6787 png_charp
6788 dp;
6789
6790 png_uint_32
6791 allocated_length,
6792 description_length;
6793
6794 unsigned char
6795 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006796
cristy3ed852e2009-09-05 21:47:34 +00006797 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6798 return;
6799
6800 if (image_info->verbose)
6801 {
glennrp0fe50b42010-11-16 03:52:51 +00006802 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6803 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006804 }
glennrp0fe50b42010-11-16 03:52:51 +00006805
cristy3ed852e2009-09-05 21:47:34 +00006806 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6807 description_length=(png_uint_32) strlen((const char *) profile_description);
6808 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6809 + description_length);
6810 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6811 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6812 text[0].key[0]='\0';
6813 (void) ConcatenateMagickString(text[0].key,
6814 "Raw profile type ",MaxTextExtent);
6815 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6816 sp=profile_data;
6817 dp=text[0].text;
6818 *dp++='\n';
6819 (void) CopyMagickString(dp,(const char *) profile_description,
6820 allocated_length);
6821 dp+=description_length;
6822 *dp++='\n';
6823 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006824 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006825 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00006826
cristybb503372010-05-27 20:51:26 +00006827 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006828 {
6829 if (i%36 == 0)
6830 *dp++='\n';
6831 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6832 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6833 }
glennrp47b9dd52010-11-24 18:12:06 +00006834
cristy3ed852e2009-09-05 21:47:34 +00006835 *dp++='\n';
6836 *dp='\0';
6837 text[0].text_length=(png_size_t) (dp-text[0].text);
6838 text[0].compression=image_info->compression == NoCompression ||
6839 (image_info->compression == UndefinedCompression &&
6840 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00006841
cristy3ed852e2009-09-05 21:47:34 +00006842 if (text[0].text_length <= allocated_length)
6843 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00006844
cristy3ed852e2009-09-05 21:47:34 +00006845 png_free(ping,text[0].text);
6846 png_free(ping,text[0].key);
6847 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006848}
6849
glennrpcf002022011-01-30 02:38:15 +00006850static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00006851 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00006852{
6853 char
6854 *name;
6855
6856 const StringInfo
6857 *profile;
6858
6859 unsigned char
6860 *data;
6861
6862 png_uint_32 length;
6863
6864 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00006865
6866 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6867 {
cristy3ed852e2009-09-05 21:47:34 +00006868 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00006869
cristy3ed852e2009-09-05 21:47:34 +00006870 if (profile != (const StringInfo *) NULL)
6871 {
6872 StringInfo
glennrpcf002022011-01-30 02:38:15 +00006873 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00006874
glennrp47b9dd52010-11-24 18:12:06 +00006875 if (LocaleNCompare(name,string,11) == 0)
6876 {
6877 if (logging != MagickFalse)
6878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6879 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00006880
glennrpcf002022011-01-30 02:38:15 +00006881 ping_profile=CloneStringInfo(profile);
6882 data=GetStringInfoDatum(ping_profile),
6883 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006884 data[4]=data[3];
6885 data[3]=data[2];
6886 data[2]=data[1];
6887 data[1]=data[0];
6888 (void) WriteBlobMSBULong(image,length-5); /* data length */
6889 (void) WriteBlob(image,length-1,data+1);
6890 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00006891 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006892 }
cristy3ed852e2009-09-05 21:47:34 +00006893 }
glennrp47b9dd52010-11-24 18:12:06 +00006894
cristy3ed852e2009-09-05 21:47:34 +00006895 name=GetNextImageProfile(image);
6896 }
glennrp47b9dd52010-11-24 18:12:06 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 return(MagickTrue);
6899}
6900
glennrpb9cfe272010-12-21 15:08:06 +00006901
cristy3ed852e2009-09-05 21:47:34 +00006902/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00006903static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6904 const ImageInfo *IMimage_info,Image *IMimage)
6905{
6906 Image
6907 *image;
6908
6909 ImageInfo
6910 *image_info;
6911
cristy3ed852e2009-09-05 21:47:34 +00006912 char
6913 s[2];
6914
6915 const char
6916 *name,
6917 *property,
6918 *value;
6919
6920 const StringInfo
6921 *profile;
6922
cristy3ed852e2009-09-05 21:47:34 +00006923 int
cristy3ed852e2009-09-05 21:47:34 +00006924 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006925 pass;
6926
glennrpe9c26dc2010-05-30 01:56:35 +00006927 png_byte
6928 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006929
glennrp39992b42010-11-14 00:03:43 +00006930 png_color
6931 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00006932
glennrp5af765f2010-03-30 11:12:18 +00006933 png_color_16
6934 ping_background,
6935 ping_trans_color;
6936
cristy3ed852e2009-09-05 21:47:34 +00006937 png_info
6938 *ping_info;
6939
6940 png_struct
6941 *ping;
6942
glennrp5af765f2010-03-30 11:12:18 +00006943 png_uint_32
6944 ping_height,
6945 ping_width;
6946
cristybb503372010-05-27 20:51:26 +00006947 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006948 y;
6949
6950 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00006951 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00006952 logging,
glennrp58e01762011-01-07 15:28:54 +00006953 matte,
6954
glennrpda8f3a72011-02-27 23:54:12 +00006955 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00006956 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00006957 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00006958 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00006959 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00006960 ping_have_bKGD,
6961 ping_have_pHYs,
6962 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00006963
6964 ping_exclude_bKGD,
6965 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00006966 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00006967 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00006968 ping_exclude_gAMA,
6969 ping_exclude_iCCP,
6970 /* ping_exclude_iTXt, */
6971 ping_exclude_oFFs,
6972 ping_exclude_pHYs,
6973 ping_exclude_sRGB,
6974 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00006975 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00006976 ping_exclude_vpAg,
6977 ping_exclude_zCCP, /* hex-encoded iCCP */
6978 ping_exclude_zTXt,
6979
glennrp8d3d6e52011-04-19 04:39:51 +00006980 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00006981 ping_need_colortype_warning,
6982
glennrp82b3c532011-03-22 19:20:54 +00006983 status,
glennrpd3371642011-03-22 19:42:23 +00006984 tried_333,
6985 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00006986
6987 QuantumInfo
6988 *quantum_info;
6989
cristybb503372010-05-27 20:51:26 +00006990 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006991 i,
6992 x;
6993
6994 unsigned char
glennrpcf002022011-01-30 02:38:15 +00006995 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00006996
glennrp5af765f2010-03-30 11:12:18 +00006997 volatile int
glennrpf09bded2011-01-08 01:15:59 +00006998 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00006999 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007000 ping_color_type,
7001 ping_interlace_method,
7002 ping_compression_method,
7003 ping_filter_method,
7004 ping_num_trans;
7005
cristybb503372010-05-27 20:51:26 +00007006 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007007 image_depth,
7008 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007009
cristybb503372010-05-27 20:51:26 +00007010 size_t
cristy3ed852e2009-09-05 21:47:34 +00007011 quality,
7012 rowbytes,
7013 save_image_depth;
7014
glennrpdfd70802010-11-14 01:23:35 +00007015 int
glennrpfd05d622011-02-25 04:10:33 +00007016 j,
glennrpf09bded2011-01-08 01:15:59 +00007017 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007018 number_opaque,
7019 number_semitransparent,
7020 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007021 ping_pHYs_unit_type;
7022
7023 png_uint_32
7024 ping_pHYs_x_resolution,
7025 ping_pHYs_y_resolution;
7026
cristy3ed852e2009-09-05 21:47:34 +00007027 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007028 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007029
glennrpb9cfe272010-12-21 15:08:06 +00007030 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7031 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007032 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007033 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007034
cristy3ed852e2009-09-05 21:47:34 +00007035#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007036 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007037#endif
7038
glennrp5af765f2010-03-30 11:12:18 +00007039 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007040 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007041 ping_color_type=0,
7042 ping_interlace_method=0,
7043 ping_compression_method=0,
7044 ping_filter_method=0,
7045 ping_num_trans = 0;
7046
7047 ping_background.red = 0;
7048 ping_background.green = 0;
7049 ping_background.blue = 0;
7050 ping_background.gray = 0;
7051 ping_background.index = 0;
7052
7053 ping_trans_color.red=0;
7054 ping_trans_color.green=0;
7055 ping_trans_color.blue=0;
7056 ping_trans_color.gray=0;
7057
glennrpdfd70802010-11-14 01:23:35 +00007058 ping_pHYs_unit_type = 0;
7059 ping_pHYs_x_resolution = 0;
7060 ping_pHYs_y_resolution = 0;
7061
glennrpda8f3a72011-02-27 23:54:12 +00007062 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007063 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007064 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007065 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007066 ping_have_bKGD=MagickFalse;
7067 ping_have_pHYs=MagickFalse;
7068 ping_have_tRNS=MagickFalse;
7069
glennrp0e8ea192010-12-24 18:00:33 +00007070 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7071 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007072 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007073 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007074 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007075 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7076 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7077 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7078 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7079 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7080 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007081 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007082 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7083 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7084 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7085
glennrp8d3d6e52011-04-19 04:39:51 +00007086 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007087 ping_need_colortype_warning = MagickFalse;
7088
glennrp8bb3a022010-12-13 20:40:04 +00007089 number_opaque = 0;
7090 number_semitransparent = 0;
7091 number_transparent = 0;
7092
glennrpfd05d622011-02-25 04:10:33 +00007093 if (logging != MagickFalse)
7094 {
7095 if (image->storage_class == UndefinedClass)
7096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7097 " storage_class=UndefinedClass");
7098 if (image->storage_class == DirectClass)
7099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7100 " storage_class=DirectClass");
7101 if (image->storage_class == PseudoClass)
7102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7103 " storage_class=PseudoClass");
7104 }
glennrp28af3712011-04-06 18:07:30 +00007105
7106 if (image->storage_class != PseudoClass && image->colormap != NULL)
7107 {
7108 /* Free the bogus colormap; it can cause trouble later */
7109 if (logging != MagickFalse)
7110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7111 " Freeing bogus colormap");
7112 (void *) RelinquishMagickMemory(image->colormap);
7113 image->colormap=NULL;
7114 }
glennrpfd05d622011-02-25 04:10:33 +00007115
cristy3ed852e2009-09-05 21:47:34 +00007116 if (image->colorspace != RGBColorspace)
7117 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007118
glennrp3241bd02010-12-12 04:36:28 +00007119 /*
7120 Sometimes we get PseudoClass images whose RGB values don't match
7121 the colors in the colormap. This code syncs the RGB values.
7122 */
7123 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7124 (void) SyncImage(image);
7125
glennrpa6a06632011-01-19 15:15:34 +00007126#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7127 if (image->depth > 8)
7128 {
7129 if (logging != MagickFalse)
7130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7131 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7132
7133 image->depth=8;
7134 }
7135#endif
7136
glennrp67b9c1a2011-04-22 18:47:36 +00007137#if 0 /* To do: Option to use the original colormap */
7138 if (ping_preserve_colormap != MagickFalse)
7139 {
7140 }
7141#endif
7142
glennrp3faa9a32011-04-23 14:00:25 +00007143#if 0 /* To do: respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007144 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7145 {
glennrp9d0ea4d2011-04-22 18:35:57 +00007146 }
glennrp67b9c1a2011-04-22 18:47:36 +00007147#endif
glennrp9d0ea4d2011-04-22 18:35:57 +00007148
glennrp67b9c1a2011-04-22 18:47:36 +00007149 /* To do: set to next higher multiple of 8 */
7150 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007151 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007152
glennrp2b013e42010-11-24 16:55:50 +00007153#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7154 /* PNG does not handle depths greater than 16 so reduce it even
7155 * if lossy
7156 */
7157 if (image->depth > 16)
7158 image->depth=16;
7159#endif
7160
glennrp3faa9a32011-04-23 14:00:25 +00007161#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007162 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007163 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007164 image->depth = 8;
7165#endif
7166
glennrpc8c2f062011-02-25 19:00:33 +00007167 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007168 * we reduce the transparency to binary and run again, then if there
7169 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7170 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7171 * palette. The final reduction can only fail if there are still 256
7172 * colors present and one of them has both transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007173 */
glennrp82b3c532011-03-22 19:20:54 +00007174
7175 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007176 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007177
glennrpd3371642011-03-22 19:42:23 +00007178 for (j=0; j<5; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007179 {
7180 /* BUILD_PALETTE
7181 *
7182 * Sometimes we get DirectClass images that have 256 colors or fewer.
7183 * This code will build a colormap.
7184 *
7185 * Also, sometimes we get PseudoClass images with an out-of-date
7186 * colormap. This code will replace the colormap with a new one.
7187 * Sometimes we get PseudoClass images that have more than 256 colors.
7188 * This code will delete the colormap and change the image to
7189 * DirectClass.
7190 *
7191 * If image->matte is MagickFalse, we ignore the opacity channel
7192 * even though it sometimes contains left-over non-opaque values.
7193 *
7194 * Also we gather some information (number of opaque, transparent,
7195 * and semitransparent pixels, and whether the image has any non-gray
7196 * pixels or only black-and-white pixels) that we might need later.
7197 *
7198 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7199 * we need to check for bogus non-opaque values, at least.
7200 */
glennrp3c218112010-11-27 15:31:26 +00007201
glennrpd71e86a2011-02-24 01:28:37 +00007202 ExceptionInfo
7203 *exception;
glennrp3c218112010-11-27 15:31:26 +00007204
glennrpd71e86a2011-02-24 01:28:37 +00007205 int
7206 n;
glennrp3c218112010-11-27 15:31:26 +00007207
glennrpd71e86a2011-02-24 01:28:37 +00007208 PixelPacket
7209 opaque[260],
7210 semitransparent[260],
7211 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007212
glennrpd71e86a2011-02-24 01:28:37 +00007213 register IndexPacket
7214 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007215
glennrpd71e86a2011-02-24 01:28:37 +00007216 register const PixelPacket
7217 *s,
7218 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007219
glennrpfd05d622011-02-25 04:10:33 +00007220 register PixelPacket
7221 *r;
7222
glennrpd71e86a2011-02-24 01:28:37 +00007223 if (logging != MagickFalse)
7224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7225 " Enter BUILD_PALETTE:");
7226
7227 if (logging != MagickFalse)
7228 {
glennrp03812ae2010-12-24 01:31:34 +00007229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007230 " image->columns=%.20g",(double) image->columns);
7231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7232 " image->rows=%.20g",(double) image->rows);
7233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7234 " image->matte=%.20g",(double) image->matte);
7235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7236 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007237
glennrpfd05d622011-02-25 04:10:33 +00007238 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007239 {
7240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007241 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007243 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007244
glennrpd71e86a2011-02-24 01:28:37 +00007245 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007246 {
glennrpd71e86a2011-02-24 01:28:37 +00007247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7248 " %d (%d,%d,%d,%d)",
7249 (int) i,
7250 (int) image->colormap[i].red,
7251 (int) image->colormap[i].green,
7252 (int) image->colormap[i].blue,
7253 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007254 }
glennrp2cc891a2010-12-24 13:44:32 +00007255
glennrpd71e86a2011-02-24 01:28:37 +00007256 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7257 {
7258 if (i > 255)
7259 {
7260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7261 " %d (%d,%d,%d,%d)",
7262 (int) i,
7263 (int) image->colormap[i].red,
7264 (int) image->colormap[i].green,
7265 (int) image->colormap[i].blue,
7266 (int) image->colormap[i].opacity);
7267 }
7268 }
glennrp03812ae2010-12-24 01:31:34 +00007269 }
glennrp7ddcc222010-12-11 05:01:05 +00007270
glennrpd71e86a2011-02-24 01:28:37 +00007271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7272 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007273
glennrpd71e86a2011-02-24 01:28:37 +00007274 if (image->colors == 0)
7275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7276 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007277
glennrp8d3d6e52011-04-19 04:39:51 +00007278 if (ping_preserve_colormap == MagickFalse)
7279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7280 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007281 }
7282
7283 exception=(&image->exception);
7284
7285 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007286 number_opaque = 0;
7287 number_semitransparent = 0;
7288 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007289
7290 for (y=0; y < (ssize_t) image->rows; y++)
7291 {
7292 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7293
7294 if (q == (PixelPacket *) NULL)
7295 break;
7296
7297 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007298 {
glennrpd71e86a2011-02-24 01:28:37 +00007299 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7300 {
7301 if (number_opaque < 259)
7302 {
7303 if (number_opaque == 0)
7304 {
glennrpca7ad3a2011-04-26 04:44:54 +00007305 opaque[0].red=GetRedPixelComponent(q);
7306 opaque[0].green=GetGreenPixelComponent(q);
7307 opaque[0].blue=GetBluePixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007308 opaque[0].opacity=OpaqueOpacity;
7309 number_opaque=1;
7310 }
glennrp2cc891a2010-12-24 13:44:32 +00007311
glennrpd71e86a2011-02-24 01:28:37 +00007312 for (i=0; i< (ssize_t) number_opaque; i++)
7313 {
glennrp0e68fac2011-04-26 04:51:47 +00007314 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007315 break;
7316 }
glennrp7ddcc222010-12-11 05:01:05 +00007317
glennrpd71e86a2011-02-24 01:28:37 +00007318 if (i == (ssize_t) number_opaque &&
7319 number_opaque < 259)
7320 {
7321 number_opaque++;
glennrpca7ad3a2011-04-26 04:44:54 +00007322 opaque[i].red=GetRedPixelComponent(q);
7323 opaque[i].green=GetGreenPixelComponent(q);
7324 opaque[i].blue=GetBluePixelComponent(q);
7325 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00007326 }
7327 }
7328 }
7329 else if (q->opacity == TransparentOpacity)
7330 {
7331 if (number_transparent < 259)
7332 {
7333 if (number_transparent == 0)
7334 {
glennrpca7ad3a2011-04-26 04:44:54 +00007335 transparent[0].red=GetRedPixelComponent(q);
7336 transparent[0].green=GetGreenPixelComponent(q);
7337 transparent[0].blue=GetBluePixelComponent(q);
7338 transparent[0].opacity=GetOpacityPixelComponent(q);
glennrpa18d5bc2011-04-23 14:51:34 +00007339 ping_trans_color.red=
7340 (unsigned short) GetRedPixelComponent(q);
7341 ping_trans_color.green=
7342 (unsigned short) GetGreenPixelComponent(q);
7343 ping_trans_color.blue=
7344 (unsigned short) GetBluePixelComponent(q);
7345 ping_trans_color.gray=
7346 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007347 number_transparent = 1;
7348 }
7349
7350 for (i=0; i< (ssize_t) number_transparent; i++)
7351 {
glennrp0e68fac2011-04-26 04:51:47 +00007352 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00007353 break;
7354 }
7355
7356 if (i == (ssize_t) number_transparent &&
7357 number_transparent < 259)
7358 {
7359 number_transparent++;
glennrpca7ad3a2011-04-26 04:44:54 +00007360 transparent[i].red=GetRedPixelComponent(q);
7361 transparent[i].green=GetGreenPixelComponent(q);
7362 transparent[i].blue=GetBluePixelComponent(q);
7363 transparent[i].opacity=GetOpacityPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007364 }
7365 }
7366 }
7367 else
7368 {
7369 if (number_semitransparent < 259)
7370 {
7371 if (number_semitransparent == 0)
7372 {
glennrpca7ad3a2011-04-26 04:44:54 +00007373 semitransparent[0].red=GetRedPixelComponent(q);
7374 semitransparent[0].green=GetGreenPixelComponent(q);
7375 semitransparent[0].blue=GetBluePixelComponent(q);
7376 semitransparent[0].opacity=GetOpacityPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007377 number_semitransparent = 1;
7378 }
7379
7380 for (i=0; i< (ssize_t) number_semitransparent; i++)
7381 {
glennrp0e68fac2011-04-26 04:51:47 +00007382 if (IsColorEqual(q, semitransparent+i)
glennrpca7ad3a2011-04-26 04:44:54 +00007383 && GetOpacityPixelComponent(q) ==
7384 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00007385 break;
7386 }
7387
7388 if (i == (ssize_t) number_semitransparent &&
7389 number_semitransparent < 259)
7390 {
7391 number_semitransparent++;
glennrpca7ad3a2011-04-26 04:44:54 +00007392 semitransparent[i].red=GetRedPixelComponent(q);
7393 semitransparent[i].green=GetGreenPixelComponent(q);
7394 semitransparent[i].blue=GetBluePixelComponent(q);
7395 semitransparent[i].opacity=GetOpacityPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007396 }
7397 }
7398 }
7399 q++;
7400 }
7401 }
7402
7403 if (ping_exclude_bKGD == MagickFalse)
7404 {
7405 /* Add the background color to the palette, if it
7406 * isn't already there.
7407 */
7408 for (i=0; i<number_opaque; i++)
7409 {
glennrpca7ad3a2011-04-26 04:44:54 +00007410 if (opaque[i].red == image->background_color.red &&
7411 opaque[i].green == image->background_color.green &&
7412 opaque[i].blue == image->background_color.blue)
7413 break;
glennrpd71e86a2011-02-24 01:28:37 +00007414 }
7415
7416 if (number_opaque < 259 && i == number_opaque)
7417 {
7418 opaque[i]=image->background_color;
7419 opaque[i].opacity = OpaqueOpacity;
7420 number_opaque++;
7421 }
glennrpa080bc32011-03-11 18:03:44 +00007422 else if (logging != MagickFalse)
7423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7424 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007425 }
7426
7427 image_colors=number_opaque+number_transparent+number_semitransparent;
7428
glennrpa080bc32011-03-11 18:03:44 +00007429 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7430 {
7431 /* No room for the background color; remove it. */
7432 number_opaque--;
7433 image_colors--;
7434 }
7435
glennrpd71e86a2011-02-24 01:28:37 +00007436 if (logging != MagickFalse)
7437 {
7438 if (image_colors > 256)
7439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7440 " image has more than 256 colors");
7441
7442 else
7443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7444 " image has %d colors",image_colors);
7445 }
7446
glennrp8d3d6e52011-04-19 04:39:51 +00007447 if (ping_preserve_colormap != MagickFalse)
7448 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007449
glennrpfd05d622011-02-25 04:10:33 +00007450 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007451 {
7452 ping_have_color=MagickFalse;
7453 ping_have_non_bw=MagickFalse;
7454
7455 if(image_colors > 256)
7456 {
7457 for (y=0; y < (ssize_t) image->rows; y++)
7458 {
7459 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7460
7461 if (q == (PixelPacket *) NULL)
7462 break;
7463
7464 /* Worst case is black-and-white; we are looking at every
7465 * pixel twice.
7466 */
7467
7468 if (ping_have_color == MagickFalse)
7469 {
7470 s=q;
7471 for (x=0; x < (ssize_t) image->columns; x++)
7472 {
glennrpa18d5bc2011-04-23 14:51:34 +00007473 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7474 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00007475 {
7476 ping_have_color=MagickTrue;
7477 ping_have_non_bw=MagickTrue;
7478 break;
7479 }
7480 s++;
7481 }
7482 }
7483
7484 if (ping_have_non_bw == MagickFalse)
7485 {
7486 s=q;
7487 for (x=0; x < (ssize_t) image->columns; x++)
7488 {
glennrpa18d5bc2011-04-23 14:51:34 +00007489 if (GetRedPixelComponent(s) != 0 &&
7490 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00007491 {
7492 ping_have_non_bw=MagickTrue;
7493 }
7494 s++;
7495 }
7496 }
7497 }
7498 }
7499 }
7500
7501 if (image_colors < 257)
7502 {
7503 PixelPacket
7504 colormap[260];
7505
7506 /*
7507 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007508 */
7509
glennrpd71e86a2011-02-24 01:28:37 +00007510 if (logging != MagickFalse)
7511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7512 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007513
glennrpd71e86a2011-02-24 01:28:37 +00007514 /* Sort palette, transparent first */;
7515
7516 n = 0;
7517
7518 for (i=0; i<number_transparent; i++)
7519 colormap[n++] = transparent[i];
7520
7521 for (i=0; i<number_semitransparent; i++)
7522 colormap[n++] = semitransparent[i];
7523
7524 for (i=0; i<number_opaque; i++)
7525 colormap[n++] = opaque[i];
7526
7527
7528 /* image_colors < 257; search the colormap instead of the pixels
7529 * to get ping_have_color and ping_have_non_bw
7530 */
7531 for (i=0; i<n; i++)
7532 {
7533 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007534 {
glennrpd71e86a2011-02-24 01:28:37 +00007535 if (colormap[i].red != colormap[i].green ||
7536 colormap[i].red != colormap[i].blue)
7537 {
7538 ping_have_color=MagickTrue;
7539 ping_have_non_bw=MagickTrue;
7540 break;
7541 }
7542 }
7543
7544 if (ping_have_non_bw == MagickFalse)
7545 {
7546 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007547 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007548 }
glennrp8bb3a022010-12-13 20:40:04 +00007549 }
7550
glennrpd71e86a2011-02-24 01:28:37 +00007551 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7552 (number_transparent == 0 && number_semitransparent == 0)) &&
7553 (((mng_info->write_png_colortype-1) ==
7554 PNG_COLOR_TYPE_PALETTE) ||
7555 (mng_info->write_png_colortype == 0)))
7556 {
glennrp6185c532011-01-14 17:58:40 +00007557 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007558 {
glennrpd71e86a2011-02-24 01:28:37 +00007559 if (n != (ssize_t) image_colors)
7560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7561 " image_colors (%d) and n (%d) don't match",
7562 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007563
glennrpd71e86a2011-02-24 01:28:37 +00007564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7565 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007566 }
glennrp03812ae2010-12-24 01:31:34 +00007567
glennrpd71e86a2011-02-24 01:28:37 +00007568 image->colors = image_colors;
7569
7570 if (AcquireImageColormap(image,image_colors) ==
7571 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007572 ThrowWriterException(ResourceLimitError,
7573 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007574
7575 for (i=0; i< (ssize_t) image_colors; i++)
7576 image->colormap[i] = colormap[i];
7577
7578 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007579 {
glennrpd71e86a2011-02-24 01:28:37 +00007580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7581 " image->colors=%d (%d)",
7582 (int) image->colors, image_colors);
7583
7584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7585 " Update the pixel indexes");
7586 }
glennrp6185c532011-01-14 17:58:40 +00007587
glennrpfd05d622011-02-25 04:10:33 +00007588 /* Sync the pixel indices with the new colormap */
7589
glennrpd71e86a2011-02-24 01:28:37 +00007590 for (y=0; y < (ssize_t) image->rows; y++)
7591 {
7592 q=GetAuthenticPixels(image,0,y,image->columns,1,
7593 exception);
glennrp6185c532011-01-14 17:58:40 +00007594
glennrpd71e86a2011-02-24 01:28:37 +00007595 if (q == (PixelPacket *) NULL)
7596 break;
glennrp6185c532011-01-14 17:58:40 +00007597
glennrpd71e86a2011-02-24 01:28:37 +00007598 indexes=GetAuthenticIndexQueue(image);
7599
7600 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007601 {
glennrpd71e86a2011-02-24 01:28:37 +00007602 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007603 {
glennrpd71e86a2011-02-24 01:28:37 +00007604 if ((image->matte == MagickFalse ||
glennrpca7ad3a2011-04-26 04:44:54 +00007605 image->colormap[i].opacity ==
7606 GetOpacityPixelComponent(q)) &&
7607 image->colormap[i].red ==
7608 GetRedPixelComponent(q) &&
7609 image->colormap[i].green ==
7610 GetGreenPixelComponent(q) &&
7611 image->colormap[i].blue ==
7612 GetBluePixelComponent(q))
glennrp6185c532011-01-14 17:58:40 +00007613 {
glennrpd71e86a2011-02-24 01:28:37 +00007614 indexes[x]=(IndexPacket) i;
7615 break;
glennrp6185c532011-01-14 17:58:40 +00007616 }
glennrp6185c532011-01-14 17:58:40 +00007617 }
glennrpd71e86a2011-02-24 01:28:37 +00007618 q++;
7619 }
glennrp6185c532011-01-14 17:58:40 +00007620
glennrpd71e86a2011-02-24 01:28:37 +00007621 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7622 break;
7623 }
7624 }
7625 }
7626
7627 if (logging != MagickFalse)
7628 {
7629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7630 " image->colors=%d", (int) image->colors);
7631
7632 if (image->colormap != NULL)
7633 {
7634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7635 " i (red,green,blue,opacity)");
7636
7637 for (i=0; i < (ssize_t) image->colors; i++)
7638 {
cristy72988482011-03-29 16:34:38 +00007639 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007640 {
7641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7642 " %d (%d,%d,%d,%d)",
7643 (int) i,
7644 (int) image->colormap[i].red,
7645 (int) image->colormap[i].green,
7646 (int) image->colormap[i].blue,
7647 (int) image->colormap[i].opacity);
7648 }
glennrp6185c532011-01-14 17:58:40 +00007649 }
7650 }
glennrp03812ae2010-12-24 01:31:34 +00007651
glennrpd71e86a2011-02-24 01:28:37 +00007652 if (number_transparent < 257)
7653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7654 " number_transparent = %d",
7655 number_transparent);
7656 else
glennrp03812ae2010-12-24 01:31:34 +00007657
glennrpd71e86a2011-02-24 01:28:37 +00007658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7659 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007660
glennrpd71e86a2011-02-24 01:28:37 +00007661 if (number_opaque < 257)
7662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7663 " number_opaque = %d",
7664 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007665
glennrpd71e86a2011-02-24 01:28:37 +00007666 else
7667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7668 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007669
glennrpd71e86a2011-02-24 01:28:37 +00007670 if (number_semitransparent < 257)
7671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7672 " number_semitransparent = %d",
7673 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007674
glennrpd71e86a2011-02-24 01:28:37 +00007675 else
7676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7677 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007678
glennrpd71e86a2011-02-24 01:28:37 +00007679 if (ping_have_non_bw == MagickFalse)
7680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7681 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007682
glennrpd71e86a2011-02-24 01:28:37 +00007683 else if (ping_have_color == MagickFalse)
7684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7685 " All pixels and the background are gray");
7686
7687 else
7688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7689 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007690
glennrp03812ae2010-12-24 01:31:34 +00007691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7692 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007693 }
glennrpfd05d622011-02-25 04:10:33 +00007694
glennrpc8c2f062011-02-25 19:00:33 +00007695 if (mng_info->write_png8 == MagickFalse)
7696 break;
glennrpfd05d622011-02-25 04:10:33 +00007697
glennrpc8c2f062011-02-25 19:00:33 +00007698 /* Make any reductions necessary for the PNG8 format */
7699 if (image_colors <= 256 &&
7700 image_colors != 0 && image->colormap != NULL &&
7701 number_semitransparent == 0 &&
7702 number_transparent <= 1)
7703 break;
7704
7705 /* PNG8 can't have semitransparent colors so we threshold the
7706 * opacity to 0 or OpaqueOpacity
7707 */
7708 if (number_semitransparent != 0)
7709 {
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " Thresholding the alpha channel to binary");
7712
7713 for (y=0; y < (ssize_t) image->rows; y++)
7714 {
7715 r=GetAuthenticPixels(image,0,y,image->columns,1,
7716 exception);
7717
7718 if (r == (PixelPacket *) NULL)
7719 break;
7720
7721 for (x=0; x < (ssize_t) image->columns; x++)
7722 {
glennrpa18d5bc2011-04-23 14:51:34 +00007723 SetOpacityPixelComponent(r,
7724 (GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7725 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00007726 r++;
7727 }
7728
7729 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7730 break;
7731
7732 if (image_colors != 0 && image_colors <= 256 &&
7733 image->colormap != NULL)
7734 for (i=0; i<image_colors; i++)
7735 image->colormap[i].opacity =
glennrp82b3c532011-03-22 19:20:54 +00007736 image->colormap[i].opacity > TransparentOpacity/2 ?
7737 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007738 }
7739 continue;
7740 }
7741
7742 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007743 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7744 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7745 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007746 */
glennrpd3371642011-03-22 19:42:23 +00007747 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7748 {
7749 if (logging != MagickFalse)
7750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7751 " Quantizing the background color to 4-4-4");
7752
7753 tried_444 = MagickTrue;
7754
7755 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007756 ScaleCharToQuantum(
7757 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7758 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007759 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007760 ScaleCharToQuantum(
7761 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7762 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007763 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007764 ScaleCharToQuantum(
7765 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7766 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007767
7768 if (logging != MagickFalse)
7769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7770 " Quantizing the pixel colors to 4-4-4");
7771
7772 if (image->colormap == NULL)
7773 {
7774 for (y=0; y < (ssize_t) image->rows; y++)
7775 {
7776 r=GetAuthenticPixels(image,0,y,image->columns,1,
7777 exception);
7778
7779 if (r == (PixelPacket *) NULL)
7780 break;
7781
7782 for (x=0; x < (ssize_t) image->columns; x++)
7783 {
7784 if (r->opacity == TransparentOpacity)
7785 {
7786 r->red = image->background_color.red;
7787 r->green = image->background_color.green;
7788 r->blue = image->background_color.blue;
7789 }
7790 else
7791 {
glennrp3faa9a32011-04-23 14:00:25 +00007792 r->red=ScaleCharToQuantum(
7793 (ScaleQuantumToChar(r->red) & 0xf0) |
7794 (ScaleQuantumToChar(r->red) & 0xf0) >> 4);
7795 r->green=ScaleCharToQuantum(
7796 (ScaleQuantumToChar(r->green) & 0xf0) |
7797 (ScaleQuantumToChar(r->green) & 0xf0) >> 4);
7798 r->blue=ScaleCharToQuantum(
7799 (ScaleQuantumToChar(r->blue) & 0xf0) |
7800 (ScaleQuantumToChar(r->blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007801 }
7802 r++;
7803 }
7804
7805 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7806 break;
7807 }
7808 }
7809
7810 else /* Should not reach this; colormap already exists and
7811 must be <= 256 */
7812 {
7813 if (logging != MagickFalse)
7814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7815 " Quantizing the colormap to 4-4-4");
7816 for (i=0; i<image_colors; i++)
7817 {
glennrp3faa9a32011-04-23 14:00:25 +00007818 image->colormap[i].red=ScaleCharToQuantum(
7819 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7820 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7821 image->colormap[i].green=ScaleCharToQuantum(
7822 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7823 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7824 image->colormap[i].blue=ScaleCharToQuantum(
7825 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7826 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
glennrpd3371642011-03-22 19:42:23 +00007827 }
7828 }
7829 continue;
7830 }
7831
glennrp82b3c532011-03-22 19:20:54 +00007832 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7833 {
7834 if (logging != MagickFalse)
7835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7836 " Quantizing the background color to 3-3-3");
7837
7838 tried_333 = MagickTrue;
7839
7840 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007841 ScaleCharToQuantum(
7842 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7843 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7844 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007845 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007846 ScaleCharToQuantum(
7847 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7848 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7849 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007850 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007851 ScaleCharToQuantum(
7852 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7853 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7854 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007855
7856 if (logging != MagickFalse)
7857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007858 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007859
7860 if (image->colormap == NULL)
7861 {
7862 for (y=0; y < (ssize_t) image->rows; y++)
7863 {
7864 r=GetAuthenticPixels(image,0,y,image->columns,1,
7865 exception);
7866
7867 if (r == (PixelPacket *) NULL)
7868 break;
7869
7870 for (x=0; x < (ssize_t) image->columns; x++)
7871 {
7872 if (r->opacity == TransparentOpacity)
7873 {
7874 r->red = image->background_color.red;
7875 r->green = image->background_color.green;
7876 r->blue = image->background_color.blue;
7877 }
7878 else
7879 {
glennrp3faa9a32011-04-23 14:00:25 +00007880 r->red=ScaleCharToQuantum(
7881 (ScaleQuantumToChar(r->red) & 0xe0) |
7882 (ScaleQuantumToChar(r->red) & 0xe0) >> 3 |
7883 (ScaleQuantumToChar(r->red) & 0xc0) >> 6);
7884 r->green=ScaleCharToQuantum(
7885 (ScaleQuantumToChar(r->green) & 0xe0) |
7886 (ScaleQuantumToChar(r->green) & 0xe0) >> 3 |
7887 (ScaleQuantumToChar(r->green) & 0xc0) >> 6);
7888 r->blue=ScaleCharToQuantum(
7889 (ScaleQuantumToChar(r->blue) & 0xe0) |
7890 (ScaleQuantumToChar(r->blue) & 0xe0) >> 3 |
7891 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007892 }
7893 r++;
7894 }
7895
7896 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7897 break;
7898 }
7899 }
7900
7901 else /* Should not reach this; colormap already exists and
7902 must be <= 256 */
7903 {
7904 if (logging != MagickFalse)
7905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007906 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007907 for (i=0; i<image_colors; i++)
7908 {
glennrp3faa9a32011-04-23 14:00:25 +00007909 image->colormap[i].red=ScaleCharToQuantum(
7910 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7911 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7912 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7913 image->colormap[i].green=ScaleCharToQuantum(
7914 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7915 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7916 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7917 image->colormap[i].blue=ScaleCharToQuantum(
7918 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7919 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7920 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007921 }
glennrpd3371642011-03-22 19:42:23 +00007922 }
7923 continue;
glennrp82b3c532011-03-22 19:20:54 +00007924 }
glennrpc8c2f062011-02-25 19:00:33 +00007925
glennrpc8c2f062011-02-25 19:00:33 +00007926 if (image_colors == 0 || image_colors > 256)
7927 {
7928 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007930 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007931
glennrp3faa9a32011-04-23 14:00:25 +00007932 /* Red and green were already done so we only quantize the blue
7933 * channel
7934 */
7935
7936 image->background_color.blue=ScaleCharToQuantum(
7937 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7938 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7939 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7940 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrpfd05d622011-02-25 04:10:33 +00007941
glennrpc8c2f062011-02-25 19:00:33 +00007942 if (logging != MagickFalse)
7943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007944 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007945
glennrpc8c2f062011-02-25 19:00:33 +00007946 if (image->colormap == NULL)
7947 {
7948 for (y=0; y < (ssize_t) image->rows; y++)
7949 {
7950 r=GetAuthenticPixels(image,0,y,image->columns,1,
7951 exception);
7952
7953 if (r == (PixelPacket *) NULL)
7954 break;
7955
7956 for (x=0; x < (ssize_t) image->columns; x++)
7957 {
glennrp82b3c532011-03-22 19:20:54 +00007958 if (r->opacity == TransparentOpacity)
7959 {
7960 r->red = image->background_color.red;
7961 r->green = image->background_color.green;
7962 r->blue = image->background_color.blue;
7963 }
7964 else
7965 {
glennrp3faa9a32011-04-23 14:00:25 +00007966 r->blue=ScaleCharToQuantum(
7967 (ScaleQuantumToChar(r->blue) & 0xc0) |
7968 (ScaleQuantumToChar(r->blue) & 0xc0) >> 2 |
7969 (ScaleQuantumToChar(r->blue) & 0xc0) >> 4 |
7970 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007971 }
glennrp52a479c2011-02-26 21:14:38 +00007972 r++;
glennrpc8c2f062011-02-25 19:00:33 +00007973 }
glennrpfd05d622011-02-25 04:10:33 +00007974
glennrpc8c2f062011-02-25 19:00:33 +00007975 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7976 break;
7977 }
7978 }
glennrpfd05d622011-02-25 04:10:33 +00007979
glennrpc8c2f062011-02-25 19:00:33 +00007980 else /* Should not reach this; colormap already exists and
7981 must be <= 256 */
7982 {
7983 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007985 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00007986 for (i=0; i<image_colors; i++)
7987 {
glennrp3faa9a32011-04-23 14:00:25 +00007988 image->colormap[i].blue=ScaleCharToQuantum(
7989 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
7990 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
7991 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
7992 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrpc8c2f062011-02-25 19:00:33 +00007993 }
7994 }
7995 continue;
7996 }
7997 break;
glennrpd71e86a2011-02-24 01:28:37 +00007998 }
glennrpfd05d622011-02-25 04:10:33 +00007999 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008000
glennrpfd05d622011-02-25 04:10:33 +00008001 /* If we are excluding the tRNS chunk and there is transparency,
8002 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8003 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008004 */
glennrp0e8ea192010-12-24 18:00:33 +00008005 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8006 (number_transparent != 0 || number_semitransparent != 0))
8007 {
8008 int colortype=mng_info->write_png_colortype;
8009
8010 if (ping_have_color == MagickFalse)
8011 mng_info->write_png_colortype = 5;
8012
8013 else
8014 mng_info->write_png_colortype = 7;
8015
glennrp8d579662011-02-23 02:05:02 +00008016 if (colortype != 0 &&
8017 mng_info->write_png_colortype != (ssize_t) colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008018 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008019
glennrp0e8ea192010-12-24 18:00:33 +00008020 }
8021
glennrpfd05d622011-02-25 04:10:33 +00008022 /* See if cheap transparency is possible. It is only possible
8023 * when there is a single transparent color, no semitransparent
8024 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008025 * as the transparent color. We only need this information if
8026 * we are writing a PNG with colortype 0 or 2, and we have not
8027 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008028 */
glennrp5a39f372011-02-25 04:52:16 +00008029 if (number_transparent == 1 &&
8030 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008031 {
8032 ping_have_cheap_transparency = MagickTrue;
8033
8034 if (number_semitransparent != 0)
8035 ping_have_cheap_transparency = MagickFalse;
8036
8037 else if (image_colors == 0 || image_colors > 256 ||
8038 image->colormap == NULL)
8039 {
8040 ExceptionInfo
8041 *exception;
8042
8043 register const PixelPacket
8044 *q;
8045
8046 exception=(&image->exception);
8047
8048 for (y=0; y < (ssize_t) image->rows; y++)
8049 {
8050 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8051
8052 if (q == (PixelPacket *) NULL)
8053 break;
8054
8055 for (x=0; x < (ssize_t) image->columns; x++)
8056 {
8057 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008058 (unsigned short) GetRedPixelComponent(q) ==
8059 ping_trans_color.red &&
8060 (unsigned short) GetGreenPixelComponent(q) ==
8061 ping_trans_color.green &&
8062 (unsigned short) GetBluePixelComponent(q) ==
8063 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008064 {
8065 ping_have_cheap_transparency = MagickFalse;
8066 break;
8067 }
8068
8069 q++;
8070 }
8071
8072 if (ping_have_cheap_transparency == MagickFalse)
8073 break;
8074 }
8075 }
8076 else
8077 {
glennrp67b9c1a2011-04-22 18:47:36 +00008078 /* Assuming that image->colormap[0] is the one transparent color
8079 * and that all others are opaque.
8080 */
glennrpfd05d622011-02-25 04:10:33 +00008081 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008082 for (i=1; i<image_colors; i++)
8083 if (image->colormap[i].red == image->colormap[0].red &&
8084 image->colormap[i].green == image->colormap[0].green &&
8085 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008086 {
glennrp67b9c1a2011-04-22 18:47:36 +00008087 ping_have_cheap_transparency = MagickFalse;
8088 break;
glennrpfd05d622011-02-25 04:10:33 +00008089 }
8090 }
8091
8092 if (logging != MagickFalse)
8093 {
8094 if (ping_have_cheap_transparency == MagickFalse)
8095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8096 " Cheap transparency is not possible.");
8097
8098 else
8099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8100 " Cheap transparency is possible.");
8101 }
8102 }
8103 else
8104 ping_have_cheap_transparency = MagickFalse;
8105
glennrp8640fb52010-11-23 15:48:26 +00008106 image_depth=image->depth;
8107
glennrp26c990a2010-11-23 02:23:20 +00008108 quantum_info = (QuantumInfo *) NULL;
8109 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008110 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008111 image_matte=image->matte;
8112
glennrp0fe50b42010-11-16 03:52:51 +00008113 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008114 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008115
glennrp52a479c2011-02-26 21:14:38 +00008116 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8117 (image->colors == 0 || image->colormap == NULL))
8118 {
glennrp52a479c2011-02-26 21:14:38 +00008119 image_info=DestroyImageInfo(image_info);
8120 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008121 (void) ThrowMagickException(&IMimage->exception,
8122 GetMagickModule(),CoderError,
8123 "Cannot write PNG8 or color-type 3; colormap is NULL",
8124 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008125#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8126 UnlockSemaphoreInfo(ping_semaphore);
8127#endif
8128 return(MagickFalse);
8129 }
8130
cristy3ed852e2009-09-05 21:47:34 +00008131 /*
8132 Allocate the PNG structures
8133 */
8134#ifdef PNG_USER_MEM_SUPPORTED
8135 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008136 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8137 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008138
cristy3ed852e2009-09-05 21:47:34 +00008139#else
8140 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008141 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008142
cristy3ed852e2009-09-05 21:47:34 +00008143#endif
8144 if (ping == (png_struct *) NULL)
8145 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008146
cristy3ed852e2009-09-05 21:47:34 +00008147 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008148
cristy3ed852e2009-09-05 21:47:34 +00008149 if (ping_info == (png_info *) NULL)
8150 {
8151 png_destroy_write_struct(&ping,(png_info **) NULL);
8152 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8153 }
glennrp0fe50b42010-11-16 03:52:51 +00008154
cristy3ed852e2009-09-05 21:47:34 +00008155 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008156 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008157
glennrp5af765f2010-03-30 11:12:18 +00008158 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008159 {
8160 /*
8161 PNG write failed.
8162 */
8163#ifdef PNG_DEBUG
8164 if (image_info->verbose)
8165 (void) printf("PNG write has failed.\n");
8166#endif
8167 png_destroy_write_struct(&ping,&ping_info);
8168#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008169 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008170#endif
glennrpda8f3a72011-02-27 23:54:12 +00008171 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008172 (void) CloseBlob(image);
8173 image_info=DestroyImageInfo(image_info);
8174 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008175 return(MagickFalse);
8176 }
8177 /*
8178 Prepare PNG for writing.
8179 */
8180#if defined(PNG_MNG_FEATURES_SUPPORTED)
8181 if (mng_info->write_mng)
8182 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008183
cristy3ed852e2009-09-05 21:47:34 +00008184#else
8185# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8186 if (mng_info->write_mng)
8187 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008188
cristy3ed852e2009-09-05 21:47:34 +00008189# endif
8190#endif
glennrp2b013e42010-11-24 16:55:50 +00008191
cristy3ed852e2009-09-05 21:47:34 +00008192 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008193
cristy4e5bc842010-06-09 13:56:01 +00008194 ping_width=(png_uint_32) image->columns;
8195 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008196
cristy3ed852e2009-09-05 21:47:34 +00008197 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8198 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008199
cristy3ed852e2009-09-05 21:47:34 +00008200 if (mng_info->write_png_depth != 0)
8201 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008202
cristy3ed852e2009-09-05 21:47:34 +00008203 /* Adjust requested depth to next higher valid depth if necessary */
8204 if (image_depth > 8)
8205 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008206
cristy3ed852e2009-09-05 21:47:34 +00008207 if ((image_depth > 4) && (image_depth < 8))
8208 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008209
cristy3ed852e2009-09-05 21:47:34 +00008210 if (image_depth == 3)
8211 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008212
cristy3ed852e2009-09-05 21:47:34 +00008213 if (logging != MagickFalse)
8214 {
8215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008216 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008218 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008220 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008222 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008224 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008225 }
glennrp8640fb52010-11-23 15:48:26 +00008226
cristy3ed852e2009-09-05 21:47:34 +00008227 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008228 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008229
glennrp26f37912010-12-23 16:22:42 +00008230
cristy3ed852e2009-09-05 21:47:34 +00008231#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008232 if (ping_exclude_pHYs == MagickFalse)
8233 {
cristy3ed852e2009-09-05 21:47:34 +00008234 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8235 (!mng_info->write_mng || !mng_info->equal_physs))
8236 {
glennrp0fe50b42010-11-16 03:52:51 +00008237 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008240
8241 if (image->units == PixelsPerInchResolution)
8242 {
glennrpdfd70802010-11-14 01:23:35 +00008243 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008244 ping_pHYs_x_resolution=
8245 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8246 ping_pHYs_y_resolution=
8247 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008248 }
glennrpdfd70802010-11-14 01:23:35 +00008249
cristy3ed852e2009-09-05 21:47:34 +00008250 else if (image->units == PixelsPerCentimeterResolution)
8251 {
glennrpdfd70802010-11-14 01:23:35 +00008252 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008253 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8254 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008255 }
glennrp991d11d2010-11-12 21:55:28 +00008256
cristy3ed852e2009-09-05 21:47:34 +00008257 else
8258 {
glennrpdfd70802010-11-14 01:23:35 +00008259 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8260 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8261 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008262 }
glennrp991d11d2010-11-12 21:55:28 +00008263
glennrp823b55c2011-03-14 18:46:46 +00008264 if (logging != MagickFalse)
8265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8266 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8267 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8268 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008269 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008270 }
glennrp26f37912010-12-23 16:22:42 +00008271 }
cristy3ed852e2009-09-05 21:47:34 +00008272#endif
glennrpa521b2f2010-10-29 04:11:03 +00008273
glennrp26f37912010-12-23 16:22:42 +00008274 if (ping_exclude_bKGD == MagickFalse)
8275 {
glennrpa521b2f2010-10-29 04:11:03 +00008276 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008277 {
glennrpa521b2f2010-10-29 04:11:03 +00008278 unsigned int
8279 mask;
cristy3ed852e2009-09-05 21:47:34 +00008280
glennrpa521b2f2010-10-29 04:11:03 +00008281 mask=0xffff;
8282 if (ping_bit_depth == 8)
8283 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008284
glennrpa521b2f2010-10-29 04:11:03 +00008285 if (ping_bit_depth == 4)
8286 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008287
glennrpa521b2f2010-10-29 04:11:03 +00008288 if (ping_bit_depth == 2)
8289 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008290
glennrpa521b2f2010-10-29 04:11:03 +00008291 if (ping_bit_depth == 1)
8292 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008293
glennrpa521b2f2010-10-29 04:11:03 +00008294 ping_background.red=(png_uint_16)
8295 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008296
glennrpa521b2f2010-10-29 04:11:03 +00008297 ping_background.green=(png_uint_16)
8298 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008299
glennrpa521b2f2010-10-29 04:11:03 +00008300 ping_background.blue=(png_uint_16)
8301 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008302 }
cristy3ed852e2009-09-05 21:47:34 +00008303
glennrp0fe50b42010-11-16 03:52:51 +00008304 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008305 {
8306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8307 " Setting up bKGD chunk (1)");
8308
8309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8310 " ping_bit_depth=%d",ping_bit_depth);
8311 }
glennrp0fe50b42010-11-16 03:52:51 +00008312
8313 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008314 }
glennrp0fe50b42010-11-16 03:52:51 +00008315
cristy3ed852e2009-09-05 21:47:34 +00008316 /*
8317 Select the color type.
8318 */
8319 matte=image_matte;
8320 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008321
glennrp1273f7b2011-02-24 03:20:30 +00008322 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008323 {
glennrp0fe50b42010-11-16 03:52:51 +00008324
glennrpfd05d622011-02-25 04:10:33 +00008325 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008326 for reducing the sample depth from 8. */
8327
glennrp0fe50b42010-11-16 03:52:51 +00008328 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008329
glennrp8bb3a022010-12-13 20:40:04 +00008330 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008331
8332 /*
8333 Set image palette.
8334 */
8335 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8336
glennrp0fe50b42010-11-16 03:52:51 +00008337 if (logging != MagickFalse)
8338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8339 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008340 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008341
8342 for (i=0; i < (ssize_t) number_colors; i++)
8343 {
8344 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8345 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8346 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8347 if (logging != MagickFalse)
8348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008349#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008350 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008351#else
8352 " %5ld (%5d,%5d,%5d)",
8353#endif
glennrp0fe50b42010-11-16 03:52:51 +00008354 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8355
8356 }
glennrp2b013e42010-11-24 16:55:50 +00008357
glennrp8bb3a022010-12-13 20:40:04 +00008358 ping_have_PLTE=MagickTrue;
8359 image_depth=ping_bit_depth;
8360 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008361
glennrp58e01762011-01-07 15:28:54 +00008362 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008363 {
glennrp0fe50b42010-11-16 03:52:51 +00008364 /*
8365 Identify which colormap entry is transparent.
8366 */
8367 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008368 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008369
glennrp8bb3a022010-12-13 20:40:04 +00008370 for (i=0; i < (ssize_t) number_transparent; i++)
8371 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008372
glennrp0fe50b42010-11-16 03:52:51 +00008373
glennrp2cc891a2010-12-24 13:44:32 +00008374 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008375 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008376
8377 if (ping_num_trans == 0)
8378 ping_have_tRNS=MagickFalse;
8379
glennrp8bb3a022010-12-13 20:40:04 +00008380 else
8381 ping_have_tRNS=MagickTrue;
8382 }
glennrp0fe50b42010-11-16 03:52:51 +00008383
glennrp1273f7b2011-02-24 03:20:30 +00008384 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008385 {
glennrp1273f7b2011-02-24 03:20:30 +00008386 /*
8387 * Identify which colormap entry is the background color.
8388 */
8389
glennrp4f25bd02011-01-01 18:51:28 +00008390 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8391 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8392 break;
glennrp0fe50b42010-11-16 03:52:51 +00008393
glennrp4f25bd02011-01-01 18:51:28 +00008394 ping_background.index=(png_byte) i;
8395 }
cristy3ed852e2009-09-05 21:47:34 +00008396 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008397
cristy3ed852e2009-09-05 21:47:34 +00008398 else if (mng_info->write_png24)
8399 {
8400 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008401 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008402 }
glennrp0fe50b42010-11-16 03:52:51 +00008403
cristy3ed852e2009-09-05 21:47:34 +00008404 else if (mng_info->write_png32)
8405 {
8406 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008407 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008408 }
glennrp0fe50b42010-11-16 03:52:51 +00008409
glennrp8bb3a022010-12-13 20:40:04 +00008410 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008411 {
glennrp5af765f2010-03-30 11:12:18 +00008412 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008413
glennrp8bb3a022010-12-13 20:40:04 +00008414 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008415 {
glennrp5af765f2010-03-30 11:12:18 +00008416 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008417
glennrp5af765f2010-03-30 11:12:18 +00008418 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8419 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008420 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008421
glennrp8bb3a022010-12-13 20:40:04 +00008422 else
8423 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008424
8425 if (logging != MagickFalse)
8426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8427 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008428 }
glennrp0fe50b42010-11-16 03:52:51 +00008429
glennrp7c4c9e62011-03-21 20:23:32 +00008430 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008431 {
8432 if (logging != MagickFalse)
8433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008434 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008435
glennrpd6bf1612010-12-17 17:28:54 +00008436 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008437 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008438
glennrpd6bf1612010-12-17 17:28:54 +00008439 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008440 {
glennrp5af765f2010-03-30 11:12:18 +00008441 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008442 image_matte=MagickFalse;
8443 }
glennrp0fe50b42010-11-16 03:52:51 +00008444
glennrpd6bf1612010-12-17 17:28:54 +00008445 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008446 {
glennrp5af765f2010-03-30 11:12:18 +00008447 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008448 image_matte=MagickTrue;
8449 }
glennrp0fe50b42010-11-16 03:52:51 +00008450
glennrp5aa37f62011-01-02 03:07:57 +00008451 if (image_info->type == PaletteType ||
8452 image_info->type == PaletteMatteType)
8453 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8454
glennrp7c4c9e62011-03-21 20:23:32 +00008455 if (mng_info->write_png_colortype == 0 &&
8456 (image_info->type == UndefinedType ||
8457 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008458 {
glennrp5aa37f62011-01-02 03:07:57 +00008459 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008460 {
glennrp5aa37f62011-01-02 03:07:57 +00008461 if (image_matte == MagickFalse)
8462 {
8463 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8464 image_matte=MagickFalse;
8465 }
glennrp0fe50b42010-11-16 03:52:51 +00008466
glennrp0b206f52011-01-07 04:55:32 +00008467 else
glennrp5aa37f62011-01-02 03:07:57 +00008468 {
8469 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8470 image_matte=MagickTrue;
8471 }
8472 }
8473 else
glennrp8bb3a022010-12-13 20:40:04 +00008474 {
glennrp5aa37f62011-01-02 03:07:57 +00008475 if (image_matte == MagickFalse)
8476 {
8477 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8478 image_matte=MagickFalse;
8479 }
glennrp8bb3a022010-12-13 20:40:04 +00008480
glennrp0b206f52011-01-07 04:55:32 +00008481 else
glennrp5aa37f62011-01-02 03:07:57 +00008482 {
8483 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8484 image_matte=MagickTrue;
8485 }
8486 }
glennrp0fe50b42010-11-16 03:52:51 +00008487 }
glennrp5aa37f62011-01-02 03:07:57 +00008488
cristy3ed852e2009-09-05 21:47:34 +00008489 }
glennrp0fe50b42010-11-16 03:52:51 +00008490
cristy3ed852e2009-09-05 21:47:34 +00008491 if (logging != MagickFalse)
8492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008493 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008494
glennrp5af765f2010-03-30 11:12:18 +00008495 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008496 {
8497 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8498 ping_color_type == PNG_COLOR_TYPE_RGB ||
8499 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8500 ping_bit_depth=8;
8501 }
cristy3ed852e2009-09-05 21:47:34 +00008502
glennrpd6bf1612010-12-17 17:28:54 +00008503 old_bit_depth=ping_bit_depth;
8504
glennrp5af765f2010-03-30 11:12:18 +00008505 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008506 {
glennrp8d579662011-02-23 02:05:02 +00008507 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8508 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008509 }
glennrp8640fb52010-11-23 15:48:26 +00008510
glennrp5af765f2010-03-30 11:12:18 +00008511 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008512 {
cristy35ef8242010-06-03 16:24:13 +00008513 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008514 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008515
8516 if (image->colors == 0)
8517 {
glennrp0fe50b42010-11-16 03:52:51 +00008518 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008519 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008520 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008521 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008522 }
8523
cristy35ef8242010-06-03 16:24:13 +00008524 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008525 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008526 }
glennrp2b013e42010-11-24 16:55:50 +00008527
glennrpd6bf1612010-12-17 17:28:54 +00008528 if (logging != MagickFalse)
8529 {
8530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8531 " Number of colors: %.20g",(double) image_colors);
8532
8533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8534 " Tentative PNG bit depth: %d",ping_bit_depth);
8535 }
8536
8537 if (ping_bit_depth < (int) mng_info->write_png_depth)
8538 ping_bit_depth = mng_info->write_png_depth;
8539 }
glennrp2cc891a2010-12-24 13:44:32 +00008540
glennrp5af765f2010-03-30 11:12:18 +00008541 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008542
cristy3ed852e2009-09-05 21:47:34 +00008543 if (logging != MagickFalse)
8544 {
8545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008546 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008547
cristy3ed852e2009-09-05 21:47:34 +00008548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008549 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008550
cristy3ed852e2009-09-05 21:47:34 +00008551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008552 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008553
cristy3ed852e2009-09-05 21:47:34 +00008554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008555
glennrp8640fb52010-11-23 15:48:26 +00008556 " image->depth: %.20g",(double) image->depth);
8557
8558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008559 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008560 }
8561
glennrp58e01762011-01-07 15:28:54 +00008562 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008563 {
glennrp4f25bd02011-01-01 18:51:28 +00008564 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008565 {
glennrp7c4c9e62011-03-21 20:23:32 +00008566 if (mng_info->write_png_colortype == 0)
8567 {
8568 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008569
glennrp7c4c9e62011-03-21 20:23:32 +00008570 if (ping_have_color != MagickFalse)
8571 ping_color_type=PNG_COLOR_TYPE_RGBA;
8572 }
glennrp4f25bd02011-01-01 18:51:28 +00008573
8574 /*
8575 * Determine if there is any transparent color.
8576 */
8577 if (number_transparent + number_semitransparent == 0)
8578 {
8579 /*
8580 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8581 */
glennrpa6a06632011-01-19 15:15:34 +00008582
glennrp4f25bd02011-01-01 18:51:28 +00008583 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008584
8585 if (mng_info->write_png_colortype == 0)
8586 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008587 }
8588
8589 else
8590 {
8591 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008592 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008593
8594 mask=0xffff;
8595
8596 if (ping_bit_depth == 8)
8597 mask=0x00ff;
8598
8599 if (ping_bit_depth == 4)
8600 mask=0x000f;
8601
8602 if (ping_bit_depth == 2)
8603 mask=0x0003;
8604
8605 if (ping_bit_depth == 1)
8606 mask=0x0001;
8607
8608 ping_trans_color.red=(png_uint_16)
8609 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8610
8611 ping_trans_color.green=(png_uint_16)
8612 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8613
8614 ping_trans_color.blue=(png_uint_16)
8615 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8616
8617 ping_trans_color.gray=(png_uint_16)
8618 (ScaleQuantumToShort(PixelIntensityToQuantum(
8619 image->colormap)) & mask);
8620
8621 ping_trans_color.index=(png_byte) 0;
8622
8623 ping_have_tRNS=MagickTrue;
8624 }
8625
8626 if (ping_have_tRNS != MagickFalse)
8627 {
8628 /*
glennrpfd05d622011-02-25 04:10:33 +00008629 * Determine if there is one and only one transparent color
8630 * and if so if it is fully transparent.
8631 */
8632 if (ping_have_cheap_transparency == MagickFalse)
8633 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008634 }
8635
8636 if (ping_have_tRNS != MagickFalse)
8637 {
glennrp7c4c9e62011-03-21 20:23:32 +00008638 if (mng_info->write_png_colortype == 0)
8639 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008640
8641 if (image_depth == 8)
8642 {
8643 ping_trans_color.red&=0xff;
8644 ping_trans_color.green&=0xff;
8645 ping_trans_color.blue&=0xff;
8646 ping_trans_color.gray&=0xff;
8647 }
8648 }
8649 }
cristy3ed852e2009-09-05 21:47:34 +00008650 else
8651 {
cristy3ed852e2009-09-05 21:47:34 +00008652 if (image_depth == 8)
8653 {
glennrp5af765f2010-03-30 11:12:18 +00008654 ping_trans_color.red&=0xff;
8655 ping_trans_color.green&=0xff;
8656 ping_trans_color.blue&=0xff;
8657 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008658 }
8659 }
8660 }
glennrp8640fb52010-11-23 15:48:26 +00008661
cristy3ed852e2009-09-05 21:47:34 +00008662 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008663
glennrp2e09f552010-11-14 00:38:48 +00008664 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008665 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008666
glennrp39992b42010-11-14 00:03:43 +00008667 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008668 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008669 ping_have_color == MagickFalse &&
8670 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008671 {
cristy35ef8242010-06-03 16:24:13 +00008672 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008673
cristy3ed852e2009-09-05 21:47:34 +00008674 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008675 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008676
glennrp7c4c9e62011-03-21 20:23:32 +00008677 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008678 {
glennrp5af765f2010-03-30 11:12:18 +00008679 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008680
cristy3ed852e2009-09-05 21:47:34 +00008681 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008682 {
8683 if (logging != MagickFalse)
8684 {
8685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8686 " Scaling ping_trans_color (0)");
8687 }
8688 ping_trans_color.gray*=0x0101;
8689 }
cristy3ed852e2009-09-05 21:47:34 +00008690 }
glennrp0fe50b42010-11-16 03:52:51 +00008691
cristy3ed852e2009-09-05 21:47:34 +00008692 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8693 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008694
cristy85380932011-04-25 17:11:52 +00008695 if ((image_colors == 0) || ((ssize_t) (image_colors-1) > MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008696 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008697
cristy3ed852e2009-09-05 21:47:34 +00008698 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008699 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008700
cristy3ed852e2009-09-05 21:47:34 +00008701 else
8702 {
glennrp5af765f2010-03-30 11:12:18 +00008703 ping_bit_depth=8;
8704 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008705 {
8706 if(!mng_info->write_png_depth)
8707 {
glennrp5af765f2010-03-30 11:12:18 +00008708 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008709
cristy35ef8242010-06-03 16:24:13 +00008710 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008711 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008712 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008713 }
8714 }
glennrp2b013e42010-11-24 16:55:50 +00008715
glennrp0fe50b42010-11-16 03:52:51 +00008716 else if (ping_color_type ==
8717 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008718 mng_info->IsPalette)
8719 {
cristy3ed852e2009-09-05 21:47:34 +00008720 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008721
cristy3ed852e2009-09-05 21:47:34 +00008722 int
8723 depth_4_ok=MagickTrue,
8724 depth_2_ok=MagickTrue,
8725 depth_1_ok=MagickTrue;
8726
cristybb503372010-05-27 20:51:26 +00008727 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008728 {
8729 unsigned char
8730 intensity;
8731
8732 intensity=ScaleQuantumToChar(image->colormap[i].red);
8733
8734 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8735 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8736 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8737 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008738 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008739 depth_1_ok=MagickFalse;
8740 }
glennrp2b013e42010-11-24 16:55:50 +00008741
cristy3ed852e2009-09-05 21:47:34 +00008742 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008743 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008744
cristy3ed852e2009-09-05 21:47:34 +00008745 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008746 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008747
cristy3ed852e2009-09-05 21:47:34 +00008748 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008749 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008750 }
8751 }
glennrp2b013e42010-11-24 16:55:50 +00008752
glennrp5af765f2010-03-30 11:12:18 +00008753 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008754 }
glennrp0fe50b42010-11-16 03:52:51 +00008755
cristy3ed852e2009-09-05 21:47:34 +00008756 else
glennrp0fe50b42010-11-16 03:52:51 +00008757
cristy3ed852e2009-09-05 21:47:34 +00008758 if (mng_info->IsPalette)
8759 {
glennrp17a14852010-05-10 03:01:59 +00008760 number_colors=image_colors;
8761
cristy3ed852e2009-09-05 21:47:34 +00008762 if (image_depth <= 8)
8763 {
cristy3ed852e2009-09-05 21:47:34 +00008764 /*
8765 Set image palette.
8766 */
glennrp5af765f2010-03-30 11:12:18 +00008767 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008768
glennrp58e01762011-01-07 15:28:54 +00008769 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008770 {
glennrp9c1eb072010-06-06 22:19:15 +00008771 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008772
glennrp3b51f0e2010-11-27 18:14:08 +00008773 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8775 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008776 }
glennrp0fe50b42010-11-16 03:52:51 +00008777
cristy3ed852e2009-09-05 21:47:34 +00008778 else
8779 {
cristybb503372010-05-27 20:51:26 +00008780 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008781 {
8782 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8783 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8784 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8785 }
glennrp0fe50b42010-11-16 03:52:51 +00008786
glennrp3b51f0e2010-11-27 18:14:08 +00008787 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008789 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008790 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008791
glennrp39992b42010-11-14 00:03:43 +00008792 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008793 }
glennrp0fe50b42010-11-16 03:52:51 +00008794
cristy3ed852e2009-09-05 21:47:34 +00008795 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008796 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008797 {
cristybefe4d22010-06-07 01:18:58 +00008798 size_t
8799 one;
8800
glennrp5af765f2010-03-30 11:12:18 +00008801 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008802 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008803
cristybefe4d22010-06-07 01:18:58 +00008804 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008805 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008806 }
glennrp0fe50b42010-11-16 03:52:51 +00008807
glennrp5af765f2010-03-30 11:12:18 +00008808 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008809
glennrp58e01762011-01-07 15:28:54 +00008810 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008811 {
glennrp0fe50b42010-11-16 03:52:51 +00008812 /*
glennrpd6bf1612010-12-17 17:28:54 +00008813 * Set up trans_colors array.
8814 */
glennrp0fe50b42010-11-16 03:52:51 +00008815 assert(number_colors <= 256);
8816
glennrpd6bf1612010-12-17 17:28:54 +00008817 ping_num_trans=(unsigned short) (number_transparent +
8818 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008819
8820 if (ping_num_trans == 0)
8821 ping_have_tRNS=MagickFalse;
8822
glennrpd6bf1612010-12-17 17:28:54 +00008823 else
glennrp0fe50b42010-11-16 03:52:51 +00008824 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008825 if (logging != MagickFalse)
8826 {
8827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8828 " Scaling ping_trans_color (1)");
8829 }
glennrpd6bf1612010-12-17 17:28:54 +00008830 ping_have_tRNS=MagickTrue;
8831
8832 for (i=0; i < ping_num_trans; i++)
8833 {
8834 ping_trans_alpha[i]= (png_byte) (255-
8835 ScaleQuantumToChar(image->colormap[i].opacity));
8836 }
glennrp0fe50b42010-11-16 03:52:51 +00008837 }
8838 }
cristy3ed852e2009-09-05 21:47:34 +00008839 }
8840 }
glennrp0fe50b42010-11-16 03:52:51 +00008841
cristy3ed852e2009-09-05 21:47:34 +00008842 else
8843 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008844
cristy3ed852e2009-09-05 21:47:34 +00008845 if (image_depth < 8)
8846 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008847
cristy3ed852e2009-09-05 21:47:34 +00008848 if ((save_image_depth == 16) && (image_depth == 8))
8849 {
glennrp4f25bd02011-01-01 18:51:28 +00008850 if (logging != MagickFalse)
8851 {
8852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8853 " Scaling ping_trans_color from (%d,%d,%d)",
8854 (int) ping_trans_color.red,
8855 (int) ping_trans_color.green,
8856 (int) ping_trans_color.blue);
8857 }
8858
glennrp5af765f2010-03-30 11:12:18 +00008859 ping_trans_color.red*=0x0101;
8860 ping_trans_color.green*=0x0101;
8861 ping_trans_color.blue*=0x0101;
8862 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008863
8864 if (logging != MagickFalse)
8865 {
8866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8867 " to (%d,%d,%d)",
8868 (int) ping_trans_color.red,
8869 (int) ping_trans_color.green,
8870 (int) ping_trans_color.blue);
8871 }
cristy3ed852e2009-09-05 21:47:34 +00008872 }
8873 }
8874
cristy4383ec82011-01-05 15:42:32 +00008875 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8876 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008877
cristy3ed852e2009-09-05 21:47:34 +00008878 /*
8879 Adjust background and transparency samples in sub-8-bit grayscale files.
8880 */
glennrp5af765f2010-03-30 11:12:18 +00008881 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008882 PNG_COLOR_TYPE_GRAY)
8883 {
8884 png_uint_16
8885 maxval;
8886
cristy35ef8242010-06-03 16:24:13 +00008887 size_t
8888 one=1;
8889
cristy22ffd972010-06-03 16:51:47 +00008890 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008891
glennrp4f25bd02011-01-01 18:51:28 +00008892 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008893 {
cristy3ed852e2009-09-05 21:47:34 +00008894
glennrpa521b2f2010-10-29 04:11:03 +00008895 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008896 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8897
8898 if (logging != MagickFalse)
8899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008900 " Setting up bKGD chunk (2)");
glennrp3b51f0e2010-11-27 18:14:08 +00008901
glennrp991d11d2010-11-12 21:55:28 +00008902 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008903 }
cristy3ed852e2009-09-05 21:47:34 +00008904
glennrp5af765f2010-03-30 11:12:18 +00008905 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8906 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008907 }
glennrp17a14852010-05-10 03:01:59 +00008908
glennrp26f37912010-12-23 16:22:42 +00008909 if (ping_exclude_bKGD == MagickFalse)
8910 {
glennrp1273f7b2011-02-24 03:20:30 +00008911 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008912 {
8913 /*
8914 Identify which colormap entry is the background color.
8915 */
8916
glennrp17a14852010-05-10 03:01:59 +00008917 number_colors=image_colors;
8918
glennrpa521b2f2010-10-29 04:11:03 +00008919 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8920 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008921 break;
8922
8923 ping_background.index=(png_byte) i;
8924
glennrp3b51f0e2010-11-27 18:14:08 +00008925 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008926 {
8927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008928 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008929 }
glennrp0fe50b42010-11-16 03:52:51 +00008930
cristy13d07042010-11-21 20:56:18 +00008931 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008932 {
8933 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008934
8935 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008936 {
8937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8938 " background =(%d,%d,%d)",
8939 (int) ping_background.red,
8940 (int) ping_background.green,
8941 (int) ping_background.blue);
8942 }
8943 }
glennrpa521b2f2010-10-29 04:11:03 +00008944
glennrpd6bf1612010-12-17 17:28:54 +00008945 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008946 {
glennrp3b51f0e2010-11-27 18:14:08 +00008947 if (logging != MagickFalse)
8948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8949 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008950 ping_have_bKGD = MagickFalse;
8951 }
glennrp17a14852010-05-10 03:01:59 +00008952 }
glennrp26f37912010-12-23 16:22:42 +00008953 }
glennrp17a14852010-05-10 03:01:59 +00008954
cristy3ed852e2009-09-05 21:47:34 +00008955 if (logging != MagickFalse)
8956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00008957 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008958 /*
8959 Initialize compression level and filtering.
8960 */
8961 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008962 {
8963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8964 " Setting up deflate compression");
8965
8966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8967 " Compression buffer size: 32768");
8968 }
8969
cristy3ed852e2009-09-05 21:47:34 +00008970 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00008971
cristy3ed852e2009-09-05 21:47:34 +00008972 if (logging != MagickFalse)
8973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8974 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00008975
cristy3ed852e2009-09-05 21:47:34 +00008976 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00008977
cristy3ed852e2009-09-05 21:47:34 +00008978 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8979 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00008980
cristy3ed852e2009-09-05 21:47:34 +00008981 if (quality > 9)
8982 {
8983 int
8984 level;
8985
cristybb503372010-05-27 20:51:26 +00008986 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00008987
cristy3ed852e2009-09-05 21:47:34 +00008988 if (logging != MagickFalse)
8989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8990 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00008991
cristy3ed852e2009-09-05 21:47:34 +00008992 png_set_compression_level(ping,level);
8993 }
glennrp0fe50b42010-11-16 03:52:51 +00008994
cristy3ed852e2009-09-05 21:47:34 +00008995 else
8996 {
8997 if (logging != MagickFalse)
8998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8999 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009000
cristy3ed852e2009-09-05 21:47:34 +00009001 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9002 }
glennrp0fe50b42010-11-16 03:52:51 +00009003
cristy3ed852e2009-09-05 21:47:34 +00009004 if (logging != MagickFalse)
9005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9006 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009007
glennrp2b013e42010-11-24 16:55:50 +00009008#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009009 /* This became available in libpng-1.0.9. Output must be a MNG. */
9010 if (mng_info->write_mng && ((quality % 10) == 7))
9011 {
9012 if (logging != MagickFalse)
9013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9014 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009015
glennrp5af765f2010-03-30 11:12:18 +00009016 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009017 }
glennrp0fe50b42010-11-16 03:52:51 +00009018
cristy3ed852e2009-09-05 21:47:34 +00009019 else
9020 if (logging != MagickFalse)
9021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9022 " Filter_type: 0");
9023#endif
glennrp2b013e42010-11-24 16:55:50 +00009024
cristy3ed852e2009-09-05 21:47:34 +00009025 {
9026 int
9027 base_filter;
9028
9029 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009030 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009031
glennrp26c990a2010-11-23 02:23:20 +00009032 else
glennrp8640fb52010-11-23 15:48:26 +00009033 if ((quality % 10) != 5)
9034 base_filter=(int) quality % 10;
9035
9036 else
9037 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9038 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9039 (quality < 50))
9040 base_filter=PNG_NO_FILTERS;
9041
9042 else
9043 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009044
cristy3ed852e2009-09-05 21:47:34 +00009045 if (logging != MagickFalse)
9046 {
9047 if (base_filter == PNG_ALL_FILTERS)
9048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9049 " Base filter method: ADAPTIVE");
9050 else
9051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052 " Base filter method: NONE");
9053 }
glennrp2b013e42010-11-24 16:55:50 +00009054
cristy3ed852e2009-09-05 21:47:34 +00009055 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9056 }
9057
glennrp823b55c2011-03-14 18:46:46 +00009058 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9059 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009060 {
9061 ResetImageProfileIterator(image);
9062 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009063 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009064 profile=GetImageProfile(image,name);
9065
9066 if (profile != (StringInfo *) NULL)
9067 {
glennrp5af765f2010-03-30 11:12:18 +00009068#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009069 if ((LocaleCompare(name,"ICC") == 0) ||
9070 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009071 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009072
9073 if (ping_exclude_iCCP == MagickFalse)
9074 {
9075 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009076#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009077 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009078#else
9079 (png_const_bytep) GetStringInfoDatum(profile),
9080#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009081 (png_uint_32) GetStringInfoLength(profile));
9082 }
glennrp26f37912010-12-23 16:22:42 +00009083 }
glennrp0fe50b42010-11-16 03:52:51 +00009084
glennrpc8cbc5d2011-01-01 00:12:34 +00009085 else
cristy3ed852e2009-09-05 21:47:34 +00009086#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009087 if (ping_exclude_zCCP == MagickFalse)
9088 {
glennrpcf002022011-01-30 02:38:15 +00009089 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009090 (unsigned char *) name,(unsigned char *) name,
9091 GetStringInfoDatum(profile),
9092 (png_uint_32) GetStringInfoLength(profile));
9093 }
9094 }
glennrp0b206f52011-01-07 04:55:32 +00009095
glennrpc8cbc5d2011-01-01 00:12:34 +00009096 if (logging != MagickFalse)
9097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9098 " Setting up text chunk with %s profile",name);
9099
9100 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009101 }
cristy3ed852e2009-09-05 21:47:34 +00009102 }
9103
9104#if defined(PNG_WRITE_sRGB_SUPPORTED)
9105 if ((mng_info->have_write_global_srgb == 0) &&
9106 ((image->rendering_intent != UndefinedIntent) ||
9107 (image->colorspace == sRGBColorspace)))
9108 {
glennrp26f37912010-12-23 16:22:42 +00009109 if (ping_exclude_sRGB == MagickFalse)
9110 {
9111 /*
9112 Note image rendering intent.
9113 */
9114 if (logging != MagickFalse)
9115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9116 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009117
glennrp26f37912010-12-23 16:22:42 +00009118 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009119 Magick_RenderingIntent_to_PNG_RenderingIntent(
9120 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009121
glennrp26f37912010-12-23 16:22:42 +00009122 if (ping_exclude_gAMA == MagickFalse)
9123 png_set_gAMA(ping,ping_info,0.45455);
9124 }
cristy3ed852e2009-09-05 21:47:34 +00009125 }
glennrp26f37912010-12-23 16:22:42 +00009126
glennrp5af765f2010-03-30 11:12:18 +00009127 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009128#endif
9129 {
glennrp2cc891a2010-12-24 13:44:32 +00009130 if (ping_exclude_gAMA == MagickFalse &&
9131 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009132 (image->gamma < .45 || image->gamma > .46)))
9133 {
cristy3ed852e2009-09-05 21:47:34 +00009134 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9135 {
9136 /*
9137 Note image gamma.
9138 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9139 */
9140 if (logging != MagickFalse)
9141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9142 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009143
cristy3ed852e2009-09-05 21:47:34 +00009144 png_set_gAMA(ping,ping_info,image->gamma);
9145 }
glennrp26f37912010-12-23 16:22:42 +00009146 }
glennrp2b013e42010-11-24 16:55:50 +00009147
glennrp26f37912010-12-23 16:22:42 +00009148 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009149 {
glennrp26f37912010-12-23 16:22:42 +00009150 if ((mng_info->have_write_global_chrm == 0) &&
9151 (image->chromaticity.red_primary.x != 0.0))
9152 {
9153 /*
9154 Note image chromaticity.
9155 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9156 */
9157 PrimaryInfo
9158 bp,
9159 gp,
9160 rp,
9161 wp;
cristy3ed852e2009-09-05 21:47:34 +00009162
glennrp26f37912010-12-23 16:22:42 +00009163 wp=image->chromaticity.white_point;
9164 rp=image->chromaticity.red_primary;
9165 gp=image->chromaticity.green_primary;
9166 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009167
glennrp26f37912010-12-23 16:22:42 +00009168 if (logging != MagickFalse)
9169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9170 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009171
glennrp26f37912010-12-23 16:22:42 +00009172 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9173 bp.x,bp.y);
9174 }
9175 }
cristy3ed852e2009-09-05 21:47:34 +00009176 }
glennrpdfd70802010-11-14 01:23:35 +00009177
glennrp5af765f2010-03-30 11:12:18 +00009178 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009179
9180 if (mng_info->write_mng)
9181 png_set_sig_bytes(ping,8);
9182
9183 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9184
glennrpd6bf1612010-12-17 17:28:54 +00009185 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009186 {
9187 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009188 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009189 {
glennrp5af765f2010-03-30 11:12:18 +00009190 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009191
glennrp5af765f2010-03-30 11:12:18 +00009192 if (ping_bit_depth < 8)
9193 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009194 }
glennrp0fe50b42010-11-16 03:52:51 +00009195
cristy3ed852e2009-09-05 21:47:34 +00009196 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009197 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009198 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009199 }
9200
glennrp0e8ea192010-12-24 18:00:33 +00009201 if (ping_need_colortype_warning != MagickFalse ||
9202 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009203 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009204 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009205 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009206 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009207 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009208 {
9209 if (logging != MagickFalse)
9210 {
glennrp0e8ea192010-12-24 18:00:33 +00009211 if (ping_need_colortype_warning != MagickFalse)
9212 {
9213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9214 " Image has transparency but tRNS chunk was excluded");
9215 }
9216
cristy3ed852e2009-09-05 21:47:34 +00009217 if (mng_info->write_png_depth)
9218 {
9219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9220 " Defined PNG:bit-depth=%u, Computed depth=%u",
9221 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009222 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009223 }
glennrp0e8ea192010-12-24 18:00:33 +00009224
cristy3ed852e2009-09-05 21:47:34 +00009225 if (mng_info->write_png_colortype)
9226 {
9227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9228 " Defined PNG:color-type=%u, Computed color type=%u",
9229 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009230 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009231 }
9232 }
glennrp0e8ea192010-12-24 18:00:33 +00009233
glennrp3bd2e412010-08-10 13:34:52 +00009234 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009235 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9236 }
9237
glennrp58e01762011-01-07 15:28:54 +00009238 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009239 {
9240 /* Add an opaque matte channel */
9241 image->matte = MagickTrue;
9242 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009243
glennrpb4a13412010-05-05 12:47:19 +00009244 if (logging != MagickFalse)
9245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9246 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009247 }
9248
glennrp0e319732011-01-25 21:53:13 +00009249 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009250 {
glennrp991d11d2010-11-12 21:55:28 +00009251 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009252 {
glennrp991d11d2010-11-12 21:55:28 +00009253 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009254 if (logging != MagickFalse)
9255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9256 " Setting ping_have_tRNS=MagickTrue.");
9257 }
glennrpe9c26dc2010-05-30 01:56:35 +00009258 }
9259
cristy3ed852e2009-09-05 21:47:34 +00009260 if (logging != MagickFalse)
9261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9262 " Writing PNG header chunks");
9263
glennrp5af765f2010-03-30 11:12:18 +00009264 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9265 ping_bit_depth,ping_color_type,
9266 ping_interlace_method,ping_compression_method,
9267 ping_filter_method);
9268
glennrp39992b42010-11-14 00:03:43 +00009269 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9270 {
glennrpf09bded2011-01-08 01:15:59 +00009271 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009272
glennrp3b51f0e2010-11-27 18:14:08 +00009273 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009274 {
glennrp8640fb52010-11-23 15:48:26 +00009275 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009276 {
glennrpd6bf1612010-12-17 17:28:54 +00009277 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009279 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9280 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009281 (int) palette[i].red,
9282 (int) palette[i].green,
9283 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009284 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009285 (int) ping_trans_alpha[i]);
9286 else
9287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009288 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009289 (int) i,
9290 (int) palette[i].red,
9291 (int) palette[i].green,
9292 (int) palette[i].blue);
9293 }
glennrp39992b42010-11-14 00:03:43 +00009294 }
glennrp39992b42010-11-14 00:03:43 +00009295 }
9296
glennrp26f37912010-12-23 16:22:42 +00009297 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009298 {
glennrp26f37912010-12-23 16:22:42 +00009299 if (ping_have_bKGD != MagickFalse)
9300 png_set_bKGD(ping,ping_info,&ping_background);
9301 }
9302
9303 if (ping_exclude_pHYs == MagickFalse)
9304 {
9305 if (ping_have_pHYs != MagickFalse)
9306 {
9307 png_set_pHYs(ping,ping_info,
9308 ping_pHYs_x_resolution,
9309 ping_pHYs_y_resolution,
9310 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009311
9312 if (logging)
9313 {
9314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9315 " Setting up pHYs chunk");
9316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9317 " x_resolution=%lu",
9318 (unsigned long) ping_pHYs_x_resolution);
9319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9320 " y_resolution=%lu",
9321 (unsigned long) ping_pHYs_y_resolution);
9322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9323 " unit_type=%lu",
9324 (unsigned long) ping_pHYs_unit_type);
9325 }
glennrp26f37912010-12-23 16:22:42 +00009326 }
glennrpdfd70802010-11-14 01:23:35 +00009327 }
9328
9329#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009330 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009331 {
glennrp26f37912010-12-23 16:22:42 +00009332 if (image->page.x || image->page.y)
9333 {
9334 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9335 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009336
glennrp26f37912010-12-23 16:22:42 +00009337 if (logging != MagickFalse)
9338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9339 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9340 (int) image->page.x, (int) image->page.y);
9341 }
glennrpdfd70802010-11-14 01:23:35 +00009342 }
9343#endif
9344
glennrpda8f3a72011-02-27 23:54:12 +00009345 if (mng_info->need_blob != MagickFalse)
9346 {
9347 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9348 MagickFalse)
9349 png_error(ping,"WriteBlob Failed");
9350
9351 ping_have_blob=MagickTrue;
9352 }
9353
cristy3ed852e2009-09-05 21:47:34 +00009354 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009355
glennrp39992b42010-11-14 00:03:43 +00009356 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009357 {
glennrp3b51f0e2010-11-27 18:14:08 +00009358 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009359 {
9360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9361 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9362 }
9363
9364 if (ping_color_type == 3)
9365 (void) png_set_tRNS(ping, ping_info,
9366 ping_trans_alpha,
9367 ping_num_trans,
9368 NULL);
9369
9370 else
9371 {
9372 (void) png_set_tRNS(ping, ping_info,
9373 NULL,
9374 0,
9375 &ping_trans_color);
9376
glennrp3b51f0e2010-11-27 18:14:08 +00009377 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009378 {
9379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009380 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009381 (int) ping_trans_color.red,
9382 (int) ping_trans_color.green,
9383 (int) ping_trans_color.blue);
9384 }
9385 }
glennrp991d11d2010-11-12 21:55:28 +00009386 }
9387
cristy3ed852e2009-09-05 21:47:34 +00009388 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009389 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009390
cristy3ed852e2009-09-05 21:47:34 +00009391 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009392
cristy3ed852e2009-09-05 21:47:34 +00009393 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009394 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009395
glennrp26f37912010-12-23 16:22:42 +00009396 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009397 {
glennrp4f25bd02011-01-01 18:51:28 +00009398 if ((image->page.width != 0 && image->page.width != image->columns) ||
9399 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009400 {
9401 unsigned char
9402 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009403
glennrp26f37912010-12-23 16:22:42 +00009404 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9405 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009406 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009407 PNGLong(chunk+4,(png_uint_32) image->page.width);
9408 PNGLong(chunk+8,(png_uint_32) image->page.height);
9409 chunk[12]=0; /* unit = pixels */
9410 (void) WriteBlob(image,13,chunk);
9411 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9412 }
cristy3ed852e2009-09-05 21:47:34 +00009413 }
9414
9415#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009416 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009417#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009418 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009419#undef PNG_HAVE_IDAT
9420#endif
9421
9422 png_set_packing(ping);
9423 /*
9424 Allocate memory.
9425 */
9426 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009427 if (image_depth > 8)
9428 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009429 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009430 {
glennrpb4a13412010-05-05 12:47:19 +00009431 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009432 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009433 break;
glennrp0fe50b42010-11-16 03:52:51 +00009434
glennrpb4a13412010-05-05 12:47:19 +00009435 case PNG_COLOR_TYPE_GRAY_ALPHA:
9436 rowbytes*=2;
9437 break;
glennrp0fe50b42010-11-16 03:52:51 +00009438
glennrpb4a13412010-05-05 12:47:19 +00009439 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009440 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009441 break;
glennrp0fe50b42010-11-16 03:52:51 +00009442
glennrpb4a13412010-05-05 12:47:19 +00009443 default:
9444 break;
cristy3ed852e2009-09-05 21:47:34 +00009445 }
glennrp3b51f0e2010-11-27 18:14:08 +00009446
9447 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009448 {
9449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9450 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009451
glennrpb4a13412010-05-05 12:47:19 +00009452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009453 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009454 }
glennrpcf002022011-01-30 02:38:15 +00009455 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9456 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009457
glennrpcf002022011-01-30 02:38:15 +00009458 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009459 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009460
cristy3ed852e2009-09-05 21:47:34 +00009461 /*
9462 Initialize image scanlines.
9463 */
glennrp5af765f2010-03-30 11:12:18 +00009464 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009465 {
9466 /*
9467 PNG write failed.
9468 */
9469#ifdef PNG_DEBUG
9470 if (image_info->verbose)
9471 (void) printf("PNG write has failed.\n");
9472#endif
9473 png_destroy_write_struct(&ping,&ping_info);
9474 if (quantum_info != (QuantumInfo *) NULL)
9475 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009476 if (ping_pixels != (unsigned char *) NULL)
9477 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009478#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009479 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009480#endif
glennrpda8f3a72011-02-27 23:54:12 +00009481 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009482 (void) CloseBlob(image);
9483 image_info=DestroyImageInfo(image_info);
9484 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009485 return(MagickFalse);
9486 }
cristyed552522009-10-16 14:04:35 +00009487 quantum_info=AcquireQuantumInfo(image_info,image);
9488 if (quantum_info == (QuantumInfo *) NULL)
9489 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009490 quantum_info->format=UndefinedQuantumFormat;
9491 quantum_info->depth=image_depth;
9492 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009493
cristy3ed852e2009-09-05 21:47:34 +00009494 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009495 !mng_info->write_png32) &&
9496 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009497 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009498 image_matte == MagickFalse &&
9499 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009500 {
glennrp8bb3a022010-12-13 20:40:04 +00009501 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009502 register const PixelPacket
9503 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009504
cristy3ed852e2009-09-05 21:47:34 +00009505 quantum_info->depth=8;
9506 for (pass=0; pass < num_passes; pass++)
9507 {
9508 /*
9509 Convert PseudoClass image to a PNG monochrome image.
9510 */
cristybb503372010-05-27 20:51:26 +00009511 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009512 {
glennrpd71e86a2011-02-24 01:28:37 +00009513 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9515 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009516
cristy3ed852e2009-09-05 21:47:34 +00009517 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009518
cristy3ed852e2009-09-05 21:47:34 +00009519 if (p == (const PixelPacket *) NULL)
9520 break;
glennrp0fe50b42010-11-16 03:52:51 +00009521
cristy3ed852e2009-09-05 21:47:34 +00009522 if (mng_info->IsPalette)
9523 {
9524 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009525 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009526 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9527 mng_info->write_png_depth &&
9528 mng_info->write_png_depth != old_bit_depth)
9529 {
9530 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009531 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009532 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009533 >> (8-old_bit_depth));
9534 }
9535 }
glennrp0fe50b42010-11-16 03:52:51 +00009536
cristy3ed852e2009-09-05 21:47:34 +00009537 else
9538 {
9539 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009540 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009541 }
glennrp0fe50b42010-11-16 03:52:51 +00009542
cristy3ed852e2009-09-05 21:47:34 +00009543 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009544 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009545 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009546 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009547
glennrp3b51f0e2010-11-27 18:14:08 +00009548 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9550 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009551
glennrpcf002022011-01-30 02:38:15 +00009552 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009553 }
9554 if (image->previous == (Image *) NULL)
9555 {
9556 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9557 if (status == MagickFalse)
9558 break;
9559 }
9560 }
9561 }
glennrp0fe50b42010-11-16 03:52:51 +00009562
glennrp8bb3a022010-12-13 20:40:04 +00009563 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009564 {
glennrp0fe50b42010-11-16 03:52:51 +00009565 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009566 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009567 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009568 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009569 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009570 {
glennrp8bb3a022010-12-13 20:40:04 +00009571 register const PixelPacket
9572 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009573
glennrp8bb3a022010-12-13 20:40:04 +00009574 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009575 {
glennrp8bb3a022010-12-13 20:40:04 +00009576
cristybb503372010-05-27 20:51:26 +00009577 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009578 {
9579 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009580
cristy3ed852e2009-09-05 21:47:34 +00009581 if (p == (const PixelPacket *) NULL)
9582 break;
glennrp2cc891a2010-12-24 13:44:32 +00009583
glennrp5af765f2010-03-30 11:12:18 +00009584 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009585 {
glennrp8bb3a022010-12-13 20:40:04 +00009586 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009587 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009588 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009589
glennrp8bb3a022010-12-13 20:40:04 +00009590 else
9591 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009592 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009593
glennrp3b51f0e2010-11-27 18:14:08 +00009594 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009596 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009597 }
glennrp2cc891a2010-12-24 13:44:32 +00009598
glennrp8bb3a022010-12-13 20:40:04 +00009599 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9600 {
9601 if (logging != MagickFalse && y == 0)
9602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9603 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009604
glennrp8bb3a022010-12-13 20:40:04 +00009605 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009606 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009607 }
glennrp2cc891a2010-12-24 13:44:32 +00009608
glennrp3b51f0e2010-11-27 18:14:08 +00009609 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009611 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009612
glennrpcf002022011-01-30 02:38:15 +00009613 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009614 }
glennrp2cc891a2010-12-24 13:44:32 +00009615
glennrp8bb3a022010-12-13 20:40:04 +00009616 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009617 {
glennrp8bb3a022010-12-13 20:40:04 +00009618 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9619 if (status == MagickFalse)
9620 break;
cristy3ed852e2009-09-05 21:47:34 +00009621 }
cristy3ed852e2009-09-05 21:47:34 +00009622 }
9623 }
glennrp8bb3a022010-12-13 20:40:04 +00009624
9625 else
9626 {
9627 register const PixelPacket
9628 *p;
9629
9630 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009631 {
glennrp8bb3a022010-12-13 20:40:04 +00009632 if ((image_depth > 8) || (mng_info->write_png24 ||
9633 mng_info->write_png32 ||
9634 (!mng_info->write_png8 && !mng_info->IsPalette)))
9635 {
9636 for (y=0; y < (ssize_t) image->rows; y++)
9637 {
9638 p=GetVirtualPixels(image,0,y,image->columns,1,
9639 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009640
glennrp8bb3a022010-12-13 20:40:04 +00009641 if (p == (const PixelPacket *) NULL)
9642 break;
glennrp2cc891a2010-12-24 13:44:32 +00009643
glennrp8bb3a022010-12-13 20:40:04 +00009644 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9645 {
9646 if (image->storage_class == DirectClass)
9647 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009648 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009649
glennrp8bb3a022010-12-13 20:40:04 +00009650 else
9651 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009652 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009653 }
glennrp2cc891a2010-12-24 13:44:32 +00009654
glennrp8bb3a022010-12-13 20:40:04 +00009655 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9656 {
9657 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009658 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009659 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009660
glennrp8bb3a022010-12-13 20:40:04 +00009661 if (logging != MagickFalse && y == 0)
9662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9663 " Writing GRAY_ALPHA PNG pixels (3)");
9664 }
glennrp2cc891a2010-12-24 13:44:32 +00009665
glennrp8bb3a022010-12-13 20:40:04 +00009666 else if (image_matte != MagickFalse)
9667 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009668 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009669
glennrp8bb3a022010-12-13 20:40:04 +00009670 else
9671 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009672 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009673
glennrp8bb3a022010-12-13 20:40:04 +00009674 if (logging != MagickFalse && y == 0)
9675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9676 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009677
glennrpcf002022011-01-30 02:38:15 +00009678 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009679 }
9680 }
glennrp2cc891a2010-12-24 13:44:32 +00009681
glennrp8bb3a022010-12-13 20:40:04 +00009682 else
9683 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9684 mng_info->write_png32 ||
9685 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9686 {
9687 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9688 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9689 {
9690 if (logging != MagickFalse)
9691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9692 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009693
glennrp8bb3a022010-12-13 20:40:04 +00009694 quantum_info->depth=8;
9695 image_depth=8;
9696 }
glennrp2cc891a2010-12-24 13:44:32 +00009697
glennrp8bb3a022010-12-13 20:40:04 +00009698 for (y=0; y < (ssize_t) image->rows; y++)
9699 {
9700 if (logging != MagickFalse && y == 0)
9701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9702 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009703
glennrp770d1932011-03-06 22:11:17 +00009704 p=GetVirtualPixels(image,0,y,image->columns,1,
9705 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009706
glennrp8bb3a022010-12-13 20:40:04 +00009707 if (p == (const PixelPacket *) NULL)
9708 break;
glennrp2cc891a2010-12-24 13:44:32 +00009709
glennrp8bb3a022010-12-13 20:40:04 +00009710 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009711 {
glennrp4bf89732011-03-21 13:48:28 +00009712 quantum_info->depth=image->depth;
9713
glennrp44757ab2011-03-17 12:57:03 +00009714 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009715 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009716 }
glennrp2cc891a2010-12-24 13:44:32 +00009717
glennrp8bb3a022010-12-13 20:40:04 +00009718 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9719 {
9720 if (logging != MagickFalse && y == 0)
9721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9722 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009723
glennrp8bb3a022010-12-13 20:40:04 +00009724 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009725 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009726 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009727 }
glennrp2cc891a2010-12-24 13:44:32 +00009728
glennrp8bb3a022010-12-13 20:40:04 +00009729 else
glennrp8bb3a022010-12-13 20:40:04 +00009730 {
glennrp179d0752011-03-17 13:02:10 +00009731 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009732 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9733
9734 if (logging != MagickFalse && y <= 2)
9735 {
9736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009737 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009738
9739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9740 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9741 (int)ping_pixels[0],(int)ping_pixels[1]);
9742 }
glennrp8bb3a022010-12-13 20:40:04 +00009743 }
glennrpcf002022011-01-30 02:38:15 +00009744 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009745 }
9746 }
glennrp2cc891a2010-12-24 13:44:32 +00009747
glennrp8bb3a022010-12-13 20:40:04 +00009748 if (image->previous == (Image *) NULL)
9749 {
9750 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9751 if (status == MagickFalse)
9752 break;
9753 }
cristy3ed852e2009-09-05 21:47:34 +00009754 }
glennrp8bb3a022010-12-13 20:40:04 +00009755 }
9756 }
9757
cristyb32b90a2009-09-07 21:45:48 +00009758 if (quantum_info != (QuantumInfo *) NULL)
9759 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009760
9761 if (logging != MagickFalse)
9762 {
9763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009764 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009765
cristy3ed852e2009-09-05 21:47:34 +00009766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009767 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009768
cristy3ed852e2009-09-05 21:47:34 +00009769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009770 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009771
cristy3ed852e2009-09-05 21:47:34 +00009772 if (mng_info->write_png_depth)
9773 {
9774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9775 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9776 }
glennrp0fe50b42010-11-16 03:52:51 +00009777
cristy3ed852e2009-09-05 21:47:34 +00009778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009779 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009780
cristy3ed852e2009-09-05 21:47:34 +00009781 if (mng_info->write_png_colortype)
9782 {
9783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9784 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9785 }
glennrp0fe50b42010-11-16 03:52:51 +00009786
cristy3ed852e2009-09-05 21:47:34 +00009787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009788 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009789
cristy3ed852e2009-09-05 21:47:34 +00009790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009791 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009792 }
9793 /*
glennrpa0ed0092011-04-18 16:36:29 +00009794 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009795 */
glennrp823b55c2011-03-14 18:46:46 +00009796 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009797 {
glennrp26f37912010-12-23 16:22:42 +00009798 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009799 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009800 while (property != (const char *) NULL)
9801 {
9802 png_textp
9803 text;
glennrp2cc891a2010-12-24 13:44:32 +00009804
glennrp26f37912010-12-23 16:22:42 +00009805 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009806
9807 /* Don't write any "png:" properties; those are just for "identify" */
9808 if (LocaleNCompare(property,"png:",4) != 0 &&
9809
9810 /* Suppress density and units if we wrote a pHYs chunk */
9811 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009812 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009813 LocaleCompare(property,"units") != 0) &&
9814
9815 /* Suppress the IM-generated Date:create and Date:modify */
9816 (ping_exclude_date == MagickFalse ||
9817 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009818 {
glennrpc70af4a2011-03-07 00:08:23 +00009819 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009820 {
glennrpc70af4a2011-03-07 00:08:23 +00009821 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9822 text[0].key=(char *) property;
9823 text[0].text=(char *) value;
9824 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009825
glennrpc70af4a2011-03-07 00:08:23 +00009826 if (ping_exclude_tEXt != MagickFalse)
9827 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9828
9829 else if (ping_exclude_zTXt != MagickFalse)
9830 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9831
9832 else
glennrp26f37912010-12-23 16:22:42 +00009833 {
glennrpc70af4a2011-03-07 00:08:23 +00009834 text[0].compression=image_info->compression == NoCompression ||
9835 (image_info->compression == UndefinedCompression &&
9836 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9837 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009838 }
glennrp2cc891a2010-12-24 13:44:32 +00009839
glennrpc70af4a2011-03-07 00:08:23 +00009840 if (logging != MagickFalse)
9841 {
9842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9843 " Setting up text chunk");
9844
9845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9846 " keyword: %s",text[0].key);
9847 }
9848
9849 png_set_text(ping,ping_info,text,1);
9850 png_free(ping,text);
9851 }
glennrp26f37912010-12-23 16:22:42 +00009852 }
9853 property=GetNextImageProperty(image);
9854 }
cristy3ed852e2009-09-05 21:47:34 +00009855 }
9856
9857 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009858 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009859
9860 if (logging != MagickFalse)
9861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9862 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009863
cristy3ed852e2009-09-05 21:47:34 +00009864 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009865
cristy3ed852e2009-09-05 21:47:34 +00009866 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9867 {
9868 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009869 (ping_width != mng_info->page.width) ||
9870 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009871 {
9872 unsigned char
9873 chunk[32];
9874
9875 /*
9876 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9877 */
9878 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9879 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009880 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009881 chunk[4]=4;
9882 chunk[5]=0; /* frame name separator (no name) */
9883 chunk[6]=1; /* flag for changing delay, for next frame only */
9884 chunk[7]=0; /* flag for changing frame timeout */
9885 chunk[8]=1; /* flag for changing frame clipping for next frame */
9886 chunk[9]=0; /* flag for changing frame sync_id */
9887 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9888 chunk[14]=0; /* clipping boundaries delta type */
9889 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9890 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009891 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009892 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9893 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009894 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009895 (void) WriteBlob(image,31,chunk);
9896 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9897 mng_info->old_framing_mode=4;
9898 mng_info->framing_mode=1;
9899 }
glennrp0fe50b42010-11-16 03:52:51 +00009900
cristy3ed852e2009-09-05 21:47:34 +00009901 else
9902 mng_info->framing_mode=3;
9903 }
9904 if (mng_info->write_mng && !mng_info->need_fram &&
9905 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009906 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009907 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009908 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009909
cristy3ed852e2009-09-05 21:47:34 +00009910 /*
9911 Free PNG resources.
9912 */
glennrp5af765f2010-03-30 11:12:18 +00009913
cristy3ed852e2009-09-05 21:47:34 +00009914 png_destroy_write_struct(&ping,&ping_info);
9915
glennrpcf002022011-01-30 02:38:15 +00009916 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009917
9918#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009919 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009920#endif
9921
glennrpda8f3a72011-02-27 23:54:12 +00009922 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009923 (void) CloseBlob(image);
9924
9925 image_info=DestroyImageInfo(image_info);
9926 image=DestroyImage(image);
9927
9928 /* Store bit depth actually written */
9929 s[0]=(char) ping_bit_depth;
9930 s[1]='\0';
9931
9932 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9933
cristy3ed852e2009-09-05 21:47:34 +00009934 if (logging != MagickFalse)
9935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9936 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009937
cristy3ed852e2009-09-05 21:47:34 +00009938 return(MagickTrue);
9939/* End write one PNG image */
9940}
9941
9942/*
9943%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9944% %
9945% %
9946% %
9947% W r i t e P N G I m a g e %
9948% %
9949% %
9950% %
9951%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9952%
9953% WritePNGImage() writes a Portable Network Graphics (PNG) or
9954% Multiple-image Network Graphics (MNG) image file.
9955%
9956% MNG support written by Glenn Randers-Pehrson, glennrp@image...
9957%
9958% The format of the WritePNGImage method is:
9959%
9960% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9961%
9962% A description of each parameter follows:
9963%
9964% o image_info: the image info.
9965%
9966% o image: The image.
9967%
9968% Returns MagickTrue on success, MagickFalse on failure.
9969%
9970% Communicating with the PNG encoder:
9971%
9972% While the datastream written is always in PNG format and normally would
9973% be given the "png" file extension, this method also writes the following
9974% pseudo-formats which are subsets of PNG:
9975%
glennrp5a39f372011-02-25 04:52:16 +00009976% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
9977% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +00009978% is present, the tRNS chunk must only have values 0 and 255
9979% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +00009980% transparent). If other values are present they will be
9981% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +00009982% colors are present, they will be quantized to the 4-4-4-1,
9983% 3-3-3-1, or 3-3-2-1 palette.
9984%
9985% If you want better quantization or dithering of the colors
9986% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +00009987% PNG encoder. The pixels contain 8-bit indices even if
9988% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +00009989% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +00009990% PNG grayscale type might be slightly more efficient. Please
9991% note that writing to the PNG8 format may result in loss
9992% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +00009993%
9994% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9995% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +00009996% one of the colors as transparent. The only loss incurred
9997% is reduction of sample depth to 8. If the image has more
9998% than one transparent color, has semitransparent pixels, or
9999% has an opaque pixel with the same RGB components as the
10000% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010001%
10002% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10003% transparency is permitted, i.e., the alpha sample for
10004% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010005% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010006% The only loss in data is the reduction of the sample depth
10007% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010008%
10009% o -define: For more precise control of the PNG output, you can use the
10010% Image options "png:bit-depth" and "png:color-type". These
10011% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010012% from the application programming interfaces. The options
10013% are case-independent and are converted to lowercase before
10014% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010015%
10016% png:color-type can be 0, 2, 3, 4, or 6.
10017%
10018% When png:color-type is 0 (Grayscale), png:bit-depth can
10019% be 1, 2, 4, 8, or 16.
10020%
10021% When png:color-type is 2 (RGB), png:bit-depth can
10022% be 8 or 16.
10023%
10024% When png:color-type is 3 (Indexed), png:bit-depth can
10025% be 1, 2, 4, or 8. This refers to the number of bits
10026% used to store the index. The color samples always have
10027% bit-depth 8 in indexed PNG files.
10028%
10029% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10030% png:bit-depth can be 8 or 16.
10031%
glennrp5a39f372011-02-25 04:52:16 +000010032% If the image cannot be written without loss with the requested bit-depth
10033% and color-type, a PNG file will not be written, and the encoder will
10034% return MagickFalse.
10035%
cristy3ed852e2009-09-05 21:47:34 +000010036% Since image encoders should not be responsible for the "heavy lifting",
10037% the user should make sure that ImageMagick has already reduced the
10038% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010039% transparency prior to attempting to write the image with depth, color,
10040% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010041%
glennrp97cefe22011-04-22 16:17:00 +000010042% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010043%
cristy3ed852e2009-09-05 21:47:34 +000010044% Note that another definition, "png:bit-depth-written" exists, but it
10045% is not intended for external use. It is only used internally by the
10046% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10047%
10048% It is possible to request that the PNG encoder write previously-formatted
10049% ancillary chunks in the output PNG file, using the "-profile" commandline
10050% option as shown below or by setting the profile via a programming
10051% interface:
10052%
10053% -profile PNG-chunk-x:<file>
10054%
10055% where x is a location flag and <file> is a file containing the chunk
10056% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010057% This encoder will compute the chunk length and CRC, so those must not
10058% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010059%
10060% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10061% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10062% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010063% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010064%
glennrpbb8a7332010-11-13 15:17:35 +000010065% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010066%
glennrp3241bd02010-12-12 04:36:28 +000010067% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010068%
glennrpd6afd542010-11-19 01:53:05 +000010069% o 32-bit depth is reduced to 16.
10070% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10071% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010072% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010073% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010074% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010075% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10076% this can be done without loss and a larger bit depth N was not
10077% requested via the "-define PNG:bit-depth=N" option.
10078% o If matte channel is present but only one transparent color is
10079% present, RGB+tRNS is written instead of RGBA
10080% o Opaque matte channel is removed (or added, if color-type 4 or 6
10081% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010082%
cristy3ed852e2009-09-05 21:47:34 +000010083%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10084*/
10085static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10086 Image *image)
10087{
10088 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010089 excluding,
10090 logging,
10091 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010092 status;
10093
10094 MngInfo
10095 *mng_info;
10096
10097 const char
10098 *value;
10099
10100 int
glennrp21f0e622011-01-07 16:20:57 +000010101 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010102 source;
10103
cristy3ed852e2009-09-05 21:47:34 +000010104 /*
10105 Open image file.
10106 */
10107 assert(image_info != (const ImageInfo *) NULL);
10108 assert(image_info->signature == MagickSignature);
10109 assert(image != (Image *) NULL);
10110 assert(image->signature == MagickSignature);
10111 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010112 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010113 /*
10114 Allocate a MngInfo structure.
10115 */
10116 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010117 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010118
cristy3ed852e2009-09-05 21:47:34 +000010119 if (mng_info == (MngInfo *) NULL)
10120 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010121
cristy3ed852e2009-09-05 21:47:34 +000010122 /*
10123 Initialize members of the MngInfo structure.
10124 */
10125 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10126 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010127 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010128 have_mng_structure=MagickTrue;
10129
10130 /* See if user has requested a specific PNG subformat */
10131
10132 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10133 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10134 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10135
10136 if (mng_info->write_png8)
10137 {
glennrp9c1eb072010-06-06 22:19:15 +000010138 mng_info->write_png_colortype = /* 3 */ 4;
10139 mng_info->write_png_depth = 8;
10140 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010141 }
10142
10143 if (mng_info->write_png24)
10144 {
glennrp9c1eb072010-06-06 22:19:15 +000010145 mng_info->write_png_colortype = /* 2 */ 3;
10146 mng_info->write_png_depth = 8;
10147 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010148
glennrp9c1eb072010-06-06 22:19:15 +000010149 if (image->matte == MagickTrue)
10150 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010151
glennrp9c1eb072010-06-06 22:19:15 +000010152 else
10153 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010154
glennrp9c1eb072010-06-06 22:19:15 +000010155 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010156 }
10157
10158 if (mng_info->write_png32)
10159 {
glennrp9c1eb072010-06-06 22:19:15 +000010160 mng_info->write_png_colortype = /* 6 */ 7;
10161 mng_info->write_png_depth = 8;
10162 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010163
glennrp9c1eb072010-06-06 22:19:15 +000010164 if (image->matte == MagickTrue)
10165 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010166
glennrp9c1eb072010-06-06 22:19:15 +000010167 else
10168 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010169
glennrp9c1eb072010-06-06 22:19:15 +000010170 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010171 }
10172
10173 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010174
cristy3ed852e2009-09-05 21:47:34 +000010175 if (value != (char *) NULL)
10176 {
10177 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010178 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010179
cristy3ed852e2009-09-05 21:47:34 +000010180 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010181 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010182
cristy3ed852e2009-09-05 21:47:34 +000010183 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010184 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010185
cristy3ed852e2009-09-05 21:47:34 +000010186 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010187 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010188
cristy3ed852e2009-09-05 21:47:34 +000010189 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010190 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010191
glennrpbb8a7332010-11-13 15:17:35 +000010192 else
10193 (void) ThrowMagickException(&image->exception,
10194 GetMagickModule(),CoderWarning,
10195 "ignoring invalid defined png:bit-depth",
10196 "=%s",value);
10197
cristy3ed852e2009-09-05 21:47:34 +000010198 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010200 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010201 }
glennrp0fe50b42010-11-16 03:52:51 +000010202
cristy3ed852e2009-09-05 21:47:34 +000010203 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010204
cristy3ed852e2009-09-05 21:47:34 +000010205 if (value != (char *) NULL)
10206 {
10207 /* We must store colortype+1 because 0 is a valid colortype */
10208 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010209 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010210
cristy3ed852e2009-09-05 21:47:34 +000010211 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010212 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010213
cristy3ed852e2009-09-05 21:47:34 +000010214 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010215 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010216
cristy3ed852e2009-09-05 21:47:34 +000010217 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010218 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010219
cristy3ed852e2009-09-05 21:47:34 +000010220 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010221 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010222
glennrpbb8a7332010-11-13 15:17:35 +000010223 else
10224 (void) ThrowMagickException(&image->exception,
10225 GetMagickModule(),CoderWarning,
10226 "ignoring invalid defined png:color-type",
10227 "=%s",value);
10228
cristy3ed852e2009-09-05 21:47:34 +000010229 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010231 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010232 }
10233
glennrp0e8ea192010-12-24 18:00:33 +000010234 /* Check for chunks to be excluded:
10235 *
glennrp0dff56c2011-01-29 19:10:02 +000010236 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010237 * listed in the "unused_chunks" array, above.
10238 *
10239 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10240 * define (in the image properties or in the image artifacts)
10241 * or via a mng_info member. For convenience, in addition
10242 * to or instead of a comma-separated list of chunks, the
10243 * "exclude-chunk" string can be simply "all" or "none".
10244 *
10245 * The exclude-chunk define takes priority over the mng_info.
10246 *
10247 * A "PNG:include-chunk" define takes priority over both the
10248 * mng_info and the "PNG:exclude-chunk" define. Like the
10249 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010250 * well as a comma-separated list. Chunks that are unknown to
10251 * ImageMagick are always excluded, regardless of their "copy-safe"
10252 * status according to the PNG specification, and even if they
10253 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010254 *
10255 * Finally, all chunks listed in the "unused_chunks" array are
10256 * automatically excluded, regardless of the other instructions
10257 * or lack thereof.
10258 *
10259 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10260 * will not be written and the gAMA chunk will only be written if it
10261 * is not between .45 and .46, or approximately (1.0/2.2).
10262 *
10263 * If you exclude tRNS and the image has transparency, the colortype
10264 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10265 *
10266 * The -strip option causes StripImage() to set the png:include-chunk
10267 * artifact to "none,gama".
10268 */
10269
glennrp26f37912010-12-23 16:22:42 +000010270 mng_info->ping_exclude_bKGD=MagickFalse;
10271 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010272 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010273 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10274 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010275 mng_info->ping_exclude_iCCP=MagickFalse;
10276 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10277 mng_info->ping_exclude_oFFs=MagickFalse;
10278 mng_info->ping_exclude_pHYs=MagickFalse;
10279 mng_info->ping_exclude_sRGB=MagickFalse;
10280 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010281 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010282 mng_info->ping_exclude_vpAg=MagickFalse;
10283 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10284 mng_info->ping_exclude_zTXt=MagickFalse;
10285
glennrp8d3d6e52011-04-19 04:39:51 +000010286 mng_info->ping_preserve_colormap=MagickFalse;
10287
10288 value=GetImageArtifact(image,"png:preserve-colormap");
10289 if (value == NULL)
10290 value=GetImageOption(image_info,"png:preserve-colormap");
10291 if (value != NULL)
10292 mng_info->ping_preserve_colormap=MagickTrue;
10293
glennrp03812ae2010-12-24 01:31:34 +000010294 excluding=MagickFalse;
10295
glennrp5c7cf4e2010-12-24 00:30:00 +000010296 for (source=0; source<1; source++)
10297 {
10298 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010299 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010300 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010301
10302 if (value == NULL)
10303 value=GetImageArtifact(image,"png:exclude-chunks");
10304 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010305 else
glennrpacba0042010-12-24 14:27:26 +000010306 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010307 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010308
glennrpacba0042010-12-24 14:27:26 +000010309 if (value == NULL)
10310 value=GetImageOption(image_info,"png:exclude-chunks");
10311 }
10312
glennrp03812ae2010-12-24 01:31:34 +000010313 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010314 {
glennrp03812ae2010-12-24 01:31:34 +000010315
10316 size_t
10317 last;
10318
10319 excluding=MagickTrue;
10320
10321 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010322 {
10323 if (source == 0)
10324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10325 " png:exclude-chunk=%s found in image artifacts.\n", value);
10326 else
10327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10328 " png:exclude-chunk=%s found in image properties.\n", value);
10329 }
glennrp03812ae2010-12-24 01:31:34 +000010330
10331 last=strlen(value);
10332
10333 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010334 {
glennrp03812ae2010-12-24 01:31:34 +000010335
10336 if (LocaleNCompare(value+i,"all",3) == 0)
10337 {
10338 mng_info->ping_exclude_bKGD=MagickTrue;
10339 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010340 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010341 mng_info->ping_exclude_EXIF=MagickTrue;
10342 mng_info->ping_exclude_gAMA=MagickTrue;
10343 mng_info->ping_exclude_iCCP=MagickTrue;
10344 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10345 mng_info->ping_exclude_oFFs=MagickTrue;
10346 mng_info->ping_exclude_pHYs=MagickTrue;
10347 mng_info->ping_exclude_sRGB=MagickTrue;
10348 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010349 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010350 mng_info->ping_exclude_vpAg=MagickTrue;
10351 mng_info->ping_exclude_zCCP=MagickTrue;
10352 mng_info->ping_exclude_zTXt=MagickTrue;
10353 i--;
10354 }
glennrp2cc891a2010-12-24 13:44:32 +000010355
glennrp03812ae2010-12-24 01:31:34 +000010356 if (LocaleNCompare(value+i,"none",4) == 0)
10357 {
10358 mng_info->ping_exclude_bKGD=MagickFalse;
10359 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010360 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010361 mng_info->ping_exclude_EXIF=MagickFalse;
10362 mng_info->ping_exclude_gAMA=MagickFalse;
10363 mng_info->ping_exclude_iCCP=MagickFalse;
10364 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10365 mng_info->ping_exclude_oFFs=MagickFalse;
10366 mng_info->ping_exclude_pHYs=MagickFalse;
10367 mng_info->ping_exclude_sRGB=MagickFalse;
10368 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010369 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010370 mng_info->ping_exclude_vpAg=MagickFalse;
10371 mng_info->ping_exclude_zCCP=MagickFalse;
10372 mng_info->ping_exclude_zTXt=MagickFalse;
10373 }
glennrp2cc891a2010-12-24 13:44:32 +000010374
glennrp03812ae2010-12-24 01:31:34 +000010375 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10376 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010377
glennrp03812ae2010-12-24 01:31:34 +000010378 if (LocaleNCompare(value+i,"chrm",4) == 0)
10379 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010380
glennrpa0ed0092011-04-18 16:36:29 +000010381 if (LocaleNCompare(value+i,"date",4) == 0)
10382 mng_info->ping_exclude_date=MagickTrue;
10383
glennrp03812ae2010-12-24 01:31:34 +000010384 if (LocaleNCompare(value+i,"exif",4) == 0)
10385 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010386
glennrp03812ae2010-12-24 01:31:34 +000010387 if (LocaleNCompare(value+i,"gama",4) == 0)
10388 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010389
glennrp03812ae2010-12-24 01:31:34 +000010390 if (LocaleNCompare(value+i,"iccp",4) == 0)
10391 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010392
glennrp03812ae2010-12-24 01:31:34 +000010393 /*
10394 if (LocaleNCompare(value+i,"itxt",4) == 0)
10395 mng_info->ping_exclude_iTXt=MagickTrue;
10396 */
glennrp2cc891a2010-12-24 13:44:32 +000010397
glennrp03812ae2010-12-24 01:31:34 +000010398 if (LocaleNCompare(value+i,"gama",4) == 0)
10399 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010400
glennrp03812ae2010-12-24 01:31:34 +000010401 if (LocaleNCompare(value+i,"offs",4) == 0)
10402 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010403
glennrp03812ae2010-12-24 01:31:34 +000010404 if (LocaleNCompare(value+i,"phys",4) == 0)
10405 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010406
glennrpa1e3b7b2010-12-24 16:37:33 +000010407 if (LocaleNCompare(value+i,"srgb",4) == 0)
10408 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010409
glennrp03812ae2010-12-24 01:31:34 +000010410 if (LocaleNCompare(value+i,"text",4) == 0)
10411 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010412
glennrpa1e3b7b2010-12-24 16:37:33 +000010413 if (LocaleNCompare(value+i,"trns",4) == 0)
10414 mng_info->ping_exclude_tRNS=MagickTrue;
10415
glennrp03812ae2010-12-24 01:31:34 +000010416 if (LocaleNCompare(value+i,"vpag",4) == 0)
10417 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010418
glennrp03812ae2010-12-24 01:31:34 +000010419 if (LocaleNCompare(value+i,"zccp",4) == 0)
10420 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010421
glennrp03812ae2010-12-24 01:31:34 +000010422 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10423 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010424
glennrp03812ae2010-12-24 01:31:34 +000010425 }
glennrpce91ed52010-12-23 22:37:49 +000010426 }
glennrp26f37912010-12-23 16:22:42 +000010427 }
10428
glennrp5c7cf4e2010-12-24 00:30:00 +000010429 for (source=0; source<1; source++)
10430 {
10431 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010432 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010433 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010434
10435 if (value == NULL)
10436 value=GetImageArtifact(image,"png:include-chunks");
10437 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010438 else
glennrpacba0042010-12-24 14:27:26 +000010439 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010440 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010441
glennrpacba0042010-12-24 14:27:26 +000010442 if (value == NULL)
10443 value=GetImageOption(image_info,"png:include-chunks");
10444 }
10445
glennrp03812ae2010-12-24 01:31:34 +000010446 if (value != NULL)
10447 {
10448 size_t
10449 last;
glennrp26f37912010-12-23 16:22:42 +000010450
glennrp03812ae2010-12-24 01:31:34 +000010451 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010452
glennrp03812ae2010-12-24 01:31:34 +000010453 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010454 {
10455 if (source == 0)
10456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10457 " png:include-chunk=%s found in image artifacts.\n", value);
10458 else
10459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10460 " png:include-chunk=%s found in image properties.\n", value);
10461 }
glennrp03812ae2010-12-24 01:31:34 +000010462
10463 last=strlen(value);
10464
10465 for (i=0; i<(int) last; i+=5)
10466 {
10467 if (LocaleNCompare(value+i,"all",3) == 0)
10468 {
10469 mng_info->ping_exclude_bKGD=MagickFalse;
10470 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010471 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010472 mng_info->ping_exclude_EXIF=MagickFalse;
10473 mng_info->ping_exclude_gAMA=MagickFalse;
10474 mng_info->ping_exclude_iCCP=MagickFalse;
10475 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10476 mng_info->ping_exclude_oFFs=MagickFalse;
10477 mng_info->ping_exclude_pHYs=MagickFalse;
10478 mng_info->ping_exclude_sRGB=MagickFalse;
10479 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010480 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010481 mng_info->ping_exclude_vpAg=MagickFalse;
10482 mng_info->ping_exclude_zCCP=MagickFalse;
10483 mng_info->ping_exclude_zTXt=MagickFalse;
10484 i--;
10485 }
glennrp2cc891a2010-12-24 13:44:32 +000010486
glennrp03812ae2010-12-24 01:31:34 +000010487 if (LocaleNCompare(value+i,"none",4) == 0)
10488 {
10489 mng_info->ping_exclude_bKGD=MagickTrue;
10490 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010491 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010492 mng_info->ping_exclude_EXIF=MagickTrue;
10493 mng_info->ping_exclude_gAMA=MagickTrue;
10494 mng_info->ping_exclude_iCCP=MagickTrue;
10495 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10496 mng_info->ping_exclude_oFFs=MagickTrue;
10497 mng_info->ping_exclude_pHYs=MagickTrue;
10498 mng_info->ping_exclude_sRGB=MagickTrue;
10499 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010500 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010501 mng_info->ping_exclude_vpAg=MagickTrue;
10502 mng_info->ping_exclude_zCCP=MagickTrue;
10503 mng_info->ping_exclude_zTXt=MagickTrue;
10504 }
glennrp2cc891a2010-12-24 13:44:32 +000010505
glennrp03812ae2010-12-24 01:31:34 +000010506 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10507 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010508
glennrp03812ae2010-12-24 01:31:34 +000010509 if (LocaleNCompare(value+i,"chrm",4) == 0)
10510 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010511
glennrpa0ed0092011-04-18 16:36:29 +000010512 if (LocaleNCompare(value+i,"date",4) == 0)
10513 mng_info->ping_exclude_date=MagickFalse;
10514
glennrp03812ae2010-12-24 01:31:34 +000010515 if (LocaleNCompare(value+i,"exif",4) == 0)
10516 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010517
glennrp03812ae2010-12-24 01:31:34 +000010518 if (LocaleNCompare(value+i,"gama",4) == 0)
10519 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010520
glennrp03812ae2010-12-24 01:31:34 +000010521 if (LocaleNCompare(value+i,"iccp",4) == 0)
10522 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010523
glennrp03812ae2010-12-24 01:31:34 +000010524 /*
10525 if (LocaleNCompare(value+i,"itxt",4) == 0)
10526 mng_info->ping_exclude_iTXt=MagickFalse;
10527 */
glennrp2cc891a2010-12-24 13:44:32 +000010528
glennrp03812ae2010-12-24 01:31:34 +000010529 if (LocaleNCompare(value+i,"gama",4) == 0)
10530 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010531
glennrp03812ae2010-12-24 01:31:34 +000010532 if (LocaleNCompare(value+i,"offs",4) == 0)
10533 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010534
glennrp03812ae2010-12-24 01:31:34 +000010535 if (LocaleNCompare(value+i,"phys",4) == 0)
10536 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010537
glennrpa1e3b7b2010-12-24 16:37:33 +000010538 if (LocaleNCompare(value+i,"srgb",4) == 0)
10539 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010540
glennrp03812ae2010-12-24 01:31:34 +000010541 if (LocaleNCompare(value+i,"text",4) == 0)
10542 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010543
glennrpa1e3b7b2010-12-24 16:37:33 +000010544 if (LocaleNCompare(value+i,"trns",4) == 0)
10545 mng_info->ping_exclude_tRNS=MagickFalse;
10546
glennrp03812ae2010-12-24 01:31:34 +000010547 if (LocaleNCompare(value+i,"vpag",4) == 0)
10548 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010549
glennrp03812ae2010-12-24 01:31:34 +000010550 if (LocaleNCompare(value+i,"zccp",4) == 0)
10551 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010552
glennrp03812ae2010-12-24 01:31:34 +000010553 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10554 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010555
glennrp03812ae2010-12-24 01:31:34 +000010556 }
glennrpce91ed52010-12-23 22:37:49 +000010557 }
glennrp26f37912010-12-23 16:22:42 +000010558 }
10559
glennrp03812ae2010-12-24 01:31:34 +000010560 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010561 {
10562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10563 " Chunks to be excluded from the output PNG:");
10564 if (mng_info->ping_exclude_bKGD != MagickFalse)
10565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10566 " bKGD");
10567 if (mng_info->ping_exclude_cHRM != MagickFalse)
10568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10569 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010570 if (mng_info->ping_exclude_date != MagickFalse)
10571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10572 " date");
glennrp26f37912010-12-23 16:22:42 +000010573 if (mng_info->ping_exclude_EXIF != MagickFalse)
10574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10575 " EXIF");
10576 if (mng_info->ping_exclude_gAMA != MagickFalse)
10577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10578 " gAMA");
10579 if (mng_info->ping_exclude_iCCP != MagickFalse)
10580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10581 " iCCP");
10582/*
10583 if (mng_info->ping_exclude_iTXt != MagickFalse)
10584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10585 " iTXt");
10586*/
10587 if (mng_info->ping_exclude_oFFs != MagickFalse)
10588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10589 " oFFs");
10590 if (mng_info->ping_exclude_pHYs != MagickFalse)
10591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10592 " pHYs");
10593 if (mng_info->ping_exclude_sRGB != MagickFalse)
10594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10595 " sRGB");
10596 if (mng_info->ping_exclude_tEXt != MagickFalse)
10597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10598 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010599 if (mng_info->ping_exclude_tRNS != MagickFalse)
10600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10601 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010602 if (mng_info->ping_exclude_vpAg != MagickFalse)
10603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10604 " vpAg");
10605 if (mng_info->ping_exclude_zCCP != MagickFalse)
10606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10607 " zCCP");
10608 if (mng_info->ping_exclude_zTXt != MagickFalse)
10609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610 " zTXt");
10611 }
10612
glennrpb9cfe272010-12-21 15:08:06 +000010613 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010614
glennrpb9cfe272010-12-21 15:08:06 +000010615 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010616
10617 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010618
cristy3ed852e2009-09-05 21:47:34 +000010619 if (logging != MagickFalse)
10620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010621
cristy3ed852e2009-09-05 21:47:34 +000010622 return(status);
10623}
10624
10625#if defined(JNG_SUPPORTED)
10626
10627/* Write one JNG image */
10628static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10629 const ImageInfo *image_info,Image *image)
10630{
10631 Image
10632 *jpeg_image;
10633
10634 ImageInfo
10635 *jpeg_image_info;
10636
10637 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010638 logging,
cristy3ed852e2009-09-05 21:47:34 +000010639 status;
10640
10641 size_t
10642 length;
10643
10644 unsigned char
10645 *blob,
10646 chunk[80],
10647 *p;
10648
10649 unsigned int
10650 jng_alpha_compression_method,
10651 jng_alpha_sample_depth,
10652 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010653 transparent;
10654
cristybb503372010-05-27 20:51:26 +000010655 size_t
cristy3ed852e2009-09-05 21:47:34 +000010656 jng_quality;
10657
10658 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010659 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010660
10661 blob=(unsigned char *) NULL;
10662 jpeg_image=(Image *) NULL;
10663 jpeg_image_info=(ImageInfo *) NULL;
10664
10665 status=MagickTrue;
10666 transparent=image_info->type==GrayscaleMatteType ||
10667 image_info->type==TrueColorMatteType;
10668 jng_color_type=10;
10669 jng_alpha_sample_depth=0;
10670 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10671 jng_alpha_compression_method=0;
10672
10673 if (image->matte != MagickFalse)
10674 {
10675 /* if any pixels are transparent */
10676 transparent=MagickTrue;
10677 if (image_info->compression==JPEGCompression)
10678 jng_alpha_compression_method=8;
10679 }
10680
10681 if (transparent)
10682 {
10683 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010684
cristy3ed852e2009-09-05 21:47:34 +000010685 /* Create JPEG blob, image, and image_info */
10686 if (logging != MagickFalse)
10687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10688 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010689
cristy3ed852e2009-09-05 21:47:34 +000010690 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010691
cristy3ed852e2009-09-05 21:47:34 +000010692 if (jpeg_image_info == (ImageInfo *) NULL)
10693 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010694
cristy3ed852e2009-09-05 21:47:34 +000010695 if (logging != MagickFalse)
10696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10697 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010698
cristy3ed852e2009-09-05 21:47:34 +000010699 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010700
cristy3ed852e2009-09-05 21:47:34 +000010701 if (jpeg_image == (Image *) NULL)
10702 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010703
cristy3ed852e2009-09-05 21:47:34 +000010704 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10705 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10706 status=NegateImage(jpeg_image,MagickFalse);
10707 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010708
cristy3ed852e2009-09-05 21:47:34 +000010709 if (jng_quality >= 1000)
10710 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010711
cristy3ed852e2009-09-05 21:47:34 +000010712 else
10713 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010714
cristy3ed852e2009-09-05 21:47:34 +000010715 jpeg_image_info->type=GrayscaleType;
10716 (void) SetImageType(jpeg_image,GrayscaleType);
10717 (void) AcquireUniqueFilename(jpeg_image->filename);
10718 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10719 "%s",jpeg_image->filename);
10720 }
10721
10722 /* To do: check bit depth of PNG alpha channel */
10723
10724 /* Check if image is grayscale. */
10725 if (image_info->type != TrueColorMatteType && image_info->type !=
10726 TrueColorType && ImageIsGray(image))
10727 jng_color_type-=2;
10728
10729 if (transparent)
10730 {
10731 if (jng_alpha_compression_method==0)
10732 {
10733 const char
10734 *value;
10735
10736 /* Encode opacity as a grayscale PNG blob */
10737 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10738 &image->exception);
10739 if (logging != MagickFalse)
10740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10741 " Creating PNG blob.");
10742 length=0;
10743
10744 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10745 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10746 jpeg_image_info->interlace=NoInterlace;
10747
10748 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10749 &image->exception);
10750
10751 /* Retrieve sample depth used */
10752 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10753 if (value != (char *) NULL)
10754 jng_alpha_sample_depth= (unsigned int) value[0];
10755 }
10756 else
10757 {
10758 /* Encode opacity as a grayscale JPEG blob */
10759
10760 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10761 &image->exception);
10762
10763 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10764 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10765 jpeg_image_info->interlace=NoInterlace;
10766 if (logging != MagickFalse)
10767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10768 " Creating blob.");
10769 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010770 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010771 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010772
cristy3ed852e2009-09-05 21:47:34 +000010773 if (logging != MagickFalse)
10774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010775 " Successfully read jpeg_image into a blob, length=%.20g.",
10776 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010777
10778 }
10779 /* Destroy JPEG image and image_info */
10780 jpeg_image=DestroyImage(jpeg_image);
10781 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10782 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10783 }
10784
10785 /* Write JHDR chunk */
10786 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10787 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010788 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010789 PNGLong(chunk+4,(png_uint_32) image->columns);
10790 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010791 chunk[12]=jng_color_type;
10792 chunk[13]=8; /* sample depth */
10793 chunk[14]=8; /*jng_image_compression_method */
10794 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10795 chunk[16]=jng_alpha_sample_depth;
10796 chunk[17]=jng_alpha_compression_method;
10797 chunk[18]=0; /*jng_alpha_filter_method */
10798 chunk[19]=0; /*jng_alpha_interlace_method */
10799 (void) WriteBlob(image,20,chunk);
10800 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10801 if (logging != MagickFalse)
10802 {
10803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010804 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010805
cristy3ed852e2009-09-05 21:47:34 +000010806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010807 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010808
cristy3ed852e2009-09-05 21:47:34 +000010809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10810 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010811
cristy3ed852e2009-09-05 21:47:34 +000010812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10813 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010814
cristy3ed852e2009-09-05 21:47:34 +000010815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10816 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010817
cristy3ed852e2009-09-05 21:47:34 +000010818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10819 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010820
cristy3ed852e2009-09-05 21:47:34 +000010821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10822 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010823
cristy3ed852e2009-09-05 21:47:34 +000010824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10825 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010826
cristy3ed852e2009-09-05 21:47:34 +000010827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10828 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010829
cristy3ed852e2009-09-05 21:47:34 +000010830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10831 " JNG alpha interlace:%5d",0);
10832 }
10833
glennrp0fe50b42010-11-16 03:52:51 +000010834 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010835 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010836
10837 /*
10838 Write leading ancillary chunks
10839 */
10840
10841 if (transparent)
10842 {
10843 /*
10844 Write JNG bKGD chunk
10845 */
10846
10847 unsigned char
10848 blue,
10849 green,
10850 red;
10851
cristybb503372010-05-27 20:51:26 +000010852 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010853 num_bytes;
10854
10855 if (jng_color_type == 8 || jng_color_type == 12)
10856 num_bytes=6L;
10857 else
10858 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010859 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010860 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010861 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010862 red=ScaleQuantumToChar(image->background_color.red);
10863 green=ScaleQuantumToChar(image->background_color.green);
10864 blue=ScaleQuantumToChar(image->background_color.blue);
10865 *(chunk+4)=0;
10866 *(chunk+5)=red;
10867 *(chunk+6)=0;
10868 *(chunk+7)=green;
10869 *(chunk+8)=0;
10870 *(chunk+9)=blue;
10871 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10872 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10873 }
10874
10875 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10876 {
10877 /*
10878 Write JNG sRGB chunk
10879 */
10880 (void) WriteBlobMSBULong(image,1L);
10881 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010882 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010883
cristy3ed852e2009-09-05 21:47:34 +000010884 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010885 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010886 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010887 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010888
cristy3ed852e2009-09-05 21:47:34 +000010889 else
glennrpe610a072010-08-05 17:08:46 +000010890 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010891 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010892 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010893
cristy3ed852e2009-09-05 21:47:34 +000010894 (void) WriteBlob(image,5,chunk);
10895 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10896 }
10897 else
10898 {
10899 if (image->gamma != 0.0)
10900 {
10901 /*
10902 Write JNG gAMA chunk
10903 */
10904 (void) WriteBlobMSBULong(image,4L);
10905 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010906 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010907 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010908 (void) WriteBlob(image,8,chunk);
10909 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10910 }
glennrp0fe50b42010-11-16 03:52:51 +000010911
cristy3ed852e2009-09-05 21:47:34 +000010912 if ((mng_info->equal_chrms == MagickFalse) &&
10913 (image->chromaticity.red_primary.x != 0.0))
10914 {
10915 PrimaryInfo
10916 primary;
10917
10918 /*
10919 Write JNG cHRM chunk
10920 */
10921 (void) WriteBlobMSBULong(image,32L);
10922 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010923 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010924 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010925 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10926 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010927 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010928 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10929 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010930 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010931 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10932 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010933 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010934 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10935 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010936 (void) WriteBlob(image,36,chunk);
10937 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10938 }
10939 }
glennrp0fe50b42010-11-16 03:52:51 +000010940
cristy3ed852e2009-09-05 21:47:34 +000010941 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10942 {
10943 /*
10944 Write JNG pHYs chunk
10945 */
10946 (void) WriteBlobMSBULong(image,9L);
10947 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000010948 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000010949 if (image->units == PixelsPerInchResolution)
10950 {
cristy35ef8242010-06-03 16:24:13 +000010951 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010952 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010953
cristy35ef8242010-06-03 16:24:13 +000010954 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010955 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010956
cristy3ed852e2009-09-05 21:47:34 +000010957 chunk[12]=1;
10958 }
glennrp0fe50b42010-11-16 03:52:51 +000010959
cristy3ed852e2009-09-05 21:47:34 +000010960 else
10961 {
10962 if (image->units == PixelsPerCentimeterResolution)
10963 {
cristy35ef8242010-06-03 16:24:13 +000010964 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010965 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010966
cristy35ef8242010-06-03 16:24:13 +000010967 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010968 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010969
cristy3ed852e2009-09-05 21:47:34 +000010970 chunk[12]=1;
10971 }
glennrp0fe50b42010-11-16 03:52:51 +000010972
cristy3ed852e2009-09-05 21:47:34 +000010973 else
10974 {
cristy35ef8242010-06-03 16:24:13 +000010975 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10976 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010977 chunk[12]=0;
10978 }
10979 }
10980 (void) WriteBlob(image,13,chunk);
10981 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10982 }
10983
10984 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10985 {
10986 /*
10987 Write JNG oFFs chunk
10988 */
10989 (void) WriteBlobMSBULong(image,9L);
10990 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000010991 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000010992 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10993 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000010994 chunk[12]=0;
10995 (void) WriteBlob(image,13,chunk);
10996 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10997 }
10998 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10999 {
11000 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11001 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011002 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011003 PNGLong(chunk+4,(png_uint_32) image->page.width);
11004 PNGLong(chunk+8,(png_uint_32) image->page.height);
11005 chunk[12]=0; /* unit = pixels */
11006 (void) WriteBlob(image,13,chunk);
11007 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11008 }
11009
11010
11011 if (transparent)
11012 {
11013 if (jng_alpha_compression_method==0)
11014 {
cristybb503372010-05-27 20:51:26 +000011015 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011016 i;
11017
cristybb503372010-05-27 20:51:26 +000011018 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011019 len;
11020
11021 /* Write IDAT chunk header */
11022 if (logging != MagickFalse)
11023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011024 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011025 length);
cristy3ed852e2009-09-05 21:47:34 +000011026
11027 /* Copy IDAT chunks */
11028 len=0;
11029 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011030 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011031 {
11032 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11033 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011034
cristy3ed852e2009-09-05 21:47:34 +000011035 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11036 {
11037 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011038 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011039 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011040 (void) WriteBlob(image,(size_t) len+4,p);
11041 (void) WriteBlobMSBULong(image,
11042 crc32(0,p,(uInt) len+4));
11043 }
glennrp0fe50b42010-11-16 03:52:51 +000011044
cristy3ed852e2009-09-05 21:47:34 +000011045 else
11046 {
11047 if (logging != MagickFalse)
11048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011049 " Skipping %c%c%c%c chunk, length=%.20g.",
11050 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011051 }
11052 p+=(8+len);
11053 }
11054 }
11055 else
11056 {
11057 /* Write JDAA chunk header */
11058 if (logging != MagickFalse)
11059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011060 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011061 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011062 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011063 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011064 /* Write JDAT chunk(s) data */
11065 (void) WriteBlob(image,4,chunk);
11066 (void) WriteBlob(image,length,blob);
11067 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11068 (uInt) length));
11069 }
11070 blob=(unsigned char *) RelinquishMagickMemory(blob);
11071 }
11072
11073 /* Encode image as a JPEG blob */
11074 if (logging != MagickFalse)
11075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11076 " Creating jpeg_image_info.");
11077 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11078 if (jpeg_image_info == (ImageInfo *) NULL)
11079 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11080
11081 if (logging != MagickFalse)
11082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11083 " Creating jpeg_image.");
11084
11085 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11086 if (jpeg_image == (Image *) NULL)
11087 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11088 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11089
11090 (void) AcquireUniqueFilename(jpeg_image->filename);
11091 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11092 jpeg_image->filename);
11093
11094 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11095 &image->exception);
11096
11097 if (logging != MagickFalse)
11098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011099 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11100 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011101
11102 if (jng_color_type == 8 || jng_color_type == 12)
11103 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011104
cristy3ed852e2009-09-05 21:47:34 +000011105 jpeg_image_info->quality=jng_quality % 1000;
11106 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11107 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011108
cristy3ed852e2009-09-05 21:47:34 +000011109 if (logging != MagickFalse)
11110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11111 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011112
cristy3ed852e2009-09-05 21:47:34 +000011113 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011114
cristy3ed852e2009-09-05 21:47:34 +000011115 if (logging != MagickFalse)
11116 {
11117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011118 " Successfully read jpeg_image into a blob, length=%.20g.",
11119 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011120
11121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011122 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011123 }
glennrp0fe50b42010-11-16 03:52:51 +000011124
cristy3ed852e2009-09-05 21:47:34 +000011125 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011126 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011127 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011128 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011129 (void) WriteBlob(image,4,chunk);
11130 (void) WriteBlob(image,length,blob);
11131 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11132
11133 jpeg_image=DestroyImage(jpeg_image);
11134 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11135 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11136 blob=(unsigned char *) RelinquishMagickMemory(blob);
11137
11138 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011139 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011140
11141 /* Write IEND chunk */
11142 (void) WriteBlobMSBULong(image,0L);
11143 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011144 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011145 (void) WriteBlob(image,4,chunk);
11146 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11147
11148 if (logging != MagickFalse)
11149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11150 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011151
cristy3ed852e2009-09-05 21:47:34 +000011152 return(status);
11153}
11154
11155
11156/*
11157%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11158% %
11159% %
11160% %
11161% W r i t e J N G I m a g e %
11162% %
11163% %
11164% %
11165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11166%
11167% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11168%
11169% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11170%
11171% The format of the WriteJNGImage method is:
11172%
11173% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11174%
11175% A description of each parameter follows:
11176%
11177% o image_info: the image info.
11178%
11179% o image: The image.
11180%
11181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11182*/
11183static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11184{
11185 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011186 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011187 logging,
cristy3ed852e2009-09-05 21:47:34 +000011188 status;
11189
11190 MngInfo
11191 *mng_info;
11192
cristy3ed852e2009-09-05 21:47:34 +000011193 /*
11194 Open image file.
11195 */
11196 assert(image_info != (const ImageInfo *) NULL);
11197 assert(image_info->signature == MagickSignature);
11198 assert(image != (Image *) NULL);
11199 assert(image->signature == MagickSignature);
11200 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011201 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011202 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11203 if (status == MagickFalse)
11204 return(status);
11205
11206 /*
11207 Allocate a MngInfo structure.
11208 */
11209 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011210 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011211 if (mng_info == (MngInfo *) NULL)
11212 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11213 /*
11214 Initialize members of the MngInfo structure.
11215 */
11216 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11217 mng_info->image=image;
11218 have_mng_structure=MagickTrue;
11219
11220 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11221
11222 status=WriteOneJNGImage(mng_info,image_info,image);
11223 (void) CloseBlob(image);
11224
11225 (void) CatchImageException(image);
11226 MngInfoFreeStruct(mng_info,&have_mng_structure);
11227 if (logging != MagickFalse)
11228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11229 return(status);
11230}
11231#endif
11232
11233
11234
11235static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11236{
11237 const char
11238 *option;
11239
11240 Image
11241 *next_image;
11242
11243 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011244 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011245 status;
11246
glennrp03812ae2010-12-24 01:31:34 +000011247 volatile MagickBooleanType
11248 logging;
11249
cristy3ed852e2009-09-05 21:47:34 +000011250 MngInfo
11251 *mng_info;
11252
11253 int
cristy3ed852e2009-09-05 21:47:34 +000011254 image_count,
11255 need_iterations,
11256 need_matte;
11257
11258 volatile int
11259#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11260 defined(PNG_MNG_FEATURES_SUPPORTED)
11261 need_local_plte,
11262#endif
11263 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011264 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011265 use_global_plte;
11266
cristybb503372010-05-27 20:51:26 +000011267 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011268 i;
11269
11270 unsigned char
11271 chunk[800];
11272
11273 volatile unsigned int
11274 write_jng,
11275 write_mng;
11276
cristybb503372010-05-27 20:51:26 +000011277 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011278 scene;
11279
cristybb503372010-05-27 20:51:26 +000011280 size_t
cristy3ed852e2009-09-05 21:47:34 +000011281 final_delay=0,
11282 initial_delay;
11283
glennrpd5045b42010-03-24 12:40:35 +000011284#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011285 if (image_info->verbose)
11286 printf("Your PNG library (libpng-%s) is rather old.\n",
11287 PNG_LIBPNG_VER_STRING);
11288#endif
11289
11290 /*
11291 Open image file.
11292 */
11293 assert(image_info != (const ImageInfo *) NULL);
11294 assert(image_info->signature == MagickSignature);
11295 assert(image != (Image *) NULL);
11296 assert(image->signature == MagickSignature);
11297 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011298 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011299 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11300 if (status == MagickFalse)
11301 return(status);
11302
11303 /*
11304 Allocate a MngInfo structure.
11305 */
11306 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011307 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011308 if (mng_info == (MngInfo *) NULL)
11309 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11310 /*
11311 Initialize members of the MngInfo structure.
11312 */
11313 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11314 mng_info->image=image;
11315 have_mng_structure=MagickTrue;
11316 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11317
11318 /*
11319 * See if user has requested a specific PNG subformat to be used
11320 * for all of the PNGs in the MNG being written, e.g.,
11321 *
11322 * convert *.png png8:animation.mng
11323 *
11324 * To do: check -define png:bit_depth and png:color_type as well,
11325 * or perhaps use mng:bit_depth and mng:color_type instead for
11326 * global settings.
11327 */
11328
11329 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11330 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11331 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11332
11333 write_jng=MagickFalse;
11334 if (image_info->compression == JPEGCompression)
11335 write_jng=MagickTrue;
11336
11337 mng_info->adjoin=image_info->adjoin &&
11338 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11339
cristy3ed852e2009-09-05 21:47:34 +000011340 if (logging != MagickFalse)
11341 {
11342 /* Log some info about the input */
11343 Image
11344 *p;
11345
11346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11347 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011348
cristy3ed852e2009-09-05 21:47:34 +000011349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011350 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011351
cristy3ed852e2009-09-05 21:47:34 +000011352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11353 " Type: %d",image_info->type);
11354
11355 scene=0;
11356 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11357 {
11358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011359 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011360
cristy3ed852e2009-09-05 21:47:34 +000011361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011362 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011363
cristy3ed852e2009-09-05 21:47:34 +000011364 if (p->matte)
11365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11366 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011367
cristy3ed852e2009-09-05 21:47:34 +000011368 else
11369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11370 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011371
cristy3ed852e2009-09-05 21:47:34 +000011372 if (p->storage_class == PseudoClass)
11373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11374 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011375
cristy3ed852e2009-09-05 21:47:34 +000011376 else
11377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11378 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011379
cristy3ed852e2009-09-05 21:47:34 +000011380 if (p->colors)
11381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011382 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011383
cristy3ed852e2009-09-05 21:47:34 +000011384 else
11385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11386 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011387
cristy3ed852e2009-09-05 21:47:34 +000011388 if (mng_info->adjoin == MagickFalse)
11389 break;
11390 }
11391 }
11392
cristy3ed852e2009-09-05 21:47:34 +000011393 use_global_plte=MagickFalse;
11394 all_images_are_gray=MagickFalse;
11395#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11396 need_local_plte=MagickTrue;
11397#endif
11398 need_defi=MagickFalse;
11399 need_matte=MagickFalse;
11400 mng_info->framing_mode=1;
11401 mng_info->old_framing_mode=1;
11402
11403 if (write_mng)
11404 if (image_info->page != (char *) NULL)
11405 {
11406 /*
11407 Determine image bounding box.
11408 */
11409 SetGeometry(image,&mng_info->page);
11410 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11411 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11412 }
11413 if (write_mng)
11414 {
11415 unsigned int
11416 need_geom;
11417
11418 unsigned short
11419 red,
11420 green,
11421 blue;
11422
11423 mng_info->page=image->page;
11424 need_geom=MagickTrue;
11425 if (mng_info->page.width || mng_info->page.height)
11426 need_geom=MagickFalse;
11427 /*
11428 Check all the scenes.
11429 */
11430 initial_delay=image->delay;
11431 need_iterations=MagickFalse;
11432 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11433 mng_info->equal_physs=MagickTrue,
11434 mng_info->equal_gammas=MagickTrue;
11435 mng_info->equal_srgbs=MagickTrue;
11436 mng_info->equal_backgrounds=MagickTrue;
11437 image_count=0;
11438#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11439 defined(PNG_MNG_FEATURES_SUPPORTED)
11440 all_images_are_gray=MagickTrue;
11441 mng_info->equal_palettes=MagickFalse;
11442 need_local_plte=MagickFalse;
11443#endif
11444 for (next_image=image; next_image != (Image *) NULL; )
11445 {
11446 if (need_geom)
11447 {
11448 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11449 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011450
cristy3ed852e2009-09-05 21:47:34 +000011451 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11452 mng_info->page.height=next_image->rows+next_image->page.y;
11453 }
glennrp0fe50b42010-11-16 03:52:51 +000011454
cristy3ed852e2009-09-05 21:47:34 +000011455 if (next_image->page.x || next_image->page.y)
11456 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011457
cristy3ed852e2009-09-05 21:47:34 +000011458 if (next_image->matte)
11459 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011460
cristy3ed852e2009-09-05 21:47:34 +000011461 if ((int) next_image->dispose >= BackgroundDispose)
11462 if (next_image->matte || next_image->page.x || next_image->page.y ||
11463 ((next_image->columns < mng_info->page.width) &&
11464 (next_image->rows < mng_info->page.height)))
11465 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011466
cristy3ed852e2009-09-05 21:47:34 +000011467 if (next_image->iterations)
11468 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011469
cristy3ed852e2009-09-05 21:47:34 +000011470 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011471
cristy3ed852e2009-09-05 21:47:34 +000011472 if (final_delay != initial_delay || final_delay > 1UL*
11473 next_image->ticks_per_second)
11474 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011475
cristy3ed852e2009-09-05 21:47:34 +000011476#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11477 defined(PNG_MNG_FEATURES_SUPPORTED)
11478 /*
11479 check for global palette possibility.
11480 */
11481 if (image->matte != MagickFalse)
11482 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011483
cristy3ed852e2009-09-05 21:47:34 +000011484 if (need_local_plte == 0)
11485 {
11486 if (ImageIsGray(image) == MagickFalse)
11487 all_images_are_gray=MagickFalse;
11488 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11489 if (use_global_plte == 0)
11490 use_global_plte=mng_info->equal_palettes;
11491 need_local_plte=!mng_info->equal_palettes;
11492 }
11493#endif
11494 if (GetNextImageInList(next_image) != (Image *) NULL)
11495 {
11496 if (next_image->background_color.red !=
11497 next_image->next->background_color.red ||
11498 next_image->background_color.green !=
11499 next_image->next->background_color.green ||
11500 next_image->background_color.blue !=
11501 next_image->next->background_color.blue)
11502 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011503
cristy3ed852e2009-09-05 21:47:34 +000011504 if (next_image->gamma != next_image->next->gamma)
11505 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011506
cristy3ed852e2009-09-05 21:47:34 +000011507 if (next_image->rendering_intent !=
11508 next_image->next->rendering_intent)
11509 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011510
cristy3ed852e2009-09-05 21:47:34 +000011511 if ((next_image->units != next_image->next->units) ||
11512 (next_image->x_resolution != next_image->next->x_resolution) ||
11513 (next_image->y_resolution != next_image->next->y_resolution))
11514 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011515
cristy3ed852e2009-09-05 21:47:34 +000011516 if (mng_info->equal_chrms)
11517 {
11518 if (next_image->chromaticity.red_primary.x !=
11519 next_image->next->chromaticity.red_primary.x ||
11520 next_image->chromaticity.red_primary.y !=
11521 next_image->next->chromaticity.red_primary.y ||
11522 next_image->chromaticity.green_primary.x !=
11523 next_image->next->chromaticity.green_primary.x ||
11524 next_image->chromaticity.green_primary.y !=
11525 next_image->next->chromaticity.green_primary.y ||
11526 next_image->chromaticity.blue_primary.x !=
11527 next_image->next->chromaticity.blue_primary.x ||
11528 next_image->chromaticity.blue_primary.y !=
11529 next_image->next->chromaticity.blue_primary.y ||
11530 next_image->chromaticity.white_point.x !=
11531 next_image->next->chromaticity.white_point.x ||
11532 next_image->chromaticity.white_point.y !=
11533 next_image->next->chromaticity.white_point.y)
11534 mng_info->equal_chrms=MagickFalse;
11535 }
11536 }
11537 image_count++;
11538 next_image=GetNextImageInList(next_image);
11539 }
11540 if (image_count < 2)
11541 {
11542 mng_info->equal_backgrounds=MagickFalse;
11543 mng_info->equal_chrms=MagickFalse;
11544 mng_info->equal_gammas=MagickFalse;
11545 mng_info->equal_srgbs=MagickFalse;
11546 mng_info->equal_physs=MagickFalse;
11547 use_global_plte=MagickFalse;
11548#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11549 need_local_plte=MagickTrue;
11550#endif
11551 need_iterations=MagickFalse;
11552 }
glennrp0fe50b42010-11-16 03:52:51 +000011553
cristy3ed852e2009-09-05 21:47:34 +000011554 if (mng_info->need_fram == MagickFalse)
11555 {
11556 /*
11557 Only certain framing rates 100/n are exactly representable without
11558 the FRAM chunk but we'll allow some slop in VLC files
11559 */
11560 if (final_delay == 0)
11561 {
11562 if (need_iterations != MagickFalse)
11563 {
11564 /*
11565 It's probably a GIF with loop; don't run it *too* fast.
11566 */
glennrp02617122010-07-28 13:07:35 +000011567 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011568 {
11569 final_delay=10;
11570 (void) ThrowMagickException(&image->exception,
11571 GetMagickModule(),CoderWarning,
11572 "input has zero delay between all frames; assuming",
11573 " 10 cs `%s'","");
11574 }
cristy3ed852e2009-09-05 21:47:34 +000011575 }
11576 else
11577 mng_info->ticks_per_second=0;
11578 }
11579 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011580 mng_info->ticks_per_second=(png_uint_32)
11581 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011582 if (final_delay > 50)
11583 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011584
cristy3ed852e2009-09-05 21:47:34 +000011585 if (final_delay > 75)
11586 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011587
cristy3ed852e2009-09-05 21:47:34 +000011588 if (final_delay > 125)
11589 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011590
cristy3ed852e2009-09-05 21:47:34 +000011591 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11592 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11593 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11594 1UL*image->ticks_per_second))
11595 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11596 }
glennrp0fe50b42010-11-16 03:52:51 +000011597
cristy3ed852e2009-09-05 21:47:34 +000011598 if (mng_info->need_fram != MagickFalse)
11599 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11600 /*
11601 If pseudocolor, we should also check to see if all the
11602 palettes are identical and write a global PLTE if they are.
11603 ../glennrp Feb 99.
11604 */
11605 /*
11606 Write the MNG version 1.0 signature and MHDR chunk.
11607 */
11608 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11609 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11610 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011611 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011612 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11613 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011614 PNGLong(chunk+12,mng_info->ticks_per_second);
11615 PNGLong(chunk+16,0L); /* layer count=unknown */
11616 PNGLong(chunk+20,0L); /* frame count=unknown */
11617 PNGLong(chunk+24,0L); /* play time=unknown */
11618 if (write_jng)
11619 {
11620 if (need_matte)
11621 {
11622 if (need_defi || mng_info->need_fram || use_global_plte)
11623 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011624
cristy3ed852e2009-09-05 21:47:34 +000011625 else
11626 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11627 }
glennrp0fe50b42010-11-16 03:52:51 +000011628
cristy3ed852e2009-09-05 21:47:34 +000011629 else
11630 {
11631 if (need_defi || mng_info->need_fram || use_global_plte)
11632 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011633
cristy3ed852e2009-09-05 21:47:34 +000011634 else
11635 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11636 }
11637 }
glennrp0fe50b42010-11-16 03:52:51 +000011638
cristy3ed852e2009-09-05 21:47:34 +000011639 else
11640 {
11641 if (need_matte)
11642 {
11643 if (need_defi || mng_info->need_fram || use_global_plte)
11644 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011645
cristy3ed852e2009-09-05 21:47:34 +000011646 else
11647 PNGLong(chunk+28,9L); /* simplicity=VLC */
11648 }
glennrp0fe50b42010-11-16 03:52:51 +000011649
cristy3ed852e2009-09-05 21:47:34 +000011650 else
11651 {
11652 if (need_defi || mng_info->need_fram || use_global_plte)
11653 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011654
cristy3ed852e2009-09-05 21:47:34 +000011655 else
11656 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11657 }
11658 }
11659 (void) WriteBlob(image,32,chunk);
11660 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11661 option=GetImageOption(image_info,"mng:need-cacheoff");
11662 if (option != (const char *) NULL)
11663 {
11664 size_t
11665 length;
11666
11667 /*
11668 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11669 */
11670 PNGType(chunk,mng_nEED);
11671 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011672 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011673 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011674 length+=4;
11675 (void) WriteBlob(image,length,chunk);
11676 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11677 }
11678 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11679 (GetNextImageInList(image) != (Image *) NULL) &&
11680 (image->iterations != 1))
11681 {
11682 /*
11683 Write MNG TERM chunk
11684 */
11685 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11686 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011687 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011688 chunk[4]=3; /* repeat animation */
11689 chunk[5]=0; /* show last frame when done */
11690 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11691 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011692
cristy3ed852e2009-09-05 21:47:34 +000011693 if (image->iterations == 0)
11694 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011695
cristy3ed852e2009-09-05 21:47:34 +000011696 else
11697 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011698
cristy3ed852e2009-09-05 21:47:34 +000011699 if (logging != MagickFalse)
11700 {
11701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011702 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11703 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011704
cristy3ed852e2009-09-05 21:47:34 +000011705 if (image->iterations == 0)
11706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011707 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011708
cristy3ed852e2009-09-05 21:47:34 +000011709 else
11710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011711 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011712 }
11713 (void) WriteBlob(image,14,chunk);
11714 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11715 }
11716 /*
11717 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11718 */
11719 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11720 mng_info->equal_srgbs)
11721 {
11722 /*
11723 Write MNG sRGB chunk
11724 */
11725 (void) WriteBlobMSBULong(image,1L);
11726 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011727 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011728
cristy3ed852e2009-09-05 21:47:34 +000011729 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011730 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011731 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011732 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011733
cristy3ed852e2009-09-05 21:47:34 +000011734 else
glennrpe610a072010-08-05 17:08:46 +000011735 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011736 Magick_RenderingIntent_to_PNG_RenderingIntent(
11737 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011738
cristy3ed852e2009-09-05 21:47:34 +000011739 (void) WriteBlob(image,5,chunk);
11740 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11741 mng_info->have_write_global_srgb=MagickTrue;
11742 }
glennrp0fe50b42010-11-16 03:52:51 +000011743
cristy3ed852e2009-09-05 21:47:34 +000011744 else
11745 {
11746 if (image->gamma && mng_info->equal_gammas)
11747 {
11748 /*
11749 Write MNG gAMA chunk
11750 */
11751 (void) WriteBlobMSBULong(image,4L);
11752 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011753 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011754 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011755 (void) WriteBlob(image,8,chunk);
11756 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11757 mng_info->have_write_global_gama=MagickTrue;
11758 }
11759 if (mng_info->equal_chrms)
11760 {
11761 PrimaryInfo
11762 primary;
11763
11764 /*
11765 Write MNG cHRM chunk
11766 */
11767 (void) WriteBlobMSBULong(image,32L);
11768 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011769 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011770 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011771 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11772 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011773 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011774 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11775 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011776 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011777 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11778 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011779 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011780 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11781 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011782 (void) WriteBlob(image,36,chunk);
11783 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11784 mng_info->have_write_global_chrm=MagickTrue;
11785 }
11786 }
11787 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11788 {
11789 /*
11790 Write MNG pHYs chunk
11791 */
11792 (void) WriteBlobMSBULong(image,9L);
11793 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011794 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011795
cristy3ed852e2009-09-05 21:47:34 +000011796 if (image->units == PixelsPerInchResolution)
11797 {
cristy35ef8242010-06-03 16:24:13 +000011798 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011799 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011800
cristy35ef8242010-06-03 16:24:13 +000011801 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011802 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011803
cristy3ed852e2009-09-05 21:47:34 +000011804 chunk[12]=1;
11805 }
glennrp0fe50b42010-11-16 03:52:51 +000011806
cristy3ed852e2009-09-05 21:47:34 +000011807 else
11808 {
11809 if (image->units == PixelsPerCentimeterResolution)
11810 {
cristy35ef8242010-06-03 16:24:13 +000011811 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011812 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011813
cristy35ef8242010-06-03 16:24:13 +000011814 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011815 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011816
cristy3ed852e2009-09-05 21:47:34 +000011817 chunk[12]=1;
11818 }
glennrp0fe50b42010-11-16 03:52:51 +000011819
cristy3ed852e2009-09-05 21:47:34 +000011820 else
11821 {
cristy35ef8242010-06-03 16:24:13 +000011822 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11823 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011824 chunk[12]=0;
11825 }
11826 }
11827 (void) WriteBlob(image,13,chunk);
11828 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11829 }
11830 /*
11831 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11832 or does not cover the entire frame.
11833 */
11834 if (write_mng && (image->matte || image->page.x > 0 ||
11835 image->page.y > 0 || (image->page.width &&
11836 (image->page.width+image->page.x < mng_info->page.width))
11837 || (image->page.height && (image->page.height+image->page.y
11838 < mng_info->page.height))))
11839 {
11840 (void) WriteBlobMSBULong(image,6L);
11841 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011842 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011843 red=ScaleQuantumToShort(image->background_color.red);
11844 green=ScaleQuantumToShort(image->background_color.green);
11845 blue=ScaleQuantumToShort(image->background_color.blue);
11846 PNGShort(chunk+4,red);
11847 PNGShort(chunk+6,green);
11848 PNGShort(chunk+8,blue);
11849 (void) WriteBlob(image,10,chunk);
11850 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11851 if (mng_info->equal_backgrounds)
11852 {
11853 (void) WriteBlobMSBULong(image,6L);
11854 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011855 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011856 (void) WriteBlob(image,10,chunk);
11857 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11858 }
11859 }
11860
11861#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11862 if ((need_local_plte == MagickFalse) &&
11863 (image->storage_class == PseudoClass) &&
11864 (all_images_are_gray == MagickFalse))
11865 {
cristybb503372010-05-27 20:51:26 +000011866 size_t
cristy3ed852e2009-09-05 21:47:34 +000011867 data_length;
11868
11869 /*
11870 Write MNG PLTE chunk
11871 */
11872 data_length=3*image->colors;
11873 (void) WriteBlobMSBULong(image,data_length);
11874 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011875 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011876
cristybb503372010-05-27 20:51:26 +000011877 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011878 {
11879 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11880 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11881 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11882 }
glennrp0fe50b42010-11-16 03:52:51 +000011883
cristy3ed852e2009-09-05 21:47:34 +000011884 (void) WriteBlob(image,data_length+4,chunk);
11885 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11886 mng_info->have_write_global_plte=MagickTrue;
11887 }
11888#endif
11889 }
11890 scene=0;
11891 mng_info->delay=0;
11892#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11893 defined(PNG_MNG_FEATURES_SUPPORTED)
11894 mng_info->equal_palettes=MagickFalse;
11895#endif
11896 do
11897 {
11898 if (mng_info->adjoin)
11899 {
11900#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11901 defined(PNG_MNG_FEATURES_SUPPORTED)
11902 /*
11903 If we aren't using a global palette for the entire MNG, check to
11904 see if we can use one for two or more consecutive images.
11905 */
11906 if (need_local_plte && use_global_plte && !all_images_are_gray)
11907 {
11908 if (mng_info->IsPalette)
11909 {
11910 /*
11911 When equal_palettes is true, this image has the same palette
11912 as the previous PseudoClass image
11913 */
11914 mng_info->have_write_global_plte=mng_info->equal_palettes;
11915 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11916 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11917 {
11918 /*
11919 Write MNG PLTE chunk
11920 */
cristybb503372010-05-27 20:51:26 +000011921 size_t
cristy3ed852e2009-09-05 21:47:34 +000011922 data_length;
11923
11924 data_length=3*image->colors;
11925 (void) WriteBlobMSBULong(image,data_length);
11926 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011927 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011928
cristybb503372010-05-27 20:51:26 +000011929 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011930 {
11931 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11932 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11933 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11934 }
glennrp0fe50b42010-11-16 03:52:51 +000011935
cristy3ed852e2009-09-05 21:47:34 +000011936 (void) WriteBlob(image,data_length+4,chunk);
11937 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11938 (uInt) (data_length+4)));
11939 mng_info->have_write_global_plte=MagickTrue;
11940 }
11941 }
11942 else
11943 mng_info->have_write_global_plte=MagickFalse;
11944 }
11945#endif
11946 if (need_defi)
11947 {
cristybb503372010-05-27 20:51:26 +000011948 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011949 previous_x,
11950 previous_y;
11951
11952 if (scene)
11953 {
11954 previous_x=mng_info->page.x;
11955 previous_y=mng_info->page.y;
11956 }
11957 else
11958 {
11959 previous_x=0;
11960 previous_y=0;
11961 }
11962 mng_info->page=image->page;
11963 if ((mng_info->page.x != previous_x) ||
11964 (mng_info->page.y != previous_y))
11965 {
11966 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11967 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000011968 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000011969 chunk[4]=0; /* object 0 MSB */
11970 chunk[5]=0; /* object 0 LSB */
11971 chunk[6]=0; /* visible */
11972 chunk[7]=0; /* abstract */
11973 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11974 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11975 (void) WriteBlob(image,16,chunk);
11976 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11977 }
11978 }
11979 }
11980
11981 mng_info->write_mng=write_mng;
11982
11983 if ((int) image->dispose >= 3)
11984 mng_info->framing_mode=3;
11985
11986 if (mng_info->need_fram && mng_info->adjoin &&
11987 ((image->delay != mng_info->delay) ||
11988 (mng_info->framing_mode != mng_info->old_framing_mode)))
11989 {
11990 if (image->delay == mng_info->delay)
11991 {
11992 /*
11993 Write a MNG FRAM chunk with the new framing mode.
11994 */
11995 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11996 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011997 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000011998 chunk[4]=(unsigned char) mng_info->framing_mode;
11999 (void) WriteBlob(image,5,chunk);
12000 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12001 }
12002 else
12003 {
12004 /*
12005 Write a MNG FRAM chunk with the delay.
12006 */
12007 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12008 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012009 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012010 chunk[4]=(unsigned char) mng_info->framing_mode;
12011 chunk[5]=0; /* frame name separator (no name) */
12012 chunk[6]=2; /* flag for changing default delay */
12013 chunk[7]=0; /* flag for changing frame timeout */
12014 chunk[8]=0; /* flag for changing frame clipping */
12015 chunk[9]=0; /* flag for changing frame sync_id */
12016 PNGLong(chunk+10,(png_uint_32)
12017 ((mng_info->ticks_per_second*
12018 image->delay)/MagickMax(image->ticks_per_second,1)));
12019 (void) WriteBlob(image,14,chunk);
12020 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012021 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012022 }
12023 mng_info->old_framing_mode=mng_info->framing_mode;
12024 }
12025
12026#if defined(JNG_SUPPORTED)
12027 if (image_info->compression == JPEGCompression)
12028 {
12029 ImageInfo
12030 *write_info;
12031
12032 if (logging != MagickFalse)
12033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12034 " Writing JNG object.");
12035 /* To do: specify the desired alpha compression method. */
12036 write_info=CloneImageInfo(image_info);
12037 write_info->compression=UndefinedCompression;
12038 status=WriteOneJNGImage(mng_info,write_info,image);
12039 write_info=DestroyImageInfo(write_info);
12040 }
12041 else
12042#endif
12043 {
12044 if (logging != MagickFalse)
12045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12046 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012047
glennrpb9cfe272010-12-21 15:08:06 +000012048 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012049 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012050
12051 /* We don't want any ancillary chunks written */
12052 mng_info->ping_exclude_bKGD=MagickTrue;
12053 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012054 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012055 mng_info->ping_exclude_EXIF=MagickTrue;
12056 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012057 mng_info->ping_exclude_iCCP=MagickTrue;
12058 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12059 mng_info->ping_exclude_oFFs=MagickTrue;
12060 mng_info->ping_exclude_pHYs=MagickTrue;
12061 mng_info->ping_exclude_sRGB=MagickTrue;
12062 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012063 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012064 mng_info->ping_exclude_vpAg=MagickTrue;
12065 mng_info->ping_exclude_zCCP=MagickTrue;
12066 mng_info->ping_exclude_zTXt=MagickTrue;
12067
cristy3ed852e2009-09-05 21:47:34 +000012068 status=WriteOnePNGImage(mng_info,image_info,image);
12069 }
12070
12071 if (status == MagickFalse)
12072 {
12073 MngInfoFreeStruct(mng_info,&have_mng_structure);
12074 (void) CloseBlob(image);
12075 return(MagickFalse);
12076 }
12077 (void) CatchImageException(image);
12078 if (GetNextImageInList(image) == (Image *) NULL)
12079 break;
12080 image=SyncNextImageInList(image);
12081 status=SetImageProgress(image,SaveImagesTag,scene++,
12082 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012083
cristy3ed852e2009-09-05 21:47:34 +000012084 if (status == MagickFalse)
12085 break;
glennrp0fe50b42010-11-16 03:52:51 +000012086
cristy3ed852e2009-09-05 21:47:34 +000012087 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012088
cristy3ed852e2009-09-05 21:47:34 +000012089 if (write_mng)
12090 {
12091 while (GetPreviousImageInList(image) != (Image *) NULL)
12092 image=GetPreviousImageInList(image);
12093 /*
12094 Write the MEND chunk.
12095 */
12096 (void) WriteBlobMSBULong(image,0x00000000L);
12097 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012098 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012099 (void) WriteBlob(image,4,chunk);
12100 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12101 }
12102 /*
12103 Relinquish resources.
12104 */
12105 (void) CloseBlob(image);
12106 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012107
cristy3ed852e2009-09-05 21:47:34 +000012108 if (logging != MagickFalse)
12109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012110
cristy3ed852e2009-09-05 21:47:34 +000012111 return(MagickTrue);
12112}
glennrpd5045b42010-03-24 12:40:35 +000012113#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012114
cristy3ed852e2009-09-05 21:47:34 +000012115static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12116{
12117 image=image;
12118 printf("Your PNG library is too old: You have libpng-%s\n",
12119 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012120
cristy3ed852e2009-09-05 21:47:34 +000012121 ThrowBinaryException(CoderError,"PNG library is too old",
12122 image_info->filename);
12123}
glennrp39992b42010-11-14 00:03:43 +000012124
cristy3ed852e2009-09-05 21:47:34 +000012125static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12126{
12127 return(WritePNGImage(image_info,image));
12128}
glennrpd5045b42010-03-24 12:40:35 +000012129#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012130#endif