blob: 26380352b1beb92b6d2849f26c07ed52e5aa4e81 [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) \
glennrp8e045c82011-04-27 16:40:27 +0000123 (((color).red == (target).red) && \
124 ((color).green == (target).green) && \
125 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000126#endif
127
glennrpb5d5e6d2011-04-27 17:08:19 +0000128/* Convenience macros for copying RGB or RGB+opacity components
129 * between a pixel and a PixelPacket.
130 */
glennrp8e045c82011-04-27 16:40:27 +0000131
glennrpb5d5e6d2011-04-27 17:08:19 +0000132#define GetRGBOPixelComponents(pixel, packet) \
133 (packet).red = GetRedPixelComponent((pixel)); \
134 (packet).green = GetGreenPixelComponent((pixel)); \
135 (packet).red = GetBluePixelComponent((pixel)); \
136 (packet).opacity = GetOpacityPixelComponent((pixel)); \
137
138#define SetRGBOPixelComponents(pixel, packet) \
139 SetRedPixelComponent((pixel),(packet).red); \
140 SetGreenPixelComponent((pixel),(packet).green); \
141 SetBluePixelComponent((pixel),(packet).blue); \
142 SetOpacityPixelComponent((pixel),(packet).opacity); \
glennrp8e045c82011-04-27 16:40:27 +0000143
144
glennrpb5d5e6d2011-04-27 17:08:19 +0000145#define GetRGBPixelComponents(pixel, packet) \
146 (packet).red = GetRedPixelComponent((pixel)); \
147 (packet).green = GetGreenPixelComponent((pixel)); \
148 (packet).red = GetBluePixelComponent((pixel));
glennrp8e045c82011-04-27 16:40:27 +0000149
glennrpb5d5e6d2011-04-27 17:08:19 +0000150#define SetRGBPixelComponents(pixel, packet) \
151 SetRedPixelComponent((pixel),(packet).red); \
152 SetGreenPixelComponent((pixel),(packet).green); \
153 SetBluePixelComponent((pixel),(packet).blue);
glennrp8e045c82011-04-27 16:40:27 +0000154
cristy3ed852e2009-09-05 21:47:34 +0000155/*
156 Establish thread safety.
157 setjmp/longjmp is claimed to be safe on these platforms:
158 setjmp/longjmp is alleged to be unsafe on these platforms:
159*/
160#ifndef SETJMP_IS_THREAD_SAFE
161#define PNG_SETJMP_NOT_THREAD_SAFE
162#endif
163
164#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
165static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000166 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000167#endif
168
169/*
170 This temporary until I set up malloc'ed object attributes array.
171 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
172 waste more memory.
173*/
174#define MNG_MAX_OBJECTS 256
175
176/*
177 If this not defined, spec is interpreted strictly. If it is
178 defined, an attempt will be made to recover from some errors,
179 including
180 o global PLTE too short
181*/
182#undef MNG_LOOSE
183
184/*
185 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
186 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
187 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
188 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
189 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
190 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
191 will be enabled by default in libpng-1.2.0.
192*/
cristy3ed852e2009-09-05 21:47:34 +0000193#ifdef PNG_MNG_FEATURES_SUPPORTED
194# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
195# define PNG_READ_EMPTY_PLTE_SUPPORTED
196# endif
197# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
198# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
199# endif
200#endif
201
202/*
cristybb503372010-05-27 20:51:26 +0000203 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000204 This macro is only defined in libpng-1.0.3 and later.
205 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
206*/
207#ifndef PNG_UINT_31_MAX
208#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
209#endif
210
211/*
212 Constant strings for known chunk types. If you need to add a chunk,
213 add a string holding the name here. To make the code more
214 portable, we use ASCII numbers like this, not characters.
215*/
216
217static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
218static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
219static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
220static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
221static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
222static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
223static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
224static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
225static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
226static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
227static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
228static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
229static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
230static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
231static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
232static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
233static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
234static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
235static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
236static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
237static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
238static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
239static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
240static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
241static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
242static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
243static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
244static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
245static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
246static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
247static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
248static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
249static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
250static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
251
252#if defined(JNG_SUPPORTED)
253static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
254static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
255static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
256static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
257static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
258static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
259#endif
260
261/*
262Other known chunks that are not yet supported by ImageMagick:
263static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
264static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
265static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
266static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
267static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
268static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
269static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
270static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
271*/
272
273typedef struct _MngBox
274{
cristy8182b072010-05-30 20:10:53 +0000275 long
cristy3ed852e2009-09-05 21:47:34 +0000276 left,
277 right,
278 top,
279 bottom;
280} MngBox;
281
282typedef struct _MngPair
283{
cristy8182b072010-05-30 20:10:53 +0000284 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000285 a,
286 b;
287} MngPair;
288
289#ifdef MNG_OBJECT_BUFFERS
290typedef struct _MngBuffer
291{
292
cristybb503372010-05-27 20:51:26 +0000293 size_t
cristy3ed852e2009-09-05 21:47:34 +0000294 height,
295 width;
296
297 Image
298 *image;
299
300 png_color
301 plte[256];
302
303 int
304 reference_count;
305
306 unsigned char
307 alpha_sample_depth,
308 compression_method,
309 color_type,
310 concrete,
311 filter_method,
312 frozen,
313 image_type,
314 interlace_method,
315 pixel_sample_depth,
316 plte_length,
317 sample_depth,
318 viewable;
319} MngBuffer;
320#endif
321
322typedef struct _MngInfo
323{
324
325#ifdef MNG_OBJECT_BUFFERS
326 MngBuffer
327 *ob[MNG_MAX_OBJECTS];
328#endif
329
330 Image *
331 image;
332
333 RectangleInfo
334 page;
335
336 int
337 adjoin,
338#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
339 bytes_in_read_buffer,
340 found_empty_plte,
341#endif
342 equal_backgrounds,
343 equal_chrms,
344 equal_gammas,
345#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
346 defined(PNG_MNG_FEATURES_SUPPORTED)
347 equal_palettes,
348#endif
349 equal_physs,
350 equal_srgbs,
351 framing_mode,
352 have_global_bkgd,
353 have_global_chrm,
354 have_global_gama,
355 have_global_phys,
356 have_global_sbit,
357 have_global_srgb,
358 have_saved_bkgd_index,
359 have_write_global_chrm,
360 have_write_global_gama,
361 have_write_global_plte,
362 have_write_global_srgb,
363 need_fram,
364 object_id,
365 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000366 saved_bkgd_index;
367
368 int
369 new_number_colors;
370
cristybb503372010-05-27 20:51:26 +0000371 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000372 image_found,
373 loop_count[256],
374 loop_iteration[256],
375 scenes_found,
376 x_off[MNG_MAX_OBJECTS],
377 y_off[MNG_MAX_OBJECTS];
378
379 MngBox
380 clip,
381 frame,
382 image_box,
383 object_clip[MNG_MAX_OBJECTS];
384
385 unsigned char
386 /* These flags could be combined into one byte */
387 exists[MNG_MAX_OBJECTS],
388 frozen[MNG_MAX_OBJECTS],
389 loop_active[256],
390 invisible[MNG_MAX_OBJECTS],
391 viewable[MNG_MAX_OBJECTS];
392
393 MagickOffsetType
394 loop_jump[256];
395
396 png_colorp
397 global_plte;
398
399 png_color_8
400 global_sbit;
401
402 png_byte
403#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
404 read_buffer[8],
405#endif
406 global_trns[256];
407
408 float
409 global_gamma;
410
411 ChromaticityInfo
412 global_chrm;
413
414 RenderingIntent
415 global_srgb_intent;
416
cristy35ef8242010-06-03 16:24:13 +0000417 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000418 delay,
419 global_plte_length,
420 global_trns_length,
421 global_x_pixels_per_unit,
422 global_y_pixels_per_unit,
423 mng_width,
424 mng_height,
425 ticks_per_second;
426
glennrpb9cfe272010-12-21 15:08:06 +0000427 MagickBooleanType
428 need_blob;
429
cristy3ed852e2009-09-05 21:47:34 +0000430 unsigned int
431 IsPalette,
432 global_phys_unit_type,
433 basi_warning,
434 clon_warning,
435 dhdr_warning,
436 jhdr_warning,
437 magn_warning,
438 past_warning,
439 phyg_warning,
440 phys_warning,
441 sbit_warning,
442 show_warning,
443 mng_type,
444 write_mng,
445 write_png_colortype,
446 write_png_depth,
447 write_png8,
448 write_png24,
449 write_png32;
450
451#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000452 size_t
cristy3ed852e2009-09-05 21:47:34 +0000453 basi_width,
454 basi_height;
455
456 unsigned int
457 basi_depth,
458 basi_color_type,
459 basi_compression_method,
460 basi_filter_type,
461 basi_interlace_method,
462 basi_red,
463 basi_green,
464 basi_blue,
465 basi_alpha,
466 basi_viewable;
467#endif
468
469 png_uint_16
470 magn_first,
471 magn_last,
472 magn_mb,
473 magn_ml,
474 magn_mr,
475 magn_mt,
476 magn_mx,
477 magn_my,
478 magn_methx,
479 magn_methy;
480
481 PixelPacket
482 mng_global_bkgd;
483
glennrp26f37912010-12-23 16:22:42 +0000484 /* Added at version 6.6.6-7 */
485 MagickBooleanType
486 ping_exclude_bKGD,
487 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000488 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000489 ping_exclude_EXIF,
490 ping_exclude_gAMA,
491 ping_exclude_iCCP,
492 /* ping_exclude_iTXt, */
493 ping_exclude_oFFs,
494 ping_exclude_pHYs,
495 ping_exclude_sRGB,
496 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000497 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000498 ping_exclude_vpAg,
499 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000500 ping_exclude_zTXt,
501 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000502
cristy3ed852e2009-09-05 21:47:34 +0000503} MngInfo;
504#endif /* VER */
505
506/*
507 Forward declarations.
508*/
509static MagickBooleanType
510 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000511
cristy3ed852e2009-09-05 21:47:34 +0000512static MagickBooleanType
513 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000514
cristy3ed852e2009-09-05 21:47:34 +0000515#if defined(JNG_SUPPORTED)
516static MagickBooleanType
517 WriteJNGImage(const ImageInfo *,Image *);
518#endif
519
glennrp0c3e06b2010-11-19 13:45:02 +0000520#if PNG_LIBPNG_VER > 10011
521
glennrpfd05d622011-02-25 04:10:33 +0000522
glennrp0c3e06b2010-11-19 13:45:02 +0000523#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
524static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000525LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000526{
glennrp67b9c1a2011-04-22 18:47:36 +0000527 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
528 *
529 * This is true if the high byte and the next highest byte of
530 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000531 * are equal to each other. We check this by seeing if the samples
532 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000533 *
534 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000535 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000536 */
537
glennrp3faa9a32011-04-23 14:00:25 +0000538#define QuantumToCharToQuantumEqQuantum(quantum) \
539 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
540
glennrp0c3e06b2010-11-19 13:45:02 +0000541 MagickBooleanType
542 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000543
glennrp03e11f62011-04-22 13:30:16 +0000544 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000545 {
546
547 const PixelPacket
548 *p;
549
550 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000551 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
552 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
553 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
554 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000555
556 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
557 {
558 int indx;
559
560 for (indx=0; indx < (ssize_t) image->colors; indx++)
561 {
glennrp3faa9a32011-04-23 14:00:25 +0000562 ok_to_reduce=(
563 QuantumToCharToQuantumEqQuantum(
564 image->colormap[indx].red) &&
565 QuantumToCharToQuantumEqQuantum(
566 image->colormap[indx].green) &&
567 QuantumToCharToQuantumEqQuantum(
568 image->colormap[indx].blue)) ?
569 MagickTrue : MagickFalse;
570
glennrp0c3e06b2010-11-19 13:45:02 +0000571 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000572 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000573 }
574 }
575
576 if ((ok_to_reduce != MagickFalse) &&
577 (image->storage_class != PseudoClass))
578 {
579 ssize_t
580 y;
581
582 register ssize_t
583 x;
584
585 for (y=0; y < (ssize_t) image->rows; y++)
586 {
587 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
588
589 if (p == (const PixelPacket *) NULL)
590 {
591 ok_to_reduce = MagickFalse;
592 break;
593 }
594
595 for (x=(ssize_t) image->columns-1; x >= 0; x--)
596 {
glennrp3faa9a32011-04-23 14:00:25 +0000597 ok_to_reduce=
598 QuantumToCharToQuantumEqQuantum(GetRedPixelComponent(p)) &&
599 QuantumToCharToQuantumEqQuantum(GetGreenPixelComponent(p)) &&
600 QuantumToCharToQuantumEqQuantum(GetBluePixelComponent(p)) ?
601 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000602
603 if (ok_to_reduce == MagickFalse)
604 break;
605
606 p++;
607 }
glennrp8640fb52010-11-23 15:48:26 +0000608 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +0000609 break;
610 }
611 }
612
613 if (ok_to_reduce != MagickFalse)
614 {
glennrp0c3e06b2010-11-19 13:45:02 +0000615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000616 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +0000617 }
glennrpa6a06632011-01-19 15:15:34 +0000618 else
619 {
620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000621 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +0000622 }
glennrp0c3e06b2010-11-19 13:45:02 +0000623 }
624
625 return ok_to_reduce;
626}
627#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
628
glennrpe610a072010-08-05 17:08:46 +0000629static int
glennrpcf002022011-01-30 02:38:15 +0000630Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +0000631{
glennrpe610a072010-08-05 17:08:46 +0000632 switch (intent)
633 {
634 case PerceptualIntent:
635 return 0;
glennrp0fe50b42010-11-16 03:52:51 +0000636
glennrpe610a072010-08-05 17:08:46 +0000637 case RelativeIntent:
638 return 1;
glennrp0fe50b42010-11-16 03:52:51 +0000639
glennrpe610a072010-08-05 17:08:46 +0000640 case SaturationIntent:
641 return 2;
glennrp0fe50b42010-11-16 03:52:51 +0000642
glennrpe610a072010-08-05 17:08:46 +0000643 case AbsoluteIntent:
644 return 3;
glennrp0fe50b42010-11-16 03:52:51 +0000645
glennrpe610a072010-08-05 17:08:46 +0000646 default:
647 return -1;
648 }
649}
650
651static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +0000652Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +0000653{
glennrpcf002022011-01-30 02:38:15 +0000654 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +0000655 {
656 case 0:
657 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000658
glennrpe610a072010-08-05 17:08:46 +0000659 case 1:
660 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000661
glennrpe610a072010-08-05 17:08:46 +0000662 case 2:
663 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000664
glennrpe610a072010-08-05 17:08:46 +0000665 case 3:
666 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000667
glennrpe610a072010-08-05 17:08:46 +0000668 default:
669 return UndefinedIntent;
670 }
671}
672
cristybb503372010-05-27 20:51:26 +0000673static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000674{
675 if (x > y)
676 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000677
cristy3ed852e2009-09-05 21:47:34 +0000678 return(y);
679}
glennrp0c3e06b2010-11-19 13:45:02 +0000680
cristybb503372010-05-27 20:51:26 +0000681static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000682{
683 if (x < y)
684 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000685
cristy3ed852e2009-09-05 21:47:34 +0000686 return(y);
687}
glennrp0c3e06b2010-11-19 13:45:02 +0000688
cristy3ed852e2009-09-05 21:47:34 +0000689
690/*
691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
692% %
693% %
694% %
695% I m a g e I s G r a y %
696% %
697% %
698% %
699%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
700% %
701% Like IsGrayImage except does not change DirectClass to PseudoClass %
702% %
703%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
704*/
705static MagickBooleanType ImageIsGray(Image *image)
706{
707 register const PixelPacket
708 *p;
709
cristybb503372010-05-27 20:51:26 +0000710 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000711 i,
712 x,
713 y;
714
715 assert(image != (Image *) NULL);
716 assert(image->signature == MagickSignature);
717 if (image->debug != MagickFalse)
718 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
719
720 if (image->storage_class == PseudoClass)
721 {
cristybb503372010-05-27 20:51:26 +0000722 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000723 if (IsGray(image->colormap+i) == MagickFalse)
724 return(MagickFalse);
725 return(MagickTrue);
726 }
cristybb503372010-05-27 20:51:26 +0000727 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000728 {
729 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
730 if (p == (const PixelPacket *) NULL)
731 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000732 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +0000733 {
734 if (IsGray(p) == MagickFalse)
735 return(MagickFalse);
736 p++;
737 }
738 }
739 return(MagickTrue);
740}
glennrpd5045b42010-03-24 12:40:35 +0000741#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000742#endif /* MAGICKCORE_PNG_DELEGATE */
743
744/*
745%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
746% %
747% %
748% %
749% I s M N G %
750% %
751% %
752% %
753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
754%
755% IsMNG() returns MagickTrue if the image format type, identified by the
756% magick string, is MNG.
757%
758% The format of the IsMNG method is:
759%
760% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
761%
762% A description of each parameter follows:
763%
764% o magick: compare image format pattern against these bytes.
765%
766% o length: Specifies the length of the magick string.
767%
768%
769*/
770static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
771{
772 if (length < 8)
773 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000774
cristy3ed852e2009-09-05 21:47:34 +0000775 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
776 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000777
cristy3ed852e2009-09-05 21:47:34 +0000778 return(MagickFalse);
779}
780
781/*
782%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
783% %
784% %
785% %
786% I s J N G %
787% %
788% %
789% %
790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791%
792% IsJNG() returns MagickTrue if the image format type, identified by the
793% magick string, is JNG.
794%
795% The format of the IsJNG method is:
796%
797% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
798%
799% A description of each parameter follows:
800%
801% o magick: compare image format pattern against these bytes.
802%
803% o length: Specifies the length of the magick string.
804%
805%
806*/
807static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
808{
809 if (length < 8)
810 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000811
cristy3ed852e2009-09-05 21:47:34 +0000812 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
813 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000814
cristy3ed852e2009-09-05 21:47:34 +0000815 return(MagickFalse);
816}
817
818/*
819%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
820% %
821% %
822% %
823% I s P N G %
824% %
825% %
826% %
827%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
828%
829% IsPNG() returns MagickTrue if the image format type, identified by the
830% magick string, is PNG.
831%
832% The format of the IsPNG method is:
833%
834% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
835%
836% A description of each parameter follows:
837%
838% o magick: compare image format pattern against these bytes.
839%
840% o length: Specifies the length of the magick string.
841%
842*/
843static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
844{
845 if (length < 8)
846 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000847
cristy3ed852e2009-09-05 21:47:34 +0000848 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
849 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000850
cristy3ed852e2009-09-05 21:47:34 +0000851 return(MagickFalse);
852}
853
854#if defined(MAGICKCORE_PNG_DELEGATE)
855#if defined(__cplusplus) || defined(c_plusplus)
856extern "C" {
857#endif
858
glennrpd5045b42010-03-24 12:40:35 +0000859#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +0000860static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +0000861{
862 unsigned char
863 buffer[4];
864
865 assert(image != (Image *) NULL);
866 assert(image->signature == MagickSignature);
867 buffer[0]=(unsigned char) (value >> 24);
868 buffer[1]=(unsigned char) (value >> 16);
869 buffer[2]=(unsigned char) (value >> 8);
870 buffer[3]=(unsigned char) value;
871 return((size_t) WriteBlob(image,4,buffer));
872}
873
874static void PNGLong(png_bytep p,png_uint_32 value)
875{
876 *p++=(png_byte) ((value >> 24) & 0xff);
877 *p++=(png_byte) ((value >> 16) & 0xff);
878 *p++=(png_byte) ((value >> 8) & 0xff);
879 *p++=(png_byte) (value & 0xff);
880}
881
glennrpa521b2f2010-10-29 04:11:03 +0000882#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +0000883static void PNGsLong(png_bytep p,png_int_32 value)
884{
885 *p++=(png_byte) ((value >> 24) & 0xff);
886 *p++=(png_byte) ((value >> 16) & 0xff);
887 *p++=(png_byte) ((value >> 8) & 0xff);
888 *p++=(png_byte) (value & 0xff);
889}
glennrpa521b2f2010-10-29 04:11:03 +0000890#endif
cristy3ed852e2009-09-05 21:47:34 +0000891
892static void PNGShort(png_bytep p,png_uint_16 value)
893{
894 *p++=(png_byte) ((value >> 8) & 0xff);
895 *p++=(png_byte) (value & 0xff);
896}
897
898static void PNGType(png_bytep p,png_bytep type)
899{
900 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
901}
902
glennrp03812ae2010-12-24 01:31:34 +0000903static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
904 size_t length)
cristy3ed852e2009-09-05 21:47:34 +0000905{
906 if (logging != MagickFalse)
907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000908 " Writing %c%c%c%c chunk, length: %.20g",
909 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000910}
glennrpd5045b42010-03-24 12:40:35 +0000911#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000912
913#if defined(__cplusplus) || defined(c_plusplus)
914}
915#endif
916
glennrpd5045b42010-03-24 12:40:35 +0000917#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000918/*
919%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
920% %
921% %
922% %
923% R e a d P N G I m a g e %
924% %
925% %
926% %
927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
928%
929% ReadPNGImage() reads a Portable Network Graphics (PNG) or
930% Multiple-image Network Graphics (MNG) image file and returns it. It
931% allocates the memory necessary for the new Image structure and returns a
932% pointer to the new image or set of images.
933%
934% MNG support written by Glenn Randers-Pehrson, glennrp@image...
935%
936% The format of the ReadPNGImage method is:
937%
938% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
939%
940% A description of each parameter follows:
941%
942% o image_info: the image info.
943%
944% o exception: return any errors or warnings in this structure.
945%
946% To do, more or less in chronological order (as of version 5.5.2,
947% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
948%
949% Get 16-bit cheap transparency working.
950%
951% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
952%
953% Preserve all unknown and not-yet-handled known chunks found in input
954% PNG file and copy them into output PNG files according to the PNG
955% copying rules.
956%
957% (At this point, PNG encoding should be in full MNG compliance)
958%
959% Provide options for choice of background to use when the MNG BACK
960% chunk is not present or is not mandatory (i.e., leave transparent,
961% user specified, MNG BACK, PNG bKGD)
962%
963% Implement LOOP/ENDL [done, but could do discretionary loops more
964% efficiently by linking in the duplicate frames.].
965%
966% Decode and act on the MHDR simplicity profile (offer option to reject
967% files or attempt to process them anyway when the profile isn't LC or VLC).
968%
969% Upgrade to full MNG without Delta-PNG.
970%
971% o BACK [done a while ago except for background image ID]
972% o MOVE [done 15 May 1999]
973% o CLIP [done 15 May 1999]
974% o DISC [done 19 May 1999]
975% o SAVE [partially done 19 May 1999 (marks objects frozen)]
976% o SEEK [partially done 19 May 1999 (discard function only)]
977% o SHOW
978% o PAST
979% o BASI
980% o MNG-level tEXt/iTXt/zTXt
981% o pHYg
982% o pHYs
983% o sBIT
984% o bKGD
985% o iTXt (wait for libpng implementation).
986%
987% Use the scene signature to discover when an identical scene is
988% being reused, and just point to the original image->exception instead
989% of storing another set of pixels. This not specific to MNG
990% but could be applied generally.
991%
992% Upgrade to full MNG with Delta-PNG.
993%
994% JNG tEXt/iTXt/zTXt
995%
996% We will not attempt to read files containing the CgBI chunk.
997% They are really Xcode files meant for display on the iPhone.
998% These are not valid PNG files and it is impossible to recover
999% the orginal PNG from files that have been converted to Xcode-PNG,
1000% since irretrievable loss of color data has occurred due to the
1001% use of premultiplied alpha.
1002*/
1003
1004#if defined(__cplusplus) || defined(c_plusplus)
1005extern "C" {
1006#endif
1007
1008/*
1009 This the function that does the actual reading of data. It is
1010 the same as the one supplied in libpng, except that it receives the
1011 datastream from the ReadBlob() function instead of standard input.
1012*/
1013static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1014{
1015 Image
1016 *image;
1017
1018 image=(Image *) png_get_io_ptr(png_ptr);
1019 if (length)
1020 {
1021 png_size_t
1022 check;
1023
1024 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1025 if (check != length)
1026 {
1027 char
1028 msg[MaxTextExtent];
1029
1030 (void) FormatMagickString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001031 "Expected %.20g bytes; found %.20g bytes",(double) length,
1032 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001033 png_warning(png_ptr,msg);
1034 png_error(png_ptr,"Read Exception");
1035 }
1036 }
1037}
1038
1039#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1040 !defined(PNG_MNG_FEATURES_SUPPORTED)
1041/* We use mng_get_data() instead of png_get_data() if we have a libpng
1042 * older than libpng-1.0.3a, which was the first to allow the empty
1043 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1044 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1045 * encountered after an empty PLTE, so we have to look ahead for bKGD
1046 * chunks and remove them from the datastream that is passed to libpng,
1047 * and store their contents for later use.
1048 */
1049static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1050{
1051 MngInfo
1052 *mng_info;
1053
1054 Image
1055 *image;
1056
1057 png_size_t
1058 check;
1059
cristybb503372010-05-27 20:51:26 +00001060 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001061 i;
1062
1063 i=0;
1064 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1065 image=(Image *) mng_info->image;
1066 while (mng_info->bytes_in_read_buffer && length)
1067 {
1068 data[i]=mng_info->read_buffer[i];
1069 mng_info->bytes_in_read_buffer--;
1070 length--;
1071 i++;
1072 }
1073 if (length)
1074 {
1075 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001076
cristy3ed852e2009-09-05 21:47:34 +00001077 if (check != length)
1078 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001079
cristy3ed852e2009-09-05 21:47:34 +00001080 if (length == 4)
1081 {
1082 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1083 (data[3] == 0))
1084 {
1085 check=(png_size_t) ReadBlob(image,(size_t) length,
1086 (char *) mng_info->read_buffer);
1087 mng_info->read_buffer[4]=0;
1088 mng_info->bytes_in_read_buffer=4;
1089 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1090 mng_info->found_empty_plte=MagickTrue;
1091 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1092 {
1093 mng_info->found_empty_plte=MagickFalse;
1094 mng_info->have_saved_bkgd_index=MagickFalse;
1095 }
1096 }
glennrp0fe50b42010-11-16 03:52:51 +00001097
cristy3ed852e2009-09-05 21:47:34 +00001098 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1099 (data[3] == 1))
1100 {
1101 check=(png_size_t) ReadBlob(image,(size_t) length,
1102 (char *) mng_info->read_buffer);
1103 mng_info->read_buffer[4]=0;
1104 mng_info->bytes_in_read_buffer=4;
1105 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1106 if (mng_info->found_empty_plte)
1107 {
1108 /*
1109 Skip the bKGD data byte and CRC.
1110 */
1111 check=(png_size_t)
1112 ReadBlob(image,5,(char *) mng_info->read_buffer);
1113 check=(png_size_t) ReadBlob(image,(size_t) length,
1114 (char *) mng_info->read_buffer);
1115 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1116 mng_info->have_saved_bkgd_index=MagickTrue;
1117 mng_info->bytes_in_read_buffer=0;
1118 }
1119 }
1120 }
1121 }
1122}
1123#endif
1124
1125static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1126{
1127 Image
1128 *image;
1129
1130 image=(Image *) png_get_io_ptr(png_ptr);
1131 if (length)
1132 {
1133 png_size_t
1134 check;
1135
cristybb503372010-05-27 20:51:26 +00001136 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001137
cristy3ed852e2009-09-05 21:47:34 +00001138 if (check != length)
1139 png_error(png_ptr,"WriteBlob Failed");
1140 }
1141}
1142
1143static void png_flush_data(png_structp png_ptr)
1144{
1145 (void) png_ptr;
1146}
1147
1148#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1149static int PalettesAreEqual(Image *a,Image *b)
1150{
cristybb503372010-05-27 20:51:26 +00001151 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001152 i;
1153
1154 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1155 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001156
cristy3ed852e2009-09-05 21:47:34 +00001157 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1158 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001159
cristy3ed852e2009-09-05 21:47:34 +00001160 if (a->colors != b->colors)
1161 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001162
cristybb503372010-05-27 20:51:26 +00001163 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001164 {
1165 if ((a->colormap[i].red != b->colormap[i].red) ||
1166 (a->colormap[i].green != b->colormap[i].green) ||
1167 (a->colormap[i].blue != b->colormap[i].blue))
1168 return((int) MagickFalse);
1169 }
glennrp0fe50b42010-11-16 03:52:51 +00001170
cristy3ed852e2009-09-05 21:47:34 +00001171 return((int) MagickTrue);
1172}
1173#endif
1174
1175static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1176{
1177 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1178 mng_info->exists[i] && !mng_info->frozen[i])
1179 {
1180#ifdef MNG_OBJECT_BUFFERS
1181 if (mng_info->ob[i] != (MngBuffer *) NULL)
1182 {
1183 if (mng_info->ob[i]->reference_count > 0)
1184 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001185
cristy3ed852e2009-09-05 21:47:34 +00001186 if (mng_info->ob[i]->reference_count == 0)
1187 {
1188 if (mng_info->ob[i]->image != (Image *) NULL)
1189 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001190
cristy3ed852e2009-09-05 21:47:34 +00001191 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1192 }
1193 }
1194 mng_info->ob[i]=(MngBuffer *) NULL;
1195#endif
1196 mng_info->exists[i]=MagickFalse;
1197 mng_info->invisible[i]=MagickFalse;
1198 mng_info->viewable[i]=MagickFalse;
1199 mng_info->frozen[i]=MagickFalse;
1200 mng_info->x_off[i]=0;
1201 mng_info->y_off[i]=0;
1202 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001203 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001204 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001205 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001206 }
1207}
1208
glennrp21f0e622011-01-07 16:20:57 +00001209static void MngInfoFreeStruct(MngInfo *mng_info,
1210 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001211{
glennrp21f0e622011-01-07 16:20:57 +00001212 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001213 {
cristybb503372010-05-27 20:51:26 +00001214 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001215 i;
1216
1217 for (i=1; i < MNG_MAX_OBJECTS; i++)
1218 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001219
cristy3ed852e2009-09-05 21:47:34 +00001220 if (mng_info->global_plte != (png_colorp) NULL)
1221 mng_info->global_plte=(png_colorp)
1222 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001223
cristy3ed852e2009-09-05 21:47:34 +00001224 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1225 *have_mng_structure=MagickFalse;
1226 }
1227}
1228
1229static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1230{
1231 MngBox
1232 box;
1233
1234 box=box1;
1235 if (box.left < box2.left)
1236 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001237
cristy3ed852e2009-09-05 21:47:34 +00001238 if (box.top < box2.top)
1239 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001240
cristy3ed852e2009-09-05 21:47:34 +00001241 if (box.right > box2.right)
1242 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001243
cristy3ed852e2009-09-05 21:47:34 +00001244 if (box.bottom > box2.bottom)
1245 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001246
cristy3ed852e2009-09-05 21:47:34 +00001247 return box;
1248}
1249
1250static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1251{
1252 MngBox
1253 box;
1254
1255 /*
1256 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1257 */
cristybb503372010-05-27 20:51:26 +00001258 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1259 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1260 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1261 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001262 if (delta_type != 0)
1263 {
1264 box.left+=previous_box.left;
1265 box.right+=previous_box.right;
1266 box.top+=previous_box.top;
1267 box.bottom+=previous_box.bottom;
1268 }
glennrp0fe50b42010-11-16 03:52:51 +00001269
cristy3ed852e2009-09-05 21:47:34 +00001270 return(box);
1271}
1272
1273static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1274 unsigned char *p)
1275{
1276 MngPair
1277 pair;
1278 /*
cristybb503372010-05-27 20:51:26 +00001279 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001280 */
cristy8182b072010-05-30 20:10:53 +00001281 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1282 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001283
cristy3ed852e2009-09-05 21:47:34 +00001284 if (delta_type != 0)
1285 {
1286 pair.a+=previous_pair.a;
1287 pair.b+=previous_pair.b;
1288 }
glennrp0fe50b42010-11-16 03:52:51 +00001289
cristy3ed852e2009-09-05 21:47:34 +00001290 return(pair);
1291}
1292
cristy8182b072010-05-30 20:10:53 +00001293static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001294{
cristy8182b072010-05-30 20:10:53 +00001295 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001296}
1297
glennrpcf002022011-01-30 02:38:15 +00001298static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001299{
1300 Image
1301 *image;
1302
1303 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001304
cristy3ed852e2009-09-05 21:47:34 +00001305 if (image->debug != MagickFalse)
1306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1307 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001308
cristy3ed852e2009-09-05 21:47:34 +00001309 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1310 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001311
glennrpe4017e32011-01-08 17:16:09 +00001312#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001313 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1314 * are building with libpng-1.4.x and can be ignored.
1315 */
cristy3ed852e2009-09-05 21:47:34 +00001316 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001317#else
1318 png_longjmp(ping,1);
1319#endif
cristy3ed852e2009-09-05 21:47:34 +00001320}
1321
glennrpcf002022011-01-30 02:38:15 +00001322static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001323{
1324 Image
1325 *image;
1326
1327 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1328 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001329
cristy3ed852e2009-09-05 21:47:34 +00001330 image=(Image *) png_get_error_ptr(ping);
1331 if (image->debug != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001333 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001334
cristy3ed852e2009-09-05 21:47:34 +00001335 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1336 message,"`%s'",image->filename);
1337}
1338
1339#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001340static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001341{
1342#if (PNG_LIBPNG_VER < 10011)
1343 png_voidp
1344 ret;
1345
1346 png_ptr=png_ptr;
1347 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001348
cristy3ed852e2009-09-05 21:47:34 +00001349 if (ret == NULL)
1350 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001351
cristy3ed852e2009-09-05 21:47:34 +00001352 return(ret);
1353#else
1354 png_ptr=png_ptr;
1355 return((png_voidp) AcquireMagickMemory((size_t) size));
1356#endif
1357}
1358
1359/*
1360 Free a pointer. It is removed from the list at the same time.
1361*/
glennrpcf002022011-01-30 02:38:15 +00001362static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001363{
1364 png_ptr=png_ptr;
1365 ptr=RelinquishMagickMemory(ptr);
1366 return((png_free_ptr) NULL);
1367}
1368#endif
1369
1370#if defined(__cplusplus) || defined(c_plusplus)
1371}
1372#endif
1373
1374static int
glennrpcf002022011-01-30 02:38:15 +00001375Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001376 png_textp text,int ii)
1377{
cristybb503372010-05-27 20:51:26 +00001378 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001379 i;
1380
1381 register unsigned char
1382 *dp;
1383
1384 register png_charp
1385 sp;
1386
1387 png_uint_32
1388 length,
1389 nibbles;
1390
1391 StringInfo
1392 *profile;
1393
glennrp0c3e06b2010-11-19 13:45:02 +00001394 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001395 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1396 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1397 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1398 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1399 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1400 13,14,15};
1401
1402 sp=text[ii].text+1;
1403 /* look for newline */
1404 while (*sp != '\n')
1405 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001406
cristy3ed852e2009-09-05 21:47:34 +00001407 /* look for length */
1408 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1409 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001410
cristyf2f27272009-12-17 14:48:46 +00001411 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001412
glennrp97f90e22011-02-22 05:47:58 +00001413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1414 " length: %lu",(unsigned long) length);
1415
cristy3ed852e2009-09-05 21:47:34 +00001416 while (*sp != ' ' && *sp != '\n')
1417 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001418
cristy3ed852e2009-09-05 21:47:34 +00001419 /* allocate space */
1420 if (length == 0)
1421 {
1422 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1423 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1424 return(MagickFalse);
1425 }
glennrp0fe50b42010-11-16 03:52:51 +00001426
cristy3ed852e2009-09-05 21:47:34 +00001427 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001428
cristy3ed852e2009-09-05 21:47:34 +00001429 if (profile == (StringInfo *) NULL)
1430 {
1431 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1432 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1433 "unable to copy profile");
1434 return(MagickFalse);
1435 }
glennrp0fe50b42010-11-16 03:52:51 +00001436
cristy3ed852e2009-09-05 21:47:34 +00001437 /* copy profile, skipping white space and column 1 "=" signs */
1438 dp=GetStringInfoDatum(profile);
1439 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001440
cristybb503372010-05-27 20:51:26 +00001441 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001442 {
1443 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1444 {
1445 if (*sp == '\0')
1446 {
1447 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1448 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1449 profile=DestroyStringInfo(profile);
1450 return(MagickFalse);
1451 }
1452 sp++;
1453 }
glennrp0fe50b42010-11-16 03:52:51 +00001454
cristy3ed852e2009-09-05 21:47:34 +00001455 if (i%2 == 0)
1456 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001457
cristy3ed852e2009-09-05 21:47:34 +00001458 else
1459 (*dp++)+=unhex[(int) *sp++];
1460 }
1461 /*
1462 We have already read "Raw profile type.
1463 */
1464 (void) SetImageProfile(image,&text[ii].key[17],profile);
1465 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001466
cristy3ed852e2009-09-05 21:47:34 +00001467 if (image_info->verbose)
1468 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001469
cristy3ed852e2009-09-05 21:47:34 +00001470 return MagickTrue;
1471}
1472
1473#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1474static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1475{
1476 Image
1477 *image;
1478
1479
1480 /* The unknown chunk structure contains the chunk data:
1481 png_byte name[5];
1482 png_byte *data;
1483 png_size_t size;
1484
1485 Note that libpng has already taken care of the CRC handling.
1486 */
1487
1488
1489 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1490 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1491 return(0); /* Did not recognize */
1492
1493 /* recognized vpAg */
1494
1495 if (chunk->size != 9)
1496 return(-1); /* Error return */
1497
1498 if (chunk->data[8] != 0)
1499 return(0); /* ImageMagick requires pixel units */
1500
1501 image=(Image *) png_get_user_chunk_ptr(ping);
1502
cristybb503372010-05-27 20:51:26 +00001503 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001504 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001505
cristybb503372010-05-27 20:51:26 +00001506 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001507 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1508
1509 /* Return one of the following: */
1510 /* return(-n); chunk had an error */
1511 /* return(0); did not recognize */
1512 /* return(n); success */
1513
1514 return(1);
1515
1516}
1517#endif
1518
1519/*
1520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521% %
1522% %
1523% %
1524% R e a d O n e P N G I m a g e %
1525% %
1526% %
1527% %
1528%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1529%
1530% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1531% (minus the 8-byte signature) and returns it. It allocates the memory
1532% necessary for the new Image structure and returns a pointer to the new
1533% image.
1534%
1535% The format of the ReadOnePNGImage method is:
1536%
1537% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1538% ExceptionInfo *exception)
1539%
1540% A description of each parameter follows:
1541%
1542% o mng_info: Specifies a pointer to a MngInfo structure.
1543%
1544% o image_info: the image info.
1545%
1546% o exception: return any errors or warnings in this structure.
1547%
1548*/
1549static Image *ReadOnePNGImage(MngInfo *mng_info,
1550 const ImageInfo *image_info, ExceptionInfo *exception)
1551{
1552 /* Read one PNG image */
1553
glennrpcc95c3f2011-04-18 16:46:48 +00001554 /* To do: Read the tIME chunk into the date:modify property */
1555 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1556
cristy3ed852e2009-09-05 21:47:34 +00001557 Image
1558 *image;
1559
1560 int
glennrp4eb39312011-03-30 21:34:55 +00001561 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001562 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001563 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001564 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001565 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001566 pass,
1567 ping_bit_depth,
1568 ping_color_type,
1569 ping_interlace_method,
1570 ping_compression_method,
1571 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001572 ping_num_trans,
1573 unit_type;
1574
1575 double
1576 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001577
glennrpa6a06632011-01-19 15:15:34 +00001578 LongPixelPacket
1579 transparent_color;
1580
cristy3ed852e2009-09-05 21:47:34 +00001581 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00001582 logging,
cristy3ed852e2009-09-05 21:47:34 +00001583 status;
1584
glennrpfaa852b2010-03-30 12:17:00 +00001585 png_bytep
1586 ping_trans_alpha;
1587
1588 png_color_16p
1589 ping_background,
1590 ping_trans_color;
1591
cristy3ed852e2009-09-05 21:47:34 +00001592 png_info
1593 *end_info,
1594 *ping_info;
1595
1596 png_struct
1597 *ping;
1598
1599 png_textp
1600 text;
1601
glennrpfaa852b2010-03-30 12:17:00 +00001602 png_uint_32
1603 ping_height,
1604 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00001605 ping_rowbytes,
1606 x_resolution,
1607 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00001608
cristy3ed852e2009-09-05 21:47:34 +00001609 QuantumInfo
1610 *quantum_info;
1611
1612 unsigned char
glennrpcf002022011-01-30 02:38:15 +00001613 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001614
cristybb503372010-05-27 20:51:26 +00001615 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001616 y;
1617
1618 register unsigned char
1619 *p;
1620
1621 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00001622 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00001623
cristybb503372010-05-27 20:51:26 +00001624 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001625 i,
1626 x;
1627
1628 register PixelPacket
1629 *q;
1630
1631 size_t
glennrp39992b42010-11-14 00:03:43 +00001632 length,
cristy3ed852e2009-09-05 21:47:34 +00001633 row_offset;
1634
cristyeb3b22a2011-03-31 20:16:11 +00001635 ssize_t
1636 j;
1637
cristy3ed852e2009-09-05 21:47:34 +00001638#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1639 png_byte unused_chunks[]=
1640 {
1641 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1642 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1643 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1644 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1645 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1646 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1647 };
1648#endif
1649
1650 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001651 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00001652
1653#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001654 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001655#endif
1656
glennrp25c1e2b2010-03-25 01:39:56 +00001657#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00001658 if (image_info->verbose)
1659 printf("Your PNG library (libpng-%s) is rather old.\n",
1660 PNG_LIBPNG_VER_STRING);
1661#endif
1662
glennrp61b4c952009-11-10 20:40:41 +00001663#if (PNG_LIBPNG_VER >= 10400)
1664# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1665 if (image_info->verbose)
1666 {
1667 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1668 PNG_LIBPNG_VER_STRING);
1669 printf("Please update it.\n");
1670 }
1671# endif
1672#endif
1673
1674
cristyed552522009-10-16 14:04:35 +00001675 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00001676 image=mng_info->image;
1677
glennrpa6a06632011-01-19 15:15:34 +00001678 if (logging != MagickFalse)
1679 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1680 " image->matte=%d",(int) image->matte);
1681
glennrp0e319732011-01-25 21:53:13 +00001682 /* Set to an out-of-range color unless tRNS chunk is present */
1683 transparent_color.red=65537;
1684 transparent_color.green=65537;
1685 transparent_color.blue=65537;
1686 transparent_color.opacity=65537;
1687
glennrpcb395ac2011-03-30 19:50:23 +00001688 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00001689 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00001690 num_raw_profiles = 0;
1691
cristy3ed852e2009-09-05 21:47:34 +00001692 /*
1693 Allocate the PNG structures
1694 */
1695#ifdef PNG_USER_MEM_SUPPORTED
1696 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00001697 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1698 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00001699#else
1700 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00001701 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00001702#endif
1703 if (ping == (png_struct *) NULL)
1704 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00001705
cristy3ed852e2009-09-05 21:47:34 +00001706 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001707
cristy3ed852e2009-09-05 21:47:34 +00001708 if (ping_info == (png_info *) NULL)
1709 {
1710 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1711 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1712 }
glennrp0fe50b42010-11-16 03:52:51 +00001713
cristy3ed852e2009-09-05 21:47:34 +00001714 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001715
cristy3ed852e2009-09-05 21:47:34 +00001716 if (end_info == (png_info *) NULL)
1717 {
1718 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1719 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1720 }
glennrp0fe50b42010-11-16 03:52:51 +00001721
glennrpcf002022011-01-30 02:38:15 +00001722 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00001723
glennrpfaa852b2010-03-30 12:17:00 +00001724 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00001725 {
1726 /*
1727 PNG image is corrupt.
1728 */
1729 png_destroy_read_struct(&ping,&ping_info,&end_info);
1730#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001731 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001732#endif
1733 if (logging != MagickFalse)
1734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1735 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00001736
cristy3ed852e2009-09-05 21:47:34 +00001737 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00001738 {
1739 InheritException(exception,&image->exception);
1740 image->columns=0;
1741 }
glennrp0fe50b42010-11-16 03:52:51 +00001742
cristy3ed852e2009-09-05 21:47:34 +00001743 return(GetFirstImageInList(image));
1744 }
1745 /*
1746 Prepare PNG for reading.
1747 */
glennrpfaa852b2010-03-30 12:17:00 +00001748
cristy3ed852e2009-09-05 21:47:34 +00001749 mng_info->image_found++;
1750 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00001751
cristy3ed852e2009-09-05 21:47:34 +00001752 if (LocaleCompare(image_info->magick,"MNG") == 0)
1753 {
1754#if defined(PNG_MNG_FEATURES_SUPPORTED)
1755 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1756 png_set_read_fn(ping,image,png_get_data);
1757#else
1758#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1759 png_permit_empty_plte(ping,MagickTrue);
1760 png_set_read_fn(ping,image,png_get_data);
1761#else
1762 mng_info->image=image;
1763 mng_info->bytes_in_read_buffer=0;
1764 mng_info->found_empty_plte=MagickFalse;
1765 mng_info->have_saved_bkgd_index=MagickFalse;
1766 png_set_read_fn(ping,mng_info,mng_get_data);
1767#endif
1768#endif
1769 }
glennrp0fe50b42010-11-16 03:52:51 +00001770
cristy3ed852e2009-09-05 21:47:34 +00001771 else
1772 png_set_read_fn(ping,image,png_get_data);
1773
1774#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1775 /* Ignore unused chunks and all unknown chunks except for vpAg */
1776 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1777 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1778 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1779 (int)sizeof(unused_chunks)/5);
1780 /* Callback for other unknown chunks */
1781 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1782#endif
1783
glennrp991e92a2010-01-28 03:09:00 +00001784#if (PNG_LIBPNG_VER < 10400)
1785# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1786 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00001787 /* Disable thread-unsafe features of pnggccrd */
1788 if (png_access_version_number() >= 10200)
1789 {
1790 png_uint_32 mmx_disable_mask=0;
1791 png_uint_32 asm_flags;
1792
1793 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1794 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1795 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1796 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1797 asm_flags=png_get_asm_flags(ping);
1798 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1799 }
glennrp991e92a2010-01-28 03:09:00 +00001800# endif
cristy3ed852e2009-09-05 21:47:34 +00001801#endif
1802
1803 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00001804
1805 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1806 &ping_bit_depth,&ping_color_type,
1807 &ping_interlace_method,&ping_compression_method,
1808 &ping_filter_method);
1809
1810 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1811 &ping_trans_color);
1812
1813 (void) png_get_bKGD(ping, ping_info, &ping_background);
1814
1815 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00001816 {
glennrpfaa852b2010-03-30 12:17:00 +00001817 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1818 {
1819 png_set_packing(ping);
1820 ping_bit_depth = 8;
1821 }
cristy3ed852e2009-09-05 21:47:34 +00001822 }
glennrpfaa852b2010-03-30 12:17:00 +00001823
1824 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00001825 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00001826 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00001827 if (logging != MagickFalse)
1828 {
1829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001830 " PNG width: %.20g, height: %.20g",
1831 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00001832
cristy3ed852e2009-09-05 21:47:34 +00001833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1834 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001835 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00001836
cristy3ed852e2009-09-05 21:47:34 +00001837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1838 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001839 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00001840
cristy3ed852e2009-09-05 21:47:34 +00001841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1842 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001843 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00001844 }
1845
glennrpfaa852b2010-03-30 12:17:00 +00001846#ifdef PNG_READ_iCCP_SUPPORTED
1847 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00001848 {
1849 int
1850 compression;
1851
glennrpe4017e32011-01-08 17:16:09 +00001852#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00001853 png_charp
glennrpe4017e32011-01-08 17:16:09 +00001854 info;
1855#else
1856 png_bytep
1857 info;
1858#endif
1859
1860 png_charp
cristy3ed852e2009-09-05 21:47:34 +00001861 name;
1862
1863 png_uint_32
1864 profile_length;
1865
1866 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1867 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00001868
cristy3ed852e2009-09-05 21:47:34 +00001869 if (profile_length != 0)
1870 {
1871 StringInfo
1872 *profile;
1873
1874 if (logging != MagickFalse)
1875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1876 " Reading PNG iCCP chunk.");
1877 profile=AcquireStringInfo(profile_length);
1878 SetStringInfoDatum(profile,(const unsigned char *) info);
1879 (void) SetImageProfile(image,"icc",profile);
1880 profile=DestroyStringInfo(profile);
1881 }
1882 }
1883#endif
1884#if defined(PNG_READ_sRGB_SUPPORTED)
1885 {
cristy3ed852e2009-09-05 21:47:34 +00001886 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00001887 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1888 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00001889
cristy3ed852e2009-09-05 21:47:34 +00001890 if (png_get_sRGB(ping,ping_info,&intent))
1891 {
glennrpcf002022011-01-30 02:38:15 +00001892 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1893 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00001894
cristy3ed852e2009-09-05 21:47:34 +00001895 if (logging != MagickFalse)
1896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00001897 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00001898 }
1899 }
1900#endif
1901 {
glennrpfaa852b2010-03-30 12:17:00 +00001902 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1903 if (mng_info->have_global_gama)
1904 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00001905
cristy3ed852e2009-09-05 21:47:34 +00001906 if (png_get_gAMA(ping,ping_info,&file_gamma))
1907 {
1908 image->gamma=(float) file_gamma;
1909 if (logging != MagickFalse)
1910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1911 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1912 }
1913 }
glennrpfaa852b2010-03-30 12:17:00 +00001914 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1915 {
1916 if (mng_info->have_global_chrm != MagickFalse)
1917 {
1918 (void) png_set_cHRM(ping,ping_info,
1919 mng_info->global_chrm.white_point.x,
1920 mng_info->global_chrm.white_point.y,
1921 mng_info->global_chrm.red_primary.x,
1922 mng_info->global_chrm.red_primary.y,
1923 mng_info->global_chrm.green_primary.x,
1924 mng_info->global_chrm.green_primary.y,
1925 mng_info->global_chrm.blue_primary.x,
1926 mng_info->global_chrm.blue_primary.y);
1927 }
1928 }
glennrp0fe50b42010-11-16 03:52:51 +00001929
glennrpfaa852b2010-03-30 12:17:00 +00001930 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00001931 {
1932 (void) png_get_cHRM(ping,ping_info,
1933 &image->chromaticity.white_point.x,
1934 &image->chromaticity.white_point.y,
1935 &image->chromaticity.red_primary.x,
1936 &image->chromaticity.red_primary.y,
1937 &image->chromaticity.green_primary.x,
1938 &image->chromaticity.green_primary.y,
1939 &image->chromaticity.blue_primary.x,
1940 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00001941
cristy3ed852e2009-09-05 21:47:34 +00001942 if (logging != MagickFalse)
1943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1944 " Reading PNG cHRM chunk.");
1945 }
glennrp0fe50b42010-11-16 03:52:51 +00001946
glennrpe610a072010-08-05 17:08:46 +00001947 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00001948 {
glennrpe610a072010-08-05 17:08:46 +00001949 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00001950 Magick_RenderingIntent_to_PNG_RenderingIntent
1951 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00001952 png_set_gAMA(ping,ping_info,0.45455f);
1953 png_set_cHRM(ping,ping_info,
1954 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1955 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00001956 }
cristy3ed852e2009-09-05 21:47:34 +00001957#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001958 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00001959 {
cristy905ef802011-02-23 00:29:18 +00001960 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1961 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00001962
cristy3ed852e2009-09-05 21:47:34 +00001963 if (logging != MagickFalse)
1964 if (image->page.x || image->page.y)
1965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001966 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1967 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001968 }
1969#endif
1970#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001971 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1972 {
1973 if (mng_info->have_global_phys)
1974 {
1975 png_set_pHYs(ping,ping_info,
1976 mng_info->global_x_pixels_per_unit,
1977 mng_info->global_y_pixels_per_unit,
1978 mng_info->global_phys_unit_type);
1979 }
1980 }
1981
1982 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00001983 {
cristy3ed852e2009-09-05 21:47:34 +00001984 /*
1985 Set image resolution.
1986 */
1987 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00001988 &unit_type);
1989 image->x_resolution=(double) x_resolution;
1990 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00001991
cristy3ed852e2009-09-05 21:47:34 +00001992 if (unit_type == PNG_RESOLUTION_METER)
1993 {
1994 image->units=PixelsPerCentimeterResolution;
1995 image->x_resolution=(double) x_resolution/100.0;
1996 image->y_resolution=(double) y_resolution/100.0;
1997 }
glennrp0fe50b42010-11-16 03:52:51 +00001998
cristy3ed852e2009-09-05 21:47:34 +00001999 if (logging != MagickFalse)
2000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002001 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2002 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002003 }
cristy3ed852e2009-09-05 21:47:34 +00002004#endif
glennrp823b55c2011-03-14 18:46:46 +00002005
glennrpfaa852b2010-03-30 12:17:00 +00002006 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002007 {
2008 int
2009 number_colors;
2010
2011 png_colorp
2012 palette;
2013
2014 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002015
cristy3ed852e2009-09-05 21:47:34 +00002016 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002017 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002018 {
2019 if (mng_info->global_plte_length)
2020 {
2021 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2022 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002023
glennrpfaa852b2010-03-30 12:17:00 +00002024 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002025 if (mng_info->global_trns_length)
2026 {
2027 if (mng_info->global_trns_length >
2028 mng_info->global_plte_length)
2029 (void) ThrowMagickException(&image->exception,
2030 GetMagickModule(),CoderError,
2031 "global tRNS has more entries than global PLTE",
2032 "`%s'",image_info->filename);
2033 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2034 (int) mng_info->global_trns_length,NULL);
2035 }
glennrpbfd9e612011-04-22 14:02:20 +00002036#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002037 if (
2038#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2039 mng_info->have_saved_bkgd_index ||
2040#endif
glennrpfaa852b2010-03-30 12:17:00 +00002041 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002042 {
2043 png_color_16
2044 background;
2045
2046#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2047 if (mng_info->have_saved_bkgd_index)
2048 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002049#endif
glennrpfaa852b2010-03-30 12:17:00 +00002050 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2051 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002052
cristy3ed852e2009-09-05 21:47:34 +00002053 background.red=(png_uint_16)
2054 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002055
cristy3ed852e2009-09-05 21:47:34 +00002056 background.green=(png_uint_16)
2057 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002058
cristy3ed852e2009-09-05 21:47:34 +00002059 background.blue=(png_uint_16)
2060 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002061
glennrpc6c391a2011-04-27 02:23:56 +00002062 background.gray=(png_uint_16)
2063 mng_info->global_plte[background.index].green;
2064
cristy3ed852e2009-09-05 21:47:34 +00002065 png_set_bKGD(ping,ping_info,&background);
2066 }
2067#endif
2068 }
2069 else
2070 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2071 CoderError,"No global PLTE in file","`%s'",
2072 image_info->filename);
2073 }
2074 }
2075
glennrpbfd9e612011-04-22 14:02:20 +00002076#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002077 if (mng_info->have_global_bkgd &&
2078 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002079 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002080
glennrpfaa852b2010-03-30 12:17:00 +00002081 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002082 {
glennrpbfd9e612011-04-22 14:02:20 +00002083 unsigned int
2084 bkgd_scale;
2085
cristy3ed852e2009-09-05 21:47:34 +00002086 /*
2087 Set image background color.
2088 */
2089 if (logging != MagickFalse)
2090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2091 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002092
glennrpbfd9e612011-04-22 14:02:20 +00002093 /* Scale background components to 16-bit, then scale
2094 * to quantum depth
2095 */
2096 if (logging != MagickFalse)
2097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2098 " raw ping_background=(%d,%d,%d).",ping_background->red,
2099 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002100
glennrpbfd9e612011-04-22 14:02:20 +00002101 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002102
glennrpbfd9e612011-04-22 14:02:20 +00002103 if (ping_bit_depth == 1)
2104 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002105
glennrpbfd9e612011-04-22 14:02:20 +00002106 else if (ping_bit_depth == 2)
2107 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002108
glennrpbfd9e612011-04-22 14:02:20 +00002109 else if (ping_bit_depth == 4)
2110 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002111
glennrpbfd9e612011-04-22 14:02:20 +00002112 if (ping_bit_depth <= 8)
2113 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002114
glennrpbfd9e612011-04-22 14:02:20 +00002115 ping_background->red *= bkgd_scale;
2116 ping_background->green *= bkgd_scale;
2117 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002118
glennrpbfd9e612011-04-22 14:02:20 +00002119 if (logging != MagickFalse)
2120 {
glennrp2cbb4482010-06-02 04:37:24 +00002121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2122 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002123
glennrp2cbb4482010-06-02 04:37:24 +00002124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2125 " ping_background=(%d,%d,%d).",ping_background->red,
2126 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002127 }
glennrp2cbb4482010-06-02 04:37:24 +00002128
glennrpbfd9e612011-04-22 14:02:20 +00002129 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002130 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002131
glennrpbfd9e612011-04-22 14:02:20 +00002132 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002133 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002134
glennrpbfd9e612011-04-22 14:02:20 +00002135 image->background_color.blue=
2136 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002137
glennrpbfd9e612011-04-22 14:02:20 +00002138 image->background_color.opacity=OpaqueOpacity;
glennrp2cbb4482010-06-02 04:37:24 +00002139
glennrpbfd9e612011-04-22 14:02:20 +00002140 if (logging != MagickFalse)
2141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2142 " image->background_color=(%.20g,%.20g,%.20g).",
2143 (double) image->background_color.red,
2144 (double) image->background_color.green,
2145 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002146 }
glennrpbfd9e612011-04-22 14:02:20 +00002147#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002148
glennrpfaa852b2010-03-30 12:17:00 +00002149 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002150 {
2151 /*
glennrpa6a06632011-01-19 15:15:34 +00002152 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002153 */
2154 int
2155 max_sample;
2156
cristy35ef8242010-06-03 16:24:13 +00002157 size_t
2158 one=1;
2159
cristy3ed852e2009-09-05 21:47:34 +00002160 if (logging != MagickFalse)
2161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2162 " Reading PNG tRNS chunk.");
2163
cristyf9cca6a2010-06-04 23:49:28 +00002164 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002165
glennrpfaa852b2010-03-30 12:17:00 +00002166 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2167 (int)ping_trans_color->gray > max_sample) ||
2168 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2169 ((int)ping_trans_color->red > max_sample ||
2170 (int)ping_trans_color->green > max_sample ||
2171 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002172 {
2173 if (logging != MagickFalse)
2174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2175 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002176 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002177 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002178 image->matte=MagickFalse;
2179 }
2180 else
2181 {
glennrpa6a06632011-01-19 15:15:34 +00002182 int
2183 scale_to_short;
2184
2185 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2186
2187 /* Scale transparent_color to short */
2188 transparent_color.red= scale_to_short*ping_trans_color->red;
2189 transparent_color.green= scale_to_short*ping_trans_color->green;
2190 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2191 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002192
glennrpfaa852b2010-03-30 12:17:00 +00002193 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002194 {
glennrp0f111982010-07-07 20:18:33 +00002195 if (logging != MagickFalse)
2196 {
2197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2198 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002199
glennrp0f111982010-07-07 20:18:33 +00002200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2201 " scaled graylevel is %d.",transparent_color.opacity);
2202 }
cristy3ed852e2009-09-05 21:47:34 +00002203 transparent_color.red=transparent_color.opacity;
2204 transparent_color.green=transparent_color.opacity;
2205 transparent_color.blue=transparent_color.opacity;
2206 }
2207 }
2208 }
2209#if defined(PNG_READ_sBIT_SUPPORTED)
2210 if (mng_info->have_global_sbit)
2211 {
glennrpfaa852b2010-03-30 12:17:00 +00002212 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002213 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2214 }
2215#endif
2216 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002217
cristy3ed852e2009-09-05 21:47:34 +00002218 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002219
2220 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2221
cristy3ed852e2009-09-05 21:47:34 +00002222 /*
2223 Initialize image structure.
2224 */
2225 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002226 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002227 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002228 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002229 if (mng_info->mng_type == 0)
2230 {
glennrpfaa852b2010-03-30 12:17:00 +00002231 mng_info->mng_width=ping_width;
2232 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002233 mng_info->frame=mng_info->image_box;
2234 mng_info->clip=mng_info->image_box;
2235 }
glennrp0fe50b42010-11-16 03:52:51 +00002236
cristy3ed852e2009-09-05 21:47:34 +00002237 else
2238 {
2239 image->page.y=mng_info->y_off[mng_info->object_id];
2240 }
glennrp0fe50b42010-11-16 03:52:51 +00002241
cristy3ed852e2009-09-05 21:47:34 +00002242 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002243 image->columns=ping_width;
2244 image->rows=ping_height;
2245 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002246 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002247 {
cristybefe4d22010-06-07 01:18:58 +00002248 size_t
2249 one;
2250
cristy3ed852e2009-09-05 21:47:34 +00002251 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002252 one=1;
2253 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002254#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2255 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002256 image->colors=256;
2257#else
2258 if (image->colors > 65536L)
2259 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002260#endif
glennrpfaa852b2010-03-30 12:17:00 +00002261 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002262 {
2263 int
2264 number_colors;
2265
2266 png_colorp
2267 palette;
2268
2269 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002270 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002271
cristy3ed852e2009-09-05 21:47:34 +00002272 if (logging != MagickFalse)
2273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2274 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2275 }
2276 }
2277
2278 if (image->storage_class == PseudoClass)
2279 {
2280 /*
2281 Initialize image colormap.
2282 */
2283 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2284 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002285
glennrpfaa852b2010-03-30 12:17:00 +00002286 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002287 {
2288 int
2289 number_colors;
2290
2291 png_colorp
2292 palette;
2293
2294 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002295
glennrp6af6cf12011-04-22 13:05:16 +00002296 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002297 {
2298 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2299 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2300 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2301 }
glennrp6af6cf12011-04-22 13:05:16 +00002302
glennrp67b9c1a2011-04-22 18:47:36 +00002303 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002304 {
2305 image->colormap[i].red=0;
2306 image->colormap[i].green=0;
2307 image->colormap[i].blue=0;
2308 }
cristy3ed852e2009-09-05 21:47:34 +00002309 }
glennrp0fe50b42010-11-16 03:52:51 +00002310
cristy3ed852e2009-09-05 21:47:34 +00002311 else
2312 {
cristybb503372010-05-27 20:51:26 +00002313 size_t
cristy3ed852e2009-09-05 21:47:34 +00002314 scale;
2315
glennrpfaa852b2010-03-30 12:17:00 +00002316 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002317
cristy3ed852e2009-09-05 21:47:34 +00002318 if (scale < 1)
2319 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002320
cristybb503372010-05-27 20:51:26 +00002321 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002322 {
2323 image->colormap[i].red=(Quantum) (i*scale);
2324 image->colormap[i].green=(Quantum) (i*scale);
2325 image->colormap[i].blue=(Quantum) (i*scale);
2326 }
2327 }
2328 }
glennrp147bc912011-03-30 18:47:21 +00002329
glennrpcb395ac2011-03-30 19:50:23 +00002330 /* Set some properties for reporting by "identify" */
2331 {
glennrp147bc912011-03-30 18:47:21 +00002332 char
2333 msg[MaxTextExtent];
2334
2335 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2336 ping_interlace_method in value */
2337
glennrp7cdb11c2011-03-31 18:17:25 +00002338 (void) FormatMagickString(msg,MaxTextExtent,
2339 "%d, %d",(int) ping_width, (int) ping_height);
2340 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002341
2342 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2343 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2344
2345 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2346 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2347
2348 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2349 (int) ping_interlace_method);
2350 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002351 }
glennrp147bc912011-03-30 18:47:21 +00002352
cristy3ed852e2009-09-05 21:47:34 +00002353 /*
2354 Read image scanlines.
2355 */
2356 if (image->delay != 0)
2357 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002358
glennrp0ca69b12010-07-26 01:57:52 +00002359 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002360 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2361 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002362 {
2363 if (logging != MagickFalse)
2364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002365 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002366 mng_info->scenes_found-1);
2367 png_destroy_read_struct(&ping,&ping_info,&end_info);
2368#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002369 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002370#endif
2371 if (logging != MagickFalse)
2372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2373 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002374
cristy3ed852e2009-09-05 21:47:34 +00002375 return(image);
2376 }
glennrp0fe50b42010-11-16 03:52:51 +00002377
cristy3ed852e2009-09-05 21:47:34 +00002378 if (logging != MagickFalse)
2379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2380 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002381
cristy3ed852e2009-09-05 21:47:34 +00002382 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002383 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2384 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002385
cristy3ed852e2009-09-05 21:47:34 +00002386 else
glennrpcf002022011-01-30 02:38:15 +00002387 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2388 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002389
glennrpcf002022011-01-30 02:38:15 +00002390 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002391 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002392
cristy3ed852e2009-09-05 21:47:34 +00002393 if (logging != MagickFalse)
2394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2395 " Converting PNG pixels to pixel packets");
2396 /*
2397 Convert PNG pixels to pixel packets.
2398 */
glennrpfaa852b2010-03-30 12:17:00 +00002399 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002400 {
2401 /*
2402 PNG image is corrupt.
2403 */
2404 png_destroy_read_struct(&ping,&ping_info,&end_info);
2405#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002406 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002407#endif
2408 if (quantum_info != (QuantumInfo *) NULL)
2409 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002410
glennrpcf002022011-01-30 02:38:15 +00002411 if (ping_pixels != (unsigned char *) NULL)
2412 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002413
cristy3ed852e2009-09-05 21:47:34 +00002414 if (logging != MagickFalse)
2415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2416 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002417
cristy3ed852e2009-09-05 21:47:34 +00002418 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002419 {
2420 InheritException(exception,&image->exception);
2421 image->columns=0;
2422 }
glennrp0fe50b42010-11-16 03:52:51 +00002423
cristy3ed852e2009-09-05 21:47:34 +00002424 return(GetFirstImageInList(image));
2425 }
glennrp0fe50b42010-11-16 03:52:51 +00002426
cristyed552522009-10-16 14:04:35 +00002427 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002428
cristyed552522009-10-16 14:04:35 +00002429 if (quantum_info == (QuantumInfo *) NULL)
2430 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002431
glennrpc8cbc5d2011-01-01 00:12:34 +00002432 {
2433
2434 MagickBooleanType
2435 found_transparent_pixel;
2436
2437 found_transparent_pixel=MagickFalse;
2438
cristy3ed852e2009-09-05 21:47:34 +00002439 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002440 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002441 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002442 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002443 /*
2444 Convert image to DirectClass pixel packets.
2445 */
glennrp67b9c1a2011-04-22 18:47:36 +00002446#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2447 int
2448 depth;
2449
2450 depth=(ssize_t) ping_bit_depth;
2451#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002452 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2453 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2454 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2455 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002456
glennrpc8cbc5d2011-01-01 00:12:34 +00002457 for (y=0; y < (ssize_t) image->rows; y++)
2458 {
2459 if (num_passes > 1)
2460 row_offset=ping_rowbytes*y;
2461
2462 else
2463 row_offset=0;
2464
glennrpcf002022011-01-30 02:38:15 +00002465 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002466 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2467
2468 if (q == (PixelPacket *) NULL)
2469 break;
2470
glennrpc8cbc5d2011-01-01 00:12:34 +00002471 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2472 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002473 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002474
2475 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2476 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002477 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002478
2479 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2480 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002481 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002482
2483 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2484 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002485 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002486
2487 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2488 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002489 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002490
glennrpc8cbc5d2011-01-01 00:12:34 +00002491 if (found_transparent_pixel == MagickFalse)
2492 {
2493 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002494 if (y== 0 && logging != MagickFalse)
2495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2496 " Looking for cheap transparent pixel");
2497
glennrpc8cbc5d2011-01-01 00:12:34 +00002498 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2499 {
glennrp5aa37f62011-01-02 03:07:57 +00002500 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2501 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
glennrp8b698592011-04-26 03:38:21 +00002502 (GetOpacityPixelComponent(q) != OpaqueOpacity))
glennrpc8cbc5d2011-01-01 00:12:34 +00002503 {
glennrpa6a06632011-01-19 15:15:34 +00002504 if (logging != MagickFalse)
2505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2506 " ...got one.");
2507
glennrpc8cbc5d2011-01-01 00:12:34 +00002508 found_transparent_pixel = MagickTrue;
2509 break;
2510 }
glennrp4f25bd02011-01-01 18:51:28 +00002511 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2512 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp8b698592011-04-26 03:38:21 +00002513 (ScaleQuantumToShort(GetRedPixelComponent(q))
2514 == transparent_color.red &&
2515 ScaleQuantumToShort(GetGreenPixelComponent(q))
2516 == transparent_color.green &&
2517 ScaleQuantumToShort(GetBluePixelComponent(q))
2518 == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002519 {
glennrpa6a06632011-01-19 15:15:34 +00002520 if (logging != MagickFalse)
2521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2522 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002523 found_transparent_pixel = MagickTrue;
2524 break;
2525 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002526 q++;
2527 }
2528 }
2529
2530 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2531 {
2532 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2533 image->rows);
2534
2535 if (status == MagickFalse)
2536 break;
2537 }
2538 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2539 break;
2540 }
2541
2542 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2543 {
2544 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002545 if (status == MagickFalse)
2546 break;
2547 }
cristy3ed852e2009-09-05 21:47:34 +00002548 }
cristy3ed852e2009-09-05 21:47:34 +00002549 }
glennrp0fe50b42010-11-16 03:52:51 +00002550
cristy3ed852e2009-09-05 21:47:34 +00002551 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002552
cristy3ed852e2009-09-05 21:47:34 +00002553 for (pass=0; pass < num_passes; pass++)
2554 {
2555 Quantum
2556 *quantum_scanline;
2557
2558 register Quantum
2559 *r;
2560
2561 /*
2562 Convert grayscale image to PseudoClass pixel packets.
2563 */
glennrpfaa852b2010-03-30 12:17:00 +00002564 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002565 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002566
cristy3ed852e2009-09-05 21:47:34 +00002567 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2568 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002569
cristy3ed852e2009-09-05 21:47:34 +00002570 if (quantum_scanline == (Quantum *) NULL)
2571 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002572
cristybb503372010-05-27 20:51:26 +00002573 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002574 {
2575 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002576 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002577
cristy3ed852e2009-09-05 21:47:34 +00002578 else
2579 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00002580
glennrpcf002022011-01-30 02:38:15 +00002581 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002582 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00002583
cristy3ed852e2009-09-05 21:47:34 +00002584 if (q == (PixelPacket *) NULL)
2585 break;
glennrp0fe50b42010-11-16 03:52:51 +00002586
cristy5c6f7892010-05-05 22:53:29 +00002587 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00002588 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00002589 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00002590
glennrpfaa852b2010-03-30 12:17:00 +00002591 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00002592 {
2593 case 1:
2594 {
cristybb503372010-05-27 20:51:26 +00002595 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002596 bit;
2597
cristybb503372010-05-27 20:51:26 +00002598 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00002599 {
2600 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002601 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002602 p++;
2603 }
glennrp0fe50b42010-11-16 03:52:51 +00002604
cristy3ed852e2009-09-05 21:47:34 +00002605 if ((image->columns % 8) != 0)
2606 {
cristybb503372010-05-27 20:51:26 +00002607 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002608 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002609 }
glennrp0fe50b42010-11-16 03:52:51 +00002610
cristy3ed852e2009-09-05 21:47:34 +00002611 break;
2612 }
glennrp47b9dd52010-11-24 18:12:06 +00002613
cristy3ed852e2009-09-05 21:47:34 +00002614 case 2:
2615 {
cristybb503372010-05-27 20:51:26 +00002616 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002617 {
glennrpa18d5bc2011-04-23 14:51:34 +00002618 *r++=(*p >> 6) & 0x03;
2619 *r++=(*p >> 4) & 0x03;
2620 *r++=(*p >> 2) & 0x03;
2621 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00002622 }
glennrp0fe50b42010-11-16 03:52:51 +00002623
cristy3ed852e2009-09-05 21:47:34 +00002624 if ((image->columns % 4) != 0)
2625 {
cristybb503372010-05-27 20:51:26 +00002626 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00002627 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00002628 }
glennrp0fe50b42010-11-16 03:52:51 +00002629
cristy3ed852e2009-09-05 21:47:34 +00002630 break;
2631 }
glennrp47b9dd52010-11-24 18:12:06 +00002632
cristy3ed852e2009-09-05 21:47:34 +00002633 case 4:
2634 {
cristybb503372010-05-27 20:51:26 +00002635 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00002636 {
glennrpa18d5bc2011-04-23 14:51:34 +00002637 *r++=(*p >> 4) & 0x0f;
2638 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00002639 }
glennrp0fe50b42010-11-16 03:52:51 +00002640
cristy3ed852e2009-09-05 21:47:34 +00002641 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00002642 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00002643
cristy3ed852e2009-09-05 21:47:34 +00002644 break;
2645 }
glennrp47b9dd52010-11-24 18:12:06 +00002646
cristy3ed852e2009-09-05 21:47:34 +00002647 case 8:
2648 {
glennrpfaa852b2010-03-30 12:17:00 +00002649 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00002650 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002651 {
glennrpa18d5bc2011-04-23 14:51:34 +00002652 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00002653 /* In image.h, OpaqueOpacity is 0
2654 * TransparentOpacity is QuantumRange
2655 * In a PNG datastream, Opaque is QuantumRange
2656 * and Transparent is 0.
2657 */
glennrp8b698592011-04-26 03:38:21 +00002658 SetOpacityPixelComponent(q,
2659 ScaleCharToQuantum((unsigned char) (255-(*p++))));
2660 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00002661 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002662 q++;
2663 }
glennrp0fe50b42010-11-16 03:52:51 +00002664
cristy3ed852e2009-09-05 21:47:34 +00002665 else
cristybb503372010-05-27 20:51:26 +00002666 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002667 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00002668
cristy3ed852e2009-09-05 21:47:34 +00002669 break;
2670 }
glennrp47b9dd52010-11-24 18:12:06 +00002671
cristy3ed852e2009-09-05 21:47:34 +00002672 case 16:
2673 {
cristybb503372010-05-27 20:51:26 +00002674 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002675 {
glennrp58f77c72011-04-23 14:09:09 +00002676#if (MAGICKCORE_QUANTUM_DEPTH == 16)
2677 size_t
2678 quantum;
2679
2680 if (image->colors > 256)
2681 *r=((*p++) << 8);
2682
2683 else
2684 *r=0;
2685
2686 quantum=(*r);
2687 quantum|=(*p++);
2688 *r=(Quantum) quantum;
2689 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00002690
2691 if (ping_color_type == 4)
2692 {
glennrp58f77c72011-04-23 14:09:09 +00002693 quantum=((*p++) << 8);
2694 quantum|=(*p++);
glennrp8b698592011-04-26 03:38:21 +00002695 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-quantum));
2696 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002697 found_transparent_pixel = MagickTrue;
2698 q++;
2699 }
2700#else
2701#if (MAGICKCORE_QUANTUM_DEPTH == 32)
2702 size_t
2703 quantum;
2704
2705 if (image->colors > 256)
2706 *r=((*p++) << 8);
2707
2708 else
2709 *r=0;
2710
2711 quantum=(*r);
2712 quantum|=(*p++);
2713 *r=quantum;
2714 r++;
2715
2716 if (ping_color_type == 4)
2717 {
glennrp8b698592011-04-26 03:38:21 +00002718 quantum=(*p << 8) | *(p+1);
2719 quantum*=65537L;
2720 SetOpacityPixelComponent(q,
2721 (Quantum) GetAlphaPixelComponent(q));
2722 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002723 found_transparent_pixel = MagickTrue;
2724 p+=2;
2725 q++;
2726 }
2727
2728#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2729 *r++=(*p++);
2730 p++; /* strip low byte */
2731
2732 if (ping_color_type == 4)
2733 {
glennrp8b698592011-04-26 03:38:21 +00002734 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-(*p++)));
2735 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp9d0ea4d2011-04-22 18:35:57 +00002736 found_transparent_pixel = MagickTrue;
2737 p++;
2738 q++;
2739 }
cristy3ed852e2009-09-05 21:47:34 +00002740#endif
glennrp58f77c72011-04-23 14:09:09 +00002741#endif
glennrpa18d5bc2011-04-23 14:51:34 +00002742 }
glennrp47b9dd52010-11-24 18:12:06 +00002743
cristy3ed852e2009-09-05 21:47:34 +00002744 break;
2745 }
glennrp47b9dd52010-11-24 18:12:06 +00002746
cristy3ed852e2009-09-05 21:47:34 +00002747 default:
2748 break;
2749 }
glennrp3faa9a32011-04-23 14:00:25 +00002750
cristy3ed852e2009-09-05 21:47:34 +00002751 /*
2752 Transfer image scanline.
2753 */
2754 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00002755
cristybb503372010-05-27 20:51:26 +00002756 for (x=0; x < (ssize_t) image->columns; x++)
cristy9fff7b42011-04-29 01:09:31 +00002757 SetIndexPixelComponent(indexes+x,*r++);
glennrp0fe50b42010-11-16 03:52:51 +00002758
cristy3ed852e2009-09-05 21:47:34 +00002759 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2760 break;
glennrp0fe50b42010-11-16 03:52:51 +00002761
cristy7a287bf2010-02-14 02:18:09 +00002762 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2763 {
cristycee97112010-05-28 00:44:52 +00002764 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00002765 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00002766
cristy7a287bf2010-02-14 02:18:09 +00002767 if (status == MagickFalse)
2768 break;
2769 }
cristy3ed852e2009-09-05 21:47:34 +00002770 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002771
cristy7a287bf2010-02-14 02:18:09 +00002772 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002773 {
2774 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00002775
cristy3ed852e2009-09-05 21:47:34 +00002776 if (status == MagickFalse)
2777 break;
2778 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002779
cristy3ed852e2009-09-05 21:47:34 +00002780 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2781 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002782
2783 image->matte=found_transparent_pixel;
2784
2785 if (logging != MagickFalse)
2786 {
2787 if (found_transparent_pixel != MagickFalse)
2788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2789 " Found transparent pixel");
2790 else
glennrp5aa37f62011-01-02 03:07:57 +00002791 {
2792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2793 " No transparent pixel was found");
glennrpa6a06632011-01-19 15:15:34 +00002794
glennrp5aa37f62011-01-02 03:07:57 +00002795 ping_color_type&=0x03;
2796 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002797 }
2798 }
2799
cristyb32b90a2009-09-07 21:45:48 +00002800 if (quantum_info != (QuantumInfo *) NULL)
2801 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002802
cristy5c6f7892010-05-05 22:53:29 +00002803 if (image->storage_class == PseudoClass)
2804 {
cristyaeb2cbc2010-05-07 13:28:58 +00002805 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002806 matte;
2807
2808 matte=image->matte;
2809 image->matte=MagickFalse;
2810 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002811 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002812 }
glennrp47b9dd52010-11-24 18:12:06 +00002813
glennrp4eb39312011-03-30 21:34:55 +00002814 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00002815
2816 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002817 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002818 {
2819 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00002820 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002821 image->colors=2;
2822 (void) SetImageBackgroundColor(image);
2823#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002824 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002825#endif
2826 if (logging != MagickFalse)
2827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2828 " exit ReadOnePNGImage() early.");
2829 return(image);
2830 }
glennrp47b9dd52010-11-24 18:12:06 +00002831
glennrpfaa852b2010-03-30 12:17:00 +00002832 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002833 {
2834 ClassType
2835 storage_class;
2836
2837 /*
2838 Image has a transparent background.
2839 */
2840 storage_class=image->storage_class;
2841 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002842
glennrp3c218112010-11-27 15:31:26 +00002843/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00002844
glennrp0fe50b42010-11-16 03:52:51 +00002845 if (storage_class == PseudoClass)
2846 {
2847 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002848 {
glennrp0fe50b42010-11-16 03:52:51 +00002849 for (x=0; x < ping_num_trans; x++)
2850 {
2851 image->colormap[x].opacity =
2852 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2853 }
glennrpc11cf6a2010-03-20 16:46:19 +00002854 }
glennrp47b9dd52010-11-24 18:12:06 +00002855
glennrp0fe50b42010-11-16 03:52:51 +00002856 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2857 {
2858 for (x=0; x < (int) image->colors; x++)
2859 {
2860 if (ScaleQuantumToShort(image->colormap[x].red) ==
2861 transparent_color.opacity)
2862 {
2863 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2864 }
2865 }
2866 }
2867 (void) SyncImage(image);
2868 }
glennrp47b9dd52010-11-24 18:12:06 +00002869
glennrpa6a06632011-01-19 15:15:34 +00002870#if 1 /* Should have already been done above, but glennrp problem P10
2871 * needs this.
2872 */
glennrp0fe50b42010-11-16 03:52:51 +00002873 else
2874 {
2875 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002876 {
glennrp0fe50b42010-11-16 03:52:51 +00002877 image->storage_class=storage_class;
2878 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2879
2880 if (q == (PixelPacket *) NULL)
2881 break;
2882
2883 indexes=GetAuthenticIndexQueue(image);
2884
glennrpa6a06632011-01-19 15:15:34 +00002885 /* Caution: on a Q8 build, this does not distinguish between
2886 * 16-bit colors that differ only in the low byte
2887 */
glennrp0fe50b42010-11-16 03:52:51 +00002888 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2889 {
glennrp8b698592011-04-26 03:38:21 +00002890 if (ScaleQuantumToShort(GetRedPixelComponent(q))
2891 == transparent_color.red &&
2892 ScaleQuantumToShort(GetGreenPixelComponent(q))
2893 == transparent_color.green &&
2894 ScaleQuantumToShort(GetBluePixelComponent(q))
2895 == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00002896 {
glennrp8b698592011-04-26 03:38:21 +00002897 SetOpacityPixelComponent(q,TransparentOpacity);
glennrp4f25bd02011-01-01 18:51:28 +00002898 }
glennrp0fe50b42010-11-16 03:52:51 +00002899
glennrp67b9c1a2011-04-22 18:47:36 +00002900#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00002901 else
glennrp4f25bd02011-01-01 18:51:28 +00002902 {
glennrp4737d522011-04-29 03:33:42 +00002903 SetOpacityPixelComponent(q)=(Quantum) OpaqueOpacity;
glennrp4f25bd02011-01-01 18:51:28 +00002904 }
glennrpa6a06632011-01-19 15:15:34 +00002905#endif
glennrp0fe50b42010-11-16 03:52:51 +00002906
2907 q++;
2908 }
2909
2910 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2911 break;
glennrpc11cf6a2010-03-20 16:46:19 +00002912 }
glennrp0fe50b42010-11-16 03:52:51 +00002913 }
glennrpa6a06632011-01-19 15:15:34 +00002914#endif
glennrpc11cf6a2010-03-20 16:46:19 +00002915
cristy3ed852e2009-09-05 21:47:34 +00002916 image->storage_class=DirectClass;
2917 }
glennrp3c218112010-11-27 15:31:26 +00002918
cristyb40fc462010-08-08 00:49:49 +00002919 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2920 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2921 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00002922
cristyeb3b22a2011-03-31 20:16:11 +00002923 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00002924 {
2925 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00002926 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2927 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00002928 else
glennrpa0ed0092011-04-18 16:36:29 +00002929 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2930 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002931
glennrp4eb39312011-03-30 21:34:55 +00002932 if (status != MagickFalse)
2933 for (i=0; i < (ssize_t) num_text; i++)
2934 {
2935 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00002936
glennrp4eb39312011-03-30 21:34:55 +00002937 if (logging != MagickFalse)
2938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2939 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00002940
glennrp4eb39312011-03-30 21:34:55 +00002941 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00002942 {
glennrp4eb39312011-03-30 21:34:55 +00002943 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2944 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00002945 }
glennrp0fe50b42010-11-16 03:52:51 +00002946
glennrp4eb39312011-03-30 21:34:55 +00002947 else
2948 {
2949 char
2950 *value;
2951
2952 length=text[i].text_length;
2953 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2954 sizeof(*value));
2955 if (value == (char *) NULL)
2956 {
2957 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2958 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2959 image->filename);
2960 break;
2961 }
2962 *value='\0';
2963 (void) ConcatenateMagickString(value,text[i].text,length+2);
2964
2965 /* Don't save "density" or "units" property if we have a pHYs
2966 * chunk
2967 */
2968 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2969 (LocaleCompare(text[i].key,"density") != 0 &&
2970 LocaleCompare(text[i].key,"units") != 0))
2971 (void) SetImageProperty(image,text[i].key,value);
2972
2973 if (logging != MagickFalse)
2974 {
2975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2976 " length: %lu",(unsigned long) length);
2977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2978 " Keyword: %s",text[i].key);
2979 }
2980
2981 value=DestroyString(value);
2982 }
2983 }
2984 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00002985 }
glennrp3c218112010-11-27 15:31:26 +00002986
cristy3ed852e2009-09-05 21:47:34 +00002987#ifdef MNG_OBJECT_BUFFERS
2988 /*
2989 Store the object if necessary.
2990 */
2991 if (object_id && !mng_info->frozen[object_id])
2992 {
2993 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2994 {
2995 /*
2996 create a new object buffer.
2997 */
2998 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00002999 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003000
cristy3ed852e2009-09-05 21:47:34 +00003001 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3002 {
3003 mng_info->ob[object_id]->image=(Image *) NULL;
3004 mng_info->ob[object_id]->reference_count=1;
3005 }
3006 }
glennrp47b9dd52010-11-24 18:12:06 +00003007
cristy3ed852e2009-09-05 21:47:34 +00003008 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3009 mng_info->ob[object_id]->frozen)
3010 {
3011 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3012 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3013 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3014 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003015
cristy3ed852e2009-09-05 21:47:34 +00003016 if (mng_info->ob[object_id]->frozen)
3017 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3018 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3019 "`%s'",image->filename);
3020 }
glennrp0fe50b42010-11-16 03:52:51 +00003021
cristy3ed852e2009-09-05 21:47:34 +00003022 else
3023 {
cristy3ed852e2009-09-05 21:47:34 +00003024
3025 if (mng_info->ob[object_id]->image != (Image *) NULL)
3026 mng_info->ob[object_id]->image=DestroyImage
3027 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003028
cristy3ed852e2009-09-05 21:47:34 +00003029 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3030 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003031
cristy3ed852e2009-09-05 21:47:34 +00003032 if (mng_info->ob[object_id]->image != (Image *) NULL)
3033 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003034
cristy3ed852e2009-09-05 21:47:34 +00003035 else
3036 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3037 ResourceLimitError,"Cloning image for object buffer failed",
3038 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003039
glennrpfaa852b2010-03-30 12:17:00 +00003040 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003041 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003042
glennrpfaa852b2010-03-30 12:17:00 +00003043 mng_info->ob[object_id]->width=ping_width;
3044 mng_info->ob[object_id]->height=ping_height;
3045 mng_info->ob[object_id]->color_type=ping_color_type;
3046 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3047 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3048 mng_info->ob[object_id]->compression_method=
3049 ping_compression_method;
3050 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003051
glennrpfaa852b2010-03-30 12:17:00 +00003052 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003053 {
3054 int
3055 number_colors;
3056
3057 png_colorp
3058 plte;
3059
3060 /*
3061 Copy the PLTE to the object buffer.
3062 */
3063 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3064 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003065
cristy3ed852e2009-09-05 21:47:34 +00003066 for (i=0; i < number_colors; i++)
3067 {
3068 mng_info->ob[object_id]->plte[i]=plte[i];
3069 }
3070 }
glennrp47b9dd52010-11-24 18:12:06 +00003071
cristy3ed852e2009-09-05 21:47:34 +00003072 else
3073 mng_info->ob[object_id]->plte_length=0;
3074 }
3075 }
3076#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003077
3078 /* Set image->matte to MagickTrue if the input colortype supports
3079 * alpha or if a valid tRNS chunk is present, no matter whether there
3080 * is actual transparency present.
3081 */
3082 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3083 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3084 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3085 MagickTrue : MagickFalse;
3086
glennrpcb395ac2011-03-30 19:50:23 +00003087 /* Set more properties for identify to retrieve */
3088 {
3089 char
3090 msg[MaxTextExtent];
3091
glennrp4eb39312011-03-30 21:34:55 +00003092 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003093 {
3094 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3095 (void) FormatMagickString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003096 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003097 (void) SetImageProperty(image,"PNG:text ",msg);
3098 }
3099
3100 if (num_raw_profiles != 0)
3101 {
3102 (void) FormatMagickString(msg,MaxTextExtent,
3103 "%d were found", num_raw_profiles);
3104 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3105 }
3106
glennrpcb395ac2011-03-30 19:50:23 +00003107 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003108 {
3109 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3110 "chunk was found (see Chromaticity, above)");
3111 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3112 }
glennrpcb395ac2011-03-30 19:50:23 +00003113
3114 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003115 {
3116 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3117 "chunk was found (see Background color, above)");
3118 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3119 }
3120
3121 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3122 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003123
3124 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3125 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3126
glennrpcb395ac2011-03-30 19:50:23 +00003127 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3128 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003129
3130#if defined(PNG_sRGB_SUPPORTED)
3131 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3132 {
glennrp07523c72011-03-31 18:12:10 +00003133 (void) FormatMagickString(msg,MaxTextExtent,
3134 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003135 (int) intent);
3136 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3137 }
3138#endif
3139
3140 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3141 {
glennrp07523c72011-03-31 18:12:10 +00003142 (void) FormatMagickString(msg,MaxTextExtent,
3143 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003144 file_gamma);
3145 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3146 }
3147
3148#if defined(PNG_pHYs_SUPPORTED)
3149 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3150 {
glennrp07523c72011-03-31 18:12:10 +00003151 (void) FormatMagickString(msg,MaxTextExtent,
3152 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003153 (double) x_resolution,(double) y_resolution, unit_type);
3154 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3155 }
3156#endif
3157
3158#if defined(PNG_oFFs_SUPPORTED)
3159 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3160 {
3161 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3162 (double) image->page.x,(double) image->page.y);
3163 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3164 }
3165#endif
3166
glennrp07523c72011-03-31 18:12:10 +00003167 if ((image->page.width != 0 && image->page.width != image->columns) ||
3168 (image->page.height != 0 && image->page.height != image->rows))
3169 {
3170 (void) FormatMagickString(msg,MaxTextExtent,
3171 "width=%.20g, height=%.20g",
3172 (double) image->page.width,(double) image->page.height);
3173 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3174 }
glennrpcb395ac2011-03-30 19:50:23 +00003175 }
3176
cristy3ed852e2009-09-05 21:47:34 +00003177 /*
3178 Relinquish resources.
3179 */
3180 png_destroy_read_struct(&ping,&ping_info,&end_info);
3181
glennrpcf002022011-01-30 02:38:15 +00003182 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003183#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003184 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003185#endif
3186
3187 if (logging != MagickFalse)
3188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3189 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003190
cristy3ed852e2009-09-05 21:47:34 +00003191 return(image);
3192
3193/* end of reading one PNG image */
3194}
3195
3196static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3197{
3198 Image
3199 *image,
3200 *previous;
3201
3202 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003203 have_mng_structure,
3204 logging,
cristy3ed852e2009-09-05 21:47:34 +00003205 status;
3206
3207 MngInfo
3208 *mng_info;
3209
3210 char
3211 magic_number[MaxTextExtent];
3212
cristy3ed852e2009-09-05 21:47:34 +00003213 ssize_t
3214 count;
3215
3216 /*
3217 Open image file.
3218 */
3219 assert(image_info != (const ImageInfo *) NULL);
3220 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003221
cristy3ed852e2009-09-05 21:47:34 +00003222 if (image_info->debug != MagickFalse)
3223 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3224 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003225
cristy3ed852e2009-09-05 21:47:34 +00003226 assert(exception != (ExceptionInfo *) NULL);
3227 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003228 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003229 image=AcquireImage(image_info);
3230 mng_info=(MngInfo *) NULL;
3231 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003232
cristy3ed852e2009-09-05 21:47:34 +00003233 if (status == MagickFalse)
3234 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003235
cristy3ed852e2009-09-05 21:47:34 +00003236 /*
3237 Verify PNG signature.
3238 */
3239 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003240
glennrpdde35db2011-02-21 12:06:32 +00003241 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003242 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003243
cristy3ed852e2009-09-05 21:47:34 +00003244 /*
3245 Allocate a MngInfo structure.
3246 */
3247 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003248 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003249
cristy3ed852e2009-09-05 21:47:34 +00003250 if (mng_info == (MngInfo *) NULL)
3251 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003252
cristy3ed852e2009-09-05 21:47:34 +00003253 /*
3254 Initialize members of the MngInfo structure.
3255 */
3256 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3257 mng_info->image=image;
3258 have_mng_structure=MagickTrue;
3259
3260 previous=image;
3261 image=ReadOnePNGImage(mng_info,image_info,exception);
3262 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003263
cristy3ed852e2009-09-05 21:47:34 +00003264 if (image == (Image *) NULL)
3265 {
3266 if (previous != (Image *) NULL)
3267 {
3268 if (previous->signature != MagickSignature)
3269 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003270
cristy3ed852e2009-09-05 21:47:34 +00003271 (void) CloseBlob(previous);
3272 (void) DestroyImageList(previous);
3273 }
glennrp0fe50b42010-11-16 03:52:51 +00003274
cristy3ed852e2009-09-05 21:47:34 +00003275 if (logging != MagickFalse)
3276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3277 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003278
cristy3ed852e2009-09-05 21:47:34 +00003279 return((Image *) NULL);
3280 }
glennrp47b9dd52010-11-24 18:12:06 +00003281
cristy3ed852e2009-09-05 21:47:34 +00003282 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003283
cristy3ed852e2009-09-05 21:47:34 +00003284 if ((image->columns == 0) || (image->rows == 0))
3285 {
3286 if (logging != MagickFalse)
3287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3288 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003289
cristy3ed852e2009-09-05 21:47:34 +00003290 ThrowReaderException(CorruptImageError,"CorruptImage");
3291 }
glennrp47b9dd52010-11-24 18:12:06 +00003292
glennrp3faa9a32011-04-23 14:00:25 +00003293#if 0 /* This is probably redundant now */
cristy3ed852e2009-09-05 21:47:34 +00003294 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3295 {
3296 (void) SetImageType(image,PaletteType);
glennrp0fe50b42010-11-16 03:52:51 +00003297
cristy3ed852e2009-09-05 21:47:34 +00003298 if (image->matte != MagickFalse)
3299 {
3300 /* To do: Reduce to binary transparency */
3301 }
3302 }
glennrp3faa9a32011-04-23 14:00:25 +00003303#endif
glennrp47b9dd52010-11-24 18:12:06 +00003304
cristy3ed852e2009-09-05 21:47:34 +00003305 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3306 {
3307 (void) SetImageType(image,TrueColorType);
3308 image->matte=MagickFalse;
3309 }
glennrp0fe50b42010-11-16 03:52:51 +00003310
cristy3ed852e2009-09-05 21:47:34 +00003311 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3312 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003313
cristy3ed852e2009-09-05 21:47:34 +00003314 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3316 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3317 (double) image->page.width,(double) image->page.height,
3318 (double) image->page.x,(double) image->page.y);
3319
3320 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003322
cristy3ed852e2009-09-05 21:47:34 +00003323 return(image);
3324}
3325
3326
3327
3328#if defined(JNG_SUPPORTED)
3329/*
3330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3331% %
3332% %
3333% %
3334% R e a d O n e J N G I m a g e %
3335% %
3336% %
3337% %
3338%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3339%
3340% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3341% (minus the 8-byte signature) and returns it. It allocates the memory
3342% necessary for the new Image structure and returns a pointer to the new
3343% image.
3344%
3345% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3346%
3347% The format of the ReadOneJNGImage method is:
3348%
3349% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3350% ExceptionInfo *exception)
3351%
3352% A description of each parameter follows:
3353%
3354% o mng_info: Specifies a pointer to a MngInfo structure.
3355%
3356% o image_info: the image info.
3357%
3358% o exception: return any errors or warnings in this structure.
3359%
3360*/
3361static Image *ReadOneJNGImage(MngInfo *mng_info,
3362 const ImageInfo *image_info, ExceptionInfo *exception)
3363{
3364 Image
3365 *alpha_image,
3366 *color_image,
3367 *image,
3368 *jng_image;
3369
3370 ImageInfo
3371 *alpha_image_info,
3372 *color_image_info;
3373
cristy4383ec82011-01-05 15:42:32 +00003374 MagickBooleanType
3375 logging;
3376
cristybb503372010-05-27 20:51:26 +00003377 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003378 y;
3379
3380 MagickBooleanType
3381 status;
3382
3383 png_uint_32
3384 jng_height,
3385 jng_width;
3386
3387 png_byte
3388 jng_color_type,
3389 jng_image_sample_depth,
3390 jng_image_compression_method,
3391 jng_image_interlace_method,
3392 jng_alpha_sample_depth,
3393 jng_alpha_compression_method,
3394 jng_alpha_filter_method,
3395 jng_alpha_interlace_method;
3396
3397 register const PixelPacket
3398 *s;
3399
cristybb503372010-05-27 20:51:26 +00003400 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003401 i,
3402 x;
3403
3404 register PixelPacket
3405 *q;
3406
3407 register unsigned char
3408 *p;
3409
3410 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003411 read_JSEP,
3412 reading_idat,
3413 skip_to_iend;
3414
cristybb503372010-05-27 20:51:26 +00003415 size_t
cristy3ed852e2009-09-05 21:47:34 +00003416 length;
3417
3418 jng_alpha_compression_method=0;
3419 jng_alpha_sample_depth=8;
3420 jng_color_type=0;
3421 jng_height=0;
3422 jng_width=0;
3423 alpha_image=(Image *) NULL;
3424 color_image=(Image *) NULL;
3425 alpha_image_info=(ImageInfo *) NULL;
3426 color_image_info=(ImageInfo *) NULL;
3427
3428 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003429 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003430
3431 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003432
cristy3ed852e2009-09-05 21:47:34 +00003433 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3434 {
3435 /*
3436 Allocate next image structure.
3437 */
3438 if (logging != MagickFalse)
3439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3440 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003441
cristy3ed852e2009-09-05 21:47:34 +00003442 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003443
cristy3ed852e2009-09-05 21:47:34 +00003444 if (GetNextImageInList(image) == (Image *) NULL)
3445 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003446
cristy3ed852e2009-09-05 21:47:34 +00003447 image=SyncNextImageInList(image);
3448 }
3449 mng_info->image=image;
3450
3451 /*
3452 Signature bytes have already been read.
3453 */
3454
3455 read_JSEP=MagickFalse;
3456 reading_idat=MagickFalse;
3457 skip_to_iend=MagickFalse;
3458 for (;;)
3459 {
3460 char
3461 type[MaxTextExtent];
3462
3463 unsigned char
3464 *chunk;
3465
3466 unsigned int
3467 count;
3468
3469 /*
3470 Read a new JNG chunk.
3471 */
3472 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3473 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003474
cristy3ed852e2009-09-05 21:47:34 +00003475 if (status == MagickFalse)
3476 break;
glennrp0fe50b42010-11-16 03:52:51 +00003477
cristy3ed852e2009-09-05 21:47:34 +00003478 type[0]='\0';
3479 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3480 length=ReadBlobMSBLong(image);
3481 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3482
3483 if (logging != MagickFalse)
3484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003485 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3486 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003487
3488 if (length > PNG_UINT_31_MAX || count == 0)
3489 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003490
cristy3ed852e2009-09-05 21:47:34 +00003491 p=NULL;
3492 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003493
cristy3ed852e2009-09-05 21:47:34 +00003494 if (length)
3495 {
3496 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003497
cristy3ed852e2009-09-05 21:47:34 +00003498 if (chunk == (unsigned char *) NULL)
3499 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003500
cristybb503372010-05-27 20:51:26 +00003501 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003502 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003503
cristy3ed852e2009-09-05 21:47:34 +00003504 p=chunk;
3505 }
glennrp47b9dd52010-11-24 18:12:06 +00003506
cristy3ed852e2009-09-05 21:47:34 +00003507 (void) ReadBlobMSBLong(image); /* read crc word */
3508
3509 if (skip_to_iend)
3510 {
3511 if (length)
3512 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003513
cristy3ed852e2009-09-05 21:47:34 +00003514 continue;
3515 }
3516
3517 if (memcmp(type,mng_JHDR,4) == 0)
3518 {
3519 if (length == 16)
3520 {
cristybb503372010-05-27 20:51:26 +00003521 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003522 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003523 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003524 (p[6] << 8) | p[7]);
3525 jng_color_type=p[8];
3526 jng_image_sample_depth=p[9];
3527 jng_image_compression_method=p[10];
3528 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003529
cristy3ed852e2009-09-05 21:47:34 +00003530 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3531 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003532
cristy3ed852e2009-09-05 21:47:34 +00003533 jng_alpha_sample_depth=p[12];
3534 jng_alpha_compression_method=p[13];
3535 jng_alpha_filter_method=p[14];
3536 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003537
cristy3ed852e2009-09-05 21:47:34 +00003538 if (logging != MagickFalse)
3539 {
3540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003541 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003542
cristy3ed852e2009-09-05 21:47:34 +00003543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003544 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003545
cristy3ed852e2009-09-05 21:47:34 +00003546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3547 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003548
cristy3ed852e2009-09-05 21:47:34 +00003549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3550 " jng_image_sample_depth: %3d",
3551 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003552
cristy3ed852e2009-09-05 21:47:34 +00003553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3554 " jng_image_compression_method:%3d",
3555 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003556
cristy3ed852e2009-09-05 21:47:34 +00003557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3558 " jng_image_interlace_method: %3d",
3559 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003560
cristy3ed852e2009-09-05 21:47:34 +00003561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3562 " jng_alpha_sample_depth: %3d",
3563 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003564
cristy3ed852e2009-09-05 21:47:34 +00003565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3566 " jng_alpha_compression_method:%3d",
3567 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003568
cristy3ed852e2009-09-05 21:47:34 +00003569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3570 " jng_alpha_filter_method: %3d",
3571 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003572
cristy3ed852e2009-09-05 21:47:34 +00003573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3574 " jng_alpha_interlace_method: %3d",
3575 jng_alpha_interlace_method);
3576 }
3577 }
glennrp47b9dd52010-11-24 18:12:06 +00003578
cristy3ed852e2009-09-05 21:47:34 +00003579 if (length)
3580 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003581
cristy3ed852e2009-09-05 21:47:34 +00003582 continue;
3583 }
3584
3585
3586 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3587 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3588 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3589 {
3590 /*
3591 o create color_image
3592 o open color_blob, attached to color_image
3593 o if (color type has alpha)
3594 open alpha_blob, attached to alpha_image
3595 */
3596
cristy73bd4a52010-10-05 11:24:23 +00003597 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003598
cristy3ed852e2009-09-05 21:47:34 +00003599 if (color_image_info == (ImageInfo *) NULL)
3600 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003601
cristy3ed852e2009-09-05 21:47:34 +00003602 GetImageInfo(color_image_info);
3603 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003604
cristy3ed852e2009-09-05 21:47:34 +00003605 if (color_image == (Image *) NULL)
3606 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3607
3608 if (logging != MagickFalse)
3609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3610 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003611
cristy3ed852e2009-09-05 21:47:34 +00003612 (void) AcquireUniqueFilename(color_image->filename);
3613 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3614 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003615
cristy3ed852e2009-09-05 21:47:34 +00003616 if (status == MagickFalse)
3617 return((Image *) NULL);
3618
3619 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3620 {
3621 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00003622 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00003623
cristy3ed852e2009-09-05 21:47:34 +00003624 if (alpha_image_info == (ImageInfo *) NULL)
3625 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003626
cristy3ed852e2009-09-05 21:47:34 +00003627 GetImageInfo(alpha_image_info);
3628 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003629
cristy3ed852e2009-09-05 21:47:34 +00003630 if (alpha_image == (Image *) NULL)
3631 {
3632 alpha_image=DestroyImage(alpha_image);
3633 ThrowReaderException(ResourceLimitError,
3634 "MemoryAllocationFailed");
3635 }
glennrp0fe50b42010-11-16 03:52:51 +00003636
cristy3ed852e2009-09-05 21:47:34 +00003637 if (logging != MagickFalse)
3638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3639 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003640
cristy3ed852e2009-09-05 21:47:34 +00003641 (void) AcquireUniqueFilename(alpha_image->filename);
3642 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3643 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003644
cristy3ed852e2009-09-05 21:47:34 +00003645 if (status == MagickFalse)
3646 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003647
cristy3ed852e2009-09-05 21:47:34 +00003648 if (jng_alpha_compression_method == 0)
3649 {
3650 unsigned char
3651 data[18];
3652
3653 if (logging != MagickFalse)
3654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3655 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003656
cristy3ed852e2009-09-05 21:47:34 +00003657 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3658 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00003659
cristy3ed852e2009-09-05 21:47:34 +00003660 (void) WriteBlobMSBULong(alpha_image,13L);
3661 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00003662 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00003663 PNGLong(data+4,jng_width);
3664 PNGLong(data+8,jng_height);
3665 data[12]=jng_alpha_sample_depth;
3666 data[13]=0; /* color_type gray */
3667 data[14]=0; /* compression method 0 */
3668 data[15]=0; /* filter_method 0 */
3669 data[16]=0; /* interlace_method 0 */
3670 (void) WriteBlob(alpha_image,17,data);
3671 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3672 }
3673 }
3674 reading_idat=MagickTrue;
3675 }
3676
3677 if (memcmp(type,mng_JDAT,4) == 0)
3678 {
glennrp47b9dd52010-11-24 18:12:06 +00003679 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003680
3681 if (logging != MagickFalse)
3682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3683 " Copying JDAT chunk data to color_blob.");
3684
3685 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003686
cristy3ed852e2009-09-05 21:47:34 +00003687 if (length)
3688 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003689
cristy3ed852e2009-09-05 21:47:34 +00003690 continue;
3691 }
3692
3693 if (memcmp(type,mng_IDAT,4) == 0)
3694 {
3695 png_byte
3696 data[5];
3697
glennrp47b9dd52010-11-24 18:12:06 +00003698 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003699
3700 if (image_info->ping == MagickFalse)
3701 {
3702 if (logging != MagickFalse)
3703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3704 " Copying IDAT chunk data to alpha_blob.");
3705
cristybb503372010-05-27 20:51:26 +00003706 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003707 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00003708 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00003709 (void) WriteBlob(alpha_image,4,data);
3710 (void) WriteBlob(alpha_image,length,chunk);
3711 (void) WriteBlobMSBULong(alpha_image,
3712 crc32(crc32(0,data,4),chunk,(uInt) length));
3713 }
glennrp0fe50b42010-11-16 03:52:51 +00003714
cristy3ed852e2009-09-05 21:47:34 +00003715 if (length)
3716 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003717
cristy3ed852e2009-09-05 21:47:34 +00003718 continue;
3719 }
3720
3721 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3722 {
glennrp47b9dd52010-11-24 18:12:06 +00003723 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003724
3725 if (image_info->ping == MagickFalse)
3726 {
3727 if (logging != MagickFalse)
3728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3729 " Copying JDAA chunk data to alpha_blob.");
3730
3731 (void) WriteBlob(alpha_image,length,chunk);
3732 }
glennrp0fe50b42010-11-16 03:52:51 +00003733
cristy3ed852e2009-09-05 21:47:34 +00003734 if (length)
3735 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003736
cristy3ed852e2009-09-05 21:47:34 +00003737 continue;
3738 }
3739
3740 if (memcmp(type,mng_JSEP,4) == 0)
3741 {
3742 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00003743
cristy3ed852e2009-09-05 21:47:34 +00003744 if (length)
3745 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003746
cristy3ed852e2009-09-05 21:47:34 +00003747 continue;
3748 }
3749
3750 if (memcmp(type,mng_bKGD,4) == 0)
3751 {
3752 if (length == 2)
3753 {
3754 image->background_color.red=ScaleCharToQuantum(p[1]);
3755 image->background_color.green=image->background_color.red;
3756 image->background_color.blue=image->background_color.red;
3757 }
glennrp0fe50b42010-11-16 03:52:51 +00003758
cristy3ed852e2009-09-05 21:47:34 +00003759 if (length == 6)
3760 {
3761 image->background_color.red=ScaleCharToQuantum(p[1]);
3762 image->background_color.green=ScaleCharToQuantum(p[3]);
3763 image->background_color.blue=ScaleCharToQuantum(p[5]);
3764 }
glennrp0fe50b42010-11-16 03:52:51 +00003765
cristy3ed852e2009-09-05 21:47:34 +00003766 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3767 continue;
3768 }
3769
3770 if (memcmp(type,mng_gAMA,4) == 0)
3771 {
3772 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00003773 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00003774
cristy3ed852e2009-09-05 21:47:34 +00003775 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3776 continue;
3777 }
3778
3779 if (memcmp(type,mng_cHRM,4) == 0)
3780 {
3781 if (length == 32)
3782 {
cristy8182b072010-05-30 20:10:53 +00003783 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3784 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3785 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3786 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3787 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3788 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3789 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3790 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003791 }
glennrp47b9dd52010-11-24 18:12:06 +00003792
cristy3ed852e2009-09-05 21:47:34 +00003793 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3794 continue;
3795 }
3796
3797 if (memcmp(type,mng_sRGB,4) == 0)
3798 {
3799 if (length == 1)
3800 {
glennrpe610a072010-08-05 17:08:46 +00003801 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00003802 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00003803 image->gamma=0.45455f;
3804 image->chromaticity.red_primary.x=0.6400f;
3805 image->chromaticity.red_primary.y=0.3300f;
3806 image->chromaticity.green_primary.x=0.3000f;
3807 image->chromaticity.green_primary.y=0.6000f;
3808 image->chromaticity.blue_primary.x=0.1500f;
3809 image->chromaticity.blue_primary.y=0.0600f;
3810 image->chromaticity.white_point.x=0.3127f;
3811 image->chromaticity.white_point.y=0.3290f;
3812 }
glennrp47b9dd52010-11-24 18:12:06 +00003813
cristy3ed852e2009-09-05 21:47:34 +00003814 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3815 continue;
3816 }
3817
3818 if (memcmp(type,mng_oFFs,4) == 0)
3819 {
3820 if (length > 8)
3821 {
glennrp5eae7602011-02-22 15:21:32 +00003822 image->page.x=(ssize_t) mng_get_long(p);
3823 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00003824
cristy3ed852e2009-09-05 21:47:34 +00003825 if ((int) p[8] != 0)
3826 {
3827 image->page.x/=10000;
3828 image->page.y/=10000;
3829 }
3830 }
glennrp47b9dd52010-11-24 18:12:06 +00003831
cristy3ed852e2009-09-05 21:47:34 +00003832 if (length)
3833 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003834
cristy3ed852e2009-09-05 21:47:34 +00003835 continue;
3836 }
3837
3838 if (memcmp(type,mng_pHYs,4) == 0)
3839 {
3840 if (length > 8)
3841 {
cristy8182b072010-05-30 20:10:53 +00003842 image->x_resolution=(double) mng_get_long(p);
3843 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003844 if ((int) p[8] == PNG_RESOLUTION_METER)
3845 {
3846 image->units=PixelsPerCentimeterResolution;
3847 image->x_resolution=image->x_resolution/100.0f;
3848 image->y_resolution=image->y_resolution/100.0f;
3849 }
3850 }
glennrp0fe50b42010-11-16 03:52:51 +00003851
cristy3ed852e2009-09-05 21:47:34 +00003852 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3853 continue;
3854 }
3855
3856#if 0
3857 if (memcmp(type,mng_iCCP,4) == 0)
3858 {
glennrpfd05d622011-02-25 04:10:33 +00003859 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00003860 if (length)
3861 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003862
cristy3ed852e2009-09-05 21:47:34 +00003863 continue;
3864 }
3865#endif
3866
3867 if (length)
3868 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3869
3870 if (memcmp(type,mng_IEND,4))
3871 continue;
glennrp0fe50b42010-11-16 03:52:51 +00003872
cristy3ed852e2009-09-05 21:47:34 +00003873 break;
3874 }
3875
3876
3877 /* IEND found */
3878
3879 /*
3880 Finish up reading image data:
3881
3882 o read main image from color_blob.
3883
3884 o close color_blob.
3885
3886 o if (color_type has alpha)
3887 if alpha_encoding is PNG
3888 read secondary image from alpha_blob via ReadPNG
3889 if alpha_encoding is JPEG
3890 read secondary image from alpha_blob via ReadJPEG
3891
3892 o close alpha_blob.
3893
3894 o copy intensity of secondary image into
3895 opacity samples of main image.
3896
3897 o destroy the secondary image.
3898 */
3899
3900 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00003901
cristy3ed852e2009-09-05 21:47:34 +00003902 if (logging != MagickFalse)
3903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3904 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003905
cristy3ed852e2009-09-05 21:47:34 +00003906 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3907 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003908
cristy3ed852e2009-09-05 21:47:34 +00003909 color_image_info->ping=MagickFalse; /* To do: avoid this */
3910 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003911
cristy3ed852e2009-09-05 21:47:34 +00003912 if (jng_image == (Image *) NULL)
3913 return((Image *) NULL);
3914
3915 (void) RelinquishUniqueFileResource(color_image->filename);
3916 color_image=DestroyImage(color_image);
3917 color_image_info=DestroyImageInfo(color_image_info);
3918
3919 if (jng_image == (Image *) NULL)
3920 return((Image *) NULL);
3921
3922 if (logging != MagickFalse)
3923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3924 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00003925
cristy3ed852e2009-09-05 21:47:34 +00003926 image->rows=jng_height;
3927 image->columns=jng_width;
3928 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00003929
cristybb503372010-05-27 20:51:26 +00003930 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003931 {
3932 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3933 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3934 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00003935
cristy3ed852e2009-09-05 21:47:34 +00003936 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3937 break;
3938 }
glennrp0fe50b42010-11-16 03:52:51 +00003939
cristy3ed852e2009-09-05 21:47:34 +00003940 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00003941
cristy3ed852e2009-09-05 21:47:34 +00003942 if (image_info->ping == MagickFalse)
3943 {
3944 if (jng_color_type >= 12)
3945 {
3946 if (jng_alpha_compression_method == 0)
3947 {
3948 png_byte
3949 data[5];
3950 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3951 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00003952 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00003953 (void) WriteBlob(alpha_image,4,data);
3954 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3955 }
glennrp0fe50b42010-11-16 03:52:51 +00003956
cristy3ed852e2009-09-05 21:47:34 +00003957 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00003958
cristy3ed852e2009-09-05 21:47:34 +00003959 if (logging != MagickFalse)
3960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " Reading opacity from alpha_blob.");
3962
3963 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3964 "%s",alpha_image->filename);
3965
3966 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003969 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003970 {
3971 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3972 &image->exception);
3973 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003974
cristy3ed852e2009-09-05 21:47:34 +00003975 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003976 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
glennrp4737d522011-04-29 03:33:42 +00003977 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
3978 GetRedPixelComponent(s));
glennrp0fe50b42010-11-16 03:52:51 +00003979
cristy3ed852e2009-09-05 21:47:34 +00003980 else
cristybb503372010-05-27 20:51:26 +00003981 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003982 {
glennrp7c7b3152011-04-26 04:01:27 +00003983 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
3984 GetRedPixelComponent(s));
3985 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +00003986 image->matte=MagickTrue;
3987 }
glennrp0fe50b42010-11-16 03:52:51 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3990 break;
3991 }
3992 (void) RelinquishUniqueFileResource(alpha_image->filename);
3993 alpha_image=DestroyImage(alpha_image);
3994 alpha_image_info=DestroyImageInfo(alpha_image_info);
3995 if (jng_image != (Image *) NULL)
3996 jng_image=DestroyImage(jng_image);
3997 }
3998 }
3999
glennrp47b9dd52010-11-24 18:12:06 +00004000 /* Read the JNG image. */
4001
cristy3ed852e2009-09-05 21:47:34 +00004002 if (mng_info->mng_type == 0)
4003 {
4004 mng_info->mng_width=jng_width;
4005 mng_info->mng_height=jng_height;
4006 }
glennrp0fe50b42010-11-16 03:52:51 +00004007
cristy3ed852e2009-09-05 21:47:34 +00004008 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004009 {
4010 image->page.width=jng_width;
4011 image->page.height=jng_height;
4012 }
4013
cristy3ed852e2009-09-05 21:47:34 +00004014 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004015 {
4016 image->page.x=mng_info->x_off[mng_info->object_id];
4017 image->page.y=mng_info->y_off[mng_info->object_id];
4018 }
4019
cristy3ed852e2009-09-05 21:47:34 +00004020 else
glennrp0fe50b42010-11-16 03:52:51 +00004021 {
4022 image->page.y=mng_info->y_off[mng_info->object_id];
4023 }
4024
cristy3ed852e2009-09-05 21:47:34 +00004025 mng_info->image_found++;
4026 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4027 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004028
cristy3ed852e2009-09-05 21:47:34 +00004029 if (logging != MagickFalse)
4030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4031 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 return(image);
4034}
4035
4036/*
4037%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4038% %
4039% %
4040% %
4041% R e a d J N G I m a g e %
4042% %
4043% %
4044% %
4045%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4046%
4047% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4048% (including the 8-byte signature) and returns it. It allocates the memory
4049% necessary for the new Image structure and returns a pointer to the new
4050% image.
4051%
4052% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4053%
4054% The format of the ReadJNGImage method is:
4055%
4056% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4057% *exception)
4058%
4059% A description of each parameter follows:
4060%
4061% o image_info: the image info.
4062%
4063% o exception: return any errors or warnings in this structure.
4064%
4065*/
4066
4067static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4068{
4069 Image
4070 *image,
4071 *previous;
4072
4073 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004074 have_mng_structure,
4075 logging,
cristy3ed852e2009-09-05 21:47:34 +00004076 status;
4077
4078 MngInfo
4079 *mng_info;
4080
4081 char
4082 magic_number[MaxTextExtent];
4083
cristy3ed852e2009-09-05 21:47:34 +00004084 size_t
4085 count;
4086
4087 /*
4088 Open image file.
4089 */
4090 assert(image_info != (const ImageInfo *) NULL);
4091 assert(image_info->signature == MagickSignature);
4092 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4093 assert(exception != (ExceptionInfo *) NULL);
4094 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004095 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004096 image=AcquireImage(image_info);
4097 mng_info=(MngInfo *) NULL;
4098 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004099
cristy3ed852e2009-09-05 21:47:34 +00004100 if (status == MagickFalse)
4101 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004102
cristy3ed852e2009-09-05 21:47:34 +00004103 if (LocaleCompare(image_info->magick,"JNG") != 0)
4104 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004105
glennrp47b9dd52010-11-24 18:12:06 +00004106 /* Verify JNG signature. */
4107
cristy3ed852e2009-09-05 21:47:34 +00004108 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004109
glennrp3b8763e2011-02-21 12:08:18 +00004110 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004111 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004112
glennrp47b9dd52010-11-24 18:12:06 +00004113 /* Allocate a MngInfo structure. */
4114
cristy3ed852e2009-09-05 21:47:34 +00004115 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004116 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004117
cristy3ed852e2009-09-05 21:47:34 +00004118 if (mng_info == (MngInfo *) NULL)
4119 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004120
glennrp47b9dd52010-11-24 18:12:06 +00004121 /* Initialize members of the MngInfo structure. */
4122
cristy3ed852e2009-09-05 21:47:34 +00004123 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4124 have_mng_structure=MagickTrue;
4125
4126 mng_info->image=image;
4127 previous=image;
4128 image=ReadOneJNGImage(mng_info,image_info,exception);
4129 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004130
cristy3ed852e2009-09-05 21:47:34 +00004131 if (image == (Image *) NULL)
4132 {
4133 if (IsImageObject(previous) != MagickFalse)
4134 {
4135 (void) CloseBlob(previous);
4136 (void) DestroyImageList(previous);
4137 }
glennrp0fe50b42010-11-16 03:52:51 +00004138
cristy3ed852e2009-09-05 21:47:34 +00004139 if (logging != MagickFalse)
4140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4141 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004142
cristy3ed852e2009-09-05 21:47:34 +00004143 return((Image *) NULL);
4144 }
4145 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004146
cristy3ed852e2009-09-05 21:47:34 +00004147 if (image->columns == 0 || image->rows == 0)
4148 {
4149 if (logging != MagickFalse)
4150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4151 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004152
cristy3ed852e2009-09-05 21:47:34 +00004153 ThrowReaderException(CorruptImageError,"CorruptImage");
4154 }
glennrp0fe50b42010-11-16 03:52:51 +00004155
cristy3ed852e2009-09-05 21:47:34 +00004156 if (logging != MagickFalse)
4157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004158
cristy3ed852e2009-09-05 21:47:34 +00004159 return(image);
4160}
4161#endif
4162
4163static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4164{
4165 char
4166 page_geometry[MaxTextExtent];
4167
4168 Image
4169 *image,
4170 *previous;
4171
cristy4383ec82011-01-05 15:42:32 +00004172 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004173 logging,
4174 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004175
cristy3ed852e2009-09-05 21:47:34 +00004176 volatile int
4177 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004178 object_id,
4179 term_chunk_found,
4180 skip_to_iend;
4181
cristybb503372010-05-27 20:51:26 +00004182 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004183 image_count=0;
4184
4185 MagickBooleanType
4186 status;
4187
4188 MagickOffsetType
4189 offset;
4190
4191 MngInfo
4192 *mng_info;
4193
4194 MngBox
4195 default_fb,
4196 fb,
4197 previous_fb;
4198
4199#if defined(MNG_INSERT_LAYERS)
4200 PixelPacket
4201 mng_background_color;
4202#endif
4203
4204 register unsigned char
4205 *p;
4206
cristybb503372010-05-27 20:51:26 +00004207 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004208 i;
4209
4210 size_t
4211 count;
4212
cristybb503372010-05-27 20:51:26 +00004213 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004214 loop_level;
4215
4216 volatile short
4217 skipping_loop;
4218
4219#if defined(MNG_INSERT_LAYERS)
4220 unsigned int
4221 mandatory_back=0;
4222#endif
4223
4224 volatile unsigned int
4225#ifdef MNG_OBJECT_BUFFERS
4226 mng_background_object=0,
4227#endif
4228 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4229
cristybb503372010-05-27 20:51:26 +00004230 size_t
cristy3ed852e2009-09-05 21:47:34 +00004231 default_frame_timeout,
4232 frame_timeout,
4233#if defined(MNG_INSERT_LAYERS)
4234 image_height,
4235 image_width,
4236#endif
4237 length;
4238
glennrp38ea0832010-06-02 18:50:28 +00004239 /* These delays are all measured in image ticks_per_second,
4240 * not in MNG ticks_per_second
4241 */
cristybb503372010-05-27 20:51:26 +00004242 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004243 default_frame_delay,
4244 final_delay,
4245 final_image_delay,
4246 frame_delay,
4247#if defined(MNG_INSERT_LAYERS)
4248 insert_layers,
4249#endif
4250 mng_iterations=1,
4251 simplicity=0,
4252 subframe_height=0,
4253 subframe_width=0;
4254
4255 previous_fb.top=0;
4256 previous_fb.bottom=0;
4257 previous_fb.left=0;
4258 previous_fb.right=0;
4259 default_fb.top=0;
4260 default_fb.bottom=0;
4261 default_fb.left=0;
4262 default_fb.right=0;
4263
glennrp47b9dd52010-11-24 18:12:06 +00004264 /* Open image file. */
4265
cristy3ed852e2009-09-05 21:47:34 +00004266 assert(image_info != (const ImageInfo *) NULL);
4267 assert(image_info->signature == MagickSignature);
4268 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4269 assert(exception != (ExceptionInfo *) NULL);
4270 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004271 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004272 image=AcquireImage(image_info);
4273 mng_info=(MngInfo *) NULL;
4274 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004275
cristy3ed852e2009-09-05 21:47:34 +00004276 if (status == MagickFalse)
4277 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004278
cristy3ed852e2009-09-05 21:47:34 +00004279 first_mng_object=MagickFalse;
4280 skipping_loop=(-1);
4281 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004282
4283 /* Allocate a MngInfo structure. */
4284
cristy73bd4a52010-10-05 11:24:23 +00004285 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004286
cristy3ed852e2009-09-05 21:47:34 +00004287 if (mng_info == (MngInfo *) NULL)
4288 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004289
glennrp47b9dd52010-11-24 18:12:06 +00004290 /* Initialize members of the MngInfo structure. */
4291
cristy3ed852e2009-09-05 21:47:34 +00004292 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4293 mng_info->image=image;
4294 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004295
4296 if (LocaleCompare(image_info->magick,"MNG") == 0)
4297 {
4298 char
4299 magic_number[MaxTextExtent];
4300
glennrp47b9dd52010-11-24 18:12:06 +00004301 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004302 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4303 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4304 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004305
4306 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004307 for (i=0; i < MNG_MAX_OBJECTS; i++)
4308 {
cristybb503372010-05-27 20:51:26 +00004309 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4310 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004311 }
4312 mng_info->exists[0]=MagickTrue;
4313 }
glennrp47b9dd52010-11-24 18:12:06 +00004314
cristy3ed852e2009-09-05 21:47:34 +00004315 first_mng_object=MagickTrue;
4316 mng_type=0;
4317#if defined(MNG_INSERT_LAYERS)
4318 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4319#endif
4320 default_frame_delay=0;
4321 default_frame_timeout=0;
4322 frame_delay=0;
4323 final_delay=1;
4324 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4325 object_id=0;
4326 skip_to_iend=MagickFalse;
4327 term_chunk_found=MagickFalse;
4328 mng_info->framing_mode=1;
4329#if defined(MNG_INSERT_LAYERS)
4330 mandatory_back=MagickFalse;
4331#endif
4332#if defined(MNG_INSERT_LAYERS)
4333 mng_background_color=image->background_color;
4334#endif
4335 default_fb=mng_info->frame;
4336 previous_fb=mng_info->frame;
4337 do
4338 {
4339 char
4340 type[MaxTextExtent];
4341
4342 if (LocaleCompare(image_info->magick,"MNG") == 0)
4343 {
4344 unsigned char
4345 *chunk;
4346
4347 /*
4348 Read a new chunk.
4349 */
4350 type[0]='\0';
4351 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4352 length=ReadBlobMSBLong(image);
4353 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4354
4355 if (logging != MagickFalse)
4356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004357 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4358 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004359
4360 if (length > PNG_UINT_31_MAX)
4361 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004362
cristy3ed852e2009-09-05 21:47:34 +00004363 if (count == 0)
4364 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004365
cristy3ed852e2009-09-05 21:47:34 +00004366 p=NULL;
4367 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004368
cristy3ed852e2009-09-05 21:47:34 +00004369 if (length)
4370 {
4371 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004372
cristy3ed852e2009-09-05 21:47:34 +00004373 if (chunk == (unsigned char *) NULL)
4374 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004375
cristybb503372010-05-27 20:51:26 +00004376 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004377 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004378
cristy3ed852e2009-09-05 21:47:34 +00004379 p=chunk;
4380 }
glennrp0fe50b42010-11-16 03:52:51 +00004381
cristy3ed852e2009-09-05 21:47:34 +00004382 (void) ReadBlobMSBLong(image); /* read crc word */
4383
4384#if !defined(JNG_SUPPORTED)
4385 if (memcmp(type,mng_JHDR,4) == 0)
4386 {
4387 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004388
cristy3ed852e2009-09-05 21:47:34 +00004389 if (mng_info->jhdr_warning == 0)
4390 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4391 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004392
cristy3ed852e2009-09-05 21:47:34 +00004393 mng_info->jhdr_warning++;
4394 }
4395#endif
4396 if (memcmp(type,mng_DHDR,4) == 0)
4397 {
4398 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004399
cristy3ed852e2009-09-05 21:47:34 +00004400 if (mng_info->dhdr_warning == 0)
4401 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4402 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004403
cristy3ed852e2009-09-05 21:47:34 +00004404 mng_info->dhdr_warning++;
4405 }
4406 if (memcmp(type,mng_MEND,4) == 0)
4407 break;
glennrp47b9dd52010-11-24 18:12:06 +00004408
cristy3ed852e2009-09-05 21:47:34 +00004409 if (skip_to_iend)
4410 {
4411 if (memcmp(type,mng_IEND,4) == 0)
4412 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004413
cristy3ed852e2009-09-05 21:47:34 +00004414 if (length)
4415 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004416
cristy3ed852e2009-09-05 21:47:34 +00004417 if (logging != MagickFalse)
4418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4419 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004420
cristy3ed852e2009-09-05 21:47:34 +00004421 continue;
4422 }
glennrp0fe50b42010-11-16 03:52:51 +00004423
cristy3ed852e2009-09-05 21:47:34 +00004424 if (memcmp(type,mng_MHDR,4) == 0)
4425 {
cristybb503372010-05-27 20:51:26 +00004426 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004427 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004428
cristybb503372010-05-27 20:51:26 +00004429 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004430 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004431
cristy3ed852e2009-09-05 21:47:34 +00004432 if (logging != MagickFalse)
4433 {
4434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004435 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004437 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004438 }
glennrp0fe50b42010-11-16 03:52:51 +00004439
cristy3ed852e2009-09-05 21:47:34 +00004440 p+=8;
cristy8182b072010-05-30 20:10:53 +00004441 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004442
cristy3ed852e2009-09-05 21:47:34 +00004443 if (mng_info->ticks_per_second == 0)
4444 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004445
cristy3ed852e2009-09-05 21:47:34 +00004446 else
4447 default_frame_delay=1UL*image->ticks_per_second/
4448 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004449
cristy3ed852e2009-09-05 21:47:34 +00004450 frame_delay=default_frame_delay;
4451 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004452
cristy3ed852e2009-09-05 21:47:34 +00004453 if (length > 16)
4454 {
4455 p+=16;
cristy8182b072010-05-30 20:10:53 +00004456 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004457 }
glennrp0fe50b42010-11-16 03:52:51 +00004458
cristy3ed852e2009-09-05 21:47:34 +00004459 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004460
cristy3ed852e2009-09-05 21:47:34 +00004461 if ((simplicity != 0) && ((simplicity | 11) == 11))
4462 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004463
cristy3ed852e2009-09-05 21:47:34 +00004464 if ((simplicity != 0) && ((simplicity | 9) == 9))
4465 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004466
cristy3ed852e2009-09-05 21:47:34 +00004467#if defined(MNG_INSERT_LAYERS)
4468 if (mng_type != 3)
4469 insert_layers=MagickTrue;
4470#endif
4471 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4472 {
glennrp47b9dd52010-11-24 18:12:06 +00004473 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004474 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004475
cristy3ed852e2009-09-05 21:47:34 +00004476 if (GetNextImageInList(image) == (Image *) NULL)
4477 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004478
cristy3ed852e2009-09-05 21:47:34 +00004479 image=SyncNextImageInList(image);
4480 mng_info->image=image;
4481 }
4482
4483 if ((mng_info->mng_width > 65535L) ||
4484 (mng_info->mng_height > 65535L))
4485 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004486
cristye8c25f92010-06-03 00:53:06 +00004487 (void) FormatMagickString(page_geometry,MaxTextExtent,
4488 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004489 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004490
cristy3ed852e2009-09-05 21:47:34 +00004491 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004492 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004493 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004494 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004495 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004496
cristy3ed852e2009-09-05 21:47:34 +00004497 for (i=0; i < MNG_MAX_OBJECTS; i++)
4498 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004499
cristy3ed852e2009-09-05 21:47:34 +00004500 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4501 continue;
4502 }
4503
4504 if (memcmp(type,mng_TERM,4) == 0)
4505 {
4506 int
4507 repeat=0;
4508
4509
4510 if (length)
4511 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004512
cristy3ed852e2009-09-05 21:47:34 +00004513 if (repeat == 3)
4514 {
cristy8182b072010-05-30 20:10:53 +00004515 final_delay=(png_uint_32) mng_get_long(&p[2]);
4516 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004517
cristy3ed852e2009-09-05 21:47:34 +00004518 if (mng_iterations == PNG_UINT_31_MAX)
4519 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004520
cristy3ed852e2009-09-05 21:47:34 +00004521 image->iterations=mng_iterations;
4522 term_chunk_found=MagickTrue;
4523 }
glennrp0fe50b42010-11-16 03:52:51 +00004524
cristy3ed852e2009-09-05 21:47:34 +00004525 if (logging != MagickFalse)
4526 {
4527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4528 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004529
cristy3ed852e2009-09-05 21:47:34 +00004530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004531 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004532
cristy3ed852e2009-09-05 21:47:34 +00004533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004534 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004535 }
glennrp0fe50b42010-11-16 03:52:51 +00004536
cristy3ed852e2009-09-05 21:47:34 +00004537 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4538 continue;
4539 }
4540 if (memcmp(type,mng_DEFI,4) == 0)
4541 {
4542 if (mng_type == 3)
4543 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4544 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4545 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004546
cristy3ed852e2009-09-05 21:47:34 +00004547 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004548
cristy3ed852e2009-09-05 21:47:34 +00004549 if (mng_type == 2 && object_id != 0)
4550 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4551 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4552 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004553
cristy3ed852e2009-09-05 21:47:34 +00004554 if (object_id > MNG_MAX_OBJECTS)
4555 {
4556 /*
4557 Instead ofsuing a warning we should allocate a larger
4558 MngInfo structure and continue.
4559 */
4560 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4561 CoderError,"object id too large","`%s'",image->filename);
4562 object_id=MNG_MAX_OBJECTS;
4563 }
glennrp0fe50b42010-11-16 03:52:51 +00004564
cristy3ed852e2009-09-05 21:47:34 +00004565 if (mng_info->exists[object_id])
4566 if (mng_info->frozen[object_id])
4567 {
4568 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4569 (void) ThrowMagickException(&image->exception,
4570 GetMagickModule(),CoderError,
4571 "DEFI cannot redefine a frozen MNG object","`%s'",
4572 image->filename);
4573 continue;
4574 }
glennrp0fe50b42010-11-16 03:52:51 +00004575
cristy3ed852e2009-09-05 21:47:34 +00004576 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004577
cristy3ed852e2009-09-05 21:47:34 +00004578 if (length > 2)
4579 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 /*
4582 Extract object offset info.
4583 */
4584 if (length > 11)
4585 {
glennrp0fe50b42010-11-16 03:52:51 +00004586 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4587 (p[5] << 16) | (p[6] << 8) | p[7]);
4588
4589 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4590 (p[9] << 16) | (p[10] << 8) | p[11]);
4591
cristy3ed852e2009-09-05 21:47:34 +00004592 if (logging != MagickFalse)
4593 {
4594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004595 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004596 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004597
cristy3ed852e2009-09-05 21:47:34 +00004598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004599 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004600 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004601 }
4602 }
glennrp0fe50b42010-11-16 03:52:51 +00004603
cristy3ed852e2009-09-05 21:47:34 +00004604 /*
4605 Extract object clipping info.
4606 */
4607 if (length > 27)
4608 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4609 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004610
cristy3ed852e2009-09-05 21:47:34 +00004611 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4612 continue;
4613 }
4614 if (memcmp(type,mng_bKGD,4) == 0)
4615 {
4616 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004617
cristy3ed852e2009-09-05 21:47:34 +00004618 if (length > 5)
4619 {
4620 mng_info->mng_global_bkgd.red=
4621 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004622
cristy3ed852e2009-09-05 21:47:34 +00004623 mng_info->mng_global_bkgd.green=
4624 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004625
cristy3ed852e2009-09-05 21:47:34 +00004626 mng_info->mng_global_bkgd.blue=
4627 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004628
cristy3ed852e2009-09-05 21:47:34 +00004629 mng_info->have_global_bkgd=MagickTrue;
4630 }
glennrp0fe50b42010-11-16 03:52:51 +00004631
cristy3ed852e2009-09-05 21:47:34 +00004632 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4633 continue;
4634 }
4635 if (memcmp(type,mng_BACK,4) == 0)
4636 {
4637#if defined(MNG_INSERT_LAYERS)
4638 if (length > 6)
4639 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00004640
cristy3ed852e2009-09-05 21:47:34 +00004641 else
4642 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00004643
cristy3ed852e2009-09-05 21:47:34 +00004644 if (mandatory_back && length > 5)
4645 {
4646 mng_background_color.red=
4647 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004648
cristy3ed852e2009-09-05 21:47:34 +00004649 mng_background_color.green=
4650 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004651
cristy3ed852e2009-09-05 21:47:34 +00004652 mng_background_color.blue=
4653 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004654
cristy3ed852e2009-09-05 21:47:34 +00004655 mng_background_color.opacity=OpaqueOpacity;
4656 }
glennrp0fe50b42010-11-16 03:52:51 +00004657
cristy3ed852e2009-09-05 21:47:34 +00004658#ifdef MNG_OBJECT_BUFFERS
4659 if (length > 8)
4660 mng_background_object=(p[7] << 8) | p[8];
4661#endif
4662#endif
4663 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4664 continue;
4665 }
glennrp47b9dd52010-11-24 18:12:06 +00004666
cristy3ed852e2009-09-05 21:47:34 +00004667 if (memcmp(type,mng_PLTE,4) == 0)
4668 {
glennrp47b9dd52010-11-24 18:12:06 +00004669 /* Read global PLTE. */
4670
cristy3ed852e2009-09-05 21:47:34 +00004671 if (length && (length < 769))
4672 {
4673 if (mng_info->global_plte == (png_colorp) NULL)
4674 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4675 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00004676
cristybb503372010-05-27 20:51:26 +00004677 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004678 {
4679 mng_info->global_plte[i].red=p[3*i];
4680 mng_info->global_plte[i].green=p[3*i+1];
4681 mng_info->global_plte[i].blue=p[3*i+2];
4682 }
glennrp0fe50b42010-11-16 03:52:51 +00004683
cristy35ef8242010-06-03 16:24:13 +00004684 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00004685 }
4686#ifdef MNG_LOOSE
4687 for ( ; i < 256; i++)
4688 {
4689 mng_info->global_plte[i].red=i;
4690 mng_info->global_plte[i].green=i;
4691 mng_info->global_plte[i].blue=i;
4692 }
glennrp0fe50b42010-11-16 03:52:51 +00004693
cristy3ed852e2009-09-05 21:47:34 +00004694 if (length)
4695 mng_info->global_plte_length=256;
4696#endif
4697 else
4698 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00004699
cristy3ed852e2009-09-05 21:47:34 +00004700 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4701 continue;
4702 }
glennrp47b9dd52010-11-24 18:12:06 +00004703
cristy3ed852e2009-09-05 21:47:34 +00004704 if (memcmp(type,mng_tRNS,4) == 0)
4705 {
4706 /* read global tRNS */
4707
4708 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004709 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004710 mng_info->global_trns[i]=p[i];
4711
4712#ifdef MNG_LOOSE
4713 for ( ; i < 256; i++)
4714 mng_info->global_trns[i]=255;
4715#endif
cristy12560f32010-06-03 16:51:08 +00004716 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00004717 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4718 continue;
4719 }
4720 if (memcmp(type,mng_gAMA,4) == 0)
4721 {
4722 if (length == 4)
4723 {
cristybb503372010-05-27 20:51:26 +00004724 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004725 igamma;
4726
cristy8182b072010-05-30 20:10:53 +00004727 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004728 mng_info->global_gamma=((float) igamma)*0.00001;
4729 mng_info->have_global_gama=MagickTrue;
4730 }
glennrp0fe50b42010-11-16 03:52:51 +00004731
cristy3ed852e2009-09-05 21:47:34 +00004732 else
4733 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004734
cristy3ed852e2009-09-05 21:47:34 +00004735 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4736 continue;
4737 }
4738
4739 if (memcmp(type,mng_cHRM,4) == 0)
4740 {
glennrp47b9dd52010-11-24 18:12:06 +00004741 /* Read global cHRM */
4742
cristy3ed852e2009-09-05 21:47:34 +00004743 if (length == 32)
4744 {
cristy8182b072010-05-30 20:10:53 +00004745 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4746 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4747 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004748 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004749 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004750 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004751 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004752 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004753 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004754 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004755 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004756 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004757 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004758 mng_info->have_global_chrm=MagickTrue;
4759 }
4760 else
4761 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004762
cristy3ed852e2009-09-05 21:47:34 +00004763 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4764 continue;
4765 }
glennrp47b9dd52010-11-24 18:12:06 +00004766
cristy3ed852e2009-09-05 21:47:34 +00004767 if (memcmp(type,mng_sRGB,4) == 0)
4768 {
4769 /*
4770 Read global sRGB.
4771 */
4772 if (length)
4773 {
glennrpe610a072010-08-05 17:08:46 +00004774 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00004775 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004776 mng_info->have_global_srgb=MagickTrue;
4777 }
4778 else
4779 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004780
cristy3ed852e2009-09-05 21:47:34 +00004781 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4782 continue;
4783 }
glennrp47b9dd52010-11-24 18:12:06 +00004784
cristy3ed852e2009-09-05 21:47:34 +00004785 if (memcmp(type,mng_iCCP,4) == 0)
4786 {
glennrpfd05d622011-02-25 04:10:33 +00004787 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004788
4789 /*
4790 Read global iCCP.
4791 */
4792 if (length)
4793 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004794
cristy3ed852e2009-09-05 21:47:34 +00004795 continue;
4796 }
glennrp47b9dd52010-11-24 18:12:06 +00004797
cristy3ed852e2009-09-05 21:47:34 +00004798 if (memcmp(type,mng_FRAM,4) == 0)
4799 {
4800 if (mng_type == 3)
4801 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4802 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4803 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004804
cristy3ed852e2009-09-05 21:47:34 +00004805 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4806 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004807
cristy3ed852e2009-09-05 21:47:34 +00004808 frame_delay=default_frame_delay;
4809 frame_timeout=default_frame_timeout;
4810 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00004811
cristy3ed852e2009-09-05 21:47:34 +00004812 if (length)
4813 if (p[0])
4814 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004815
cristy3ed852e2009-09-05 21:47:34 +00004816 if (logging != MagickFalse)
4817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4818 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00004819
cristy3ed852e2009-09-05 21:47:34 +00004820 if (length > 6)
4821 {
glennrp47b9dd52010-11-24 18:12:06 +00004822 /* Note the delay and frame clipping boundaries. */
4823
cristy3ed852e2009-09-05 21:47:34 +00004824 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00004825
cristybb503372010-05-27 20:51:26 +00004826 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004827 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00004830
cristybb503372010-05-27 20:51:26 +00004831 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004832 {
4833 int
4834 change_delay,
4835 change_timeout,
4836 change_clipping;
4837
4838 change_delay=(*p++);
4839 change_timeout=(*p++);
4840 change_clipping=(*p++);
4841 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00004842
cristy3ed852e2009-09-05 21:47:34 +00004843 if (change_delay)
4844 {
cristy8182b072010-05-30 20:10:53 +00004845 frame_delay=1UL*image->ticks_per_second*
4846 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy8182b072010-05-30 20:10:53 +00004848 if (mng_info->ticks_per_second != 0)
4849 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004850
glennrpbb010dd2010-06-01 13:07:15 +00004851 else
4852 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854 if (change_delay == 2)
4855 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004856
cristy3ed852e2009-09-05 21:47:34 +00004857 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if (logging != MagickFalse)
4860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004861 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004862 }
glennrp47b9dd52010-11-24 18:12:06 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 if (change_timeout)
4865 {
glennrpbb010dd2010-06-01 13:07:15 +00004866 frame_timeout=1UL*image->ticks_per_second*
4867 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004868
glennrpbb010dd2010-06-01 13:07:15 +00004869 if (mng_info->ticks_per_second != 0)
4870 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004871
glennrpbb010dd2010-06-01 13:07:15 +00004872 else
4873 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004874
cristy3ed852e2009-09-05 21:47:34 +00004875 if (change_delay == 2)
4876 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristy3ed852e2009-09-05 21:47:34 +00004878 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004879
cristy3ed852e2009-09-05 21:47:34 +00004880 if (logging != MagickFalse)
4881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004882 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004883 }
glennrp47b9dd52010-11-24 18:12:06 +00004884
cristy3ed852e2009-09-05 21:47:34 +00004885 if (change_clipping)
4886 {
4887 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4888 p+=17;
4889 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00004890
cristy3ed852e2009-09-05 21:47:34 +00004891 if (logging != MagickFalse)
4892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004893 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004894 (double) fb.left,(double) fb.right,(double) fb.top,
4895 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 if (change_clipping == 2)
4898 default_fb=fb;
4899 }
4900 }
4901 }
4902 mng_info->clip=fb;
4903 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00004904
cristybb503372010-05-27 20:51:26 +00004905 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004906 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00004907
cristybb503372010-05-27 20:51:26 +00004908 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004909 -mng_info->clip.top);
4910 /*
4911 Insert a background layer behind the frame if framing_mode is 4.
4912 */
4913#if defined(MNG_INSERT_LAYERS)
4914 if (logging != MagickFalse)
4915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004916 " subframe_width=%.20g, subframe_height=%.20g",(double)
4917 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00004918
cristy3ed852e2009-09-05 21:47:34 +00004919 if (insert_layers && (mng_info->framing_mode == 4) &&
4920 (subframe_width) && (subframe_height))
4921 {
glennrp47b9dd52010-11-24 18:12:06 +00004922 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004923 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4924 {
4925 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00004926
cristy3ed852e2009-09-05 21:47:34 +00004927 if (GetNextImageInList(image) == (Image *) NULL)
4928 {
4929 image=DestroyImageList(image);
4930 MngInfoFreeStruct(mng_info,&have_mng_structure);
4931 return((Image *) NULL);
4932 }
glennrp47b9dd52010-11-24 18:12:06 +00004933
cristy3ed852e2009-09-05 21:47:34 +00004934 image=SyncNextImageInList(image);
4935 }
glennrp0fe50b42010-11-16 03:52:51 +00004936
cristy3ed852e2009-09-05 21:47:34 +00004937 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00004938
cristy3ed852e2009-09-05 21:47:34 +00004939 if (term_chunk_found)
4940 {
4941 image->start_loop=MagickTrue;
4942 image->iterations=mng_iterations;
4943 term_chunk_found=MagickFalse;
4944 }
glennrp0fe50b42010-11-16 03:52:51 +00004945
cristy3ed852e2009-09-05 21:47:34 +00004946 else
4947 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004948
cristy3ed852e2009-09-05 21:47:34 +00004949 image->columns=subframe_width;
4950 image->rows=subframe_height;
4951 image->page.width=subframe_width;
4952 image->page.height=subframe_height;
4953 image->page.x=mng_info->clip.left;
4954 image->page.y=mng_info->clip.top;
4955 image->background_color=mng_background_color;
4956 image->matte=MagickFalse;
4957 image->delay=0;
4958 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00004959
cristy3ed852e2009-09-05 21:47:34 +00004960 if (logging != MagickFalse)
4961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004962 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004963 (double) mng_info->clip.left,(double) mng_info->clip.right,
4964 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004965 }
4966#endif
4967 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4968 continue;
4969 }
4970 if (memcmp(type,mng_CLIP,4) == 0)
4971 {
4972 unsigned int
4973 first_object,
4974 last_object;
4975
4976 /*
4977 Read CLIP.
4978 */
4979 first_object=(p[0] << 8) | p[1];
4980 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 for (i=(int) first_object; i <= (int) last_object; i++)
4983 {
4984 if (mng_info->exists[i] && !mng_info->frozen[i])
4985 {
4986 MngBox
4987 box;
4988
4989 box=mng_info->object_clip[i];
4990 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4991 }
4992 }
glennrp47b9dd52010-11-24 18:12:06 +00004993
cristy3ed852e2009-09-05 21:47:34 +00004994 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4995 continue;
4996 }
4997 if (memcmp(type,mng_SAVE,4) == 0)
4998 {
4999 for (i=1; i < MNG_MAX_OBJECTS; i++)
5000 if (mng_info->exists[i])
5001 {
5002 mng_info->frozen[i]=MagickTrue;
5003#ifdef MNG_OBJECT_BUFFERS
5004 if (mng_info->ob[i] != (MngBuffer *) NULL)
5005 mng_info->ob[i]->frozen=MagickTrue;
5006#endif
5007 }
glennrp0fe50b42010-11-16 03:52:51 +00005008
cristy3ed852e2009-09-05 21:47:34 +00005009 if (length)
5010 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005011
cristy3ed852e2009-09-05 21:47:34 +00005012 continue;
5013 }
5014
5015 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5016 {
glennrp47b9dd52010-11-24 18:12:06 +00005017 /* Read DISC or SEEK. */
5018
cristy3ed852e2009-09-05 21:47:34 +00005019 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5020 {
5021 for (i=1; i < MNG_MAX_OBJECTS; i++)
5022 MngInfoDiscardObject(mng_info,i);
5023 }
glennrp0fe50b42010-11-16 03:52:51 +00005024
cristy3ed852e2009-09-05 21:47:34 +00005025 else
5026 {
cristybb503372010-05-27 20:51:26 +00005027 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005028 j;
5029
cristybb503372010-05-27 20:51:26 +00005030 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005031 {
5032 i=p[j] << 8 | p[j+1];
5033 MngInfoDiscardObject(mng_info,i);
5034 }
5035 }
glennrp0fe50b42010-11-16 03:52:51 +00005036
cristy3ed852e2009-09-05 21:47:34 +00005037 if (length)
5038 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005039
cristy3ed852e2009-09-05 21:47:34 +00005040 continue;
5041 }
glennrp47b9dd52010-11-24 18:12:06 +00005042
cristy3ed852e2009-09-05 21:47:34 +00005043 if (memcmp(type,mng_MOVE,4) == 0)
5044 {
cristybb503372010-05-27 20:51:26 +00005045 size_t
cristy3ed852e2009-09-05 21:47:34 +00005046 first_object,
5047 last_object;
5048
glennrp47b9dd52010-11-24 18:12:06 +00005049 /* read MOVE */
5050
cristy3ed852e2009-09-05 21:47:34 +00005051 first_object=(p[0] << 8) | p[1];
5052 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005053 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005054 {
5055 if (mng_info->exists[i] && !mng_info->frozen[i])
5056 {
5057 MngPair
5058 new_pair;
5059
5060 MngPair
5061 old_pair;
5062
5063 old_pair.a=mng_info->x_off[i];
5064 old_pair.b=mng_info->y_off[i];
5065 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5066 mng_info->x_off[i]=new_pair.a;
5067 mng_info->y_off[i]=new_pair.b;
5068 }
5069 }
glennrp47b9dd52010-11-24 18:12:06 +00005070
cristy3ed852e2009-09-05 21:47:34 +00005071 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5072 continue;
5073 }
5074
5075 if (memcmp(type,mng_LOOP,4) == 0)
5076 {
cristybb503372010-05-27 20:51:26 +00005077 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005078 loop_level=chunk[0];
5079 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005080
5081 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005082 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005083
cristy3ed852e2009-09-05 21:47:34 +00005084 if (logging != MagickFalse)
5085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005086 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5087 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005088
cristy3ed852e2009-09-05 21:47:34 +00005089 if (loop_iters == 0)
5090 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005091
cristy3ed852e2009-09-05 21:47:34 +00005092 else
5093 {
5094 mng_info->loop_jump[loop_level]=TellBlob(image);
5095 mng_info->loop_count[loop_level]=loop_iters;
5096 }
glennrp0fe50b42010-11-16 03:52:51 +00005097
cristy3ed852e2009-09-05 21:47:34 +00005098 mng_info->loop_iteration[loop_level]=0;
5099 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5100 continue;
5101 }
glennrp47b9dd52010-11-24 18:12:06 +00005102
cristy3ed852e2009-09-05 21:47:34 +00005103 if (memcmp(type,mng_ENDL,4) == 0)
5104 {
5105 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005106
cristy3ed852e2009-09-05 21:47:34 +00005107 if (skipping_loop > 0)
5108 {
5109 if (skipping_loop == loop_level)
5110 {
5111 /*
5112 Found end of zero-iteration loop.
5113 */
5114 skipping_loop=(-1);
5115 mng_info->loop_active[loop_level]=0;
5116 }
5117 }
glennrp47b9dd52010-11-24 18:12:06 +00005118
cristy3ed852e2009-09-05 21:47:34 +00005119 else
5120 {
5121 if (mng_info->loop_active[loop_level] == 1)
5122 {
5123 mng_info->loop_count[loop_level]--;
5124 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005125
cristy3ed852e2009-09-05 21:47:34 +00005126 if (logging != MagickFalse)
5127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005128 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005129 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005130 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005131
cristy3ed852e2009-09-05 21:47:34 +00005132 if (mng_info->loop_count[loop_level] != 0)
5133 {
5134 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5135 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005136
cristy3ed852e2009-09-05 21:47:34 +00005137 if (offset < 0)
5138 ThrowReaderException(CorruptImageError,
5139 "ImproperImageHeader");
5140 }
glennrp47b9dd52010-11-24 18:12:06 +00005141
cristy3ed852e2009-09-05 21:47:34 +00005142 else
5143 {
5144 short
5145 last_level;
5146
5147 /*
5148 Finished loop.
5149 */
5150 mng_info->loop_active[loop_level]=0;
5151 last_level=(-1);
5152 for (i=0; i < loop_level; i++)
5153 if (mng_info->loop_active[i] == 1)
5154 last_level=(short) i;
5155 loop_level=last_level;
5156 }
5157 }
5158 }
glennrp47b9dd52010-11-24 18:12:06 +00005159
cristy3ed852e2009-09-05 21:47:34 +00005160 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5161 continue;
5162 }
glennrp47b9dd52010-11-24 18:12:06 +00005163
cristy3ed852e2009-09-05 21:47:34 +00005164 if (memcmp(type,mng_CLON,4) == 0)
5165 {
5166 if (mng_info->clon_warning == 0)
5167 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5168 CoderError,"CLON is not implemented yet","`%s'",
5169 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005170
cristy3ed852e2009-09-05 21:47:34 +00005171 mng_info->clon_warning++;
5172 }
glennrp47b9dd52010-11-24 18:12:06 +00005173
cristy3ed852e2009-09-05 21:47:34 +00005174 if (memcmp(type,mng_MAGN,4) == 0)
5175 {
5176 png_uint_16
5177 magn_first,
5178 magn_last,
5179 magn_mb,
5180 magn_ml,
5181 magn_mr,
5182 magn_mt,
5183 magn_mx,
5184 magn_my,
5185 magn_methx,
5186 magn_methy;
5187
5188 if (length > 1)
5189 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005190
cristy3ed852e2009-09-05 21:47:34 +00005191 else
5192 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005193
cristy3ed852e2009-09-05 21:47:34 +00005194 if (length > 3)
5195 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005196
cristy3ed852e2009-09-05 21:47:34 +00005197 else
5198 magn_last=magn_first;
5199#ifndef MNG_OBJECT_BUFFERS
5200 if (magn_first || magn_last)
5201 if (mng_info->magn_warning == 0)
5202 {
5203 (void) ThrowMagickException(&image->exception,
5204 GetMagickModule(),CoderError,
5205 "MAGN is not implemented yet for nonzero objects",
5206 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005207
cristy3ed852e2009-09-05 21:47:34 +00005208 mng_info->magn_warning++;
5209 }
5210#endif
5211 if (length > 4)
5212 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 else
5215 magn_methx=0;
5216
5217 if (length > 6)
5218 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005219
cristy3ed852e2009-09-05 21:47:34 +00005220 else
5221 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005222
cristy3ed852e2009-09-05 21:47:34 +00005223 if (magn_mx == 0)
5224 magn_mx=1;
5225
5226 if (length > 8)
5227 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005228
cristy3ed852e2009-09-05 21:47:34 +00005229 else
5230 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005231
cristy3ed852e2009-09-05 21:47:34 +00005232 if (magn_my == 0)
5233 magn_my=1;
5234
5235 if (length > 10)
5236 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005237
cristy3ed852e2009-09-05 21:47:34 +00005238 else
5239 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005240
cristy3ed852e2009-09-05 21:47:34 +00005241 if (magn_ml == 0)
5242 magn_ml=1;
5243
5244 if (length > 12)
5245 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005246
cristy3ed852e2009-09-05 21:47:34 +00005247 else
5248 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005249
cristy3ed852e2009-09-05 21:47:34 +00005250 if (magn_mr == 0)
5251 magn_mr=1;
5252
5253 if (length > 14)
5254 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005255
cristy3ed852e2009-09-05 21:47:34 +00005256 else
5257 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005258
cristy3ed852e2009-09-05 21:47:34 +00005259 if (magn_mt == 0)
5260 magn_mt=1;
5261
5262 if (length > 16)
5263 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005264
cristy3ed852e2009-09-05 21:47:34 +00005265 else
5266 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005267
cristy3ed852e2009-09-05 21:47:34 +00005268 if (magn_mb == 0)
5269 magn_mb=1;
5270
5271 if (length > 17)
5272 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005273
cristy3ed852e2009-09-05 21:47:34 +00005274 else
5275 magn_methy=magn_methx;
5276
glennrp47b9dd52010-11-24 18:12:06 +00005277
cristy3ed852e2009-09-05 21:47:34 +00005278 if (magn_methx > 5 || magn_methy > 5)
5279 if (mng_info->magn_warning == 0)
5280 {
5281 (void) ThrowMagickException(&image->exception,
5282 GetMagickModule(),CoderError,
5283 "Unknown MAGN method in MNG datastream","`%s'",
5284 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005285
cristy3ed852e2009-09-05 21:47:34 +00005286 mng_info->magn_warning++;
5287 }
5288#ifdef MNG_OBJECT_BUFFERS
5289 /* Magnify existing objects in the range magn_first to magn_last */
5290#endif
5291 if (magn_first == 0 || magn_last == 0)
5292 {
5293 /* Save the magnification factors for object 0 */
5294 mng_info->magn_mb=magn_mb;
5295 mng_info->magn_ml=magn_ml;
5296 mng_info->magn_mr=magn_mr;
5297 mng_info->magn_mt=magn_mt;
5298 mng_info->magn_mx=magn_mx;
5299 mng_info->magn_my=magn_my;
5300 mng_info->magn_methx=magn_methx;
5301 mng_info->magn_methy=magn_methy;
5302 }
5303 }
glennrp47b9dd52010-11-24 18:12:06 +00005304
cristy3ed852e2009-09-05 21:47:34 +00005305 if (memcmp(type,mng_PAST,4) == 0)
5306 {
5307 if (mng_info->past_warning == 0)
5308 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5309 CoderError,"PAST is not implemented yet","`%s'",
5310 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005311
cristy3ed852e2009-09-05 21:47:34 +00005312 mng_info->past_warning++;
5313 }
glennrp47b9dd52010-11-24 18:12:06 +00005314
cristy3ed852e2009-09-05 21:47:34 +00005315 if (memcmp(type,mng_SHOW,4) == 0)
5316 {
5317 if (mng_info->show_warning == 0)
5318 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5319 CoderError,"SHOW is not implemented yet","`%s'",
5320 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005321
cristy3ed852e2009-09-05 21:47:34 +00005322 mng_info->show_warning++;
5323 }
glennrp47b9dd52010-11-24 18:12:06 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 if (memcmp(type,mng_sBIT,4) == 0)
5326 {
5327 if (length < 4)
5328 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 else
5331 {
5332 mng_info->global_sbit.gray=p[0];
5333 mng_info->global_sbit.red=p[0];
5334 mng_info->global_sbit.green=p[1];
5335 mng_info->global_sbit.blue=p[2];
5336 mng_info->global_sbit.alpha=p[3];
5337 mng_info->have_global_sbit=MagickTrue;
5338 }
5339 }
5340 if (memcmp(type,mng_pHYs,4) == 0)
5341 {
5342 if (length > 8)
5343 {
5344 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005345 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005346 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005347 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005348 mng_info->global_phys_unit_type=p[8];
5349 mng_info->have_global_phys=MagickTrue;
5350 }
glennrp47b9dd52010-11-24 18:12:06 +00005351
cristy3ed852e2009-09-05 21:47:34 +00005352 else
5353 mng_info->have_global_phys=MagickFalse;
5354 }
5355 if (memcmp(type,mng_pHYg,4) == 0)
5356 {
5357 if (mng_info->phyg_warning == 0)
5358 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5359 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005360
cristy3ed852e2009-09-05 21:47:34 +00005361 mng_info->phyg_warning++;
5362 }
5363 if (memcmp(type,mng_BASI,4) == 0)
5364 {
5365 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005366
cristy3ed852e2009-09-05 21:47:34 +00005367 if (mng_info->basi_warning == 0)
5368 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5369 CoderError,"BASI is not implemented yet","`%s'",
5370 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005371
cristy3ed852e2009-09-05 21:47:34 +00005372 mng_info->basi_warning++;
5373#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005374 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005375 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005376 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005377 (p[6] << 8) | p[7]);
5378 basi_color_type=p[8];
5379 basi_compression_method=p[9];
5380 basi_filter_type=p[10];
5381 basi_interlace_method=p[11];
5382 if (length > 11)
5383 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005384
cristy3ed852e2009-09-05 21:47:34 +00005385 else
5386 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005387
cristy3ed852e2009-09-05 21:47:34 +00005388 if (length > 13)
5389 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005390
cristy3ed852e2009-09-05 21:47:34 +00005391 else
5392 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005393
cristy3ed852e2009-09-05 21:47:34 +00005394 if (length > 15)
5395 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005396
cristy3ed852e2009-09-05 21:47:34 +00005397 else
5398 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005399
cristy3ed852e2009-09-05 21:47:34 +00005400 if (length > 17)
5401 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005402
cristy3ed852e2009-09-05 21:47:34 +00005403 else
5404 {
5405 if (basi_sample_depth == 16)
5406 basi_alpha=65535L;
5407 else
5408 basi_alpha=255;
5409 }
glennrp47b9dd52010-11-24 18:12:06 +00005410
cristy3ed852e2009-09-05 21:47:34 +00005411 if (length > 19)
5412 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005413
cristy3ed852e2009-09-05 21:47:34 +00005414 else
5415 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005416
cristy3ed852e2009-09-05 21:47:34 +00005417#endif
5418 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5419 continue;
5420 }
glennrp47b9dd52010-11-24 18:12:06 +00005421
cristy3ed852e2009-09-05 21:47:34 +00005422 if (memcmp(type,mng_IHDR,4)
5423#if defined(JNG_SUPPORTED)
5424 && memcmp(type,mng_JHDR,4)
5425#endif
5426 )
5427 {
5428 /* Not an IHDR or JHDR chunk */
5429 if (length)
5430 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005431
cristy3ed852e2009-09-05 21:47:34 +00005432 continue;
5433 }
5434/* Process IHDR */
5435 if (logging != MagickFalse)
5436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5437 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005438
cristy3ed852e2009-09-05 21:47:34 +00005439 mng_info->exists[object_id]=MagickTrue;
5440 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005441
cristy3ed852e2009-09-05 21:47:34 +00005442 if (mng_info->invisible[object_id])
5443 {
5444 if (logging != MagickFalse)
5445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5446 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005447
cristy3ed852e2009-09-05 21:47:34 +00005448 skip_to_iend=MagickTrue;
5449 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5450 continue;
5451 }
5452#if defined(MNG_INSERT_LAYERS)
5453 if (length < 8)
5454 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005455
cristy8182b072010-05-30 20:10:53 +00005456 image_width=(size_t) mng_get_long(p);
5457 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005458#endif
5459 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5460
5461 /*
5462 Insert a transparent background layer behind the entire animation
5463 if it is not full screen.
5464 */
5465#if defined(MNG_INSERT_LAYERS)
5466 if (insert_layers && mng_type && first_mng_object)
5467 {
5468 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5469 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005470 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005471 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005472 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005473 {
5474 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5475 {
5476 /*
5477 Allocate next image structure.
5478 */
5479 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005480
cristy3ed852e2009-09-05 21:47:34 +00005481 if (GetNextImageInList(image) == (Image *) NULL)
5482 {
5483 image=DestroyImageList(image);
5484 MngInfoFreeStruct(mng_info,&have_mng_structure);
5485 return((Image *) NULL);
5486 }
glennrp47b9dd52010-11-24 18:12:06 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 image=SyncNextImageInList(image);
5489 }
5490 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005491
cristy3ed852e2009-09-05 21:47:34 +00005492 if (term_chunk_found)
5493 {
5494 image->start_loop=MagickTrue;
5495 image->iterations=mng_iterations;
5496 term_chunk_found=MagickFalse;
5497 }
glennrp47b9dd52010-11-24 18:12:06 +00005498
cristy3ed852e2009-09-05 21:47:34 +00005499 else
5500 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005501
5502 /* Make a background rectangle. */
5503
cristy3ed852e2009-09-05 21:47:34 +00005504 image->delay=0;
5505 image->columns=mng_info->mng_width;
5506 image->rows=mng_info->mng_height;
5507 image->page.width=mng_info->mng_width;
5508 image->page.height=mng_info->mng_height;
5509 image->page.x=0;
5510 image->page.y=0;
5511 image->background_color=mng_background_color;
5512 (void) SetImageBackgroundColor(image);
5513 if (logging != MagickFalse)
5514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005515 " Inserted transparent background layer, W=%.20g, H=%.20g",
5516 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005517 }
5518 }
5519 /*
5520 Insert a background layer behind the upcoming image if
5521 framing_mode is 3, and we haven't already inserted one.
5522 */
5523 if (insert_layers && (mng_info->framing_mode == 3) &&
5524 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5525 (simplicity & 0x08)))
5526 {
5527 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5528 {
5529 /*
5530 Allocate next image structure.
5531 */
5532 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005533
cristy3ed852e2009-09-05 21:47:34 +00005534 if (GetNextImageInList(image) == (Image *) NULL)
5535 {
5536 image=DestroyImageList(image);
5537 MngInfoFreeStruct(mng_info,&have_mng_structure);
5538 return((Image *) NULL);
5539 }
glennrp47b9dd52010-11-24 18:12:06 +00005540
cristy3ed852e2009-09-05 21:47:34 +00005541 image=SyncNextImageInList(image);
5542 }
glennrp0fe50b42010-11-16 03:52:51 +00005543
cristy3ed852e2009-09-05 21:47:34 +00005544 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005545
cristy3ed852e2009-09-05 21:47:34 +00005546 if (term_chunk_found)
5547 {
5548 image->start_loop=MagickTrue;
5549 image->iterations=mng_iterations;
5550 term_chunk_found=MagickFalse;
5551 }
glennrp0fe50b42010-11-16 03:52:51 +00005552
cristy3ed852e2009-09-05 21:47:34 +00005553 else
5554 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005555
cristy3ed852e2009-09-05 21:47:34 +00005556 image->delay=0;
5557 image->columns=subframe_width;
5558 image->rows=subframe_height;
5559 image->page.width=subframe_width;
5560 image->page.height=subframe_height;
5561 image->page.x=mng_info->clip.left;
5562 image->page.y=mng_info->clip.top;
5563 image->background_color=mng_background_color;
5564 image->matte=MagickFalse;
5565 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005566
cristy3ed852e2009-09-05 21:47:34 +00005567 if (logging != MagickFalse)
5568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005569 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005570 (double) mng_info->clip.left,(double) mng_info->clip.right,
5571 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005572 }
5573#endif /* MNG_INSERT_LAYERS */
5574 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5577 {
5578 /*
5579 Allocate next image structure.
5580 */
5581 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005582
cristy3ed852e2009-09-05 21:47:34 +00005583 if (GetNextImageInList(image) == (Image *) NULL)
5584 {
5585 image=DestroyImageList(image);
5586 MngInfoFreeStruct(mng_info,&have_mng_structure);
5587 return((Image *) NULL);
5588 }
glennrp47b9dd52010-11-24 18:12:06 +00005589
cristy3ed852e2009-09-05 21:47:34 +00005590 image=SyncNextImageInList(image);
5591 }
5592 mng_info->image=image;
5593 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5594 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005595
cristy3ed852e2009-09-05 21:47:34 +00005596 if (status == MagickFalse)
5597 break;
glennrp0fe50b42010-11-16 03:52:51 +00005598
cristy3ed852e2009-09-05 21:47:34 +00005599 if (term_chunk_found)
5600 {
5601 image->start_loop=MagickTrue;
5602 term_chunk_found=MagickFalse;
5603 }
glennrp0fe50b42010-11-16 03:52:51 +00005604
cristy3ed852e2009-09-05 21:47:34 +00005605 else
5606 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005607
cristy3ed852e2009-09-05 21:47:34 +00005608 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5609 {
5610 image->delay=frame_delay;
5611 frame_delay=default_frame_delay;
5612 }
glennrp0fe50b42010-11-16 03:52:51 +00005613
cristy3ed852e2009-09-05 21:47:34 +00005614 else
5615 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005616
cristy3ed852e2009-09-05 21:47:34 +00005617 image->page.width=mng_info->mng_width;
5618 image->page.height=mng_info->mng_height;
5619 image->page.x=mng_info->x_off[object_id];
5620 image->page.y=mng_info->y_off[object_id];
5621 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00005622
cristy3ed852e2009-09-05 21:47:34 +00005623 /*
5624 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5625 */
glennrp47b9dd52010-11-24 18:12:06 +00005626
cristy3ed852e2009-09-05 21:47:34 +00005627 if (logging != MagickFalse)
5628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5629 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5630 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005631
cristybb503372010-05-27 20:51:26 +00005632 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00005633
cristy3ed852e2009-09-05 21:47:34 +00005634 if (offset < 0)
5635 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5636 }
5637
5638 previous=image;
5639 mng_info->image=image;
5640 mng_info->mng_type=mng_type;
5641 mng_info->object_id=object_id;
5642
5643 if (memcmp(type,mng_IHDR,4) == 0)
5644 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005645
cristy3ed852e2009-09-05 21:47:34 +00005646#if defined(JNG_SUPPORTED)
5647 else
5648 image=ReadOneJNGImage(mng_info,image_info,exception);
5649#endif
5650
5651 if (image == (Image *) NULL)
5652 {
5653 if (IsImageObject(previous) != MagickFalse)
5654 {
5655 (void) DestroyImageList(previous);
5656 (void) CloseBlob(previous);
5657 }
glennrp47b9dd52010-11-24 18:12:06 +00005658
cristy3ed852e2009-09-05 21:47:34 +00005659 MngInfoFreeStruct(mng_info,&have_mng_structure);
5660 return((Image *) NULL);
5661 }
glennrp0fe50b42010-11-16 03:52:51 +00005662
cristy3ed852e2009-09-05 21:47:34 +00005663 if (image->columns == 0 || image->rows == 0)
5664 {
5665 (void) CloseBlob(image);
5666 image=DestroyImageList(image);
5667 MngInfoFreeStruct(mng_info,&have_mng_structure);
5668 return((Image *) NULL);
5669 }
glennrp0fe50b42010-11-16 03:52:51 +00005670
cristy3ed852e2009-09-05 21:47:34 +00005671 mng_info->image=image;
5672
5673 if (mng_type)
5674 {
5675 MngBox
5676 crop_box;
5677
5678 if (mng_info->magn_methx || mng_info->magn_methy)
5679 {
5680 png_uint_32
5681 magnified_height,
5682 magnified_width;
5683
5684 if (logging != MagickFalse)
5685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5686 " Processing MNG MAGN chunk");
5687
5688 if (mng_info->magn_methx == 1)
5689 {
5690 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 if (image->columns > 1)
5693 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005694
cristy3ed852e2009-09-05 21:47:34 +00005695 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005696 magnified_width += (png_uint_32)
5697 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00005698 }
glennrp47b9dd52010-11-24 18:12:06 +00005699
cristy3ed852e2009-09-05 21:47:34 +00005700 else
5701 {
cristy4e5bc842010-06-09 13:56:01 +00005702 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00005703
cristy3ed852e2009-09-05 21:47:34 +00005704 if (image->columns > 1)
5705 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00005706
cristy3ed852e2009-09-05 21:47:34 +00005707 if (image->columns > 2)
5708 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005711 magnified_width += (png_uint_32)
5712 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00005713 }
glennrp47b9dd52010-11-24 18:12:06 +00005714
cristy3ed852e2009-09-05 21:47:34 +00005715 if (mng_info->magn_methy == 1)
5716 {
5717 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005718
cristy3ed852e2009-09-05 21:47:34 +00005719 if (image->rows > 1)
5720 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005721
cristy3ed852e2009-09-05 21:47:34 +00005722 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005723 magnified_height += (png_uint_32)
5724 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00005725 }
glennrp47b9dd52010-11-24 18:12:06 +00005726
cristy3ed852e2009-09-05 21:47:34 +00005727 else
5728 {
cristy4e5bc842010-06-09 13:56:01 +00005729 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00005730
cristy3ed852e2009-09-05 21:47:34 +00005731 if (image->rows > 1)
5732 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00005733
cristy3ed852e2009-09-05 21:47:34 +00005734 if (image->rows > 2)
5735 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00005736
cristy3ed852e2009-09-05 21:47:34 +00005737 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005738 magnified_height += (png_uint_32)
5739 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00005740 }
glennrp47b9dd52010-11-24 18:12:06 +00005741
cristy3ed852e2009-09-05 21:47:34 +00005742 if (magnified_height > image->rows ||
5743 magnified_width > image->columns)
5744 {
5745 Image
5746 *large_image;
5747
5748 int
5749 yy;
5750
cristybb503372010-05-27 20:51:26 +00005751 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005752 m,
5753 y;
5754
cristybb503372010-05-27 20:51:26 +00005755 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005756 x;
5757
5758 register PixelPacket
5759 *n,
5760 *q;
5761
5762 PixelPacket
5763 *next,
5764 *prev;
5765
5766 png_uint_16
5767 magn_methx,
5768 magn_methy;
5769
glennrp47b9dd52010-11-24 18:12:06 +00005770 /* Allocate next image structure. */
5771
cristy3ed852e2009-09-05 21:47:34 +00005772 if (logging != MagickFalse)
5773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5774 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00005775
cristy3ed852e2009-09-05 21:47:34 +00005776 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005777
cristy3ed852e2009-09-05 21:47:34 +00005778 if (GetNextImageInList(image) == (Image *) NULL)
5779 {
5780 image=DestroyImageList(image);
5781 MngInfoFreeStruct(mng_info,&have_mng_structure);
5782 return((Image *) NULL);
5783 }
5784
5785 large_image=SyncNextImageInList(image);
5786
5787 large_image->columns=magnified_width;
5788 large_image->rows=magnified_height;
5789
5790 magn_methx=mng_info->magn_methx;
5791 magn_methy=mng_info->magn_methy;
5792
glennrp3faa9a32011-04-23 14:00:25 +00005793#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00005794#define QM unsigned short
5795 if (magn_methx != 1 || magn_methy != 1)
5796 {
5797 /*
5798 Scale pixels to unsigned shorts to prevent
5799 overflow of intermediate values of interpolations
5800 */
cristybb503372010-05-27 20:51:26 +00005801 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005802 {
5803 q=GetAuthenticPixels(image,0,y,image->columns,1,
5804 exception);
glennrp47b9dd52010-11-24 18:12:06 +00005805
cristybb503372010-05-27 20:51:26 +00005806 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005807 {
glennrp7c7b3152011-04-26 04:01:27 +00005808 SetRedPixelComponent(q,ScaleQuantumToShort(
5809 GetRedPixelComponent(q));
5810 SetGreenPixelComponent(q,ScaleQuantumToShort(
5811 GetGreenPixelComponent(q));
5812 SetBluePixelComponent(q,ScaleQuantumToShort(
5813 GetBluePixelComponent(q));
5814 SetOpacityPixelComponent(q,ScaleQuantumToShort(
5815 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00005816 q++;
5817 }
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5820 break;
5821 }
5822 }
5823#else
5824#define QM Quantum
5825#endif
5826
5827 if (image->matte != MagickFalse)
5828 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005829
cristy3ed852e2009-09-05 21:47:34 +00005830 else
5831 {
5832 large_image->background_color.opacity=OpaqueOpacity;
5833 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 if (magn_methx == 4)
5836 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 if (magn_methx == 5)
5839 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy3ed852e2009-09-05 21:47:34 +00005841 if (magn_methy == 4)
5842 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 if (magn_methy == 5)
5845 magn_methy=3;
5846 }
5847
5848 /* magnify the rows into the right side of the large image */
5849
5850 if (logging != MagickFalse)
5851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005852 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00005853 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005854 yy=0;
5855 length=(size_t) image->columns;
5856 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5857 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00005858
cristy3ed852e2009-09-05 21:47:34 +00005859 if ((prev == (PixelPacket *) NULL) ||
5860 (next == (PixelPacket *) NULL))
5861 {
5862 image=DestroyImageList(image);
5863 MngInfoFreeStruct(mng_info,&have_mng_structure);
5864 ThrowReaderException(ResourceLimitError,
5865 "MemoryAllocationFailed");
5866 }
glennrp47b9dd52010-11-24 18:12:06 +00005867
cristy3ed852e2009-09-05 21:47:34 +00005868 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5869 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00005870
cristybb503372010-05-27 20:51:26 +00005871 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005872 {
5873 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005874 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005875
cristybb503372010-05-27 20:51:26 +00005876 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5877 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005878
cristybb503372010-05-27 20:51:26 +00005879 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5880 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005881
cristybb503372010-05-27 20:51:26 +00005882 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005883 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005884
cristy3ed852e2009-09-05 21:47:34 +00005885 else
cristybb503372010-05-27 20:51:26 +00005886 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005887
cristy3ed852e2009-09-05 21:47:34 +00005888 n=prev;
5889 prev=next;
5890 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00005891
cristybb503372010-05-27 20:51:26 +00005892 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005893 {
5894 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5895 exception);
5896 (void) CopyMagickMemory(next,n,length);
5897 }
glennrp47b9dd52010-11-24 18:12:06 +00005898
cristy3ed852e2009-09-05 21:47:34 +00005899 for (i=0; i < m; i++, yy++)
5900 {
glennrp7c7b3152011-04-26 04:01:27 +00005901 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00005902 register PixelPacket
5903 *pixels;
5904
cristybb503372010-05-27 20:51:26 +00005905 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005906 pixels=prev;
5907 n=next;
5908 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00005909 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00005910 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00005911
cristybb503372010-05-27 20:51:26 +00005912 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005913 {
glennrpfd05d622011-02-25 04:10:33 +00005914 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005915 /*
5916 if (image->storage_class == PseudoClass)
5917 {
5918 }
5919 */
5920
5921 if (magn_methy <= 1)
5922 {
5923 *q=(*pixels); /* replicate previous */
5924 }
glennrp47b9dd52010-11-24 18:12:06 +00005925
cristy3ed852e2009-09-05 21:47:34 +00005926 else if (magn_methy == 2 || magn_methy == 4)
5927 {
5928 if (i == 0)
5929 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005930
cristy3ed852e2009-09-05 21:47:34 +00005931 else
5932 {
5933 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005934 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5935 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005936 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005937 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5938 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005939 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005940 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5941 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005942 +(*pixels).blue);
glennrp47b9dd52010-11-24 18:12:06 +00005943
cristy3ed852e2009-09-05 21:47:34 +00005944 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005945 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005946 (2*i*((*n).opacity
5947 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005948 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005949 }
glennrp47b9dd52010-11-24 18:12:06 +00005950
cristy3ed852e2009-09-05 21:47:34 +00005951 if (magn_methy == 4)
5952 {
5953 /* Replicate nearest */
5954 if (i <= ((m+1) << 1))
5955 (*q).opacity=(*pixels).opacity+0;
5956 else
5957 (*q).opacity=(*n).opacity+0;
5958 }
5959 }
glennrp47b9dd52010-11-24 18:12:06 +00005960
cristy3ed852e2009-09-05 21:47:34 +00005961 else /* if (magn_methy == 3 || magn_methy == 5) */
5962 {
5963 /* Replicate nearest */
5964 if (i <= ((m+1) << 1))
5965 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005966
cristy3ed852e2009-09-05 21:47:34 +00005967 else
5968 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00005969
cristy3ed852e2009-09-05 21:47:34 +00005970 if (magn_methy == 5)
5971 {
cristybb503372010-05-27 20:51:26 +00005972 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5973 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005974 +(*pixels).opacity);
5975 }
5976 }
5977 n++;
5978 q++;
5979 pixels++;
5980 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00005981
cristy3ed852e2009-09-05 21:47:34 +00005982 if (SyncAuthenticPixels(large_image,exception) == 0)
5983 break;
glennrp47b9dd52010-11-24 18:12:06 +00005984
cristy3ed852e2009-09-05 21:47:34 +00005985 } /* i */
5986 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00005987
cristy3ed852e2009-09-05 21:47:34 +00005988 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5989 next=(PixelPacket *) RelinquishMagickMemory(next);
5990
5991 length=image->columns;
5992
5993 if (logging != MagickFalse)
5994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5995 " Delete original image");
5996
5997 DeleteImageFromList(&image);
5998
5999 image=large_image;
6000
6001 mng_info->image=image;
6002
6003 /* magnify the columns */
6004 if (logging != MagickFalse)
6005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006006 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006007
cristybb503372010-05-27 20:51:26 +00006008 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006009 {
6010 register PixelPacket
6011 *pixels;
6012
6013 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp4737d522011-04-29 03:33:42 +00006014 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00006015 pixels=q+(image->columns-length);
6016 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00006017
cristybb503372010-05-27 20:51:26 +00006018 for (x=(ssize_t) (image->columns-length);
6019 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006020 {
glennrp7c7b3152011-04-26 04:01:27 +00006021 /* To do: Rewrite using Get/Set***PixelComponent() */
6022
cristybb503372010-05-27 20:51:26 +00006023 if (x == (ssize_t) (image->columns-length))
6024 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006025
cristybb503372010-05-27 20:51:26 +00006026 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6027 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006028
cristybb503372010-05-27 20:51:26 +00006029 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6030 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006031
cristybb503372010-05-27 20:51:26 +00006032 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006033 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006034
cristy3ed852e2009-09-05 21:47:34 +00006035 else
cristybb503372010-05-27 20:51:26 +00006036 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006037
cristy3ed852e2009-09-05 21:47:34 +00006038 for (i=0; i < m; i++)
6039 {
6040 if (magn_methx <= 1)
6041 {
6042 /* replicate previous */
glennrp4737d522011-04-29 03:33:42 +00006043 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00006044 *q=(*pixels);
6045 }
glennrp47b9dd52010-11-24 18:12:06 +00006046
cristy3ed852e2009-09-05 21:47:34 +00006047 else if (magn_methx == 2 || magn_methx == 4)
6048 {
6049 if (i == 0)
6050 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006051
cristy3ed852e2009-09-05 21:47:34 +00006052 else
6053 {
6054 /* Interpolate */
6055 (*q).red=(QM) ((2*i*((*n).red
6056 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00006057 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00006058 (*q).green=(QM) ((2*i*((*n).green
6059 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00006060 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00006061 (*q).blue=(QM) ((2*i*((*n).blue
6062 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00006063 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00006064 if (image->matte != MagickFalse)
6065 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006066 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006067 +(*pixels).opacity);
6068 }
glennrp47b9dd52010-11-24 18:12:06 +00006069
cristy3ed852e2009-09-05 21:47:34 +00006070 if (magn_methx == 4)
6071 {
6072 /* Replicate nearest */
6073 if (i <= ((m+1) << 1))
6074 (*q).opacity=(*pixels).opacity+0;
6075 else
6076 (*q).opacity=(*n).opacity+0;
6077 }
6078 }
glennrp47b9dd52010-11-24 18:12:06 +00006079
cristy3ed852e2009-09-05 21:47:34 +00006080 else /* if (magn_methx == 3 || magn_methx == 5) */
6081 {
6082 /* Replicate nearest */
6083 if (i <= ((m+1) << 1))
6084 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006085
cristy3ed852e2009-09-05 21:47:34 +00006086 else
6087 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00006088
cristy3ed852e2009-09-05 21:47:34 +00006089 if (magn_methx == 5)
6090 {
6091 /* Interpolate */
6092 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006093 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006094 +(*pixels).opacity);
6095 }
6096 }
6097 q++;
6098 }
6099 n++;
6100 p++;
6101 }
glennrp47b9dd52010-11-24 18:12:06 +00006102
cristy3ed852e2009-09-05 21:47:34 +00006103 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6104 break;
6105 }
glennrp3faa9a32011-04-23 14:00:25 +00006106#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006107 if (magn_methx != 1 || magn_methy != 1)
6108 {
6109 /*
6110 Rescale pixels to Quantum
6111 */
cristybb503372010-05-27 20:51:26 +00006112 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006113 {
6114 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006115
cristybb503372010-05-27 20:51:26 +00006116 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006117 {
glennrp7c7b3152011-04-26 04:01:27 +00006118 SetRedPixelComponent(q,ScaleShortToQuantum(
6119 GetRedPixelComponent(q));
6120 SetGreenPixelComponent(q,ScaleShortToQuantum(
6121 GetGreenPixelComponent(q));
6122 SetBluePixelComponent(q,ScaleShortToQuantum(
6123 GetBluePixelComponent(q));
6124 SetOpacityPixelComponent(q,ScaleShortToQuantum(
6125 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00006126 q++;
6127 }
glennrp47b9dd52010-11-24 18:12:06 +00006128
cristy3ed852e2009-09-05 21:47:34 +00006129 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6130 break;
6131 }
6132 }
6133#endif
6134 if (logging != MagickFalse)
6135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6136 " Finished MAGN processing");
6137 }
6138 }
6139
6140 /*
6141 Crop_box is with respect to the upper left corner of the MNG.
6142 */
6143 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6144 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6145 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6146 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6147 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6148 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6149 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6150 if ((crop_box.left != (mng_info->image_box.left
6151 +mng_info->x_off[object_id])) ||
6152 (crop_box.right != (mng_info->image_box.right
6153 +mng_info->x_off[object_id])) ||
6154 (crop_box.top != (mng_info->image_box.top
6155 +mng_info->y_off[object_id])) ||
6156 (crop_box.bottom != (mng_info->image_box.bottom
6157 +mng_info->y_off[object_id])))
6158 {
6159 if (logging != MagickFalse)
6160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6161 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006162
cristy3ed852e2009-09-05 21:47:34 +00006163 if ((crop_box.left < crop_box.right) &&
6164 (crop_box.top < crop_box.bottom))
6165 {
6166 Image
6167 *im;
6168
6169 RectangleInfo
6170 crop_info;
6171
6172 /*
6173 Crop_info is with respect to the upper left corner of
6174 the image.
6175 */
6176 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6177 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006178 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6179 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006180 image->page.width=image->columns;
6181 image->page.height=image->rows;
6182 image->page.x=0;
6183 image->page.y=0;
6184 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006185
cristy3ed852e2009-09-05 21:47:34 +00006186 if (im != (Image *) NULL)
6187 {
6188 image->columns=im->columns;
6189 image->rows=im->rows;
6190 im=DestroyImage(im);
6191 image->page.width=image->columns;
6192 image->page.height=image->rows;
6193 image->page.x=crop_box.left;
6194 image->page.y=crop_box.top;
6195 }
6196 }
glennrp47b9dd52010-11-24 18:12:06 +00006197
cristy3ed852e2009-09-05 21:47:34 +00006198 else
6199 {
6200 /*
6201 No pixels in crop area. The MNG spec still requires
6202 a layer, though, so make a single transparent pixel in
6203 the top left corner.
6204 */
6205 image->columns=1;
6206 image->rows=1;
6207 image->colors=2;
6208 (void) SetImageBackgroundColor(image);
6209 image->page.width=1;
6210 image->page.height=1;
6211 image->page.x=0;
6212 image->page.y=0;
6213 }
6214 }
6215#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6216 image=mng_info->image;
6217#endif
6218 }
6219
glennrp2b013e42010-11-24 16:55:50 +00006220#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6221 /* PNG does not handle depths greater than 16 so reduce it even
6222 * if lossy
6223 */
6224 if (image->depth > 16)
6225 image->depth=16;
6226#endif
6227
glennrp3faa9a32011-04-23 14:00:25 +00006228#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006229 if (LosslessReduceDepthOK(image) != MagickFalse)
6230 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006231#endif
glennrpd6afd542010-11-19 01:53:05 +00006232
cristy3ed852e2009-09-05 21:47:34 +00006233 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 if (image_info->number_scenes != 0)
6236 {
6237 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006238 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006239 break;
6240 }
glennrpd6afd542010-11-19 01:53:05 +00006241
cristy3ed852e2009-09-05 21:47:34 +00006242 if (logging != MagickFalse)
6243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6244 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006245
cristy3ed852e2009-09-05 21:47:34 +00006246 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006247
cristy3ed852e2009-09-05 21:47:34 +00006248 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006249
cristy3ed852e2009-09-05 21:47:34 +00006250 if (logging != MagickFalse)
6251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6252 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006253
cristy3ed852e2009-09-05 21:47:34 +00006254#if defined(MNG_INSERT_LAYERS)
6255 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6256 (mng_info->mng_height))
6257 {
6258 /*
6259 Insert a background layer if nothing else was found.
6260 */
6261 if (logging != MagickFalse)
6262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6263 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006264
cristy3ed852e2009-09-05 21:47:34 +00006265 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6266 {
6267 /*
6268 Allocate next image structure.
6269 */
6270 AcquireNextImage(image_info,image);
6271 if (GetNextImageInList(image) == (Image *) NULL)
6272 {
6273 image=DestroyImageList(image);
6274 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristy3ed852e2009-09-05 21:47:34 +00006276 if (logging != MagickFalse)
6277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6278 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006279
cristy3ed852e2009-09-05 21:47:34 +00006280 return((Image *) NULL);
6281 }
6282 image=SyncNextImageInList(image);
6283 }
6284 image->columns=mng_info->mng_width;
6285 image->rows=mng_info->mng_height;
6286 image->page.width=mng_info->mng_width;
6287 image->page.height=mng_info->mng_height;
6288 image->page.x=0;
6289 image->page.y=0;
6290 image->background_color=mng_background_color;
6291 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006292
cristy3ed852e2009-09-05 21:47:34 +00006293 if (image_info->ping == MagickFalse)
6294 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006295
cristy3ed852e2009-09-05 21:47:34 +00006296 mng_info->image_found++;
6297 }
6298#endif
6299 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006300
cristy3ed852e2009-09-05 21:47:34 +00006301 if (mng_iterations == 1)
6302 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006303
cristy3ed852e2009-09-05 21:47:34 +00006304 while (GetPreviousImageInList(image) != (Image *) NULL)
6305 {
6306 image_count++;
6307 if (image_count > 10*mng_info->image_found)
6308 {
6309 if (logging != MagickFalse)
6310 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006311
cristy3ed852e2009-09-05 21:47:34 +00006312 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6313 CoderError,"Linked list is corrupted, beginning of list not found",
6314 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristy3ed852e2009-09-05 21:47:34 +00006316 return((Image *) NULL);
6317 }
glennrp0fe50b42010-11-16 03:52:51 +00006318
cristy3ed852e2009-09-05 21:47:34 +00006319 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006320
cristy3ed852e2009-09-05 21:47:34 +00006321 if (GetNextImageInList(image) == (Image *) NULL)
6322 {
6323 if (logging != MagickFalse)
6324 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006325
cristy3ed852e2009-09-05 21:47:34 +00006326 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6327 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6328 image_info->filename);
6329 }
6330 }
glennrp47b9dd52010-11-24 18:12:06 +00006331
cristy3ed852e2009-09-05 21:47:34 +00006332 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6333 GetNextImageInList(image) ==
6334 (Image *) NULL)
6335 {
6336 if (logging != MagickFalse)
6337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6338 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006339
cristy3ed852e2009-09-05 21:47:34 +00006340 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6341 CoderError,"image->next for first image is NULL but shouldn't be.",
6342 "`%s'",image_info->filename);
6343 }
glennrp47b9dd52010-11-24 18:12:06 +00006344
cristy3ed852e2009-09-05 21:47:34 +00006345 if (mng_info->image_found == 0)
6346 {
6347 if (logging != MagickFalse)
6348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6349 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006350
cristy3ed852e2009-09-05 21:47:34 +00006351 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6352 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006353
cristy3ed852e2009-09-05 21:47:34 +00006354 if (image != (Image *) NULL)
6355 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006356
cristy3ed852e2009-09-05 21:47:34 +00006357 MngInfoFreeStruct(mng_info,&have_mng_structure);
6358 return((Image *) NULL);
6359 }
6360
6361 if (mng_info->ticks_per_second)
6362 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6363 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006364
cristy3ed852e2009-09-05 21:47:34 +00006365 else
6366 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006367
cristy3ed852e2009-09-05 21:47:34 +00006368 /* Find final nonzero image delay */
6369 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006370
cristy3ed852e2009-09-05 21:47:34 +00006371 while (GetNextImageInList(image) != (Image *) NULL)
6372 {
6373 if (image->delay)
6374 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006375
cristy3ed852e2009-09-05 21:47:34 +00006376 image=GetNextImageInList(image);
6377 }
glennrp0fe50b42010-11-16 03:52:51 +00006378
cristy3ed852e2009-09-05 21:47:34 +00006379 if (final_delay < final_image_delay)
6380 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006381
cristy3ed852e2009-09-05 21:47:34 +00006382 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006383
cristy3ed852e2009-09-05 21:47:34 +00006384 if (logging != MagickFalse)
6385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006386 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6387 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006388
cristy3ed852e2009-09-05 21:47:34 +00006389 if (logging != MagickFalse)
6390 {
6391 int
6392 scene;
6393
6394 scene=0;
6395 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006396
cristy3ed852e2009-09-05 21:47:34 +00006397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6398 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006399
cristy3ed852e2009-09-05 21:47:34 +00006400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006401 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006402
cristy3ed852e2009-09-05 21:47:34 +00006403 while (GetNextImageInList(image) != (Image *) NULL)
6404 {
6405 image=GetNextImageInList(image);
6406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006407 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006408 }
6409 }
6410
6411 image=GetFirstImageInList(image);
6412#ifdef MNG_COALESCE_LAYERS
6413 if (insert_layers)
6414 {
6415 Image
6416 *next_image,
6417 *next;
6418
cristybb503372010-05-27 20:51:26 +00006419 size_t
cristy3ed852e2009-09-05 21:47:34 +00006420 scene;
6421
6422 if (logging != MagickFalse)
6423 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006424
cristy3ed852e2009-09-05 21:47:34 +00006425 scene=image->scene;
6426 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006427
cristy3ed852e2009-09-05 21:47:34 +00006428 if (next_image == (Image *) NULL)
6429 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristy3ed852e2009-09-05 21:47:34 +00006431 image=DestroyImageList(image);
6432 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006433
cristy3ed852e2009-09-05 21:47:34 +00006434 for (next=image; next != (Image *) NULL; next=next_image)
6435 {
6436 next->page.width=mng_info->mng_width;
6437 next->page.height=mng_info->mng_height;
6438 next->page.x=0;
6439 next->page.y=0;
6440 next->scene=scene++;
6441 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006442
cristy3ed852e2009-09-05 21:47:34 +00006443 if (next_image == (Image *) NULL)
6444 break;
glennrp47b9dd52010-11-24 18:12:06 +00006445
cristy3ed852e2009-09-05 21:47:34 +00006446 if (next->delay == 0)
6447 {
6448 scene--;
6449 next_image->previous=GetPreviousImageInList(next);
6450 if (GetPreviousImageInList(next) == (Image *) NULL)
6451 image=next_image;
6452 else
6453 next->previous->next=next_image;
6454 next=DestroyImage(next);
6455 }
6456 }
6457 }
6458#endif
6459
6460 while (GetNextImageInList(image) != (Image *) NULL)
6461 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006462
cristy3ed852e2009-09-05 21:47:34 +00006463 image->dispose=BackgroundDispose;
6464
6465 if (logging != MagickFalse)
6466 {
6467 int
6468 scene;
6469
6470 scene=0;
6471 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006472
cristy3ed852e2009-09-05 21:47:34 +00006473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6474 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristy3ed852e2009-09-05 21:47:34 +00006476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006477 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6478 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006479
cristy3ed852e2009-09-05 21:47:34 +00006480 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006481 {
6482 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006483
cristyf2faecf2010-05-28 19:19:36 +00006484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006485 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6486 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006487 }
6488 }
glennrp47b9dd52010-11-24 18:12:06 +00006489
cristy3ed852e2009-09-05 21:47:34 +00006490 image=GetFirstImageInList(image);
6491 MngInfoFreeStruct(mng_info,&have_mng_structure);
6492 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006493
cristy3ed852e2009-09-05 21:47:34 +00006494 if (logging != MagickFalse)
6495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006496
cristy3ed852e2009-09-05 21:47:34 +00006497 return(GetFirstImageInList(image));
6498}
glennrp25c1e2b2010-03-25 01:39:56 +00006499#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006500static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6501{
6502 printf("Your PNG library is too old: You have libpng-%s\n",
6503 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006504
cristy3ed852e2009-09-05 21:47:34 +00006505 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6506 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006507
cristy3ed852e2009-09-05 21:47:34 +00006508 return(Image *) NULL;
6509}
glennrp47b9dd52010-11-24 18:12:06 +00006510
cristy3ed852e2009-09-05 21:47:34 +00006511static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6512{
6513 return(ReadPNGImage(image_info,exception));
6514}
glennrp25c1e2b2010-03-25 01:39:56 +00006515#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006516#endif
6517
6518/*
6519%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6520% %
6521% %
6522% %
6523% R e g i s t e r P N G I m a g e %
6524% %
6525% %
6526% %
6527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6528%
6529% RegisterPNGImage() adds properties for the PNG image format to
6530% the list of supported formats. The properties include the image format
6531% tag, a method to read and/or write the format, whether the format
6532% supports the saving of more than one frame to the same file or blob,
6533% whether the format supports native in-memory I/O, and a brief
6534% description of the format.
6535%
6536% The format of the RegisterPNGImage method is:
6537%
cristybb503372010-05-27 20:51:26 +00006538% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006539%
6540*/
cristybb503372010-05-27 20:51:26 +00006541ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006542{
6543 char
6544 version[MaxTextExtent];
6545
6546 MagickInfo
6547 *entry;
6548
6549 static const char
6550 *PNGNote=
6551 {
6552 "See http://www.libpng.org/ for details about the PNG format."
6553 },
glennrp47b9dd52010-11-24 18:12:06 +00006554
cristy3ed852e2009-09-05 21:47:34 +00006555 *JNGNote=
6556 {
6557 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6558 "format."
6559 },
glennrp47b9dd52010-11-24 18:12:06 +00006560
cristy3ed852e2009-09-05 21:47:34 +00006561 *MNGNote=
6562 {
6563 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6564 "format."
6565 };
6566
6567 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristy3ed852e2009-09-05 21:47:34 +00006569#if defined(PNG_LIBPNG_VER_STRING)
6570 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6571 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006572
cristy3ed852e2009-09-05 21:47:34 +00006573 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6574 {
6575 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6576 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6577 MaxTextExtent);
6578 }
6579#endif
glennrp47b9dd52010-11-24 18:12:06 +00006580
cristy3ed852e2009-09-05 21:47:34 +00006581 entry=SetMagickInfo("MNG");
6582 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00006583
cristy3ed852e2009-09-05 21:47:34 +00006584#if defined(MAGICKCORE_PNG_DELEGATE)
6585 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6586 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6587#endif
glennrp47b9dd52010-11-24 18:12:06 +00006588
cristy3ed852e2009-09-05 21:47:34 +00006589 entry->magick=(IsImageFormatHandler *) IsMNG;
6590 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00006591
cristy3ed852e2009-09-05 21:47:34 +00006592 if (*version != '\0')
6593 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006594
cristy3ed852e2009-09-05 21:47:34 +00006595 entry->module=ConstantString("PNG");
6596 entry->note=ConstantString(MNGNote);
6597 (void) RegisterMagickInfo(entry);
6598
6599 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006600
cristy3ed852e2009-09-05 21:47:34 +00006601#if defined(MAGICKCORE_PNG_DELEGATE)
6602 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6603 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6604#endif
glennrp47b9dd52010-11-24 18:12:06 +00006605
cristy3ed852e2009-09-05 21:47:34 +00006606 entry->magick=(IsImageFormatHandler *) IsPNG;
6607 entry->adjoin=MagickFalse;
6608 entry->description=ConstantString("Portable Network Graphics");
6609 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006610
cristy3ed852e2009-09-05 21:47:34 +00006611 if (*version != '\0')
6612 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006613
cristy3ed852e2009-09-05 21:47:34 +00006614 entry->note=ConstantString(PNGNote);
6615 (void) RegisterMagickInfo(entry);
6616
6617 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00006618
cristy3ed852e2009-09-05 21:47:34 +00006619#if defined(MAGICKCORE_PNG_DELEGATE)
6620 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6621 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6622#endif
glennrp47b9dd52010-11-24 18:12:06 +00006623
cristy3ed852e2009-09-05 21:47:34 +00006624 entry->magick=(IsImageFormatHandler *) IsPNG;
6625 entry->adjoin=MagickFalse;
6626 entry->description=ConstantString(
6627 "8-bit indexed with optional binary transparency");
6628 entry->module=ConstantString("PNG");
6629 (void) RegisterMagickInfo(entry);
6630
6631 entry=SetMagickInfo("PNG24");
6632 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006633
cristy3ed852e2009-09-05 21:47:34 +00006634#if defined(ZLIB_VERSION)
6635 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6636 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006637
cristy3ed852e2009-09-05 21:47:34 +00006638 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6639 {
6640 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6641 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6642 }
6643#endif
glennrp47b9dd52010-11-24 18:12:06 +00006644
cristy3ed852e2009-09-05 21:47:34 +00006645 if (*version != '\0')
6646 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006647
cristy3ed852e2009-09-05 21:47:34 +00006648#if defined(MAGICKCORE_PNG_DELEGATE)
6649 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6650 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6651#endif
glennrp47b9dd52010-11-24 18:12:06 +00006652
cristy3ed852e2009-09-05 21:47:34 +00006653 entry->magick=(IsImageFormatHandler *) IsPNG;
6654 entry->adjoin=MagickFalse;
6655 entry->description=ConstantString("opaque 24-bit RGB");
6656 entry->module=ConstantString("PNG");
6657 (void) RegisterMagickInfo(entry);
6658
6659 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00006660
cristy3ed852e2009-09-05 21:47:34 +00006661#if defined(MAGICKCORE_PNG_DELEGATE)
6662 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6663 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6664#endif
glennrp47b9dd52010-11-24 18:12:06 +00006665
cristy3ed852e2009-09-05 21:47:34 +00006666 entry->magick=(IsImageFormatHandler *) IsPNG;
6667 entry->adjoin=MagickFalse;
6668 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6669 entry->module=ConstantString("PNG");
6670 (void) RegisterMagickInfo(entry);
6671
6672 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006673
cristy3ed852e2009-09-05 21:47:34 +00006674#if defined(JNG_SUPPORTED)
6675#if defined(MAGICKCORE_PNG_DELEGATE)
6676 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6677 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6678#endif
6679#endif
glennrp47b9dd52010-11-24 18:12:06 +00006680
cristy3ed852e2009-09-05 21:47:34 +00006681 entry->magick=(IsImageFormatHandler *) IsJNG;
6682 entry->adjoin=MagickFalse;
6683 entry->description=ConstantString("JPEG Network Graphics");
6684 entry->module=ConstantString("PNG");
6685 entry->note=ConstantString(JNGNote);
6686 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00006687
cristy18b17442009-10-25 18:36:48 +00006688#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006689 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00006690#endif
glennrp47b9dd52010-11-24 18:12:06 +00006691
cristy3ed852e2009-09-05 21:47:34 +00006692 return(MagickImageCoderSignature);
6693}
6694
6695/*
6696%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6697% %
6698% %
6699% %
6700% U n r e g i s t e r P N G I m a g e %
6701% %
6702% %
6703% %
6704%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6705%
6706% UnregisterPNGImage() removes format registrations made by the
6707% PNG module from the list of supported formats.
6708%
6709% The format of the UnregisterPNGImage method is:
6710%
6711% UnregisterPNGImage(void)
6712%
6713*/
6714ModuleExport void UnregisterPNGImage(void)
6715{
6716 (void) UnregisterMagickInfo("MNG");
6717 (void) UnregisterMagickInfo("PNG");
6718 (void) UnregisterMagickInfo("PNG8");
6719 (void) UnregisterMagickInfo("PNG24");
6720 (void) UnregisterMagickInfo("PNG32");
6721 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006722
cristy3ed852e2009-09-05 21:47:34 +00006723#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006724 if (ping_semaphore != (SemaphoreInfo *) NULL)
6725 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006726#endif
6727}
6728
6729#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006730#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006731/*
6732%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6733% %
6734% %
6735% %
6736% W r i t e M N G I m a g e %
6737% %
6738% %
6739% %
6740%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6741%
6742% WriteMNGImage() writes an image in the Portable Network Graphics
6743% Group's "Multiple-image Network Graphics" encoded image format.
6744%
6745% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6746%
6747% The format of the WriteMNGImage method is:
6748%
6749% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6750%
6751% A description of each parameter follows.
6752%
6753% o image_info: the image info.
6754%
6755% o image: The image.
6756%
6757%
6758% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6759% "To do" under ReadPNGImage):
6760%
cristy3ed852e2009-09-05 21:47:34 +00006761% Preserve all unknown and not-yet-handled known chunks found in input
6762% PNG file and copy them into output PNG files according to the PNG
6763% copying rules.
6764%
6765% Write the iCCP chunk at MNG level when (icc profile length > 0)
6766%
6767% Improve selection of color type (use indexed-colour or indexed-colour
6768% with tRNS when 256 or fewer unique RGBA values are present).
6769%
6770% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6771% This will be complicated if we limit ourselves to generating MNG-LC
6772% files. For now we ignore disposal method 3 and simply overlay the next
6773% image on it.
6774%
6775% Check for identical PLTE's or PLTE/tRNS combinations and use a
6776% global MNG PLTE or PLTE/tRNS combination when appropriate.
6777% [mostly done 15 June 1999 but still need to take care of tRNS]
6778%
6779% Check for identical sRGB and replace with a global sRGB (and remove
6780% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6781% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6782% local gAMA/cHRM with local sRGB if appropriate).
6783%
6784% Check for identical sBIT chunks and write global ones.
6785%
6786% Provide option to skip writing the signature tEXt chunks.
6787%
6788% Use signatures to detect identical objects and reuse the first
6789% instance of such objects instead of writing duplicate objects.
6790%
6791% Use a smaller-than-32k value of compression window size when
6792% appropriate.
6793%
6794% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6795% ancillary text chunks and save profiles.
6796%
6797% Provide an option to force LC files (to ensure exact framing rate)
6798% instead of VLC.
6799%
6800% Provide an option to force VLC files instead of LC, even when offsets
6801% are present. This will involve expanding the embedded images with a
6802% transparent region at the top and/or left.
6803*/
6804
cristy3ed852e2009-09-05 21:47:34 +00006805static void
glennrpcf002022011-01-30 02:38:15 +00006806Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00006807 png_info *ping_info, unsigned char *profile_type, unsigned char
6808 *profile_description, unsigned char *profile_data, png_uint_32 length)
6809{
cristy3ed852e2009-09-05 21:47:34 +00006810 png_textp
6811 text;
6812
cristybb503372010-05-27 20:51:26 +00006813 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006814 i;
6815
6816 unsigned char
6817 *sp;
6818
6819 png_charp
6820 dp;
6821
6822 png_uint_32
6823 allocated_length,
6824 description_length;
6825
6826 unsigned char
6827 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006828
cristy3ed852e2009-09-05 21:47:34 +00006829 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6830 return;
6831
6832 if (image_info->verbose)
6833 {
glennrp0fe50b42010-11-16 03:52:51 +00006834 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6835 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006836 }
glennrp0fe50b42010-11-16 03:52:51 +00006837
cristy3ed852e2009-09-05 21:47:34 +00006838 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6839 description_length=(png_uint_32) strlen((const char *) profile_description);
6840 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6841 + description_length);
6842 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6843 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6844 text[0].key[0]='\0';
6845 (void) ConcatenateMagickString(text[0].key,
6846 "Raw profile type ",MaxTextExtent);
6847 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6848 sp=profile_data;
6849 dp=text[0].text;
6850 *dp++='\n';
6851 (void) CopyMagickString(dp,(const char *) profile_description,
6852 allocated_length);
6853 dp+=description_length;
6854 *dp++='\n';
6855 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006856 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006857 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00006858
cristybb503372010-05-27 20:51:26 +00006859 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006860 {
6861 if (i%36 == 0)
6862 *dp++='\n';
6863 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6864 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6865 }
glennrp47b9dd52010-11-24 18:12:06 +00006866
cristy3ed852e2009-09-05 21:47:34 +00006867 *dp++='\n';
6868 *dp='\0';
6869 text[0].text_length=(png_size_t) (dp-text[0].text);
6870 text[0].compression=image_info->compression == NoCompression ||
6871 (image_info->compression == UndefinedCompression &&
6872 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00006873
cristy3ed852e2009-09-05 21:47:34 +00006874 if (text[0].text_length <= allocated_length)
6875 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00006876
cristy3ed852e2009-09-05 21:47:34 +00006877 png_free(ping,text[0].text);
6878 png_free(ping,text[0].key);
6879 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006880}
6881
glennrpcf002022011-01-30 02:38:15 +00006882static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00006883 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00006884{
6885 char
6886 *name;
6887
6888 const StringInfo
6889 *profile;
6890
6891 unsigned char
6892 *data;
6893
6894 png_uint_32 length;
6895
6896 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00006897
6898 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6899 {
cristy3ed852e2009-09-05 21:47:34 +00006900 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00006901
cristy3ed852e2009-09-05 21:47:34 +00006902 if (profile != (const StringInfo *) NULL)
6903 {
6904 StringInfo
glennrpcf002022011-01-30 02:38:15 +00006905 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00006906
glennrp47b9dd52010-11-24 18:12:06 +00006907 if (LocaleNCompare(name,string,11) == 0)
6908 {
6909 if (logging != MagickFalse)
6910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6911 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00006912
glennrpcf002022011-01-30 02:38:15 +00006913 ping_profile=CloneStringInfo(profile);
6914 data=GetStringInfoDatum(ping_profile),
6915 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006916 data[4]=data[3];
6917 data[3]=data[2];
6918 data[2]=data[1];
6919 data[1]=data[0];
6920 (void) WriteBlobMSBULong(image,length-5); /* data length */
6921 (void) WriteBlob(image,length-1,data+1);
6922 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00006923 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006924 }
cristy3ed852e2009-09-05 21:47:34 +00006925 }
glennrp47b9dd52010-11-24 18:12:06 +00006926
cristy3ed852e2009-09-05 21:47:34 +00006927 name=GetNextImageProfile(image);
6928 }
glennrp47b9dd52010-11-24 18:12:06 +00006929
cristy3ed852e2009-09-05 21:47:34 +00006930 return(MagickTrue);
6931}
6932
glennrpb9cfe272010-12-21 15:08:06 +00006933
cristy3ed852e2009-09-05 21:47:34 +00006934/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00006935static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6936 const ImageInfo *IMimage_info,Image *IMimage)
6937{
6938 Image
6939 *image;
6940
6941 ImageInfo
6942 *image_info;
6943
cristy3ed852e2009-09-05 21:47:34 +00006944 char
6945 s[2];
6946
6947 const char
6948 *name,
6949 *property,
6950 *value;
6951
6952 const StringInfo
6953 *profile;
6954
cristy3ed852e2009-09-05 21:47:34 +00006955 int
cristy3ed852e2009-09-05 21:47:34 +00006956 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006957 pass;
6958
glennrpe9c26dc2010-05-30 01:56:35 +00006959 png_byte
6960 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006961
glennrp39992b42010-11-14 00:03:43 +00006962 png_color
6963 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00006964
glennrp5af765f2010-03-30 11:12:18 +00006965 png_color_16
6966 ping_background,
6967 ping_trans_color;
6968
cristy3ed852e2009-09-05 21:47:34 +00006969 png_info
6970 *ping_info;
6971
6972 png_struct
6973 *ping;
6974
glennrp5af765f2010-03-30 11:12:18 +00006975 png_uint_32
6976 ping_height,
6977 ping_width;
6978
cristybb503372010-05-27 20:51:26 +00006979 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006980 y;
6981
6982 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00006983 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00006984 logging,
glennrp58e01762011-01-07 15:28:54 +00006985 matte,
6986
glennrpda8f3a72011-02-27 23:54:12 +00006987 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00006988 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00006989 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00006990 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00006991 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00006992 ping_have_bKGD,
6993 ping_have_pHYs,
6994 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00006995
6996 ping_exclude_bKGD,
6997 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00006998 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00006999 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007000 ping_exclude_gAMA,
7001 ping_exclude_iCCP,
7002 /* ping_exclude_iTXt, */
7003 ping_exclude_oFFs,
7004 ping_exclude_pHYs,
7005 ping_exclude_sRGB,
7006 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007007 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007008 ping_exclude_vpAg,
7009 ping_exclude_zCCP, /* hex-encoded iCCP */
7010 ping_exclude_zTXt,
7011
glennrp8d3d6e52011-04-19 04:39:51 +00007012 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007013 ping_need_colortype_warning,
7014
glennrp82b3c532011-03-22 19:20:54 +00007015 status,
glennrpd3371642011-03-22 19:42:23 +00007016 tried_333,
7017 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007018
7019 QuantumInfo
7020 *quantum_info;
7021
cristybb503372010-05-27 20:51:26 +00007022 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007023 i,
7024 x;
7025
7026 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007027 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007028
glennrp5af765f2010-03-30 11:12:18 +00007029 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007030 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007031 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007032 ping_color_type,
7033 ping_interlace_method,
7034 ping_compression_method,
7035 ping_filter_method,
7036 ping_num_trans;
7037
cristybb503372010-05-27 20:51:26 +00007038 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007039 image_depth,
7040 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007041
cristybb503372010-05-27 20:51:26 +00007042 size_t
cristy3ed852e2009-09-05 21:47:34 +00007043 quality,
7044 rowbytes,
7045 save_image_depth;
7046
glennrpdfd70802010-11-14 01:23:35 +00007047 int
glennrpfd05d622011-02-25 04:10:33 +00007048 j,
glennrpf09bded2011-01-08 01:15:59 +00007049 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007050 number_opaque,
7051 number_semitransparent,
7052 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007053 ping_pHYs_unit_type;
7054
7055 png_uint_32
7056 ping_pHYs_x_resolution,
7057 ping_pHYs_y_resolution;
7058
cristy3ed852e2009-09-05 21:47:34 +00007059 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007060 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007061
glennrpb9cfe272010-12-21 15:08:06 +00007062 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7063 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007064 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007065 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007066
cristy3ed852e2009-09-05 21:47:34 +00007067#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007068 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007069#endif
7070
glennrp5af765f2010-03-30 11:12:18 +00007071 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007072 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007073 ping_color_type=0,
7074 ping_interlace_method=0,
7075 ping_compression_method=0,
7076 ping_filter_method=0,
7077 ping_num_trans = 0;
7078
7079 ping_background.red = 0;
7080 ping_background.green = 0;
7081 ping_background.blue = 0;
7082 ping_background.gray = 0;
7083 ping_background.index = 0;
7084
7085 ping_trans_color.red=0;
7086 ping_trans_color.green=0;
7087 ping_trans_color.blue=0;
7088 ping_trans_color.gray=0;
7089
glennrpdfd70802010-11-14 01:23:35 +00007090 ping_pHYs_unit_type = 0;
7091 ping_pHYs_x_resolution = 0;
7092 ping_pHYs_y_resolution = 0;
7093
glennrpda8f3a72011-02-27 23:54:12 +00007094 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007095 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007096 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007097 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007098 ping_have_bKGD=MagickFalse;
7099 ping_have_pHYs=MagickFalse;
7100 ping_have_tRNS=MagickFalse;
7101
glennrp0e8ea192010-12-24 18:00:33 +00007102 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7103 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007104 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007105 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007106 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007107 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7108 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7109 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7110 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7111 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7112 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007113 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007114 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7115 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7116 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7117
glennrp8d3d6e52011-04-19 04:39:51 +00007118 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007119 ping_need_colortype_warning = MagickFalse;
7120
glennrp8bb3a022010-12-13 20:40:04 +00007121 number_opaque = 0;
7122 number_semitransparent = 0;
7123 number_transparent = 0;
7124
glennrpfd05d622011-02-25 04:10:33 +00007125 if (logging != MagickFalse)
7126 {
7127 if (image->storage_class == UndefinedClass)
7128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7129 " storage_class=UndefinedClass");
7130 if (image->storage_class == DirectClass)
7131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7132 " storage_class=DirectClass");
7133 if (image->storage_class == PseudoClass)
7134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7135 " storage_class=PseudoClass");
7136 }
glennrp28af3712011-04-06 18:07:30 +00007137
glennrpc6c391a2011-04-27 02:23:56 +00007138 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007139 {
glennrpc6c391a2011-04-27 02:23:56 +00007140 if (image->storage_class != PseudoClass && image->colormap != NULL)
7141 {
7142 /* Free the bogus colormap; it can cause trouble later */
7143 if (logging != MagickFalse)
7144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7145 " Freeing bogus colormap");
7146 (void *) RelinquishMagickMemory(image->colormap);
7147 image->colormap=NULL;
7148 }
glennrp28af3712011-04-06 18:07:30 +00007149 }
glennrpfd05d622011-02-25 04:10:33 +00007150
cristy3ed852e2009-09-05 21:47:34 +00007151 if (image->colorspace != RGBColorspace)
7152 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007153
glennrp3241bd02010-12-12 04:36:28 +00007154 /*
7155 Sometimes we get PseudoClass images whose RGB values don't match
7156 the colors in the colormap. This code syncs the RGB values.
7157 */
7158 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7159 (void) SyncImage(image);
7160
glennrpa6a06632011-01-19 15:15:34 +00007161#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7162 if (image->depth > 8)
7163 {
7164 if (logging != MagickFalse)
7165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7166 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7167
7168 image->depth=8;
7169 }
7170#endif
7171
glennrp67b9c1a2011-04-22 18:47:36 +00007172#if 0 /* To do: Option to use the original colormap */
7173 if (ping_preserve_colormap != MagickFalse)
7174 {
7175 }
7176#endif
7177
glennrp3faa9a32011-04-23 14:00:25 +00007178#if 0 /* To do: respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007179 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7180 {
glennrp9d0ea4d2011-04-22 18:35:57 +00007181 }
glennrp67b9c1a2011-04-22 18:47:36 +00007182#endif
glennrp9d0ea4d2011-04-22 18:35:57 +00007183
glennrp67b9c1a2011-04-22 18:47:36 +00007184 /* To do: set to next higher multiple of 8 */
7185 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007186 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007187
glennrp2b013e42010-11-24 16:55:50 +00007188#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7189 /* PNG does not handle depths greater than 16 so reduce it even
7190 * if lossy
7191 */
7192 if (image->depth > 16)
7193 image->depth=16;
7194#endif
7195
glennrp3faa9a32011-04-23 14:00:25 +00007196#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007197 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007198 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007199 image->depth = 8;
7200#endif
7201
glennrpc8c2f062011-02-25 19:00:33 +00007202 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007203 * we reduce the transparency to binary and run again, then if there
7204 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7205 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7206 * palette. The final reduction can only fail if there are still 256
7207 * colors present and one of them has both transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007208 */
glennrp82b3c532011-03-22 19:20:54 +00007209
7210 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007211 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007212
glennrpd3371642011-03-22 19:42:23 +00007213 for (j=0; j<5; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007214 {
7215 /* BUILD_PALETTE
7216 *
7217 * Sometimes we get DirectClass images that have 256 colors or fewer.
7218 * This code will build a colormap.
7219 *
7220 * Also, sometimes we get PseudoClass images with an out-of-date
7221 * colormap. This code will replace the colormap with a new one.
7222 * Sometimes we get PseudoClass images that have more than 256 colors.
7223 * This code will delete the colormap and change the image to
7224 * DirectClass.
7225 *
7226 * If image->matte is MagickFalse, we ignore the opacity channel
7227 * even though it sometimes contains left-over non-opaque values.
7228 *
7229 * Also we gather some information (number of opaque, transparent,
7230 * and semitransparent pixels, and whether the image has any non-gray
7231 * pixels or only black-and-white pixels) that we might need later.
7232 *
7233 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7234 * we need to check for bogus non-opaque values, at least.
7235 */
glennrp3c218112010-11-27 15:31:26 +00007236
glennrpd71e86a2011-02-24 01:28:37 +00007237 ExceptionInfo
7238 *exception;
glennrp3c218112010-11-27 15:31:26 +00007239
glennrpd71e86a2011-02-24 01:28:37 +00007240 int
7241 n;
glennrp3c218112010-11-27 15:31:26 +00007242
glennrpd71e86a2011-02-24 01:28:37 +00007243 PixelPacket
7244 opaque[260],
7245 semitransparent[260],
7246 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007247
glennrpd71e86a2011-02-24 01:28:37 +00007248 register IndexPacket
7249 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007250
glennrpd71e86a2011-02-24 01:28:37 +00007251 register const PixelPacket
7252 *s,
7253 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007254
glennrpfd05d622011-02-25 04:10:33 +00007255 register PixelPacket
7256 *r;
7257
glennrpd71e86a2011-02-24 01:28:37 +00007258 if (logging != MagickFalse)
7259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7260 " Enter BUILD_PALETTE:");
7261
7262 if (logging != MagickFalse)
7263 {
glennrp03812ae2010-12-24 01:31:34 +00007264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007265 " image->columns=%.20g",(double) image->columns);
7266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7267 " image->rows=%.20g",(double) image->rows);
7268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7269 " image->matte=%.20g",(double) image->matte);
7270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7271 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007272
glennrpfd05d622011-02-25 04:10:33 +00007273 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007274 {
7275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007276 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007278 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007279
glennrpd71e86a2011-02-24 01:28:37 +00007280 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007281 {
glennrpd71e86a2011-02-24 01:28:37 +00007282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7283 " %d (%d,%d,%d,%d)",
7284 (int) i,
7285 (int) image->colormap[i].red,
7286 (int) image->colormap[i].green,
7287 (int) image->colormap[i].blue,
7288 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007289 }
glennrp2cc891a2010-12-24 13:44:32 +00007290
glennrpd71e86a2011-02-24 01:28:37 +00007291 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7292 {
7293 if (i > 255)
7294 {
7295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7296 " %d (%d,%d,%d,%d)",
7297 (int) i,
7298 (int) image->colormap[i].red,
7299 (int) image->colormap[i].green,
7300 (int) image->colormap[i].blue,
7301 (int) image->colormap[i].opacity);
7302 }
7303 }
glennrp03812ae2010-12-24 01:31:34 +00007304 }
glennrp7ddcc222010-12-11 05:01:05 +00007305
glennrpd71e86a2011-02-24 01:28:37 +00007306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7307 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007308
glennrpd71e86a2011-02-24 01:28:37 +00007309 if (image->colors == 0)
7310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7311 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007312
glennrp8d3d6e52011-04-19 04:39:51 +00007313 if (ping_preserve_colormap == MagickFalse)
7314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7315 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007316 }
7317
7318 exception=(&image->exception);
7319
7320 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007321 number_opaque = 0;
7322 number_semitransparent = 0;
7323 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007324
7325 for (y=0; y < (ssize_t) image->rows; y++)
7326 {
7327 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7328
7329 if (q == (PixelPacket *) NULL)
7330 break;
7331
7332 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007333 {
glennrp4737d522011-04-29 03:33:42 +00007334 if (image->matte == MagickFalse ||
7335 GetOpacityPixelComponent(q) == OpaqueOpacity)
glennrpd71e86a2011-02-24 01:28:37 +00007336 {
7337 if (number_opaque < 259)
7338 {
7339 if (number_opaque == 0)
7340 {
glennrp8e045c82011-04-27 16:40:27 +00007341 GetRGBPixelComponents(q, opaque[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007342 opaque[0].opacity=OpaqueOpacity;
7343 number_opaque=1;
7344 }
glennrp2cc891a2010-12-24 13:44:32 +00007345
glennrpd71e86a2011-02-24 01:28:37 +00007346 for (i=0; i< (ssize_t) number_opaque; i++)
7347 {
glennrp0e68fac2011-04-26 04:51:47 +00007348 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007349 break;
7350 }
glennrp7ddcc222010-12-11 05:01:05 +00007351
glennrpd71e86a2011-02-24 01:28:37 +00007352 if (i == (ssize_t) number_opaque &&
7353 number_opaque < 259)
7354 {
7355 number_opaque++;
glennrp8e045c82011-04-27 16:40:27 +00007356 GetRGBPixelComponents(q, opaque[i]);
glennrpca7ad3a2011-04-26 04:44:54 +00007357 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00007358 }
7359 }
7360 }
7361 else if (q->opacity == TransparentOpacity)
7362 {
7363 if (number_transparent < 259)
7364 {
7365 if (number_transparent == 0)
7366 {
glennrp8e045c82011-04-27 16:40:27 +00007367 GetRGBOPixelComponents(q, transparent[0]);
glennrpa18d5bc2011-04-23 14:51:34 +00007368 ping_trans_color.red=
7369 (unsigned short) GetRedPixelComponent(q);
7370 ping_trans_color.green=
7371 (unsigned short) GetGreenPixelComponent(q);
7372 ping_trans_color.blue=
7373 (unsigned short) GetBluePixelComponent(q);
7374 ping_trans_color.gray=
7375 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007376 number_transparent = 1;
7377 }
7378
7379 for (i=0; i< (ssize_t) number_transparent; i++)
7380 {
glennrp0e68fac2011-04-26 04:51:47 +00007381 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00007382 break;
7383 }
7384
7385 if (i == (ssize_t) number_transparent &&
7386 number_transparent < 259)
7387 {
7388 number_transparent++;
glennrp8e045c82011-04-27 16:40:27 +00007389 GetRGBOPixelComponents(q, transparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00007390 }
7391 }
7392 }
7393 else
7394 {
7395 if (number_semitransparent < 259)
7396 {
7397 if (number_semitransparent == 0)
7398 {
glennrp8e045c82011-04-27 16:40:27 +00007399 GetRGBOPixelComponents(q, semitransparent[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007400 number_semitransparent = 1;
7401 }
7402
7403 for (i=0; i< (ssize_t) number_semitransparent; i++)
7404 {
glennrp0e68fac2011-04-26 04:51:47 +00007405 if (IsColorEqual(q, semitransparent+i)
glennrpca7ad3a2011-04-26 04:44:54 +00007406 && GetOpacityPixelComponent(q) ==
7407 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00007408 break;
7409 }
7410
7411 if (i == (ssize_t) number_semitransparent &&
7412 number_semitransparent < 259)
7413 {
7414 number_semitransparent++;
glennrp8e045c82011-04-27 16:40:27 +00007415 GetRGBOPixelComponents(q, semitransparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00007416 }
7417 }
7418 }
7419 q++;
7420 }
7421 }
7422
7423 if (ping_exclude_bKGD == MagickFalse)
7424 {
7425 /* Add the background color to the palette, if it
7426 * isn't already there.
7427 */
glennrpc6c391a2011-04-27 02:23:56 +00007428 if (logging != MagickFalse)
7429 {
7430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7431 " Check colormap for background (%d,%d,%d)",
7432 (int) image->background_color.red,
7433 (int) image->background_color.green,
7434 (int) image->background_color.blue);
7435 }
glennrpd71e86a2011-02-24 01:28:37 +00007436 for (i=0; i<number_opaque; i++)
7437 {
glennrpca7ad3a2011-04-26 04:44:54 +00007438 if (opaque[i].red == image->background_color.red &&
7439 opaque[i].green == image->background_color.green &&
7440 opaque[i].blue == image->background_color.blue)
7441 break;
glennrpd71e86a2011-02-24 01:28:37 +00007442 }
glennrpd71e86a2011-02-24 01:28:37 +00007443 if (number_opaque < 259 && i == number_opaque)
7444 {
glennrp8e045c82011-04-27 16:40:27 +00007445 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00007446 ping_background.index = i;
7447 if (logging != MagickFalse)
7448 {
7449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7450 " background_color index is %d",(int) i);
7451 }
7452
glennrpd71e86a2011-02-24 01:28:37 +00007453 }
glennrpa080bc32011-03-11 18:03:44 +00007454 else if (logging != MagickFalse)
7455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7456 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007457 }
7458
7459 image_colors=number_opaque+number_transparent+number_semitransparent;
7460
glennrpa080bc32011-03-11 18:03:44 +00007461 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7462 {
7463 /* No room for the background color; remove it. */
7464 number_opaque--;
7465 image_colors--;
7466 }
7467
glennrpd71e86a2011-02-24 01:28:37 +00007468 if (logging != MagickFalse)
7469 {
7470 if (image_colors > 256)
7471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7472 " image has more than 256 colors");
7473
7474 else
7475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7476 " image has %d colors",image_colors);
7477 }
7478
glennrp8d3d6e52011-04-19 04:39:51 +00007479 if (ping_preserve_colormap != MagickFalse)
7480 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007481
glennrpfd05d622011-02-25 04:10:33 +00007482 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007483 {
7484 ping_have_color=MagickFalse;
7485 ping_have_non_bw=MagickFalse;
7486
7487 if(image_colors > 256)
7488 {
7489 for (y=0; y < (ssize_t) image->rows; y++)
7490 {
7491 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7492
7493 if (q == (PixelPacket *) NULL)
7494 break;
7495
7496 /* Worst case is black-and-white; we are looking at every
7497 * pixel twice.
7498 */
7499
7500 if (ping_have_color == MagickFalse)
7501 {
7502 s=q;
7503 for (x=0; x < (ssize_t) image->columns; x++)
7504 {
glennrpa18d5bc2011-04-23 14:51:34 +00007505 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7506 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00007507 {
7508 ping_have_color=MagickTrue;
7509 ping_have_non_bw=MagickTrue;
7510 break;
7511 }
7512 s++;
7513 }
7514 }
7515
7516 if (ping_have_non_bw == MagickFalse)
7517 {
7518 s=q;
7519 for (x=0; x < (ssize_t) image->columns; x++)
7520 {
glennrpa18d5bc2011-04-23 14:51:34 +00007521 if (GetRedPixelComponent(s) != 0 &&
7522 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00007523 {
7524 ping_have_non_bw=MagickTrue;
7525 }
7526 s++;
7527 }
7528 }
7529 }
7530 }
7531 }
7532
7533 if (image_colors < 257)
7534 {
7535 PixelPacket
7536 colormap[260];
7537
7538 /*
7539 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007540 */
7541
glennrpd71e86a2011-02-24 01:28:37 +00007542 if (logging != MagickFalse)
7543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7544 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007545
glennrpd71e86a2011-02-24 01:28:37 +00007546 /* Sort palette, transparent first */;
7547
7548 n = 0;
7549
7550 for (i=0; i<number_transparent; i++)
7551 colormap[n++] = transparent[i];
7552
7553 for (i=0; i<number_semitransparent; i++)
7554 colormap[n++] = semitransparent[i];
7555
7556 for (i=0; i<number_opaque; i++)
7557 colormap[n++] = opaque[i];
7558
glennrpc6c391a2011-04-27 02:23:56 +00007559 ping_background.index +=
7560 (number_transparent + number_semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00007561
7562 /* image_colors < 257; search the colormap instead of the pixels
7563 * to get ping_have_color and ping_have_non_bw
7564 */
7565 for (i=0; i<n; i++)
7566 {
7567 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007568 {
glennrpd71e86a2011-02-24 01:28:37 +00007569 if (colormap[i].red != colormap[i].green ||
7570 colormap[i].red != colormap[i].blue)
7571 {
7572 ping_have_color=MagickTrue;
7573 ping_have_non_bw=MagickTrue;
7574 break;
7575 }
7576 }
7577
7578 if (ping_have_non_bw == MagickFalse)
7579 {
7580 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007581 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007582 }
glennrp8bb3a022010-12-13 20:40:04 +00007583 }
7584
glennrpd71e86a2011-02-24 01:28:37 +00007585 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7586 (number_transparent == 0 && number_semitransparent == 0)) &&
7587 (((mng_info->write_png_colortype-1) ==
7588 PNG_COLOR_TYPE_PALETTE) ||
7589 (mng_info->write_png_colortype == 0)))
7590 {
glennrp6185c532011-01-14 17:58:40 +00007591 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007592 {
glennrpd71e86a2011-02-24 01:28:37 +00007593 if (n != (ssize_t) image_colors)
7594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7595 " image_colors (%d) and n (%d) don't match",
7596 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007597
glennrpd71e86a2011-02-24 01:28:37 +00007598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7599 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007600 }
glennrp03812ae2010-12-24 01:31:34 +00007601
glennrpd71e86a2011-02-24 01:28:37 +00007602 image->colors = image_colors;
7603
7604 if (AcquireImageColormap(image,image_colors) ==
7605 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007606 ThrowWriterException(ResourceLimitError,
7607 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007608
7609 for (i=0; i< (ssize_t) image_colors; i++)
7610 image->colormap[i] = colormap[i];
7611
7612 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007613 {
glennrpd71e86a2011-02-24 01:28:37 +00007614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7615 " image->colors=%d (%d)",
7616 (int) image->colors, image_colors);
7617
7618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7619 " Update the pixel indexes");
7620 }
glennrp6185c532011-01-14 17:58:40 +00007621
glennrpfd05d622011-02-25 04:10:33 +00007622 /* Sync the pixel indices with the new colormap */
7623
glennrpd71e86a2011-02-24 01:28:37 +00007624 for (y=0; y < (ssize_t) image->rows; y++)
7625 {
7626 q=GetAuthenticPixels(image,0,y,image->columns,1,
7627 exception);
glennrp6185c532011-01-14 17:58:40 +00007628
glennrpd71e86a2011-02-24 01:28:37 +00007629 if (q == (PixelPacket *) NULL)
7630 break;
glennrp6185c532011-01-14 17:58:40 +00007631
glennrpd71e86a2011-02-24 01:28:37 +00007632 indexes=GetAuthenticIndexQueue(image);
7633
7634 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007635 {
glennrpd71e86a2011-02-24 01:28:37 +00007636 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007637 {
glennrpd71e86a2011-02-24 01:28:37 +00007638 if ((image->matte == MagickFalse ||
glennrpca7ad3a2011-04-26 04:44:54 +00007639 image->colormap[i].opacity ==
7640 GetOpacityPixelComponent(q)) &&
7641 image->colormap[i].red ==
7642 GetRedPixelComponent(q) &&
7643 image->colormap[i].green ==
7644 GetGreenPixelComponent(q) &&
7645 image->colormap[i].blue ==
7646 GetBluePixelComponent(q))
glennrp6185c532011-01-14 17:58:40 +00007647 {
cristy9fff7b42011-04-29 01:09:31 +00007648 SetIndexPixelComponent(indexes+x,i);
glennrpd71e86a2011-02-24 01:28:37 +00007649 break;
glennrp6185c532011-01-14 17:58:40 +00007650 }
glennrp6185c532011-01-14 17:58:40 +00007651 }
glennrpd71e86a2011-02-24 01:28:37 +00007652 q++;
7653 }
glennrp6185c532011-01-14 17:58:40 +00007654
glennrpd71e86a2011-02-24 01:28:37 +00007655 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7656 break;
7657 }
7658 }
7659 }
7660
7661 if (logging != MagickFalse)
7662 {
7663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7664 " image->colors=%d", (int) image->colors);
7665
7666 if (image->colormap != NULL)
7667 {
7668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7669 " i (red,green,blue,opacity)");
7670
7671 for (i=0; i < (ssize_t) image->colors; i++)
7672 {
cristy72988482011-03-29 16:34:38 +00007673 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007674 {
7675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7676 " %d (%d,%d,%d,%d)",
7677 (int) i,
7678 (int) image->colormap[i].red,
7679 (int) image->colormap[i].green,
7680 (int) image->colormap[i].blue,
7681 (int) image->colormap[i].opacity);
7682 }
glennrp6185c532011-01-14 17:58:40 +00007683 }
7684 }
glennrp03812ae2010-12-24 01:31:34 +00007685
glennrpd71e86a2011-02-24 01:28:37 +00007686 if (number_transparent < 257)
7687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7688 " number_transparent = %d",
7689 number_transparent);
7690 else
glennrp03812ae2010-12-24 01:31:34 +00007691
glennrpd71e86a2011-02-24 01:28:37 +00007692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7693 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007694
glennrpd71e86a2011-02-24 01:28:37 +00007695 if (number_opaque < 257)
7696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7697 " number_opaque = %d",
7698 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007699
glennrpd71e86a2011-02-24 01:28:37 +00007700 else
7701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7702 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007703
glennrpd71e86a2011-02-24 01:28:37 +00007704 if (number_semitransparent < 257)
7705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7706 " number_semitransparent = %d",
7707 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007708
glennrpd71e86a2011-02-24 01:28:37 +00007709 else
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007712
glennrpd71e86a2011-02-24 01:28:37 +00007713 if (ping_have_non_bw == MagickFalse)
7714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7715 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007716
glennrpd71e86a2011-02-24 01:28:37 +00007717 else if (ping_have_color == MagickFalse)
7718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719 " All pixels and the background are gray");
7720
7721 else
7722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7723 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007724
glennrp03812ae2010-12-24 01:31:34 +00007725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7726 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007727 }
glennrpfd05d622011-02-25 04:10:33 +00007728
glennrpc8c2f062011-02-25 19:00:33 +00007729 if (mng_info->write_png8 == MagickFalse)
7730 break;
glennrpfd05d622011-02-25 04:10:33 +00007731
glennrpc8c2f062011-02-25 19:00:33 +00007732 /* Make any reductions necessary for the PNG8 format */
7733 if (image_colors <= 256 &&
7734 image_colors != 0 && image->colormap != NULL &&
7735 number_semitransparent == 0 &&
7736 number_transparent <= 1)
7737 break;
7738
7739 /* PNG8 can't have semitransparent colors so we threshold the
7740 * opacity to 0 or OpaqueOpacity
7741 */
7742 if (number_semitransparent != 0)
7743 {
7744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7745 " Thresholding the alpha channel to binary");
7746
7747 for (y=0; y < (ssize_t) image->rows; y++)
7748 {
7749 r=GetAuthenticPixels(image,0,y,image->columns,1,
7750 exception);
7751
7752 if (r == (PixelPacket *) NULL)
7753 break;
7754
7755 for (x=0; x < (ssize_t) image->columns; x++)
7756 {
glennrpa18d5bc2011-04-23 14:51:34 +00007757 SetOpacityPixelComponent(r,
7758 (GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7759 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00007760 r++;
7761 }
7762
7763 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7764 break;
7765
7766 if (image_colors != 0 && image_colors <= 256 &&
7767 image->colormap != NULL)
7768 for (i=0; i<image_colors; i++)
7769 image->colormap[i].opacity =
glennrp82b3c532011-03-22 19:20:54 +00007770 image->colormap[i].opacity > TransparentOpacity/2 ?
7771 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007772 }
7773 continue;
7774 }
7775
7776 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007777 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7778 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7779 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007780 */
glennrpd3371642011-03-22 19:42:23 +00007781 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7782 {
7783 if (logging != MagickFalse)
7784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7785 " Quantizing the background color to 4-4-4");
7786
7787 tried_444 = MagickTrue;
7788
7789 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007790 ScaleCharToQuantum(
7791 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7792 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007793 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007794 ScaleCharToQuantum(
7795 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7796 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007797 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007798 ScaleCharToQuantum(
7799 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7800 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007801
7802 if (logging != MagickFalse)
7803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7804 " Quantizing the pixel colors to 4-4-4");
7805
7806 if (image->colormap == NULL)
7807 {
7808 for (y=0; y < (ssize_t) image->rows; y++)
7809 {
7810 r=GetAuthenticPixels(image,0,y,image->columns,1,
7811 exception);
7812
7813 if (r == (PixelPacket *) NULL)
7814 break;
7815
7816 for (x=0; x < (ssize_t) image->columns; x++)
7817 {
glennrp4737d522011-04-29 03:33:42 +00007818 if (GetOpacityPixelComponent(r) == TransparentOpacity)
glennrpd3371642011-03-22 19:42:23 +00007819 {
glennrpb5d5e6d2011-04-27 17:08:19 +00007820 SetRGBPixelComponents(r,image->background_color);
glennrpd3371642011-03-22 19:42:23 +00007821 }
7822 else
7823 {
glennrp4737d522011-04-29 03:33:42 +00007824 SetRedPixelComponent(r,ScaleCharToQuantum(
glennrp19180202011-04-29 14:05:58 +00007825 (ScaleQuantumToChar(GetRedPixelComponent(r)) & 0xf0) |
7826 (ScaleQuantumToChar(GetRedPixelComponent(r)) & 0xf0) >> 4));
glennrp4737d522011-04-29 03:33:42 +00007827 SetGreenPixelComponent(r,ScaleCharToQuantum(
glennrp19180202011-04-29 14:05:58 +00007828 (ScaleQuantumToChar(GetGreenPixelComponent(r)) & 0xf0) |
7829 (ScaleQuantumToChar(GetGreenPixelComponent(r)) & 0xf0) >>
7830 4));
glennrp4737d522011-04-29 03:33:42 +00007831 SetBluePixelComponent(r,ScaleCharToQuantum(
glennrp19180202011-04-29 14:05:58 +00007832 (ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xf0) |
7833 (ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xf0) >>
7834 4));
glennrpd3371642011-03-22 19:42:23 +00007835 }
7836 r++;
7837 }
7838
7839 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7840 break;
7841 }
7842 }
7843
7844 else /* Should not reach this; colormap already exists and
7845 must be <= 256 */
7846 {
7847 if (logging != MagickFalse)
7848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7849 " Quantizing the colormap to 4-4-4");
7850 for (i=0; i<image_colors; i++)
7851 {
glennrp3faa9a32011-04-23 14:00:25 +00007852 image->colormap[i].red=ScaleCharToQuantum(
7853 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7854 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7855 image->colormap[i].green=ScaleCharToQuantum(
7856 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7857 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7858 image->colormap[i].blue=ScaleCharToQuantum(
7859 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7860 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
glennrpd3371642011-03-22 19:42:23 +00007861 }
7862 }
7863 continue;
7864 }
7865
glennrp82b3c532011-03-22 19:20:54 +00007866 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7867 {
7868 if (logging != MagickFalse)
7869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7870 " Quantizing the background color to 3-3-3");
7871
7872 tried_333 = MagickTrue;
7873
7874 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007875 ScaleCharToQuantum(
7876 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7877 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7878 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007879 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007880 ScaleCharToQuantum(
7881 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7882 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7883 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007884 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007885 ScaleCharToQuantum(
7886 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7887 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7888 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007889
7890 if (logging != MagickFalse)
7891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007892 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007893
7894 if (image->colormap == NULL)
7895 {
7896 for (y=0; y < (ssize_t) image->rows; y++)
7897 {
7898 r=GetAuthenticPixels(image,0,y,image->columns,1,
7899 exception);
7900
7901 if (r == (PixelPacket *) NULL)
7902 break;
7903
7904 for (x=0; x < (ssize_t) image->columns; x++)
7905 {
glennrp4737d522011-04-29 03:33:42 +00007906 if (GetOpacityPixelComponent(r) == TransparentOpacity)
glennrp82b3c532011-03-22 19:20:54 +00007907 {
glennrp4737d522011-04-29 03:33:42 +00007908 SetRGBPixelComponents(r,image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00007909 }
7910 else
7911 {
glennrp4737d522011-04-29 03:33:42 +00007912 SetRedPixelComponent(r,ScaleCharToQuantum(
glennrp19180202011-04-29 14:05:58 +00007913 (ScaleQuantumToChar(GetRedPixelComponent(r)) & 0xe0) |
7914 (ScaleQuantumToChar(GetRedPixelComponent(r)) & 0xe0) >> 3 |
7915 (ScaleQuantumToChar(GetRedPixelComponent(r)) & 0xc0) >>
7916 6));
glennrp4737d522011-04-29 03:33:42 +00007917 SetGreenPixelComponent(r,ScaleCharToQuantum(
glennrp19180202011-04-29 14:05:58 +00007918 (ScaleQuantumToChar(GetGreenPixelComponent(r)) & 0xe0) |
7919 (ScaleQuantumToChar(GetGreenPixelComponent(r)) & 0xe0) >>
7920 3 |
7921 (ScaleQuantumToChar(GetGreenPixelComponent(r)) & 0xc0) >>
7922 6));
glennrp4737d522011-04-29 03:33:42 +00007923 SetBluePixelComponent(r,ScaleCharToQuantum(
glennrp19180202011-04-29 14:05:58 +00007924 (ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xe0) |
7925 (ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xe0) >> 3 |
7926 (ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xc0) >>
7927 6));
glennrp82b3c532011-03-22 19:20:54 +00007928 }
7929 r++;
7930 }
7931
7932 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7933 break;
7934 }
7935 }
7936
7937 else /* Should not reach this; colormap already exists and
7938 must be <= 256 */
7939 {
7940 if (logging != MagickFalse)
7941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007942 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007943 for (i=0; i<image_colors; i++)
7944 {
glennrp3faa9a32011-04-23 14:00:25 +00007945 image->colormap[i].red=ScaleCharToQuantum(
7946 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7947 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7948 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7949 image->colormap[i].green=ScaleCharToQuantum(
7950 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7951 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7952 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7953 image->colormap[i].blue=ScaleCharToQuantum(
7954 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7955 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7956 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007957 }
glennrpd3371642011-03-22 19:42:23 +00007958 }
7959 continue;
glennrp82b3c532011-03-22 19:20:54 +00007960 }
glennrpc8c2f062011-02-25 19:00:33 +00007961
glennrpc8c2f062011-02-25 19:00:33 +00007962 if (image_colors == 0 || image_colors > 256)
7963 {
7964 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007966 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007967
glennrp3faa9a32011-04-23 14:00:25 +00007968 /* Red and green were already done so we only quantize the blue
7969 * channel
7970 */
7971
7972 image->background_color.blue=ScaleCharToQuantum(
7973 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7974 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7975 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7976 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrpfd05d622011-02-25 04:10:33 +00007977
glennrpc8c2f062011-02-25 19:00:33 +00007978 if (logging != MagickFalse)
7979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007980 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007981
glennrpc8c2f062011-02-25 19:00:33 +00007982 if (image->colormap == NULL)
7983 {
7984 for (y=0; y < (ssize_t) image->rows; y++)
7985 {
7986 r=GetAuthenticPixels(image,0,y,image->columns,1,
7987 exception);
7988
7989 if (r == (PixelPacket *) NULL)
7990 break;
7991
7992 for (x=0; x < (ssize_t) image->columns; x++)
7993 {
glennrp4737d522011-04-29 03:33:42 +00007994 if (GetOpacityPixelComponent(r) == TransparentOpacity)
glennrp82b3c532011-03-22 19:20:54 +00007995 {
glennrpb5d5e6d2011-04-27 17:08:19 +00007996 SetRGBPixelComponents(r,image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00007997 }
7998 else
7999 {
glennrp4737d522011-04-29 03:33:42 +00008000 SetBluePixelComponent(r,ScaleCharToQuantum(
glennrp19180202011-04-29 14:05:58 +00008001 (ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xc0) |
8002 (ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xc0) >> 2 |
8003 (ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xc0) >> 4 |
8004 (ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xc0) >>
8005 6));
glennrp82b3c532011-03-22 19:20:54 +00008006 }
glennrp52a479c2011-02-26 21:14:38 +00008007 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008008 }
glennrpfd05d622011-02-25 04:10:33 +00008009
glennrpc8c2f062011-02-25 19:00:33 +00008010 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8011 break;
8012 }
8013 }
glennrpfd05d622011-02-25 04:10:33 +00008014
glennrpc8c2f062011-02-25 19:00:33 +00008015 else /* Should not reach this; colormap already exists and
8016 must be <= 256 */
8017 {
8018 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008020 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008021 for (i=0; i<image_colors; i++)
8022 {
glennrp3faa9a32011-04-23 14:00:25 +00008023 image->colormap[i].blue=ScaleCharToQuantum(
8024 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
8025 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
8026 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
8027 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrpc8c2f062011-02-25 19:00:33 +00008028 }
8029 }
8030 continue;
8031 }
8032 break;
glennrpd71e86a2011-02-24 01:28:37 +00008033 }
glennrpfd05d622011-02-25 04:10:33 +00008034 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008035
glennrpfd05d622011-02-25 04:10:33 +00008036 /* If we are excluding the tRNS chunk and there is transparency,
8037 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8038 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008039 */
glennrp0e8ea192010-12-24 18:00:33 +00008040 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8041 (number_transparent != 0 || number_semitransparent != 0))
8042 {
glennrpd17915c2011-04-29 14:24:22 +00008043 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008044
8045 if (ping_have_color == MagickFalse)
8046 mng_info->write_png_colortype = 5;
8047
8048 else
8049 mng_info->write_png_colortype = 7;
8050
glennrp8d579662011-02-23 02:05:02 +00008051 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008052 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008053 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008054
glennrp0e8ea192010-12-24 18:00:33 +00008055 }
8056
glennrpfd05d622011-02-25 04:10:33 +00008057 /* See if cheap transparency is possible. It is only possible
8058 * when there is a single transparent color, no semitransparent
8059 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008060 * as the transparent color. We only need this information if
8061 * we are writing a PNG with colortype 0 or 2, and we have not
8062 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008063 */
glennrp5a39f372011-02-25 04:52:16 +00008064 if (number_transparent == 1 &&
8065 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008066 {
8067 ping_have_cheap_transparency = MagickTrue;
8068
8069 if (number_semitransparent != 0)
8070 ping_have_cheap_transparency = MagickFalse;
8071
8072 else if (image_colors == 0 || image_colors > 256 ||
8073 image->colormap == NULL)
8074 {
8075 ExceptionInfo
8076 *exception;
8077
8078 register const PixelPacket
8079 *q;
8080
8081 exception=(&image->exception);
8082
8083 for (y=0; y < (ssize_t) image->rows; y++)
8084 {
8085 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8086
8087 if (q == (PixelPacket *) NULL)
8088 break;
8089
8090 for (x=0; x < (ssize_t) image->columns; x++)
8091 {
8092 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008093 (unsigned short) GetRedPixelComponent(q) ==
8094 ping_trans_color.red &&
8095 (unsigned short) GetGreenPixelComponent(q) ==
8096 ping_trans_color.green &&
8097 (unsigned short) GetBluePixelComponent(q) ==
8098 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008099 {
8100 ping_have_cheap_transparency = MagickFalse;
8101 break;
8102 }
8103
8104 q++;
8105 }
8106
8107 if (ping_have_cheap_transparency == MagickFalse)
8108 break;
8109 }
8110 }
8111 else
8112 {
glennrp67b9c1a2011-04-22 18:47:36 +00008113 /* Assuming that image->colormap[0] is the one transparent color
8114 * and that all others are opaque.
8115 */
glennrpfd05d622011-02-25 04:10:33 +00008116 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008117 for (i=1; i<image_colors; i++)
8118 if (image->colormap[i].red == image->colormap[0].red &&
8119 image->colormap[i].green == image->colormap[0].green &&
8120 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008121 {
glennrp67b9c1a2011-04-22 18:47:36 +00008122 ping_have_cheap_transparency = MagickFalse;
8123 break;
glennrpfd05d622011-02-25 04:10:33 +00008124 }
8125 }
8126
8127 if (logging != MagickFalse)
8128 {
8129 if (ping_have_cheap_transparency == MagickFalse)
8130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8131 " Cheap transparency is not possible.");
8132
8133 else
8134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8135 " Cheap transparency is possible.");
8136 }
8137 }
8138 else
8139 ping_have_cheap_transparency = MagickFalse;
8140
glennrp8640fb52010-11-23 15:48:26 +00008141 image_depth=image->depth;
8142
glennrp26c990a2010-11-23 02:23:20 +00008143 quantum_info = (QuantumInfo *) NULL;
8144 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008145 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008146 image_matte=image->matte;
8147
glennrp0fe50b42010-11-16 03:52:51 +00008148 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008149 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008150
glennrp52a479c2011-02-26 21:14:38 +00008151 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8152 (image->colors == 0 || image->colormap == NULL))
8153 {
glennrp52a479c2011-02-26 21:14:38 +00008154 image_info=DestroyImageInfo(image_info);
8155 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008156 (void) ThrowMagickException(&IMimage->exception,
8157 GetMagickModule(),CoderError,
8158 "Cannot write PNG8 or color-type 3; colormap is NULL",
8159 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008160#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8161 UnlockSemaphoreInfo(ping_semaphore);
8162#endif
8163 return(MagickFalse);
8164 }
8165
cristy3ed852e2009-09-05 21:47:34 +00008166 /*
8167 Allocate the PNG structures
8168 */
8169#ifdef PNG_USER_MEM_SUPPORTED
8170 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008171 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8172 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008173
cristy3ed852e2009-09-05 21:47:34 +00008174#else
8175 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008176 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008177
cristy3ed852e2009-09-05 21:47:34 +00008178#endif
8179 if (ping == (png_struct *) NULL)
8180 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008181
cristy3ed852e2009-09-05 21:47:34 +00008182 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008183
cristy3ed852e2009-09-05 21:47:34 +00008184 if (ping_info == (png_info *) NULL)
8185 {
8186 png_destroy_write_struct(&ping,(png_info **) NULL);
8187 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8188 }
glennrp0fe50b42010-11-16 03:52:51 +00008189
cristy3ed852e2009-09-05 21:47:34 +00008190 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008191 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008192
glennrp5af765f2010-03-30 11:12:18 +00008193 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008194 {
8195 /*
8196 PNG write failed.
8197 */
8198#ifdef PNG_DEBUG
8199 if (image_info->verbose)
8200 (void) printf("PNG write has failed.\n");
8201#endif
8202 png_destroy_write_struct(&ping,&ping_info);
8203#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008204 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008205#endif
glennrpda8f3a72011-02-27 23:54:12 +00008206 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008207 (void) CloseBlob(image);
8208 image_info=DestroyImageInfo(image_info);
8209 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008210 return(MagickFalse);
8211 }
8212 /*
8213 Prepare PNG for writing.
8214 */
8215#if defined(PNG_MNG_FEATURES_SUPPORTED)
8216 if (mng_info->write_mng)
8217 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008218
cristy3ed852e2009-09-05 21:47:34 +00008219#else
8220# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8221 if (mng_info->write_mng)
8222 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008223
cristy3ed852e2009-09-05 21:47:34 +00008224# endif
8225#endif
glennrp2b013e42010-11-24 16:55:50 +00008226
cristy3ed852e2009-09-05 21:47:34 +00008227 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008228
cristy4e5bc842010-06-09 13:56:01 +00008229 ping_width=(png_uint_32) image->columns;
8230 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008231
cristy3ed852e2009-09-05 21:47:34 +00008232 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8233 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008234
cristy3ed852e2009-09-05 21:47:34 +00008235 if (mng_info->write_png_depth != 0)
8236 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008237
cristy3ed852e2009-09-05 21:47:34 +00008238 /* Adjust requested depth to next higher valid depth if necessary */
8239 if (image_depth > 8)
8240 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008241
cristy3ed852e2009-09-05 21:47:34 +00008242 if ((image_depth > 4) && (image_depth < 8))
8243 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008244
cristy3ed852e2009-09-05 21:47:34 +00008245 if (image_depth == 3)
8246 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008247
cristy3ed852e2009-09-05 21:47:34 +00008248 if (logging != MagickFalse)
8249 {
8250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008251 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008253 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008255 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008257 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008259 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008260 }
glennrp8640fb52010-11-23 15:48:26 +00008261
cristy3ed852e2009-09-05 21:47:34 +00008262 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008263 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008264
glennrp26f37912010-12-23 16:22:42 +00008265
cristy3ed852e2009-09-05 21:47:34 +00008266#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008267 if (ping_exclude_pHYs == MagickFalse)
8268 {
cristy3ed852e2009-09-05 21:47:34 +00008269 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8270 (!mng_info->write_mng || !mng_info->equal_physs))
8271 {
glennrp0fe50b42010-11-16 03:52:51 +00008272 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8274 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008275
8276 if (image->units == PixelsPerInchResolution)
8277 {
glennrpdfd70802010-11-14 01:23:35 +00008278 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008279 ping_pHYs_x_resolution=
8280 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8281 ping_pHYs_y_resolution=
8282 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008283 }
glennrpdfd70802010-11-14 01:23:35 +00008284
cristy3ed852e2009-09-05 21:47:34 +00008285 else if (image->units == PixelsPerCentimeterResolution)
8286 {
glennrpdfd70802010-11-14 01:23:35 +00008287 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008288 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8289 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008290 }
glennrp991d11d2010-11-12 21:55:28 +00008291
cristy3ed852e2009-09-05 21:47:34 +00008292 else
8293 {
glennrpdfd70802010-11-14 01:23:35 +00008294 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8295 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8296 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008297 }
glennrp991d11d2010-11-12 21:55:28 +00008298
glennrp823b55c2011-03-14 18:46:46 +00008299 if (logging != MagickFalse)
8300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8301 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8302 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8303 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008304 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008305 }
glennrp26f37912010-12-23 16:22:42 +00008306 }
cristy3ed852e2009-09-05 21:47:34 +00008307#endif
glennrpa521b2f2010-10-29 04:11:03 +00008308
glennrp26f37912010-12-23 16:22:42 +00008309 if (ping_exclude_bKGD == MagickFalse)
8310 {
glennrpa521b2f2010-10-29 04:11:03 +00008311 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008312 {
glennrpa521b2f2010-10-29 04:11:03 +00008313 unsigned int
8314 mask;
cristy3ed852e2009-09-05 21:47:34 +00008315
glennrpa521b2f2010-10-29 04:11:03 +00008316 mask=0xffff;
8317 if (ping_bit_depth == 8)
8318 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008319
glennrpa521b2f2010-10-29 04:11:03 +00008320 if (ping_bit_depth == 4)
8321 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008322
glennrpa521b2f2010-10-29 04:11:03 +00008323 if (ping_bit_depth == 2)
8324 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008325
glennrpa521b2f2010-10-29 04:11:03 +00008326 if (ping_bit_depth == 1)
8327 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008328
glennrpa521b2f2010-10-29 04:11:03 +00008329 ping_background.red=(png_uint_16)
8330 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008331
glennrpa521b2f2010-10-29 04:11:03 +00008332 ping_background.green=(png_uint_16)
8333 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008334
glennrpa521b2f2010-10-29 04:11:03 +00008335 ping_background.blue=(png_uint_16)
8336 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008337
8338 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008339 }
cristy3ed852e2009-09-05 21:47:34 +00008340
glennrp0fe50b42010-11-16 03:52:51 +00008341 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008342 {
8343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8344 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8346 " background_color index is %d",
8347 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008348
8349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8350 " ping_bit_depth=%d",ping_bit_depth);
8351 }
glennrp0fe50b42010-11-16 03:52:51 +00008352
8353 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008354 }
glennrp0fe50b42010-11-16 03:52:51 +00008355
cristy3ed852e2009-09-05 21:47:34 +00008356 /*
8357 Select the color type.
8358 */
8359 matte=image_matte;
8360 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008361
glennrp1273f7b2011-02-24 03:20:30 +00008362 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008363 {
glennrp0fe50b42010-11-16 03:52:51 +00008364
glennrpfd05d622011-02-25 04:10:33 +00008365 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008366 for reducing the sample depth from 8. */
8367
glennrp0fe50b42010-11-16 03:52:51 +00008368 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008369
glennrp8bb3a022010-12-13 20:40:04 +00008370 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008371
8372 /*
8373 Set image palette.
8374 */
8375 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8376
glennrp0fe50b42010-11-16 03:52:51 +00008377 if (logging != MagickFalse)
8378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8379 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008380 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008381
8382 for (i=0; i < (ssize_t) number_colors; i++)
8383 {
8384 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8385 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8386 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8387 if (logging != MagickFalse)
8388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008389#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008390 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008391#else
8392 " %5ld (%5d,%5d,%5d)",
8393#endif
glennrp0fe50b42010-11-16 03:52:51 +00008394 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8395
8396 }
glennrp2b013e42010-11-24 16:55:50 +00008397
glennrp8bb3a022010-12-13 20:40:04 +00008398 ping_have_PLTE=MagickTrue;
8399 image_depth=ping_bit_depth;
8400 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008401
glennrp58e01762011-01-07 15:28:54 +00008402 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008403 {
glennrp0fe50b42010-11-16 03:52:51 +00008404 /*
8405 Identify which colormap entry is transparent.
8406 */
8407 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008408 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008409
glennrp8bb3a022010-12-13 20:40:04 +00008410 for (i=0; i < (ssize_t) number_transparent; i++)
8411 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008412
glennrp0fe50b42010-11-16 03:52:51 +00008413
glennrp2cc891a2010-12-24 13:44:32 +00008414 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008415 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008416
8417 if (ping_num_trans == 0)
8418 ping_have_tRNS=MagickFalse;
8419
glennrp8bb3a022010-12-13 20:40:04 +00008420 else
8421 ping_have_tRNS=MagickTrue;
8422 }
glennrp0fe50b42010-11-16 03:52:51 +00008423
glennrp1273f7b2011-02-24 03:20:30 +00008424 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008425 {
glennrp1273f7b2011-02-24 03:20:30 +00008426 /*
8427 * Identify which colormap entry is the background color.
8428 */
8429
glennrp4f25bd02011-01-01 18:51:28 +00008430 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8431 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8432 break;
glennrp0fe50b42010-11-16 03:52:51 +00008433
glennrp4f25bd02011-01-01 18:51:28 +00008434 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00008435
8436 if (logging != MagickFalse)
8437 {
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " background_color index is %d",
8440 (int) ping_background.index);
8441 }
glennrp4f25bd02011-01-01 18:51:28 +00008442 }
cristy3ed852e2009-09-05 21:47:34 +00008443 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008444
cristy3ed852e2009-09-05 21:47:34 +00008445 else if (mng_info->write_png24)
8446 {
8447 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008448 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008449 }
glennrp0fe50b42010-11-16 03:52:51 +00008450
cristy3ed852e2009-09-05 21:47:34 +00008451 else if (mng_info->write_png32)
8452 {
8453 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008454 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008455 }
glennrp0fe50b42010-11-16 03:52:51 +00008456
glennrp8bb3a022010-12-13 20:40:04 +00008457 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008458 {
glennrp5af765f2010-03-30 11:12:18 +00008459 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008460
glennrp8bb3a022010-12-13 20:40:04 +00008461 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008462 {
glennrp5af765f2010-03-30 11:12:18 +00008463 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008464
glennrp5af765f2010-03-30 11:12:18 +00008465 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8466 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008467 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008468
glennrp8bb3a022010-12-13 20:40:04 +00008469 else
8470 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008471
8472 if (logging != MagickFalse)
8473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8474 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008475 }
glennrp0fe50b42010-11-16 03:52:51 +00008476
glennrp7c4c9e62011-03-21 20:23:32 +00008477 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008478 {
8479 if (logging != MagickFalse)
8480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008481 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008482
glennrpd6bf1612010-12-17 17:28:54 +00008483 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008484 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008485
glennrpd6bf1612010-12-17 17:28:54 +00008486 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008487 {
glennrp5af765f2010-03-30 11:12:18 +00008488 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008489 image_matte=MagickFalse;
8490 }
glennrp0fe50b42010-11-16 03:52:51 +00008491
glennrpd6bf1612010-12-17 17:28:54 +00008492 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008493 {
glennrp5af765f2010-03-30 11:12:18 +00008494 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008495 image_matte=MagickTrue;
8496 }
glennrp0fe50b42010-11-16 03:52:51 +00008497
glennrp5aa37f62011-01-02 03:07:57 +00008498 if (image_info->type == PaletteType ||
8499 image_info->type == PaletteMatteType)
8500 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8501
glennrp7c4c9e62011-03-21 20:23:32 +00008502 if (mng_info->write_png_colortype == 0 &&
8503 (image_info->type == UndefinedType ||
8504 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008505 {
glennrp5aa37f62011-01-02 03:07:57 +00008506 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008507 {
glennrp5aa37f62011-01-02 03:07:57 +00008508 if (image_matte == MagickFalse)
8509 {
8510 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8511 image_matte=MagickFalse;
8512 }
glennrp0fe50b42010-11-16 03:52:51 +00008513
glennrp0b206f52011-01-07 04:55:32 +00008514 else
glennrp5aa37f62011-01-02 03:07:57 +00008515 {
8516 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8517 image_matte=MagickTrue;
8518 }
8519 }
8520 else
glennrp8bb3a022010-12-13 20:40:04 +00008521 {
glennrp5aa37f62011-01-02 03:07:57 +00008522 if (image_matte == MagickFalse)
8523 {
8524 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8525 image_matte=MagickFalse;
8526 }
glennrp8bb3a022010-12-13 20:40:04 +00008527
glennrp0b206f52011-01-07 04:55:32 +00008528 else
glennrp5aa37f62011-01-02 03:07:57 +00008529 {
8530 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8531 image_matte=MagickTrue;
8532 }
8533 }
glennrp0fe50b42010-11-16 03:52:51 +00008534 }
glennrp5aa37f62011-01-02 03:07:57 +00008535
cristy3ed852e2009-09-05 21:47:34 +00008536 }
glennrp0fe50b42010-11-16 03:52:51 +00008537
cristy3ed852e2009-09-05 21:47:34 +00008538 if (logging != MagickFalse)
8539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008540 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008541
glennrp5af765f2010-03-30 11:12:18 +00008542 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008543 {
8544 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8545 ping_color_type == PNG_COLOR_TYPE_RGB ||
8546 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8547 ping_bit_depth=8;
8548 }
cristy3ed852e2009-09-05 21:47:34 +00008549
glennrpd6bf1612010-12-17 17:28:54 +00008550 old_bit_depth=ping_bit_depth;
8551
glennrp5af765f2010-03-30 11:12:18 +00008552 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008553 {
glennrp8d579662011-02-23 02:05:02 +00008554 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8555 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008556 }
glennrp8640fb52010-11-23 15:48:26 +00008557
glennrp5af765f2010-03-30 11:12:18 +00008558 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008559 {
cristy35ef8242010-06-03 16:24:13 +00008560 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008561 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008562
8563 if (image->colors == 0)
8564 {
glennrp0fe50b42010-11-16 03:52:51 +00008565 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008566 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008567 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008568 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008569 }
8570
cristy35ef8242010-06-03 16:24:13 +00008571 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008572 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008573 }
glennrp2b013e42010-11-24 16:55:50 +00008574
glennrpd6bf1612010-12-17 17:28:54 +00008575 if (logging != MagickFalse)
8576 {
8577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8578 " Number of colors: %.20g",(double) image_colors);
8579
8580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8581 " Tentative PNG bit depth: %d",ping_bit_depth);
8582 }
8583
8584 if (ping_bit_depth < (int) mng_info->write_png_depth)
8585 ping_bit_depth = mng_info->write_png_depth;
8586 }
glennrp2cc891a2010-12-24 13:44:32 +00008587
glennrp5af765f2010-03-30 11:12:18 +00008588 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008589
cristy3ed852e2009-09-05 21:47:34 +00008590 if (logging != MagickFalse)
8591 {
8592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008593 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008594
cristy3ed852e2009-09-05 21:47:34 +00008595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008596 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008597
cristy3ed852e2009-09-05 21:47:34 +00008598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008599 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008600
cristy3ed852e2009-09-05 21:47:34 +00008601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008602
glennrp8640fb52010-11-23 15:48:26 +00008603 " image->depth: %.20g",(double) image->depth);
8604
8605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008606 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008607 }
8608
glennrp58e01762011-01-07 15:28:54 +00008609 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008610 {
glennrp4f25bd02011-01-01 18:51:28 +00008611 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008612 {
glennrp7c4c9e62011-03-21 20:23:32 +00008613 if (mng_info->write_png_colortype == 0)
8614 {
8615 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008616
glennrp7c4c9e62011-03-21 20:23:32 +00008617 if (ping_have_color != MagickFalse)
8618 ping_color_type=PNG_COLOR_TYPE_RGBA;
8619 }
glennrp4f25bd02011-01-01 18:51:28 +00008620
8621 /*
8622 * Determine if there is any transparent color.
8623 */
8624 if (number_transparent + number_semitransparent == 0)
8625 {
8626 /*
8627 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8628 */
glennrpa6a06632011-01-19 15:15:34 +00008629
glennrp4f25bd02011-01-01 18:51:28 +00008630 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008631
8632 if (mng_info->write_png_colortype == 0)
8633 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008634 }
8635
8636 else
8637 {
8638 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008639 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008640
8641 mask=0xffff;
8642
8643 if (ping_bit_depth == 8)
8644 mask=0x00ff;
8645
8646 if (ping_bit_depth == 4)
8647 mask=0x000f;
8648
8649 if (ping_bit_depth == 2)
8650 mask=0x0003;
8651
8652 if (ping_bit_depth == 1)
8653 mask=0x0001;
8654
8655 ping_trans_color.red=(png_uint_16)
8656 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8657
8658 ping_trans_color.green=(png_uint_16)
8659 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8660
8661 ping_trans_color.blue=(png_uint_16)
8662 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8663
8664 ping_trans_color.gray=(png_uint_16)
8665 (ScaleQuantumToShort(PixelIntensityToQuantum(
8666 image->colormap)) & mask);
8667
8668 ping_trans_color.index=(png_byte) 0;
8669
8670 ping_have_tRNS=MagickTrue;
8671 }
8672
8673 if (ping_have_tRNS != MagickFalse)
8674 {
8675 /*
glennrpfd05d622011-02-25 04:10:33 +00008676 * Determine if there is one and only one transparent color
8677 * and if so if it is fully transparent.
8678 */
8679 if (ping_have_cheap_transparency == MagickFalse)
8680 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008681 }
8682
8683 if (ping_have_tRNS != MagickFalse)
8684 {
glennrp7c4c9e62011-03-21 20:23:32 +00008685 if (mng_info->write_png_colortype == 0)
8686 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008687
8688 if (image_depth == 8)
8689 {
8690 ping_trans_color.red&=0xff;
8691 ping_trans_color.green&=0xff;
8692 ping_trans_color.blue&=0xff;
8693 ping_trans_color.gray&=0xff;
8694 }
8695 }
8696 }
cristy3ed852e2009-09-05 21:47:34 +00008697 else
8698 {
cristy3ed852e2009-09-05 21:47:34 +00008699 if (image_depth == 8)
8700 {
glennrp5af765f2010-03-30 11:12:18 +00008701 ping_trans_color.red&=0xff;
8702 ping_trans_color.green&=0xff;
8703 ping_trans_color.blue&=0xff;
8704 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008705 }
8706 }
8707 }
glennrp8640fb52010-11-23 15:48:26 +00008708
cristy3ed852e2009-09-05 21:47:34 +00008709 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008710
glennrp2e09f552010-11-14 00:38:48 +00008711 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008712 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008713
glennrp39992b42010-11-14 00:03:43 +00008714 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008715 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008716 ping_have_color == MagickFalse &&
8717 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008718 {
cristy35ef8242010-06-03 16:24:13 +00008719 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008720
cristy3ed852e2009-09-05 21:47:34 +00008721 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008722 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008723
glennrp7c4c9e62011-03-21 20:23:32 +00008724 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008725 {
glennrp5af765f2010-03-30 11:12:18 +00008726 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008727
cristy3ed852e2009-09-05 21:47:34 +00008728 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008729 {
8730 if (logging != MagickFalse)
8731 {
8732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8733 " Scaling ping_trans_color (0)");
8734 }
8735 ping_trans_color.gray*=0x0101;
8736 }
cristy3ed852e2009-09-05 21:47:34 +00008737 }
glennrp0fe50b42010-11-16 03:52:51 +00008738
cristy3ed852e2009-09-05 21:47:34 +00008739 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8740 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008741
glennrp136ee3a2011-04-27 15:47:45 +00008742 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00008743 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008744 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008745
cristy3ed852e2009-09-05 21:47:34 +00008746 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008747 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008748
cristy3ed852e2009-09-05 21:47:34 +00008749 else
8750 {
glennrp5af765f2010-03-30 11:12:18 +00008751 ping_bit_depth=8;
8752 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008753 {
8754 if(!mng_info->write_png_depth)
8755 {
glennrp5af765f2010-03-30 11:12:18 +00008756 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008757
cristy35ef8242010-06-03 16:24:13 +00008758 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008759 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008760 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008761 }
8762 }
glennrp2b013e42010-11-24 16:55:50 +00008763
glennrp0fe50b42010-11-16 03:52:51 +00008764 else if (ping_color_type ==
8765 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008766 mng_info->IsPalette)
8767 {
cristy3ed852e2009-09-05 21:47:34 +00008768 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008769
cristy3ed852e2009-09-05 21:47:34 +00008770 int
8771 depth_4_ok=MagickTrue,
8772 depth_2_ok=MagickTrue,
8773 depth_1_ok=MagickTrue;
8774
cristybb503372010-05-27 20:51:26 +00008775 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008776 {
8777 unsigned char
8778 intensity;
8779
8780 intensity=ScaleQuantumToChar(image->colormap[i].red);
8781
8782 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8783 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8784 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8785 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008786 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008787 depth_1_ok=MagickFalse;
8788 }
glennrp2b013e42010-11-24 16:55:50 +00008789
cristy3ed852e2009-09-05 21:47:34 +00008790 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008791 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008792
cristy3ed852e2009-09-05 21:47:34 +00008793 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008794 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008795
cristy3ed852e2009-09-05 21:47:34 +00008796 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008797 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008798 }
8799 }
glennrp2b013e42010-11-24 16:55:50 +00008800
glennrp5af765f2010-03-30 11:12:18 +00008801 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008802 }
glennrp0fe50b42010-11-16 03:52:51 +00008803
cristy3ed852e2009-09-05 21:47:34 +00008804 else
glennrp0fe50b42010-11-16 03:52:51 +00008805
cristy3ed852e2009-09-05 21:47:34 +00008806 if (mng_info->IsPalette)
8807 {
glennrp17a14852010-05-10 03:01:59 +00008808 number_colors=image_colors;
8809
cristy3ed852e2009-09-05 21:47:34 +00008810 if (image_depth <= 8)
8811 {
cristy3ed852e2009-09-05 21:47:34 +00008812 /*
8813 Set image palette.
8814 */
glennrp5af765f2010-03-30 11:12:18 +00008815 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008816
glennrp58e01762011-01-07 15:28:54 +00008817 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008818 {
glennrp9c1eb072010-06-06 22:19:15 +00008819 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008820
glennrp3b51f0e2010-11-27 18:14:08 +00008821 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8823 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008824 }
glennrp0fe50b42010-11-16 03:52:51 +00008825
cristy3ed852e2009-09-05 21:47:34 +00008826 else
8827 {
cristybb503372010-05-27 20:51:26 +00008828 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008829 {
8830 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8831 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8832 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8833 }
glennrp0fe50b42010-11-16 03:52:51 +00008834
glennrp3b51f0e2010-11-27 18:14:08 +00008835 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008837 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008838 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008839
glennrp39992b42010-11-14 00:03:43 +00008840 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008841 }
glennrp0fe50b42010-11-16 03:52:51 +00008842
cristy3ed852e2009-09-05 21:47:34 +00008843 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008844 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008845 {
cristybefe4d22010-06-07 01:18:58 +00008846 size_t
8847 one;
8848
glennrp5af765f2010-03-30 11:12:18 +00008849 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008850 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008851
glennrpd17915c2011-04-29 14:24:22 +00008852 while ((one << ping_bit_depth) < (ssize_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008853 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008854 }
glennrp0fe50b42010-11-16 03:52:51 +00008855
glennrp5af765f2010-03-30 11:12:18 +00008856 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008857
glennrp58e01762011-01-07 15:28:54 +00008858 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008859 {
glennrp0fe50b42010-11-16 03:52:51 +00008860 /*
glennrpd6bf1612010-12-17 17:28:54 +00008861 * Set up trans_colors array.
8862 */
glennrp0fe50b42010-11-16 03:52:51 +00008863 assert(number_colors <= 256);
8864
glennrpd6bf1612010-12-17 17:28:54 +00008865 ping_num_trans=(unsigned short) (number_transparent +
8866 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008867
8868 if (ping_num_trans == 0)
8869 ping_have_tRNS=MagickFalse;
8870
glennrpd6bf1612010-12-17 17:28:54 +00008871 else
glennrp0fe50b42010-11-16 03:52:51 +00008872 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008873 if (logging != MagickFalse)
8874 {
8875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8876 " Scaling ping_trans_color (1)");
8877 }
glennrpd6bf1612010-12-17 17:28:54 +00008878 ping_have_tRNS=MagickTrue;
8879
8880 for (i=0; i < ping_num_trans; i++)
8881 {
8882 ping_trans_alpha[i]= (png_byte) (255-
8883 ScaleQuantumToChar(image->colormap[i].opacity));
8884 }
glennrp0fe50b42010-11-16 03:52:51 +00008885 }
8886 }
cristy3ed852e2009-09-05 21:47:34 +00008887 }
8888 }
glennrp0fe50b42010-11-16 03:52:51 +00008889
cristy3ed852e2009-09-05 21:47:34 +00008890 else
8891 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008892
cristy3ed852e2009-09-05 21:47:34 +00008893 if (image_depth < 8)
8894 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008895
cristy3ed852e2009-09-05 21:47:34 +00008896 if ((save_image_depth == 16) && (image_depth == 8))
8897 {
glennrp4f25bd02011-01-01 18:51:28 +00008898 if (logging != MagickFalse)
8899 {
8900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8901 " Scaling ping_trans_color from (%d,%d,%d)",
8902 (int) ping_trans_color.red,
8903 (int) ping_trans_color.green,
8904 (int) ping_trans_color.blue);
8905 }
8906
glennrp5af765f2010-03-30 11:12:18 +00008907 ping_trans_color.red*=0x0101;
8908 ping_trans_color.green*=0x0101;
8909 ping_trans_color.blue*=0x0101;
8910 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008911
8912 if (logging != MagickFalse)
8913 {
8914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8915 " to (%d,%d,%d)",
8916 (int) ping_trans_color.red,
8917 (int) ping_trans_color.green,
8918 (int) ping_trans_color.blue);
8919 }
cristy3ed852e2009-09-05 21:47:34 +00008920 }
8921 }
8922
cristy4383ec82011-01-05 15:42:32 +00008923 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8924 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008925
cristy3ed852e2009-09-05 21:47:34 +00008926 /*
8927 Adjust background and transparency samples in sub-8-bit grayscale files.
8928 */
glennrp5af765f2010-03-30 11:12:18 +00008929 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008930 PNG_COLOR_TYPE_GRAY)
8931 {
8932 png_uint_16
8933 maxval;
8934
cristy35ef8242010-06-03 16:24:13 +00008935 size_t
8936 one=1;
8937
cristy22ffd972010-06-03 16:51:47 +00008938 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008939
glennrp4f25bd02011-01-01 18:51:28 +00008940 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008941 {
cristy3ed852e2009-09-05 21:47:34 +00008942
glennrpa521b2f2010-10-29 04:11:03 +00008943 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008944 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8945
8946 if (logging != MagickFalse)
8947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008948 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00008949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8950 " background_color index is %d",
8951 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008952
glennrp991d11d2010-11-12 21:55:28 +00008953 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008954 }
cristy3ed852e2009-09-05 21:47:34 +00008955
glennrp5af765f2010-03-30 11:12:18 +00008956 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8957 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008958 }
glennrp17a14852010-05-10 03:01:59 +00008959
glennrp26f37912010-12-23 16:22:42 +00008960 if (ping_exclude_bKGD == MagickFalse)
8961 {
glennrp1273f7b2011-02-24 03:20:30 +00008962 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008963 {
8964 /*
8965 Identify which colormap entry is the background color.
8966 */
8967
glennrp17a14852010-05-10 03:01:59 +00008968 number_colors=image_colors;
8969
glennrpa521b2f2010-10-29 04:11:03 +00008970 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8971 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008972 break;
8973
8974 ping_background.index=(png_byte) i;
8975
glennrp3b51f0e2010-11-27 18:14:08 +00008976 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008977 {
8978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008979 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008980 }
glennrp0fe50b42010-11-16 03:52:51 +00008981
cristy13d07042010-11-21 20:56:18 +00008982 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008983 {
8984 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008985
8986 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008987 {
8988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8989 " background =(%d,%d,%d)",
8990 (int) ping_background.red,
8991 (int) ping_background.green,
8992 (int) ping_background.blue);
8993 }
8994 }
glennrpa521b2f2010-10-29 04:11:03 +00008995
glennrpd6bf1612010-12-17 17:28:54 +00008996 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008997 {
glennrp3b51f0e2010-11-27 18:14:08 +00008998 if (logging != MagickFalse)
8999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9000 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009001 ping_have_bKGD = MagickFalse;
9002 }
glennrp17a14852010-05-10 03:01:59 +00009003 }
glennrp26f37912010-12-23 16:22:42 +00009004 }
glennrp17a14852010-05-10 03:01:59 +00009005
cristy3ed852e2009-09-05 21:47:34 +00009006 if (logging != MagickFalse)
9007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009008 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009009 /*
9010 Initialize compression level and filtering.
9011 */
9012 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009013 {
9014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9015 " Setting up deflate compression");
9016
9017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9018 " Compression buffer size: 32768");
9019 }
9020
cristy3ed852e2009-09-05 21:47:34 +00009021 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009022
cristy3ed852e2009-09-05 21:47:34 +00009023 if (logging != MagickFalse)
9024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9025 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009026
cristy3ed852e2009-09-05 21:47:34 +00009027 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009028
cristy3ed852e2009-09-05 21:47:34 +00009029 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9030 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009031
cristy3ed852e2009-09-05 21:47:34 +00009032 if (quality > 9)
9033 {
9034 int
9035 level;
9036
cristybb503372010-05-27 20:51:26 +00009037 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009038
cristy3ed852e2009-09-05 21:47:34 +00009039 if (logging != MagickFalse)
9040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9041 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00009042
cristy3ed852e2009-09-05 21:47:34 +00009043 png_set_compression_level(ping,level);
9044 }
glennrp0fe50b42010-11-16 03:52:51 +00009045
cristy3ed852e2009-09-05 21:47:34 +00009046 else
9047 {
9048 if (logging != MagickFalse)
9049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9050 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009051
cristy3ed852e2009-09-05 21:47:34 +00009052 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9053 }
glennrp0fe50b42010-11-16 03:52:51 +00009054
cristy3ed852e2009-09-05 21:47:34 +00009055 if (logging != MagickFalse)
9056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9057 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009058
glennrp2b013e42010-11-24 16:55:50 +00009059#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009060 /* This became available in libpng-1.0.9. Output must be a MNG. */
9061 if (mng_info->write_mng && ((quality % 10) == 7))
9062 {
9063 if (logging != MagickFalse)
9064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9065 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009066
glennrp5af765f2010-03-30 11:12:18 +00009067 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009068 }
glennrp0fe50b42010-11-16 03:52:51 +00009069
cristy3ed852e2009-09-05 21:47:34 +00009070 else
9071 if (logging != MagickFalse)
9072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9073 " Filter_type: 0");
9074#endif
glennrp2b013e42010-11-24 16:55:50 +00009075
cristy3ed852e2009-09-05 21:47:34 +00009076 {
9077 int
9078 base_filter;
9079
9080 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009081 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009082
glennrp26c990a2010-11-23 02:23:20 +00009083 else
glennrp8640fb52010-11-23 15:48:26 +00009084 if ((quality % 10) != 5)
9085 base_filter=(int) quality % 10;
9086
9087 else
9088 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9089 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9090 (quality < 50))
9091 base_filter=PNG_NO_FILTERS;
9092
9093 else
9094 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009095
cristy3ed852e2009-09-05 21:47:34 +00009096 if (logging != MagickFalse)
9097 {
9098 if (base_filter == PNG_ALL_FILTERS)
9099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9100 " Base filter method: ADAPTIVE");
9101 else
9102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9103 " Base filter method: NONE");
9104 }
glennrp2b013e42010-11-24 16:55:50 +00009105
cristy3ed852e2009-09-05 21:47:34 +00009106 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9107 }
9108
glennrp823b55c2011-03-14 18:46:46 +00009109 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9110 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009111 {
9112 ResetImageProfileIterator(image);
9113 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009114 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009115 profile=GetImageProfile(image,name);
9116
9117 if (profile != (StringInfo *) NULL)
9118 {
glennrp5af765f2010-03-30 11:12:18 +00009119#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009120 if ((LocaleCompare(name,"ICC") == 0) ||
9121 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009122 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009123
9124 if (ping_exclude_iCCP == MagickFalse)
9125 {
9126 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009127#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009128 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009129#else
9130 (png_const_bytep) GetStringInfoDatum(profile),
9131#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009132 (png_uint_32) GetStringInfoLength(profile));
9133 }
glennrp26f37912010-12-23 16:22:42 +00009134 }
glennrp0fe50b42010-11-16 03:52:51 +00009135
glennrpc8cbc5d2011-01-01 00:12:34 +00009136 else
cristy3ed852e2009-09-05 21:47:34 +00009137#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009138 if (ping_exclude_zCCP == MagickFalse)
9139 {
glennrpcf002022011-01-30 02:38:15 +00009140 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009141 (unsigned char *) name,(unsigned char *) name,
9142 GetStringInfoDatum(profile),
9143 (png_uint_32) GetStringInfoLength(profile));
9144 }
9145 }
glennrp0b206f52011-01-07 04:55:32 +00009146
glennrpc8cbc5d2011-01-01 00:12:34 +00009147 if (logging != MagickFalse)
9148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9149 " Setting up text chunk with %s profile",name);
9150
9151 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009152 }
cristy3ed852e2009-09-05 21:47:34 +00009153 }
9154
9155#if defined(PNG_WRITE_sRGB_SUPPORTED)
9156 if ((mng_info->have_write_global_srgb == 0) &&
9157 ((image->rendering_intent != UndefinedIntent) ||
9158 (image->colorspace == sRGBColorspace)))
9159 {
glennrp26f37912010-12-23 16:22:42 +00009160 if (ping_exclude_sRGB == MagickFalse)
9161 {
9162 /*
9163 Note image rendering intent.
9164 */
9165 if (logging != MagickFalse)
9166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9167 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009168
glennrp26f37912010-12-23 16:22:42 +00009169 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009170 Magick_RenderingIntent_to_PNG_RenderingIntent(
9171 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009172
glennrp26f37912010-12-23 16:22:42 +00009173 if (ping_exclude_gAMA == MagickFalse)
9174 png_set_gAMA(ping,ping_info,0.45455);
9175 }
cristy3ed852e2009-09-05 21:47:34 +00009176 }
glennrp26f37912010-12-23 16:22:42 +00009177
glennrp5af765f2010-03-30 11:12:18 +00009178 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009179#endif
9180 {
glennrp2cc891a2010-12-24 13:44:32 +00009181 if (ping_exclude_gAMA == MagickFalse &&
9182 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009183 (image->gamma < .45 || image->gamma > .46)))
9184 {
cristy3ed852e2009-09-05 21:47:34 +00009185 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9186 {
9187 /*
9188 Note image gamma.
9189 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9190 */
9191 if (logging != MagickFalse)
9192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9193 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009194
cristy3ed852e2009-09-05 21:47:34 +00009195 png_set_gAMA(ping,ping_info,image->gamma);
9196 }
glennrp26f37912010-12-23 16:22:42 +00009197 }
glennrp2b013e42010-11-24 16:55:50 +00009198
glennrp26f37912010-12-23 16:22:42 +00009199 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009200 {
glennrp26f37912010-12-23 16:22:42 +00009201 if ((mng_info->have_write_global_chrm == 0) &&
9202 (image->chromaticity.red_primary.x != 0.0))
9203 {
9204 /*
9205 Note image chromaticity.
9206 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9207 */
9208 PrimaryInfo
9209 bp,
9210 gp,
9211 rp,
9212 wp;
cristy3ed852e2009-09-05 21:47:34 +00009213
glennrp26f37912010-12-23 16:22:42 +00009214 wp=image->chromaticity.white_point;
9215 rp=image->chromaticity.red_primary;
9216 gp=image->chromaticity.green_primary;
9217 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009218
glennrp26f37912010-12-23 16:22:42 +00009219 if (logging != MagickFalse)
9220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9221 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009222
glennrp26f37912010-12-23 16:22:42 +00009223 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9224 bp.x,bp.y);
9225 }
9226 }
cristy3ed852e2009-09-05 21:47:34 +00009227 }
glennrpdfd70802010-11-14 01:23:35 +00009228
glennrp5af765f2010-03-30 11:12:18 +00009229 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009230
9231 if (mng_info->write_mng)
9232 png_set_sig_bytes(ping,8);
9233
9234 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9235
glennrpd6bf1612010-12-17 17:28:54 +00009236 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009237 {
9238 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009239 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009240 {
glennrp5af765f2010-03-30 11:12:18 +00009241 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009242
glennrp5af765f2010-03-30 11:12:18 +00009243 if (ping_bit_depth < 8)
9244 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009245 }
glennrp0fe50b42010-11-16 03:52:51 +00009246
cristy3ed852e2009-09-05 21:47:34 +00009247 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009248 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009249 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009250 }
9251
glennrp0e8ea192010-12-24 18:00:33 +00009252 if (ping_need_colortype_warning != MagickFalse ||
9253 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009254 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009255 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009256 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009257 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009258 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009259 {
9260 if (logging != MagickFalse)
9261 {
glennrp0e8ea192010-12-24 18:00:33 +00009262 if (ping_need_colortype_warning != MagickFalse)
9263 {
9264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9265 " Image has transparency but tRNS chunk was excluded");
9266 }
9267
cristy3ed852e2009-09-05 21:47:34 +00009268 if (mng_info->write_png_depth)
9269 {
9270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9271 " Defined PNG:bit-depth=%u, Computed depth=%u",
9272 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009273 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009274 }
glennrp0e8ea192010-12-24 18:00:33 +00009275
cristy3ed852e2009-09-05 21:47:34 +00009276 if (mng_info->write_png_colortype)
9277 {
9278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9279 " Defined PNG:color-type=%u, Computed color type=%u",
9280 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009281 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009282 }
9283 }
glennrp0e8ea192010-12-24 18:00:33 +00009284
glennrp3bd2e412010-08-10 13:34:52 +00009285 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009286 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9287 }
9288
glennrp58e01762011-01-07 15:28:54 +00009289 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009290 {
9291 /* Add an opaque matte channel */
9292 image->matte = MagickTrue;
9293 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009294
glennrpb4a13412010-05-05 12:47:19 +00009295 if (logging != MagickFalse)
9296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9297 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009298 }
9299
glennrp0e319732011-01-25 21:53:13 +00009300 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009301 {
glennrp991d11d2010-11-12 21:55:28 +00009302 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009303 {
glennrp991d11d2010-11-12 21:55:28 +00009304 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009305 if (logging != MagickFalse)
9306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9307 " Setting ping_have_tRNS=MagickTrue.");
9308 }
glennrpe9c26dc2010-05-30 01:56:35 +00009309 }
9310
cristy3ed852e2009-09-05 21:47:34 +00009311 if (logging != MagickFalse)
9312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9313 " Writing PNG header chunks");
9314
glennrp5af765f2010-03-30 11:12:18 +00009315 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9316 ping_bit_depth,ping_color_type,
9317 ping_interlace_method,ping_compression_method,
9318 ping_filter_method);
9319
glennrp39992b42010-11-14 00:03:43 +00009320 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9321 {
glennrpf09bded2011-01-08 01:15:59 +00009322 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009323
glennrp3b51f0e2010-11-27 18:14:08 +00009324 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009325 {
glennrp8640fb52010-11-23 15:48:26 +00009326 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009327 {
glennrpd6bf1612010-12-17 17:28:54 +00009328 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009330 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9331 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009332 (int) palette[i].red,
9333 (int) palette[i].green,
9334 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009335 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009336 (int) ping_trans_alpha[i]);
9337 else
9338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009339 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009340 (int) i,
9341 (int) palette[i].red,
9342 (int) palette[i].green,
9343 (int) palette[i].blue);
9344 }
glennrp39992b42010-11-14 00:03:43 +00009345 }
glennrp39992b42010-11-14 00:03:43 +00009346 }
9347
glennrp26f37912010-12-23 16:22:42 +00009348 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009349 {
glennrp26f37912010-12-23 16:22:42 +00009350 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009351 {
glennrp26f37912010-12-23 16:22:42 +00009352 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009353 if (logging)
9354 {
9355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9356 " Setting up bKGD chunk");
9357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9358 " background color = (%d,%d,%d)",
9359 (int) ping_background.red,
9360 (int) ping_background.green,
9361 (int) ping_background.blue);
9362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9363 " index = %d, gray=%d",
9364 (int) ping_background.index,
9365 (int) ping_background.gray);
9366 }
9367 }
glennrp26f37912010-12-23 16:22:42 +00009368 }
9369
9370 if (ping_exclude_pHYs == MagickFalse)
9371 {
9372 if (ping_have_pHYs != MagickFalse)
9373 {
9374 png_set_pHYs(ping,ping_info,
9375 ping_pHYs_x_resolution,
9376 ping_pHYs_y_resolution,
9377 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009378
9379 if (logging)
9380 {
9381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9382 " Setting up pHYs chunk");
9383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9384 " x_resolution=%lu",
9385 (unsigned long) ping_pHYs_x_resolution);
9386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9387 " y_resolution=%lu",
9388 (unsigned long) ping_pHYs_y_resolution);
9389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9390 " unit_type=%lu",
9391 (unsigned long) ping_pHYs_unit_type);
9392 }
glennrp26f37912010-12-23 16:22:42 +00009393 }
glennrpdfd70802010-11-14 01:23:35 +00009394 }
9395
9396#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009397 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009398 {
glennrp26f37912010-12-23 16:22:42 +00009399 if (image->page.x || image->page.y)
9400 {
9401 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9402 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009403
glennrp26f37912010-12-23 16:22:42 +00009404 if (logging != MagickFalse)
9405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9406 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9407 (int) image->page.x, (int) image->page.y);
9408 }
glennrpdfd70802010-11-14 01:23:35 +00009409 }
9410#endif
9411
glennrpda8f3a72011-02-27 23:54:12 +00009412 if (mng_info->need_blob != MagickFalse)
9413 {
9414 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9415 MagickFalse)
9416 png_error(ping,"WriteBlob Failed");
9417
9418 ping_have_blob=MagickTrue;
9419 }
9420
cristy3ed852e2009-09-05 21:47:34 +00009421 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009422
glennrp39992b42010-11-14 00:03:43 +00009423 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009424 {
glennrp3b51f0e2010-11-27 18:14:08 +00009425 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009426 {
9427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9428 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9429 }
9430
9431 if (ping_color_type == 3)
9432 (void) png_set_tRNS(ping, ping_info,
9433 ping_trans_alpha,
9434 ping_num_trans,
9435 NULL);
9436
9437 else
9438 {
9439 (void) png_set_tRNS(ping, ping_info,
9440 NULL,
9441 0,
9442 &ping_trans_color);
9443
glennrp3b51f0e2010-11-27 18:14:08 +00009444 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009445 {
9446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009447 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009448 (int) ping_trans_color.red,
9449 (int) ping_trans_color.green,
9450 (int) ping_trans_color.blue);
9451 }
9452 }
glennrp991d11d2010-11-12 21:55:28 +00009453 }
9454
cristy3ed852e2009-09-05 21:47:34 +00009455 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009456 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009457
cristy3ed852e2009-09-05 21:47:34 +00009458 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009459
cristy3ed852e2009-09-05 21:47:34 +00009460 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009461 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009462
glennrp26f37912010-12-23 16:22:42 +00009463 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009464 {
glennrp4f25bd02011-01-01 18:51:28 +00009465 if ((image->page.width != 0 && image->page.width != image->columns) ||
9466 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009467 {
9468 unsigned char
9469 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009470
glennrp26f37912010-12-23 16:22:42 +00009471 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9472 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009473 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009474 PNGLong(chunk+4,(png_uint_32) image->page.width);
9475 PNGLong(chunk+8,(png_uint_32) image->page.height);
9476 chunk[12]=0; /* unit = pixels */
9477 (void) WriteBlob(image,13,chunk);
9478 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9479 }
cristy3ed852e2009-09-05 21:47:34 +00009480 }
9481
9482#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009483 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009484#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009485 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009486#undef PNG_HAVE_IDAT
9487#endif
9488
9489 png_set_packing(ping);
9490 /*
9491 Allocate memory.
9492 */
9493 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009494 if (image_depth > 8)
9495 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009496 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009497 {
glennrpb4a13412010-05-05 12:47:19 +00009498 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009499 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009500 break;
glennrp0fe50b42010-11-16 03:52:51 +00009501
glennrpb4a13412010-05-05 12:47:19 +00009502 case PNG_COLOR_TYPE_GRAY_ALPHA:
9503 rowbytes*=2;
9504 break;
glennrp0fe50b42010-11-16 03:52:51 +00009505
glennrpb4a13412010-05-05 12:47:19 +00009506 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009507 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009508 break;
glennrp0fe50b42010-11-16 03:52:51 +00009509
glennrpb4a13412010-05-05 12:47:19 +00009510 default:
9511 break;
cristy3ed852e2009-09-05 21:47:34 +00009512 }
glennrp3b51f0e2010-11-27 18:14:08 +00009513
9514 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009515 {
9516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9517 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009518
glennrpb4a13412010-05-05 12:47:19 +00009519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009520 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009521 }
glennrpcf002022011-01-30 02:38:15 +00009522 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9523 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009524
glennrpcf002022011-01-30 02:38:15 +00009525 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009526 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009527
cristy3ed852e2009-09-05 21:47:34 +00009528 /*
9529 Initialize image scanlines.
9530 */
glennrp5af765f2010-03-30 11:12:18 +00009531 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009532 {
9533 /*
9534 PNG write failed.
9535 */
9536#ifdef PNG_DEBUG
9537 if (image_info->verbose)
9538 (void) printf("PNG write has failed.\n");
9539#endif
9540 png_destroy_write_struct(&ping,&ping_info);
9541 if (quantum_info != (QuantumInfo *) NULL)
9542 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009543 if (ping_pixels != (unsigned char *) NULL)
9544 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009545#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009546 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009547#endif
glennrpda8f3a72011-02-27 23:54:12 +00009548 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009549 (void) CloseBlob(image);
9550 image_info=DestroyImageInfo(image_info);
9551 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009552 return(MagickFalse);
9553 }
cristyed552522009-10-16 14:04:35 +00009554 quantum_info=AcquireQuantumInfo(image_info,image);
9555 if (quantum_info == (QuantumInfo *) NULL)
9556 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009557 quantum_info->format=UndefinedQuantumFormat;
9558 quantum_info->depth=image_depth;
9559 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009560
cristy3ed852e2009-09-05 21:47:34 +00009561 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009562 !mng_info->write_png32) &&
9563 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009564 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009565 image_matte == MagickFalse &&
9566 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009567 {
glennrp8bb3a022010-12-13 20:40:04 +00009568 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009569 register const PixelPacket
9570 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009571
cristy3ed852e2009-09-05 21:47:34 +00009572 quantum_info->depth=8;
9573 for (pass=0; pass < num_passes; pass++)
9574 {
9575 /*
9576 Convert PseudoClass image to a PNG monochrome image.
9577 */
cristybb503372010-05-27 20:51:26 +00009578 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009579 {
glennrpd71e86a2011-02-24 01:28:37 +00009580 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9582 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009583
cristy3ed852e2009-09-05 21:47:34 +00009584 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009585
cristy3ed852e2009-09-05 21:47:34 +00009586 if (p == (const PixelPacket *) NULL)
9587 break;
glennrp0fe50b42010-11-16 03:52:51 +00009588
cristy3ed852e2009-09-05 21:47:34 +00009589 if (mng_info->IsPalette)
9590 {
9591 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009592 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009593 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9594 mng_info->write_png_depth &&
9595 mng_info->write_png_depth != old_bit_depth)
9596 {
9597 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009598 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009599 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009600 >> (8-old_bit_depth));
9601 }
9602 }
glennrp0fe50b42010-11-16 03:52:51 +00009603
cristy3ed852e2009-09-05 21:47:34 +00009604 else
9605 {
9606 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009607 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009608 }
glennrp0fe50b42010-11-16 03:52:51 +00009609
cristy3ed852e2009-09-05 21:47:34 +00009610 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009611 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009612 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009613 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009614
glennrp3b51f0e2010-11-27 18:14:08 +00009615 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9617 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009618
glennrpcf002022011-01-30 02:38:15 +00009619 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009620 }
9621 if (image->previous == (Image *) NULL)
9622 {
9623 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9624 if (status == MagickFalse)
9625 break;
9626 }
9627 }
9628 }
glennrp0fe50b42010-11-16 03:52:51 +00009629
glennrp8bb3a022010-12-13 20:40:04 +00009630 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009631 {
glennrp0fe50b42010-11-16 03:52:51 +00009632 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009633 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009634 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009635 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009636 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009637 {
glennrp8bb3a022010-12-13 20:40:04 +00009638 register const PixelPacket
9639 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009640
glennrp8bb3a022010-12-13 20:40:04 +00009641 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009642 {
glennrp8bb3a022010-12-13 20:40:04 +00009643
cristybb503372010-05-27 20:51:26 +00009644 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009645 {
9646 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009647
cristy3ed852e2009-09-05 21:47:34 +00009648 if (p == (const PixelPacket *) NULL)
9649 break;
glennrp2cc891a2010-12-24 13:44:32 +00009650
glennrp5af765f2010-03-30 11:12:18 +00009651 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009652 {
glennrp8bb3a022010-12-13 20:40:04 +00009653 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009654 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009655 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009656
glennrp8bb3a022010-12-13 20:40:04 +00009657 else
9658 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009659 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009660
glennrp3b51f0e2010-11-27 18:14:08 +00009661 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009663 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009664 }
glennrp2cc891a2010-12-24 13:44:32 +00009665
glennrp8bb3a022010-12-13 20:40:04 +00009666 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9667 {
9668 if (logging != MagickFalse && y == 0)
9669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9670 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009671
glennrp8bb3a022010-12-13 20:40:04 +00009672 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009673 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009674 }
glennrp2cc891a2010-12-24 13:44:32 +00009675
glennrp3b51f0e2010-11-27 18:14:08 +00009676 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009678 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009679
glennrpcf002022011-01-30 02:38:15 +00009680 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009681 }
glennrp2cc891a2010-12-24 13:44:32 +00009682
glennrp8bb3a022010-12-13 20:40:04 +00009683 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009684 {
glennrp8bb3a022010-12-13 20:40:04 +00009685 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9686 if (status == MagickFalse)
9687 break;
cristy3ed852e2009-09-05 21:47:34 +00009688 }
cristy3ed852e2009-09-05 21:47:34 +00009689 }
9690 }
glennrp8bb3a022010-12-13 20:40:04 +00009691
9692 else
9693 {
9694 register const PixelPacket
9695 *p;
9696
9697 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009698 {
glennrp8bb3a022010-12-13 20:40:04 +00009699 if ((image_depth > 8) || (mng_info->write_png24 ||
9700 mng_info->write_png32 ||
9701 (!mng_info->write_png8 && !mng_info->IsPalette)))
9702 {
9703 for (y=0; y < (ssize_t) image->rows; y++)
9704 {
9705 p=GetVirtualPixels(image,0,y,image->columns,1,
9706 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009707
glennrp8bb3a022010-12-13 20:40:04 +00009708 if (p == (const PixelPacket *) NULL)
9709 break;
glennrp2cc891a2010-12-24 13:44:32 +00009710
glennrp8bb3a022010-12-13 20:40:04 +00009711 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9712 {
9713 if (image->storage_class == DirectClass)
9714 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009715 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009716
glennrp8bb3a022010-12-13 20:40:04 +00009717 else
9718 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009719 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009720 }
glennrp2cc891a2010-12-24 13:44:32 +00009721
glennrp8bb3a022010-12-13 20:40:04 +00009722 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9723 {
9724 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009725 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009726 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009727
glennrp8bb3a022010-12-13 20:40:04 +00009728 if (logging != MagickFalse && y == 0)
9729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9730 " Writing GRAY_ALPHA PNG pixels (3)");
9731 }
glennrp2cc891a2010-12-24 13:44:32 +00009732
glennrp8bb3a022010-12-13 20:40:04 +00009733 else if (image_matte != MagickFalse)
9734 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009735 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009736
glennrp8bb3a022010-12-13 20:40:04 +00009737 else
9738 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009739 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009740
glennrp8bb3a022010-12-13 20:40:04 +00009741 if (logging != MagickFalse && y == 0)
9742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9743 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009744
glennrpcf002022011-01-30 02:38:15 +00009745 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009746 }
9747 }
glennrp2cc891a2010-12-24 13:44:32 +00009748
glennrp8bb3a022010-12-13 20:40:04 +00009749 else
9750 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9751 mng_info->write_png32 ||
9752 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9753 {
9754 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9755 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9756 {
9757 if (logging != MagickFalse)
9758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9759 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009760
glennrp8bb3a022010-12-13 20:40:04 +00009761 quantum_info->depth=8;
9762 image_depth=8;
9763 }
glennrp2cc891a2010-12-24 13:44:32 +00009764
glennrp8bb3a022010-12-13 20:40:04 +00009765 for (y=0; y < (ssize_t) image->rows; y++)
9766 {
9767 if (logging != MagickFalse && y == 0)
9768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9769 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009770
glennrp770d1932011-03-06 22:11:17 +00009771 p=GetVirtualPixels(image,0,y,image->columns,1,
9772 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009773
glennrp8bb3a022010-12-13 20:40:04 +00009774 if (p == (const PixelPacket *) NULL)
9775 break;
glennrp2cc891a2010-12-24 13:44:32 +00009776
glennrp8bb3a022010-12-13 20:40:04 +00009777 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009778 {
glennrp4bf89732011-03-21 13:48:28 +00009779 quantum_info->depth=image->depth;
9780
glennrp44757ab2011-03-17 12:57:03 +00009781 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009782 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009783 }
glennrp2cc891a2010-12-24 13:44:32 +00009784
glennrp8bb3a022010-12-13 20:40:04 +00009785 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9786 {
9787 if (logging != MagickFalse && y == 0)
9788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9789 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009790
glennrp8bb3a022010-12-13 20:40:04 +00009791 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009792 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009793 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009794 }
glennrp2cc891a2010-12-24 13:44:32 +00009795
glennrp8bb3a022010-12-13 20:40:04 +00009796 else
glennrp8bb3a022010-12-13 20:40:04 +00009797 {
glennrp179d0752011-03-17 13:02:10 +00009798 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009799 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9800
9801 if (logging != MagickFalse && y <= 2)
9802 {
9803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009804 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009805
9806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9807 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9808 (int)ping_pixels[0],(int)ping_pixels[1]);
9809 }
glennrp8bb3a022010-12-13 20:40:04 +00009810 }
glennrpcf002022011-01-30 02:38:15 +00009811 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009812 }
9813 }
glennrp2cc891a2010-12-24 13:44:32 +00009814
glennrp8bb3a022010-12-13 20:40:04 +00009815 if (image->previous == (Image *) NULL)
9816 {
9817 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9818 if (status == MagickFalse)
9819 break;
9820 }
cristy3ed852e2009-09-05 21:47:34 +00009821 }
glennrp8bb3a022010-12-13 20:40:04 +00009822 }
9823 }
9824
cristyb32b90a2009-09-07 21:45:48 +00009825 if (quantum_info != (QuantumInfo *) NULL)
9826 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009827
9828 if (logging != MagickFalse)
9829 {
9830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009831 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009832
cristy3ed852e2009-09-05 21:47:34 +00009833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009834 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009835
cristy3ed852e2009-09-05 21:47:34 +00009836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009837 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009838
cristy3ed852e2009-09-05 21:47:34 +00009839 if (mng_info->write_png_depth)
9840 {
9841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9842 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9843 }
glennrp0fe50b42010-11-16 03:52:51 +00009844
cristy3ed852e2009-09-05 21:47:34 +00009845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009846 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009847
cristy3ed852e2009-09-05 21:47:34 +00009848 if (mng_info->write_png_colortype)
9849 {
9850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9851 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9852 }
glennrp0fe50b42010-11-16 03:52:51 +00009853
cristy3ed852e2009-09-05 21:47:34 +00009854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009855 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009856
cristy3ed852e2009-09-05 21:47:34 +00009857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009858 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009859 }
9860 /*
glennrpa0ed0092011-04-18 16:36:29 +00009861 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009862 */
glennrp823b55c2011-03-14 18:46:46 +00009863 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009864 {
glennrp26f37912010-12-23 16:22:42 +00009865 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009866 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009867 while (property != (const char *) NULL)
9868 {
9869 png_textp
9870 text;
glennrp2cc891a2010-12-24 13:44:32 +00009871
glennrp26f37912010-12-23 16:22:42 +00009872 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009873
9874 /* Don't write any "png:" properties; those are just for "identify" */
9875 if (LocaleNCompare(property,"png:",4) != 0 &&
9876
9877 /* Suppress density and units if we wrote a pHYs chunk */
9878 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009879 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009880 LocaleCompare(property,"units") != 0) &&
9881
9882 /* Suppress the IM-generated Date:create and Date:modify */
9883 (ping_exclude_date == MagickFalse ||
9884 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009885 {
glennrpc70af4a2011-03-07 00:08:23 +00009886 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009887 {
glennrpc70af4a2011-03-07 00:08:23 +00009888 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9889 text[0].key=(char *) property;
9890 text[0].text=(char *) value;
9891 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009892
glennrpc70af4a2011-03-07 00:08:23 +00009893 if (ping_exclude_tEXt != MagickFalse)
9894 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9895
9896 else if (ping_exclude_zTXt != MagickFalse)
9897 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9898
9899 else
glennrp26f37912010-12-23 16:22:42 +00009900 {
glennrpc70af4a2011-03-07 00:08:23 +00009901 text[0].compression=image_info->compression == NoCompression ||
9902 (image_info->compression == UndefinedCompression &&
9903 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9904 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009905 }
glennrp2cc891a2010-12-24 13:44:32 +00009906
glennrpc70af4a2011-03-07 00:08:23 +00009907 if (logging != MagickFalse)
9908 {
9909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9910 " Setting up text chunk");
9911
9912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9913 " keyword: %s",text[0].key);
9914 }
9915
9916 png_set_text(ping,ping_info,text,1);
9917 png_free(ping,text);
9918 }
glennrp26f37912010-12-23 16:22:42 +00009919 }
9920 property=GetNextImageProperty(image);
9921 }
cristy3ed852e2009-09-05 21:47:34 +00009922 }
9923
9924 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009925 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009926
9927 if (logging != MagickFalse)
9928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9929 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009930
cristy3ed852e2009-09-05 21:47:34 +00009931 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009932
cristy3ed852e2009-09-05 21:47:34 +00009933 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9934 {
9935 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009936 (ping_width != mng_info->page.width) ||
9937 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009938 {
9939 unsigned char
9940 chunk[32];
9941
9942 /*
9943 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9944 */
9945 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9946 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009947 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009948 chunk[4]=4;
9949 chunk[5]=0; /* frame name separator (no name) */
9950 chunk[6]=1; /* flag for changing delay, for next frame only */
9951 chunk[7]=0; /* flag for changing frame timeout */
9952 chunk[8]=1; /* flag for changing frame clipping for next frame */
9953 chunk[9]=0; /* flag for changing frame sync_id */
9954 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9955 chunk[14]=0; /* clipping boundaries delta type */
9956 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9957 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009958 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009959 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9960 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009961 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009962 (void) WriteBlob(image,31,chunk);
9963 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9964 mng_info->old_framing_mode=4;
9965 mng_info->framing_mode=1;
9966 }
glennrp0fe50b42010-11-16 03:52:51 +00009967
cristy3ed852e2009-09-05 21:47:34 +00009968 else
9969 mng_info->framing_mode=3;
9970 }
9971 if (mng_info->write_mng && !mng_info->need_fram &&
9972 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009973 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009974 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009975 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009976
cristy3ed852e2009-09-05 21:47:34 +00009977 /*
9978 Free PNG resources.
9979 */
glennrp5af765f2010-03-30 11:12:18 +00009980
cristy3ed852e2009-09-05 21:47:34 +00009981 png_destroy_write_struct(&ping,&ping_info);
9982
glennrpcf002022011-01-30 02:38:15 +00009983 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009984
9985#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009986 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009987#endif
9988
glennrpda8f3a72011-02-27 23:54:12 +00009989 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009990 (void) CloseBlob(image);
9991
9992 image_info=DestroyImageInfo(image_info);
9993 image=DestroyImage(image);
9994
9995 /* Store bit depth actually written */
9996 s[0]=(char) ping_bit_depth;
9997 s[1]='\0';
9998
9999 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10000
cristy3ed852e2009-09-05 21:47:34 +000010001 if (logging != MagickFalse)
10002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10003 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010004
cristy3ed852e2009-09-05 21:47:34 +000010005 return(MagickTrue);
10006/* End write one PNG image */
10007}
10008
10009/*
10010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10011% %
10012% %
10013% %
10014% W r i t e P N G I m a g e %
10015% %
10016% %
10017% %
10018%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10019%
10020% WritePNGImage() writes a Portable Network Graphics (PNG) or
10021% Multiple-image Network Graphics (MNG) image file.
10022%
10023% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10024%
10025% The format of the WritePNGImage method is:
10026%
10027% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10028%
10029% A description of each parameter follows:
10030%
10031% o image_info: the image info.
10032%
10033% o image: The image.
10034%
10035% Returns MagickTrue on success, MagickFalse on failure.
10036%
10037% Communicating with the PNG encoder:
10038%
10039% While the datastream written is always in PNG format and normally would
10040% be given the "png" file extension, this method also writes the following
10041% pseudo-formats which are subsets of PNG:
10042%
glennrp5a39f372011-02-25 04:52:16 +000010043% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10044% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010045% is present, the tRNS chunk must only have values 0 and 255
10046% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010047% transparent). If other values are present they will be
10048% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010049% colors are present, they will be quantized to the 4-4-4-1,
10050% 3-3-3-1, or 3-3-2-1 palette.
10051%
10052% If you want better quantization or dithering of the colors
10053% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010054% PNG encoder. The pixels contain 8-bit indices even if
10055% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010056% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010057% PNG grayscale type might be slightly more efficient. Please
10058% note that writing to the PNG8 format may result in loss
10059% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010060%
10061% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10062% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010063% one of the colors as transparent. The only loss incurred
10064% is reduction of sample depth to 8. If the image has more
10065% than one transparent color, has semitransparent pixels, or
10066% has an opaque pixel with the same RGB components as the
10067% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010068%
10069% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10070% transparency is permitted, i.e., the alpha sample for
10071% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010072% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010073% The only loss in data is the reduction of the sample depth
10074% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010075%
10076% o -define: For more precise control of the PNG output, you can use the
10077% Image options "png:bit-depth" and "png:color-type". These
10078% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010079% from the application programming interfaces. The options
10080% are case-independent and are converted to lowercase before
10081% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010082%
10083% png:color-type can be 0, 2, 3, 4, or 6.
10084%
10085% When png:color-type is 0 (Grayscale), png:bit-depth can
10086% be 1, 2, 4, 8, or 16.
10087%
10088% When png:color-type is 2 (RGB), png:bit-depth can
10089% be 8 or 16.
10090%
10091% When png:color-type is 3 (Indexed), png:bit-depth can
10092% be 1, 2, 4, or 8. This refers to the number of bits
10093% used to store the index. The color samples always have
10094% bit-depth 8 in indexed PNG files.
10095%
10096% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10097% png:bit-depth can be 8 or 16.
10098%
glennrp5a39f372011-02-25 04:52:16 +000010099% If the image cannot be written without loss with the requested bit-depth
10100% and color-type, a PNG file will not be written, and the encoder will
10101% return MagickFalse.
10102%
cristy3ed852e2009-09-05 21:47:34 +000010103% Since image encoders should not be responsible for the "heavy lifting",
10104% the user should make sure that ImageMagick has already reduced the
10105% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010106% transparency prior to attempting to write the image with depth, color,
10107% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010108%
glennrp97cefe22011-04-22 16:17:00 +000010109% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010110%
cristy3ed852e2009-09-05 21:47:34 +000010111% Note that another definition, "png:bit-depth-written" exists, but it
10112% is not intended for external use. It is only used internally by the
10113% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10114%
10115% It is possible to request that the PNG encoder write previously-formatted
10116% ancillary chunks in the output PNG file, using the "-profile" commandline
10117% option as shown below or by setting the profile via a programming
10118% interface:
10119%
10120% -profile PNG-chunk-x:<file>
10121%
10122% where x is a location flag and <file> is a file containing the chunk
10123% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010124% This encoder will compute the chunk length and CRC, so those must not
10125% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010126%
10127% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10128% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10129% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010130% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010131%
glennrpbb8a7332010-11-13 15:17:35 +000010132% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010133%
glennrp3241bd02010-12-12 04:36:28 +000010134% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010135%
glennrpd6afd542010-11-19 01:53:05 +000010136% o 32-bit depth is reduced to 16.
10137% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10138% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010139% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010140% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010141% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010142% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10143% this can be done without loss and a larger bit depth N was not
10144% requested via the "-define PNG:bit-depth=N" option.
10145% o If matte channel is present but only one transparent color is
10146% present, RGB+tRNS is written instead of RGBA
10147% o Opaque matte channel is removed (or added, if color-type 4 or 6
10148% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010149%
cristy3ed852e2009-09-05 21:47:34 +000010150%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10151*/
10152static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10153 Image *image)
10154{
10155 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010156 excluding,
10157 logging,
10158 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010159 status;
10160
10161 MngInfo
10162 *mng_info;
10163
10164 const char
10165 *value;
10166
10167 int
glennrp21f0e622011-01-07 16:20:57 +000010168 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010169 source;
10170
cristy3ed852e2009-09-05 21:47:34 +000010171 /*
10172 Open image file.
10173 */
10174 assert(image_info != (const ImageInfo *) NULL);
10175 assert(image_info->signature == MagickSignature);
10176 assert(image != (Image *) NULL);
10177 assert(image->signature == MagickSignature);
10178 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010179 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010180 /*
10181 Allocate a MngInfo structure.
10182 */
10183 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010184 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010185
cristy3ed852e2009-09-05 21:47:34 +000010186 if (mng_info == (MngInfo *) NULL)
10187 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010188
cristy3ed852e2009-09-05 21:47:34 +000010189 /*
10190 Initialize members of the MngInfo structure.
10191 */
10192 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10193 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010194 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010195 have_mng_structure=MagickTrue;
10196
10197 /* See if user has requested a specific PNG subformat */
10198
10199 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10200 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10201 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10202
10203 if (mng_info->write_png8)
10204 {
glennrp9c1eb072010-06-06 22:19:15 +000010205 mng_info->write_png_colortype = /* 3 */ 4;
10206 mng_info->write_png_depth = 8;
10207 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010208 }
10209
10210 if (mng_info->write_png24)
10211 {
glennrp9c1eb072010-06-06 22:19:15 +000010212 mng_info->write_png_colortype = /* 2 */ 3;
10213 mng_info->write_png_depth = 8;
10214 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010215
glennrp9c1eb072010-06-06 22:19:15 +000010216 if (image->matte == MagickTrue)
10217 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010218
glennrp9c1eb072010-06-06 22:19:15 +000010219 else
10220 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010221
glennrp9c1eb072010-06-06 22:19:15 +000010222 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010223 }
10224
10225 if (mng_info->write_png32)
10226 {
glennrp9c1eb072010-06-06 22:19:15 +000010227 mng_info->write_png_colortype = /* 6 */ 7;
10228 mng_info->write_png_depth = 8;
10229 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010230
glennrp9c1eb072010-06-06 22:19:15 +000010231 if (image->matte == MagickTrue)
10232 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010233
glennrp9c1eb072010-06-06 22:19:15 +000010234 else
10235 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010236
glennrp9c1eb072010-06-06 22:19:15 +000010237 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010238 }
10239
10240 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010241
cristy3ed852e2009-09-05 21:47:34 +000010242 if (value != (char *) NULL)
10243 {
10244 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010245 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010246
cristy3ed852e2009-09-05 21:47:34 +000010247 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010248 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010249
cristy3ed852e2009-09-05 21:47:34 +000010250 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010251 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010252
cristy3ed852e2009-09-05 21:47:34 +000010253 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010254 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010255
cristy3ed852e2009-09-05 21:47:34 +000010256 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010257 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010258
glennrpbb8a7332010-11-13 15:17:35 +000010259 else
10260 (void) ThrowMagickException(&image->exception,
10261 GetMagickModule(),CoderWarning,
10262 "ignoring invalid defined png:bit-depth",
10263 "=%s",value);
10264
cristy3ed852e2009-09-05 21:47:34 +000010265 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010267 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010268 }
glennrp0fe50b42010-11-16 03:52:51 +000010269
cristy3ed852e2009-09-05 21:47:34 +000010270 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010271
cristy3ed852e2009-09-05 21:47:34 +000010272 if (value != (char *) NULL)
10273 {
10274 /* We must store colortype+1 because 0 is a valid colortype */
10275 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010276 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010277
cristy3ed852e2009-09-05 21:47:34 +000010278 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010279 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010280
cristy3ed852e2009-09-05 21:47:34 +000010281 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010282 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010283
cristy3ed852e2009-09-05 21:47:34 +000010284 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010285 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010286
cristy3ed852e2009-09-05 21:47:34 +000010287 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010288 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010289
glennrpbb8a7332010-11-13 15:17:35 +000010290 else
10291 (void) ThrowMagickException(&image->exception,
10292 GetMagickModule(),CoderWarning,
10293 "ignoring invalid defined png:color-type",
10294 "=%s",value);
10295
cristy3ed852e2009-09-05 21:47:34 +000010296 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010298 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010299 }
10300
glennrp0e8ea192010-12-24 18:00:33 +000010301 /* Check for chunks to be excluded:
10302 *
glennrp0dff56c2011-01-29 19:10:02 +000010303 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010304 * listed in the "unused_chunks" array, above.
10305 *
10306 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10307 * define (in the image properties or in the image artifacts)
10308 * or via a mng_info member. For convenience, in addition
10309 * to or instead of a comma-separated list of chunks, the
10310 * "exclude-chunk" string can be simply "all" or "none".
10311 *
10312 * The exclude-chunk define takes priority over the mng_info.
10313 *
10314 * A "PNG:include-chunk" define takes priority over both the
10315 * mng_info and the "PNG:exclude-chunk" define. Like the
10316 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010317 * well as a comma-separated list. Chunks that are unknown to
10318 * ImageMagick are always excluded, regardless of their "copy-safe"
10319 * status according to the PNG specification, and even if they
10320 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010321 *
10322 * Finally, all chunks listed in the "unused_chunks" array are
10323 * automatically excluded, regardless of the other instructions
10324 * or lack thereof.
10325 *
10326 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10327 * will not be written and the gAMA chunk will only be written if it
10328 * is not between .45 and .46, or approximately (1.0/2.2).
10329 *
10330 * If you exclude tRNS and the image has transparency, the colortype
10331 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10332 *
10333 * The -strip option causes StripImage() to set the png:include-chunk
10334 * artifact to "none,gama".
10335 */
10336
glennrp26f37912010-12-23 16:22:42 +000010337 mng_info->ping_exclude_bKGD=MagickFalse;
10338 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010339 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010340 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10341 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010342 mng_info->ping_exclude_iCCP=MagickFalse;
10343 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10344 mng_info->ping_exclude_oFFs=MagickFalse;
10345 mng_info->ping_exclude_pHYs=MagickFalse;
10346 mng_info->ping_exclude_sRGB=MagickFalse;
10347 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010348 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010349 mng_info->ping_exclude_vpAg=MagickFalse;
10350 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10351 mng_info->ping_exclude_zTXt=MagickFalse;
10352
glennrp8d3d6e52011-04-19 04:39:51 +000010353 mng_info->ping_preserve_colormap=MagickFalse;
10354
10355 value=GetImageArtifact(image,"png:preserve-colormap");
10356 if (value == NULL)
10357 value=GetImageOption(image_info,"png:preserve-colormap");
10358 if (value != NULL)
10359 mng_info->ping_preserve_colormap=MagickTrue;
10360
glennrp03812ae2010-12-24 01:31:34 +000010361 excluding=MagickFalse;
10362
glennrp5c7cf4e2010-12-24 00:30:00 +000010363 for (source=0; source<1; source++)
10364 {
10365 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010366 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010367 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010368
10369 if (value == NULL)
10370 value=GetImageArtifact(image,"png:exclude-chunks");
10371 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010372 else
glennrpacba0042010-12-24 14:27:26 +000010373 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010374 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010375
glennrpacba0042010-12-24 14:27:26 +000010376 if (value == NULL)
10377 value=GetImageOption(image_info,"png:exclude-chunks");
10378 }
10379
glennrp03812ae2010-12-24 01:31:34 +000010380 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010381 {
glennrp03812ae2010-12-24 01:31:34 +000010382
10383 size_t
10384 last;
10385
10386 excluding=MagickTrue;
10387
10388 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010389 {
10390 if (source == 0)
10391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10392 " png:exclude-chunk=%s found in image artifacts.\n", value);
10393 else
10394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10395 " png:exclude-chunk=%s found in image properties.\n", value);
10396 }
glennrp03812ae2010-12-24 01:31:34 +000010397
10398 last=strlen(value);
10399
10400 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010401 {
glennrp03812ae2010-12-24 01:31:34 +000010402
10403 if (LocaleNCompare(value+i,"all",3) == 0)
10404 {
10405 mng_info->ping_exclude_bKGD=MagickTrue;
10406 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010407 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010408 mng_info->ping_exclude_EXIF=MagickTrue;
10409 mng_info->ping_exclude_gAMA=MagickTrue;
10410 mng_info->ping_exclude_iCCP=MagickTrue;
10411 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10412 mng_info->ping_exclude_oFFs=MagickTrue;
10413 mng_info->ping_exclude_pHYs=MagickTrue;
10414 mng_info->ping_exclude_sRGB=MagickTrue;
10415 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010416 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010417 mng_info->ping_exclude_vpAg=MagickTrue;
10418 mng_info->ping_exclude_zCCP=MagickTrue;
10419 mng_info->ping_exclude_zTXt=MagickTrue;
10420 i--;
10421 }
glennrp2cc891a2010-12-24 13:44:32 +000010422
glennrp03812ae2010-12-24 01:31:34 +000010423 if (LocaleNCompare(value+i,"none",4) == 0)
10424 {
10425 mng_info->ping_exclude_bKGD=MagickFalse;
10426 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010427 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010428 mng_info->ping_exclude_EXIF=MagickFalse;
10429 mng_info->ping_exclude_gAMA=MagickFalse;
10430 mng_info->ping_exclude_iCCP=MagickFalse;
10431 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10432 mng_info->ping_exclude_oFFs=MagickFalse;
10433 mng_info->ping_exclude_pHYs=MagickFalse;
10434 mng_info->ping_exclude_sRGB=MagickFalse;
10435 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010436 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010437 mng_info->ping_exclude_vpAg=MagickFalse;
10438 mng_info->ping_exclude_zCCP=MagickFalse;
10439 mng_info->ping_exclude_zTXt=MagickFalse;
10440 }
glennrp2cc891a2010-12-24 13:44:32 +000010441
glennrp03812ae2010-12-24 01:31:34 +000010442 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10443 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010444
glennrp03812ae2010-12-24 01:31:34 +000010445 if (LocaleNCompare(value+i,"chrm",4) == 0)
10446 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010447
glennrpa0ed0092011-04-18 16:36:29 +000010448 if (LocaleNCompare(value+i,"date",4) == 0)
10449 mng_info->ping_exclude_date=MagickTrue;
10450
glennrp03812ae2010-12-24 01:31:34 +000010451 if (LocaleNCompare(value+i,"exif",4) == 0)
10452 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010453
glennrp03812ae2010-12-24 01:31:34 +000010454 if (LocaleNCompare(value+i,"gama",4) == 0)
10455 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010456
glennrp03812ae2010-12-24 01:31:34 +000010457 if (LocaleNCompare(value+i,"iccp",4) == 0)
10458 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010459
glennrp03812ae2010-12-24 01:31:34 +000010460 /*
10461 if (LocaleNCompare(value+i,"itxt",4) == 0)
10462 mng_info->ping_exclude_iTXt=MagickTrue;
10463 */
glennrp2cc891a2010-12-24 13:44:32 +000010464
glennrp03812ae2010-12-24 01:31:34 +000010465 if (LocaleNCompare(value+i,"gama",4) == 0)
10466 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010467
glennrp03812ae2010-12-24 01:31:34 +000010468 if (LocaleNCompare(value+i,"offs",4) == 0)
10469 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010470
glennrp03812ae2010-12-24 01:31:34 +000010471 if (LocaleNCompare(value+i,"phys",4) == 0)
10472 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010473
glennrpa1e3b7b2010-12-24 16:37:33 +000010474 if (LocaleNCompare(value+i,"srgb",4) == 0)
10475 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010476
glennrp03812ae2010-12-24 01:31:34 +000010477 if (LocaleNCompare(value+i,"text",4) == 0)
10478 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010479
glennrpa1e3b7b2010-12-24 16:37:33 +000010480 if (LocaleNCompare(value+i,"trns",4) == 0)
10481 mng_info->ping_exclude_tRNS=MagickTrue;
10482
glennrp03812ae2010-12-24 01:31:34 +000010483 if (LocaleNCompare(value+i,"vpag",4) == 0)
10484 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010485
glennrp03812ae2010-12-24 01:31:34 +000010486 if (LocaleNCompare(value+i,"zccp",4) == 0)
10487 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010488
glennrp03812ae2010-12-24 01:31:34 +000010489 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10490 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010491
glennrp03812ae2010-12-24 01:31:34 +000010492 }
glennrpce91ed52010-12-23 22:37:49 +000010493 }
glennrp26f37912010-12-23 16:22:42 +000010494 }
10495
glennrp5c7cf4e2010-12-24 00:30:00 +000010496 for (source=0; source<1; source++)
10497 {
10498 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010499 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010500 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010501
10502 if (value == NULL)
10503 value=GetImageArtifact(image,"png:include-chunks");
10504 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010505 else
glennrpacba0042010-12-24 14:27:26 +000010506 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010507 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010508
glennrpacba0042010-12-24 14:27:26 +000010509 if (value == NULL)
10510 value=GetImageOption(image_info,"png:include-chunks");
10511 }
10512
glennrp03812ae2010-12-24 01:31:34 +000010513 if (value != NULL)
10514 {
10515 size_t
10516 last;
glennrp26f37912010-12-23 16:22:42 +000010517
glennrp03812ae2010-12-24 01:31:34 +000010518 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010519
glennrp03812ae2010-12-24 01:31:34 +000010520 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010521 {
10522 if (source == 0)
10523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10524 " png:include-chunk=%s found in image artifacts.\n", value);
10525 else
10526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10527 " png:include-chunk=%s found in image properties.\n", value);
10528 }
glennrp03812ae2010-12-24 01:31:34 +000010529
10530 last=strlen(value);
10531
10532 for (i=0; i<(int) last; i+=5)
10533 {
10534 if (LocaleNCompare(value+i,"all",3) == 0)
10535 {
10536 mng_info->ping_exclude_bKGD=MagickFalse;
10537 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010538 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010539 mng_info->ping_exclude_EXIF=MagickFalse;
10540 mng_info->ping_exclude_gAMA=MagickFalse;
10541 mng_info->ping_exclude_iCCP=MagickFalse;
10542 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10543 mng_info->ping_exclude_oFFs=MagickFalse;
10544 mng_info->ping_exclude_pHYs=MagickFalse;
10545 mng_info->ping_exclude_sRGB=MagickFalse;
10546 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010547 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010548 mng_info->ping_exclude_vpAg=MagickFalse;
10549 mng_info->ping_exclude_zCCP=MagickFalse;
10550 mng_info->ping_exclude_zTXt=MagickFalse;
10551 i--;
10552 }
glennrp2cc891a2010-12-24 13:44:32 +000010553
glennrp03812ae2010-12-24 01:31:34 +000010554 if (LocaleNCompare(value+i,"none",4) == 0)
10555 {
10556 mng_info->ping_exclude_bKGD=MagickTrue;
10557 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010558 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010559 mng_info->ping_exclude_EXIF=MagickTrue;
10560 mng_info->ping_exclude_gAMA=MagickTrue;
10561 mng_info->ping_exclude_iCCP=MagickTrue;
10562 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10563 mng_info->ping_exclude_oFFs=MagickTrue;
10564 mng_info->ping_exclude_pHYs=MagickTrue;
10565 mng_info->ping_exclude_sRGB=MagickTrue;
10566 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010567 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010568 mng_info->ping_exclude_vpAg=MagickTrue;
10569 mng_info->ping_exclude_zCCP=MagickTrue;
10570 mng_info->ping_exclude_zTXt=MagickTrue;
10571 }
glennrp2cc891a2010-12-24 13:44:32 +000010572
glennrp03812ae2010-12-24 01:31:34 +000010573 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10574 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010575
glennrp03812ae2010-12-24 01:31:34 +000010576 if (LocaleNCompare(value+i,"chrm",4) == 0)
10577 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010578
glennrpa0ed0092011-04-18 16:36:29 +000010579 if (LocaleNCompare(value+i,"date",4) == 0)
10580 mng_info->ping_exclude_date=MagickFalse;
10581
glennrp03812ae2010-12-24 01:31:34 +000010582 if (LocaleNCompare(value+i,"exif",4) == 0)
10583 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010584
glennrp03812ae2010-12-24 01:31:34 +000010585 if (LocaleNCompare(value+i,"gama",4) == 0)
10586 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010587
glennrp03812ae2010-12-24 01:31:34 +000010588 if (LocaleNCompare(value+i,"iccp",4) == 0)
10589 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010590
glennrp03812ae2010-12-24 01:31:34 +000010591 /*
10592 if (LocaleNCompare(value+i,"itxt",4) == 0)
10593 mng_info->ping_exclude_iTXt=MagickFalse;
10594 */
glennrp2cc891a2010-12-24 13:44:32 +000010595
glennrp03812ae2010-12-24 01:31:34 +000010596 if (LocaleNCompare(value+i,"gama",4) == 0)
10597 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010598
glennrp03812ae2010-12-24 01:31:34 +000010599 if (LocaleNCompare(value+i,"offs",4) == 0)
10600 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010601
glennrp03812ae2010-12-24 01:31:34 +000010602 if (LocaleNCompare(value+i,"phys",4) == 0)
10603 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010604
glennrpa1e3b7b2010-12-24 16:37:33 +000010605 if (LocaleNCompare(value+i,"srgb",4) == 0)
10606 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010607
glennrp03812ae2010-12-24 01:31:34 +000010608 if (LocaleNCompare(value+i,"text",4) == 0)
10609 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010610
glennrpa1e3b7b2010-12-24 16:37:33 +000010611 if (LocaleNCompare(value+i,"trns",4) == 0)
10612 mng_info->ping_exclude_tRNS=MagickFalse;
10613
glennrp03812ae2010-12-24 01:31:34 +000010614 if (LocaleNCompare(value+i,"vpag",4) == 0)
10615 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010616
glennrp03812ae2010-12-24 01:31:34 +000010617 if (LocaleNCompare(value+i,"zccp",4) == 0)
10618 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010619
glennrp03812ae2010-12-24 01:31:34 +000010620 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10621 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010622
glennrp03812ae2010-12-24 01:31:34 +000010623 }
glennrpce91ed52010-12-23 22:37:49 +000010624 }
glennrp26f37912010-12-23 16:22:42 +000010625 }
10626
glennrp03812ae2010-12-24 01:31:34 +000010627 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010628 {
10629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10630 " Chunks to be excluded from the output PNG:");
10631 if (mng_info->ping_exclude_bKGD != MagickFalse)
10632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633 " bKGD");
10634 if (mng_info->ping_exclude_cHRM != MagickFalse)
10635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010637 if (mng_info->ping_exclude_date != MagickFalse)
10638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639 " date");
glennrp26f37912010-12-23 16:22:42 +000010640 if (mng_info->ping_exclude_EXIF != MagickFalse)
10641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10642 " EXIF");
10643 if (mng_info->ping_exclude_gAMA != MagickFalse)
10644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10645 " gAMA");
10646 if (mng_info->ping_exclude_iCCP != MagickFalse)
10647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10648 " iCCP");
10649/*
10650 if (mng_info->ping_exclude_iTXt != MagickFalse)
10651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652 " iTXt");
10653*/
10654 if (mng_info->ping_exclude_oFFs != MagickFalse)
10655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10656 " oFFs");
10657 if (mng_info->ping_exclude_pHYs != MagickFalse)
10658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10659 " pHYs");
10660 if (mng_info->ping_exclude_sRGB != MagickFalse)
10661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10662 " sRGB");
10663 if (mng_info->ping_exclude_tEXt != MagickFalse)
10664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10665 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010666 if (mng_info->ping_exclude_tRNS != MagickFalse)
10667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10668 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010669 if (mng_info->ping_exclude_vpAg != MagickFalse)
10670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10671 " vpAg");
10672 if (mng_info->ping_exclude_zCCP != MagickFalse)
10673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10674 " zCCP");
10675 if (mng_info->ping_exclude_zTXt != MagickFalse)
10676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10677 " zTXt");
10678 }
10679
glennrpb9cfe272010-12-21 15:08:06 +000010680 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010681
glennrpb9cfe272010-12-21 15:08:06 +000010682 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010683
10684 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010685
cristy3ed852e2009-09-05 21:47:34 +000010686 if (logging != MagickFalse)
10687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010688
cristy3ed852e2009-09-05 21:47:34 +000010689 return(status);
10690}
10691
10692#if defined(JNG_SUPPORTED)
10693
10694/* Write one JNG image */
10695static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10696 const ImageInfo *image_info,Image *image)
10697{
10698 Image
10699 *jpeg_image;
10700
10701 ImageInfo
10702 *jpeg_image_info;
10703
10704 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010705 logging,
cristy3ed852e2009-09-05 21:47:34 +000010706 status;
10707
10708 size_t
10709 length;
10710
10711 unsigned char
10712 *blob,
10713 chunk[80],
10714 *p;
10715
10716 unsigned int
10717 jng_alpha_compression_method,
10718 jng_alpha_sample_depth,
10719 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010720 transparent;
10721
cristybb503372010-05-27 20:51:26 +000010722 size_t
cristy3ed852e2009-09-05 21:47:34 +000010723 jng_quality;
10724
10725 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010726 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010727
10728 blob=(unsigned char *) NULL;
10729 jpeg_image=(Image *) NULL;
10730 jpeg_image_info=(ImageInfo *) NULL;
10731
10732 status=MagickTrue;
10733 transparent=image_info->type==GrayscaleMatteType ||
10734 image_info->type==TrueColorMatteType;
10735 jng_color_type=10;
10736 jng_alpha_sample_depth=0;
10737 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10738 jng_alpha_compression_method=0;
10739
10740 if (image->matte != MagickFalse)
10741 {
10742 /* if any pixels are transparent */
10743 transparent=MagickTrue;
10744 if (image_info->compression==JPEGCompression)
10745 jng_alpha_compression_method=8;
10746 }
10747
10748 if (transparent)
10749 {
10750 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010751
cristy3ed852e2009-09-05 21:47:34 +000010752 /* Create JPEG blob, image, and image_info */
10753 if (logging != MagickFalse)
10754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10755 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010756
cristy3ed852e2009-09-05 21:47:34 +000010757 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010758
cristy3ed852e2009-09-05 21:47:34 +000010759 if (jpeg_image_info == (ImageInfo *) NULL)
10760 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010761
cristy3ed852e2009-09-05 21:47:34 +000010762 if (logging != MagickFalse)
10763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10764 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010765
cristy3ed852e2009-09-05 21:47:34 +000010766 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010767
cristy3ed852e2009-09-05 21:47:34 +000010768 if (jpeg_image == (Image *) NULL)
10769 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010770
cristy3ed852e2009-09-05 21:47:34 +000010771 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10772 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10773 status=NegateImage(jpeg_image,MagickFalse);
10774 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010775
cristy3ed852e2009-09-05 21:47:34 +000010776 if (jng_quality >= 1000)
10777 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010778
cristy3ed852e2009-09-05 21:47:34 +000010779 else
10780 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010781
cristy3ed852e2009-09-05 21:47:34 +000010782 jpeg_image_info->type=GrayscaleType;
10783 (void) SetImageType(jpeg_image,GrayscaleType);
10784 (void) AcquireUniqueFilename(jpeg_image->filename);
10785 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10786 "%s",jpeg_image->filename);
10787 }
10788
10789 /* To do: check bit depth of PNG alpha channel */
10790
10791 /* Check if image is grayscale. */
10792 if (image_info->type != TrueColorMatteType && image_info->type !=
10793 TrueColorType && ImageIsGray(image))
10794 jng_color_type-=2;
10795
10796 if (transparent)
10797 {
10798 if (jng_alpha_compression_method==0)
10799 {
10800 const char
10801 *value;
10802
10803 /* Encode opacity as a grayscale PNG blob */
10804 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10805 &image->exception);
10806 if (logging != MagickFalse)
10807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10808 " Creating PNG blob.");
10809 length=0;
10810
10811 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10812 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10813 jpeg_image_info->interlace=NoInterlace;
10814
10815 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10816 &image->exception);
10817
10818 /* Retrieve sample depth used */
10819 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10820 if (value != (char *) NULL)
10821 jng_alpha_sample_depth= (unsigned int) value[0];
10822 }
10823 else
10824 {
10825 /* Encode opacity as a grayscale JPEG blob */
10826
10827 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10828 &image->exception);
10829
10830 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10831 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10832 jpeg_image_info->interlace=NoInterlace;
10833 if (logging != MagickFalse)
10834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10835 " Creating blob.");
10836 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010837 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010838 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010839
cristy3ed852e2009-09-05 21:47:34 +000010840 if (logging != MagickFalse)
10841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010842 " Successfully read jpeg_image into a blob, length=%.20g.",
10843 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010844
10845 }
10846 /* Destroy JPEG image and image_info */
10847 jpeg_image=DestroyImage(jpeg_image);
10848 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10849 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10850 }
10851
10852 /* Write JHDR chunk */
10853 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10854 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010855 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010856 PNGLong(chunk+4,(png_uint_32) image->columns);
10857 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010858 chunk[12]=jng_color_type;
10859 chunk[13]=8; /* sample depth */
10860 chunk[14]=8; /*jng_image_compression_method */
10861 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10862 chunk[16]=jng_alpha_sample_depth;
10863 chunk[17]=jng_alpha_compression_method;
10864 chunk[18]=0; /*jng_alpha_filter_method */
10865 chunk[19]=0; /*jng_alpha_interlace_method */
10866 (void) WriteBlob(image,20,chunk);
10867 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10868 if (logging != MagickFalse)
10869 {
10870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010871 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010872
cristy3ed852e2009-09-05 21:47:34 +000010873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010874 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010875
cristy3ed852e2009-09-05 21:47:34 +000010876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10877 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010878
cristy3ed852e2009-09-05 21:47:34 +000010879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10880 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010881
cristy3ed852e2009-09-05 21:47:34 +000010882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10883 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010884
cristy3ed852e2009-09-05 21:47:34 +000010885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10886 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010887
cristy3ed852e2009-09-05 21:47:34 +000010888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10889 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010890
cristy3ed852e2009-09-05 21:47:34 +000010891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10892 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010893
cristy3ed852e2009-09-05 21:47:34 +000010894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10895 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010896
cristy3ed852e2009-09-05 21:47:34 +000010897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10898 " JNG alpha interlace:%5d",0);
10899 }
10900
glennrp0fe50b42010-11-16 03:52:51 +000010901 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010902 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010903
10904 /*
10905 Write leading ancillary chunks
10906 */
10907
10908 if (transparent)
10909 {
10910 /*
10911 Write JNG bKGD chunk
10912 */
10913
10914 unsigned char
10915 blue,
10916 green,
10917 red;
10918
cristybb503372010-05-27 20:51:26 +000010919 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010920 num_bytes;
10921
10922 if (jng_color_type == 8 || jng_color_type == 12)
10923 num_bytes=6L;
10924 else
10925 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010926 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010927 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010928 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010929 red=ScaleQuantumToChar(image->background_color.red);
10930 green=ScaleQuantumToChar(image->background_color.green);
10931 blue=ScaleQuantumToChar(image->background_color.blue);
10932 *(chunk+4)=0;
10933 *(chunk+5)=red;
10934 *(chunk+6)=0;
10935 *(chunk+7)=green;
10936 *(chunk+8)=0;
10937 *(chunk+9)=blue;
10938 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10939 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10940 }
10941
10942 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10943 {
10944 /*
10945 Write JNG sRGB chunk
10946 */
10947 (void) WriteBlobMSBULong(image,1L);
10948 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010949 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010950
cristy3ed852e2009-09-05 21:47:34 +000010951 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010952 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010953 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010954 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010955
cristy3ed852e2009-09-05 21:47:34 +000010956 else
glennrpe610a072010-08-05 17:08:46 +000010957 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010958 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010959 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010960
cristy3ed852e2009-09-05 21:47:34 +000010961 (void) WriteBlob(image,5,chunk);
10962 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10963 }
10964 else
10965 {
10966 if (image->gamma != 0.0)
10967 {
10968 /*
10969 Write JNG gAMA chunk
10970 */
10971 (void) WriteBlobMSBULong(image,4L);
10972 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010973 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010974 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010975 (void) WriteBlob(image,8,chunk);
10976 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10977 }
glennrp0fe50b42010-11-16 03:52:51 +000010978
cristy3ed852e2009-09-05 21:47:34 +000010979 if ((mng_info->equal_chrms == MagickFalse) &&
10980 (image->chromaticity.red_primary.x != 0.0))
10981 {
10982 PrimaryInfo
10983 primary;
10984
10985 /*
10986 Write JNG cHRM chunk
10987 */
10988 (void) WriteBlobMSBULong(image,32L);
10989 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010990 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010991 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010992 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10993 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010994 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010995 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10996 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010997 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010998 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10999 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011000 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011001 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11002 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011003 (void) WriteBlob(image,36,chunk);
11004 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11005 }
11006 }
glennrp0fe50b42010-11-16 03:52:51 +000011007
cristy3ed852e2009-09-05 21:47:34 +000011008 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11009 {
11010 /*
11011 Write JNG pHYs chunk
11012 */
11013 (void) WriteBlobMSBULong(image,9L);
11014 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011015 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011016 if (image->units == PixelsPerInchResolution)
11017 {
cristy35ef8242010-06-03 16:24:13 +000011018 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011019 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011020
cristy35ef8242010-06-03 16:24:13 +000011021 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011022 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011023
cristy3ed852e2009-09-05 21:47:34 +000011024 chunk[12]=1;
11025 }
glennrp0fe50b42010-11-16 03:52:51 +000011026
cristy3ed852e2009-09-05 21:47:34 +000011027 else
11028 {
11029 if (image->units == PixelsPerCentimeterResolution)
11030 {
cristy35ef8242010-06-03 16:24:13 +000011031 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011032 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011033
cristy35ef8242010-06-03 16:24:13 +000011034 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011035 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011036
cristy3ed852e2009-09-05 21:47:34 +000011037 chunk[12]=1;
11038 }
glennrp0fe50b42010-11-16 03:52:51 +000011039
cristy3ed852e2009-09-05 21:47:34 +000011040 else
11041 {
cristy35ef8242010-06-03 16:24:13 +000011042 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11043 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011044 chunk[12]=0;
11045 }
11046 }
11047 (void) WriteBlob(image,13,chunk);
11048 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11049 }
11050
11051 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11052 {
11053 /*
11054 Write JNG oFFs chunk
11055 */
11056 (void) WriteBlobMSBULong(image,9L);
11057 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011058 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011059 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11060 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011061 chunk[12]=0;
11062 (void) WriteBlob(image,13,chunk);
11063 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11064 }
11065 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11066 {
11067 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11068 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011069 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011070 PNGLong(chunk+4,(png_uint_32) image->page.width);
11071 PNGLong(chunk+8,(png_uint_32) image->page.height);
11072 chunk[12]=0; /* unit = pixels */
11073 (void) WriteBlob(image,13,chunk);
11074 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11075 }
11076
11077
11078 if (transparent)
11079 {
11080 if (jng_alpha_compression_method==0)
11081 {
cristybb503372010-05-27 20:51:26 +000011082 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011083 i;
11084
cristybb503372010-05-27 20:51:26 +000011085 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011086 len;
11087
11088 /* Write IDAT chunk header */
11089 if (logging != MagickFalse)
11090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011091 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011092 length);
cristy3ed852e2009-09-05 21:47:34 +000011093
11094 /* Copy IDAT chunks */
11095 len=0;
11096 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011097 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011098 {
11099 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11100 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011101
cristy3ed852e2009-09-05 21:47:34 +000011102 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11103 {
11104 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011105 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011106 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011107 (void) WriteBlob(image,(size_t) len+4,p);
11108 (void) WriteBlobMSBULong(image,
11109 crc32(0,p,(uInt) len+4));
11110 }
glennrp0fe50b42010-11-16 03:52:51 +000011111
cristy3ed852e2009-09-05 21:47:34 +000011112 else
11113 {
11114 if (logging != MagickFalse)
11115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011116 " Skipping %c%c%c%c chunk, length=%.20g.",
11117 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011118 }
11119 p+=(8+len);
11120 }
11121 }
11122 else
11123 {
11124 /* Write JDAA chunk header */
11125 if (logging != MagickFalse)
11126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011127 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011128 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011129 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011130 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011131 /* Write JDAT chunk(s) data */
11132 (void) WriteBlob(image,4,chunk);
11133 (void) WriteBlob(image,length,blob);
11134 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11135 (uInt) length));
11136 }
11137 blob=(unsigned char *) RelinquishMagickMemory(blob);
11138 }
11139
11140 /* Encode image as a JPEG blob */
11141 if (logging != MagickFalse)
11142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11143 " Creating jpeg_image_info.");
11144 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11145 if (jpeg_image_info == (ImageInfo *) NULL)
11146 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11147
11148 if (logging != MagickFalse)
11149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11150 " Creating jpeg_image.");
11151
11152 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11153 if (jpeg_image == (Image *) NULL)
11154 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11155 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11156
11157 (void) AcquireUniqueFilename(jpeg_image->filename);
11158 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11159 jpeg_image->filename);
11160
11161 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11162 &image->exception);
11163
11164 if (logging != MagickFalse)
11165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011166 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11167 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011168
11169 if (jng_color_type == 8 || jng_color_type == 12)
11170 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011171
cristy3ed852e2009-09-05 21:47:34 +000011172 jpeg_image_info->quality=jng_quality % 1000;
11173 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11174 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011175
cristy3ed852e2009-09-05 21:47:34 +000011176 if (logging != MagickFalse)
11177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11178 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011179
cristy3ed852e2009-09-05 21:47:34 +000011180 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011181
cristy3ed852e2009-09-05 21:47:34 +000011182 if (logging != MagickFalse)
11183 {
11184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011185 " Successfully read jpeg_image into a blob, length=%.20g.",
11186 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011187
11188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011189 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011190 }
glennrp0fe50b42010-11-16 03:52:51 +000011191
cristy3ed852e2009-09-05 21:47:34 +000011192 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011193 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011194 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011195 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011196 (void) WriteBlob(image,4,chunk);
11197 (void) WriteBlob(image,length,blob);
11198 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11199
11200 jpeg_image=DestroyImage(jpeg_image);
11201 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11202 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11203 blob=(unsigned char *) RelinquishMagickMemory(blob);
11204
11205 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011206 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011207
11208 /* Write IEND chunk */
11209 (void) WriteBlobMSBULong(image,0L);
11210 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011211 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011212 (void) WriteBlob(image,4,chunk);
11213 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11214
11215 if (logging != MagickFalse)
11216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11217 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011218
cristy3ed852e2009-09-05 21:47:34 +000011219 return(status);
11220}
11221
11222
11223/*
11224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11225% %
11226% %
11227% %
11228% W r i t e J N G I m a g e %
11229% %
11230% %
11231% %
11232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11233%
11234% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11235%
11236% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11237%
11238% The format of the WriteJNGImage method is:
11239%
11240% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11241%
11242% A description of each parameter follows:
11243%
11244% o image_info: the image info.
11245%
11246% o image: The image.
11247%
11248%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11249*/
11250static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11251{
11252 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011253 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011254 logging,
cristy3ed852e2009-09-05 21:47:34 +000011255 status;
11256
11257 MngInfo
11258 *mng_info;
11259
cristy3ed852e2009-09-05 21:47:34 +000011260 /*
11261 Open image file.
11262 */
11263 assert(image_info != (const ImageInfo *) NULL);
11264 assert(image_info->signature == MagickSignature);
11265 assert(image != (Image *) NULL);
11266 assert(image->signature == MagickSignature);
11267 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011268 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011269 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11270 if (status == MagickFalse)
11271 return(status);
11272
11273 /*
11274 Allocate a MngInfo structure.
11275 */
11276 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011277 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011278 if (mng_info == (MngInfo *) NULL)
11279 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11280 /*
11281 Initialize members of the MngInfo structure.
11282 */
11283 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11284 mng_info->image=image;
11285 have_mng_structure=MagickTrue;
11286
11287 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11288
11289 status=WriteOneJNGImage(mng_info,image_info,image);
11290 (void) CloseBlob(image);
11291
11292 (void) CatchImageException(image);
11293 MngInfoFreeStruct(mng_info,&have_mng_structure);
11294 if (logging != MagickFalse)
11295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11296 return(status);
11297}
11298#endif
11299
11300
11301
11302static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11303{
11304 const char
11305 *option;
11306
11307 Image
11308 *next_image;
11309
11310 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011311 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011312 status;
11313
glennrp03812ae2010-12-24 01:31:34 +000011314 volatile MagickBooleanType
11315 logging;
11316
cristy3ed852e2009-09-05 21:47:34 +000011317 MngInfo
11318 *mng_info;
11319
11320 int
cristy3ed852e2009-09-05 21:47:34 +000011321 image_count,
11322 need_iterations,
11323 need_matte;
11324
11325 volatile int
11326#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11327 defined(PNG_MNG_FEATURES_SUPPORTED)
11328 need_local_plte,
11329#endif
11330 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011331 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011332 use_global_plte;
11333
cristybb503372010-05-27 20:51:26 +000011334 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011335 i;
11336
11337 unsigned char
11338 chunk[800];
11339
11340 volatile unsigned int
11341 write_jng,
11342 write_mng;
11343
cristybb503372010-05-27 20:51:26 +000011344 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011345 scene;
11346
cristybb503372010-05-27 20:51:26 +000011347 size_t
cristy3ed852e2009-09-05 21:47:34 +000011348 final_delay=0,
11349 initial_delay;
11350
glennrpd5045b42010-03-24 12:40:35 +000011351#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011352 if (image_info->verbose)
11353 printf("Your PNG library (libpng-%s) is rather old.\n",
11354 PNG_LIBPNG_VER_STRING);
11355#endif
11356
11357 /*
11358 Open image file.
11359 */
11360 assert(image_info != (const ImageInfo *) NULL);
11361 assert(image_info->signature == MagickSignature);
11362 assert(image != (Image *) NULL);
11363 assert(image->signature == MagickSignature);
11364 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011365 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011366 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11367 if (status == MagickFalse)
11368 return(status);
11369
11370 /*
11371 Allocate a MngInfo structure.
11372 */
11373 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011374 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011375 if (mng_info == (MngInfo *) NULL)
11376 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11377 /*
11378 Initialize members of the MngInfo structure.
11379 */
11380 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11381 mng_info->image=image;
11382 have_mng_structure=MagickTrue;
11383 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11384
11385 /*
11386 * See if user has requested a specific PNG subformat to be used
11387 * for all of the PNGs in the MNG being written, e.g.,
11388 *
11389 * convert *.png png8:animation.mng
11390 *
11391 * To do: check -define png:bit_depth and png:color_type as well,
11392 * or perhaps use mng:bit_depth and mng:color_type instead for
11393 * global settings.
11394 */
11395
11396 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11397 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11398 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11399
11400 write_jng=MagickFalse;
11401 if (image_info->compression == JPEGCompression)
11402 write_jng=MagickTrue;
11403
11404 mng_info->adjoin=image_info->adjoin &&
11405 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11406
cristy3ed852e2009-09-05 21:47:34 +000011407 if (logging != MagickFalse)
11408 {
11409 /* Log some info about the input */
11410 Image
11411 *p;
11412
11413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11414 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011415
cristy3ed852e2009-09-05 21:47:34 +000011416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011417 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011418
cristy3ed852e2009-09-05 21:47:34 +000011419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11420 " Type: %d",image_info->type);
11421
11422 scene=0;
11423 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11424 {
11425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011426 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011427
cristy3ed852e2009-09-05 21:47:34 +000011428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011429 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011430
cristy3ed852e2009-09-05 21:47:34 +000011431 if (p->matte)
11432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11433 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011434
cristy3ed852e2009-09-05 21:47:34 +000011435 else
11436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11437 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011438
cristy3ed852e2009-09-05 21:47:34 +000011439 if (p->storage_class == PseudoClass)
11440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11441 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011442
cristy3ed852e2009-09-05 21:47:34 +000011443 else
11444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11445 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011446
cristy3ed852e2009-09-05 21:47:34 +000011447 if (p->colors)
11448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011449 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011450
cristy3ed852e2009-09-05 21:47:34 +000011451 else
11452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11453 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011454
cristy3ed852e2009-09-05 21:47:34 +000011455 if (mng_info->adjoin == MagickFalse)
11456 break;
11457 }
11458 }
11459
cristy3ed852e2009-09-05 21:47:34 +000011460 use_global_plte=MagickFalse;
11461 all_images_are_gray=MagickFalse;
11462#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11463 need_local_plte=MagickTrue;
11464#endif
11465 need_defi=MagickFalse;
11466 need_matte=MagickFalse;
11467 mng_info->framing_mode=1;
11468 mng_info->old_framing_mode=1;
11469
11470 if (write_mng)
11471 if (image_info->page != (char *) NULL)
11472 {
11473 /*
11474 Determine image bounding box.
11475 */
11476 SetGeometry(image,&mng_info->page);
11477 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11478 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11479 }
11480 if (write_mng)
11481 {
11482 unsigned int
11483 need_geom;
11484
11485 unsigned short
11486 red,
11487 green,
11488 blue;
11489
11490 mng_info->page=image->page;
11491 need_geom=MagickTrue;
11492 if (mng_info->page.width || mng_info->page.height)
11493 need_geom=MagickFalse;
11494 /*
11495 Check all the scenes.
11496 */
11497 initial_delay=image->delay;
11498 need_iterations=MagickFalse;
11499 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11500 mng_info->equal_physs=MagickTrue,
11501 mng_info->equal_gammas=MagickTrue;
11502 mng_info->equal_srgbs=MagickTrue;
11503 mng_info->equal_backgrounds=MagickTrue;
11504 image_count=0;
11505#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11506 defined(PNG_MNG_FEATURES_SUPPORTED)
11507 all_images_are_gray=MagickTrue;
11508 mng_info->equal_palettes=MagickFalse;
11509 need_local_plte=MagickFalse;
11510#endif
11511 for (next_image=image; next_image != (Image *) NULL; )
11512 {
11513 if (need_geom)
11514 {
11515 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11516 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011517
cristy3ed852e2009-09-05 21:47:34 +000011518 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11519 mng_info->page.height=next_image->rows+next_image->page.y;
11520 }
glennrp0fe50b42010-11-16 03:52:51 +000011521
cristy3ed852e2009-09-05 21:47:34 +000011522 if (next_image->page.x || next_image->page.y)
11523 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011524
cristy3ed852e2009-09-05 21:47:34 +000011525 if (next_image->matte)
11526 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011527
cristy3ed852e2009-09-05 21:47:34 +000011528 if ((int) next_image->dispose >= BackgroundDispose)
11529 if (next_image->matte || next_image->page.x || next_image->page.y ||
11530 ((next_image->columns < mng_info->page.width) &&
11531 (next_image->rows < mng_info->page.height)))
11532 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011533
cristy3ed852e2009-09-05 21:47:34 +000011534 if (next_image->iterations)
11535 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011536
cristy3ed852e2009-09-05 21:47:34 +000011537 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011538
cristy3ed852e2009-09-05 21:47:34 +000011539 if (final_delay != initial_delay || final_delay > 1UL*
11540 next_image->ticks_per_second)
11541 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011542
cristy3ed852e2009-09-05 21:47:34 +000011543#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11544 defined(PNG_MNG_FEATURES_SUPPORTED)
11545 /*
11546 check for global palette possibility.
11547 */
11548 if (image->matte != MagickFalse)
11549 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011550
cristy3ed852e2009-09-05 21:47:34 +000011551 if (need_local_plte == 0)
11552 {
11553 if (ImageIsGray(image) == MagickFalse)
11554 all_images_are_gray=MagickFalse;
11555 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11556 if (use_global_plte == 0)
11557 use_global_plte=mng_info->equal_palettes;
11558 need_local_plte=!mng_info->equal_palettes;
11559 }
11560#endif
11561 if (GetNextImageInList(next_image) != (Image *) NULL)
11562 {
11563 if (next_image->background_color.red !=
11564 next_image->next->background_color.red ||
11565 next_image->background_color.green !=
11566 next_image->next->background_color.green ||
11567 next_image->background_color.blue !=
11568 next_image->next->background_color.blue)
11569 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011570
cristy3ed852e2009-09-05 21:47:34 +000011571 if (next_image->gamma != next_image->next->gamma)
11572 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011573
cristy3ed852e2009-09-05 21:47:34 +000011574 if (next_image->rendering_intent !=
11575 next_image->next->rendering_intent)
11576 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011577
cristy3ed852e2009-09-05 21:47:34 +000011578 if ((next_image->units != next_image->next->units) ||
11579 (next_image->x_resolution != next_image->next->x_resolution) ||
11580 (next_image->y_resolution != next_image->next->y_resolution))
11581 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011582
cristy3ed852e2009-09-05 21:47:34 +000011583 if (mng_info->equal_chrms)
11584 {
11585 if (next_image->chromaticity.red_primary.x !=
11586 next_image->next->chromaticity.red_primary.x ||
11587 next_image->chromaticity.red_primary.y !=
11588 next_image->next->chromaticity.red_primary.y ||
11589 next_image->chromaticity.green_primary.x !=
11590 next_image->next->chromaticity.green_primary.x ||
11591 next_image->chromaticity.green_primary.y !=
11592 next_image->next->chromaticity.green_primary.y ||
11593 next_image->chromaticity.blue_primary.x !=
11594 next_image->next->chromaticity.blue_primary.x ||
11595 next_image->chromaticity.blue_primary.y !=
11596 next_image->next->chromaticity.blue_primary.y ||
11597 next_image->chromaticity.white_point.x !=
11598 next_image->next->chromaticity.white_point.x ||
11599 next_image->chromaticity.white_point.y !=
11600 next_image->next->chromaticity.white_point.y)
11601 mng_info->equal_chrms=MagickFalse;
11602 }
11603 }
11604 image_count++;
11605 next_image=GetNextImageInList(next_image);
11606 }
11607 if (image_count < 2)
11608 {
11609 mng_info->equal_backgrounds=MagickFalse;
11610 mng_info->equal_chrms=MagickFalse;
11611 mng_info->equal_gammas=MagickFalse;
11612 mng_info->equal_srgbs=MagickFalse;
11613 mng_info->equal_physs=MagickFalse;
11614 use_global_plte=MagickFalse;
11615#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11616 need_local_plte=MagickTrue;
11617#endif
11618 need_iterations=MagickFalse;
11619 }
glennrp0fe50b42010-11-16 03:52:51 +000011620
cristy3ed852e2009-09-05 21:47:34 +000011621 if (mng_info->need_fram == MagickFalse)
11622 {
11623 /*
11624 Only certain framing rates 100/n are exactly representable without
11625 the FRAM chunk but we'll allow some slop in VLC files
11626 */
11627 if (final_delay == 0)
11628 {
11629 if (need_iterations != MagickFalse)
11630 {
11631 /*
11632 It's probably a GIF with loop; don't run it *too* fast.
11633 */
glennrp02617122010-07-28 13:07:35 +000011634 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011635 {
11636 final_delay=10;
11637 (void) ThrowMagickException(&image->exception,
11638 GetMagickModule(),CoderWarning,
11639 "input has zero delay between all frames; assuming",
11640 " 10 cs `%s'","");
11641 }
cristy3ed852e2009-09-05 21:47:34 +000011642 }
11643 else
11644 mng_info->ticks_per_second=0;
11645 }
11646 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011647 mng_info->ticks_per_second=(png_uint_32)
11648 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011649 if (final_delay > 50)
11650 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011651
cristy3ed852e2009-09-05 21:47:34 +000011652 if (final_delay > 75)
11653 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011654
cristy3ed852e2009-09-05 21:47:34 +000011655 if (final_delay > 125)
11656 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011657
cristy3ed852e2009-09-05 21:47:34 +000011658 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11659 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11660 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11661 1UL*image->ticks_per_second))
11662 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11663 }
glennrp0fe50b42010-11-16 03:52:51 +000011664
cristy3ed852e2009-09-05 21:47:34 +000011665 if (mng_info->need_fram != MagickFalse)
11666 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11667 /*
11668 If pseudocolor, we should also check to see if all the
11669 palettes are identical and write a global PLTE if they are.
11670 ../glennrp Feb 99.
11671 */
11672 /*
11673 Write the MNG version 1.0 signature and MHDR chunk.
11674 */
11675 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11676 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11677 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011678 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011679 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11680 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011681 PNGLong(chunk+12,mng_info->ticks_per_second);
11682 PNGLong(chunk+16,0L); /* layer count=unknown */
11683 PNGLong(chunk+20,0L); /* frame count=unknown */
11684 PNGLong(chunk+24,0L); /* play time=unknown */
11685 if (write_jng)
11686 {
11687 if (need_matte)
11688 {
11689 if (need_defi || mng_info->need_fram || use_global_plte)
11690 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011691
cristy3ed852e2009-09-05 21:47:34 +000011692 else
11693 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11694 }
glennrp0fe50b42010-11-16 03:52:51 +000011695
cristy3ed852e2009-09-05 21:47:34 +000011696 else
11697 {
11698 if (need_defi || mng_info->need_fram || use_global_plte)
11699 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011700
cristy3ed852e2009-09-05 21:47:34 +000011701 else
11702 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11703 }
11704 }
glennrp0fe50b42010-11-16 03:52:51 +000011705
cristy3ed852e2009-09-05 21:47:34 +000011706 else
11707 {
11708 if (need_matte)
11709 {
11710 if (need_defi || mng_info->need_fram || use_global_plte)
11711 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011712
cristy3ed852e2009-09-05 21:47:34 +000011713 else
11714 PNGLong(chunk+28,9L); /* simplicity=VLC */
11715 }
glennrp0fe50b42010-11-16 03:52:51 +000011716
cristy3ed852e2009-09-05 21:47:34 +000011717 else
11718 {
11719 if (need_defi || mng_info->need_fram || use_global_plte)
11720 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011721
cristy3ed852e2009-09-05 21:47:34 +000011722 else
11723 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11724 }
11725 }
11726 (void) WriteBlob(image,32,chunk);
11727 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11728 option=GetImageOption(image_info,"mng:need-cacheoff");
11729 if (option != (const char *) NULL)
11730 {
11731 size_t
11732 length;
11733
11734 /*
11735 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11736 */
11737 PNGType(chunk,mng_nEED);
11738 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011739 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011740 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011741 length+=4;
11742 (void) WriteBlob(image,length,chunk);
11743 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11744 }
11745 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11746 (GetNextImageInList(image) != (Image *) NULL) &&
11747 (image->iterations != 1))
11748 {
11749 /*
11750 Write MNG TERM chunk
11751 */
11752 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11753 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011754 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011755 chunk[4]=3; /* repeat animation */
11756 chunk[5]=0; /* show last frame when done */
11757 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11758 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011759
cristy3ed852e2009-09-05 21:47:34 +000011760 if (image->iterations == 0)
11761 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011762
cristy3ed852e2009-09-05 21:47:34 +000011763 else
11764 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011765
cristy3ed852e2009-09-05 21:47:34 +000011766 if (logging != MagickFalse)
11767 {
11768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011769 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11770 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011771
cristy3ed852e2009-09-05 21:47:34 +000011772 if (image->iterations == 0)
11773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011774 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011775
cristy3ed852e2009-09-05 21:47:34 +000011776 else
11777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011778 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011779 }
11780 (void) WriteBlob(image,14,chunk);
11781 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11782 }
11783 /*
11784 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11785 */
11786 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11787 mng_info->equal_srgbs)
11788 {
11789 /*
11790 Write MNG sRGB chunk
11791 */
11792 (void) WriteBlobMSBULong(image,1L);
11793 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011794 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011795
cristy3ed852e2009-09-05 21:47:34 +000011796 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011797 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011798 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011799 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011800
cristy3ed852e2009-09-05 21:47:34 +000011801 else
glennrpe610a072010-08-05 17:08:46 +000011802 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011803 Magick_RenderingIntent_to_PNG_RenderingIntent(
11804 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011805
cristy3ed852e2009-09-05 21:47:34 +000011806 (void) WriteBlob(image,5,chunk);
11807 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11808 mng_info->have_write_global_srgb=MagickTrue;
11809 }
glennrp0fe50b42010-11-16 03:52:51 +000011810
cristy3ed852e2009-09-05 21:47:34 +000011811 else
11812 {
11813 if (image->gamma && mng_info->equal_gammas)
11814 {
11815 /*
11816 Write MNG gAMA chunk
11817 */
11818 (void) WriteBlobMSBULong(image,4L);
11819 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011820 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011821 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011822 (void) WriteBlob(image,8,chunk);
11823 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11824 mng_info->have_write_global_gama=MagickTrue;
11825 }
11826 if (mng_info->equal_chrms)
11827 {
11828 PrimaryInfo
11829 primary;
11830
11831 /*
11832 Write MNG cHRM chunk
11833 */
11834 (void) WriteBlobMSBULong(image,32L);
11835 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011836 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011837 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011838 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11839 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011840 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011841 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11842 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011843 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011844 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11845 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011846 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011847 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11848 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011849 (void) WriteBlob(image,36,chunk);
11850 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11851 mng_info->have_write_global_chrm=MagickTrue;
11852 }
11853 }
11854 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11855 {
11856 /*
11857 Write MNG pHYs chunk
11858 */
11859 (void) WriteBlobMSBULong(image,9L);
11860 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011861 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011862
cristy3ed852e2009-09-05 21:47:34 +000011863 if (image->units == PixelsPerInchResolution)
11864 {
cristy35ef8242010-06-03 16:24:13 +000011865 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011866 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011867
cristy35ef8242010-06-03 16:24:13 +000011868 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011869 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011870
cristy3ed852e2009-09-05 21:47:34 +000011871 chunk[12]=1;
11872 }
glennrp0fe50b42010-11-16 03:52:51 +000011873
cristy3ed852e2009-09-05 21:47:34 +000011874 else
11875 {
11876 if (image->units == PixelsPerCentimeterResolution)
11877 {
cristy35ef8242010-06-03 16:24:13 +000011878 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011879 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011880
cristy35ef8242010-06-03 16:24:13 +000011881 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011882 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011883
cristy3ed852e2009-09-05 21:47:34 +000011884 chunk[12]=1;
11885 }
glennrp0fe50b42010-11-16 03:52:51 +000011886
cristy3ed852e2009-09-05 21:47:34 +000011887 else
11888 {
cristy35ef8242010-06-03 16:24:13 +000011889 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11890 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011891 chunk[12]=0;
11892 }
11893 }
11894 (void) WriteBlob(image,13,chunk);
11895 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11896 }
11897 /*
11898 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11899 or does not cover the entire frame.
11900 */
11901 if (write_mng && (image->matte || image->page.x > 0 ||
11902 image->page.y > 0 || (image->page.width &&
11903 (image->page.width+image->page.x < mng_info->page.width))
11904 || (image->page.height && (image->page.height+image->page.y
11905 < mng_info->page.height))))
11906 {
11907 (void) WriteBlobMSBULong(image,6L);
11908 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011909 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011910 red=ScaleQuantumToShort(image->background_color.red);
11911 green=ScaleQuantumToShort(image->background_color.green);
11912 blue=ScaleQuantumToShort(image->background_color.blue);
11913 PNGShort(chunk+4,red);
11914 PNGShort(chunk+6,green);
11915 PNGShort(chunk+8,blue);
11916 (void) WriteBlob(image,10,chunk);
11917 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11918 if (mng_info->equal_backgrounds)
11919 {
11920 (void) WriteBlobMSBULong(image,6L);
11921 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011922 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011923 (void) WriteBlob(image,10,chunk);
11924 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11925 }
11926 }
11927
11928#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11929 if ((need_local_plte == MagickFalse) &&
11930 (image->storage_class == PseudoClass) &&
11931 (all_images_are_gray == MagickFalse))
11932 {
cristybb503372010-05-27 20:51:26 +000011933 size_t
cristy3ed852e2009-09-05 21:47:34 +000011934 data_length;
11935
11936 /*
11937 Write MNG PLTE chunk
11938 */
11939 data_length=3*image->colors;
11940 (void) WriteBlobMSBULong(image,data_length);
11941 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011942 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011943
cristybb503372010-05-27 20:51:26 +000011944 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011945 {
11946 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11947 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11948 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11949 }
glennrp0fe50b42010-11-16 03:52:51 +000011950
cristy3ed852e2009-09-05 21:47:34 +000011951 (void) WriteBlob(image,data_length+4,chunk);
11952 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11953 mng_info->have_write_global_plte=MagickTrue;
11954 }
11955#endif
11956 }
11957 scene=0;
11958 mng_info->delay=0;
11959#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11960 defined(PNG_MNG_FEATURES_SUPPORTED)
11961 mng_info->equal_palettes=MagickFalse;
11962#endif
11963 do
11964 {
11965 if (mng_info->adjoin)
11966 {
11967#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11968 defined(PNG_MNG_FEATURES_SUPPORTED)
11969 /*
11970 If we aren't using a global palette for the entire MNG, check to
11971 see if we can use one for two or more consecutive images.
11972 */
11973 if (need_local_plte && use_global_plte && !all_images_are_gray)
11974 {
11975 if (mng_info->IsPalette)
11976 {
11977 /*
11978 When equal_palettes is true, this image has the same palette
11979 as the previous PseudoClass image
11980 */
11981 mng_info->have_write_global_plte=mng_info->equal_palettes;
11982 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11983 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11984 {
11985 /*
11986 Write MNG PLTE chunk
11987 */
cristybb503372010-05-27 20:51:26 +000011988 size_t
cristy3ed852e2009-09-05 21:47:34 +000011989 data_length;
11990
11991 data_length=3*image->colors;
11992 (void) WriteBlobMSBULong(image,data_length);
11993 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011994 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011995
cristybb503372010-05-27 20:51:26 +000011996 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011997 {
11998 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11999 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12000 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12001 }
glennrp0fe50b42010-11-16 03:52:51 +000012002
cristy3ed852e2009-09-05 21:47:34 +000012003 (void) WriteBlob(image,data_length+4,chunk);
12004 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12005 (uInt) (data_length+4)));
12006 mng_info->have_write_global_plte=MagickTrue;
12007 }
12008 }
12009 else
12010 mng_info->have_write_global_plte=MagickFalse;
12011 }
12012#endif
12013 if (need_defi)
12014 {
cristybb503372010-05-27 20:51:26 +000012015 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012016 previous_x,
12017 previous_y;
12018
12019 if (scene)
12020 {
12021 previous_x=mng_info->page.x;
12022 previous_y=mng_info->page.y;
12023 }
12024 else
12025 {
12026 previous_x=0;
12027 previous_y=0;
12028 }
12029 mng_info->page=image->page;
12030 if ((mng_info->page.x != previous_x) ||
12031 (mng_info->page.y != previous_y))
12032 {
12033 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12034 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012035 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012036 chunk[4]=0; /* object 0 MSB */
12037 chunk[5]=0; /* object 0 LSB */
12038 chunk[6]=0; /* visible */
12039 chunk[7]=0; /* abstract */
12040 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12041 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12042 (void) WriteBlob(image,16,chunk);
12043 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12044 }
12045 }
12046 }
12047
12048 mng_info->write_mng=write_mng;
12049
12050 if ((int) image->dispose >= 3)
12051 mng_info->framing_mode=3;
12052
12053 if (mng_info->need_fram && mng_info->adjoin &&
12054 ((image->delay != mng_info->delay) ||
12055 (mng_info->framing_mode != mng_info->old_framing_mode)))
12056 {
12057 if (image->delay == mng_info->delay)
12058 {
12059 /*
12060 Write a MNG FRAM chunk with the new framing mode.
12061 */
12062 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12063 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012064 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012065 chunk[4]=(unsigned char) mng_info->framing_mode;
12066 (void) WriteBlob(image,5,chunk);
12067 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12068 }
12069 else
12070 {
12071 /*
12072 Write a MNG FRAM chunk with the delay.
12073 */
12074 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12075 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012076 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012077 chunk[4]=(unsigned char) mng_info->framing_mode;
12078 chunk[5]=0; /* frame name separator (no name) */
12079 chunk[6]=2; /* flag for changing default delay */
12080 chunk[7]=0; /* flag for changing frame timeout */
12081 chunk[8]=0; /* flag for changing frame clipping */
12082 chunk[9]=0; /* flag for changing frame sync_id */
12083 PNGLong(chunk+10,(png_uint_32)
12084 ((mng_info->ticks_per_second*
12085 image->delay)/MagickMax(image->ticks_per_second,1)));
12086 (void) WriteBlob(image,14,chunk);
12087 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012088 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012089 }
12090 mng_info->old_framing_mode=mng_info->framing_mode;
12091 }
12092
12093#if defined(JNG_SUPPORTED)
12094 if (image_info->compression == JPEGCompression)
12095 {
12096 ImageInfo
12097 *write_info;
12098
12099 if (logging != MagickFalse)
12100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12101 " Writing JNG object.");
12102 /* To do: specify the desired alpha compression method. */
12103 write_info=CloneImageInfo(image_info);
12104 write_info->compression=UndefinedCompression;
12105 status=WriteOneJNGImage(mng_info,write_info,image);
12106 write_info=DestroyImageInfo(write_info);
12107 }
12108 else
12109#endif
12110 {
12111 if (logging != MagickFalse)
12112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12113 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012114
glennrpb9cfe272010-12-21 15:08:06 +000012115 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012116 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012117
12118 /* We don't want any ancillary chunks written */
12119 mng_info->ping_exclude_bKGD=MagickTrue;
12120 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012121 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012122 mng_info->ping_exclude_EXIF=MagickTrue;
12123 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012124 mng_info->ping_exclude_iCCP=MagickTrue;
12125 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12126 mng_info->ping_exclude_oFFs=MagickTrue;
12127 mng_info->ping_exclude_pHYs=MagickTrue;
12128 mng_info->ping_exclude_sRGB=MagickTrue;
12129 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012130 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012131 mng_info->ping_exclude_vpAg=MagickTrue;
12132 mng_info->ping_exclude_zCCP=MagickTrue;
12133 mng_info->ping_exclude_zTXt=MagickTrue;
12134
cristy3ed852e2009-09-05 21:47:34 +000012135 status=WriteOnePNGImage(mng_info,image_info,image);
12136 }
12137
12138 if (status == MagickFalse)
12139 {
12140 MngInfoFreeStruct(mng_info,&have_mng_structure);
12141 (void) CloseBlob(image);
12142 return(MagickFalse);
12143 }
12144 (void) CatchImageException(image);
12145 if (GetNextImageInList(image) == (Image *) NULL)
12146 break;
12147 image=SyncNextImageInList(image);
12148 status=SetImageProgress(image,SaveImagesTag,scene++,
12149 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012150
cristy3ed852e2009-09-05 21:47:34 +000012151 if (status == MagickFalse)
12152 break;
glennrp0fe50b42010-11-16 03:52:51 +000012153
cristy3ed852e2009-09-05 21:47:34 +000012154 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012155
cristy3ed852e2009-09-05 21:47:34 +000012156 if (write_mng)
12157 {
12158 while (GetPreviousImageInList(image) != (Image *) NULL)
12159 image=GetPreviousImageInList(image);
12160 /*
12161 Write the MEND chunk.
12162 */
12163 (void) WriteBlobMSBULong(image,0x00000000L);
12164 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012165 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012166 (void) WriteBlob(image,4,chunk);
12167 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12168 }
12169 /*
12170 Relinquish resources.
12171 */
12172 (void) CloseBlob(image);
12173 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012174
cristy3ed852e2009-09-05 21:47:34 +000012175 if (logging != MagickFalse)
12176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012177
cristy3ed852e2009-09-05 21:47:34 +000012178 return(MagickTrue);
12179}
glennrpd5045b42010-03-24 12:40:35 +000012180#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012181
cristy3ed852e2009-09-05 21:47:34 +000012182static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12183{
12184 image=image;
12185 printf("Your PNG library is too old: You have libpng-%s\n",
12186 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012187
cristy3ed852e2009-09-05 21:47:34 +000012188 ThrowBinaryException(CoderError,"PNG library is too old",
12189 image_info->filename);
12190}
glennrp39992b42010-11-14 00:03:43 +000012191
cristy3ed852e2009-09-05 21:47:34 +000012192static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12193{
12194 return(WritePNGImage(image_info,image));
12195}
glennrpd5045b42010-03-24 12:40:35 +000012196#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012197#endif