blob: aef59edbb1b4ff25b6a060deed865e89a7f9ccda [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++)
cristy80ac8b92010-05-08 13:36:57 +00002757 indexes[x]=(IndexPacket) (*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,
2765 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 {
2903 q->opacity=(Quantum) OpaqueOpacity;
2904 }
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++)
glennrp7c7b3152011-04-26 04:01:27 +00003977 q->opacity=(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,
5909 1,exception);
5910 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);
6014 pixels=q+(image->columns-length);
6015 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00006016
cristybb503372010-05-27 20:51:26 +00006017 for (x=(ssize_t) (image->columns-length);
6018 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006019 {
glennrp7c7b3152011-04-26 04:01:27 +00006020 /* To do: Rewrite using Get/Set***PixelComponent() */
6021
cristybb503372010-05-27 20:51:26 +00006022 if (x == (ssize_t) (image->columns-length))
6023 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006024
cristybb503372010-05-27 20:51:26 +00006025 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6026 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006027
cristybb503372010-05-27 20:51:26 +00006028 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6029 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006030
cristybb503372010-05-27 20:51:26 +00006031 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006032 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006033
cristy3ed852e2009-09-05 21:47:34 +00006034 else
cristybb503372010-05-27 20:51:26 +00006035 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006036
cristy3ed852e2009-09-05 21:47:34 +00006037 for (i=0; i < m; i++)
6038 {
6039 if (magn_methx <= 1)
6040 {
6041 /* replicate previous */
6042 *q=(*pixels);
6043 }
glennrp47b9dd52010-11-24 18:12:06 +00006044
cristy3ed852e2009-09-05 21:47:34 +00006045 else if (magn_methx == 2 || magn_methx == 4)
6046 {
6047 if (i == 0)
6048 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006049
cristy3ed852e2009-09-05 21:47:34 +00006050 else
6051 {
6052 /* Interpolate */
6053 (*q).red=(QM) ((2*i*((*n).red
6054 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00006055 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00006056 (*q).green=(QM) ((2*i*((*n).green
6057 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00006058 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00006059 (*q).blue=(QM) ((2*i*((*n).blue
6060 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00006061 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00006062 if (image->matte != MagickFalse)
6063 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006064 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006065 +(*pixels).opacity);
6066 }
glennrp47b9dd52010-11-24 18:12:06 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 if (magn_methx == 4)
6069 {
6070 /* Replicate nearest */
6071 if (i <= ((m+1) << 1))
6072 (*q).opacity=(*pixels).opacity+0;
6073 else
6074 (*q).opacity=(*n).opacity+0;
6075 }
6076 }
glennrp47b9dd52010-11-24 18:12:06 +00006077
cristy3ed852e2009-09-05 21:47:34 +00006078 else /* if (magn_methx == 3 || magn_methx == 5) */
6079 {
6080 /* Replicate nearest */
6081 if (i <= ((m+1) << 1))
6082 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006083
cristy3ed852e2009-09-05 21:47:34 +00006084 else
6085 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00006086
cristy3ed852e2009-09-05 21:47:34 +00006087 if (magn_methx == 5)
6088 {
6089 /* Interpolate */
6090 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006091 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006092 +(*pixels).opacity);
6093 }
6094 }
6095 q++;
6096 }
6097 n++;
6098 p++;
6099 }
glennrp47b9dd52010-11-24 18:12:06 +00006100
cristy3ed852e2009-09-05 21:47:34 +00006101 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6102 break;
6103 }
glennrp3faa9a32011-04-23 14:00:25 +00006104#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006105 if (magn_methx != 1 || magn_methy != 1)
6106 {
6107 /*
6108 Rescale pixels to Quantum
6109 */
cristybb503372010-05-27 20:51:26 +00006110 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006111 {
6112 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006113
cristybb503372010-05-27 20:51:26 +00006114 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006115 {
glennrp7c7b3152011-04-26 04:01:27 +00006116 SetRedPixelComponent(q,ScaleShortToQuantum(
6117 GetRedPixelComponent(q));
6118 SetGreenPixelComponent(q,ScaleShortToQuantum(
6119 GetGreenPixelComponent(q));
6120 SetBluePixelComponent(q,ScaleShortToQuantum(
6121 GetBluePixelComponent(q));
6122 SetOpacityPixelComponent(q,ScaleShortToQuantum(
6123 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00006124 q++;
6125 }
glennrp47b9dd52010-11-24 18:12:06 +00006126
cristy3ed852e2009-09-05 21:47:34 +00006127 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6128 break;
6129 }
6130 }
6131#endif
6132 if (logging != MagickFalse)
6133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6134 " Finished MAGN processing");
6135 }
6136 }
6137
6138 /*
6139 Crop_box is with respect to the upper left corner of the MNG.
6140 */
6141 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6142 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6143 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6144 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6145 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6146 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6147 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6148 if ((crop_box.left != (mng_info->image_box.left
6149 +mng_info->x_off[object_id])) ||
6150 (crop_box.right != (mng_info->image_box.right
6151 +mng_info->x_off[object_id])) ||
6152 (crop_box.top != (mng_info->image_box.top
6153 +mng_info->y_off[object_id])) ||
6154 (crop_box.bottom != (mng_info->image_box.bottom
6155 +mng_info->y_off[object_id])))
6156 {
6157 if (logging != MagickFalse)
6158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6159 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006160
cristy3ed852e2009-09-05 21:47:34 +00006161 if ((crop_box.left < crop_box.right) &&
6162 (crop_box.top < crop_box.bottom))
6163 {
6164 Image
6165 *im;
6166
6167 RectangleInfo
6168 crop_info;
6169
6170 /*
6171 Crop_info is with respect to the upper left corner of
6172 the image.
6173 */
6174 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6175 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006176 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6177 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006178 image->page.width=image->columns;
6179 image->page.height=image->rows;
6180 image->page.x=0;
6181 image->page.y=0;
6182 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006183
cristy3ed852e2009-09-05 21:47:34 +00006184 if (im != (Image *) NULL)
6185 {
6186 image->columns=im->columns;
6187 image->rows=im->rows;
6188 im=DestroyImage(im);
6189 image->page.width=image->columns;
6190 image->page.height=image->rows;
6191 image->page.x=crop_box.left;
6192 image->page.y=crop_box.top;
6193 }
6194 }
glennrp47b9dd52010-11-24 18:12:06 +00006195
cristy3ed852e2009-09-05 21:47:34 +00006196 else
6197 {
6198 /*
6199 No pixels in crop area. The MNG spec still requires
6200 a layer, though, so make a single transparent pixel in
6201 the top left corner.
6202 */
6203 image->columns=1;
6204 image->rows=1;
6205 image->colors=2;
6206 (void) SetImageBackgroundColor(image);
6207 image->page.width=1;
6208 image->page.height=1;
6209 image->page.x=0;
6210 image->page.y=0;
6211 }
6212 }
6213#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6214 image=mng_info->image;
6215#endif
6216 }
6217
glennrp2b013e42010-11-24 16:55:50 +00006218#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6219 /* PNG does not handle depths greater than 16 so reduce it even
6220 * if lossy
6221 */
6222 if (image->depth > 16)
6223 image->depth=16;
6224#endif
6225
glennrp3faa9a32011-04-23 14:00:25 +00006226#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006227 if (LosslessReduceDepthOK(image) != MagickFalse)
6228 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006229#endif
glennrpd6afd542010-11-19 01:53:05 +00006230
cristy3ed852e2009-09-05 21:47:34 +00006231 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006232
cristy3ed852e2009-09-05 21:47:34 +00006233 if (image_info->number_scenes != 0)
6234 {
6235 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006236 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006237 break;
6238 }
glennrpd6afd542010-11-19 01:53:05 +00006239
cristy3ed852e2009-09-05 21:47:34 +00006240 if (logging != MagickFalse)
6241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6242 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006243
cristy3ed852e2009-09-05 21:47:34 +00006244 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006245
cristy3ed852e2009-09-05 21:47:34 +00006246 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006247
cristy3ed852e2009-09-05 21:47:34 +00006248 if (logging != MagickFalse)
6249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6250 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006251
cristy3ed852e2009-09-05 21:47:34 +00006252#if defined(MNG_INSERT_LAYERS)
6253 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6254 (mng_info->mng_height))
6255 {
6256 /*
6257 Insert a background layer if nothing else was found.
6258 */
6259 if (logging != MagickFalse)
6260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6261 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006262
cristy3ed852e2009-09-05 21:47:34 +00006263 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6264 {
6265 /*
6266 Allocate next image structure.
6267 */
6268 AcquireNextImage(image_info,image);
6269 if (GetNextImageInList(image) == (Image *) NULL)
6270 {
6271 image=DestroyImageList(image);
6272 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006273
cristy3ed852e2009-09-05 21:47:34 +00006274 if (logging != MagickFalse)
6275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6276 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006277
cristy3ed852e2009-09-05 21:47:34 +00006278 return((Image *) NULL);
6279 }
6280 image=SyncNextImageInList(image);
6281 }
6282 image->columns=mng_info->mng_width;
6283 image->rows=mng_info->mng_height;
6284 image->page.width=mng_info->mng_width;
6285 image->page.height=mng_info->mng_height;
6286 image->page.x=0;
6287 image->page.y=0;
6288 image->background_color=mng_background_color;
6289 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006290
cristy3ed852e2009-09-05 21:47:34 +00006291 if (image_info->ping == MagickFalse)
6292 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006293
cristy3ed852e2009-09-05 21:47:34 +00006294 mng_info->image_found++;
6295 }
6296#endif
6297 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006298
cristy3ed852e2009-09-05 21:47:34 +00006299 if (mng_iterations == 1)
6300 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006301
cristy3ed852e2009-09-05 21:47:34 +00006302 while (GetPreviousImageInList(image) != (Image *) NULL)
6303 {
6304 image_count++;
6305 if (image_count > 10*mng_info->image_found)
6306 {
6307 if (logging != MagickFalse)
6308 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006309
cristy3ed852e2009-09-05 21:47:34 +00006310 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6311 CoderError,"Linked list is corrupted, beginning of list not found",
6312 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006313
cristy3ed852e2009-09-05 21:47:34 +00006314 return((Image *) NULL);
6315 }
glennrp0fe50b42010-11-16 03:52:51 +00006316
cristy3ed852e2009-09-05 21:47:34 +00006317 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006318
cristy3ed852e2009-09-05 21:47:34 +00006319 if (GetNextImageInList(image) == (Image *) NULL)
6320 {
6321 if (logging != MagickFalse)
6322 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006323
cristy3ed852e2009-09-05 21:47:34 +00006324 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6325 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6326 image_info->filename);
6327 }
6328 }
glennrp47b9dd52010-11-24 18:12:06 +00006329
cristy3ed852e2009-09-05 21:47:34 +00006330 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6331 GetNextImageInList(image) ==
6332 (Image *) NULL)
6333 {
6334 if (logging != MagickFalse)
6335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6336 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006337
cristy3ed852e2009-09-05 21:47:34 +00006338 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6339 CoderError,"image->next for first image is NULL but shouldn't be.",
6340 "`%s'",image_info->filename);
6341 }
glennrp47b9dd52010-11-24 18:12:06 +00006342
cristy3ed852e2009-09-05 21:47:34 +00006343 if (mng_info->image_found == 0)
6344 {
6345 if (logging != MagickFalse)
6346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6347 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006348
cristy3ed852e2009-09-05 21:47:34 +00006349 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6350 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006351
cristy3ed852e2009-09-05 21:47:34 +00006352 if (image != (Image *) NULL)
6353 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006354
cristy3ed852e2009-09-05 21:47:34 +00006355 MngInfoFreeStruct(mng_info,&have_mng_structure);
6356 return((Image *) NULL);
6357 }
6358
6359 if (mng_info->ticks_per_second)
6360 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6361 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006362
cristy3ed852e2009-09-05 21:47:34 +00006363 else
6364 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006365
cristy3ed852e2009-09-05 21:47:34 +00006366 /* Find final nonzero image delay */
6367 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006368
cristy3ed852e2009-09-05 21:47:34 +00006369 while (GetNextImageInList(image) != (Image *) NULL)
6370 {
6371 if (image->delay)
6372 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006373
cristy3ed852e2009-09-05 21:47:34 +00006374 image=GetNextImageInList(image);
6375 }
glennrp0fe50b42010-11-16 03:52:51 +00006376
cristy3ed852e2009-09-05 21:47:34 +00006377 if (final_delay < final_image_delay)
6378 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006379
cristy3ed852e2009-09-05 21:47:34 +00006380 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006381
cristy3ed852e2009-09-05 21:47:34 +00006382 if (logging != MagickFalse)
6383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006384 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6385 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006386
cristy3ed852e2009-09-05 21:47:34 +00006387 if (logging != MagickFalse)
6388 {
6389 int
6390 scene;
6391
6392 scene=0;
6393 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006394
cristy3ed852e2009-09-05 21:47:34 +00006395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6396 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006397
cristy3ed852e2009-09-05 21:47:34 +00006398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006399 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006400
cristy3ed852e2009-09-05 21:47:34 +00006401 while (GetNextImageInList(image) != (Image *) NULL)
6402 {
6403 image=GetNextImageInList(image);
6404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006405 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006406 }
6407 }
6408
6409 image=GetFirstImageInList(image);
6410#ifdef MNG_COALESCE_LAYERS
6411 if (insert_layers)
6412 {
6413 Image
6414 *next_image,
6415 *next;
6416
cristybb503372010-05-27 20:51:26 +00006417 size_t
cristy3ed852e2009-09-05 21:47:34 +00006418 scene;
6419
6420 if (logging != MagickFalse)
6421 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006422
cristy3ed852e2009-09-05 21:47:34 +00006423 scene=image->scene;
6424 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006425
cristy3ed852e2009-09-05 21:47:34 +00006426 if (next_image == (Image *) NULL)
6427 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006428
cristy3ed852e2009-09-05 21:47:34 +00006429 image=DestroyImageList(image);
6430 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006431
cristy3ed852e2009-09-05 21:47:34 +00006432 for (next=image; next != (Image *) NULL; next=next_image)
6433 {
6434 next->page.width=mng_info->mng_width;
6435 next->page.height=mng_info->mng_height;
6436 next->page.x=0;
6437 next->page.y=0;
6438 next->scene=scene++;
6439 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006440
cristy3ed852e2009-09-05 21:47:34 +00006441 if (next_image == (Image *) NULL)
6442 break;
glennrp47b9dd52010-11-24 18:12:06 +00006443
cristy3ed852e2009-09-05 21:47:34 +00006444 if (next->delay == 0)
6445 {
6446 scene--;
6447 next_image->previous=GetPreviousImageInList(next);
6448 if (GetPreviousImageInList(next) == (Image *) NULL)
6449 image=next_image;
6450 else
6451 next->previous->next=next_image;
6452 next=DestroyImage(next);
6453 }
6454 }
6455 }
6456#endif
6457
6458 while (GetNextImageInList(image) != (Image *) NULL)
6459 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006460
cristy3ed852e2009-09-05 21:47:34 +00006461 image->dispose=BackgroundDispose;
6462
6463 if (logging != MagickFalse)
6464 {
6465 int
6466 scene;
6467
6468 scene=0;
6469 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006470
cristy3ed852e2009-09-05 21:47:34 +00006471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6472 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006473
cristy3ed852e2009-09-05 21:47:34 +00006474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006475 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6476 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006477
cristy3ed852e2009-09-05 21:47:34 +00006478 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006479 {
6480 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006481
cristyf2faecf2010-05-28 19:19:36 +00006482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006483 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6484 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006485 }
6486 }
glennrp47b9dd52010-11-24 18:12:06 +00006487
cristy3ed852e2009-09-05 21:47:34 +00006488 image=GetFirstImageInList(image);
6489 MngInfoFreeStruct(mng_info,&have_mng_structure);
6490 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006491
cristy3ed852e2009-09-05 21:47:34 +00006492 if (logging != MagickFalse)
6493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006494
cristy3ed852e2009-09-05 21:47:34 +00006495 return(GetFirstImageInList(image));
6496}
glennrp25c1e2b2010-03-25 01:39:56 +00006497#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006498static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6499{
6500 printf("Your PNG library is too old: You have libpng-%s\n",
6501 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006502
cristy3ed852e2009-09-05 21:47:34 +00006503 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6504 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006505
cristy3ed852e2009-09-05 21:47:34 +00006506 return(Image *) NULL;
6507}
glennrp47b9dd52010-11-24 18:12:06 +00006508
cristy3ed852e2009-09-05 21:47:34 +00006509static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6510{
6511 return(ReadPNGImage(image_info,exception));
6512}
glennrp25c1e2b2010-03-25 01:39:56 +00006513#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006514#endif
6515
6516/*
6517%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6518% %
6519% %
6520% %
6521% R e g i s t e r P N G I m a g e %
6522% %
6523% %
6524% %
6525%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6526%
6527% RegisterPNGImage() adds properties for the PNG image format to
6528% the list of supported formats. The properties include the image format
6529% tag, a method to read and/or write the format, whether the format
6530% supports the saving of more than one frame to the same file or blob,
6531% whether the format supports native in-memory I/O, and a brief
6532% description of the format.
6533%
6534% The format of the RegisterPNGImage method is:
6535%
cristybb503372010-05-27 20:51:26 +00006536% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006537%
6538*/
cristybb503372010-05-27 20:51:26 +00006539ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006540{
6541 char
6542 version[MaxTextExtent];
6543
6544 MagickInfo
6545 *entry;
6546
6547 static const char
6548 *PNGNote=
6549 {
6550 "See http://www.libpng.org/ for details about the PNG format."
6551 },
glennrp47b9dd52010-11-24 18:12:06 +00006552
cristy3ed852e2009-09-05 21:47:34 +00006553 *JNGNote=
6554 {
6555 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6556 "format."
6557 },
glennrp47b9dd52010-11-24 18:12:06 +00006558
cristy3ed852e2009-09-05 21:47:34 +00006559 *MNGNote=
6560 {
6561 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6562 "format."
6563 };
6564
6565 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006566
cristy3ed852e2009-09-05 21:47:34 +00006567#if defined(PNG_LIBPNG_VER_STRING)
6568 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6569 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006570
cristy3ed852e2009-09-05 21:47:34 +00006571 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6572 {
6573 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6574 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6575 MaxTextExtent);
6576 }
6577#endif
glennrp47b9dd52010-11-24 18:12:06 +00006578
cristy3ed852e2009-09-05 21:47:34 +00006579 entry=SetMagickInfo("MNG");
6580 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00006581
cristy3ed852e2009-09-05 21:47:34 +00006582#if defined(MAGICKCORE_PNG_DELEGATE)
6583 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6584 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6585#endif
glennrp47b9dd52010-11-24 18:12:06 +00006586
cristy3ed852e2009-09-05 21:47:34 +00006587 entry->magick=(IsImageFormatHandler *) IsMNG;
6588 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00006589
cristy3ed852e2009-09-05 21:47:34 +00006590 if (*version != '\0')
6591 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006592
cristy3ed852e2009-09-05 21:47:34 +00006593 entry->module=ConstantString("PNG");
6594 entry->note=ConstantString(MNGNote);
6595 (void) RegisterMagickInfo(entry);
6596
6597 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006598
cristy3ed852e2009-09-05 21:47:34 +00006599#if defined(MAGICKCORE_PNG_DELEGATE)
6600 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6601 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6602#endif
glennrp47b9dd52010-11-24 18:12:06 +00006603
cristy3ed852e2009-09-05 21:47:34 +00006604 entry->magick=(IsImageFormatHandler *) IsPNG;
6605 entry->adjoin=MagickFalse;
6606 entry->description=ConstantString("Portable Network Graphics");
6607 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006608
cristy3ed852e2009-09-05 21:47:34 +00006609 if (*version != '\0')
6610 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006611
cristy3ed852e2009-09-05 21:47:34 +00006612 entry->note=ConstantString(PNGNote);
6613 (void) RegisterMagickInfo(entry);
6614
6615 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00006616
cristy3ed852e2009-09-05 21:47:34 +00006617#if defined(MAGICKCORE_PNG_DELEGATE)
6618 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6619 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6620#endif
glennrp47b9dd52010-11-24 18:12:06 +00006621
cristy3ed852e2009-09-05 21:47:34 +00006622 entry->magick=(IsImageFormatHandler *) IsPNG;
6623 entry->adjoin=MagickFalse;
6624 entry->description=ConstantString(
6625 "8-bit indexed with optional binary transparency");
6626 entry->module=ConstantString("PNG");
6627 (void) RegisterMagickInfo(entry);
6628
6629 entry=SetMagickInfo("PNG24");
6630 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006631
cristy3ed852e2009-09-05 21:47:34 +00006632#if defined(ZLIB_VERSION)
6633 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6634 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006635
cristy3ed852e2009-09-05 21:47:34 +00006636 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6637 {
6638 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6639 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6640 }
6641#endif
glennrp47b9dd52010-11-24 18:12:06 +00006642
cristy3ed852e2009-09-05 21:47:34 +00006643 if (*version != '\0')
6644 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006645
cristy3ed852e2009-09-05 21:47:34 +00006646#if defined(MAGICKCORE_PNG_DELEGATE)
6647 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6648 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6649#endif
glennrp47b9dd52010-11-24 18:12:06 +00006650
cristy3ed852e2009-09-05 21:47:34 +00006651 entry->magick=(IsImageFormatHandler *) IsPNG;
6652 entry->adjoin=MagickFalse;
6653 entry->description=ConstantString("opaque 24-bit RGB");
6654 entry->module=ConstantString("PNG");
6655 (void) RegisterMagickInfo(entry);
6656
6657 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00006658
cristy3ed852e2009-09-05 21:47:34 +00006659#if defined(MAGICKCORE_PNG_DELEGATE)
6660 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6661 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6662#endif
glennrp47b9dd52010-11-24 18:12:06 +00006663
cristy3ed852e2009-09-05 21:47:34 +00006664 entry->magick=(IsImageFormatHandler *) IsPNG;
6665 entry->adjoin=MagickFalse;
6666 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6667 entry->module=ConstantString("PNG");
6668 (void) RegisterMagickInfo(entry);
6669
6670 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006671
cristy3ed852e2009-09-05 21:47:34 +00006672#if defined(JNG_SUPPORTED)
6673#if defined(MAGICKCORE_PNG_DELEGATE)
6674 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6675 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6676#endif
6677#endif
glennrp47b9dd52010-11-24 18:12:06 +00006678
cristy3ed852e2009-09-05 21:47:34 +00006679 entry->magick=(IsImageFormatHandler *) IsJNG;
6680 entry->adjoin=MagickFalse;
6681 entry->description=ConstantString("JPEG Network Graphics");
6682 entry->module=ConstantString("PNG");
6683 entry->note=ConstantString(JNGNote);
6684 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00006685
cristy18b17442009-10-25 18:36:48 +00006686#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006687 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00006688#endif
glennrp47b9dd52010-11-24 18:12:06 +00006689
cristy3ed852e2009-09-05 21:47:34 +00006690 return(MagickImageCoderSignature);
6691}
6692
6693/*
6694%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6695% %
6696% %
6697% %
6698% U n r e g i s t e r P N G I m a g e %
6699% %
6700% %
6701% %
6702%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6703%
6704% UnregisterPNGImage() removes format registrations made by the
6705% PNG module from the list of supported formats.
6706%
6707% The format of the UnregisterPNGImage method is:
6708%
6709% UnregisterPNGImage(void)
6710%
6711*/
6712ModuleExport void UnregisterPNGImage(void)
6713{
6714 (void) UnregisterMagickInfo("MNG");
6715 (void) UnregisterMagickInfo("PNG");
6716 (void) UnregisterMagickInfo("PNG8");
6717 (void) UnregisterMagickInfo("PNG24");
6718 (void) UnregisterMagickInfo("PNG32");
6719 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006720
cristy3ed852e2009-09-05 21:47:34 +00006721#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006722 if (ping_semaphore != (SemaphoreInfo *) NULL)
6723 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006724#endif
6725}
6726
6727#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006728#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006729/*
6730%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6731% %
6732% %
6733% %
6734% W r i t e M N G I m a g e %
6735% %
6736% %
6737% %
6738%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6739%
6740% WriteMNGImage() writes an image in the Portable Network Graphics
6741% Group's "Multiple-image Network Graphics" encoded image format.
6742%
6743% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6744%
6745% The format of the WriteMNGImage method is:
6746%
6747% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6748%
6749% A description of each parameter follows.
6750%
6751% o image_info: the image info.
6752%
6753% o image: The image.
6754%
6755%
6756% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6757% "To do" under ReadPNGImage):
6758%
cristy3ed852e2009-09-05 21:47:34 +00006759% Preserve all unknown and not-yet-handled known chunks found in input
6760% PNG file and copy them into output PNG files according to the PNG
6761% copying rules.
6762%
6763% Write the iCCP chunk at MNG level when (icc profile length > 0)
6764%
6765% Improve selection of color type (use indexed-colour or indexed-colour
6766% with tRNS when 256 or fewer unique RGBA values are present).
6767%
6768% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6769% This will be complicated if we limit ourselves to generating MNG-LC
6770% files. For now we ignore disposal method 3 and simply overlay the next
6771% image on it.
6772%
6773% Check for identical PLTE's or PLTE/tRNS combinations and use a
6774% global MNG PLTE or PLTE/tRNS combination when appropriate.
6775% [mostly done 15 June 1999 but still need to take care of tRNS]
6776%
6777% Check for identical sRGB and replace with a global sRGB (and remove
6778% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6779% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6780% local gAMA/cHRM with local sRGB if appropriate).
6781%
6782% Check for identical sBIT chunks and write global ones.
6783%
6784% Provide option to skip writing the signature tEXt chunks.
6785%
6786% Use signatures to detect identical objects and reuse the first
6787% instance of such objects instead of writing duplicate objects.
6788%
6789% Use a smaller-than-32k value of compression window size when
6790% appropriate.
6791%
6792% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6793% ancillary text chunks and save profiles.
6794%
6795% Provide an option to force LC files (to ensure exact framing rate)
6796% instead of VLC.
6797%
6798% Provide an option to force VLC files instead of LC, even when offsets
6799% are present. This will involve expanding the embedded images with a
6800% transparent region at the top and/or left.
6801*/
6802
cristy3ed852e2009-09-05 21:47:34 +00006803static void
glennrpcf002022011-01-30 02:38:15 +00006804Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00006805 png_info *ping_info, unsigned char *profile_type, unsigned char
6806 *profile_description, unsigned char *profile_data, png_uint_32 length)
6807{
cristy3ed852e2009-09-05 21:47:34 +00006808 png_textp
6809 text;
6810
cristybb503372010-05-27 20:51:26 +00006811 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006812 i;
6813
6814 unsigned char
6815 *sp;
6816
6817 png_charp
6818 dp;
6819
6820 png_uint_32
6821 allocated_length,
6822 description_length;
6823
6824 unsigned char
6825 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006826
cristy3ed852e2009-09-05 21:47:34 +00006827 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6828 return;
6829
6830 if (image_info->verbose)
6831 {
glennrp0fe50b42010-11-16 03:52:51 +00006832 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6833 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006834 }
glennrp0fe50b42010-11-16 03:52:51 +00006835
cristy3ed852e2009-09-05 21:47:34 +00006836 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6837 description_length=(png_uint_32) strlen((const char *) profile_description);
6838 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6839 + description_length);
6840 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6841 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6842 text[0].key[0]='\0';
6843 (void) ConcatenateMagickString(text[0].key,
6844 "Raw profile type ",MaxTextExtent);
6845 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6846 sp=profile_data;
6847 dp=text[0].text;
6848 *dp++='\n';
6849 (void) CopyMagickString(dp,(const char *) profile_description,
6850 allocated_length);
6851 dp+=description_length;
6852 *dp++='\n';
6853 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006854 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006855 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00006856
cristybb503372010-05-27 20:51:26 +00006857 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006858 {
6859 if (i%36 == 0)
6860 *dp++='\n';
6861 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6862 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6863 }
glennrp47b9dd52010-11-24 18:12:06 +00006864
cristy3ed852e2009-09-05 21:47:34 +00006865 *dp++='\n';
6866 *dp='\0';
6867 text[0].text_length=(png_size_t) (dp-text[0].text);
6868 text[0].compression=image_info->compression == NoCompression ||
6869 (image_info->compression == UndefinedCompression &&
6870 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00006871
cristy3ed852e2009-09-05 21:47:34 +00006872 if (text[0].text_length <= allocated_length)
6873 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00006874
cristy3ed852e2009-09-05 21:47:34 +00006875 png_free(ping,text[0].text);
6876 png_free(ping,text[0].key);
6877 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006878}
6879
glennrpcf002022011-01-30 02:38:15 +00006880static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00006881 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00006882{
6883 char
6884 *name;
6885
6886 const StringInfo
6887 *profile;
6888
6889 unsigned char
6890 *data;
6891
6892 png_uint_32 length;
6893
6894 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00006895
6896 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6897 {
cristy3ed852e2009-09-05 21:47:34 +00006898 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00006899
cristy3ed852e2009-09-05 21:47:34 +00006900 if (profile != (const StringInfo *) NULL)
6901 {
6902 StringInfo
glennrpcf002022011-01-30 02:38:15 +00006903 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00006904
glennrp47b9dd52010-11-24 18:12:06 +00006905 if (LocaleNCompare(name,string,11) == 0)
6906 {
6907 if (logging != MagickFalse)
6908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6909 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00006910
glennrpcf002022011-01-30 02:38:15 +00006911 ping_profile=CloneStringInfo(profile);
6912 data=GetStringInfoDatum(ping_profile),
6913 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006914 data[4]=data[3];
6915 data[3]=data[2];
6916 data[2]=data[1];
6917 data[1]=data[0];
6918 (void) WriteBlobMSBULong(image,length-5); /* data length */
6919 (void) WriteBlob(image,length-1,data+1);
6920 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00006921 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006922 }
cristy3ed852e2009-09-05 21:47:34 +00006923 }
glennrp47b9dd52010-11-24 18:12:06 +00006924
cristy3ed852e2009-09-05 21:47:34 +00006925 name=GetNextImageProfile(image);
6926 }
glennrp47b9dd52010-11-24 18:12:06 +00006927
cristy3ed852e2009-09-05 21:47:34 +00006928 return(MagickTrue);
6929}
6930
glennrpb9cfe272010-12-21 15:08:06 +00006931
cristy3ed852e2009-09-05 21:47:34 +00006932/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00006933static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6934 const ImageInfo *IMimage_info,Image *IMimage)
6935{
6936 Image
6937 *image;
6938
6939 ImageInfo
6940 *image_info;
6941
cristy3ed852e2009-09-05 21:47:34 +00006942 char
6943 s[2];
6944
6945 const char
6946 *name,
6947 *property,
6948 *value;
6949
6950 const StringInfo
6951 *profile;
6952
cristy3ed852e2009-09-05 21:47:34 +00006953 int
cristy3ed852e2009-09-05 21:47:34 +00006954 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006955 pass;
6956
glennrpe9c26dc2010-05-30 01:56:35 +00006957 png_byte
6958 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006959
glennrp39992b42010-11-14 00:03:43 +00006960 png_color
6961 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00006962
glennrp5af765f2010-03-30 11:12:18 +00006963 png_color_16
6964 ping_background,
6965 ping_trans_color;
6966
cristy3ed852e2009-09-05 21:47:34 +00006967 png_info
6968 *ping_info;
6969
6970 png_struct
6971 *ping;
6972
glennrp5af765f2010-03-30 11:12:18 +00006973 png_uint_32
6974 ping_height,
6975 ping_width;
6976
cristybb503372010-05-27 20:51:26 +00006977 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006978 y;
6979
6980 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00006981 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00006982 logging,
glennrp58e01762011-01-07 15:28:54 +00006983 matte,
6984
glennrpda8f3a72011-02-27 23:54:12 +00006985 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00006986 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00006987 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00006988 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00006989 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00006990 ping_have_bKGD,
6991 ping_have_pHYs,
6992 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00006993
6994 ping_exclude_bKGD,
6995 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00006996 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00006997 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00006998 ping_exclude_gAMA,
6999 ping_exclude_iCCP,
7000 /* ping_exclude_iTXt, */
7001 ping_exclude_oFFs,
7002 ping_exclude_pHYs,
7003 ping_exclude_sRGB,
7004 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007005 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007006 ping_exclude_vpAg,
7007 ping_exclude_zCCP, /* hex-encoded iCCP */
7008 ping_exclude_zTXt,
7009
glennrp8d3d6e52011-04-19 04:39:51 +00007010 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007011 ping_need_colortype_warning,
7012
glennrp82b3c532011-03-22 19:20:54 +00007013 status,
glennrpd3371642011-03-22 19:42:23 +00007014 tried_333,
7015 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007016
7017 QuantumInfo
7018 *quantum_info;
7019
cristybb503372010-05-27 20:51:26 +00007020 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007021 i,
7022 x;
7023
7024 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007025 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007026
glennrp5af765f2010-03-30 11:12:18 +00007027 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007028 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007029 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007030 ping_color_type,
7031 ping_interlace_method,
7032 ping_compression_method,
7033 ping_filter_method,
7034 ping_num_trans;
7035
cristybb503372010-05-27 20:51:26 +00007036 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007037 image_depth,
7038 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007039
cristybb503372010-05-27 20:51:26 +00007040 size_t
cristy3ed852e2009-09-05 21:47:34 +00007041 quality,
7042 rowbytes,
7043 save_image_depth;
7044
glennrpdfd70802010-11-14 01:23:35 +00007045 int
glennrpfd05d622011-02-25 04:10:33 +00007046 j,
glennrpf09bded2011-01-08 01:15:59 +00007047 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007048 number_opaque,
7049 number_semitransparent,
7050 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007051 ping_pHYs_unit_type;
7052
7053 png_uint_32
7054 ping_pHYs_x_resolution,
7055 ping_pHYs_y_resolution;
7056
cristy3ed852e2009-09-05 21:47:34 +00007057 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007058 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007059
glennrpb9cfe272010-12-21 15:08:06 +00007060 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7061 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007062 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007063 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007064
cristy3ed852e2009-09-05 21:47:34 +00007065#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007066 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007067#endif
7068
glennrp5af765f2010-03-30 11:12:18 +00007069 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007070 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007071 ping_color_type=0,
7072 ping_interlace_method=0,
7073 ping_compression_method=0,
7074 ping_filter_method=0,
7075 ping_num_trans = 0;
7076
7077 ping_background.red = 0;
7078 ping_background.green = 0;
7079 ping_background.blue = 0;
7080 ping_background.gray = 0;
7081 ping_background.index = 0;
7082
7083 ping_trans_color.red=0;
7084 ping_trans_color.green=0;
7085 ping_trans_color.blue=0;
7086 ping_trans_color.gray=0;
7087
glennrpdfd70802010-11-14 01:23:35 +00007088 ping_pHYs_unit_type = 0;
7089 ping_pHYs_x_resolution = 0;
7090 ping_pHYs_y_resolution = 0;
7091
glennrpda8f3a72011-02-27 23:54:12 +00007092 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007093 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007094 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007095 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007096 ping_have_bKGD=MagickFalse;
7097 ping_have_pHYs=MagickFalse;
7098 ping_have_tRNS=MagickFalse;
7099
glennrp0e8ea192010-12-24 18:00:33 +00007100 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7101 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007102 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007103 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007104 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007105 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7106 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7107 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7108 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7109 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7110 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007111 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007112 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7113 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7114 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7115
glennrp8d3d6e52011-04-19 04:39:51 +00007116 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007117 ping_need_colortype_warning = MagickFalse;
7118
glennrp8bb3a022010-12-13 20:40:04 +00007119 number_opaque = 0;
7120 number_semitransparent = 0;
7121 number_transparent = 0;
7122
glennrpfd05d622011-02-25 04:10:33 +00007123 if (logging != MagickFalse)
7124 {
7125 if (image->storage_class == UndefinedClass)
7126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7127 " storage_class=UndefinedClass");
7128 if (image->storage_class == DirectClass)
7129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7130 " storage_class=DirectClass");
7131 if (image->storage_class == PseudoClass)
7132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7133 " storage_class=PseudoClass");
7134 }
glennrp28af3712011-04-06 18:07:30 +00007135
glennrpc6c391a2011-04-27 02:23:56 +00007136 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007137 {
glennrpc6c391a2011-04-27 02:23:56 +00007138 if (image->storage_class != PseudoClass && image->colormap != NULL)
7139 {
7140 /* Free the bogus colormap; it can cause trouble later */
7141 if (logging != MagickFalse)
7142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7143 " Freeing bogus colormap");
7144 (void *) RelinquishMagickMemory(image->colormap);
7145 image->colormap=NULL;
7146 }
glennrp28af3712011-04-06 18:07:30 +00007147 }
glennrpfd05d622011-02-25 04:10:33 +00007148
cristy3ed852e2009-09-05 21:47:34 +00007149 if (image->colorspace != RGBColorspace)
7150 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007151
glennrp3241bd02010-12-12 04:36:28 +00007152 /*
7153 Sometimes we get PseudoClass images whose RGB values don't match
7154 the colors in the colormap. This code syncs the RGB values.
7155 */
7156 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7157 (void) SyncImage(image);
7158
glennrpa6a06632011-01-19 15:15:34 +00007159#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7160 if (image->depth > 8)
7161 {
7162 if (logging != MagickFalse)
7163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7164 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7165
7166 image->depth=8;
7167 }
7168#endif
7169
glennrp67b9c1a2011-04-22 18:47:36 +00007170#if 0 /* To do: Option to use the original colormap */
7171 if (ping_preserve_colormap != MagickFalse)
7172 {
7173 }
7174#endif
7175
glennrp3faa9a32011-04-23 14:00:25 +00007176#if 0 /* To do: respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007177 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7178 {
glennrp9d0ea4d2011-04-22 18:35:57 +00007179 }
glennrp67b9c1a2011-04-22 18:47:36 +00007180#endif
glennrp9d0ea4d2011-04-22 18:35:57 +00007181
glennrp67b9c1a2011-04-22 18:47:36 +00007182 /* To do: set to next higher multiple of 8 */
7183 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007184 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007185
glennrp2b013e42010-11-24 16:55:50 +00007186#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7187 /* PNG does not handle depths greater than 16 so reduce it even
7188 * if lossy
7189 */
7190 if (image->depth > 16)
7191 image->depth=16;
7192#endif
7193
glennrp3faa9a32011-04-23 14:00:25 +00007194#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007195 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007196 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007197 image->depth = 8;
7198#endif
7199
glennrpc8c2f062011-02-25 19:00:33 +00007200 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007201 * we reduce the transparency to binary and run again, then if there
7202 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7203 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7204 * palette. The final reduction can only fail if there are still 256
7205 * colors present and one of them has both transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007206 */
glennrp82b3c532011-03-22 19:20:54 +00007207
7208 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007209 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007210
glennrpd3371642011-03-22 19:42:23 +00007211 for (j=0; j<5; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007212 {
7213 /* BUILD_PALETTE
7214 *
7215 * Sometimes we get DirectClass images that have 256 colors or fewer.
7216 * This code will build a colormap.
7217 *
7218 * Also, sometimes we get PseudoClass images with an out-of-date
7219 * colormap. This code will replace the colormap with a new one.
7220 * Sometimes we get PseudoClass images that have more than 256 colors.
7221 * This code will delete the colormap and change the image to
7222 * DirectClass.
7223 *
7224 * If image->matte is MagickFalse, we ignore the opacity channel
7225 * even though it sometimes contains left-over non-opaque values.
7226 *
7227 * Also we gather some information (number of opaque, transparent,
7228 * and semitransparent pixels, and whether the image has any non-gray
7229 * pixels or only black-and-white pixels) that we might need later.
7230 *
7231 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7232 * we need to check for bogus non-opaque values, at least.
7233 */
glennrp3c218112010-11-27 15:31:26 +00007234
glennrpd71e86a2011-02-24 01:28:37 +00007235 ExceptionInfo
7236 *exception;
glennrp3c218112010-11-27 15:31:26 +00007237
glennrpd71e86a2011-02-24 01:28:37 +00007238 int
7239 n;
glennrp3c218112010-11-27 15:31:26 +00007240
glennrpd71e86a2011-02-24 01:28:37 +00007241 PixelPacket
7242 opaque[260],
7243 semitransparent[260],
7244 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007245
glennrpd71e86a2011-02-24 01:28:37 +00007246 register IndexPacket
7247 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007248
glennrpd71e86a2011-02-24 01:28:37 +00007249 register const PixelPacket
7250 *s,
7251 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007252
glennrpfd05d622011-02-25 04:10:33 +00007253 register PixelPacket
7254 *r;
7255
glennrpd71e86a2011-02-24 01:28:37 +00007256 if (logging != MagickFalse)
7257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7258 " Enter BUILD_PALETTE:");
7259
7260 if (logging != MagickFalse)
7261 {
glennrp03812ae2010-12-24 01:31:34 +00007262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007263 " image->columns=%.20g",(double) image->columns);
7264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7265 " image->rows=%.20g",(double) image->rows);
7266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7267 " image->matte=%.20g",(double) image->matte);
7268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7269 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007270
glennrpfd05d622011-02-25 04:10:33 +00007271 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007272 {
7273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007274 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007276 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007277
glennrpd71e86a2011-02-24 01:28:37 +00007278 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007279 {
glennrpd71e86a2011-02-24 01:28:37 +00007280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7281 " %d (%d,%d,%d,%d)",
7282 (int) i,
7283 (int) image->colormap[i].red,
7284 (int) image->colormap[i].green,
7285 (int) image->colormap[i].blue,
7286 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007287 }
glennrp2cc891a2010-12-24 13:44:32 +00007288
glennrpd71e86a2011-02-24 01:28:37 +00007289 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7290 {
7291 if (i > 255)
7292 {
7293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7294 " %d (%d,%d,%d,%d)",
7295 (int) i,
7296 (int) image->colormap[i].red,
7297 (int) image->colormap[i].green,
7298 (int) image->colormap[i].blue,
7299 (int) image->colormap[i].opacity);
7300 }
7301 }
glennrp03812ae2010-12-24 01:31:34 +00007302 }
glennrp7ddcc222010-12-11 05:01:05 +00007303
glennrpd71e86a2011-02-24 01:28:37 +00007304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7305 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007306
glennrpd71e86a2011-02-24 01:28:37 +00007307 if (image->colors == 0)
7308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7309 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007310
glennrp8d3d6e52011-04-19 04:39:51 +00007311 if (ping_preserve_colormap == MagickFalse)
7312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7313 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007314 }
7315
7316 exception=(&image->exception);
7317
7318 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007319 number_opaque = 0;
7320 number_semitransparent = 0;
7321 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007322
7323 for (y=0; y < (ssize_t) image->rows; y++)
7324 {
7325 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7326
7327 if (q == (PixelPacket *) NULL)
7328 break;
7329
7330 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007331 {
glennrpd71e86a2011-02-24 01:28:37 +00007332 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7333 {
7334 if (number_opaque < 259)
7335 {
7336 if (number_opaque == 0)
7337 {
glennrp8e045c82011-04-27 16:40:27 +00007338 GetRGBPixelComponents(q, opaque[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007339 opaque[0].opacity=OpaqueOpacity;
7340 number_opaque=1;
7341 }
glennrp2cc891a2010-12-24 13:44:32 +00007342
glennrpd71e86a2011-02-24 01:28:37 +00007343 for (i=0; i< (ssize_t) number_opaque; i++)
7344 {
glennrp0e68fac2011-04-26 04:51:47 +00007345 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007346 break;
7347 }
glennrp7ddcc222010-12-11 05:01:05 +00007348
glennrpd71e86a2011-02-24 01:28:37 +00007349 if (i == (ssize_t) number_opaque &&
7350 number_opaque < 259)
7351 {
7352 number_opaque++;
glennrp8e045c82011-04-27 16:40:27 +00007353 GetRGBPixelComponents(q, opaque[i]);
glennrpca7ad3a2011-04-26 04:44:54 +00007354 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00007355 }
7356 }
7357 }
7358 else if (q->opacity == TransparentOpacity)
7359 {
7360 if (number_transparent < 259)
7361 {
7362 if (number_transparent == 0)
7363 {
glennrp8e045c82011-04-27 16:40:27 +00007364 GetRGBOPixelComponents(q, transparent[0]);
glennrpa18d5bc2011-04-23 14:51:34 +00007365 ping_trans_color.red=
7366 (unsigned short) GetRedPixelComponent(q);
7367 ping_trans_color.green=
7368 (unsigned short) GetGreenPixelComponent(q);
7369 ping_trans_color.blue=
7370 (unsigned short) GetBluePixelComponent(q);
7371 ping_trans_color.gray=
7372 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007373 number_transparent = 1;
7374 }
7375
7376 for (i=0; i< (ssize_t) number_transparent; i++)
7377 {
glennrp0e68fac2011-04-26 04:51:47 +00007378 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00007379 break;
7380 }
7381
7382 if (i == (ssize_t) number_transparent &&
7383 number_transparent < 259)
7384 {
7385 number_transparent++;
glennrp8e045c82011-04-27 16:40:27 +00007386 GetRGBOPixelComponents(q, transparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00007387 }
7388 }
7389 }
7390 else
7391 {
7392 if (number_semitransparent < 259)
7393 {
7394 if (number_semitransparent == 0)
7395 {
glennrp8e045c82011-04-27 16:40:27 +00007396 GetRGBOPixelComponents(q, semitransparent[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007397 number_semitransparent = 1;
7398 }
7399
7400 for (i=0; i< (ssize_t) number_semitransparent; i++)
7401 {
glennrp0e68fac2011-04-26 04:51:47 +00007402 if (IsColorEqual(q, semitransparent+i)
glennrpca7ad3a2011-04-26 04:44:54 +00007403 && GetOpacityPixelComponent(q) ==
7404 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00007405 break;
7406 }
7407
7408 if (i == (ssize_t) number_semitransparent &&
7409 number_semitransparent < 259)
7410 {
7411 number_semitransparent++;
glennrp8e045c82011-04-27 16:40:27 +00007412 GetRGBOPixelComponents(q, semitransparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00007413 }
7414 }
7415 }
7416 q++;
7417 }
7418 }
7419
7420 if (ping_exclude_bKGD == MagickFalse)
7421 {
7422 /* Add the background color to the palette, if it
7423 * isn't already there.
7424 */
glennrpc6c391a2011-04-27 02:23:56 +00007425 if (logging != MagickFalse)
7426 {
7427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7428 " Check colormap for background (%d,%d,%d)",
7429 (int) image->background_color.red,
7430 (int) image->background_color.green,
7431 (int) image->background_color.blue);
7432 }
glennrpd71e86a2011-02-24 01:28:37 +00007433 for (i=0; i<number_opaque; i++)
7434 {
glennrpca7ad3a2011-04-26 04:44:54 +00007435 if (opaque[i].red == image->background_color.red &&
7436 opaque[i].green == image->background_color.green &&
7437 opaque[i].blue == image->background_color.blue)
7438 break;
glennrpd71e86a2011-02-24 01:28:37 +00007439 }
glennrpd71e86a2011-02-24 01:28:37 +00007440 if (number_opaque < 259 && i == number_opaque)
7441 {
glennrp8e045c82011-04-27 16:40:27 +00007442 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00007443 ping_background.index = i;
7444 if (logging != MagickFalse)
7445 {
7446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7447 " background_color index is %d",(int) i);
7448 }
7449
glennrpd71e86a2011-02-24 01:28:37 +00007450 }
glennrpa080bc32011-03-11 18:03:44 +00007451 else if (logging != MagickFalse)
7452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7453 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007454 }
7455
7456 image_colors=number_opaque+number_transparent+number_semitransparent;
7457
glennrpa080bc32011-03-11 18:03:44 +00007458 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7459 {
7460 /* No room for the background color; remove it. */
7461 number_opaque--;
7462 image_colors--;
7463 }
7464
glennrpd71e86a2011-02-24 01:28:37 +00007465 if (logging != MagickFalse)
7466 {
7467 if (image_colors > 256)
7468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7469 " image has more than 256 colors");
7470
7471 else
7472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7473 " image has %d colors",image_colors);
7474 }
7475
glennrp8d3d6e52011-04-19 04:39:51 +00007476 if (ping_preserve_colormap != MagickFalse)
7477 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007478
glennrpfd05d622011-02-25 04:10:33 +00007479 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007480 {
7481 ping_have_color=MagickFalse;
7482 ping_have_non_bw=MagickFalse;
7483
7484 if(image_colors > 256)
7485 {
7486 for (y=0; y < (ssize_t) image->rows; y++)
7487 {
7488 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7489
7490 if (q == (PixelPacket *) NULL)
7491 break;
7492
7493 /* Worst case is black-and-white; we are looking at every
7494 * pixel twice.
7495 */
7496
7497 if (ping_have_color == MagickFalse)
7498 {
7499 s=q;
7500 for (x=0; x < (ssize_t) image->columns; x++)
7501 {
glennrpa18d5bc2011-04-23 14:51:34 +00007502 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7503 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00007504 {
7505 ping_have_color=MagickTrue;
7506 ping_have_non_bw=MagickTrue;
7507 break;
7508 }
7509 s++;
7510 }
7511 }
7512
7513 if (ping_have_non_bw == MagickFalse)
7514 {
7515 s=q;
7516 for (x=0; x < (ssize_t) image->columns; x++)
7517 {
glennrpa18d5bc2011-04-23 14:51:34 +00007518 if (GetRedPixelComponent(s) != 0 &&
7519 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00007520 {
7521 ping_have_non_bw=MagickTrue;
7522 }
7523 s++;
7524 }
7525 }
7526 }
7527 }
7528 }
7529
7530 if (image_colors < 257)
7531 {
7532 PixelPacket
7533 colormap[260];
7534
7535 /*
7536 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007537 */
7538
glennrpd71e86a2011-02-24 01:28:37 +00007539 if (logging != MagickFalse)
7540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7541 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007542
glennrpd71e86a2011-02-24 01:28:37 +00007543 /* Sort palette, transparent first */;
7544
7545 n = 0;
7546
7547 for (i=0; i<number_transparent; i++)
7548 colormap[n++] = transparent[i];
7549
7550 for (i=0; i<number_semitransparent; i++)
7551 colormap[n++] = semitransparent[i];
7552
7553 for (i=0; i<number_opaque; i++)
7554 colormap[n++] = opaque[i];
7555
glennrpc6c391a2011-04-27 02:23:56 +00007556 ping_background.index +=
7557 (number_transparent + number_semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00007558
7559 /* image_colors < 257; search the colormap instead of the pixels
7560 * to get ping_have_color and ping_have_non_bw
7561 */
7562 for (i=0; i<n; i++)
7563 {
7564 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007565 {
glennrpd71e86a2011-02-24 01:28:37 +00007566 if (colormap[i].red != colormap[i].green ||
7567 colormap[i].red != colormap[i].blue)
7568 {
7569 ping_have_color=MagickTrue;
7570 ping_have_non_bw=MagickTrue;
7571 break;
7572 }
7573 }
7574
7575 if (ping_have_non_bw == MagickFalse)
7576 {
7577 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007578 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007579 }
glennrp8bb3a022010-12-13 20:40:04 +00007580 }
7581
glennrpd71e86a2011-02-24 01:28:37 +00007582 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7583 (number_transparent == 0 && number_semitransparent == 0)) &&
7584 (((mng_info->write_png_colortype-1) ==
7585 PNG_COLOR_TYPE_PALETTE) ||
7586 (mng_info->write_png_colortype == 0)))
7587 {
glennrp6185c532011-01-14 17:58:40 +00007588 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007589 {
glennrpd71e86a2011-02-24 01:28:37 +00007590 if (n != (ssize_t) image_colors)
7591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7592 " image_colors (%d) and n (%d) don't match",
7593 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007594
glennrpd71e86a2011-02-24 01:28:37 +00007595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7596 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007597 }
glennrp03812ae2010-12-24 01:31:34 +00007598
glennrpd71e86a2011-02-24 01:28:37 +00007599 image->colors = image_colors;
7600
7601 if (AcquireImageColormap(image,image_colors) ==
7602 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007603 ThrowWriterException(ResourceLimitError,
7604 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007605
7606 for (i=0; i< (ssize_t) image_colors; i++)
7607 image->colormap[i] = colormap[i];
7608
7609 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007610 {
glennrpd71e86a2011-02-24 01:28:37 +00007611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7612 " image->colors=%d (%d)",
7613 (int) image->colors, image_colors);
7614
7615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7616 " Update the pixel indexes");
7617 }
glennrp6185c532011-01-14 17:58:40 +00007618
glennrpfd05d622011-02-25 04:10:33 +00007619 /* Sync the pixel indices with the new colormap */
7620
glennrpd71e86a2011-02-24 01:28:37 +00007621 for (y=0; y < (ssize_t) image->rows; y++)
7622 {
7623 q=GetAuthenticPixels(image,0,y,image->columns,1,
7624 exception);
glennrp6185c532011-01-14 17:58:40 +00007625
glennrpd71e86a2011-02-24 01:28:37 +00007626 if (q == (PixelPacket *) NULL)
7627 break;
glennrp6185c532011-01-14 17:58:40 +00007628
glennrpd71e86a2011-02-24 01:28:37 +00007629 indexes=GetAuthenticIndexQueue(image);
7630
7631 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007632 {
glennrpd71e86a2011-02-24 01:28:37 +00007633 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007634 {
glennrpd71e86a2011-02-24 01:28:37 +00007635 if ((image->matte == MagickFalse ||
glennrpca7ad3a2011-04-26 04:44:54 +00007636 image->colormap[i].opacity ==
7637 GetOpacityPixelComponent(q)) &&
7638 image->colormap[i].red ==
7639 GetRedPixelComponent(q) &&
7640 image->colormap[i].green ==
7641 GetGreenPixelComponent(q) &&
7642 image->colormap[i].blue ==
7643 GetBluePixelComponent(q))
glennrp6185c532011-01-14 17:58:40 +00007644 {
glennrpd71e86a2011-02-24 01:28:37 +00007645 indexes[x]=(IndexPacket) i;
7646 break;
glennrp6185c532011-01-14 17:58:40 +00007647 }
glennrp6185c532011-01-14 17:58:40 +00007648 }
glennrpd71e86a2011-02-24 01:28:37 +00007649 q++;
7650 }
glennrp6185c532011-01-14 17:58:40 +00007651
glennrpd71e86a2011-02-24 01:28:37 +00007652 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7653 break;
7654 }
7655 }
7656 }
7657
7658 if (logging != MagickFalse)
7659 {
7660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7661 " image->colors=%d", (int) image->colors);
7662
7663 if (image->colormap != NULL)
7664 {
7665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7666 " i (red,green,blue,opacity)");
7667
7668 for (i=0; i < (ssize_t) image->colors; i++)
7669 {
cristy72988482011-03-29 16:34:38 +00007670 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007671 {
7672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7673 " %d (%d,%d,%d,%d)",
7674 (int) i,
7675 (int) image->colormap[i].red,
7676 (int) image->colormap[i].green,
7677 (int) image->colormap[i].blue,
7678 (int) image->colormap[i].opacity);
7679 }
glennrp6185c532011-01-14 17:58:40 +00007680 }
7681 }
glennrp03812ae2010-12-24 01:31:34 +00007682
glennrpd71e86a2011-02-24 01:28:37 +00007683 if (number_transparent < 257)
7684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7685 " number_transparent = %d",
7686 number_transparent);
7687 else
glennrp03812ae2010-12-24 01:31:34 +00007688
glennrpd71e86a2011-02-24 01:28:37 +00007689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7690 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007691
glennrpd71e86a2011-02-24 01:28:37 +00007692 if (number_opaque < 257)
7693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7694 " number_opaque = %d",
7695 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007696
glennrpd71e86a2011-02-24 01:28:37 +00007697 else
7698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007700
glennrpd71e86a2011-02-24 01:28:37 +00007701 if (number_semitransparent < 257)
7702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7703 " number_semitransparent = %d",
7704 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007705
glennrpd71e86a2011-02-24 01:28:37 +00007706 else
7707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7708 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007709
glennrpd71e86a2011-02-24 01:28:37 +00007710 if (ping_have_non_bw == MagickFalse)
7711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7712 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007713
glennrpd71e86a2011-02-24 01:28:37 +00007714 else if (ping_have_color == MagickFalse)
7715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7716 " All pixels and the background are gray");
7717
7718 else
7719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7720 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007721
glennrp03812ae2010-12-24 01:31:34 +00007722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7723 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007724 }
glennrpfd05d622011-02-25 04:10:33 +00007725
glennrpc8c2f062011-02-25 19:00:33 +00007726 if (mng_info->write_png8 == MagickFalse)
7727 break;
glennrpfd05d622011-02-25 04:10:33 +00007728
glennrpc8c2f062011-02-25 19:00:33 +00007729 /* Make any reductions necessary for the PNG8 format */
7730 if (image_colors <= 256 &&
7731 image_colors != 0 && image->colormap != NULL &&
7732 number_semitransparent == 0 &&
7733 number_transparent <= 1)
7734 break;
7735
7736 /* PNG8 can't have semitransparent colors so we threshold the
7737 * opacity to 0 or OpaqueOpacity
7738 */
7739 if (number_semitransparent != 0)
7740 {
7741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7742 " Thresholding the alpha channel to binary");
7743
7744 for (y=0; y < (ssize_t) image->rows; y++)
7745 {
7746 r=GetAuthenticPixels(image,0,y,image->columns,1,
7747 exception);
7748
7749 if (r == (PixelPacket *) NULL)
7750 break;
7751
7752 for (x=0; x < (ssize_t) image->columns; x++)
7753 {
glennrpa18d5bc2011-04-23 14:51:34 +00007754 SetOpacityPixelComponent(r,
7755 (GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7756 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00007757 r++;
7758 }
7759
7760 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7761 break;
7762
7763 if (image_colors != 0 && image_colors <= 256 &&
7764 image->colormap != NULL)
7765 for (i=0; i<image_colors; i++)
7766 image->colormap[i].opacity =
glennrp82b3c532011-03-22 19:20:54 +00007767 image->colormap[i].opacity > TransparentOpacity/2 ?
7768 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007769 }
7770 continue;
7771 }
7772
7773 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007774 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7775 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7776 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007777 */
glennrpd3371642011-03-22 19:42:23 +00007778 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7779 {
7780 if (logging != MagickFalse)
7781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7782 " Quantizing the background color to 4-4-4");
7783
7784 tried_444 = MagickTrue;
7785
7786 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007787 ScaleCharToQuantum(
7788 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7789 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007790 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007791 ScaleCharToQuantum(
7792 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7793 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007794 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007795 ScaleCharToQuantum(
7796 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7797 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007798
7799 if (logging != MagickFalse)
7800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7801 " Quantizing the pixel colors to 4-4-4");
7802
7803 if (image->colormap == NULL)
7804 {
7805 for (y=0; y < (ssize_t) image->rows; y++)
7806 {
7807 r=GetAuthenticPixels(image,0,y,image->columns,1,
7808 exception);
7809
7810 if (r == (PixelPacket *) NULL)
7811 break;
7812
7813 for (x=0; x < (ssize_t) image->columns; x++)
7814 {
7815 if (r->opacity == TransparentOpacity)
7816 {
glennrpb5d5e6d2011-04-27 17:08:19 +00007817 SetRGBPixelComponents(r,image->background_color);
glennrpd3371642011-03-22 19:42:23 +00007818 }
7819 else
7820 {
glennrp3faa9a32011-04-23 14:00:25 +00007821 r->red=ScaleCharToQuantum(
7822 (ScaleQuantumToChar(r->red) & 0xf0) |
7823 (ScaleQuantumToChar(r->red) & 0xf0) >> 4);
7824 r->green=ScaleCharToQuantum(
7825 (ScaleQuantumToChar(r->green) & 0xf0) |
7826 (ScaleQuantumToChar(r->green) & 0xf0) >> 4);
7827 r->blue=ScaleCharToQuantum(
7828 (ScaleQuantumToChar(r->blue) & 0xf0) |
7829 (ScaleQuantumToChar(r->blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007830 }
7831 r++;
7832 }
7833
7834 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7835 break;
7836 }
7837 }
7838
7839 else /* Should not reach this; colormap already exists and
7840 must be <= 256 */
7841 {
7842 if (logging != MagickFalse)
7843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7844 " Quantizing the colormap to 4-4-4");
7845 for (i=0; i<image_colors; i++)
7846 {
glennrp3faa9a32011-04-23 14:00:25 +00007847 image->colormap[i].red=ScaleCharToQuantum(
7848 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7849 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7850 image->colormap[i].green=ScaleCharToQuantum(
7851 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7852 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7853 image->colormap[i].blue=ScaleCharToQuantum(
7854 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7855 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
glennrpd3371642011-03-22 19:42:23 +00007856 }
7857 }
7858 continue;
7859 }
7860
glennrp82b3c532011-03-22 19:20:54 +00007861 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7862 {
7863 if (logging != MagickFalse)
7864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7865 " Quantizing the background color to 3-3-3");
7866
7867 tried_333 = MagickTrue;
7868
7869 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007870 ScaleCharToQuantum(
7871 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7872 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7873 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007874 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007875 ScaleCharToQuantum(
7876 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7877 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7878 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007879 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007880 ScaleCharToQuantum(
7881 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7882 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7883 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007884
7885 if (logging != MagickFalse)
7886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007887 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007888
7889 if (image->colormap == NULL)
7890 {
7891 for (y=0; y < (ssize_t) image->rows; y++)
7892 {
7893 r=GetAuthenticPixels(image,0,y,image->columns,1,
7894 exception);
7895
7896 if (r == (PixelPacket *) NULL)
7897 break;
7898
7899 for (x=0; x < (ssize_t) image->columns; x++)
7900 {
7901 if (r->opacity == TransparentOpacity)
7902 {
7903 r->red = image->background_color.red;
7904 r->green = image->background_color.green;
7905 r->blue = image->background_color.blue;
7906 }
7907 else
7908 {
glennrp3faa9a32011-04-23 14:00:25 +00007909 r->red=ScaleCharToQuantum(
7910 (ScaleQuantumToChar(r->red) & 0xe0) |
7911 (ScaleQuantumToChar(r->red) & 0xe0) >> 3 |
7912 (ScaleQuantumToChar(r->red) & 0xc0) >> 6);
7913 r->green=ScaleCharToQuantum(
7914 (ScaleQuantumToChar(r->green) & 0xe0) |
7915 (ScaleQuantumToChar(r->green) & 0xe0) >> 3 |
7916 (ScaleQuantumToChar(r->green) & 0xc0) >> 6);
7917 r->blue=ScaleCharToQuantum(
7918 (ScaleQuantumToChar(r->blue) & 0xe0) |
7919 (ScaleQuantumToChar(r->blue) & 0xe0) >> 3 |
7920 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007921 }
7922 r++;
7923 }
7924
7925 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7926 break;
7927 }
7928 }
7929
7930 else /* Should not reach this; colormap already exists and
7931 must be <= 256 */
7932 {
7933 if (logging != MagickFalse)
7934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007935 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007936 for (i=0; i<image_colors; i++)
7937 {
glennrp3faa9a32011-04-23 14:00:25 +00007938 image->colormap[i].red=ScaleCharToQuantum(
7939 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7940 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7941 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7942 image->colormap[i].green=ScaleCharToQuantum(
7943 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7944 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7945 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7946 image->colormap[i].blue=ScaleCharToQuantum(
7947 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7948 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7949 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007950 }
glennrpd3371642011-03-22 19:42:23 +00007951 }
7952 continue;
glennrp82b3c532011-03-22 19:20:54 +00007953 }
glennrpc8c2f062011-02-25 19:00:33 +00007954
glennrpc8c2f062011-02-25 19:00:33 +00007955 if (image_colors == 0 || image_colors > 256)
7956 {
7957 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007959 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007960
glennrp3faa9a32011-04-23 14:00:25 +00007961 /* Red and green were already done so we only quantize the blue
7962 * channel
7963 */
7964
7965 image->background_color.blue=ScaleCharToQuantum(
7966 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7967 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7968 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7969 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrpfd05d622011-02-25 04:10:33 +00007970
glennrpc8c2f062011-02-25 19:00:33 +00007971 if (logging != MagickFalse)
7972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007973 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007974
glennrpc8c2f062011-02-25 19:00:33 +00007975 if (image->colormap == NULL)
7976 {
7977 for (y=0; y < (ssize_t) image->rows; y++)
7978 {
7979 r=GetAuthenticPixels(image,0,y,image->columns,1,
7980 exception);
7981
7982 if (r == (PixelPacket *) NULL)
7983 break;
7984
7985 for (x=0; x < (ssize_t) image->columns; x++)
7986 {
glennrp82b3c532011-03-22 19:20:54 +00007987 if (r->opacity == TransparentOpacity)
7988 {
glennrpb5d5e6d2011-04-27 17:08:19 +00007989 SetRGBPixelComponents(r,image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00007990 }
7991 else
7992 {
glennrp3faa9a32011-04-23 14:00:25 +00007993 r->blue=ScaleCharToQuantum(
7994 (ScaleQuantumToChar(r->blue) & 0xc0) |
7995 (ScaleQuantumToChar(r->blue) & 0xc0) >> 2 |
7996 (ScaleQuantumToChar(r->blue) & 0xc0) >> 4 |
7997 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007998 }
glennrp52a479c2011-02-26 21:14:38 +00007999 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008000 }
glennrpfd05d622011-02-25 04:10:33 +00008001
glennrpc8c2f062011-02-25 19:00:33 +00008002 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8003 break;
8004 }
8005 }
glennrpfd05d622011-02-25 04:10:33 +00008006
glennrpc8c2f062011-02-25 19:00:33 +00008007 else /* Should not reach this; colormap already exists and
8008 must be <= 256 */
8009 {
8010 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008012 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008013 for (i=0; i<image_colors; i++)
8014 {
glennrp3faa9a32011-04-23 14:00:25 +00008015 image->colormap[i].blue=ScaleCharToQuantum(
8016 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
8017 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
8018 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
8019 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrpc8c2f062011-02-25 19:00:33 +00008020 }
8021 }
8022 continue;
8023 }
8024 break;
glennrpd71e86a2011-02-24 01:28:37 +00008025 }
glennrpfd05d622011-02-25 04:10:33 +00008026 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008027
glennrpfd05d622011-02-25 04:10:33 +00008028 /* If we are excluding the tRNS chunk and there is transparency,
8029 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8030 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008031 */
glennrp0e8ea192010-12-24 18:00:33 +00008032 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8033 (number_transparent != 0 || number_semitransparent != 0))
8034 {
8035 int colortype=mng_info->write_png_colortype;
8036
8037 if (ping_have_color == MagickFalse)
8038 mng_info->write_png_colortype = 5;
8039
8040 else
8041 mng_info->write_png_colortype = 7;
8042
glennrp8d579662011-02-23 02:05:02 +00008043 if (colortype != 0 &&
8044 mng_info->write_png_colortype != (ssize_t) colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008045 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008046
glennrp0e8ea192010-12-24 18:00:33 +00008047 }
8048
glennrpfd05d622011-02-25 04:10:33 +00008049 /* See if cheap transparency is possible. It is only possible
8050 * when there is a single transparent color, no semitransparent
8051 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008052 * as the transparent color. We only need this information if
8053 * we are writing a PNG with colortype 0 or 2, and we have not
8054 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008055 */
glennrp5a39f372011-02-25 04:52:16 +00008056 if (number_transparent == 1 &&
8057 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008058 {
8059 ping_have_cheap_transparency = MagickTrue;
8060
8061 if (number_semitransparent != 0)
8062 ping_have_cheap_transparency = MagickFalse;
8063
8064 else if (image_colors == 0 || image_colors > 256 ||
8065 image->colormap == NULL)
8066 {
8067 ExceptionInfo
8068 *exception;
8069
8070 register const PixelPacket
8071 *q;
8072
8073 exception=(&image->exception);
8074
8075 for (y=0; y < (ssize_t) image->rows; y++)
8076 {
8077 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8078
8079 if (q == (PixelPacket *) NULL)
8080 break;
8081
8082 for (x=0; x < (ssize_t) image->columns; x++)
8083 {
8084 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008085 (unsigned short) GetRedPixelComponent(q) ==
8086 ping_trans_color.red &&
8087 (unsigned short) GetGreenPixelComponent(q) ==
8088 ping_trans_color.green &&
8089 (unsigned short) GetBluePixelComponent(q) ==
8090 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008091 {
8092 ping_have_cheap_transparency = MagickFalse;
8093 break;
8094 }
8095
8096 q++;
8097 }
8098
8099 if (ping_have_cheap_transparency == MagickFalse)
8100 break;
8101 }
8102 }
8103 else
8104 {
glennrp67b9c1a2011-04-22 18:47:36 +00008105 /* Assuming that image->colormap[0] is the one transparent color
8106 * and that all others are opaque.
8107 */
glennrpfd05d622011-02-25 04:10:33 +00008108 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008109 for (i=1; i<image_colors; i++)
8110 if (image->colormap[i].red == image->colormap[0].red &&
8111 image->colormap[i].green == image->colormap[0].green &&
8112 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008113 {
glennrp67b9c1a2011-04-22 18:47:36 +00008114 ping_have_cheap_transparency = MagickFalse;
8115 break;
glennrpfd05d622011-02-25 04:10:33 +00008116 }
8117 }
8118
8119 if (logging != MagickFalse)
8120 {
8121 if (ping_have_cheap_transparency == MagickFalse)
8122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8123 " Cheap transparency is not possible.");
8124
8125 else
8126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8127 " Cheap transparency is possible.");
8128 }
8129 }
8130 else
8131 ping_have_cheap_transparency = MagickFalse;
8132
glennrp8640fb52010-11-23 15:48:26 +00008133 image_depth=image->depth;
8134
glennrp26c990a2010-11-23 02:23:20 +00008135 quantum_info = (QuantumInfo *) NULL;
8136 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008137 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008138 image_matte=image->matte;
8139
glennrp0fe50b42010-11-16 03:52:51 +00008140 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008141 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008142
glennrp52a479c2011-02-26 21:14:38 +00008143 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8144 (image->colors == 0 || image->colormap == NULL))
8145 {
glennrp52a479c2011-02-26 21:14:38 +00008146 image_info=DestroyImageInfo(image_info);
8147 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008148 (void) ThrowMagickException(&IMimage->exception,
8149 GetMagickModule(),CoderError,
8150 "Cannot write PNG8 or color-type 3; colormap is NULL",
8151 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008152#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8153 UnlockSemaphoreInfo(ping_semaphore);
8154#endif
8155 return(MagickFalse);
8156 }
8157
cristy3ed852e2009-09-05 21:47:34 +00008158 /*
8159 Allocate the PNG structures
8160 */
8161#ifdef PNG_USER_MEM_SUPPORTED
8162 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008163 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8164 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008165
cristy3ed852e2009-09-05 21:47:34 +00008166#else
8167 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008168 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008169
cristy3ed852e2009-09-05 21:47:34 +00008170#endif
8171 if (ping == (png_struct *) NULL)
8172 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008173
cristy3ed852e2009-09-05 21:47:34 +00008174 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008175
cristy3ed852e2009-09-05 21:47:34 +00008176 if (ping_info == (png_info *) NULL)
8177 {
8178 png_destroy_write_struct(&ping,(png_info **) NULL);
8179 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8180 }
glennrp0fe50b42010-11-16 03:52:51 +00008181
cristy3ed852e2009-09-05 21:47:34 +00008182 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008183 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008184
glennrp5af765f2010-03-30 11:12:18 +00008185 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008186 {
8187 /*
8188 PNG write failed.
8189 */
8190#ifdef PNG_DEBUG
8191 if (image_info->verbose)
8192 (void) printf("PNG write has failed.\n");
8193#endif
8194 png_destroy_write_struct(&ping,&ping_info);
8195#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008196 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008197#endif
glennrpda8f3a72011-02-27 23:54:12 +00008198 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008199 (void) CloseBlob(image);
8200 image_info=DestroyImageInfo(image_info);
8201 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008202 return(MagickFalse);
8203 }
8204 /*
8205 Prepare PNG for writing.
8206 */
8207#if defined(PNG_MNG_FEATURES_SUPPORTED)
8208 if (mng_info->write_mng)
8209 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008210
cristy3ed852e2009-09-05 21:47:34 +00008211#else
8212# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8213 if (mng_info->write_mng)
8214 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008215
cristy3ed852e2009-09-05 21:47:34 +00008216# endif
8217#endif
glennrp2b013e42010-11-24 16:55:50 +00008218
cristy3ed852e2009-09-05 21:47:34 +00008219 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008220
cristy4e5bc842010-06-09 13:56:01 +00008221 ping_width=(png_uint_32) image->columns;
8222 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008223
cristy3ed852e2009-09-05 21:47:34 +00008224 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8225 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008226
cristy3ed852e2009-09-05 21:47:34 +00008227 if (mng_info->write_png_depth != 0)
8228 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008229
cristy3ed852e2009-09-05 21:47:34 +00008230 /* Adjust requested depth to next higher valid depth if necessary */
8231 if (image_depth > 8)
8232 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008233
cristy3ed852e2009-09-05 21:47:34 +00008234 if ((image_depth > 4) && (image_depth < 8))
8235 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008236
cristy3ed852e2009-09-05 21:47:34 +00008237 if (image_depth == 3)
8238 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008239
cristy3ed852e2009-09-05 21:47:34 +00008240 if (logging != MagickFalse)
8241 {
8242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008243 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008245 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008247 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008249 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008251 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008252 }
glennrp8640fb52010-11-23 15:48:26 +00008253
cristy3ed852e2009-09-05 21:47:34 +00008254 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008255 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008256
glennrp26f37912010-12-23 16:22:42 +00008257
cristy3ed852e2009-09-05 21:47:34 +00008258#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008259 if (ping_exclude_pHYs == MagickFalse)
8260 {
cristy3ed852e2009-09-05 21:47:34 +00008261 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8262 (!mng_info->write_mng || !mng_info->equal_physs))
8263 {
glennrp0fe50b42010-11-16 03:52:51 +00008264 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8266 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008267
8268 if (image->units == PixelsPerInchResolution)
8269 {
glennrpdfd70802010-11-14 01:23:35 +00008270 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008271 ping_pHYs_x_resolution=
8272 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8273 ping_pHYs_y_resolution=
8274 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008275 }
glennrpdfd70802010-11-14 01:23:35 +00008276
cristy3ed852e2009-09-05 21:47:34 +00008277 else if (image->units == PixelsPerCentimeterResolution)
8278 {
glennrpdfd70802010-11-14 01:23:35 +00008279 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008280 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8281 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008282 }
glennrp991d11d2010-11-12 21:55:28 +00008283
cristy3ed852e2009-09-05 21:47:34 +00008284 else
8285 {
glennrpdfd70802010-11-14 01:23:35 +00008286 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8287 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8288 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008289 }
glennrp991d11d2010-11-12 21:55:28 +00008290
glennrp823b55c2011-03-14 18:46:46 +00008291 if (logging != MagickFalse)
8292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8293 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8294 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8295 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008296 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008297 }
glennrp26f37912010-12-23 16:22:42 +00008298 }
cristy3ed852e2009-09-05 21:47:34 +00008299#endif
glennrpa521b2f2010-10-29 04:11:03 +00008300
glennrp26f37912010-12-23 16:22:42 +00008301 if (ping_exclude_bKGD == MagickFalse)
8302 {
glennrpa521b2f2010-10-29 04:11:03 +00008303 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008304 {
glennrpa521b2f2010-10-29 04:11:03 +00008305 unsigned int
8306 mask;
cristy3ed852e2009-09-05 21:47:34 +00008307
glennrpa521b2f2010-10-29 04:11:03 +00008308 mask=0xffff;
8309 if (ping_bit_depth == 8)
8310 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008311
glennrpa521b2f2010-10-29 04:11:03 +00008312 if (ping_bit_depth == 4)
8313 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008314
glennrpa521b2f2010-10-29 04:11:03 +00008315 if (ping_bit_depth == 2)
8316 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008317
glennrpa521b2f2010-10-29 04:11:03 +00008318 if (ping_bit_depth == 1)
8319 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008320
glennrpa521b2f2010-10-29 04:11:03 +00008321 ping_background.red=(png_uint_16)
8322 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008323
glennrpa521b2f2010-10-29 04:11:03 +00008324 ping_background.green=(png_uint_16)
8325 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008326
glennrpa521b2f2010-10-29 04:11:03 +00008327 ping_background.blue=(png_uint_16)
8328 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008329
8330 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008331 }
cristy3ed852e2009-09-05 21:47:34 +00008332
glennrp0fe50b42010-11-16 03:52:51 +00008333 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008334 {
8335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8336 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8338 " background_color index is %d",
8339 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008340
8341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8342 " ping_bit_depth=%d",ping_bit_depth);
8343 }
glennrp0fe50b42010-11-16 03:52:51 +00008344
8345 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008346 }
glennrp0fe50b42010-11-16 03:52:51 +00008347
cristy3ed852e2009-09-05 21:47:34 +00008348 /*
8349 Select the color type.
8350 */
8351 matte=image_matte;
8352 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008353
glennrp1273f7b2011-02-24 03:20:30 +00008354 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008355 {
glennrp0fe50b42010-11-16 03:52:51 +00008356
glennrpfd05d622011-02-25 04:10:33 +00008357 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008358 for reducing the sample depth from 8. */
8359
glennrp0fe50b42010-11-16 03:52:51 +00008360 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008361
glennrp8bb3a022010-12-13 20:40:04 +00008362 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008363
8364 /*
8365 Set image palette.
8366 */
8367 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8368
glennrp0fe50b42010-11-16 03:52:51 +00008369 if (logging != MagickFalse)
8370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8371 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008372 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008373
8374 for (i=0; i < (ssize_t) number_colors; i++)
8375 {
8376 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8377 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8378 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8379 if (logging != MagickFalse)
8380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008381#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008382 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008383#else
8384 " %5ld (%5d,%5d,%5d)",
8385#endif
glennrp0fe50b42010-11-16 03:52:51 +00008386 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8387
8388 }
glennrp2b013e42010-11-24 16:55:50 +00008389
glennrp8bb3a022010-12-13 20:40:04 +00008390 ping_have_PLTE=MagickTrue;
8391 image_depth=ping_bit_depth;
8392 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008393
glennrp58e01762011-01-07 15:28:54 +00008394 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008395 {
glennrp0fe50b42010-11-16 03:52:51 +00008396 /*
8397 Identify which colormap entry is transparent.
8398 */
8399 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008400 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008401
glennrp8bb3a022010-12-13 20:40:04 +00008402 for (i=0; i < (ssize_t) number_transparent; i++)
8403 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008404
glennrp0fe50b42010-11-16 03:52:51 +00008405
glennrp2cc891a2010-12-24 13:44:32 +00008406 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008407 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008408
8409 if (ping_num_trans == 0)
8410 ping_have_tRNS=MagickFalse;
8411
glennrp8bb3a022010-12-13 20:40:04 +00008412 else
8413 ping_have_tRNS=MagickTrue;
8414 }
glennrp0fe50b42010-11-16 03:52:51 +00008415
glennrp1273f7b2011-02-24 03:20:30 +00008416 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008417 {
glennrp1273f7b2011-02-24 03:20:30 +00008418 /*
8419 * Identify which colormap entry is the background color.
8420 */
8421
glennrp4f25bd02011-01-01 18:51:28 +00008422 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8423 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8424 break;
glennrp0fe50b42010-11-16 03:52:51 +00008425
glennrp4f25bd02011-01-01 18:51:28 +00008426 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00008427
8428 if (logging != MagickFalse)
8429 {
8430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8431 " background_color index is %d",
8432 (int) ping_background.index);
8433 }
glennrp4f25bd02011-01-01 18:51:28 +00008434 }
cristy3ed852e2009-09-05 21:47:34 +00008435 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008436
cristy3ed852e2009-09-05 21:47:34 +00008437 else if (mng_info->write_png24)
8438 {
8439 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008440 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008441 }
glennrp0fe50b42010-11-16 03:52:51 +00008442
cristy3ed852e2009-09-05 21:47:34 +00008443 else if (mng_info->write_png32)
8444 {
8445 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008446 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008447 }
glennrp0fe50b42010-11-16 03:52:51 +00008448
glennrp8bb3a022010-12-13 20:40:04 +00008449 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008450 {
glennrp5af765f2010-03-30 11:12:18 +00008451 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008452
glennrp8bb3a022010-12-13 20:40:04 +00008453 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008454 {
glennrp5af765f2010-03-30 11:12:18 +00008455 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008456
glennrp5af765f2010-03-30 11:12:18 +00008457 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8458 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008459 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008460
glennrp8bb3a022010-12-13 20:40:04 +00008461 else
8462 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008463
8464 if (logging != MagickFalse)
8465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8466 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008467 }
glennrp0fe50b42010-11-16 03:52:51 +00008468
glennrp7c4c9e62011-03-21 20:23:32 +00008469 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008470 {
8471 if (logging != MagickFalse)
8472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008473 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008474
glennrpd6bf1612010-12-17 17:28:54 +00008475 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008476 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008477
glennrpd6bf1612010-12-17 17:28:54 +00008478 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008479 {
glennrp5af765f2010-03-30 11:12:18 +00008480 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008481 image_matte=MagickFalse;
8482 }
glennrp0fe50b42010-11-16 03:52:51 +00008483
glennrpd6bf1612010-12-17 17:28:54 +00008484 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008485 {
glennrp5af765f2010-03-30 11:12:18 +00008486 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008487 image_matte=MagickTrue;
8488 }
glennrp0fe50b42010-11-16 03:52:51 +00008489
glennrp5aa37f62011-01-02 03:07:57 +00008490 if (image_info->type == PaletteType ||
8491 image_info->type == PaletteMatteType)
8492 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8493
glennrp7c4c9e62011-03-21 20:23:32 +00008494 if (mng_info->write_png_colortype == 0 &&
8495 (image_info->type == UndefinedType ||
8496 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008497 {
glennrp5aa37f62011-01-02 03:07:57 +00008498 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008499 {
glennrp5aa37f62011-01-02 03:07:57 +00008500 if (image_matte == MagickFalse)
8501 {
8502 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8503 image_matte=MagickFalse;
8504 }
glennrp0fe50b42010-11-16 03:52:51 +00008505
glennrp0b206f52011-01-07 04:55:32 +00008506 else
glennrp5aa37f62011-01-02 03:07:57 +00008507 {
8508 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8509 image_matte=MagickTrue;
8510 }
8511 }
8512 else
glennrp8bb3a022010-12-13 20:40:04 +00008513 {
glennrp5aa37f62011-01-02 03:07:57 +00008514 if (image_matte == MagickFalse)
8515 {
8516 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8517 image_matte=MagickFalse;
8518 }
glennrp8bb3a022010-12-13 20:40:04 +00008519
glennrp0b206f52011-01-07 04:55:32 +00008520 else
glennrp5aa37f62011-01-02 03:07:57 +00008521 {
8522 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8523 image_matte=MagickTrue;
8524 }
8525 }
glennrp0fe50b42010-11-16 03:52:51 +00008526 }
glennrp5aa37f62011-01-02 03:07:57 +00008527
cristy3ed852e2009-09-05 21:47:34 +00008528 }
glennrp0fe50b42010-11-16 03:52:51 +00008529
cristy3ed852e2009-09-05 21:47:34 +00008530 if (logging != MagickFalse)
8531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008532 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008533
glennrp5af765f2010-03-30 11:12:18 +00008534 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008535 {
8536 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8537 ping_color_type == PNG_COLOR_TYPE_RGB ||
8538 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8539 ping_bit_depth=8;
8540 }
cristy3ed852e2009-09-05 21:47:34 +00008541
glennrpd6bf1612010-12-17 17:28:54 +00008542 old_bit_depth=ping_bit_depth;
8543
glennrp5af765f2010-03-30 11:12:18 +00008544 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008545 {
glennrp8d579662011-02-23 02:05:02 +00008546 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8547 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008548 }
glennrp8640fb52010-11-23 15:48:26 +00008549
glennrp5af765f2010-03-30 11:12:18 +00008550 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008551 {
cristy35ef8242010-06-03 16:24:13 +00008552 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008553 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008554
8555 if (image->colors == 0)
8556 {
glennrp0fe50b42010-11-16 03:52:51 +00008557 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008558 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008559 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008560 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008561 }
8562
cristy35ef8242010-06-03 16:24:13 +00008563 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008564 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008565 }
glennrp2b013e42010-11-24 16:55:50 +00008566
glennrpd6bf1612010-12-17 17:28:54 +00008567 if (logging != MagickFalse)
8568 {
8569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8570 " Number of colors: %.20g",(double) image_colors);
8571
8572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8573 " Tentative PNG bit depth: %d",ping_bit_depth);
8574 }
8575
8576 if (ping_bit_depth < (int) mng_info->write_png_depth)
8577 ping_bit_depth = mng_info->write_png_depth;
8578 }
glennrp2cc891a2010-12-24 13:44:32 +00008579
glennrp5af765f2010-03-30 11:12:18 +00008580 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008581
cristy3ed852e2009-09-05 21:47:34 +00008582 if (logging != MagickFalse)
8583 {
8584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008585 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008586
cristy3ed852e2009-09-05 21:47:34 +00008587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008588 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008589
cristy3ed852e2009-09-05 21:47:34 +00008590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008591 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008592
cristy3ed852e2009-09-05 21:47:34 +00008593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008594
glennrp8640fb52010-11-23 15:48:26 +00008595 " image->depth: %.20g",(double) image->depth);
8596
8597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008598 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008599 }
8600
glennrp58e01762011-01-07 15:28:54 +00008601 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008602 {
glennrp4f25bd02011-01-01 18:51:28 +00008603 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008604 {
glennrp7c4c9e62011-03-21 20:23:32 +00008605 if (mng_info->write_png_colortype == 0)
8606 {
8607 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008608
glennrp7c4c9e62011-03-21 20:23:32 +00008609 if (ping_have_color != MagickFalse)
8610 ping_color_type=PNG_COLOR_TYPE_RGBA;
8611 }
glennrp4f25bd02011-01-01 18:51:28 +00008612
8613 /*
8614 * Determine if there is any transparent color.
8615 */
8616 if (number_transparent + number_semitransparent == 0)
8617 {
8618 /*
8619 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8620 */
glennrpa6a06632011-01-19 15:15:34 +00008621
glennrp4f25bd02011-01-01 18:51:28 +00008622 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008623
8624 if (mng_info->write_png_colortype == 0)
8625 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008626 }
8627
8628 else
8629 {
8630 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008631 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008632
8633 mask=0xffff;
8634
8635 if (ping_bit_depth == 8)
8636 mask=0x00ff;
8637
8638 if (ping_bit_depth == 4)
8639 mask=0x000f;
8640
8641 if (ping_bit_depth == 2)
8642 mask=0x0003;
8643
8644 if (ping_bit_depth == 1)
8645 mask=0x0001;
8646
8647 ping_trans_color.red=(png_uint_16)
8648 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8649
8650 ping_trans_color.green=(png_uint_16)
8651 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8652
8653 ping_trans_color.blue=(png_uint_16)
8654 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8655
8656 ping_trans_color.gray=(png_uint_16)
8657 (ScaleQuantumToShort(PixelIntensityToQuantum(
8658 image->colormap)) & mask);
8659
8660 ping_trans_color.index=(png_byte) 0;
8661
8662 ping_have_tRNS=MagickTrue;
8663 }
8664
8665 if (ping_have_tRNS != MagickFalse)
8666 {
8667 /*
glennrpfd05d622011-02-25 04:10:33 +00008668 * Determine if there is one and only one transparent color
8669 * and if so if it is fully transparent.
8670 */
8671 if (ping_have_cheap_transparency == MagickFalse)
8672 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008673 }
8674
8675 if (ping_have_tRNS != MagickFalse)
8676 {
glennrp7c4c9e62011-03-21 20:23:32 +00008677 if (mng_info->write_png_colortype == 0)
8678 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008679
8680 if (image_depth == 8)
8681 {
8682 ping_trans_color.red&=0xff;
8683 ping_trans_color.green&=0xff;
8684 ping_trans_color.blue&=0xff;
8685 ping_trans_color.gray&=0xff;
8686 }
8687 }
8688 }
cristy3ed852e2009-09-05 21:47:34 +00008689 else
8690 {
cristy3ed852e2009-09-05 21:47:34 +00008691 if (image_depth == 8)
8692 {
glennrp5af765f2010-03-30 11:12:18 +00008693 ping_trans_color.red&=0xff;
8694 ping_trans_color.green&=0xff;
8695 ping_trans_color.blue&=0xff;
8696 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008697 }
8698 }
8699 }
glennrp8640fb52010-11-23 15:48:26 +00008700
cristy3ed852e2009-09-05 21:47:34 +00008701 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008702
glennrp2e09f552010-11-14 00:38:48 +00008703 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008704 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008705
glennrp39992b42010-11-14 00:03:43 +00008706 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008707 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008708 ping_have_color == MagickFalse &&
8709 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008710 {
cristy35ef8242010-06-03 16:24:13 +00008711 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008712
cristy3ed852e2009-09-05 21:47:34 +00008713 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008714 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008715
glennrp7c4c9e62011-03-21 20:23:32 +00008716 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008717 {
glennrp5af765f2010-03-30 11:12:18 +00008718 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008719
cristy3ed852e2009-09-05 21:47:34 +00008720 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008721 {
8722 if (logging != MagickFalse)
8723 {
8724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8725 " Scaling ping_trans_color (0)");
8726 }
8727 ping_trans_color.gray*=0x0101;
8728 }
cristy3ed852e2009-09-05 21:47:34 +00008729 }
glennrp0fe50b42010-11-16 03:52:51 +00008730
cristy3ed852e2009-09-05 21:47:34 +00008731 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8732 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008733
glennrp136ee3a2011-04-27 15:47:45 +00008734 if ((image_colors == 0) ||
8735 ((ssize_t) (image_colors-1) > MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008736 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008737
cristy3ed852e2009-09-05 21:47:34 +00008738 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008739 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008740
cristy3ed852e2009-09-05 21:47:34 +00008741 else
8742 {
glennrp5af765f2010-03-30 11:12:18 +00008743 ping_bit_depth=8;
8744 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008745 {
8746 if(!mng_info->write_png_depth)
8747 {
glennrp5af765f2010-03-30 11:12:18 +00008748 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008749
cristy35ef8242010-06-03 16:24:13 +00008750 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008751 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008752 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008753 }
8754 }
glennrp2b013e42010-11-24 16:55:50 +00008755
glennrp0fe50b42010-11-16 03:52:51 +00008756 else if (ping_color_type ==
8757 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008758 mng_info->IsPalette)
8759 {
cristy3ed852e2009-09-05 21:47:34 +00008760 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008761
cristy3ed852e2009-09-05 21:47:34 +00008762 int
8763 depth_4_ok=MagickTrue,
8764 depth_2_ok=MagickTrue,
8765 depth_1_ok=MagickTrue;
8766
cristybb503372010-05-27 20:51:26 +00008767 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008768 {
8769 unsigned char
8770 intensity;
8771
8772 intensity=ScaleQuantumToChar(image->colormap[i].red);
8773
8774 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8775 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8776 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8777 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008778 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008779 depth_1_ok=MagickFalse;
8780 }
glennrp2b013e42010-11-24 16:55:50 +00008781
cristy3ed852e2009-09-05 21:47:34 +00008782 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008783 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008784
cristy3ed852e2009-09-05 21:47:34 +00008785 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008786 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008787
cristy3ed852e2009-09-05 21:47:34 +00008788 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008789 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008790 }
8791 }
glennrp2b013e42010-11-24 16:55:50 +00008792
glennrp5af765f2010-03-30 11:12:18 +00008793 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008794 }
glennrp0fe50b42010-11-16 03:52:51 +00008795
cristy3ed852e2009-09-05 21:47:34 +00008796 else
glennrp0fe50b42010-11-16 03:52:51 +00008797
cristy3ed852e2009-09-05 21:47:34 +00008798 if (mng_info->IsPalette)
8799 {
glennrp17a14852010-05-10 03:01:59 +00008800 number_colors=image_colors;
8801
cristy3ed852e2009-09-05 21:47:34 +00008802 if (image_depth <= 8)
8803 {
cristy3ed852e2009-09-05 21:47:34 +00008804 /*
8805 Set image palette.
8806 */
glennrp5af765f2010-03-30 11:12:18 +00008807 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008808
glennrp58e01762011-01-07 15:28:54 +00008809 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008810 {
glennrp9c1eb072010-06-06 22:19:15 +00008811 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008812
glennrp3b51f0e2010-11-27 18:14:08 +00008813 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8815 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008816 }
glennrp0fe50b42010-11-16 03:52:51 +00008817
cristy3ed852e2009-09-05 21:47:34 +00008818 else
8819 {
cristybb503372010-05-27 20:51:26 +00008820 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008821 {
8822 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8823 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8824 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8825 }
glennrp0fe50b42010-11-16 03:52:51 +00008826
glennrp3b51f0e2010-11-27 18:14:08 +00008827 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008829 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008830 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008831
glennrp39992b42010-11-14 00:03:43 +00008832 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008833 }
glennrp0fe50b42010-11-16 03:52:51 +00008834
cristy3ed852e2009-09-05 21:47:34 +00008835 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008836 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008837 {
cristybefe4d22010-06-07 01:18:58 +00008838 size_t
8839 one;
8840
glennrp5af765f2010-03-30 11:12:18 +00008841 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008842 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008843
cristybefe4d22010-06-07 01:18:58 +00008844 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008845 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008846 }
glennrp0fe50b42010-11-16 03:52:51 +00008847
glennrp5af765f2010-03-30 11:12:18 +00008848 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008849
glennrp58e01762011-01-07 15:28:54 +00008850 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008851 {
glennrp0fe50b42010-11-16 03:52:51 +00008852 /*
glennrpd6bf1612010-12-17 17:28:54 +00008853 * Set up trans_colors array.
8854 */
glennrp0fe50b42010-11-16 03:52:51 +00008855 assert(number_colors <= 256);
8856
glennrpd6bf1612010-12-17 17:28:54 +00008857 ping_num_trans=(unsigned short) (number_transparent +
8858 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008859
8860 if (ping_num_trans == 0)
8861 ping_have_tRNS=MagickFalse;
8862
glennrpd6bf1612010-12-17 17:28:54 +00008863 else
glennrp0fe50b42010-11-16 03:52:51 +00008864 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008865 if (logging != MagickFalse)
8866 {
8867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8868 " Scaling ping_trans_color (1)");
8869 }
glennrpd6bf1612010-12-17 17:28:54 +00008870 ping_have_tRNS=MagickTrue;
8871
8872 for (i=0; i < ping_num_trans; i++)
8873 {
8874 ping_trans_alpha[i]= (png_byte) (255-
8875 ScaleQuantumToChar(image->colormap[i].opacity));
8876 }
glennrp0fe50b42010-11-16 03:52:51 +00008877 }
8878 }
cristy3ed852e2009-09-05 21:47:34 +00008879 }
8880 }
glennrp0fe50b42010-11-16 03:52:51 +00008881
cristy3ed852e2009-09-05 21:47:34 +00008882 else
8883 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008884
cristy3ed852e2009-09-05 21:47:34 +00008885 if (image_depth < 8)
8886 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008887
cristy3ed852e2009-09-05 21:47:34 +00008888 if ((save_image_depth == 16) && (image_depth == 8))
8889 {
glennrp4f25bd02011-01-01 18:51:28 +00008890 if (logging != MagickFalse)
8891 {
8892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8893 " Scaling ping_trans_color from (%d,%d,%d)",
8894 (int) ping_trans_color.red,
8895 (int) ping_trans_color.green,
8896 (int) ping_trans_color.blue);
8897 }
8898
glennrp5af765f2010-03-30 11:12:18 +00008899 ping_trans_color.red*=0x0101;
8900 ping_trans_color.green*=0x0101;
8901 ping_trans_color.blue*=0x0101;
8902 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008903
8904 if (logging != MagickFalse)
8905 {
8906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8907 " to (%d,%d,%d)",
8908 (int) ping_trans_color.red,
8909 (int) ping_trans_color.green,
8910 (int) ping_trans_color.blue);
8911 }
cristy3ed852e2009-09-05 21:47:34 +00008912 }
8913 }
8914
cristy4383ec82011-01-05 15:42:32 +00008915 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8916 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008917
cristy3ed852e2009-09-05 21:47:34 +00008918 /*
8919 Adjust background and transparency samples in sub-8-bit grayscale files.
8920 */
glennrp5af765f2010-03-30 11:12:18 +00008921 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008922 PNG_COLOR_TYPE_GRAY)
8923 {
8924 png_uint_16
8925 maxval;
8926
cristy35ef8242010-06-03 16:24:13 +00008927 size_t
8928 one=1;
8929
cristy22ffd972010-06-03 16:51:47 +00008930 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008931
glennrp4f25bd02011-01-01 18:51:28 +00008932 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008933 {
cristy3ed852e2009-09-05 21:47:34 +00008934
glennrpa521b2f2010-10-29 04:11:03 +00008935 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008936 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8937
8938 if (logging != MagickFalse)
8939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008940 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00008941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8942 " background_color index is %d",
8943 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008944
glennrp991d11d2010-11-12 21:55:28 +00008945 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008946 }
cristy3ed852e2009-09-05 21:47:34 +00008947
glennrp5af765f2010-03-30 11:12:18 +00008948 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8949 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008950 }
glennrp17a14852010-05-10 03:01:59 +00008951
glennrp26f37912010-12-23 16:22:42 +00008952 if (ping_exclude_bKGD == MagickFalse)
8953 {
glennrp1273f7b2011-02-24 03:20:30 +00008954 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008955 {
8956 /*
8957 Identify which colormap entry is the background color.
8958 */
8959
glennrp17a14852010-05-10 03:01:59 +00008960 number_colors=image_colors;
8961
glennrpa521b2f2010-10-29 04:11:03 +00008962 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8963 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008964 break;
8965
8966 ping_background.index=(png_byte) i;
8967
glennrp3b51f0e2010-11-27 18:14:08 +00008968 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008969 {
8970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008971 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008972 }
glennrp0fe50b42010-11-16 03:52:51 +00008973
cristy13d07042010-11-21 20:56:18 +00008974 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008975 {
8976 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008977
8978 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008979 {
8980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8981 " background =(%d,%d,%d)",
8982 (int) ping_background.red,
8983 (int) ping_background.green,
8984 (int) ping_background.blue);
8985 }
8986 }
glennrpa521b2f2010-10-29 04:11:03 +00008987
glennrpd6bf1612010-12-17 17:28:54 +00008988 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008989 {
glennrp3b51f0e2010-11-27 18:14:08 +00008990 if (logging != MagickFalse)
8991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8992 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008993 ping_have_bKGD = MagickFalse;
8994 }
glennrp17a14852010-05-10 03:01:59 +00008995 }
glennrp26f37912010-12-23 16:22:42 +00008996 }
glennrp17a14852010-05-10 03:01:59 +00008997
cristy3ed852e2009-09-05 21:47:34 +00008998 if (logging != MagickFalse)
8999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009000 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009001 /*
9002 Initialize compression level and filtering.
9003 */
9004 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009005 {
9006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007 " Setting up deflate compression");
9008
9009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9010 " Compression buffer size: 32768");
9011 }
9012
cristy3ed852e2009-09-05 21:47:34 +00009013 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009014
cristy3ed852e2009-09-05 21:47:34 +00009015 if (logging != MagickFalse)
9016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9017 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009018
cristy3ed852e2009-09-05 21:47:34 +00009019 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009020
cristy3ed852e2009-09-05 21:47:34 +00009021 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9022 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009023
cristy3ed852e2009-09-05 21:47:34 +00009024 if (quality > 9)
9025 {
9026 int
9027 level;
9028
cristybb503372010-05-27 20:51:26 +00009029 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009030
cristy3ed852e2009-09-05 21:47:34 +00009031 if (logging != MagickFalse)
9032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9033 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00009034
cristy3ed852e2009-09-05 21:47:34 +00009035 png_set_compression_level(ping,level);
9036 }
glennrp0fe50b42010-11-16 03:52:51 +00009037
cristy3ed852e2009-09-05 21:47:34 +00009038 else
9039 {
9040 if (logging != MagickFalse)
9041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9042 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009043
cristy3ed852e2009-09-05 21:47:34 +00009044 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9045 }
glennrp0fe50b42010-11-16 03:52:51 +00009046
cristy3ed852e2009-09-05 21:47:34 +00009047 if (logging != MagickFalse)
9048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9049 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009050
glennrp2b013e42010-11-24 16:55:50 +00009051#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009052 /* This became available in libpng-1.0.9. Output must be a MNG. */
9053 if (mng_info->write_mng && ((quality % 10) == 7))
9054 {
9055 if (logging != MagickFalse)
9056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9057 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009058
glennrp5af765f2010-03-30 11:12:18 +00009059 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009060 }
glennrp0fe50b42010-11-16 03:52:51 +00009061
cristy3ed852e2009-09-05 21:47:34 +00009062 else
9063 if (logging != MagickFalse)
9064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9065 " Filter_type: 0");
9066#endif
glennrp2b013e42010-11-24 16:55:50 +00009067
cristy3ed852e2009-09-05 21:47:34 +00009068 {
9069 int
9070 base_filter;
9071
9072 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009073 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009074
glennrp26c990a2010-11-23 02:23:20 +00009075 else
glennrp8640fb52010-11-23 15:48:26 +00009076 if ((quality % 10) != 5)
9077 base_filter=(int) quality % 10;
9078
9079 else
9080 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9081 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9082 (quality < 50))
9083 base_filter=PNG_NO_FILTERS;
9084
9085 else
9086 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009087
cristy3ed852e2009-09-05 21:47:34 +00009088 if (logging != MagickFalse)
9089 {
9090 if (base_filter == PNG_ALL_FILTERS)
9091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9092 " Base filter method: ADAPTIVE");
9093 else
9094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9095 " Base filter method: NONE");
9096 }
glennrp2b013e42010-11-24 16:55:50 +00009097
cristy3ed852e2009-09-05 21:47:34 +00009098 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9099 }
9100
glennrp823b55c2011-03-14 18:46:46 +00009101 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9102 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009103 {
9104 ResetImageProfileIterator(image);
9105 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009106 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009107 profile=GetImageProfile(image,name);
9108
9109 if (profile != (StringInfo *) NULL)
9110 {
glennrp5af765f2010-03-30 11:12:18 +00009111#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009112 if ((LocaleCompare(name,"ICC") == 0) ||
9113 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009114 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009115
9116 if (ping_exclude_iCCP == MagickFalse)
9117 {
9118 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009119#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009120 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009121#else
9122 (png_const_bytep) GetStringInfoDatum(profile),
9123#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009124 (png_uint_32) GetStringInfoLength(profile));
9125 }
glennrp26f37912010-12-23 16:22:42 +00009126 }
glennrp0fe50b42010-11-16 03:52:51 +00009127
glennrpc8cbc5d2011-01-01 00:12:34 +00009128 else
cristy3ed852e2009-09-05 21:47:34 +00009129#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009130 if (ping_exclude_zCCP == MagickFalse)
9131 {
glennrpcf002022011-01-30 02:38:15 +00009132 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009133 (unsigned char *) name,(unsigned char *) name,
9134 GetStringInfoDatum(profile),
9135 (png_uint_32) GetStringInfoLength(profile));
9136 }
9137 }
glennrp0b206f52011-01-07 04:55:32 +00009138
glennrpc8cbc5d2011-01-01 00:12:34 +00009139 if (logging != MagickFalse)
9140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9141 " Setting up text chunk with %s profile",name);
9142
9143 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009144 }
cristy3ed852e2009-09-05 21:47:34 +00009145 }
9146
9147#if defined(PNG_WRITE_sRGB_SUPPORTED)
9148 if ((mng_info->have_write_global_srgb == 0) &&
9149 ((image->rendering_intent != UndefinedIntent) ||
9150 (image->colorspace == sRGBColorspace)))
9151 {
glennrp26f37912010-12-23 16:22:42 +00009152 if (ping_exclude_sRGB == MagickFalse)
9153 {
9154 /*
9155 Note image rendering intent.
9156 */
9157 if (logging != MagickFalse)
9158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9159 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009160
glennrp26f37912010-12-23 16:22:42 +00009161 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009162 Magick_RenderingIntent_to_PNG_RenderingIntent(
9163 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009164
glennrp26f37912010-12-23 16:22:42 +00009165 if (ping_exclude_gAMA == MagickFalse)
9166 png_set_gAMA(ping,ping_info,0.45455);
9167 }
cristy3ed852e2009-09-05 21:47:34 +00009168 }
glennrp26f37912010-12-23 16:22:42 +00009169
glennrp5af765f2010-03-30 11:12:18 +00009170 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009171#endif
9172 {
glennrp2cc891a2010-12-24 13:44:32 +00009173 if (ping_exclude_gAMA == MagickFalse &&
9174 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009175 (image->gamma < .45 || image->gamma > .46)))
9176 {
cristy3ed852e2009-09-05 21:47:34 +00009177 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9178 {
9179 /*
9180 Note image gamma.
9181 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9182 */
9183 if (logging != MagickFalse)
9184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9185 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009186
cristy3ed852e2009-09-05 21:47:34 +00009187 png_set_gAMA(ping,ping_info,image->gamma);
9188 }
glennrp26f37912010-12-23 16:22:42 +00009189 }
glennrp2b013e42010-11-24 16:55:50 +00009190
glennrp26f37912010-12-23 16:22:42 +00009191 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009192 {
glennrp26f37912010-12-23 16:22:42 +00009193 if ((mng_info->have_write_global_chrm == 0) &&
9194 (image->chromaticity.red_primary.x != 0.0))
9195 {
9196 /*
9197 Note image chromaticity.
9198 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9199 */
9200 PrimaryInfo
9201 bp,
9202 gp,
9203 rp,
9204 wp;
cristy3ed852e2009-09-05 21:47:34 +00009205
glennrp26f37912010-12-23 16:22:42 +00009206 wp=image->chromaticity.white_point;
9207 rp=image->chromaticity.red_primary;
9208 gp=image->chromaticity.green_primary;
9209 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009210
glennrp26f37912010-12-23 16:22:42 +00009211 if (logging != MagickFalse)
9212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9213 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009214
glennrp26f37912010-12-23 16:22:42 +00009215 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9216 bp.x,bp.y);
9217 }
9218 }
cristy3ed852e2009-09-05 21:47:34 +00009219 }
glennrpdfd70802010-11-14 01:23:35 +00009220
glennrp5af765f2010-03-30 11:12:18 +00009221 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009222
9223 if (mng_info->write_mng)
9224 png_set_sig_bytes(ping,8);
9225
9226 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9227
glennrpd6bf1612010-12-17 17:28:54 +00009228 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009229 {
9230 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009231 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009232 {
glennrp5af765f2010-03-30 11:12:18 +00009233 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009234
glennrp5af765f2010-03-30 11:12:18 +00009235 if (ping_bit_depth < 8)
9236 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009237 }
glennrp0fe50b42010-11-16 03:52:51 +00009238
cristy3ed852e2009-09-05 21:47:34 +00009239 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009240 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009241 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009242 }
9243
glennrp0e8ea192010-12-24 18:00:33 +00009244 if (ping_need_colortype_warning != MagickFalse ||
9245 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009246 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009247 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009248 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009249 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009250 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009251 {
9252 if (logging != MagickFalse)
9253 {
glennrp0e8ea192010-12-24 18:00:33 +00009254 if (ping_need_colortype_warning != MagickFalse)
9255 {
9256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9257 " Image has transparency but tRNS chunk was excluded");
9258 }
9259
cristy3ed852e2009-09-05 21:47:34 +00009260 if (mng_info->write_png_depth)
9261 {
9262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9263 " Defined PNG:bit-depth=%u, Computed depth=%u",
9264 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009265 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009266 }
glennrp0e8ea192010-12-24 18:00:33 +00009267
cristy3ed852e2009-09-05 21:47:34 +00009268 if (mng_info->write_png_colortype)
9269 {
9270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9271 " Defined PNG:color-type=%u, Computed color type=%u",
9272 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009273 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009274 }
9275 }
glennrp0e8ea192010-12-24 18:00:33 +00009276
glennrp3bd2e412010-08-10 13:34:52 +00009277 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009278 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9279 }
9280
glennrp58e01762011-01-07 15:28:54 +00009281 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009282 {
9283 /* Add an opaque matte channel */
9284 image->matte = MagickTrue;
9285 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009286
glennrpb4a13412010-05-05 12:47:19 +00009287 if (logging != MagickFalse)
9288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9289 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009290 }
9291
glennrp0e319732011-01-25 21:53:13 +00009292 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009293 {
glennrp991d11d2010-11-12 21:55:28 +00009294 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009295 {
glennrp991d11d2010-11-12 21:55:28 +00009296 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009297 if (logging != MagickFalse)
9298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9299 " Setting ping_have_tRNS=MagickTrue.");
9300 }
glennrpe9c26dc2010-05-30 01:56:35 +00009301 }
9302
cristy3ed852e2009-09-05 21:47:34 +00009303 if (logging != MagickFalse)
9304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9305 " Writing PNG header chunks");
9306
glennrp5af765f2010-03-30 11:12:18 +00009307 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9308 ping_bit_depth,ping_color_type,
9309 ping_interlace_method,ping_compression_method,
9310 ping_filter_method);
9311
glennrp39992b42010-11-14 00:03:43 +00009312 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9313 {
glennrpf09bded2011-01-08 01:15:59 +00009314 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009315
glennrp3b51f0e2010-11-27 18:14:08 +00009316 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009317 {
glennrp8640fb52010-11-23 15:48:26 +00009318 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009319 {
glennrpd6bf1612010-12-17 17:28:54 +00009320 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009322 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9323 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009324 (int) palette[i].red,
9325 (int) palette[i].green,
9326 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009327 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009328 (int) ping_trans_alpha[i]);
9329 else
9330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009331 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009332 (int) i,
9333 (int) palette[i].red,
9334 (int) palette[i].green,
9335 (int) palette[i].blue);
9336 }
glennrp39992b42010-11-14 00:03:43 +00009337 }
glennrp39992b42010-11-14 00:03:43 +00009338 }
9339
glennrp26f37912010-12-23 16:22:42 +00009340 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009341 {
glennrp26f37912010-12-23 16:22:42 +00009342 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009343 {
glennrp26f37912010-12-23 16:22:42 +00009344 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009345 if (logging)
9346 {
9347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9348 " Setting up bKGD chunk");
9349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9350 " background color = (%d,%d,%d)",
9351 (int) ping_background.red,
9352 (int) ping_background.green,
9353 (int) ping_background.blue);
9354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9355 " index = %d, gray=%d",
9356 (int) ping_background.index,
9357 (int) ping_background.gray);
9358 }
9359 }
glennrp26f37912010-12-23 16:22:42 +00009360 }
9361
9362 if (ping_exclude_pHYs == MagickFalse)
9363 {
9364 if (ping_have_pHYs != MagickFalse)
9365 {
9366 png_set_pHYs(ping,ping_info,
9367 ping_pHYs_x_resolution,
9368 ping_pHYs_y_resolution,
9369 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009370
9371 if (logging)
9372 {
9373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9374 " Setting up pHYs chunk");
9375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9376 " x_resolution=%lu",
9377 (unsigned long) ping_pHYs_x_resolution);
9378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9379 " y_resolution=%lu",
9380 (unsigned long) ping_pHYs_y_resolution);
9381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9382 " unit_type=%lu",
9383 (unsigned long) ping_pHYs_unit_type);
9384 }
glennrp26f37912010-12-23 16:22:42 +00009385 }
glennrpdfd70802010-11-14 01:23:35 +00009386 }
9387
9388#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009389 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009390 {
glennrp26f37912010-12-23 16:22:42 +00009391 if (image->page.x || image->page.y)
9392 {
9393 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9394 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009395
glennrp26f37912010-12-23 16:22:42 +00009396 if (logging != MagickFalse)
9397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9398 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9399 (int) image->page.x, (int) image->page.y);
9400 }
glennrpdfd70802010-11-14 01:23:35 +00009401 }
9402#endif
9403
glennrpda8f3a72011-02-27 23:54:12 +00009404 if (mng_info->need_blob != MagickFalse)
9405 {
9406 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9407 MagickFalse)
9408 png_error(ping,"WriteBlob Failed");
9409
9410 ping_have_blob=MagickTrue;
9411 }
9412
cristy3ed852e2009-09-05 21:47:34 +00009413 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009414
glennrp39992b42010-11-14 00:03:43 +00009415 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009416 {
glennrp3b51f0e2010-11-27 18:14:08 +00009417 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009418 {
9419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9420 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9421 }
9422
9423 if (ping_color_type == 3)
9424 (void) png_set_tRNS(ping, ping_info,
9425 ping_trans_alpha,
9426 ping_num_trans,
9427 NULL);
9428
9429 else
9430 {
9431 (void) png_set_tRNS(ping, ping_info,
9432 NULL,
9433 0,
9434 &ping_trans_color);
9435
glennrp3b51f0e2010-11-27 18:14:08 +00009436 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009437 {
9438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009439 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009440 (int) ping_trans_color.red,
9441 (int) ping_trans_color.green,
9442 (int) ping_trans_color.blue);
9443 }
9444 }
glennrp991d11d2010-11-12 21:55:28 +00009445 }
9446
cristy3ed852e2009-09-05 21:47:34 +00009447 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009448 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009449
cristy3ed852e2009-09-05 21:47:34 +00009450 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009451
cristy3ed852e2009-09-05 21:47:34 +00009452 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009453 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009454
glennrp26f37912010-12-23 16:22:42 +00009455 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009456 {
glennrp4f25bd02011-01-01 18:51:28 +00009457 if ((image->page.width != 0 && image->page.width != image->columns) ||
9458 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009459 {
9460 unsigned char
9461 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009462
glennrp26f37912010-12-23 16:22:42 +00009463 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9464 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009465 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009466 PNGLong(chunk+4,(png_uint_32) image->page.width);
9467 PNGLong(chunk+8,(png_uint_32) image->page.height);
9468 chunk[12]=0; /* unit = pixels */
9469 (void) WriteBlob(image,13,chunk);
9470 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9471 }
cristy3ed852e2009-09-05 21:47:34 +00009472 }
9473
9474#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009475 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009476#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009477 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009478#undef PNG_HAVE_IDAT
9479#endif
9480
9481 png_set_packing(ping);
9482 /*
9483 Allocate memory.
9484 */
9485 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009486 if (image_depth > 8)
9487 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009488 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009489 {
glennrpb4a13412010-05-05 12:47:19 +00009490 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009491 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009492 break;
glennrp0fe50b42010-11-16 03:52:51 +00009493
glennrpb4a13412010-05-05 12:47:19 +00009494 case PNG_COLOR_TYPE_GRAY_ALPHA:
9495 rowbytes*=2;
9496 break;
glennrp0fe50b42010-11-16 03:52:51 +00009497
glennrpb4a13412010-05-05 12:47:19 +00009498 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009499 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009500 break;
glennrp0fe50b42010-11-16 03:52:51 +00009501
glennrpb4a13412010-05-05 12:47:19 +00009502 default:
9503 break;
cristy3ed852e2009-09-05 21:47:34 +00009504 }
glennrp3b51f0e2010-11-27 18:14:08 +00009505
9506 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009507 {
9508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9509 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009510
glennrpb4a13412010-05-05 12:47:19 +00009511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009512 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009513 }
glennrpcf002022011-01-30 02:38:15 +00009514 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9515 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009516
glennrpcf002022011-01-30 02:38:15 +00009517 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009518 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009519
cristy3ed852e2009-09-05 21:47:34 +00009520 /*
9521 Initialize image scanlines.
9522 */
glennrp5af765f2010-03-30 11:12:18 +00009523 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009524 {
9525 /*
9526 PNG write failed.
9527 */
9528#ifdef PNG_DEBUG
9529 if (image_info->verbose)
9530 (void) printf("PNG write has failed.\n");
9531#endif
9532 png_destroy_write_struct(&ping,&ping_info);
9533 if (quantum_info != (QuantumInfo *) NULL)
9534 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009535 if (ping_pixels != (unsigned char *) NULL)
9536 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009537#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009538 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009539#endif
glennrpda8f3a72011-02-27 23:54:12 +00009540 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009541 (void) CloseBlob(image);
9542 image_info=DestroyImageInfo(image_info);
9543 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009544 return(MagickFalse);
9545 }
cristyed552522009-10-16 14:04:35 +00009546 quantum_info=AcquireQuantumInfo(image_info,image);
9547 if (quantum_info == (QuantumInfo *) NULL)
9548 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009549 quantum_info->format=UndefinedQuantumFormat;
9550 quantum_info->depth=image_depth;
9551 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009552
cristy3ed852e2009-09-05 21:47:34 +00009553 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009554 !mng_info->write_png32) &&
9555 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009556 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009557 image_matte == MagickFalse &&
9558 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009559 {
glennrp8bb3a022010-12-13 20:40:04 +00009560 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009561 register const PixelPacket
9562 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009563
cristy3ed852e2009-09-05 21:47:34 +00009564 quantum_info->depth=8;
9565 for (pass=0; pass < num_passes; pass++)
9566 {
9567 /*
9568 Convert PseudoClass image to a PNG monochrome image.
9569 */
cristybb503372010-05-27 20:51:26 +00009570 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009571 {
glennrpd71e86a2011-02-24 01:28:37 +00009572 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9574 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009575
cristy3ed852e2009-09-05 21:47:34 +00009576 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009577
cristy3ed852e2009-09-05 21:47:34 +00009578 if (p == (const PixelPacket *) NULL)
9579 break;
glennrp0fe50b42010-11-16 03:52:51 +00009580
cristy3ed852e2009-09-05 21:47:34 +00009581 if (mng_info->IsPalette)
9582 {
9583 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009584 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009585 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9586 mng_info->write_png_depth &&
9587 mng_info->write_png_depth != old_bit_depth)
9588 {
9589 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009590 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009591 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009592 >> (8-old_bit_depth));
9593 }
9594 }
glennrp0fe50b42010-11-16 03:52:51 +00009595
cristy3ed852e2009-09-05 21:47:34 +00009596 else
9597 {
9598 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009599 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009600 }
glennrp0fe50b42010-11-16 03:52:51 +00009601
cristy3ed852e2009-09-05 21:47:34 +00009602 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009603 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009604 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009605 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009606
glennrp3b51f0e2010-11-27 18:14:08 +00009607 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9609 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009610
glennrpcf002022011-01-30 02:38:15 +00009611 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009612 }
9613 if (image->previous == (Image *) NULL)
9614 {
9615 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9616 if (status == MagickFalse)
9617 break;
9618 }
9619 }
9620 }
glennrp0fe50b42010-11-16 03:52:51 +00009621
glennrp8bb3a022010-12-13 20:40:04 +00009622 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009623 {
glennrp0fe50b42010-11-16 03:52:51 +00009624 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009625 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009626 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009627 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009628 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009629 {
glennrp8bb3a022010-12-13 20:40:04 +00009630 register const PixelPacket
9631 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009632
glennrp8bb3a022010-12-13 20:40:04 +00009633 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009634 {
glennrp8bb3a022010-12-13 20:40:04 +00009635
cristybb503372010-05-27 20:51:26 +00009636 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009637 {
9638 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009639
cristy3ed852e2009-09-05 21:47:34 +00009640 if (p == (const PixelPacket *) NULL)
9641 break;
glennrp2cc891a2010-12-24 13:44:32 +00009642
glennrp5af765f2010-03-30 11:12:18 +00009643 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009644 {
glennrp8bb3a022010-12-13 20:40:04 +00009645 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009646 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009647 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009648
glennrp8bb3a022010-12-13 20:40:04 +00009649 else
9650 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009651 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009652
glennrp3b51f0e2010-11-27 18:14:08 +00009653 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009655 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009656 }
glennrp2cc891a2010-12-24 13:44:32 +00009657
glennrp8bb3a022010-12-13 20:40:04 +00009658 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9659 {
9660 if (logging != MagickFalse && y == 0)
9661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9662 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009663
glennrp8bb3a022010-12-13 20:40:04 +00009664 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009665 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009666 }
glennrp2cc891a2010-12-24 13:44:32 +00009667
glennrp3b51f0e2010-11-27 18:14:08 +00009668 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009670 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009671
glennrpcf002022011-01-30 02:38:15 +00009672 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009673 }
glennrp2cc891a2010-12-24 13:44:32 +00009674
glennrp8bb3a022010-12-13 20:40:04 +00009675 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009676 {
glennrp8bb3a022010-12-13 20:40:04 +00009677 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9678 if (status == MagickFalse)
9679 break;
cristy3ed852e2009-09-05 21:47:34 +00009680 }
cristy3ed852e2009-09-05 21:47:34 +00009681 }
9682 }
glennrp8bb3a022010-12-13 20:40:04 +00009683
9684 else
9685 {
9686 register const PixelPacket
9687 *p;
9688
9689 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009690 {
glennrp8bb3a022010-12-13 20:40:04 +00009691 if ((image_depth > 8) || (mng_info->write_png24 ||
9692 mng_info->write_png32 ||
9693 (!mng_info->write_png8 && !mng_info->IsPalette)))
9694 {
9695 for (y=0; y < (ssize_t) image->rows; y++)
9696 {
9697 p=GetVirtualPixels(image,0,y,image->columns,1,
9698 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009699
glennrp8bb3a022010-12-13 20:40:04 +00009700 if (p == (const PixelPacket *) NULL)
9701 break;
glennrp2cc891a2010-12-24 13:44:32 +00009702
glennrp8bb3a022010-12-13 20:40:04 +00009703 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9704 {
9705 if (image->storage_class == DirectClass)
9706 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009707 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009708
glennrp8bb3a022010-12-13 20:40:04 +00009709 else
9710 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009711 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009712 }
glennrp2cc891a2010-12-24 13:44:32 +00009713
glennrp8bb3a022010-12-13 20:40:04 +00009714 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9715 {
9716 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009717 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009718 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009719
glennrp8bb3a022010-12-13 20:40:04 +00009720 if (logging != MagickFalse && y == 0)
9721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9722 " Writing GRAY_ALPHA PNG pixels (3)");
9723 }
glennrp2cc891a2010-12-24 13:44:32 +00009724
glennrp8bb3a022010-12-13 20:40:04 +00009725 else if (image_matte != MagickFalse)
9726 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009727 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009728
glennrp8bb3a022010-12-13 20:40:04 +00009729 else
9730 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009731 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009732
glennrp8bb3a022010-12-13 20:40:04 +00009733 if (logging != MagickFalse && y == 0)
9734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9735 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009736
glennrpcf002022011-01-30 02:38:15 +00009737 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009738 }
9739 }
glennrp2cc891a2010-12-24 13:44:32 +00009740
glennrp8bb3a022010-12-13 20:40:04 +00009741 else
9742 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9743 mng_info->write_png32 ||
9744 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9745 {
9746 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9747 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9748 {
9749 if (logging != MagickFalse)
9750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9751 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009752
glennrp8bb3a022010-12-13 20:40:04 +00009753 quantum_info->depth=8;
9754 image_depth=8;
9755 }
glennrp2cc891a2010-12-24 13:44:32 +00009756
glennrp8bb3a022010-12-13 20:40:04 +00009757 for (y=0; y < (ssize_t) image->rows; y++)
9758 {
9759 if (logging != MagickFalse && y == 0)
9760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9761 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009762
glennrp770d1932011-03-06 22:11:17 +00009763 p=GetVirtualPixels(image,0,y,image->columns,1,
9764 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009765
glennrp8bb3a022010-12-13 20:40:04 +00009766 if (p == (const PixelPacket *) NULL)
9767 break;
glennrp2cc891a2010-12-24 13:44:32 +00009768
glennrp8bb3a022010-12-13 20:40:04 +00009769 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009770 {
glennrp4bf89732011-03-21 13:48:28 +00009771 quantum_info->depth=image->depth;
9772
glennrp44757ab2011-03-17 12:57:03 +00009773 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009774 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009775 }
glennrp2cc891a2010-12-24 13:44:32 +00009776
glennrp8bb3a022010-12-13 20:40:04 +00009777 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9778 {
9779 if (logging != MagickFalse && y == 0)
9780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9781 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009782
glennrp8bb3a022010-12-13 20:40:04 +00009783 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009784 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009785 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009786 }
glennrp2cc891a2010-12-24 13:44:32 +00009787
glennrp8bb3a022010-12-13 20:40:04 +00009788 else
glennrp8bb3a022010-12-13 20:40:04 +00009789 {
glennrp179d0752011-03-17 13:02:10 +00009790 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009791 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9792
9793 if (logging != MagickFalse && y <= 2)
9794 {
9795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009796 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009797
9798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9799 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9800 (int)ping_pixels[0],(int)ping_pixels[1]);
9801 }
glennrp8bb3a022010-12-13 20:40:04 +00009802 }
glennrpcf002022011-01-30 02:38:15 +00009803 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009804 }
9805 }
glennrp2cc891a2010-12-24 13:44:32 +00009806
glennrp8bb3a022010-12-13 20:40:04 +00009807 if (image->previous == (Image *) NULL)
9808 {
9809 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9810 if (status == MagickFalse)
9811 break;
9812 }
cristy3ed852e2009-09-05 21:47:34 +00009813 }
glennrp8bb3a022010-12-13 20:40:04 +00009814 }
9815 }
9816
cristyb32b90a2009-09-07 21:45:48 +00009817 if (quantum_info != (QuantumInfo *) NULL)
9818 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009819
9820 if (logging != MagickFalse)
9821 {
9822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009823 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009824
cristy3ed852e2009-09-05 21:47:34 +00009825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009826 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009827
cristy3ed852e2009-09-05 21:47:34 +00009828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009829 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009830
cristy3ed852e2009-09-05 21:47:34 +00009831 if (mng_info->write_png_depth)
9832 {
9833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9834 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9835 }
glennrp0fe50b42010-11-16 03:52:51 +00009836
cristy3ed852e2009-09-05 21:47:34 +00009837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009838 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009839
cristy3ed852e2009-09-05 21:47:34 +00009840 if (mng_info->write_png_colortype)
9841 {
9842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9843 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9844 }
glennrp0fe50b42010-11-16 03:52:51 +00009845
cristy3ed852e2009-09-05 21:47:34 +00009846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009847 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009848
cristy3ed852e2009-09-05 21:47:34 +00009849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009850 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009851 }
9852 /*
glennrpa0ed0092011-04-18 16:36:29 +00009853 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009854 */
glennrp823b55c2011-03-14 18:46:46 +00009855 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009856 {
glennrp26f37912010-12-23 16:22:42 +00009857 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009858 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009859 while (property != (const char *) NULL)
9860 {
9861 png_textp
9862 text;
glennrp2cc891a2010-12-24 13:44:32 +00009863
glennrp26f37912010-12-23 16:22:42 +00009864 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009865
9866 /* Don't write any "png:" properties; those are just for "identify" */
9867 if (LocaleNCompare(property,"png:",4) != 0 &&
9868
9869 /* Suppress density and units if we wrote a pHYs chunk */
9870 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009871 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009872 LocaleCompare(property,"units") != 0) &&
9873
9874 /* Suppress the IM-generated Date:create and Date:modify */
9875 (ping_exclude_date == MagickFalse ||
9876 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009877 {
glennrpc70af4a2011-03-07 00:08:23 +00009878 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009879 {
glennrpc70af4a2011-03-07 00:08:23 +00009880 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9881 text[0].key=(char *) property;
9882 text[0].text=(char *) value;
9883 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009884
glennrpc70af4a2011-03-07 00:08:23 +00009885 if (ping_exclude_tEXt != MagickFalse)
9886 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9887
9888 else if (ping_exclude_zTXt != MagickFalse)
9889 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9890
9891 else
glennrp26f37912010-12-23 16:22:42 +00009892 {
glennrpc70af4a2011-03-07 00:08:23 +00009893 text[0].compression=image_info->compression == NoCompression ||
9894 (image_info->compression == UndefinedCompression &&
9895 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9896 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009897 }
glennrp2cc891a2010-12-24 13:44:32 +00009898
glennrpc70af4a2011-03-07 00:08:23 +00009899 if (logging != MagickFalse)
9900 {
9901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9902 " Setting up text chunk");
9903
9904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9905 " keyword: %s",text[0].key);
9906 }
9907
9908 png_set_text(ping,ping_info,text,1);
9909 png_free(ping,text);
9910 }
glennrp26f37912010-12-23 16:22:42 +00009911 }
9912 property=GetNextImageProperty(image);
9913 }
cristy3ed852e2009-09-05 21:47:34 +00009914 }
9915
9916 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009917 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009918
9919 if (logging != MagickFalse)
9920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9921 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009922
cristy3ed852e2009-09-05 21:47:34 +00009923 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009924
cristy3ed852e2009-09-05 21:47:34 +00009925 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9926 {
9927 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009928 (ping_width != mng_info->page.width) ||
9929 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009930 {
9931 unsigned char
9932 chunk[32];
9933
9934 /*
9935 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9936 */
9937 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9938 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009939 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009940 chunk[4]=4;
9941 chunk[5]=0; /* frame name separator (no name) */
9942 chunk[6]=1; /* flag for changing delay, for next frame only */
9943 chunk[7]=0; /* flag for changing frame timeout */
9944 chunk[8]=1; /* flag for changing frame clipping for next frame */
9945 chunk[9]=0; /* flag for changing frame sync_id */
9946 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9947 chunk[14]=0; /* clipping boundaries delta type */
9948 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9949 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009950 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009951 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9952 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009953 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009954 (void) WriteBlob(image,31,chunk);
9955 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9956 mng_info->old_framing_mode=4;
9957 mng_info->framing_mode=1;
9958 }
glennrp0fe50b42010-11-16 03:52:51 +00009959
cristy3ed852e2009-09-05 21:47:34 +00009960 else
9961 mng_info->framing_mode=3;
9962 }
9963 if (mng_info->write_mng && !mng_info->need_fram &&
9964 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009965 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009966 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009967 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009968
cristy3ed852e2009-09-05 21:47:34 +00009969 /*
9970 Free PNG resources.
9971 */
glennrp5af765f2010-03-30 11:12:18 +00009972
cristy3ed852e2009-09-05 21:47:34 +00009973 png_destroy_write_struct(&ping,&ping_info);
9974
glennrpcf002022011-01-30 02:38:15 +00009975 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009976
9977#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009978 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009979#endif
9980
glennrpda8f3a72011-02-27 23:54:12 +00009981 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009982 (void) CloseBlob(image);
9983
9984 image_info=DestroyImageInfo(image_info);
9985 image=DestroyImage(image);
9986
9987 /* Store bit depth actually written */
9988 s[0]=(char) ping_bit_depth;
9989 s[1]='\0';
9990
9991 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9992
cristy3ed852e2009-09-05 21:47:34 +00009993 if (logging != MagickFalse)
9994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9995 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009996
cristy3ed852e2009-09-05 21:47:34 +00009997 return(MagickTrue);
9998/* End write one PNG image */
9999}
10000
10001/*
10002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10003% %
10004% %
10005% %
10006% W r i t e P N G I m a g e %
10007% %
10008% %
10009% %
10010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10011%
10012% WritePNGImage() writes a Portable Network Graphics (PNG) or
10013% Multiple-image Network Graphics (MNG) image file.
10014%
10015% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10016%
10017% The format of the WritePNGImage method is:
10018%
10019% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10020%
10021% A description of each parameter follows:
10022%
10023% o image_info: the image info.
10024%
10025% o image: The image.
10026%
10027% Returns MagickTrue on success, MagickFalse on failure.
10028%
10029% Communicating with the PNG encoder:
10030%
10031% While the datastream written is always in PNG format and normally would
10032% be given the "png" file extension, this method also writes the following
10033% pseudo-formats which are subsets of PNG:
10034%
glennrp5a39f372011-02-25 04:52:16 +000010035% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10036% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010037% is present, the tRNS chunk must only have values 0 and 255
10038% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010039% transparent). If other values are present they will be
10040% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010041% colors are present, they will be quantized to the 4-4-4-1,
10042% 3-3-3-1, or 3-3-2-1 palette.
10043%
10044% If you want better quantization or dithering of the colors
10045% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010046% PNG encoder. The pixels contain 8-bit indices even if
10047% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010048% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010049% PNG grayscale type might be slightly more efficient. Please
10050% note that writing to the PNG8 format may result in loss
10051% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010052%
10053% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10054% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010055% one of the colors as transparent. The only loss incurred
10056% is reduction of sample depth to 8. If the image has more
10057% than one transparent color, has semitransparent pixels, or
10058% has an opaque pixel with the same RGB components as the
10059% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010060%
10061% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10062% transparency is permitted, i.e., the alpha sample for
10063% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010064% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010065% The only loss in data is the reduction of the sample depth
10066% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010067%
10068% o -define: For more precise control of the PNG output, you can use the
10069% Image options "png:bit-depth" and "png:color-type". These
10070% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010071% from the application programming interfaces. The options
10072% are case-independent and are converted to lowercase before
10073% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010074%
10075% png:color-type can be 0, 2, 3, 4, or 6.
10076%
10077% When png:color-type is 0 (Grayscale), png:bit-depth can
10078% be 1, 2, 4, 8, or 16.
10079%
10080% When png:color-type is 2 (RGB), png:bit-depth can
10081% be 8 or 16.
10082%
10083% When png:color-type is 3 (Indexed), png:bit-depth can
10084% be 1, 2, 4, or 8. This refers to the number of bits
10085% used to store the index. The color samples always have
10086% bit-depth 8 in indexed PNG files.
10087%
10088% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10089% png:bit-depth can be 8 or 16.
10090%
glennrp5a39f372011-02-25 04:52:16 +000010091% If the image cannot be written without loss with the requested bit-depth
10092% and color-type, a PNG file will not be written, and the encoder will
10093% return MagickFalse.
10094%
cristy3ed852e2009-09-05 21:47:34 +000010095% Since image encoders should not be responsible for the "heavy lifting",
10096% the user should make sure that ImageMagick has already reduced the
10097% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010098% transparency prior to attempting to write the image with depth, color,
10099% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010100%
glennrp97cefe22011-04-22 16:17:00 +000010101% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010102%
cristy3ed852e2009-09-05 21:47:34 +000010103% Note that another definition, "png:bit-depth-written" exists, but it
10104% is not intended for external use. It is only used internally by the
10105% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10106%
10107% It is possible to request that the PNG encoder write previously-formatted
10108% ancillary chunks in the output PNG file, using the "-profile" commandline
10109% option as shown below or by setting the profile via a programming
10110% interface:
10111%
10112% -profile PNG-chunk-x:<file>
10113%
10114% where x is a location flag and <file> is a file containing the chunk
10115% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010116% This encoder will compute the chunk length and CRC, so those must not
10117% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010118%
10119% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10120% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10121% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010122% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010123%
glennrpbb8a7332010-11-13 15:17:35 +000010124% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010125%
glennrp3241bd02010-12-12 04:36:28 +000010126% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010127%
glennrpd6afd542010-11-19 01:53:05 +000010128% o 32-bit depth is reduced to 16.
10129% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10130% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010131% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010132% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010133% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010134% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10135% this can be done without loss and a larger bit depth N was not
10136% requested via the "-define PNG:bit-depth=N" option.
10137% o If matte channel is present but only one transparent color is
10138% present, RGB+tRNS is written instead of RGBA
10139% o Opaque matte channel is removed (or added, if color-type 4 or 6
10140% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010141%
cristy3ed852e2009-09-05 21:47:34 +000010142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10143*/
10144static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10145 Image *image)
10146{
10147 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010148 excluding,
10149 logging,
10150 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010151 status;
10152
10153 MngInfo
10154 *mng_info;
10155
10156 const char
10157 *value;
10158
10159 int
glennrp21f0e622011-01-07 16:20:57 +000010160 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010161 source;
10162
cristy3ed852e2009-09-05 21:47:34 +000010163 /*
10164 Open image file.
10165 */
10166 assert(image_info != (const ImageInfo *) NULL);
10167 assert(image_info->signature == MagickSignature);
10168 assert(image != (Image *) NULL);
10169 assert(image->signature == MagickSignature);
10170 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010171 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010172 /*
10173 Allocate a MngInfo structure.
10174 */
10175 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010176 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010177
cristy3ed852e2009-09-05 21:47:34 +000010178 if (mng_info == (MngInfo *) NULL)
10179 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010180
cristy3ed852e2009-09-05 21:47:34 +000010181 /*
10182 Initialize members of the MngInfo structure.
10183 */
10184 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10185 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010186 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010187 have_mng_structure=MagickTrue;
10188
10189 /* See if user has requested a specific PNG subformat */
10190
10191 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10192 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10193 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10194
10195 if (mng_info->write_png8)
10196 {
glennrp9c1eb072010-06-06 22:19:15 +000010197 mng_info->write_png_colortype = /* 3 */ 4;
10198 mng_info->write_png_depth = 8;
10199 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010200 }
10201
10202 if (mng_info->write_png24)
10203 {
glennrp9c1eb072010-06-06 22:19:15 +000010204 mng_info->write_png_colortype = /* 2 */ 3;
10205 mng_info->write_png_depth = 8;
10206 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010207
glennrp9c1eb072010-06-06 22:19:15 +000010208 if (image->matte == MagickTrue)
10209 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010210
glennrp9c1eb072010-06-06 22:19:15 +000010211 else
10212 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010213
glennrp9c1eb072010-06-06 22:19:15 +000010214 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010215 }
10216
10217 if (mng_info->write_png32)
10218 {
glennrp9c1eb072010-06-06 22:19:15 +000010219 mng_info->write_png_colortype = /* 6 */ 7;
10220 mng_info->write_png_depth = 8;
10221 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010222
glennrp9c1eb072010-06-06 22:19:15 +000010223 if (image->matte == MagickTrue)
10224 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010225
glennrp9c1eb072010-06-06 22:19:15 +000010226 else
10227 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010228
glennrp9c1eb072010-06-06 22:19:15 +000010229 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010230 }
10231
10232 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010233
cristy3ed852e2009-09-05 21:47:34 +000010234 if (value != (char *) NULL)
10235 {
10236 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010237 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010238
cristy3ed852e2009-09-05 21:47:34 +000010239 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010240 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010241
cristy3ed852e2009-09-05 21:47:34 +000010242 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010243 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010244
cristy3ed852e2009-09-05 21:47:34 +000010245 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010246 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010247
cristy3ed852e2009-09-05 21:47:34 +000010248 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010249 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010250
glennrpbb8a7332010-11-13 15:17:35 +000010251 else
10252 (void) ThrowMagickException(&image->exception,
10253 GetMagickModule(),CoderWarning,
10254 "ignoring invalid defined png:bit-depth",
10255 "=%s",value);
10256
cristy3ed852e2009-09-05 21:47:34 +000010257 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010259 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010260 }
glennrp0fe50b42010-11-16 03:52:51 +000010261
cristy3ed852e2009-09-05 21:47:34 +000010262 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010263
cristy3ed852e2009-09-05 21:47:34 +000010264 if (value != (char *) NULL)
10265 {
10266 /* We must store colortype+1 because 0 is a valid colortype */
10267 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010268 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010269
cristy3ed852e2009-09-05 21:47:34 +000010270 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010271 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010272
cristy3ed852e2009-09-05 21:47:34 +000010273 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010274 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010275
cristy3ed852e2009-09-05 21:47:34 +000010276 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010277 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010278
cristy3ed852e2009-09-05 21:47:34 +000010279 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010280 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010281
glennrpbb8a7332010-11-13 15:17:35 +000010282 else
10283 (void) ThrowMagickException(&image->exception,
10284 GetMagickModule(),CoderWarning,
10285 "ignoring invalid defined png:color-type",
10286 "=%s",value);
10287
cristy3ed852e2009-09-05 21:47:34 +000010288 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010290 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010291 }
10292
glennrp0e8ea192010-12-24 18:00:33 +000010293 /* Check for chunks to be excluded:
10294 *
glennrp0dff56c2011-01-29 19:10:02 +000010295 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010296 * listed in the "unused_chunks" array, above.
10297 *
10298 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10299 * define (in the image properties or in the image artifacts)
10300 * or via a mng_info member. For convenience, in addition
10301 * to or instead of a comma-separated list of chunks, the
10302 * "exclude-chunk" string can be simply "all" or "none".
10303 *
10304 * The exclude-chunk define takes priority over the mng_info.
10305 *
10306 * A "PNG:include-chunk" define takes priority over both the
10307 * mng_info and the "PNG:exclude-chunk" define. Like the
10308 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010309 * well as a comma-separated list. Chunks that are unknown to
10310 * ImageMagick are always excluded, regardless of their "copy-safe"
10311 * status according to the PNG specification, and even if they
10312 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010313 *
10314 * Finally, all chunks listed in the "unused_chunks" array are
10315 * automatically excluded, regardless of the other instructions
10316 * or lack thereof.
10317 *
10318 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10319 * will not be written and the gAMA chunk will only be written if it
10320 * is not between .45 and .46, or approximately (1.0/2.2).
10321 *
10322 * If you exclude tRNS and the image has transparency, the colortype
10323 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10324 *
10325 * The -strip option causes StripImage() to set the png:include-chunk
10326 * artifact to "none,gama".
10327 */
10328
glennrp26f37912010-12-23 16:22:42 +000010329 mng_info->ping_exclude_bKGD=MagickFalse;
10330 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010331 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010332 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10333 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010334 mng_info->ping_exclude_iCCP=MagickFalse;
10335 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10336 mng_info->ping_exclude_oFFs=MagickFalse;
10337 mng_info->ping_exclude_pHYs=MagickFalse;
10338 mng_info->ping_exclude_sRGB=MagickFalse;
10339 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010340 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010341 mng_info->ping_exclude_vpAg=MagickFalse;
10342 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10343 mng_info->ping_exclude_zTXt=MagickFalse;
10344
glennrp8d3d6e52011-04-19 04:39:51 +000010345 mng_info->ping_preserve_colormap=MagickFalse;
10346
10347 value=GetImageArtifact(image,"png:preserve-colormap");
10348 if (value == NULL)
10349 value=GetImageOption(image_info,"png:preserve-colormap");
10350 if (value != NULL)
10351 mng_info->ping_preserve_colormap=MagickTrue;
10352
glennrp03812ae2010-12-24 01:31:34 +000010353 excluding=MagickFalse;
10354
glennrp5c7cf4e2010-12-24 00:30:00 +000010355 for (source=0; source<1; source++)
10356 {
10357 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010358 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010359 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010360
10361 if (value == NULL)
10362 value=GetImageArtifact(image,"png:exclude-chunks");
10363 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010364 else
glennrpacba0042010-12-24 14:27:26 +000010365 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010366 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010367
glennrpacba0042010-12-24 14:27:26 +000010368 if (value == NULL)
10369 value=GetImageOption(image_info,"png:exclude-chunks");
10370 }
10371
glennrp03812ae2010-12-24 01:31:34 +000010372 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010373 {
glennrp03812ae2010-12-24 01:31:34 +000010374
10375 size_t
10376 last;
10377
10378 excluding=MagickTrue;
10379
10380 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010381 {
10382 if (source == 0)
10383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384 " png:exclude-chunk=%s found in image artifacts.\n", value);
10385 else
10386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10387 " png:exclude-chunk=%s found in image properties.\n", value);
10388 }
glennrp03812ae2010-12-24 01:31:34 +000010389
10390 last=strlen(value);
10391
10392 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010393 {
glennrp03812ae2010-12-24 01:31:34 +000010394
10395 if (LocaleNCompare(value+i,"all",3) == 0)
10396 {
10397 mng_info->ping_exclude_bKGD=MagickTrue;
10398 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010399 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010400 mng_info->ping_exclude_EXIF=MagickTrue;
10401 mng_info->ping_exclude_gAMA=MagickTrue;
10402 mng_info->ping_exclude_iCCP=MagickTrue;
10403 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10404 mng_info->ping_exclude_oFFs=MagickTrue;
10405 mng_info->ping_exclude_pHYs=MagickTrue;
10406 mng_info->ping_exclude_sRGB=MagickTrue;
10407 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010408 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010409 mng_info->ping_exclude_vpAg=MagickTrue;
10410 mng_info->ping_exclude_zCCP=MagickTrue;
10411 mng_info->ping_exclude_zTXt=MagickTrue;
10412 i--;
10413 }
glennrp2cc891a2010-12-24 13:44:32 +000010414
glennrp03812ae2010-12-24 01:31:34 +000010415 if (LocaleNCompare(value+i,"none",4) == 0)
10416 {
10417 mng_info->ping_exclude_bKGD=MagickFalse;
10418 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010419 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010420 mng_info->ping_exclude_EXIF=MagickFalse;
10421 mng_info->ping_exclude_gAMA=MagickFalse;
10422 mng_info->ping_exclude_iCCP=MagickFalse;
10423 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10424 mng_info->ping_exclude_oFFs=MagickFalse;
10425 mng_info->ping_exclude_pHYs=MagickFalse;
10426 mng_info->ping_exclude_sRGB=MagickFalse;
10427 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010428 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010429 mng_info->ping_exclude_vpAg=MagickFalse;
10430 mng_info->ping_exclude_zCCP=MagickFalse;
10431 mng_info->ping_exclude_zTXt=MagickFalse;
10432 }
glennrp2cc891a2010-12-24 13:44:32 +000010433
glennrp03812ae2010-12-24 01:31:34 +000010434 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10435 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010436
glennrp03812ae2010-12-24 01:31:34 +000010437 if (LocaleNCompare(value+i,"chrm",4) == 0)
10438 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010439
glennrpa0ed0092011-04-18 16:36:29 +000010440 if (LocaleNCompare(value+i,"date",4) == 0)
10441 mng_info->ping_exclude_date=MagickTrue;
10442
glennrp03812ae2010-12-24 01:31:34 +000010443 if (LocaleNCompare(value+i,"exif",4) == 0)
10444 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010445
glennrp03812ae2010-12-24 01:31:34 +000010446 if (LocaleNCompare(value+i,"gama",4) == 0)
10447 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010448
glennrp03812ae2010-12-24 01:31:34 +000010449 if (LocaleNCompare(value+i,"iccp",4) == 0)
10450 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010451
glennrp03812ae2010-12-24 01:31:34 +000010452 /*
10453 if (LocaleNCompare(value+i,"itxt",4) == 0)
10454 mng_info->ping_exclude_iTXt=MagickTrue;
10455 */
glennrp2cc891a2010-12-24 13:44:32 +000010456
glennrp03812ae2010-12-24 01:31:34 +000010457 if (LocaleNCompare(value+i,"gama",4) == 0)
10458 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010459
glennrp03812ae2010-12-24 01:31:34 +000010460 if (LocaleNCompare(value+i,"offs",4) == 0)
10461 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010462
glennrp03812ae2010-12-24 01:31:34 +000010463 if (LocaleNCompare(value+i,"phys",4) == 0)
10464 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010465
glennrpa1e3b7b2010-12-24 16:37:33 +000010466 if (LocaleNCompare(value+i,"srgb",4) == 0)
10467 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010468
glennrp03812ae2010-12-24 01:31:34 +000010469 if (LocaleNCompare(value+i,"text",4) == 0)
10470 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010471
glennrpa1e3b7b2010-12-24 16:37:33 +000010472 if (LocaleNCompare(value+i,"trns",4) == 0)
10473 mng_info->ping_exclude_tRNS=MagickTrue;
10474
glennrp03812ae2010-12-24 01:31:34 +000010475 if (LocaleNCompare(value+i,"vpag",4) == 0)
10476 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010477
glennrp03812ae2010-12-24 01:31:34 +000010478 if (LocaleNCompare(value+i,"zccp",4) == 0)
10479 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010480
glennrp03812ae2010-12-24 01:31:34 +000010481 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10482 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010483
glennrp03812ae2010-12-24 01:31:34 +000010484 }
glennrpce91ed52010-12-23 22:37:49 +000010485 }
glennrp26f37912010-12-23 16:22:42 +000010486 }
10487
glennrp5c7cf4e2010-12-24 00:30:00 +000010488 for (source=0; source<1; source++)
10489 {
10490 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010491 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010492 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010493
10494 if (value == NULL)
10495 value=GetImageArtifact(image,"png:include-chunks");
10496 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010497 else
glennrpacba0042010-12-24 14:27:26 +000010498 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010499 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010500
glennrpacba0042010-12-24 14:27:26 +000010501 if (value == NULL)
10502 value=GetImageOption(image_info,"png:include-chunks");
10503 }
10504
glennrp03812ae2010-12-24 01:31:34 +000010505 if (value != NULL)
10506 {
10507 size_t
10508 last;
glennrp26f37912010-12-23 16:22:42 +000010509
glennrp03812ae2010-12-24 01:31:34 +000010510 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010511
glennrp03812ae2010-12-24 01:31:34 +000010512 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010513 {
10514 if (source == 0)
10515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10516 " png:include-chunk=%s found in image artifacts.\n", value);
10517 else
10518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10519 " png:include-chunk=%s found in image properties.\n", value);
10520 }
glennrp03812ae2010-12-24 01:31:34 +000010521
10522 last=strlen(value);
10523
10524 for (i=0; i<(int) last; i+=5)
10525 {
10526 if (LocaleNCompare(value+i,"all",3) == 0)
10527 {
10528 mng_info->ping_exclude_bKGD=MagickFalse;
10529 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010530 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010531 mng_info->ping_exclude_EXIF=MagickFalse;
10532 mng_info->ping_exclude_gAMA=MagickFalse;
10533 mng_info->ping_exclude_iCCP=MagickFalse;
10534 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10535 mng_info->ping_exclude_oFFs=MagickFalse;
10536 mng_info->ping_exclude_pHYs=MagickFalse;
10537 mng_info->ping_exclude_sRGB=MagickFalse;
10538 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010539 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010540 mng_info->ping_exclude_vpAg=MagickFalse;
10541 mng_info->ping_exclude_zCCP=MagickFalse;
10542 mng_info->ping_exclude_zTXt=MagickFalse;
10543 i--;
10544 }
glennrp2cc891a2010-12-24 13:44:32 +000010545
glennrp03812ae2010-12-24 01:31:34 +000010546 if (LocaleNCompare(value+i,"none",4) == 0)
10547 {
10548 mng_info->ping_exclude_bKGD=MagickTrue;
10549 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010550 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010551 mng_info->ping_exclude_EXIF=MagickTrue;
10552 mng_info->ping_exclude_gAMA=MagickTrue;
10553 mng_info->ping_exclude_iCCP=MagickTrue;
10554 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10555 mng_info->ping_exclude_oFFs=MagickTrue;
10556 mng_info->ping_exclude_pHYs=MagickTrue;
10557 mng_info->ping_exclude_sRGB=MagickTrue;
10558 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010559 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010560 mng_info->ping_exclude_vpAg=MagickTrue;
10561 mng_info->ping_exclude_zCCP=MagickTrue;
10562 mng_info->ping_exclude_zTXt=MagickTrue;
10563 }
glennrp2cc891a2010-12-24 13:44:32 +000010564
glennrp03812ae2010-12-24 01:31:34 +000010565 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10566 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010567
glennrp03812ae2010-12-24 01:31:34 +000010568 if (LocaleNCompare(value+i,"chrm",4) == 0)
10569 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010570
glennrpa0ed0092011-04-18 16:36:29 +000010571 if (LocaleNCompare(value+i,"date",4) == 0)
10572 mng_info->ping_exclude_date=MagickFalse;
10573
glennrp03812ae2010-12-24 01:31:34 +000010574 if (LocaleNCompare(value+i,"exif",4) == 0)
10575 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010576
glennrp03812ae2010-12-24 01:31:34 +000010577 if (LocaleNCompare(value+i,"gama",4) == 0)
10578 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010579
glennrp03812ae2010-12-24 01:31:34 +000010580 if (LocaleNCompare(value+i,"iccp",4) == 0)
10581 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010582
glennrp03812ae2010-12-24 01:31:34 +000010583 /*
10584 if (LocaleNCompare(value+i,"itxt",4) == 0)
10585 mng_info->ping_exclude_iTXt=MagickFalse;
10586 */
glennrp2cc891a2010-12-24 13:44:32 +000010587
glennrp03812ae2010-12-24 01:31:34 +000010588 if (LocaleNCompare(value+i,"gama",4) == 0)
10589 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010590
glennrp03812ae2010-12-24 01:31:34 +000010591 if (LocaleNCompare(value+i,"offs",4) == 0)
10592 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010593
glennrp03812ae2010-12-24 01:31:34 +000010594 if (LocaleNCompare(value+i,"phys",4) == 0)
10595 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010596
glennrpa1e3b7b2010-12-24 16:37:33 +000010597 if (LocaleNCompare(value+i,"srgb",4) == 0)
10598 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010599
glennrp03812ae2010-12-24 01:31:34 +000010600 if (LocaleNCompare(value+i,"text",4) == 0)
10601 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010602
glennrpa1e3b7b2010-12-24 16:37:33 +000010603 if (LocaleNCompare(value+i,"trns",4) == 0)
10604 mng_info->ping_exclude_tRNS=MagickFalse;
10605
glennrp03812ae2010-12-24 01:31:34 +000010606 if (LocaleNCompare(value+i,"vpag",4) == 0)
10607 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010608
glennrp03812ae2010-12-24 01:31:34 +000010609 if (LocaleNCompare(value+i,"zccp",4) == 0)
10610 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010611
glennrp03812ae2010-12-24 01:31:34 +000010612 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10613 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010614
glennrp03812ae2010-12-24 01:31:34 +000010615 }
glennrpce91ed52010-12-23 22:37:49 +000010616 }
glennrp26f37912010-12-23 16:22:42 +000010617 }
10618
glennrp03812ae2010-12-24 01:31:34 +000010619 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010620 {
10621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 " Chunks to be excluded from the output PNG:");
10623 if (mng_info->ping_exclude_bKGD != MagickFalse)
10624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10625 " bKGD");
10626 if (mng_info->ping_exclude_cHRM != MagickFalse)
10627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10628 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010629 if (mng_info->ping_exclude_date != MagickFalse)
10630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631 " date");
glennrp26f37912010-12-23 16:22:42 +000010632 if (mng_info->ping_exclude_EXIF != MagickFalse)
10633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10634 " EXIF");
10635 if (mng_info->ping_exclude_gAMA != MagickFalse)
10636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10637 " gAMA");
10638 if (mng_info->ping_exclude_iCCP != MagickFalse)
10639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 " iCCP");
10641/*
10642 if (mng_info->ping_exclude_iTXt != MagickFalse)
10643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10644 " iTXt");
10645*/
10646 if (mng_info->ping_exclude_oFFs != MagickFalse)
10647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10648 " oFFs");
10649 if (mng_info->ping_exclude_pHYs != MagickFalse)
10650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10651 " pHYs");
10652 if (mng_info->ping_exclude_sRGB != MagickFalse)
10653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654 " sRGB");
10655 if (mng_info->ping_exclude_tEXt != MagickFalse)
10656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10657 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010658 if (mng_info->ping_exclude_tRNS != MagickFalse)
10659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10660 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010661 if (mng_info->ping_exclude_vpAg != MagickFalse)
10662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10663 " vpAg");
10664 if (mng_info->ping_exclude_zCCP != MagickFalse)
10665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10666 " zCCP");
10667 if (mng_info->ping_exclude_zTXt != MagickFalse)
10668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10669 " zTXt");
10670 }
10671
glennrpb9cfe272010-12-21 15:08:06 +000010672 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010673
glennrpb9cfe272010-12-21 15:08:06 +000010674 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010675
10676 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010677
cristy3ed852e2009-09-05 21:47:34 +000010678 if (logging != MagickFalse)
10679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010680
cristy3ed852e2009-09-05 21:47:34 +000010681 return(status);
10682}
10683
10684#if defined(JNG_SUPPORTED)
10685
10686/* Write one JNG image */
10687static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10688 const ImageInfo *image_info,Image *image)
10689{
10690 Image
10691 *jpeg_image;
10692
10693 ImageInfo
10694 *jpeg_image_info;
10695
10696 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010697 logging,
cristy3ed852e2009-09-05 21:47:34 +000010698 status;
10699
10700 size_t
10701 length;
10702
10703 unsigned char
10704 *blob,
10705 chunk[80],
10706 *p;
10707
10708 unsigned int
10709 jng_alpha_compression_method,
10710 jng_alpha_sample_depth,
10711 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010712 transparent;
10713
cristybb503372010-05-27 20:51:26 +000010714 size_t
cristy3ed852e2009-09-05 21:47:34 +000010715 jng_quality;
10716
10717 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010718 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010719
10720 blob=(unsigned char *) NULL;
10721 jpeg_image=(Image *) NULL;
10722 jpeg_image_info=(ImageInfo *) NULL;
10723
10724 status=MagickTrue;
10725 transparent=image_info->type==GrayscaleMatteType ||
10726 image_info->type==TrueColorMatteType;
10727 jng_color_type=10;
10728 jng_alpha_sample_depth=0;
10729 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10730 jng_alpha_compression_method=0;
10731
10732 if (image->matte != MagickFalse)
10733 {
10734 /* if any pixels are transparent */
10735 transparent=MagickTrue;
10736 if (image_info->compression==JPEGCompression)
10737 jng_alpha_compression_method=8;
10738 }
10739
10740 if (transparent)
10741 {
10742 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010743
cristy3ed852e2009-09-05 21:47:34 +000010744 /* Create JPEG blob, image, and image_info */
10745 if (logging != MagickFalse)
10746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10747 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010748
cristy3ed852e2009-09-05 21:47:34 +000010749 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010750
cristy3ed852e2009-09-05 21:47:34 +000010751 if (jpeg_image_info == (ImageInfo *) NULL)
10752 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010753
cristy3ed852e2009-09-05 21:47:34 +000010754 if (logging != MagickFalse)
10755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10756 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010757
cristy3ed852e2009-09-05 21:47:34 +000010758 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010759
cristy3ed852e2009-09-05 21:47:34 +000010760 if (jpeg_image == (Image *) NULL)
10761 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010762
cristy3ed852e2009-09-05 21:47:34 +000010763 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10764 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10765 status=NegateImage(jpeg_image,MagickFalse);
10766 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010767
cristy3ed852e2009-09-05 21:47:34 +000010768 if (jng_quality >= 1000)
10769 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010770
cristy3ed852e2009-09-05 21:47:34 +000010771 else
10772 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010773
cristy3ed852e2009-09-05 21:47:34 +000010774 jpeg_image_info->type=GrayscaleType;
10775 (void) SetImageType(jpeg_image,GrayscaleType);
10776 (void) AcquireUniqueFilename(jpeg_image->filename);
10777 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10778 "%s",jpeg_image->filename);
10779 }
10780
10781 /* To do: check bit depth of PNG alpha channel */
10782
10783 /* Check if image is grayscale. */
10784 if (image_info->type != TrueColorMatteType && image_info->type !=
10785 TrueColorType && ImageIsGray(image))
10786 jng_color_type-=2;
10787
10788 if (transparent)
10789 {
10790 if (jng_alpha_compression_method==0)
10791 {
10792 const char
10793 *value;
10794
10795 /* Encode opacity as a grayscale PNG blob */
10796 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10797 &image->exception);
10798 if (logging != MagickFalse)
10799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10800 " Creating PNG blob.");
10801 length=0;
10802
10803 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10804 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10805 jpeg_image_info->interlace=NoInterlace;
10806
10807 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10808 &image->exception);
10809
10810 /* Retrieve sample depth used */
10811 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10812 if (value != (char *) NULL)
10813 jng_alpha_sample_depth= (unsigned int) value[0];
10814 }
10815 else
10816 {
10817 /* Encode opacity as a grayscale JPEG blob */
10818
10819 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10820 &image->exception);
10821
10822 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10823 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10824 jpeg_image_info->interlace=NoInterlace;
10825 if (logging != MagickFalse)
10826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10827 " Creating blob.");
10828 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010829 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010830 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010831
cristy3ed852e2009-09-05 21:47:34 +000010832 if (logging != MagickFalse)
10833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010834 " Successfully read jpeg_image into a blob, length=%.20g.",
10835 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010836
10837 }
10838 /* Destroy JPEG image and image_info */
10839 jpeg_image=DestroyImage(jpeg_image);
10840 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10841 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10842 }
10843
10844 /* Write JHDR chunk */
10845 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10846 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010847 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010848 PNGLong(chunk+4,(png_uint_32) image->columns);
10849 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010850 chunk[12]=jng_color_type;
10851 chunk[13]=8; /* sample depth */
10852 chunk[14]=8; /*jng_image_compression_method */
10853 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10854 chunk[16]=jng_alpha_sample_depth;
10855 chunk[17]=jng_alpha_compression_method;
10856 chunk[18]=0; /*jng_alpha_filter_method */
10857 chunk[19]=0; /*jng_alpha_interlace_method */
10858 (void) WriteBlob(image,20,chunk);
10859 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10860 if (logging != MagickFalse)
10861 {
10862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010863 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010864
cristy3ed852e2009-09-05 21:47:34 +000010865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010866 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010867
cristy3ed852e2009-09-05 21:47:34 +000010868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10869 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010870
cristy3ed852e2009-09-05 21:47:34 +000010871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10872 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010873
cristy3ed852e2009-09-05 21:47:34 +000010874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10875 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010876
cristy3ed852e2009-09-05 21:47:34 +000010877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10878 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010879
cristy3ed852e2009-09-05 21:47:34 +000010880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10881 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010882
cristy3ed852e2009-09-05 21:47:34 +000010883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10884 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010885
cristy3ed852e2009-09-05 21:47:34 +000010886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10887 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010888
cristy3ed852e2009-09-05 21:47:34 +000010889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10890 " JNG alpha interlace:%5d",0);
10891 }
10892
glennrp0fe50b42010-11-16 03:52:51 +000010893 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010894 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010895
10896 /*
10897 Write leading ancillary chunks
10898 */
10899
10900 if (transparent)
10901 {
10902 /*
10903 Write JNG bKGD chunk
10904 */
10905
10906 unsigned char
10907 blue,
10908 green,
10909 red;
10910
cristybb503372010-05-27 20:51:26 +000010911 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010912 num_bytes;
10913
10914 if (jng_color_type == 8 || jng_color_type == 12)
10915 num_bytes=6L;
10916 else
10917 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010918 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010919 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010920 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010921 red=ScaleQuantumToChar(image->background_color.red);
10922 green=ScaleQuantumToChar(image->background_color.green);
10923 blue=ScaleQuantumToChar(image->background_color.blue);
10924 *(chunk+4)=0;
10925 *(chunk+5)=red;
10926 *(chunk+6)=0;
10927 *(chunk+7)=green;
10928 *(chunk+8)=0;
10929 *(chunk+9)=blue;
10930 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10931 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10932 }
10933
10934 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10935 {
10936 /*
10937 Write JNG sRGB chunk
10938 */
10939 (void) WriteBlobMSBULong(image,1L);
10940 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010941 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010942
cristy3ed852e2009-09-05 21:47:34 +000010943 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010944 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010945 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010946 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010947
cristy3ed852e2009-09-05 21:47:34 +000010948 else
glennrpe610a072010-08-05 17:08:46 +000010949 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010950 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010951 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010952
cristy3ed852e2009-09-05 21:47:34 +000010953 (void) WriteBlob(image,5,chunk);
10954 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10955 }
10956 else
10957 {
10958 if (image->gamma != 0.0)
10959 {
10960 /*
10961 Write JNG gAMA chunk
10962 */
10963 (void) WriteBlobMSBULong(image,4L);
10964 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010965 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010966 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010967 (void) WriteBlob(image,8,chunk);
10968 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10969 }
glennrp0fe50b42010-11-16 03:52:51 +000010970
cristy3ed852e2009-09-05 21:47:34 +000010971 if ((mng_info->equal_chrms == MagickFalse) &&
10972 (image->chromaticity.red_primary.x != 0.0))
10973 {
10974 PrimaryInfo
10975 primary;
10976
10977 /*
10978 Write JNG cHRM chunk
10979 */
10980 (void) WriteBlobMSBULong(image,32L);
10981 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010982 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010983 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010984 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10985 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010986 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010987 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10988 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010989 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010990 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10991 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010992 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010993 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10994 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010995 (void) WriteBlob(image,36,chunk);
10996 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10997 }
10998 }
glennrp0fe50b42010-11-16 03:52:51 +000010999
cristy3ed852e2009-09-05 21:47:34 +000011000 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11001 {
11002 /*
11003 Write JNG pHYs chunk
11004 */
11005 (void) WriteBlobMSBULong(image,9L);
11006 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011007 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011008 if (image->units == PixelsPerInchResolution)
11009 {
cristy35ef8242010-06-03 16:24:13 +000011010 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011011 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011012
cristy35ef8242010-06-03 16:24:13 +000011013 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011014 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011015
cristy3ed852e2009-09-05 21:47:34 +000011016 chunk[12]=1;
11017 }
glennrp0fe50b42010-11-16 03:52:51 +000011018
cristy3ed852e2009-09-05 21:47:34 +000011019 else
11020 {
11021 if (image->units == PixelsPerCentimeterResolution)
11022 {
cristy35ef8242010-06-03 16:24:13 +000011023 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011024 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011025
cristy35ef8242010-06-03 16:24:13 +000011026 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011027 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011028
cristy3ed852e2009-09-05 21:47:34 +000011029 chunk[12]=1;
11030 }
glennrp0fe50b42010-11-16 03:52:51 +000011031
cristy3ed852e2009-09-05 21:47:34 +000011032 else
11033 {
cristy35ef8242010-06-03 16:24:13 +000011034 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11035 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011036 chunk[12]=0;
11037 }
11038 }
11039 (void) WriteBlob(image,13,chunk);
11040 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11041 }
11042
11043 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11044 {
11045 /*
11046 Write JNG oFFs chunk
11047 */
11048 (void) WriteBlobMSBULong(image,9L);
11049 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011050 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011051 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11052 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011053 chunk[12]=0;
11054 (void) WriteBlob(image,13,chunk);
11055 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11056 }
11057 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11058 {
11059 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11060 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011061 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011062 PNGLong(chunk+4,(png_uint_32) image->page.width);
11063 PNGLong(chunk+8,(png_uint_32) image->page.height);
11064 chunk[12]=0; /* unit = pixels */
11065 (void) WriteBlob(image,13,chunk);
11066 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11067 }
11068
11069
11070 if (transparent)
11071 {
11072 if (jng_alpha_compression_method==0)
11073 {
cristybb503372010-05-27 20:51:26 +000011074 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011075 i;
11076
cristybb503372010-05-27 20:51:26 +000011077 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011078 len;
11079
11080 /* Write IDAT chunk header */
11081 if (logging != MagickFalse)
11082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011083 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011084 length);
cristy3ed852e2009-09-05 21:47:34 +000011085
11086 /* Copy IDAT chunks */
11087 len=0;
11088 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011089 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011090 {
11091 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11092 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011093
cristy3ed852e2009-09-05 21:47:34 +000011094 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11095 {
11096 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011097 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011098 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011099 (void) WriteBlob(image,(size_t) len+4,p);
11100 (void) WriteBlobMSBULong(image,
11101 crc32(0,p,(uInt) len+4));
11102 }
glennrp0fe50b42010-11-16 03:52:51 +000011103
cristy3ed852e2009-09-05 21:47:34 +000011104 else
11105 {
11106 if (logging != MagickFalse)
11107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011108 " Skipping %c%c%c%c chunk, length=%.20g.",
11109 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011110 }
11111 p+=(8+len);
11112 }
11113 }
11114 else
11115 {
11116 /* Write JDAA chunk header */
11117 if (logging != MagickFalse)
11118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011119 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011120 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011121 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011122 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011123 /* Write JDAT chunk(s) data */
11124 (void) WriteBlob(image,4,chunk);
11125 (void) WriteBlob(image,length,blob);
11126 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11127 (uInt) length));
11128 }
11129 blob=(unsigned char *) RelinquishMagickMemory(blob);
11130 }
11131
11132 /* Encode image as a JPEG blob */
11133 if (logging != MagickFalse)
11134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11135 " Creating jpeg_image_info.");
11136 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11137 if (jpeg_image_info == (ImageInfo *) NULL)
11138 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11139
11140 if (logging != MagickFalse)
11141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11142 " Creating jpeg_image.");
11143
11144 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11145 if (jpeg_image == (Image *) NULL)
11146 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11147 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11148
11149 (void) AcquireUniqueFilename(jpeg_image->filename);
11150 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11151 jpeg_image->filename);
11152
11153 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11154 &image->exception);
11155
11156 if (logging != MagickFalse)
11157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011158 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11159 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011160
11161 if (jng_color_type == 8 || jng_color_type == 12)
11162 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011163
cristy3ed852e2009-09-05 21:47:34 +000011164 jpeg_image_info->quality=jng_quality % 1000;
11165 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11166 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011167
cristy3ed852e2009-09-05 21:47:34 +000011168 if (logging != MagickFalse)
11169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11170 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011171
cristy3ed852e2009-09-05 21:47:34 +000011172 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011173
cristy3ed852e2009-09-05 21:47:34 +000011174 if (logging != MagickFalse)
11175 {
11176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011177 " Successfully read jpeg_image into a blob, length=%.20g.",
11178 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011179
11180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011181 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011182 }
glennrp0fe50b42010-11-16 03:52:51 +000011183
cristy3ed852e2009-09-05 21:47:34 +000011184 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011185 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011186 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011187 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011188 (void) WriteBlob(image,4,chunk);
11189 (void) WriteBlob(image,length,blob);
11190 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11191
11192 jpeg_image=DestroyImage(jpeg_image);
11193 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11194 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11195 blob=(unsigned char *) RelinquishMagickMemory(blob);
11196
11197 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011198 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011199
11200 /* Write IEND chunk */
11201 (void) WriteBlobMSBULong(image,0L);
11202 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011203 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011204 (void) WriteBlob(image,4,chunk);
11205 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11206
11207 if (logging != MagickFalse)
11208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11209 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011210
cristy3ed852e2009-09-05 21:47:34 +000011211 return(status);
11212}
11213
11214
11215/*
11216%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11217% %
11218% %
11219% %
11220% W r i t e J N G I m a g e %
11221% %
11222% %
11223% %
11224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11225%
11226% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11227%
11228% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11229%
11230% The format of the WriteJNGImage method is:
11231%
11232% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11233%
11234% A description of each parameter follows:
11235%
11236% o image_info: the image info.
11237%
11238% o image: The image.
11239%
11240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11241*/
11242static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11243{
11244 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011245 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011246 logging,
cristy3ed852e2009-09-05 21:47:34 +000011247 status;
11248
11249 MngInfo
11250 *mng_info;
11251
cristy3ed852e2009-09-05 21:47:34 +000011252 /*
11253 Open image file.
11254 */
11255 assert(image_info != (const ImageInfo *) NULL);
11256 assert(image_info->signature == MagickSignature);
11257 assert(image != (Image *) NULL);
11258 assert(image->signature == MagickSignature);
11259 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011260 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011261 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11262 if (status == MagickFalse)
11263 return(status);
11264
11265 /*
11266 Allocate a MngInfo structure.
11267 */
11268 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011269 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011270 if (mng_info == (MngInfo *) NULL)
11271 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11272 /*
11273 Initialize members of the MngInfo structure.
11274 */
11275 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11276 mng_info->image=image;
11277 have_mng_structure=MagickTrue;
11278
11279 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11280
11281 status=WriteOneJNGImage(mng_info,image_info,image);
11282 (void) CloseBlob(image);
11283
11284 (void) CatchImageException(image);
11285 MngInfoFreeStruct(mng_info,&have_mng_structure);
11286 if (logging != MagickFalse)
11287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11288 return(status);
11289}
11290#endif
11291
11292
11293
11294static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11295{
11296 const char
11297 *option;
11298
11299 Image
11300 *next_image;
11301
11302 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011303 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011304 status;
11305
glennrp03812ae2010-12-24 01:31:34 +000011306 volatile MagickBooleanType
11307 logging;
11308
cristy3ed852e2009-09-05 21:47:34 +000011309 MngInfo
11310 *mng_info;
11311
11312 int
cristy3ed852e2009-09-05 21:47:34 +000011313 image_count,
11314 need_iterations,
11315 need_matte;
11316
11317 volatile int
11318#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11319 defined(PNG_MNG_FEATURES_SUPPORTED)
11320 need_local_plte,
11321#endif
11322 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011323 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011324 use_global_plte;
11325
cristybb503372010-05-27 20:51:26 +000011326 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011327 i;
11328
11329 unsigned char
11330 chunk[800];
11331
11332 volatile unsigned int
11333 write_jng,
11334 write_mng;
11335
cristybb503372010-05-27 20:51:26 +000011336 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011337 scene;
11338
cristybb503372010-05-27 20:51:26 +000011339 size_t
cristy3ed852e2009-09-05 21:47:34 +000011340 final_delay=0,
11341 initial_delay;
11342
glennrpd5045b42010-03-24 12:40:35 +000011343#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011344 if (image_info->verbose)
11345 printf("Your PNG library (libpng-%s) is rather old.\n",
11346 PNG_LIBPNG_VER_STRING);
11347#endif
11348
11349 /*
11350 Open image file.
11351 */
11352 assert(image_info != (const ImageInfo *) NULL);
11353 assert(image_info->signature == MagickSignature);
11354 assert(image != (Image *) NULL);
11355 assert(image->signature == MagickSignature);
11356 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011357 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011358 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11359 if (status == MagickFalse)
11360 return(status);
11361
11362 /*
11363 Allocate a MngInfo structure.
11364 */
11365 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011366 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011367 if (mng_info == (MngInfo *) NULL)
11368 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11369 /*
11370 Initialize members of the MngInfo structure.
11371 */
11372 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11373 mng_info->image=image;
11374 have_mng_structure=MagickTrue;
11375 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11376
11377 /*
11378 * See if user has requested a specific PNG subformat to be used
11379 * for all of the PNGs in the MNG being written, e.g.,
11380 *
11381 * convert *.png png8:animation.mng
11382 *
11383 * To do: check -define png:bit_depth and png:color_type as well,
11384 * or perhaps use mng:bit_depth and mng:color_type instead for
11385 * global settings.
11386 */
11387
11388 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11389 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11390 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11391
11392 write_jng=MagickFalse;
11393 if (image_info->compression == JPEGCompression)
11394 write_jng=MagickTrue;
11395
11396 mng_info->adjoin=image_info->adjoin &&
11397 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11398
cristy3ed852e2009-09-05 21:47:34 +000011399 if (logging != MagickFalse)
11400 {
11401 /* Log some info about the input */
11402 Image
11403 *p;
11404
11405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11406 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011407
cristy3ed852e2009-09-05 21:47:34 +000011408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011409 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011410
cristy3ed852e2009-09-05 21:47:34 +000011411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11412 " Type: %d",image_info->type);
11413
11414 scene=0;
11415 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11416 {
11417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011418 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011419
cristy3ed852e2009-09-05 21:47:34 +000011420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011421 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011422
cristy3ed852e2009-09-05 21:47:34 +000011423 if (p->matte)
11424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11425 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011426
cristy3ed852e2009-09-05 21:47:34 +000011427 else
11428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11429 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011430
cristy3ed852e2009-09-05 21:47:34 +000011431 if (p->storage_class == PseudoClass)
11432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11433 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011434
cristy3ed852e2009-09-05 21:47:34 +000011435 else
11436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11437 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011438
cristy3ed852e2009-09-05 21:47:34 +000011439 if (p->colors)
11440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011441 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011442
cristy3ed852e2009-09-05 21:47:34 +000011443 else
11444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11445 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011446
cristy3ed852e2009-09-05 21:47:34 +000011447 if (mng_info->adjoin == MagickFalse)
11448 break;
11449 }
11450 }
11451
cristy3ed852e2009-09-05 21:47:34 +000011452 use_global_plte=MagickFalse;
11453 all_images_are_gray=MagickFalse;
11454#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11455 need_local_plte=MagickTrue;
11456#endif
11457 need_defi=MagickFalse;
11458 need_matte=MagickFalse;
11459 mng_info->framing_mode=1;
11460 mng_info->old_framing_mode=1;
11461
11462 if (write_mng)
11463 if (image_info->page != (char *) NULL)
11464 {
11465 /*
11466 Determine image bounding box.
11467 */
11468 SetGeometry(image,&mng_info->page);
11469 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11470 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11471 }
11472 if (write_mng)
11473 {
11474 unsigned int
11475 need_geom;
11476
11477 unsigned short
11478 red,
11479 green,
11480 blue;
11481
11482 mng_info->page=image->page;
11483 need_geom=MagickTrue;
11484 if (mng_info->page.width || mng_info->page.height)
11485 need_geom=MagickFalse;
11486 /*
11487 Check all the scenes.
11488 */
11489 initial_delay=image->delay;
11490 need_iterations=MagickFalse;
11491 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11492 mng_info->equal_physs=MagickTrue,
11493 mng_info->equal_gammas=MagickTrue;
11494 mng_info->equal_srgbs=MagickTrue;
11495 mng_info->equal_backgrounds=MagickTrue;
11496 image_count=0;
11497#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11498 defined(PNG_MNG_FEATURES_SUPPORTED)
11499 all_images_are_gray=MagickTrue;
11500 mng_info->equal_palettes=MagickFalse;
11501 need_local_plte=MagickFalse;
11502#endif
11503 for (next_image=image; next_image != (Image *) NULL; )
11504 {
11505 if (need_geom)
11506 {
11507 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11508 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011509
cristy3ed852e2009-09-05 21:47:34 +000011510 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11511 mng_info->page.height=next_image->rows+next_image->page.y;
11512 }
glennrp0fe50b42010-11-16 03:52:51 +000011513
cristy3ed852e2009-09-05 21:47:34 +000011514 if (next_image->page.x || next_image->page.y)
11515 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011516
cristy3ed852e2009-09-05 21:47:34 +000011517 if (next_image->matte)
11518 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011519
cristy3ed852e2009-09-05 21:47:34 +000011520 if ((int) next_image->dispose >= BackgroundDispose)
11521 if (next_image->matte || next_image->page.x || next_image->page.y ||
11522 ((next_image->columns < mng_info->page.width) &&
11523 (next_image->rows < mng_info->page.height)))
11524 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011525
cristy3ed852e2009-09-05 21:47:34 +000011526 if (next_image->iterations)
11527 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011528
cristy3ed852e2009-09-05 21:47:34 +000011529 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011530
cristy3ed852e2009-09-05 21:47:34 +000011531 if (final_delay != initial_delay || final_delay > 1UL*
11532 next_image->ticks_per_second)
11533 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011534
cristy3ed852e2009-09-05 21:47:34 +000011535#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11536 defined(PNG_MNG_FEATURES_SUPPORTED)
11537 /*
11538 check for global palette possibility.
11539 */
11540 if (image->matte != MagickFalse)
11541 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011542
cristy3ed852e2009-09-05 21:47:34 +000011543 if (need_local_plte == 0)
11544 {
11545 if (ImageIsGray(image) == MagickFalse)
11546 all_images_are_gray=MagickFalse;
11547 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11548 if (use_global_plte == 0)
11549 use_global_plte=mng_info->equal_palettes;
11550 need_local_plte=!mng_info->equal_palettes;
11551 }
11552#endif
11553 if (GetNextImageInList(next_image) != (Image *) NULL)
11554 {
11555 if (next_image->background_color.red !=
11556 next_image->next->background_color.red ||
11557 next_image->background_color.green !=
11558 next_image->next->background_color.green ||
11559 next_image->background_color.blue !=
11560 next_image->next->background_color.blue)
11561 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011562
cristy3ed852e2009-09-05 21:47:34 +000011563 if (next_image->gamma != next_image->next->gamma)
11564 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011565
cristy3ed852e2009-09-05 21:47:34 +000011566 if (next_image->rendering_intent !=
11567 next_image->next->rendering_intent)
11568 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011569
cristy3ed852e2009-09-05 21:47:34 +000011570 if ((next_image->units != next_image->next->units) ||
11571 (next_image->x_resolution != next_image->next->x_resolution) ||
11572 (next_image->y_resolution != next_image->next->y_resolution))
11573 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011574
cristy3ed852e2009-09-05 21:47:34 +000011575 if (mng_info->equal_chrms)
11576 {
11577 if (next_image->chromaticity.red_primary.x !=
11578 next_image->next->chromaticity.red_primary.x ||
11579 next_image->chromaticity.red_primary.y !=
11580 next_image->next->chromaticity.red_primary.y ||
11581 next_image->chromaticity.green_primary.x !=
11582 next_image->next->chromaticity.green_primary.x ||
11583 next_image->chromaticity.green_primary.y !=
11584 next_image->next->chromaticity.green_primary.y ||
11585 next_image->chromaticity.blue_primary.x !=
11586 next_image->next->chromaticity.blue_primary.x ||
11587 next_image->chromaticity.blue_primary.y !=
11588 next_image->next->chromaticity.blue_primary.y ||
11589 next_image->chromaticity.white_point.x !=
11590 next_image->next->chromaticity.white_point.x ||
11591 next_image->chromaticity.white_point.y !=
11592 next_image->next->chromaticity.white_point.y)
11593 mng_info->equal_chrms=MagickFalse;
11594 }
11595 }
11596 image_count++;
11597 next_image=GetNextImageInList(next_image);
11598 }
11599 if (image_count < 2)
11600 {
11601 mng_info->equal_backgrounds=MagickFalse;
11602 mng_info->equal_chrms=MagickFalse;
11603 mng_info->equal_gammas=MagickFalse;
11604 mng_info->equal_srgbs=MagickFalse;
11605 mng_info->equal_physs=MagickFalse;
11606 use_global_plte=MagickFalse;
11607#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11608 need_local_plte=MagickTrue;
11609#endif
11610 need_iterations=MagickFalse;
11611 }
glennrp0fe50b42010-11-16 03:52:51 +000011612
cristy3ed852e2009-09-05 21:47:34 +000011613 if (mng_info->need_fram == MagickFalse)
11614 {
11615 /*
11616 Only certain framing rates 100/n are exactly representable without
11617 the FRAM chunk but we'll allow some slop in VLC files
11618 */
11619 if (final_delay == 0)
11620 {
11621 if (need_iterations != MagickFalse)
11622 {
11623 /*
11624 It's probably a GIF with loop; don't run it *too* fast.
11625 */
glennrp02617122010-07-28 13:07:35 +000011626 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011627 {
11628 final_delay=10;
11629 (void) ThrowMagickException(&image->exception,
11630 GetMagickModule(),CoderWarning,
11631 "input has zero delay between all frames; assuming",
11632 " 10 cs `%s'","");
11633 }
cristy3ed852e2009-09-05 21:47:34 +000011634 }
11635 else
11636 mng_info->ticks_per_second=0;
11637 }
11638 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011639 mng_info->ticks_per_second=(png_uint_32)
11640 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011641 if (final_delay > 50)
11642 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011643
cristy3ed852e2009-09-05 21:47:34 +000011644 if (final_delay > 75)
11645 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011646
cristy3ed852e2009-09-05 21:47:34 +000011647 if (final_delay > 125)
11648 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011649
cristy3ed852e2009-09-05 21:47:34 +000011650 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11651 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11652 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11653 1UL*image->ticks_per_second))
11654 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11655 }
glennrp0fe50b42010-11-16 03:52:51 +000011656
cristy3ed852e2009-09-05 21:47:34 +000011657 if (mng_info->need_fram != MagickFalse)
11658 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11659 /*
11660 If pseudocolor, we should also check to see if all the
11661 palettes are identical and write a global PLTE if they are.
11662 ../glennrp Feb 99.
11663 */
11664 /*
11665 Write the MNG version 1.0 signature and MHDR chunk.
11666 */
11667 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11668 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11669 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011670 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011671 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11672 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011673 PNGLong(chunk+12,mng_info->ticks_per_second);
11674 PNGLong(chunk+16,0L); /* layer count=unknown */
11675 PNGLong(chunk+20,0L); /* frame count=unknown */
11676 PNGLong(chunk+24,0L); /* play time=unknown */
11677 if (write_jng)
11678 {
11679 if (need_matte)
11680 {
11681 if (need_defi || mng_info->need_fram || use_global_plte)
11682 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011683
cristy3ed852e2009-09-05 21:47:34 +000011684 else
11685 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11686 }
glennrp0fe50b42010-11-16 03:52:51 +000011687
cristy3ed852e2009-09-05 21:47:34 +000011688 else
11689 {
11690 if (need_defi || mng_info->need_fram || use_global_plte)
11691 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011692
cristy3ed852e2009-09-05 21:47:34 +000011693 else
11694 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11695 }
11696 }
glennrp0fe50b42010-11-16 03:52:51 +000011697
cristy3ed852e2009-09-05 21:47:34 +000011698 else
11699 {
11700 if (need_matte)
11701 {
11702 if (need_defi || mng_info->need_fram || use_global_plte)
11703 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011704
cristy3ed852e2009-09-05 21:47:34 +000011705 else
11706 PNGLong(chunk+28,9L); /* simplicity=VLC */
11707 }
glennrp0fe50b42010-11-16 03:52:51 +000011708
cristy3ed852e2009-09-05 21:47:34 +000011709 else
11710 {
11711 if (need_defi || mng_info->need_fram || use_global_plte)
11712 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011713
cristy3ed852e2009-09-05 21:47:34 +000011714 else
11715 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11716 }
11717 }
11718 (void) WriteBlob(image,32,chunk);
11719 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11720 option=GetImageOption(image_info,"mng:need-cacheoff");
11721 if (option != (const char *) NULL)
11722 {
11723 size_t
11724 length;
11725
11726 /*
11727 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11728 */
11729 PNGType(chunk,mng_nEED);
11730 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011731 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011732 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011733 length+=4;
11734 (void) WriteBlob(image,length,chunk);
11735 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11736 }
11737 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11738 (GetNextImageInList(image) != (Image *) NULL) &&
11739 (image->iterations != 1))
11740 {
11741 /*
11742 Write MNG TERM chunk
11743 */
11744 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11745 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011746 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011747 chunk[4]=3; /* repeat animation */
11748 chunk[5]=0; /* show last frame when done */
11749 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11750 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011751
cristy3ed852e2009-09-05 21:47:34 +000011752 if (image->iterations == 0)
11753 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011754
cristy3ed852e2009-09-05 21:47:34 +000011755 else
11756 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011757
cristy3ed852e2009-09-05 21:47:34 +000011758 if (logging != MagickFalse)
11759 {
11760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011761 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11762 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011763
cristy3ed852e2009-09-05 21:47:34 +000011764 if (image->iterations == 0)
11765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011766 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011767
cristy3ed852e2009-09-05 21:47:34 +000011768 else
11769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011770 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011771 }
11772 (void) WriteBlob(image,14,chunk);
11773 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11774 }
11775 /*
11776 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11777 */
11778 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11779 mng_info->equal_srgbs)
11780 {
11781 /*
11782 Write MNG sRGB chunk
11783 */
11784 (void) WriteBlobMSBULong(image,1L);
11785 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011786 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011787
cristy3ed852e2009-09-05 21:47:34 +000011788 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011789 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011790 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011791 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011792
cristy3ed852e2009-09-05 21:47:34 +000011793 else
glennrpe610a072010-08-05 17:08:46 +000011794 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011795 Magick_RenderingIntent_to_PNG_RenderingIntent(
11796 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011797
cristy3ed852e2009-09-05 21:47:34 +000011798 (void) WriteBlob(image,5,chunk);
11799 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11800 mng_info->have_write_global_srgb=MagickTrue;
11801 }
glennrp0fe50b42010-11-16 03:52:51 +000011802
cristy3ed852e2009-09-05 21:47:34 +000011803 else
11804 {
11805 if (image->gamma && mng_info->equal_gammas)
11806 {
11807 /*
11808 Write MNG gAMA chunk
11809 */
11810 (void) WriteBlobMSBULong(image,4L);
11811 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011812 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011813 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011814 (void) WriteBlob(image,8,chunk);
11815 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11816 mng_info->have_write_global_gama=MagickTrue;
11817 }
11818 if (mng_info->equal_chrms)
11819 {
11820 PrimaryInfo
11821 primary;
11822
11823 /*
11824 Write MNG cHRM chunk
11825 */
11826 (void) WriteBlobMSBULong(image,32L);
11827 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011828 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011829 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011830 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11831 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011832 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011833 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11834 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011835 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011836 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11837 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011838 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011839 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11840 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011841 (void) WriteBlob(image,36,chunk);
11842 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11843 mng_info->have_write_global_chrm=MagickTrue;
11844 }
11845 }
11846 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11847 {
11848 /*
11849 Write MNG pHYs chunk
11850 */
11851 (void) WriteBlobMSBULong(image,9L);
11852 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011853 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011854
cristy3ed852e2009-09-05 21:47:34 +000011855 if (image->units == PixelsPerInchResolution)
11856 {
cristy35ef8242010-06-03 16:24:13 +000011857 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011858 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011859
cristy35ef8242010-06-03 16:24:13 +000011860 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011861 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011862
cristy3ed852e2009-09-05 21:47:34 +000011863 chunk[12]=1;
11864 }
glennrp0fe50b42010-11-16 03:52:51 +000011865
cristy3ed852e2009-09-05 21:47:34 +000011866 else
11867 {
11868 if (image->units == PixelsPerCentimeterResolution)
11869 {
cristy35ef8242010-06-03 16:24:13 +000011870 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011871 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011872
cristy35ef8242010-06-03 16:24:13 +000011873 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011874 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011875
cristy3ed852e2009-09-05 21:47:34 +000011876 chunk[12]=1;
11877 }
glennrp0fe50b42010-11-16 03:52:51 +000011878
cristy3ed852e2009-09-05 21:47:34 +000011879 else
11880 {
cristy35ef8242010-06-03 16:24:13 +000011881 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11882 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011883 chunk[12]=0;
11884 }
11885 }
11886 (void) WriteBlob(image,13,chunk);
11887 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11888 }
11889 /*
11890 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11891 or does not cover the entire frame.
11892 */
11893 if (write_mng && (image->matte || image->page.x > 0 ||
11894 image->page.y > 0 || (image->page.width &&
11895 (image->page.width+image->page.x < mng_info->page.width))
11896 || (image->page.height && (image->page.height+image->page.y
11897 < mng_info->page.height))))
11898 {
11899 (void) WriteBlobMSBULong(image,6L);
11900 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011901 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011902 red=ScaleQuantumToShort(image->background_color.red);
11903 green=ScaleQuantumToShort(image->background_color.green);
11904 blue=ScaleQuantumToShort(image->background_color.blue);
11905 PNGShort(chunk+4,red);
11906 PNGShort(chunk+6,green);
11907 PNGShort(chunk+8,blue);
11908 (void) WriteBlob(image,10,chunk);
11909 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11910 if (mng_info->equal_backgrounds)
11911 {
11912 (void) WriteBlobMSBULong(image,6L);
11913 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011914 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011915 (void) WriteBlob(image,10,chunk);
11916 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11917 }
11918 }
11919
11920#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11921 if ((need_local_plte == MagickFalse) &&
11922 (image->storage_class == PseudoClass) &&
11923 (all_images_are_gray == MagickFalse))
11924 {
cristybb503372010-05-27 20:51:26 +000011925 size_t
cristy3ed852e2009-09-05 21:47:34 +000011926 data_length;
11927
11928 /*
11929 Write MNG PLTE chunk
11930 */
11931 data_length=3*image->colors;
11932 (void) WriteBlobMSBULong(image,data_length);
11933 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011934 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011935
cristybb503372010-05-27 20:51:26 +000011936 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011937 {
11938 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11939 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11940 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11941 }
glennrp0fe50b42010-11-16 03:52:51 +000011942
cristy3ed852e2009-09-05 21:47:34 +000011943 (void) WriteBlob(image,data_length+4,chunk);
11944 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11945 mng_info->have_write_global_plte=MagickTrue;
11946 }
11947#endif
11948 }
11949 scene=0;
11950 mng_info->delay=0;
11951#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11952 defined(PNG_MNG_FEATURES_SUPPORTED)
11953 mng_info->equal_palettes=MagickFalse;
11954#endif
11955 do
11956 {
11957 if (mng_info->adjoin)
11958 {
11959#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11960 defined(PNG_MNG_FEATURES_SUPPORTED)
11961 /*
11962 If we aren't using a global palette for the entire MNG, check to
11963 see if we can use one for two or more consecutive images.
11964 */
11965 if (need_local_plte && use_global_plte && !all_images_are_gray)
11966 {
11967 if (mng_info->IsPalette)
11968 {
11969 /*
11970 When equal_palettes is true, this image has the same palette
11971 as the previous PseudoClass image
11972 */
11973 mng_info->have_write_global_plte=mng_info->equal_palettes;
11974 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11975 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11976 {
11977 /*
11978 Write MNG PLTE chunk
11979 */
cristybb503372010-05-27 20:51:26 +000011980 size_t
cristy3ed852e2009-09-05 21:47:34 +000011981 data_length;
11982
11983 data_length=3*image->colors;
11984 (void) WriteBlobMSBULong(image,data_length);
11985 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011986 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011987
cristybb503372010-05-27 20:51:26 +000011988 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011989 {
11990 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11991 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11992 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11993 }
glennrp0fe50b42010-11-16 03:52:51 +000011994
cristy3ed852e2009-09-05 21:47:34 +000011995 (void) WriteBlob(image,data_length+4,chunk);
11996 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11997 (uInt) (data_length+4)));
11998 mng_info->have_write_global_plte=MagickTrue;
11999 }
12000 }
12001 else
12002 mng_info->have_write_global_plte=MagickFalse;
12003 }
12004#endif
12005 if (need_defi)
12006 {
cristybb503372010-05-27 20:51:26 +000012007 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012008 previous_x,
12009 previous_y;
12010
12011 if (scene)
12012 {
12013 previous_x=mng_info->page.x;
12014 previous_y=mng_info->page.y;
12015 }
12016 else
12017 {
12018 previous_x=0;
12019 previous_y=0;
12020 }
12021 mng_info->page=image->page;
12022 if ((mng_info->page.x != previous_x) ||
12023 (mng_info->page.y != previous_y))
12024 {
12025 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12026 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012027 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012028 chunk[4]=0; /* object 0 MSB */
12029 chunk[5]=0; /* object 0 LSB */
12030 chunk[6]=0; /* visible */
12031 chunk[7]=0; /* abstract */
12032 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12033 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12034 (void) WriteBlob(image,16,chunk);
12035 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12036 }
12037 }
12038 }
12039
12040 mng_info->write_mng=write_mng;
12041
12042 if ((int) image->dispose >= 3)
12043 mng_info->framing_mode=3;
12044
12045 if (mng_info->need_fram && mng_info->adjoin &&
12046 ((image->delay != mng_info->delay) ||
12047 (mng_info->framing_mode != mng_info->old_framing_mode)))
12048 {
12049 if (image->delay == mng_info->delay)
12050 {
12051 /*
12052 Write a MNG FRAM chunk with the new framing mode.
12053 */
12054 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12055 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012056 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012057 chunk[4]=(unsigned char) mng_info->framing_mode;
12058 (void) WriteBlob(image,5,chunk);
12059 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12060 }
12061 else
12062 {
12063 /*
12064 Write a MNG FRAM chunk with the delay.
12065 */
12066 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12067 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012068 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012069 chunk[4]=(unsigned char) mng_info->framing_mode;
12070 chunk[5]=0; /* frame name separator (no name) */
12071 chunk[6]=2; /* flag for changing default delay */
12072 chunk[7]=0; /* flag for changing frame timeout */
12073 chunk[8]=0; /* flag for changing frame clipping */
12074 chunk[9]=0; /* flag for changing frame sync_id */
12075 PNGLong(chunk+10,(png_uint_32)
12076 ((mng_info->ticks_per_second*
12077 image->delay)/MagickMax(image->ticks_per_second,1)));
12078 (void) WriteBlob(image,14,chunk);
12079 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012080 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012081 }
12082 mng_info->old_framing_mode=mng_info->framing_mode;
12083 }
12084
12085#if defined(JNG_SUPPORTED)
12086 if (image_info->compression == JPEGCompression)
12087 {
12088 ImageInfo
12089 *write_info;
12090
12091 if (logging != MagickFalse)
12092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12093 " Writing JNG object.");
12094 /* To do: specify the desired alpha compression method. */
12095 write_info=CloneImageInfo(image_info);
12096 write_info->compression=UndefinedCompression;
12097 status=WriteOneJNGImage(mng_info,write_info,image);
12098 write_info=DestroyImageInfo(write_info);
12099 }
12100 else
12101#endif
12102 {
12103 if (logging != MagickFalse)
12104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12105 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012106
glennrpb9cfe272010-12-21 15:08:06 +000012107 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012108 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012109
12110 /* We don't want any ancillary chunks written */
12111 mng_info->ping_exclude_bKGD=MagickTrue;
12112 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012113 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012114 mng_info->ping_exclude_EXIF=MagickTrue;
12115 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012116 mng_info->ping_exclude_iCCP=MagickTrue;
12117 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12118 mng_info->ping_exclude_oFFs=MagickTrue;
12119 mng_info->ping_exclude_pHYs=MagickTrue;
12120 mng_info->ping_exclude_sRGB=MagickTrue;
12121 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012122 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012123 mng_info->ping_exclude_vpAg=MagickTrue;
12124 mng_info->ping_exclude_zCCP=MagickTrue;
12125 mng_info->ping_exclude_zTXt=MagickTrue;
12126
cristy3ed852e2009-09-05 21:47:34 +000012127 status=WriteOnePNGImage(mng_info,image_info,image);
12128 }
12129
12130 if (status == MagickFalse)
12131 {
12132 MngInfoFreeStruct(mng_info,&have_mng_structure);
12133 (void) CloseBlob(image);
12134 return(MagickFalse);
12135 }
12136 (void) CatchImageException(image);
12137 if (GetNextImageInList(image) == (Image *) NULL)
12138 break;
12139 image=SyncNextImageInList(image);
12140 status=SetImageProgress(image,SaveImagesTag,scene++,
12141 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012142
cristy3ed852e2009-09-05 21:47:34 +000012143 if (status == MagickFalse)
12144 break;
glennrp0fe50b42010-11-16 03:52:51 +000012145
cristy3ed852e2009-09-05 21:47:34 +000012146 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012147
cristy3ed852e2009-09-05 21:47:34 +000012148 if (write_mng)
12149 {
12150 while (GetPreviousImageInList(image) != (Image *) NULL)
12151 image=GetPreviousImageInList(image);
12152 /*
12153 Write the MEND chunk.
12154 */
12155 (void) WriteBlobMSBULong(image,0x00000000L);
12156 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012157 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012158 (void) WriteBlob(image,4,chunk);
12159 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12160 }
12161 /*
12162 Relinquish resources.
12163 */
12164 (void) CloseBlob(image);
12165 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012166
cristy3ed852e2009-09-05 21:47:34 +000012167 if (logging != MagickFalse)
12168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012169
cristy3ed852e2009-09-05 21:47:34 +000012170 return(MagickTrue);
12171}
glennrpd5045b42010-03-24 12:40:35 +000012172#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012173
cristy3ed852e2009-09-05 21:47:34 +000012174static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12175{
12176 image=image;
12177 printf("Your PNG library is too old: You have libpng-%s\n",
12178 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012179
cristy3ed852e2009-09-05 21:47:34 +000012180 ThrowBinaryException(CoderError,"PNG library is too old",
12181 image_info->filename);
12182}
glennrp39992b42010-11-14 00:03:43 +000012183
cristy3ed852e2009-09-05 21:47:34 +000012184static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12185{
12186 return(WritePNGImage(image_info,image));
12187}
glennrpd5045b42010-03-24 12:40:35 +000012188#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012189#endif