blob: 63d9c9967fd99662e3ae04eb862f575ba5b7a14d [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
44#include "magick/studio.h"
glennrp5c7cf4e2010-12-24 00:30:00 +000045#include "magick/artifact.h"
cristy5a2ca482009-10-14 18:24:56 +000046#include "magick/attribute.h"
cristy3ed852e2009-09-05 21:47:34 +000047#include "magick/blob.h"
48#include "magick/blob-private.h"
49#include "magick/cache.h"
50#include "magick/color.h"
51#include "magick/color-private.h"
cristy4ccd4c02010-04-25 00:43:15 +000052#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000053#include "magick/colorspace.h"
54#include "magick/constitute.h"
55#include "magick/enhance.h"
56#include "magick/exception.h"
57#include "magick/exception-private.h"
58#include "magick/geometry.h"
cristyf2e11662009-10-14 01:24:43 +000059#include "magick/histogram.h"
cristy3ed852e2009-09-05 21:47:34 +000060#include "magick/image.h"
61#include "magick/image-private.h"
62#include "magick/layer.h"
63#include "magick/list.h"
64#include "magick/log.h"
65#include "magick/magick.h"
66#include "magick/memory_.h"
67#include "magick/module.h"
68#include "magick/monitor.h"
69#include "magick/monitor-private.h"
70#include "magick/option.h"
71#include "magick/quantum-private.h"
72#include "magick/profile.h"
73#include "magick/property.h"
cristy3ed852e2009-09-05 21:47:34 +000074#include "magick/resource_.h"
75#include "magick/semaphore.h"
76#include "magick/quantum-private.h"
77#include "magick/static.h"
78#include "magick/statistic.h"
79#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000080#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000081#include "magick/transform.h"
82#include "magick/utility.h"
83#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000084
glennrp7ef138c2009-11-10 13:50:20 +000085/* Suppress libpng pedantic warnings that were added in
86 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000087 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000088 * fix any code that generates warnings.
89 */
glennrp991e92a2010-01-28 03:09:00 +000090/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000091/* #define PNG_USE_RESULT The result of this function must be checked */
92/* #define PNG_NORETURN This function does not return */
93/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000094/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000095
96/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +000097#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +000098
cristy3ed852e2009-09-05 21:47:34 +000099#include "png.h"
100#include "zlib.h"
101
102/* ImageMagick differences */
103#define first_scene scene
104
glennrpd5045b42010-03-24 12:40:35 +0000105#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000106/*
107 Optional declarations. Define or undefine them as you like.
108*/
109/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
110
111/*
112 Features under construction. Define these to work on them.
113*/
114#undef MNG_OBJECT_BUFFERS
115#undef MNG_BASI_SUPPORTED
116#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
117#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000118#if defined(MAGICKCORE_JPEG_DELEGATE)
119# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
120#endif
121#if !defined(RGBColorMatchExact)
122#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000123 (((color).red == (target).red) && \
124 ((color).green == (target).green) && \
125 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000126#endif
127
glennrpb5d5e6d2011-04-27 17:08:19 +0000128/* Convenience macros for copying RGB or RGB+opacity components
129 * between a pixel and a PixelPacket.
130 */
glennrp8e045c82011-04-27 16:40:27 +0000131
glennrpb5d5e6d2011-04-27 17:08:19 +0000132#define GetRGBOPixelComponents(pixel, packet) \
133 (packet).red = GetRedPixelComponent((pixel)); \
134 (packet).green = GetGreenPixelComponent((pixel)); \
135 (packet).red = GetBluePixelComponent((pixel)); \
136 (packet).opacity = GetOpacityPixelComponent((pixel)); \
137
138#define SetRGBOPixelComponents(pixel, packet) \
139 SetRedPixelComponent((pixel),(packet).red); \
140 SetGreenPixelComponent((pixel),(packet).green); \
141 SetBluePixelComponent((pixel),(packet).blue); \
142 SetOpacityPixelComponent((pixel),(packet).opacity); \
glennrp8e045c82011-04-27 16:40:27 +0000143
144
glennrpb5d5e6d2011-04-27 17:08:19 +0000145#define GetRGBPixelComponents(pixel, packet) \
146 (packet).red = GetRedPixelComponent((pixel)); \
147 (packet).green = GetGreenPixelComponent((pixel)); \
148 (packet).red = GetBluePixelComponent((pixel));
glennrp8e045c82011-04-27 16:40:27 +0000149
glennrpb5d5e6d2011-04-27 17:08:19 +0000150#define SetRGBPixelComponents(pixel, packet) \
151 SetRedPixelComponent((pixel),(packet).red); \
152 SetGreenPixelComponent((pixel),(packet).green); \
153 SetBluePixelComponent((pixel),(packet).blue);
glennrp8e045c82011-04-27 16:40:27 +0000154
cristy3ed852e2009-09-05 21:47:34 +0000155/*
156 Establish thread safety.
157 setjmp/longjmp is claimed to be safe on these platforms:
158 setjmp/longjmp is alleged to be unsafe on these platforms:
159*/
160#ifndef SETJMP_IS_THREAD_SAFE
161#define PNG_SETJMP_NOT_THREAD_SAFE
162#endif
163
164#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
165static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000166 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000167#endif
168
169/*
170 This temporary until I set up malloc'ed object attributes array.
171 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
172 waste more memory.
173*/
174#define MNG_MAX_OBJECTS 256
175
176/*
177 If this not defined, spec is interpreted strictly. If it is
178 defined, an attempt will be made to recover from some errors,
179 including
180 o global PLTE too short
181*/
182#undef MNG_LOOSE
183
184/*
185 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
186 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
187 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
188 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
189 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
190 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
191 will be enabled by default in libpng-1.2.0.
192*/
cristy3ed852e2009-09-05 21:47:34 +0000193#ifdef PNG_MNG_FEATURES_SUPPORTED
194# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
195# define PNG_READ_EMPTY_PLTE_SUPPORTED
196# endif
197# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
198# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
199# endif
200#endif
201
202/*
cristybb503372010-05-27 20:51:26 +0000203 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000204 This macro is only defined in libpng-1.0.3 and later.
205 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
206*/
207#ifndef PNG_UINT_31_MAX
208#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
209#endif
210
211/*
212 Constant strings for known chunk types. If you need to add a chunk,
213 add a string holding the name here. To make the code more
214 portable, we use ASCII numbers like this, not characters.
215*/
216
217static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
218static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
219static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
220static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
221static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
222static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
223static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
224static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
225static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
226static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
227static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
228static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
229static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
230static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
231static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
232static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
233static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
234static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
235static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
236static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
237static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
238static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
239static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
240static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
241static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
242static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
243static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
244static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
245static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
246static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
247static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
248static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
249static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
250static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
251
252#if defined(JNG_SUPPORTED)
253static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
254static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
255static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
256static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
257static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
258static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
259#endif
260
261/*
262Other known chunks that are not yet supported by ImageMagick:
263static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
264static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
265static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
266static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
267static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
268static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
269static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
270static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
271*/
272
273typedef struct _MngBox
274{
cristy8182b072010-05-30 20:10:53 +0000275 long
cristy3ed852e2009-09-05 21:47:34 +0000276 left,
277 right,
278 top,
279 bottom;
280} MngBox;
281
282typedef struct _MngPair
283{
cristy8182b072010-05-30 20:10:53 +0000284 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000285 a,
286 b;
287} MngPair;
288
289#ifdef MNG_OBJECT_BUFFERS
290typedef struct _MngBuffer
291{
292
cristybb503372010-05-27 20:51:26 +0000293 size_t
cristy3ed852e2009-09-05 21:47:34 +0000294 height,
295 width;
296
297 Image
298 *image;
299
300 png_color
301 plte[256];
302
303 int
304 reference_count;
305
306 unsigned char
307 alpha_sample_depth,
308 compression_method,
309 color_type,
310 concrete,
311 filter_method,
312 frozen,
313 image_type,
314 interlace_method,
315 pixel_sample_depth,
316 plte_length,
317 sample_depth,
318 viewable;
319} MngBuffer;
320#endif
321
322typedef struct _MngInfo
323{
324
325#ifdef MNG_OBJECT_BUFFERS
326 MngBuffer
327 *ob[MNG_MAX_OBJECTS];
328#endif
329
330 Image *
331 image;
332
333 RectangleInfo
334 page;
335
336 int
337 adjoin,
338#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
339 bytes_in_read_buffer,
340 found_empty_plte,
341#endif
342 equal_backgrounds,
343 equal_chrms,
344 equal_gammas,
345#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
346 defined(PNG_MNG_FEATURES_SUPPORTED)
347 equal_palettes,
348#endif
349 equal_physs,
350 equal_srgbs,
351 framing_mode,
352 have_global_bkgd,
353 have_global_chrm,
354 have_global_gama,
355 have_global_phys,
356 have_global_sbit,
357 have_global_srgb,
358 have_saved_bkgd_index,
359 have_write_global_chrm,
360 have_write_global_gama,
361 have_write_global_plte,
362 have_write_global_srgb,
363 need_fram,
364 object_id,
365 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000366 saved_bkgd_index;
367
368 int
369 new_number_colors;
370
cristybb503372010-05-27 20:51:26 +0000371 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000372 image_found,
373 loop_count[256],
374 loop_iteration[256],
375 scenes_found,
376 x_off[MNG_MAX_OBJECTS],
377 y_off[MNG_MAX_OBJECTS];
378
379 MngBox
380 clip,
381 frame,
382 image_box,
383 object_clip[MNG_MAX_OBJECTS];
384
385 unsigned char
386 /* These flags could be combined into one byte */
387 exists[MNG_MAX_OBJECTS],
388 frozen[MNG_MAX_OBJECTS],
389 loop_active[256],
390 invisible[MNG_MAX_OBJECTS],
391 viewable[MNG_MAX_OBJECTS];
392
393 MagickOffsetType
394 loop_jump[256];
395
396 png_colorp
397 global_plte;
398
399 png_color_8
400 global_sbit;
401
402 png_byte
403#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
404 read_buffer[8],
405#endif
406 global_trns[256];
407
408 float
409 global_gamma;
410
411 ChromaticityInfo
412 global_chrm;
413
414 RenderingIntent
415 global_srgb_intent;
416
cristy35ef8242010-06-03 16:24:13 +0000417 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000418 delay,
419 global_plte_length,
420 global_trns_length,
421 global_x_pixels_per_unit,
422 global_y_pixels_per_unit,
423 mng_width,
424 mng_height,
425 ticks_per_second;
426
glennrpb9cfe272010-12-21 15:08:06 +0000427 MagickBooleanType
428 need_blob;
429
cristy3ed852e2009-09-05 21:47:34 +0000430 unsigned int
431 IsPalette,
432 global_phys_unit_type,
433 basi_warning,
434 clon_warning,
435 dhdr_warning,
436 jhdr_warning,
437 magn_warning,
438 past_warning,
439 phyg_warning,
440 phys_warning,
441 sbit_warning,
442 show_warning,
443 mng_type,
444 write_mng,
445 write_png_colortype,
446 write_png_depth,
447 write_png8,
448 write_png24,
449 write_png32;
450
451#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000452 size_t
cristy3ed852e2009-09-05 21:47:34 +0000453 basi_width,
454 basi_height;
455
456 unsigned int
457 basi_depth,
458 basi_color_type,
459 basi_compression_method,
460 basi_filter_type,
461 basi_interlace_method,
462 basi_red,
463 basi_green,
464 basi_blue,
465 basi_alpha,
466 basi_viewable;
467#endif
468
469 png_uint_16
470 magn_first,
471 magn_last,
472 magn_mb,
473 magn_ml,
474 magn_mr,
475 magn_mt,
476 magn_mx,
477 magn_my,
478 magn_methx,
479 magn_methy;
480
481 PixelPacket
482 mng_global_bkgd;
483
glennrp26f37912010-12-23 16:22:42 +0000484 /* Added at version 6.6.6-7 */
485 MagickBooleanType
486 ping_exclude_bKGD,
487 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000488 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000489 ping_exclude_EXIF,
490 ping_exclude_gAMA,
491 ping_exclude_iCCP,
492 /* ping_exclude_iTXt, */
493 ping_exclude_oFFs,
494 ping_exclude_pHYs,
495 ping_exclude_sRGB,
496 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000497 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000498 ping_exclude_vpAg,
499 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000500 ping_exclude_zTXt,
501 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000502
cristy3ed852e2009-09-05 21:47:34 +0000503} MngInfo;
504#endif /* VER */
505
506/*
507 Forward declarations.
508*/
509static MagickBooleanType
510 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000511
cristy3ed852e2009-09-05 21:47:34 +0000512static MagickBooleanType
513 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000514
cristy3ed852e2009-09-05 21:47:34 +0000515#if defined(JNG_SUPPORTED)
516static MagickBooleanType
517 WriteJNGImage(const ImageInfo *,Image *);
518#endif
519
glennrp0c3e06b2010-11-19 13:45:02 +0000520#if PNG_LIBPNG_VER > 10011
521
glennrpfd05d622011-02-25 04:10:33 +0000522
glennrp0c3e06b2010-11-19 13:45:02 +0000523#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
524static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000525LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000526{
glennrp67b9c1a2011-04-22 18:47:36 +0000527 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
528 *
529 * This is true if the high byte and the next highest byte of
530 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000531 * are equal to each other. We check this by seeing if the samples
532 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000533 *
534 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000535 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000536 */
537
glennrp3faa9a32011-04-23 14:00:25 +0000538#define QuantumToCharToQuantumEqQuantum(quantum) \
539 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
540
glennrp0c3e06b2010-11-19 13:45:02 +0000541 MagickBooleanType
542 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000543
glennrp03e11f62011-04-22 13:30:16 +0000544 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000545 {
546
547 const PixelPacket
548 *p;
549
550 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000551 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
552 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
553 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
554 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000555
556 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
557 {
558 int indx;
559
560 for (indx=0; indx < (ssize_t) image->colors; indx++)
561 {
glennrp3faa9a32011-04-23 14:00:25 +0000562 ok_to_reduce=(
563 QuantumToCharToQuantumEqQuantum(
564 image->colormap[indx].red) &&
565 QuantumToCharToQuantumEqQuantum(
566 image->colormap[indx].green) &&
567 QuantumToCharToQuantumEqQuantum(
568 image->colormap[indx].blue)) ?
569 MagickTrue : MagickFalse;
570
glennrp0c3e06b2010-11-19 13:45:02 +0000571 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000572 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000573 }
574 }
575
576 if ((ok_to_reduce != MagickFalse) &&
577 (image->storage_class != PseudoClass))
578 {
579 ssize_t
580 y;
581
582 register ssize_t
583 x;
584
585 for (y=0; y < (ssize_t) image->rows; y++)
586 {
587 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
588
589 if (p == (const PixelPacket *) NULL)
590 {
591 ok_to_reduce = MagickFalse;
592 break;
593 }
594
595 for (x=(ssize_t) image->columns-1; x >= 0; x--)
596 {
glennrp3faa9a32011-04-23 14:00:25 +0000597 ok_to_reduce=
598 QuantumToCharToQuantumEqQuantum(GetRedPixelComponent(p)) &&
599 QuantumToCharToQuantumEqQuantum(GetGreenPixelComponent(p)) &&
600 QuantumToCharToQuantumEqQuantum(GetBluePixelComponent(p)) ?
601 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000602
603 if (ok_to_reduce == MagickFalse)
604 break;
605
606 p++;
607 }
glennrp8640fb52010-11-23 15:48:26 +0000608 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +0000609 break;
610 }
611 }
612
613 if (ok_to_reduce != MagickFalse)
614 {
glennrp0c3e06b2010-11-19 13:45:02 +0000615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000616 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +0000617 }
glennrpa6a06632011-01-19 15:15:34 +0000618 else
619 {
620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000621 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +0000622 }
glennrp0c3e06b2010-11-19 13:45:02 +0000623 }
624
625 return ok_to_reduce;
626}
627#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
628
glennrpe610a072010-08-05 17:08:46 +0000629static int
glennrpcf002022011-01-30 02:38:15 +0000630Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +0000631{
glennrpe610a072010-08-05 17:08:46 +0000632 switch (intent)
633 {
634 case PerceptualIntent:
635 return 0;
glennrp0fe50b42010-11-16 03:52:51 +0000636
glennrpe610a072010-08-05 17:08:46 +0000637 case RelativeIntent:
638 return 1;
glennrp0fe50b42010-11-16 03:52:51 +0000639
glennrpe610a072010-08-05 17:08:46 +0000640 case SaturationIntent:
641 return 2;
glennrp0fe50b42010-11-16 03:52:51 +0000642
glennrpe610a072010-08-05 17:08:46 +0000643 case AbsoluteIntent:
644 return 3;
glennrp0fe50b42010-11-16 03:52:51 +0000645
glennrpe610a072010-08-05 17:08:46 +0000646 default:
647 return -1;
648 }
649}
650
651static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +0000652Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +0000653{
glennrpcf002022011-01-30 02:38:15 +0000654 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +0000655 {
656 case 0:
657 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000658
glennrpe610a072010-08-05 17:08:46 +0000659 case 1:
660 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000661
glennrpe610a072010-08-05 17:08:46 +0000662 case 2:
663 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000664
glennrpe610a072010-08-05 17:08:46 +0000665 case 3:
666 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000667
glennrpe610a072010-08-05 17:08:46 +0000668 default:
669 return UndefinedIntent;
670 }
671}
672
cristybb503372010-05-27 20:51:26 +0000673static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000674{
675 if (x > y)
676 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000677
cristy3ed852e2009-09-05 21:47:34 +0000678 return(y);
679}
glennrp0c3e06b2010-11-19 13:45:02 +0000680
cristybb503372010-05-27 20:51:26 +0000681static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000682{
683 if (x < y)
684 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000685
cristy3ed852e2009-09-05 21:47:34 +0000686 return(y);
687}
glennrp0c3e06b2010-11-19 13:45:02 +0000688
cristy3ed852e2009-09-05 21:47:34 +0000689
690/*
691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
692% %
693% %
694% %
695% I m a g e I s G r a y %
696% %
697% %
698% %
699%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
700% %
701% Like IsGrayImage except does not change DirectClass to PseudoClass %
702% %
703%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
704*/
705static MagickBooleanType ImageIsGray(Image *image)
706{
707 register const PixelPacket
708 *p;
709
cristybb503372010-05-27 20:51:26 +0000710 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000711 i,
712 x,
713 y;
714
715 assert(image != (Image *) NULL);
716 assert(image->signature == MagickSignature);
717 if (image->debug != MagickFalse)
718 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
719
720 if (image->storage_class == PseudoClass)
721 {
cristybb503372010-05-27 20:51:26 +0000722 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000723 if (IsGray(image->colormap+i) == MagickFalse)
724 return(MagickFalse);
725 return(MagickTrue);
726 }
cristybb503372010-05-27 20:51:26 +0000727 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000728 {
729 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
730 if (p == (const PixelPacket *) NULL)
731 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000732 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +0000733 {
734 if (IsGray(p) == MagickFalse)
735 return(MagickFalse);
736 p++;
737 }
738 }
739 return(MagickTrue);
740}
glennrpd5045b42010-03-24 12:40:35 +0000741#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000742#endif /* MAGICKCORE_PNG_DELEGATE */
743
744/*
745%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
746% %
747% %
748% %
749% I s M N G %
750% %
751% %
752% %
753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
754%
755% IsMNG() returns MagickTrue if the image format type, identified by the
756% magick string, is MNG.
757%
758% The format of the IsMNG method is:
759%
760% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
761%
762% A description of each parameter follows:
763%
764% o magick: compare image format pattern against these bytes.
765%
766% o length: Specifies the length of the magick string.
767%
768%
769*/
770static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
771{
772 if (length < 8)
773 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000774
cristy3ed852e2009-09-05 21:47:34 +0000775 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
776 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000777
cristy3ed852e2009-09-05 21:47:34 +0000778 return(MagickFalse);
779}
780
781/*
782%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
783% %
784% %
785% %
786% I s J N G %
787% %
788% %
789% %
790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791%
792% IsJNG() returns MagickTrue if the image format type, identified by the
793% magick string, is JNG.
794%
795% The format of the IsJNG method is:
796%
797% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
798%
799% A description of each parameter follows:
800%
801% o magick: compare image format pattern against these bytes.
802%
803% o length: Specifies the length of the magick string.
804%
805%
806*/
807static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
808{
809 if (length < 8)
810 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000811
cristy3ed852e2009-09-05 21:47:34 +0000812 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
813 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000814
cristy3ed852e2009-09-05 21:47:34 +0000815 return(MagickFalse);
816}
817
818/*
819%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
820% %
821% %
822% %
823% I s P N G %
824% %
825% %
826% %
827%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
828%
829% IsPNG() returns MagickTrue if the image format type, identified by the
830% magick string, is PNG.
831%
832% The format of the IsPNG method is:
833%
834% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
835%
836% A description of each parameter follows:
837%
838% o magick: compare image format pattern against these bytes.
839%
840% o length: Specifies the length of the magick string.
841%
842*/
843static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
844{
845 if (length < 8)
846 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000847
cristy3ed852e2009-09-05 21:47:34 +0000848 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
849 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000850
cristy3ed852e2009-09-05 21:47:34 +0000851 return(MagickFalse);
852}
853
854#if defined(MAGICKCORE_PNG_DELEGATE)
855#if defined(__cplusplus) || defined(c_plusplus)
856extern "C" {
857#endif
858
glennrpd5045b42010-03-24 12:40:35 +0000859#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +0000860static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +0000861{
862 unsigned char
863 buffer[4];
864
865 assert(image != (Image *) NULL);
866 assert(image->signature == MagickSignature);
867 buffer[0]=(unsigned char) (value >> 24);
868 buffer[1]=(unsigned char) (value >> 16);
869 buffer[2]=(unsigned char) (value >> 8);
870 buffer[3]=(unsigned char) value;
871 return((size_t) WriteBlob(image,4,buffer));
872}
873
874static void PNGLong(png_bytep p,png_uint_32 value)
875{
876 *p++=(png_byte) ((value >> 24) & 0xff);
877 *p++=(png_byte) ((value >> 16) & 0xff);
878 *p++=(png_byte) ((value >> 8) & 0xff);
879 *p++=(png_byte) (value & 0xff);
880}
881
glennrpa521b2f2010-10-29 04:11:03 +0000882#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +0000883static void PNGsLong(png_bytep p,png_int_32 value)
884{
885 *p++=(png_byte) ((value >> 24) & 0xff);
886 *p++=(png_byte) ((value >> 16) & 0xff);
887 *p++=(png_byte) ((value >> 8) & 0xff);
888 *p++=(png_byte) (value & 0xff);
889}
glennrpa521b2f2010-10-29 04:11:03 +0000890#endif
cristy3ed852e2009-09-05 21:47:34 +0000891
892static void PNGShort(png_bytep p,png_uint_16 value)
893{
894 *p++=(png_byte) ((value >> 8) & 0xff);
895 *p++=(png_byte) (value & 0xff);
896}
897
898static void PNGType(png_bytep p,png_bytep type)
899{
900 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
901}
902
glennrp03812ae2010-12-24 01:31:34 +0000903static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
904 size_t length)
cristy3ed852e2009-09-05 21:47:34 +0000905{
906 if (logging != MagickFalse)
907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000908 " Writing %c%c%c%c chunk, length: %.20g",
909 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000910}
glennrpd5045b42010-03-24 12:40:35 +0000911#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000912
913#if defined(__cplusplus) || defined(c_plusplus)
914}
915#endif
916
glennrpd5045b42010-03-24 12:40:35 +0000917#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000918/*
919%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
920% %
921% %
922% %
923% R e a d P N G I m a g e %
924% %
925% %
926% %
927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
928%
929% ReadPNGImage() reads a Portable Network Graphics (PNG) or
930% Multiple-image Network Graphics (MNG) image file and returns it. It
931% allocates the memory necessary for the new Image structure and returns a
932% pointer to the new image or set of images.
933%
934% MNG support written by Glenn Randers-Pehrson, glennrp@image...
935%
936% The format of the ReadPNGImage method is:
937%
938% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
939%
940% A description of each parameter follows:
941%
942% o image_info: the image info.
943%
944% o exception: return any errors or warnings in this structure.
945%
946% To do, more or less in chronological order (as of version 5.5.2,
947% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
948%
949% Get 16-bit cheap transparency working.
950%
951% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
952%
953% Preserve all unknown and not-yet-handled known chunks found in input
954% PNG file and copy them into output PNG files according to the PNG
955% copying rules.
956%
957% (At this point, PNG encoding should be in full MNG compliance)
958%
959% Provide options for choice of background to use when the MNG BACK
960% chunk is not present or is not mandatory (i.e., leave transparent,
961% user specified, MNG BACK, PNG bKGD)
962%
963% Implement LOOP/ENDL [done, but could do discretionary loops more
964% efficiently by linking in the duplicate frames.].
965%
966% Decode and act on the MHDR simplicity profile (offer option to reject
967% files or attempt to process them anyway when the profile isn't LC or VLC).
968%
969% Upgrade to full MNG without Delta-PNG.
970%
971% o BACK [done a while ago except for background image ID]
972% o MOVE [done 15 May 1999]
973% o CLIP [done 15 May 1999]
974% o DISC [done 19 May 1999]
975% o SAVE [partially done 19 May 1999 (marks objects frozen)]
976% o SEEK [partially done 19 May 1999 (discard function only)]
977% o SHOW
978% o PAST
979% o BASI
980% o MNG-level tEXt/iTXt/zTXt
981% o pHYg
982% o pHYs
983% o sBIT
984% o bKGD
985% o iTXt (wait for libpng implementation).
986%
987% Use the scene signature to discover when an identical scene is
988% being reused, and just point to the original image->exception instead
989% of storing another set of pixels. This not specific to MNG
990% but could be applied generally.
991%
992% Upgrade to full MNG with Delta-PNG.
993%
994% JNG tEXt/iTXt/zTXt
995%
996% We will not attempt to read files containing the CgBI chunk.
997% They are really Xcode files meant for display on the iPhone.
998% These are not valid PNG files and it is impossible to recover
999% the orginal PNG from files that have been converted to Xcode-PNG,
1000% since irretrievable loss of color data has occurred due to the
1001% use of premultiplied alpha.
1002*/
1003
1004#if defined(__cplusplus) || defined(c_plusplus)
1005extern "C" {
1006#endif
1007
1008/*
1009 This the function that does the actual reading of data. It is
1010 the same as the one supplied in libpng, except that it receives the
1011 datastream from the ReadBlob() function instead of standard input.
1012*/
1013static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1014{
1015 Image
1016 *image;
1017
1018 image=(Image *) png_get_io_ptr(png_ptr);
1019 if (length)
1020 {
1021 png_size_t
1022 check;
1023
1024 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1025 if (check != length)
1026 {
1027 char
1028 msg[MaxTextExtent];
1029
1030 (void) FormatMagickString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001031 "Expected %.20g bytes; found %.20g bytes",(double) length,
1032 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001033 png_warning(png_ptr,msg);
1034 png_error(png_ptr,"Read Exception");
1035 }
1036 }
1037}
1038
1039#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1040 !defined(PNG_MNG_FEATURES_SUPPORTED)
1041/* We use mng_get_data() instead of png_get_data() if we have a libpng
1042 * older than libpng-1.0.3a, which was the first to allow the empty
1043 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1044 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1045 * encountered after an empty PLTE, so we have to look ahead for bKGD
1046 * chunks and remove them from the datastream that is passed to libpng,
1047 * and store their contents for later use.
1048 */
1049static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1050{
1051 MngInfo
1052 *mng_info;
1053
1054 Image
1055 *image;
1056
1057 png_size_t
1058 check;
1059
cristybb503372010-05-27 20:51:26 +00001060 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001061 i;
1062
1063 i=0;
1064 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1065 image=(Image *) mng_info->image;
1066 while (mng_info->bytes_in_read_buffer && length)
1067 {
1068 data[i]=mng_info->read_buffer[i];
1069 mng_info->bytes_in_read_buffer--;
1070 length--;
1071 i++;
1072 }
1073 if (length)
1074 {
1075 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001076
cristy3ed852e2009-09-05 21:47:34 +00001077 if (check != length)
1078 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001079
cristy3ed852e2009-09-05 21:47:34 +00001080 if (length == 4)
1081 {
1082 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1083 (data[3] == 0))
1084 {
1085 check=(png_size_t) ReadBlob(image,(size_t) length,
1086 (char *) mng_info->read_buffer);
1087 mng_info->read_buffer[4]=0;
1088 mng_info->bytes_in_read_buffer=4;
1089 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1090 mng_info->found_empty_plte=MagickTrue;
1091 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1092 {
1093 mng_info->found_empty_plte=MagickFalse;
1094 mng_info->have_saved_bkgd_index=MagickFalse;
1095 }
1096 }
glennrp0fe50b42010-11-16 03:52:51 +00001097
cristy3ed852e2009-09-05 21:47:34 +00001098 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1099 (data[3] == 1))
1100 {
1101 check=(png_size_t) ReadBlob(image,(size_t) length,
1102 (char *) mng_info->read_buffer);
1103 mng_info->read_buffer[4]=0;
1104 mng_info->bytes_in_read_buffer=4;
1105 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1106 if (mng_info->found_empty_plte)
1107 {
1108 /*
1109 Skip the bKGD data byte and CRC.
1110 */
1111 check=(png_size_t)
1112 ReadBlob(image,5,(char *) mng_info->read_buffer);
1113 check=(png_size_t) ReadBlob(image,(size_t) length,
1114 (char *) mng_info->read_buffer);
1115 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1116 mng_info->have_saved_bkgd_index=MagickTrue;
1117 mng_info->bytes_in_read_buffer=0;
1118 }
1119 }
1120 }
1121 }
1122}
1123#endif
1124
1125static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1126{
1127 Image
1128 *image;
1129
1130 image=(Image *) png_get_io_ptr(png_ptr);
1131 if (length)
1132 {
1133 png_size_t
1134 check;
1135
cristybb503372010-05-27 20:51:26 +00001136 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001137
cristy3ed852e2009-09-05 21:47:34 +00001138 if (check != length)
1139 png_error(png_ptr,"WriteBlob Failed");
1140 }
1141}
1142
1143static void png_flush_data(png_structp png_ptr)
1144{
1145 (void) png_ptr;
1146}
1147
1148#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1149static int PalettesAreEqual(Image *a,Image *b)
1150{
cristybb503372010-05-27 20:51:26 +00001151 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001152 i;
1153
1154 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1155 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001156
cristy3ed852e2009-09-05 21:47:34 +00001157 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1158 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001159
cristy3ed852e2009-09-05 21:47:34 +00001160 if (a->colors != b->colors)
1161 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001162
cristybb503372010-05-27 20:51:26 +00001163 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001164 {
1165 if ((a->colormap[i].red != b->colormap[i].red) ||
1166 (a->colormap[i].green != b->colormap[i].green) ||
1167 (a->colormap[i].blue != b->colormap[i].blue))
1168 return((int) MagickFalse);
1169 }
glennrp0fe50b42010-11-16 03:52:51 +00001170
cristy3ed852e2009-09-05 21:47:34 +00001171 return((int) MagickTrue);
1172}
1173#endif
1174
1175static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1176{
1177 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1178 mng_info->exists[i] && !mng_info->frozen[i])
1179 {
1180#ifdef MNG_OBJECT_BUFFERS
1181 if (mng_info->ob[i] != (MngBuffer *) NULL)
1182 {
1183 if (mng_info->ob[i]->reference_count > 0)
1184 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001185
cristy3ed852e2009-09-05 21:47:34 +00001186 if (mng_info->ob[i]->reference_count == 0)
1187 {
1188 if (mng_info->ob[i]->image != (Image *) NULL)
1189 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001190
cristy3ed852e2009-09-05 21:47:34 +00001191 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1192 }
1193 }
1194 mng_info->ob[i]=(MngBuffer *) NULL;
1195#endif
1196 mng_info->exists[i]=MagickFalse;
1197 mng_info->invisible[i]=MagickFalse;
1198 mng_info->viewable[i]=MagickFalse;
1199 mng_info->frozen[i]=MagickFalse;
1200 mng_info->x_off[i]=0;
1201 mng_info->y_off[i]=0;
1202 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001203 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001204 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001205 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001206 }
1207}
1208
glennrp21f0e622011-01-07 16:20:57 +00001209static void MngInfoFreeStruct(MngInfo *mng_info,
1210 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001211{
glennrp21f0e622011-01-07 16:20:57 +00001212 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001213 {
cristybb503372010-05-27 20:51:26 +00001214 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001215 i;
1216
1217 for (i=1; i < MNG_MAX_OBJECTS; i++)
1218 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001219
cristy3ed852e2009-09-05 21:47:34 +00001220 if (mng_info->global_plte != (png_colorp) NULL)
1221 mng_info->global_plte=(png_colorp)
1222 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001223
cristy3ed852e2009-09-05 21:47:34 +00001224 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1225 *have_mng_structure=MagickFalse;
1226 }
1227}
1228
1229static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1230{
1231 MngBox
1232 box;
1233
1234 box=box1;
1235 if (box.left < box2.left)
1236 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001237
cristy3ed852e2009-09-05 21:47:34 +00001238 if (box.top < box2.top)
1239 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001240
cristy3ed852e2009-09-05 21:47:34 +00001241 if (box.right > box2.right)
1242 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001243
cristy3ed852e2009-09-05 21:47:34 +00001244 if (box.bottom > box2.bottom)
1245 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001246
cristy3ed852e2009-09-05 21:47:34 +00001247 return box;
1248}
1249
1250static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1251{
1252 MngBox
1253 box;
1254
1255 /*
1256 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1257 */
cristybb503372010-05-27 20:51:26 +00001258 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1259 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1260 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1261 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001262 if (delta_type != 0)
1263 {
1264 box.left+=previous_box.left;
1265 box.right+=previous_box.right;
1266 box.top+=previous_box.top;
1267 box.bottom+=previous_box.bottom;
1268 }
glennrp0fe50b42010-11-16 03:52:51 +00001269
cristy3ed852e2009-09-05 21:47:34 +00001270 return(box);
1271}
1272
1273static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1274 unsigned char *p)
1275{
1276 MngPair
1277 pair;
1278 /*
cristybb503372010-05-27 20:51:26 +00001279 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001280 */
cristy8182b072010-05-30 20:10:53 +00001281 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1282 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001283
cristy3ed852e2009-09-05 21:47:34 +00001284 if (delta_type != 0)
1285 {
1286 pair.a+=previous_pair.a;
1287 pair.b+=previous_pair.b;
1288 }
glennrp0fe50b42010-11-16 03:52:51 +00001289
cristy3ed852e2009-09-05 21:47:34 +00001290 return(pair);
1291}
1292
cristy8182b072010-05-30 20:10:53 +00001293static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001294{
cristy8182b072010-05-30 20:10:53 +00001295 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001296}
1297
glennrpcf002022011-01-30 02:38:15 +00001298static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001299{
1300 Image
1301 *image;
1302
1303 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001304
cristy3ed852e2009-09-05 21:47:34 +00001305 if (image->debug != MagickFalse)
1306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1307 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001308
cristy3ed852e2009-09-05 21:47:34 +00001309 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1310 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001311
glennrpe4017e32011-01-08 17:16:09 +00001312#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001313 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1314 * are building with libpng-1.4.x and can be ignored.
1315 */
cristy3ed852e2009-09-05 21:47:34 +00001316 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001317#else
1318 png_longjmp(ping,1);
1319#endif
cristy3ed852e2009-09-05 21:47:34 +00001320}
1321
glennrpcf002022011-01-30 02:38:15 +00001322static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001323{
1324 Image
1325 *image;
1326
1327 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1328 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001329
cristy3ed852e2009-09-05 21:47:34 +00001330 image=(Image *) png_get_error_ptr(ping);
1331 if (image->debug != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001333 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001334
cristy3ed852e2009-09-05 21:47:34 +00001335 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1336 message,"`%s'",image->filename);
1337}
1338
1339#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001340static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001341{
1342#if (PNG_LIBPNG_VER < 10011)
1343 png_voidp
1344 ret;
1345
1346 png_ptr=png_ptr;
1347 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001348
cristy3ed852e2009-09-05 21:47:34 +00001349 if (ret == NULL)
1350 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001351
cristy3ed852e2009-09-05 21:47:34 +00001352 return(ret);
1353#else
1354 png_ptr=png_ptr;
1355 return((png_voidp) AcquireMagickMemory((size_t) size));
1356#endif
1357}
1358
1359/*
1360 Free a pointer. It is removed from the list at the same time.
1361*/
glennrpcf002022011-01-30 02:38:15 +00001362static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001363{
1364 png_ptr=png_ptr;
1365 ptr=RelinquishMagickMemory(ptr);
1366 return((png_free_ptr) NULL);
1367}
1368#endif
1369
1370#if defined(__cplusplus) || defined(c_plusplus)
1371}
1372#endif
1373
1374static int
glennrpcf002022011-01-30 02:38:15 +00001375Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001376 png_textp text,int ii)
1377{
cristybb503372010-05-27 20:51:26 +00001378 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001379 i;
1380
1381 register unsigned char
1382 *dp;
1383
1384 register png_charp
1385 sp;
1386
1387 png_uint_32
1388 length,
1389 nibbles;
1390
1391 StringInfo
1392 *profile;
1393
glennrp0c3e06b2010-11-19 13:45:02 +00001394 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001395 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1396 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1397 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1398 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1399 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1400 13,14,15};
1401
1402 sp=text[ii].text+1;
1403 /* look for newline */
1404 while (*sp != '\n')
1405 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001406
cristy3ed852e2009-09-05 21:47:34 +00001407 /* look for length */
1408 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1409 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001410
cristyf2f27272009-12-17 14:48:46 +00001411 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001412
glennrp97f90e22011-02-22 05:47:58 +00001413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1414 " length: %lu",(unsigned long) length);
1415
cristy3ed852e2009-09-05 21:47:34 +00001416 while (*sp != ' ' && *sp != '\n')
1417 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001418
cristy3ed852e2009-09-05 21:47:34 +00001419 /* allocate space */
1420 if (length == 0)
1421 {
1422 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1423 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1424 return(MagickFalse);
1425 }
glennrp0fe50b42010-11-16 03:52:51 +00001426
cristy3ed852e2009-09-05 21:47:34 +00001427 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001428
cristy3ed852e2009-09-05 21:47:34 +00001429 if (profile == (StringInfo *) NULL)
1430 {
1431 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1432 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1433 "unable to copy profile");
1434 return(MagickFalse);
1435 }
glennrp0fe50b42010-11-16 03:52:51 +00001436
cristy3ed852e2009-09-05 21:47:34 +00001437 /* copy profile, skipping white space and column 1 "=" signs */
1438 dp=GetStringInfoDatum(profile);
1439 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001440
cristybb503372010-05-27 20:51:26 +00001441 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001442 {
1443 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1444 {
1445 if (*sp == '\0')
1446 {
1447 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1448 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1449 profile=DestroyStringInfo(profile);
1450 return(MagickFalse);
1451 }
1452 sp++;
1453 }
glennrp0fe50b42010-11-16 03:52:51 +00001454
cristy3ed852e2009-09-05 21:47:34 +00001455 if (i%2 == 0)
1456 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001457
cristy3ed852e2009-09-05 21:47:34 +00001458 else
1459 (*dp++)+=unhex[(int) *sp++];
1460 }
1461 /*
1462 We have already read "Raw profile type.
1463 */
1464 (void) SetImageProfile(image,&text[ii].key[17],profile);
1465 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001466
cristy3ed852e2009-09-05 21:47:34 +00001467 if (image_info->verbose)
1468 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001469
cristy3ed852e2009-09-05 21:47:34 +00001470 return MagickTrue;
1471}
1472
1473#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1474static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1475{
1476 Image
1477 *image;
1478
1479
1480 /* The unknown chunk structure contains the chunk data:
1481 png_byte name[5];
1482 png_byte *data;
1483 png_size_t size;
1484
1485 Note that libpng has already taken care of the CRC handling.
1486 */
1487
1488
1489 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1490 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1491 return(0); /* Did not recognize */
1492
1493 /* recognized vpAg */
1494
1495 if (chunk->size != 9)
1496 return(-1); /* Error return */
1497
1498 if (chunk->data[8] != 0)
1499 return(0); /* ImageMagick requires pixel units */
1500
1501 image=(Image *) png_get_user_chunk_ptr(ping);
1502
cristybb503372010-05-27 20:51:26 +00001503 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001504 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001505
cristybb503372010-05-27 20:51:26 +00001506 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001507 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1508
1509 /* Return one of the following: */
1510 /* return(-n); chunk had an error */
1511 /* return(0); did not recognize */
1512 /* return(n); success */
1513
1514 return(1);
1515
1516}
1517#endif
1518
1519/*
1520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521% %
1522% %
1523% %
1524% R e a d O n e P N G I m a g e %
1525% %
1526% %
1527% %
1528%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1529%
1530% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1531% (minus the 8-byte signature) and returns it. It allocates the memory
1532% necessary for the new Image structure and returns a pointer to the new
1533% image.
1534%
1535% The format of the ReadOnePNGImage method is:
1536%
1537% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1538% ExceptionInfo *exception)
1539%
1540% A description of each parameter follows:
1541%
1542% o mng_info: Specifies a pointer to a MngInfo structure.
1543%
1544% o image_info: the image info.
1545%
1546% o exception: return any errors or warnings in this structure.
1547%
1548*/
1549static Image *ReadOnePNGImage(MngInfo *mng_info,
1550 const ImageInfo *image_info, ExceptionInfo *exception)
1551{
1552 /* Read one PNG image */
1553
glennrpcc95c3f2011-04-18 16:46:48 +00001554 /* To do: Read the tIME chunk into the date:modify property */
1555 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1556
cristy3ed852e2009-09-05 21:47:34 +00001557 Image
1558 *image;
1559
1560 int
glennrp4eb39312011-03-30 21:34:55 +00001561 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001562 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001563 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001564 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001565 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001566 pass,
1567 ping_bit_depth,
1568 ping_color_type,
1569 ping_interlace_method,
1570 ping_compression_method,
1571 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001572 ping_num_trans,
1573 unit_type;
1574
1575 double
1576 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001577
glennrpa6a06632011-01-19 15:15:34 +00001578 LongPixelPacket
1579 transparent_color;
1580
cristy3ed852e2009-09-05 21:47:34 +00001581 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00001582 logging,
cristy3ed852e2009-09-05 21:47:34 +00001583 status;
1584
glennrpfaa852b2010-03-30 12:17:00 +00001585 png_bytep
1586 ping_trans_alpha;
1587
1588 png_color_16p
1589 ping_background,
1590 ping_trans_color;
1591
cristy3ed852e2009-09-05 21:47:34 +00001592 png_info
1593 *end_info,
1594 *ping_info;
1595
1596 png_struct
1597 *ping;
1598
1599 png_textp
1600 text;
1601
glennrpfaa852b2010-03-30 12:17:00 +00001602 png_uint_32
1603 ping_height,
1604 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00001605 ping_rowbytes,
1606 x_resolution,
1607 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00001608
cristy3ed852e2009-09-05 21:47:34 +00001609 QuantumInfo
1610 *quantum_info;
1611
1612 unsigned char
glennrpcf002022011-01-30 02:38:15 +00001613 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001614
cristybb503372010-05-27 20:51:26 +00001615 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001616 y;
1617
1618 register unsigned char
1619 *p;
1620
1621 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00001622 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00001623
cristybb503372010-05-27 20:51:26 +00001624 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001625 i,
1626 x;
1627
1628 register PixelPacket
1629 *q;
1630
1631 size_t
glennrp39992b42010-11-14 00:03:43 +00001632 length,
cristy3ed852e2009-09-05 21:47:34 +00001633 row_offset;
1634
cristyeb3b22a2011-03-31 20:16:11 +00001635 ssize_t
1636 j;
1637
cristy3ed852e2009-09-05 21:47:34 +00001638#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1639 png_byte unused_chunks[]=
1640 {
1641 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1642 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1643 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1644 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1645 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1646 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1647 };
1648#endif
1649
1650 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001651 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00001652
1653#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001654 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001655#endif
1656
glennrp25c1e2b2010-03-25 01:39:56 +00001657#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00001658 if (image_info->verbose)
1659 printf("Your PNG library (libpng-%s) is rather old.\n",
1660 PNG_LIBPNG_VER_STRING);
1661#endif
1662
glennrp61b4c952009-11-10 20:40:41 +00001663#if (PNG_LIBPNG_VER >= 10400)
1664# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1665 if (image_info->verbose)
1666 {
1667 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1668 PNG_LIBPNG_VER_STRING);
1669 printf("Please update it.\n");
1670 }
1671# endif
1672#endif
1673
1674
cristyed552522009-10-16 14:04:35 +00001675 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00001676 image=mng_info->image;
1677
glennrpa6a06632011-01-19 15:15:34 +00001678 if (logging != MagickFalse)
1679 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1680 " image->matte=%d",(int) image->matte);
1681
glennrp0e319732011-01-25 21:53:13 +00001682 /* Set to an out-of-range color unless tRNS chunk is present */
1683 transparent_color.red=65537;
1684 transparent_color.green=65537;
1685 transparent_color.blue=65537;
1686 transparent_color.opacity=65537;
1687
glennrpcb395ac2011-03-30 19:50:23 +00001688 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00001689 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00001690 num_raw_profiles = 0;
1691
cristy3ed852e2009-09-05 21:47:34 +00001692 /*
1693 Allocate the PNG structures
1694 */
1695#ifdef PNG_USER_MEM_SUPPORTED
1696 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00001697 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1698 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00001699#else
1700 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00001701 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00001702#endif
1703 if (ping == (png_struct *) NULL)
1704 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00001705
cristy3ed852e2009-09-05 21:47:34 +00001706 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001707
cristy3ed852e2009-09-05 21:47:34 +00001708 if (ping_info == (png_info *) NULL)
1709 {
1710 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1711 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1712 }
glennrp0fe50b42010-11-16 03:52:51 +00001713
cristy3ed852e2009-09-05 21:47:34 +00001714 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001715
cristy3ed852e2009-09-05 21:47:34 +00001716 if (end_info == (png_info *) NULL)
1717 {
1718 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1719 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1720 }
glennrp0fe50b42010-11-16 03:52:51 +00001721
glennrpcf002022011-01-30 02:38:15 +00001722 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00001723
glennrpfaa852b2010-03-30 12:17:00 +00001724 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00001725 {
1726 /*
1727 PNG image is corrupt.
1728 */
1729 png_destroy_read_struct(&ping,&ping_info,&end_info);
1730#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001731 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001732#endif
1733 if (logging != MagickFalse)
1734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1735 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00001736
cristy3ed852e2009-09-05 21:47:34 +00001737 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00001738 {
1739 InheritException(exception,&image->exception);
1740 image->columns=0;
1741 }
glennrp0fe50b42010-11-16 03:52:51 +00001742
cristy3ed852e2009-09-05 21:47:34 +00001743 return(GetFirstImageInList(image));
1744 }
1745 /*
1746 Prepare PNG for reading.
1747 */
glennrpfaa852b2010-03-30 12:17:00 +00001748
cristy3ed852e2009-09-05 21:47:34 +00001749 mng_info->image_found++;
1750 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00001751
cristy3ed852e2009-09-05 21:47:34 +00001752 if (LocaleCompare(image_info->magick,"MNG") == 0)
1753 {
1754#if defined(PNG_MNG_FEATURES_SUPPORTED)
1755 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1756 png_set_read_fn(ping,image,png_get_data);
1757#else
1758#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1759 png_permit_empty_plte(ping,MagickTrue);
1760 png_set_read_fn(ping,image,png_get_data);
1761#else
1762 mng_info->image=image;
1763 mng_info->bytes_in_read_buffer=0;
1764 mng_info->found_empty_plte=MagickFalse;
1765 mng_info->have_saved_bkgd_index=MagickFalse;
1766 png_set_read_fn(ping,mng_info,mng_get_data);
1767#endif
1768#endif
1769 }
glennrp0fe50b42010-11-16 03:52:51 +00001770
cristy3ed852e2009-09-05 21:47:34 +00001771 else
1772 png_set_read_fn(ping,image,png_get_data);
1773
1774#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1775 /* Ignore unused chunks and all unknown chunks except for vpAg */
1776 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1777 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1778 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1779 (int)sizeof(unused_chunks)/5);
1780 /* Callback for other unknown chunks */
1781 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1782#endif
1783
glennrp991e92a2010-01-28 03:09:00 +00001784#if (PNG_LIBPNG_VER < 10400)
1785# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1786 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00001787 /* Disable thread-unsafe features of pnggccrd */
1788 if (png_access_version_number() >= 10200)
1789 {
1790 png_uint_32 mmx_disable_mask=0;
1791 png_uint_32 asm_flags;
1792
1793 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1794 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1795 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1796 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1797 asm_flags=png_get_asm_flags(ping);
1798 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1799 }
glennrp991e92a2010-01-28 03:09:00 +00001800# endif
cristy3ed852e2009-09-05 21:47:34 +00001801#endif
1802
1803 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00001804
1805 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1806 &ping_bit_depth,&ping_color_type,
1807 &ping_interlace_method,&ping_compression_method,
1808 &ping_filter_method);
1809
1810 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1811 &ping_trans_color);
1812
1813 (void) png_get_bKGD(ping, ping_info, &ping_background);
1814
1815 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00001816 {
glennrpfaa852b2010-03-30 12:17:00 +00001817 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1818 {
1819 png_set_packing(ping);
1820 ping_bit_depth = 8;
1821 }
cristy3ed852e2009-09-05 21:47:34 +00001822 }
glennrpfaa852b2010-03-30 12:17:00 +00001823
1824 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00001825 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00001826 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00001827 if (logging != MagickFalse)
1828 {
1829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001830 " PNG width: %.20g, height: %.20g",
1831 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00001832
cristy3ed852e2009-09-05 21:47:34 +00001833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1834 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001835 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00001836
cristy3ed852e2009-09-05 21:47:34 +00001837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1838 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001839 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00001840
cristy3ed852e2009-09-05 21:47:34 +00001841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1842 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001843 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00001844 }
1845
glennrpfaa852b2010-03-30 12:17:00 +00001846#ifdef PNG_READ_iCCP_SUPPORTED
1847 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00001848 {
1849 int
1850 compression;
1851
glennrpe4017e32011-01-08 17:16:09 +00001852#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00001853 png_charp
glennrpe4017e32011-01-08 17:16:09 +00001854 info;
1855#else
1856 png_bytep
1857 info;
1858#endif
1859
1860 png_charp
cristy3ed852e2009-09-05 21:47:34 +00001861 name;
1862
1863 png_uint_32
1864 profile_length;
1865
1866 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1867 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00001868
cristy3ed852e2009-09-05 21:47:34 +00001869 if (profile_length != 0)
1870 {
1871 StringInfo
1872 *profile;
1873
1874 if (logging != MagickFalse)
1875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1876 " Reading PNG iCCP chunk.");
1877 profile=AcquireStringInfo(profile_length);
1878 SetStringInfoDatum(profile,(const unsigned char *) info);
1879 (void) SetImageProfile(image,"icc",profile);
1880 profile=DestroyStringInfo(profile);
1881 }
1882 }
1883#endif
1884#if defined(PNG_READ_sRGB_SUPPORTED)
1885 {
cristy3ed852e2009-09-05 21:47:34 +00001886 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00001887 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1888 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00001889
cristy3ed852e2009-09-05 21:47:34 +00001890 if (png_get_sRGB(ping,ping_info,&intent))
1891 {
glennrpcf002022011-01-30 02:38:15 +00001892 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1893 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00001894
cristy3ed852e2009-09-05 21:47:34 +00001895 if (logging != MagickFalse)
1896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00001897 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00001898 }
1899 }
1900#endif
1901 {
glennrpfaa852b2010-03-30 12:17:00 +00001902 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1903 if (mng_info->have_global_gama)
1904 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00001905
cristy3ed852e2009-09-05 21:47:34 +00001906 if (png_get_gAMA(ping,ping_info,&file_gamma))
1907 {
1908 image->gamma=(float) file_gamma;
1909 if (logging != MagickFalse)
1910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1911 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1912 }
1913 }
glennrpfaa852b2010-03-30 12:17:00 +00001914 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1915 {
1916 if (mng_info->have_global_chrm != MagickFalse)
1917 {
1918 (void) png_set_cHRM(ping,ping_info,
1919 mng_info->global_chrm.white_point.x,
1920 mng_info->global_chrm.white_point.y,
1921 mng_info->global_chrm.red_primary.x,
1922 mng_info->global_chrm.red_primary.y,
1923 mng_info->global_chrm.green_primary.x,
1924 mng_info->global_chrm.green_primary.y,
1925 mng_info->global_chrm.blue_primary.x,
1926 mng_info->global_chrm.blue_primary.y);
1927 }
1928 }
glennrp0fe50b42010-11-16 03:52:51 +00001929
glennrpfaa852b2010-03-30 12:17:00 +00001930 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00001931 {
1932 (void) png_get_cHRM(ping,ping_info,
1933 &image->chromaticity.white_point.x,
1934 &image->chromaticity.white_point.y,
1935 &image->chromaticity.red_primary.x,
1936 &image->chromaticity.red_primary.y,
1937 &image->chromaticity.green_primary.x,
1938 &image->chromaticity.green_primary.y,
1939 &image->chromaticity.blue_primary.x,
1940 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00001941
cristy3ed852e2009-09-05 21:47:34 +00001942 if (logging != MagickFalse)
1943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1944 " Reading PNG cHRM chunk.");
1945 }
glennrp0fe50b42010-11-16 03:52:51 +00001946
glennrpe610a072010-08-05 17:08:46 +00001947 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00001948 {
glennrpe610a072010-08-05 17:08:46 +00001949 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00001950 Magick_RenderingIntent_to_PNG_RenderingIntent
1951 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00001952 png_set_gAMA(ping,ping_info,0.45455f);
1953 png_set_cHRM(ping,ping_info,
1954 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1955 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00001956 }
cristy3ed852e2009-09-05 21:47:34 +00001957#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001958 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00001959 {
cristy905ef802011-02-23 00:29:18 +00001960 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1961 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00001962
cristy3ed852e2009-09-05 21:47:34 +00001963 if (logging != MagickFalse)
1964 if (image->page.x || image->page.y)
1965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001966 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1967 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001968 }
1969#endif
1970#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001971 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1972 {
1973 if (mng_info->have_global_phys)
1974 {
1975 png_set_pHYs(ping,ping_info,
1976 mng_info->global_x_pixels_per_unit,
1977 mng_info->global_y_pixels_per_unit,
1978 mng_info->global_phys_unit_type);
1979 }
1980 }
1981
1982 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00001983 {
cristy3ed852e2009-09-05 21:47:34 +00001984 /*
1985 Set image resolution.
1986 */
1987 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00001988 &unit_type);
1989 image->x_resolution=(double) x_resolution;
1990 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00001991
cristy3ed852e2009-09-05 21:47:34 +00001992 if (unit_type == PNG_RESOLUTION_METER)
1993 {
1994 image->units=PixelsPerCentimeterResolution;
1995 image->x_resolution=(double) x_resolution/100.0;
1996 image->y_resolution=(double) y_resolution/100.0;
1997 }
glennrp0fe50b42010-11-16 03:52:51 +00001998
cristy3ed852e2009-09-05 21:47:34 +00001999 if (logging != MagickFalse)
2000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002001 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2002 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002003 }
cristy3ed852e2009-09-05 21:47:34 +00002004#endif
glennrp823b55c2011-03-14 18:46:46 +00002005
glennrpfaa852b2010-03-30 12:17:00 +00002006 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002007 {
2008 int
2009 number_colors;
2010
2011 png_colorp
2012 palette;
2013
2014 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002015
cristy3ed852e2009-09-05 21:47:34 +00002016 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002017 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002018 {
2019 if (mng_info->global_plte_length)
2020 {
2021 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2022 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002023
glennrpfaa852b2010-03-30 12:17:00 +00002024 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002025 if (mng_info->global_trns_length)
2026 {
2027 if (mng_info->global_trns_length >
2028 mng_info->global_plte_length)
2029 (void) ThrowMagickException(&image->exception,
2030 GetMagickModule(),CoderError,
2031 "global tRNS has more entries than global PLTE",
2032 "`%s'",image_info->filename);
2033 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2034 (int) mng_info->global_trns_length,NULL);
2035 }
glennrpbfd9e612011-04-22 14:02:20 +00002036#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002037 if (
2038#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2039 mng_info->have_saved_bkgd_index ||
2040#endif
glennrpfaa852b2010-03-30 12:17:00 +00002041 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002042 {
2043 png_color_16
2044 background;
2045
2046#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2047 if (mng_info->have_saved_bkgd_index)
2048 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002049#endif
glennrpfaa852b2010-03-30 12:17:00 +00002050 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2051 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002052
cristy3ed852e2009-09-05 21:47:34 +00002053 background.red=(png_uint_16)
2054 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002055
cristy3ed852e2009-09-05 21:47:34 +00002056 background.green=(png_uint_16)
2057 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002058
cristy3ed852e2009-09-05 21:47:34 +00002059 background.blue=(png_uint_16)
2060 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002061
glennrpc6c391a2011-04-27 02:23:56 +00002062 background.gray=(png_uint_16)
2063 mng_info->global_plte[background.index].green;
2064
cristy3ed852e2009-09-05 21:47:34 +00002065 png_set_bKGD(ping,ping_info,&background);
2066 }
2067#endif
2068 }
2069 else
2070 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2071 CoderError,"No global PLTE in file","`%s'",
2072 image_info->filename);
2073 }
2074 }
2075
glennrpbfd9e612011-04-22 14:02:20 +00002076#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002077 if (mng_info->have_global_bkgd &&
2078 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002079 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002080
glennrpfaa852b2010-03-30 12:17:00 +00002081 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002082 {
glennrpbfd9e612011-04-22 14:02:20 +00002083 unsigned int
2084 bkgd_scale;
2085
cristy3ed852e2009-09-05 21:47:34 +00002086 /*
2087 Set image background color.
2088 */
2089 if (logging != MagickFalse)
2090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2091 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002092
glennrpbfd9e612011-04-22 14:02:20 +00002093 /* Scale background components to 16-bit, then scale
2094 * to quantum depth
2095 */
2096 if (logging != MagickFalse)
2097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2098 " raw ping_background=(%d,%d,%d).",ping_background->red,
2099 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002100
glennrpbfd9e612011-04-22 14:02:20 +00002101 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002102
glennrpbfd9e612011-04-22 14:02:20 +00002103 if (ping_bit_depth == 1)
2104 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002105
glennrpbfd9e612011-04-22 14:02:20 +00002106 else if (ping_bit_depth == 2)
2107 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002108
glennrpbfd9e612011-04-22 14:02:20 +00002109 else if (ping_bit_depth == 4)
2110 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002111
glennrpbfd9e612011-04-22 14:02:20 +00002112 if (ping_bit_depth <= 8)
2113 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002114
glennrpbfd9e612011-04-22 14:02:20 +00002115 ping_background->red *= bkgd_scale;
2116 ping_background->green *= bkgd_scale;
2117 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002118
glennrpbfd9e612011-04-22 14:02:20 +00002119 if (logging != MagickFalse)
2120 {
glennrp2cbb4482010-06-02 04:37:24 +00002121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2122 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002123
glennrp2cbb4482010-06-02 04:37:24 +00002124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2125 " ping_background=(%d,%d,%d).",ping_background->red,
2126 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002127 }
glennrp2cbb4482010-06-02 04:37:24 +00002128
glennrpbfd9e612011-04-22 14:02:20 +00002129 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002130 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002131
glennrpbfd9e612011-04-22 14:02:20 +00002132 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002133 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002134
glennrpbfd9e612011-04-22 14:02:20 +00002135 image->background_color.blue=
2136 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002137
glennrpbfd9e612011-04-22 14:02:20 +00002138 image->background_color.opacity=OpaqueOpacity;
glennrp2cbb4482010-06-02 04:37:24 +00002139
glennrpbfd9e612011-04-22 14:02:20 +00002140 if (logging != MagickFalse)
2141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2142 " image->background_color=(%.20g,%.20g,%.20g).",
2143 (double) image->background_color.red,
2144 (double) image->background_color.green,
2145 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002146 }
glennrpbfd9e612011-04-22 14:02:20 +00002147#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002148
glennrpfaa852b2010-03-30 12:17:00 +00002149 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002150 {
2151 /*
glennrpa6a06632011-01-19 15:15:34 +00002152 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002153 */
2154 int
2155 max_sample;
2156
cristy35ef8242010-06-03 16:24:13 +00002157 size_t
2158 one=1;
2159
cristy3ed852e2009-09-05 21:47:34 +00002160 if (logging != MagickFalse)
2161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2162 " Reading PNG tRNS chunk.");
2163
cristyf9cca6a2010-06-04 23:49:28 +00002164 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002165
glennrpfaa852b2010-03-30 12:17:00 +00002166 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2167 (int)ping_trans_color->gray > max_sample) ||
2168 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2169 ((int)ping_trans_color->red > max_sample ||
2170 (int)ping_trans_color->green > max_sample ||
2171 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002172 {
2173 if (logging != MagickFalse)
2174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2175 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002176 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002177 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002178 image->matte=MagickFalse;
2179 }
2180 else
2181 {
glennrpa6a06632011-01-19 15:15:34 +00002182 int
2183 scale_to_short;
2184
2185 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2186
2187 /* Scale transparent_color to short */
2188 transparent_color.red= scale_to_short*ping_trans_color->red;
2189 transparent_color.green= scale_to_short*ping_trans_color->green;
2190 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2191 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002192
glennrpfaa852b2010-03-30 12:17:00 +00002193 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002194 {
glennrp0f111982010-07-07 20:18:33 +00002195 if (logging != MagickFalse)
2196 {
2197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2198 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002199
glennrp0f111982010-07-07 20:18:33 +00002200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2201 " scaled graylevel is %d.",transparent_color.opacity);
2202 }
cristy3ed852e2009-09-05 21:47:34 +00002203 transparent_color.red=transparent_color.opacity;
2204 transparent_color.green=transparent_color.opacity;
2205 transparent_color.blue=transparent_color.opacity;
2206 }
2207 }
2208 }
2209#if defined(PNG_READ_sBIT_SUPPORTED)
2210 if (mng_info->have_global_sbit)
2211 {
glennrpfaa852b2010-03-30 12:17:00 +00002212 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002213 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2214 }
2215#endif
2216 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002217
cristy3ed852e2009-09-05 21:47:34 +00002218 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002219
2220 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2221
cristy3ed852e2009-09-05 21:47:34 +00002222 /*
2223 Initialize image structure.
2224 */
2225 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002226 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002227 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002228 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002229 if (mng_info->mng_type == 0)
2230 {
glennrpfaa852b2010-03-30 12:17:00 +00002231 mng_info->mng_width=ping_width;
2232 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002233 mng_info->frame=mng_info->image_box;
2234 mng_info->clip=mng_info->image_box;
2235 }
glennrp0fe50b42010-11-16 03:52:51 +00002236
cristy3ed852e2009-09-05 21:47:34 +00002237 else
2238 {
2239 image->page.y=mng_info->y_off[mng_info->object_id];
2240 }
glennrp0fe50b42010-11-16 03:52:51 +00002241
cristy3ed852e2009-09-05 21:47:34 +00002242 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002243 image->columns=ping_width;
2244 image->rows=ping_height;
2245 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002246 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002247 {
cristybefe4d22010-06-07 01:18:58 +00002248 size_t
2249 one;
2250
cristy3ed852e2009-09-05 21:47:34 +00002251 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002252 one=1;
2253 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002254#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2255 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002256 image->colors=256;
2257#else
2258 if (image->colors > 65536L)
2259 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002260#endif
glennrpfaa852b2010-03-30 12:17:00 +00002261 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002262 {
2263 int
2264 number_colors;
2265
2266 png_colorp
2267 palette;
2268
2269 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002270 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002271
cristy3ed852e2009-09-05 21:47:34 +00002272 if (logging != MagickFalse)
2273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2274 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2275 }
2276 }
2277
2278 if (image->storage_class == PseudoClass)
2279 {
2280 /*
2281 Initialize image colormap.
2282 */
2283 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2284 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002285
glennrpfaa852b2010-03-30 12:17:00 +00002286 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002287 {
2288 int
2289 number_colors;
2290
2291 png_colorp
2292 palette;
2293
2294 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002295
glennrp6af6cf12011-04-22 13:05:16 +00002296 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002297 {
2298 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2299 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2300 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2301 }
glennrp6af6cf12011-04-22 13:05:16 +00002302
glennrp67b9c1a2011-04-22 18:47:36 +00002303 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002304 {
2305 image->colormap[i].red=0;
2306 image->colormap[i].green=0;
2307 image->colormap[i].blue=0;
2308 }
cristy3ed852e2009-09-05 21:47:34 +00002309 }
glennrp0fe50b42010-11-16 03:52:51 +00002310
cristy3ed852e2009-09-05 21:47:34 +00002311 else
2312 {
cristybb503372010-05-27 20:51:26 +00002313 size_t
cristy3ed852e2009-09-05 21:47:34 +00002314 scale;
2315
glennrpfaa852b2010-03-30 12:17:00 +00002316 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002317
cristy3ed852e2009-09-05 21:47:34 +00002318 if (scale < 1)
2319 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002320
cristybb503372010-05-27 20:51:26 +00002321 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002322 {
2323 image->colormap[i].red=(Quantum) (i*scale);
2324 image->colormap[i].green=(Quantum) (i*scale);
2325 image->colormap[i].blue=(Quantum) (i*scale);
2326 }
2327 }
2328 }
glennrp147bc912011-03-30 18:47:21 +00002329
glennrpcb395ac2011-03-30 19:50:23 +00002330 /* Set some properties for reporting by "identify" */
2331 {
glennrp147bc912011-03-30 18:47:21 +00002332 char
2333 msg[MaxTextExtent];
2334
2335 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2336 ping_interlace_method in value */
2337
glennrp7cdb11c2011-03-31 18:17:25 +00002338 (void) FormatMagickString(msg,MaxTextExtent,
2339 "%d, %d",(int) ping_width, (int) ping_height);
2340 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002341
2342 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2343 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2344
2345 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2346 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2347
2348 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2349 (int) ping_interlace_method);
2350 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002351 }
glennrp147bc912011-03-30 18:47:21 +00002352
cristy3ed852e2009-09-05 21:47:34 +00002353 /*
2354 Read image scanlines.
2355 */
2356 if (image->delay != 0)
2357 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002358
glennrp0ca69b12010-07-26 01:57:52 +00002359 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002360 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2361 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002362 {
2363 if (logging != MagickFalse)
2364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002365 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002366 mng_info->scenes_found-1);
2367 png_destroy_read_struct(&ping,&ping_info,&end_info);
2368#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002369 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002370#endif
2371 if (logging != MagickFalse)
2372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2373 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002374
cristy3ed852e2009-09-05 21:47:34 +00002375 return(image);
2376 }
glennrp0fe50b42010-11-16 03:52:51 +00002377
cristy3ed852e2009-09-05 21:47:34 +00002378 if (logging != MagickFalse)
2379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2380 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002381
cristy3ed852e2009-09-05 21:47:34 +00002382 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002383 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2384 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002385
cristy3ed852e2009-09-05 21:47:34 +00002386 else
glennrpcf002022011-01-30 02:38:15 +00002387 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2388 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002389
glennrpcf002022011-01-30 02:38:15 +00002390 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002391 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002392
cristy3ed852e2009-09-05 21:47:34 +00002393 if (logging != MagickFalse)
2394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2395 " Converting PNG pixels to pixel packets");
2396 /*
2397 Convert PNG pixels to pixel packets.
2398 */
glennrpfaa852b2010-03-30 12:17:00 +00002399 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002400 {
2401 /*
2402 PNG image is corrupt.
2403 */
2404 png_destroy_read_struct(&ping,&ping_info,&end_info);
2405#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002406 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002407#endif
2408 if (quantum_info != (QuantumInfo *) NULL)
2409 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002410
glennrpcf002022011-01-30 02:38:15 +00002411 if (ping_pixels != (unsigned char *) NULL)
2412 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002413
cristy3ed852e2009-09-05 21:47:34 +00002414 if (logging != MagickFalse)
2415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2416 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002417
cristy3ed852e2009-09-05 21:47:34 +00002418 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002419 {
2420 InheritException(exception,&image->exception);
2421 image->columns=0;
2422 }
glennrp0fe50b42010-11-16 03:52:51 +00002423
cristy3ed852e2009-09-05 21:47:34 +00002424 return(GetFirstImageInList(image));
2425 }
glennrp0fe50b42010-11-16 03:52:51 +00002426
cristyed552522009-10-16 14:04:35 +00002427 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002428
cristyed552522009-10-16 14:04:35 +00002429 if (quantum_info == (QuantumInfo *) NULL)
2430 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002431
glennrpc8cbc5d2011-01-01 00:12:34 +00002432 {
2433
2434 MagickBooleanType
2435 found_transparent_pixel;
2436
2437 found_transparent_pixel=MagickFalse;
2438
cristy3ed852e2009-09-05 21:47:34 +00002439 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002440 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002441 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002442 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002443 /*
2444 Convert image to DirectClass pixel packets.
2445 */
glennrp67b9c1a2011-04-22 18:47:36 +00002446#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2447 int
2448 depth;
2449
2450 depth=(ssize_t) ping_bit_depth;
2451#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002452 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2453 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2454 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2455 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002456
glennrpc8cbc5d2011-01-01 00:12:34 +00002457 for (y=0; y < (ssize_t) image->rows; y++)
2458 {
2459 if (num_passes > 1)
2460 row_offset=ping_rowbytes*y;
2461
2462 else
2463 row_offset=0;
2464
glennrpcf002022011-01-30 02:38:15 +00002465 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002466 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2467
2468 if (q == (PixelPacket *) NULL)
2469 break;
2470
glennrpc8cbc5d2011-01-01 00:12:34 +00002471 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2472 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002473 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002474
2475 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2476 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002477 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002478
2479 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2480 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002481 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002482
2483 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2484 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002485 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002486
2487 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2488 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002489 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002490
glennrpc8cbc5d2011-01-01 00:12:34 +00002491 if (found_transparent_pixel == MagickFalse)
2492 {
2493 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002494 if (y== 0 && logging != MagickFalse)
2495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2496 " Looking for cheap transparent pixel");
2497
glennrpc8cbc5d2011-01-01 00:12:34 +00002498 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2499 {
glennrp5aa37f62011-01-02 03:07:57 +00002500 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2501 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
glennrp8b698592011-04-26 03:38:21 +00002502 (GetOpacityPixelComponent(q) != OpaqueOpacity))
glennrpc8cbc5d2011-01-01 00:12:34 +00002503 {
glennrpa6a06632011-01-19 15:15:34 +00002504 if (logging != MagickFalse)
2505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2506 " ...got one.");
2507
glennrpc8cbc5d2011-01-01 00:12:34 +00002508 found_transparent_pixel = MagickTrue;
2509 break;
2510 }
glennrp4f25bd02011-01-01 18:51:28 +00002511 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2512 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp8b698592011-04-26 03:38:21 +00002513 (ScaleQuantumToShort(GetRedPixelComponent(q))
2514 == transparent_color.red &&
2515 ScaleQuantumToShort(GetGreenPixelComponent(q))
2516 == transparent_color.green &&
2517 ScaleQuantumToShort(GetBluePixelComponent(q))
2518 == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002519 {
glennrpa6a06632011-01-19 15:15:34 +00002520 if (logging != MagickFalse)
2521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2522 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002523 found_transparent_pixel = MagickTrue;
2524 break;
2525 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002526 q++;
2527 }
2528 }
2529
2530 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2531 {
2532 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2533 image->rows);
2534
2535 if (status == MagickFalse)
2536 break;
2537 }
2538 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2539 break;
2540 }
2541
2542 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2543 {
2544 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002545 if (status == MagickFalse)
2546 break;
2547 }
cristy3ed852e2009-09-05 21:47:34 +00002548 }
cristy3ed852e2009-09-05 21:47:34 +00002549 }
glennrp0fe50b42010-11-16 03:52:51 +00002550
cristy3ed852e2009-09-05 21:47:34 +00002551 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002552
cristy3ed852e2009-09-05 21:47:34 +00002553 for (pass=0; pass < num_passes; pass++)
2554 {
2555 Quantum
2556 *quantum_scanline;
2557
2558 register Quantum
2559 *r;
2560
2561 /*
2562 Convert grayscale image to PseudoClass pixel packets.
2563 */
glennrpfaa852b2010-03-30 12:17:00 +00002564 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002565 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002566
cristy3ed852e2009-09-05 21:47:34 +00002567 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2568 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002569
cristy3ed852e2009-09-05 21:47:34 +00002570 if (quantum_scanline == (Quantum *) NULL)
2571 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002572
cristybb503372010-05-27 20:51:26 +00002573 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002574 {
2575 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002576 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002577
cristy3ed852e2009-09-05 21:47:34 +00002578 else
2579 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00002580
glennrpcf002022011-01-30 02:38:15 +00002581 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002582 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00002583
cristy3ed852e2009-09-05 21:47:34 +00002584 if (q == (PixelPacket *) NULL)
2585 break;
glennrp0fe50b42010-11-16 03:52:51 +00002586
cristy5c6f7892010-05-05 22:53:29 +00002587 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00002588 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00002589 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00002590
glennrpfaa852b2010-03-30 12:17:00 +00002591 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00002592 {
2593 case 1:
2594 {
cristybb503372010-05-27 20:51:26 +00002595 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002596 bit;
2597
cristybb503372010-05-27 20:51:26 +00002598 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00002599 {
2600 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002601 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002602 p++;
2603 }
glennrp0fe50b42010-11-16 03:52:51 +00002604
cristy3ed852e2009-09-05 21:47:34 +00002605 if ((image->columns % 8) != 0)
2606 {
cristybb503372010-05-27 20:51:26 +00002607 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002608 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002609 }
glennrp0fe50b42010-11-16 03:52:51 +00002610
cristy3ed852e2009-09-05 21:47:34 +00002611 break;
2612 }
glennrp47b9dd52010-11-24 18:12:06 +00002613
cristy3ed852e2009-09-05 21:47:34 +00002614 case 2:
2615 {
cristybb503372010-05-27 20:51:26 +00002616 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002617 {
glennrpa18d5bc2011-04-23 14:51:34 +00002618 *r++=(*p >> 6) & 0x03;
2619 *r++=(*p >> 4) & 0x03;
2620 *r++=(*p >> 2) & 0x03;
2621 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00002622 }
glennrp0fe50b42010-11-16 03:52:51 +00002623
cristy3ed852e2009-09-05 21:47:34 +00002624 if ((image->columns % 4) != 0)
2625 {
cristybb503372010-05-27 20:51:26 +00002626 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00002627 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00002628 }
glennrp0fe50b42010-11-16 03:52:51 +00002629
cristy3ed852e2009-09-05 21:47:34 +00002630 break;
2631 }
glennrp47b9dd52010-11-24 18:12:06 +00002632
cristy3ed852e2009-09-05 21:47:34 +00002633 case 4:
2634 {
cristybb503372010-05-27 20:51:26 +00002635 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00002636 {
glennrpa18d5bc2011-04-23 14:51:34 +00002637 *r++=(*p >> 4) & 0x0f;
2638 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00002639 }
glennrp0fe50b42010-11-16 03:52:51 +00002640
cristy3ed852e2009-09-05 21:47:34 +00002641 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00002642 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00002643
cristy3ed852e2009-09-05 21:47:34 +00002644 break;
2645 }
glennrp47b9dd52010-11-24 18:12:06 +00002646
cristy3ed852e2009-09-05 21:47:34 +00002647 case 8:
2648 {
glennrpfaa852b2010-03-30 12:17:00 +00002649 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00002650 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002651 {
glennrpa18d5bc2011-04-23 14:51:34 +00002652 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00002653 /* In image.h, OpaqueOpacity is 0
2654 * TransparentOpacity is QuantumRange
2655 * In a PNG datastream, Opaque is QuantumRange
2656 * and Transparent is 0.
2657 */
glennrp8b698592011-04-26 03:38:21 +00002658 SetOpacityPixelComponent(q,
2659 ScaleCharToQuantum((unsigned char) (255-(*p++))));
2660 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00002661 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002662 q++;
2663 }
glennrp0fe50b42010-11-16 03:52:51 +00002664
cristy3ed852e2009-09-05 21:47:34 +00002665 else
cristybb503372010-05-27 20:51:26 +00002666 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002667 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00002668
cristy3ed852e2009-09-05 21:47:34 +00002669 break;
2670 }
glennrp47b9dd52010-11-24 18:12:06 +00002671
cristy3ed852e2009-09-05 21:47:34 +00002672 case 16:
2673 {
cristybb503372010-05-27 20:51:26 +00002674 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002675 {
glennrp58f77c72011-04-23 14:09:09 +00002676#if (MAGICKCORE_QUANTUM_DEPTH == 16)
2677 size_t
2678 quantum;
2679
2680 if (image->colors > 256)
2681 *r=((*p++) << 8);
2682
2683 else
2684 *r=0;
2685
2686 quantum=(*r);
2687 quantum|=(*p++);
2688 *r=(Quantum) quantum;
2689 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00002690
2691 if (ping_color_type == 4)
2692 {
glennrp58f77c72011-04-23 14:09:09 +00002693 quantum=((*p++) << 8);
2694 quantum|=(*p++);
glennrp8b698592011-04-26 03:38:21 +00002695 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-quantum));
2696 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002697 found_transparent_pixel = MagickTrue;
2698 q++;
2699 }
2700#else
2701#if (MAGICKCORE_QUANTUM_DEPTH == 32)
2702 size_t
2703 quantum;
2704
2705 if (image->colors > 256)
2706 *r=((*p++) << 8);
2707
2708 else
2709 *r=0;
2710
2711 quantum=(*r);
2712 quantum|=(*p++);
2713 *r=quantum;
2714 r++;
2715
2716 if (ping_color_type == 4)
2717 {
glennrp8b698592011-04-26 03:38:21 +00002718 quantum=(*p << 8) | *(p+1);
2719 quantum*=65537L;
2720 SetOpacityPixelComponent(q,
2721 (Quantum) GetAlphaPixelComponent(q));
2722 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002723 found_transparent_pixel = MagickTrue;
2724 p+=2;
2725 q++;
2726 }
2727
2728#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2729 *r++=(*p++);
2730 p++; /* strip low byte */
2731
2732 if (ping_color_type == 4)
2733 {
glennrp8b698592011-04-26 03:38:21 +00002734 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-(*p++)));
2735 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp9d0ea4d2011-04-22 18:35:57 +00002736 found_transparent_pixel = MagickTrue;
2737 p++;
2738 q++;
2739 }
cristy3ed852e2009-09-05 21:47:34 +00002740#endif
glennrp58f77c72011-04-23 14:09:09 +00002741#endif
glennrpa18d5bc2011-04-23 14:51:34 +00002742 }
glennrp47b9dd52010-11-24 18:12:06 +00002743
cristy3ed852e2009-09-05 21:47:34 +00002744 break;
2745 }
glennrp47b9dd52010-11-24 18:12:06 +00002746
cristy3ed852e2009-09-05 21:47:34 +00002747 default:
2748 break;
2749 }
glennrp3faa9a32011-04-23 14:00:25 +00002750
cristy3ed852e2009-09-05 21:47:34 +00002751 /*
2752 Transfer image scanline.
2753 */
2754 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00002755
cristybb503372010-05-27 20:51:26 +00002756 for (x=0; x < (ssize_t) image->columns; x++)
cristy9fff7b42011-04-29 01:09:31 +00002757 SetIndexPixelComponent(indexes+x,*r++);
glennrp0fe50b42010-11-16 03:52:51 +00002758
cristy3ed852e2009-09-05 21:47:34 +00002759 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2760 break;
glennrp0fe50b42010-11-16 03:52:51 +00002761
cristy7a287bf2010-02-14 02:18:09 +00002762 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2763 {
cristycee97112010-05-28 00:44:52 +00002764 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00002765 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00002766
cristy7a287bf2010-02-14 02:18:09 +00002767 if (status == MagickFalse)
2768 break;
2769 }
cristy3ed852e2009-09-05 21:47:34 +00002770 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002771
cristy7a287bf2010-02-14 02:18:09 +00002772 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002773 {
2774 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00002775
cristy3ed852e2009-09-05 21:47:34 +00002776 if (status == MagickFalse)
2777 break;
2778 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002779
cristy3ed852e2009-09-05 21:47:34 +00002780 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2781 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002782
2783 image->matte=found_transparent_pixel;
2784
2785 if (logging != MagickFalse)
2786 {
2787 if (found_transparent_pixel != MagickFalse)
2788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2789 " Found transparent pixel");
2790 else
glennrp5aa37f62011-01-02 03:07:57 +00002791 {
2792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2793 " No transparent pixel was found");
glennrpa6a06632011-01-19 15:15:34 +00002794
glennrp5aa37f62011-01-02 03:07:57 +00002795 ping_color_type&=0x03;
2796 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002797 }
2798 }
2799
cristyb32b90a2009-09-07 21:45:48 +00002800 if (quantum_info != (QuantumInfo *) NULL)
2801 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002802
cristy5c6f7892010-05-05 22:53:29 +00002803 if (image->storage_class == PseudoClass)
2804 {
cristyaeb2cbc2010-05-07 13:28:58 +00002805 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002806 matte;
2807
2808 matte=image->matte;
2809 image->matte=MagickFalse;
2810 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002811 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002812 }
glennrp47b9dd52010-11-24 18:12:06 +00002813
glennrp4eb39312011-03-30 21:34:55 +00002814 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00002815
2816 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002817 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002818 {
2819 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00002820 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002821 image->colors=2;
2822 (void) SetImageBackgroundColor(image);
2823#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002824 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002825#endif
2826 if (logging != MagickFalse)
2827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2828 " exit ReadOnePNGImage() early.");
2829 return(image);
2830 }
glennrp47b9dd52010-11-24 18:12:06 +00002831
glennrpfaa852b2010-03-30 12:17:00 +00002832 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002833 {
2834 ClassType
2835 storage_class;
2836
2837 /*
2838 Image has a transparent background.
2839 */
2840 storage_class=image->storage_class;
2841 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002842
glennrp3c218112010-11-27 15:31:26 +00002843/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00002844
glennrp0fe50b42010-11-16 03:52:51 +00002845 if (storage_class == PseudoClass)
2846 {
2847 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002848 {
glennrp0fe50b42010-11-16 03:52:51 +00002849 for (x=0; x < ping_num_trans; x++)
2850 {
2851 image->colormap[x].opacity =
2852 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2853 }
glennrpc11cf6a2010-03-20 16:46:19 +00002854 }
glennrp47b9dd52010-11-24 18:12:06 +00002855
glennrp0fe50b42010-11-16 03:52:51 +00002856 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2857 {
2858 for (x=0; x < (int) image->colors; x++)
2859 {
2860 if (ScaleQuantumToShort(image->colormap[x].red) ==
2861 transparent_color.opacity)
2862 {
2863 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2864 }
2865 }
2866 }
2867 (void) SyncImage(image);
2868 }
glennrp47b9dd52010-11-24 18:12:06 +00002869
glennrpa6a06632011-01-19 15:15:34 +00002870#if 1 /* Should have already been done above, but glennrp problem P10
2871 * needs this.
2872 */
glennrp0fe50b42010-11-16 03:52:51 +00002873 else
2874 {
2875 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002876 {
glennrp0fe50b42010-11-16 03:52:51 +00002877 image->storage_class=storage_class;
2878 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2879
2880 if (q == (PixelPacket *) NULL)
2881 break;
2882
2883 indexes=GetAuthenticIndexQueue(image);
2884
glennrpa6a06632011-01-19 15:15:34 +00002885 /* Caution: on a Q8 build, this does not distinguish between
2886 * 16-bit colors that differ only in the low byte
2887 */
glennrp0fe50b42010-11-16 03:52:51 +00002888 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2889 {
glennrp8b698592011-04-26 03:38:21 +00002890 if (ScaleQuantumToShort(GetRedPixelComponent(q))
2891 == transparent_color.red &&
2892 ScaleQuantumToShort(GetGreenPixelComponent(q))
2893 == transparent_color.green &&
2894 ScaleQuantumToShort(GetBluePixelComponent(q))
2895 == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00002896 {
glennrp8b698592011-04-26 03:38:21 +00002897 SetOpacityPixelComponent(q,TransparentOpacity);
glennrp4f25bd02011-01-01 18:51:28 +00002898 }
glennrp0fe50b42010-11-16 03:52:51 +00002899
glennrp67b9c1a2011-04-22 18:47:36 +00002900#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00002901 else
glennrp4f25bd02011-01-01 18:51:28 +00002902 {
glennrp4737d522011-04-29 03:33:42 +00002903 SetOpacityPixelComponent(q)=(Quantum) OpaqueOpacity;
glennrp4f25bd02011-01-01 18:51:28 +00002904 }
glennrpa6a06632011-01-19 15:15:34 +00002905#endif
glennrp0fe50b42010-11-16 03:52:51 +00002906
2907 q++;
2908 }
2909
2910 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2911 break;
glennrpc11cf6a2010-03-20 16:46:19 +00002912 }
glennrp0fe50b42010-11-16 03:52:51 +00002913 }
glennrpa6a06632011-01-19 15:15:34 +00002914#endif
glennrpc11cf6a2010-03-20 16:46:19 +00002915
cristy3ed852e2009-09-05 21:47:34 +00002916 image->storage_class=DirectClass;
2917 }
glennrp3c218112010-11-27 15:31:26 +00002918
cristyb40fc462010-08-08 00:49:49 +00002919 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2920 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2921 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00002922
cristyeb3b22a2011-03-31 20:16:11 +00002923 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00002924 {
2925 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00002926 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2927 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00002928 else
glennrpa0ed0092011-04-18 16:36:29 +00002929 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2930 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002931
glennrp4eb39312011-03-30 21:34:55 +00002932 if (status != MagickFalse)
2933 for (i=0; i < (ssize_t) num_text; i++)
2934 {
2935 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00002936
glennrp4eb39312011-03-30 21:34:55 +00002937 if (logging != MagickFalse)
2938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2939 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00002940
glennrp4eb39312011-03-30 21:34:55 +00002941 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00002942 {
glennrp4eb39312011-03-30 21:34:55 +00002943 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2944 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00002945 }
glennrp0fe50b42010-11-16 03:52:51 +00002946
glennrp4eb39312011-03-30 21:34:55 +00002947 else
2948 {
2949 char
2950 *value;
2951
2952 length=text[i].text_length;
2953 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2954 sizeof(*value));
2955 if (value == (char *) NULL)
2956 {
2957 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2958 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2959 image->filename);
2960 break;
2961 }
2962 *value='\0';
2963 (void) ConcatenateMagickString(value,text[i].text,length+2);
2964
2965 /* Don't save "density" or "units" property if we have a pHYs
2966 * chunk
2967 */
2968 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2969 (LocaleCompare(text[i].key,"density") != 0 &&
2970 LocaleCompare(text[i].key,"units") != 0))
2971 (void) SetImageProperty(image,text[i].key,value);
2972
2973 if (logging != MagickFalse)
2974 {
2975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2976 " length: %lu",(unsigned long) length);
2977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2978 " Keyword: %s",text[i].key);
2979 }
2980
2981 value=DestroyString(value);
2982 }
2983 }
2984 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00002985 }
glennrp3c218112010-11-27 15:31:26 +00002986
cristy3ed852e2009-09-05 21:47:34 +00002987#ifdef MNG_OBJECT_BUFFERS
2988 /*
2989 Store the object if necessary.
2990 */
2991 if (object_id && !mng_info->frozen[object_id])
2992 {
2993 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2994 {
2995 /*
2996 create a new object buffer.
2997 */
2998 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00002999 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003000
cristy3ed852e2009-09-05 21:47:34 +00003001 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3002 {
3003 mng_info->ob[object_id]->image=(Image *) NULL;
3004 mng_info->ob[object_id]->reference_count=1;
3005 }
3006 }
glennrp47b9dd52010-11-24 18:12:06 +00003007
cristy3ed852e2009-09-05 21:47:34 +00003008 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3009 mng_info->ob[object_id]->frozen)
3010 {
3011 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3012 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3013 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3014 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003015
cristy3ed852e2009-09-05 21:47:34 +00003016 if (mng_info->ob[object_id]->frozen)
3017 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3018 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3019 "`%s'",image->filename);
3020 }
glennrp0fe50b42010-11-16 03:52:51 +00003021
cristy3ed852e2009-09-05 21:47:34 +00003022 else
3023 {
cristy3ed852e2009-09-05 21:47:34 +00003024
3025 if (mng_info->ob[object_id]->image != (Image *) NULL)
3026 mng_info->ob[object_id]->image=DestroyImage
3027 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003028
cristy3ed852e2009-09-05 21:47:34 +00003029 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3030 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003031
cristy3ed852e2009-09-05 21:47:34 +00003032 if (mng_info->ob[object_id]->image != (Image *) NULL)
3033 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003034
cristy3ed852e2009-09-05 21:47:34 +00003035 else
3036 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3037 ResourceLimitError,"Cloning image for object buffer failed",
3038 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003039
glennrpfaa852b2010-03-30 12:17:00 +00003040 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003041 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003042
glennrpfaa852b2010-03-30 12:17:00 +00003043 mng_info->ob[object_id]->width=ping_width;
3044 mng_info->ob[object_id]->height=ping_height;
3045 mng_info->ob[object_id]->color_type=ping_color_type;
3046 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3047 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3048 mng_info->ob[object_id]->compression_method=
3049 ping_compression_method;
3050 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003051
glennrpfaa852b2010-03-30 12:17:00 +00003052 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003053 {
3054 int
3055 number_colors;
3056
3057 png_colorp
3058 plte;
3059
3060 /*
3061 Copy the PLTE to the object buffer.
3062 */
3063 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3064 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003065
cristy3ed852e2009-09-05 21:47:34 +00003066 for (i=0; i < number_colors; i++)
3067 {
3068 mng_info->ob[object_id]->plte[i]=plte[i];
3069 }
3070 }
glennrp47b9dd52010-11-24 18:12:06 +00003071
cristy3ed852e2009-09-05 21:47:34 +00003072 else
3073 mng_info->ob[object_id]->plte_length=0;
3074 }
3075 }
3076#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003077
3078 /* Set image->matte to MagickTrue if the input colortype supports
3079 * alpha or if a valid tRNS chunk is present, no matter whether there
3080 * is actual transparency present.
3081 */
3082 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3083 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3084 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3085 MagickTrue : MagickFalse;
3086
glennrpcb395ac2011-03-30 19:50:23 +00003087 /* Set more properties for identify to retrieve */
3088 {
3089 char
3090 msg[MaxTextExtent];
3091
glennrp4eb39312011-03-30 21:34:55 +00003092 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003093 {
3094 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3095 (void) FormatMagickString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003096 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003097 (void) SetImageProperty(image,"PNG:text ",msg);
3098 }
3099
3100 if (num_raw_profiles != 0)
3101 {
3102 (void) FormatMagickString(msg,MaxTextExtent,
3103 "%d were found", num_raw_profiles);
3104 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3105 }
3106
glennrpcb395ac2011-03-30 19:50:23 +00003107 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003108 {
3109 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3110 "chunk was found (see Chromaticity, above)");
3111 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3112 }
glennrpcb395ac2011-03-30 19:50:23 +00003113
3114 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003115 {
3116 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3117 "chunk was found (see Background color, above)");
3118 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3119 }
3120
3121 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3122 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003123
3124 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3125 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3126
glennrpcb395ac2011-03-30 19:50:23 +00003127 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3128 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003129
3130#if defined(PNG_sRGB_SUPPORTED)
3131 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3132 {
glennrp07523c72011-03-31 18:12:10 +00003133 (void) FormatMagickString(msg,MaxTextExtent,
3134 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003135 (int) intent);
3136 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3137 }
3138#endif
3139
3140 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3141 {
glennrp07523c72011-03-31 18:12:10 +00003142 (void) FormatMagickString(msg,MaxTextExtent,
3143 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003144 file_gamma);
3145 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3146 }
3147
3148#if defined(PNG_pHYs_SUPPORTED)
3149 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3150 {
glennrp07523c72011-03-31 18:12:10 +00003151 (void) FormatMagickString(msg,MaxTextExtent,
3152 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003153 (double) x_resolution,(double) y_resolution, unit_type);
3154 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3155 }
3156#endif
3157
3158#if defined(PNG_oFFs_SUPPORTED)
3159 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3160 {
3161 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3162 (double) image->page.x,(double) image->page.y);
3163 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3164 }
3165#endif
3166
glennrp07523c72011-03-31 18:12:10 +00003167 if ((image->page.width != 0 && image->page.width != image->columns) ||
3168 (image->page.height != 0 && image->page.height != image->rows))
3169 {
3170 (void) FormatMagickString(msg,MaxTextExtent,
3171 "width=%.20g, height=%.20g",
3172 (double) image->page.width,(double) image->page.height);
3173 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3174 }
glennrpcb395ac2011-03-30 19:50:23 +00003175 }
3176
cristy3ed852e2009-09-05 21:47:34 +00003177 /*
3178 Relinquish resources.
3179 */
3180 png_destroy_read_struct(&ping,&ping_info,&end_info);
3181
glennrpcf002022011-01-30 02:38:15 +00003182 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003183#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003184 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003185#endif
3186
3187 if (logging != MagickFalse)
3188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3189 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003190
cristy3ed852e2009-09-05 21:47:34 +00003191 return(image);
3192
3193/* end of reading one PNG image */
3194}
3195
3196static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3197{
3198 Image
3199 *image,
3200 *previous;
3201
3202 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003203 have_mng_structure,
3204 logging,
cristy3ed852e2009-09-05 21:47:34 +00003205 status;
3206
3207 MngInfo
3208 *mng_info;
3209
3210 char
3211 magic_number[MaxTextExtent];
3212
cristy3ed852e2009-09-05 21:47:34 +00003213 ssize_t
3214 count;
3215
3216 /*
3217 Open image file.
3218 */
3219 assert(image_info != (const ImageInfo *) NULL);
3220 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003221
cristy3ed852e2009-09-05 21:47:34 +00003222 if (image_info->debug != MagickFalse)
3223 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3224 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003225
cristy3ed852e2009-09-05 21:47:34 +00003226 assert(exception != (ExceptionInfo *) NULL);
3227 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003228 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003229 image=AcquireImage(image_info);
3230 mng_info=(MngInfo *) NULL;
3231 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003232
cristy3ed852e2009-09-05 21:47:34 +00003233 if (status == MagickFalse)
3234 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003235
cristy3ed852e2009-09-05 21:47:34 +00003236 /*
3237 Verify PNG signature.
3238 */
3239 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003240
glennrpdde35db2011-02-21 12:06:32 +00003241 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003242 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003243
cristy3ed852e2009-09-05 21:47:34 +00003244 /*
3245 Allocate a MngInfo structure.
3246 */
3247 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003248 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003249
cristy3ed852e2009-09-05 21:47:34 +00003250 if (mng_info == (MngInfo *) NULL)
3251 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003252
cristy3ed852e2009-09-05 21:47:34 +00003253 /*
3254 Initialize members of the MngInfo structure.
3255 */
3256 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3257 mng_info->image=image;
3258 have_mng_structure=MagickTrue;
3259
3260 previous=image;
3261 image=ReadOnePNGImage(mng_info,image_info,exception);
3262 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003263
cristy3ed852e2009-09-05 21:47:34 +00003264 if (image == (Image *) NULL)
3265 {
3266 if (previous != (Image *) NULL)
3267 {
3268 if (previous->signature != MagickSignature)
3269 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003270
cristy3ed852e2009-09-05 21:47:34 +00003271 (void) CloseBlob(previous);
3272 (void) DestroyImageList(previous);
3273 }
glennrp0fe50b42010-11-16 03:52:51 +00003274
cristy3ed852e2009-09-05 21:47:34 +00003275 if (logging != MagickFalse)
3276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3277 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003278
cristy3ed852e2009-09-05 21:47:34 +00003279 return((Image *) NULL);
3280 }
glennrp47b9dd52010-11-24 18:12:06 +00003281
cristy3ed852e2009-09-05 21:47:34 +00003282 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003283
cristy3ed852e2009-09-05 21:47:34 +00003284 if ((image->columns == 0) || (image->rows == 0))
3285 {
3286 if (logging != MagickFalse)
3287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3288 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003289
cristy3ed852e2009-09-05 21:47:34 +00003290 ThrowReaderException(CorruptImageError,"CorruptImage");
3291 }
glennrp47b9dd52010-11-24 18:12:06 +00003292
glennrp3faa9a32011-04-23 14:00:25 +00003293#if 0 /* This is probably redundant now */
cristy3ed852e2009-09-05 21:47:34 +00003294 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3295 {
3296 (void) SetImageType(image,PaletteType);
glennrp0fe50b42010-11-16 03:52:51 +00003297
cristy3ed852e2009-09-05 21:47:34 +00003298 if (image->matte != MagickFalse)
3299 {
3300 /* To do: Reduce to binary transparency */
3301 }
3302 }
glennrp3faa9a32011-04-23 14:00:25 +00003303#endif
glennrp47b9dd52010-11-24 18:12:06 +00003304
cristy3ed852e2009-09-05 21:47:34 +00003305 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3306 {
3307 (void) SetImageType(image,TrueColorType);
3308 image->matte=MagickFalse;
3309 }
glennrp0fe50b42010-11-16 03:52:51 +00003310
cristy3ed852e2009-09-05 21:47:34 +00003311 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3312 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003313
cristy3ed852e2009-09-05 21:47:34 +00003314 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3316 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3317 (double) image->page.width,(double) image->page.height,
3318 (double) image->page.x,(double) image->page.y);
3319
3320 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003322
cristy3ed852e2009-09-05 21:47:34 +00003323 return(image);
3324}
3325
3326
3327
3328#if defined(JNG_SUPPORTED)
3329/*
3330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3331% %
3332% %
3333% %
3334% R e a d O n e J N G I m a g e %
3335% %
3336% %
3337% %
3338%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3339%
3340% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3341% (minus the 8-byte signature) and returns it. It allocates the memory
3342% necessary for the new Image structure and returns a pointer to the new
3343% image.
3344%
3345% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3346%
3347% The format of the ReadOneJNGImage method is:
3348%
3349% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3350% ExceptionInfo *exception)
3351%
3352% A description of each parameter follows:
3353%
3354% o mng_info: Specifies a pointer to a MngInfo structure.
3355%
3356% o image_info: the image info.
3357%
3358% o exception: return any errors or warnings in this structure.
3359%
3360*/
3361static Image *ReadOneJNGImage(MngInfo *mng_info,
3362 const ImageInfo *image_info, ExceptionInfo *exception)
3363{
3364 Image
3365 *alpha_image,
3366 *color_image,
3367 *image,
3368 *jng_image;
3369
3370 ImageInfo
3371 *alpha_image_info,
3372 *color_image_info;
3373
cristy4383ec82011-01-05 15:42:32 +00003374 MagickBooleanType
3375 logging;
3376
cristybb503372010-05-27 20:51:26 +00003377 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003378 y;
3379
3380 MagickBooleanType
3381 status;
3382
3383 png_uint_32
3384 jng_height,
3385 jng_width;
3386
3387 png_byte
3388 jng_color_type,
3389 jng_image_sample_depth,
3390 jng_image_compression_method,
3391 jng_image_interlace_method,
3392 jng_alpha_sample_depth,
3393 jng_alpha_compression_method,
3394 jng_alpha_filter_method,
3395 jng_alpha_interlace_method;
3396
3397 register const PixelPacket
3398 *s;
3399
cristybb503372010-05-27 20:51:26 +00003400 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003401 i,
3402 x;
3403
3404 register PixelPacket
3405 *q;
3406
3407 register unsigned char
3408 *p;
3409
3410 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003411 read_JSEP,
3412 reading_idat,
3413 skip_to_iend;
3414
cristybb503372010-05-27 20:51:26 +00003415 size_t
cristy3ed852e2009-09-05 21:47:34 +00003416 length;
3417
3418 jng_alpha_compression_method=0;
3419 jng_alpha_sample_depth=8;
3420 jng_color_type=0;
3421 jng_height=0;
3422 jng_width=0;
3423 alpha_image=(Image *) NULL;
3424 color_image=(Image *) NULL;
3425 alpha_image_info=(ImageInfo *) NULL;
3426 color_image_info=(ImageInfo *) NULL;
3427
3428 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003429 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003430
3431 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003432
cristy3ed852e2009-09-05 21:47:34 +00003433 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3434 {
3435 /*
3436 Allocate next image structure.
3437 */
3438 if (logging != MagickFalse)
3439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3440 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003441
cristy3ed852e2009-09-05 21:47:34 +00003442 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003443
cristy3ed852e2009-09-05 21:47:34 +00003444 if (GetNextImageInList(image) == (Image *) NULL)
3445 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003446
cristy3ed852e2009-09-05 21:47:34 +00003447 image=SyncNextImageInList(image);
3448 }
3449 mng_info->image=image;
3450
3451 /*
3452 Signature bytes have already been read.
3453 */
3454
3455 read_JSEP=MagickFalse;
3456 reading_idat=MagickFalse;
3457 skip_to_iend=MagickFalse;
3458 for (;;)
3459 {
3460 char
3461 type[MaxTextExtent];
3462
3463 unsigned char
3464 *chunk;
3465
3466 unsigned int
3467 count;
3468
3469 /*
3470 Read a new JNG chunk.
3471 */
3472 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3473 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003474
cristy3ed852e2009-09-05 21:47:34 +00003475 if (status == MagickFalse)
3476 break;
glennrp0fe50b42010-11-16 03:52:51 +00003477
cristy3ed852e2009-09-05 21:47:34 +00003478 type[0]='\0';
3479 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3480 length=ReadBlobMSBLong(image);
3481 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3482
3483 if (logging != MagickFalse)
3484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003485 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3486 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003487
3488 if (length > PNG_UINT_31_MAX || count == 0)
3489 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003490
cristy3ed852e2009-09-05 21:47:34 +00003491 p=NULL;
3492 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003493
cristy3ed852e2009-09-05 21:47:34 +00003494 if (length)
3495 {
3496 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003497
cristy3ed852e2009-09-05 21:47:34 +00003498 if (chunk == (unsigned char *) NULL)
3499 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003500
cristybb503372010-05-27 20:51:26 +00003501 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003502 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003503
cristy3ed852e2009-09-05 21:47:34 +00003504 p=chunk;
3505 }
glennrp47b9dd52010-11-24 18:12:06 +00003506
cristy3ed852e2009-09-05 21:47:34 +00003507 (void) ReadBlobMSBLong(image); /* read crc word */
3508
3509 if (skip_to_iend)
3510 {
3511 if (length)
3512 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003513
cristy3ed852e2009-09-05 21:47:34 +00003514 continue;
3515 }
3516
3517 if (memcmp(type,mng_JHDR,4) == 0)
3518 {
3519 if (length == 16)
3520 {
cristybb503372010-05-27 20:51:26 +00003521 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003522 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003523 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003524 (p[6] << 8) | p[7]);
3525 jng_color_type=p[8];
3526 jng_image_sample_depth=p[9];
3527 jng_image_compression_method=p[10];
3528 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003529
cristy3ed852e2009-09-05 21:47:34 +00003530 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3531 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003532
cristy3ed852e2009-09-05 21:47:34 +00003533 jng_alpha_sample_depth=p[12];
3534 jng_alpha_compression_method=p[13];
3535 jng_alpha_filter_method=p[14];
3536 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003537
cristy3ed852e2009-09-05 21:47:34 +00003538 if (logging != MagickFalse)
3539 {
3540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003541 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003542
cristy3ed852e2009-09-05 21:47:34 +00003543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003544 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003545
cristy3ed852e2009-09-05 21:47:34 +00003546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3547 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003548
cristy3ed852e2009-09-05 21:47:34 +00003549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3550 " jng_image_sample_depth: %3d",
3551 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003552
cristy3ed852e2009-09-05 21:47:34 +00003553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3554 " jng_image_compression_method:%3d",
3555 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003556
cristy3ed852e2009-09-05 21:47:34 +00003557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3558 " jng_image_interlace_method: %3d",
3559 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003560
cristy3ed852e2009-09-05 21:47:34 +00003561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3562 " jng_alpha_sample_depth: %3d",
3563 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003564
cristy3ed852e2009-09-05 21:47:34 +00003565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3566 " jng_alpha_compression_method:%3d",
3567 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003568
cristy3ed852e2009-09-05 21:47:34 +00003569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3570 " jng_alpha_filter_method: %3d",
3571 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003572
cristy3ed852e2009-09-05 21:47:34 +00003573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3574 " jng_alpha_interlace_method: %3d",
3575 jng_alpha_interlace_method);
3576 }
3577 }
glennrp47b9dd52010-11-24 18:12:06 +00003578
cristy3ed852e2009-09-05 21:47:34 +00003579 if (length)
3580 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003581
cristy3ed852e2009-09-05 21:47:34 +00003582 continue;
3583 }
3584
3585
3586 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3587 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3588 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3589 {
3590 /*
3591 o create color_image
3592 o open color_blob, attached to color_image
3593 o if (color type has alpha)
3594 open alpha_blob, attached to alpha_image
3595 */
3596
cristy73bd4a52010-10-05 11:24:23 +00003597 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003598
cristy3ed852e2009-09-05 21:47:34 +00003599 if (color_image_info == (ImageInfo *) NULL)
3600 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003601
cristy3ed852e2009-09-05 21:47:34 +00003602 GetImageInfo(color_image_info);
3603 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003604
cristy3ed852e2009-09-05 21:47:34 +00003605 if (color_image == (Image *) NULL)
3606 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3607
3608 if (logging != MagickFalse)
3609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3610 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003611
cristy3ed852e2009-09-05 21:47:34 +00003612 (void) AcquireUniqueFilename(color_image->filename);
3613 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3614 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003615
cristy3ed852e2009-09-05 21:47:34 +00003616 if (status == MagickFalse)
3617 return((Image *) NULL);
3618
3619 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3620 {
3621 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00003622 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00003623
cristy3ed852e2009-09-05 21:47:34 +00003624 if (alpha_image_info == (ImageInfo *) NULL)
3625 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003626
cristy3ed852e2009-09-05 21:47:34 +00003627 GetImageInfo(alpha_image_info);
3628 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003629
cristy3ed852e2009-09-05 21:47:34 +00003630 if (alpha_image == (Image *) NULL)
3631 {
3632 alpha_image=DestroyImage(alpha_image);
3633 ThrowReaderException(ResourceLimitError,
3634 "MemoryAllocationFailed");
3635 }
glennrp0fe50b42010-11-16 03:52:51 +00003636
cristy3ed852e2009-09-05 21:47:34 +00003637 if (logging != MagickFalse)
3638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3639 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003640
cristy3ed852e2009-09-05 21:47:34 +00003641 (void) AcquireUniqueFilename(alpha_image->filename);
3642 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3643 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003644
cristy3ed852e2009-09-05 21:47:34 +00003645 if (status == MagickFalse)
3646 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003647
cristy3ed852e2009-09-05 21:47:34 +00003648 if (jng_alpha_compression_method == 0)
3649 {
3650 unsigned char
3651 data[18];
3652
3653 if (logging != MagickFalse)
3654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3655 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003656
cristy3ed852e2009-09-05 21:47:34 +00003657 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3658 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00003659
cristy3ed852e2009-09-05 21:47:34 +00003660 (void) WriteBlobMSBULong(alpha_image,13L);
3661 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00003662 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00003663 PNGLong(data+4,jng_width);
3664 PNGLong(data+8,jng_height);
3665 data[12]=jng_alpha_sample_depth;
3666 data[13]=0; /* color_type gray */
3667 data[14]=0; /* compression method 0 */
3668 data[15]=0; /* filter_method 0 */
3669 data[16]=0; /* interlace_method 0 */
3670 (void) WriteBlob(alpha_image,17,data);
3671 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3672 }
3673 }
3674 reading_idat=MagickTrue;
3675 }
3676
3677 if (memcmp(type,mng_JDAT,4) == 0)
3678 {
glennrp47b9dd52010-11-24 18:12:06 +00003679 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003680
3681 if (logging != MagickFalse)
3682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3683 " Copying JDAT chunk data to color_blob.");
3684
3685 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003686
cristy3ed852e2009-09-05 21:47:34 +00003687 if (length)
3688 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003689
cristy3ed852e2009-09-05 21:47:34 +00003690 continue;
3691 }
3692
3693 if (memcmp(type,mng_IDAT,4) == 0)
3694 {
3695 png_byte
3696 data[5];
3697
glennrp47b9dd52010-11-24 18:12:06 +00003698 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003699
3700 if (image_info->ping == MagickFalse)
3701 {
3702 if (logging != MagickFalse)
3703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3704 " Copying IDAT chunk data to alpha_blob.");
3705
cristybb503372010-05-27 20:51:26 +00003706 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003707 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00003708 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00003709 (void) WriteBlob(alpha_image,4,data);
3710 (void) WriteBlob(alpha_image,length,chunk);
3711 (void) WriteBlobMSBULong(alpha_image,
3712 crc32(crc32(0,data,4),chunk,(uInt) length));
3713 }
glennrp0fe50b42010-11-16 03:52:51 +00003714
cristy3ed852e2009-09-05 21:47:34 +00003715 if (length)
3716 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003717
cristy3ed852e2009-09-05 21:47:34 +00003718 continue;
3719 }
3720
3721 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3722 {
glennrp47b9dd52010-11-24 18:12:06 +00003723 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003724
3725 if (image_info->ping == MagickFalse)
3726 {
3727 if (logging != MagickFalse)
3728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3729 " Copying JDAA chunk data to alpha_blob.");
3730
3731 (void) WriteBlob(alpha_image,length,chunk);
3732 }
glennrp0fe50b42010-11-16 03:52:51 +00003733
cristy3ed852e2009-09-05 21:47:34 +00003734 if (length)
3735 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003736
cristy3ed852e2009-09-05 21:47:34 +00003737 continue;
3738 }
3739
3740 if (memcmp(type,mng_JSEP,4) == 0)
3741 {
3742 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00003743
cristy3ed852e2009-09-05 21:47:34 +00003744 if (length)
3745 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003746
cristy3ed852e2009-09-05 21:47:34 +00003747 continue;
3748 }
3749
3750 if (memcmp(type,mng_bKGD,4) == 0)
3751 {
3752 if (length == 2)
3753 {
3754 image->background_color.red=ScaleCharToQuantum(p[1]);
3755 image->background_color.green=image->background_color.red;
3756 image->background_color.blue=image->background_color.red;
3757 }
glennrp0fe50b42010-11-16 03:52:51 +00003758
cristy3ed852e2009-09-05 21:47:34 +00003759 if (length == 6)
3760 {
3761 image->background_color.red=ScaleCharToQuantum(p[1]);
3762 image->background_color.green=ScaleCharToQuantum(p[3]);
3763 image->background_color.blue=ScaleCharToQuantum(p[5]);
3764 }
glennrp0fe50b42010-11-16 03:52:51 +00003765
cristy3ed852e2009-09-05 21:47:34 +00003766 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3767 continue;
3768 }
3769
3770 if (memcmp(type,mng_gAMA,4) == 0)
3771 {
3772 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00003773 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00003774
cristy3ed852e2009-09-05 21:47:34 +00003775 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3776 continue;
3777 }
3778
3779 if (memcmp(type,mng_cHRM,4) == 0)
3780 {
3781 if (length == 32)
3782 {
cristy8182b072010-05-30 20:10:53 +00003783 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3784 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3785 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3786 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3787 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3788 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3789 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3790 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003791 }
glennrp47b9dd52010-11-24 18:12:06 +00003792
cristy3ed852e2009-09-05 21:47:34 +00003793 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3794 continue;
3795 }
3796
3797 if (memcmp(type,mng_sRGB,4) == 0)
3798 {
3799 if (length == 1)
3800 {
glennrpe610a072010-08-05 17:08:46 +00003801 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00003802 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00003803 image->gamma=0.45455f;
3804 image->chromaticity.red_primary.x=0.6400f;
3805 image->chromaticity.red_primary.y=0.3300f;
3806 image->chromaticity.green_primary.x=0.3000f;
3807 image->chromaticity.green_primary.y=0.6000f;
3808 image->chromaticity.blue_primary.x=0.1500f;
3809 image->chromaticity.blue_primary.y=0.0600f;
3810 image->chromaticity.white_point.x=0.3127f;
3811 image->chromaticity.white_point.y=0.3290f;
3812 }
glennrp47b9dd52010-11-24 18:12:06 +00003813
cristy3ed852e2009-09-05 21:47:34 +00003814 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3815 continue;
3816 }
3817
3818 if (memcmp(type,mng_oFFs,4) == 0)
3819 {
3820 if (length > 8)
3821 {
glennrp5eae7602011-02-22 15:21:32 +00003822 image->page.x=(ssize_t) mng_get_long(p);
3823 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00003824
cristy3ed852e2009-09-05 21:47:34 +00003825 if ((int) p[8] != 0)
3826 {
3827 image->page.x/=10000;
3828 image->page.y/=10000;
3829 }
3830 }
glennrp47b9dd52010-11-24 18:12:06 +00003831
cristy3ed852e2009-09-05 21:47:34 +00003832 if (length)
3833 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003834
cristy3ed852e2009-09-05 21:47:34 +00003835 continue;
3836 }
3837
3838 if (memcmp(type,mng_pHYs,4) == 0)
3839 {
3840 if (length > 8)
3841 {
cristy8182b072010-05-30 20:10:53 +00003842 image->x_resolution=(double) mng_get_long(p);
3843 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003844 if ((int) p[8] == PNG_RESOLUTION_METER)
3845 {
3846 image->units=PixelsPerCentimeterResolution;
3847 image->x_resolution=image->x_resolution/100.0f;
3848 image->y_resolution=image->y_resolution/100.0f;
3849 }
3850 }
glennrp0fe50b42010-11-16 03:52:51 +00003851
cristy3ed852e2009-09-05 21:47:34 +00003852 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3853 continue;
3854 }
3855
3856#if 0
3857 if (memcmp(type,mng_iCCP,4) == 0)
3858 {
glennrpfd05d622011-02-25 04:10:33 +00003859 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00003860 if (length)
3861 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003862
cristy3ed852e2009-09-05 21:47:34 +00003863 continue;
3864 }
3865#endif
3866
3867 if (length)
3868 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3869
3870 if (memcmp(type,mng_IEND,4))
3871 continue;
glennrp0fe50b42010-11-16 03:52:51 +00003872
cristy3ed852e2009-09-05 21:47:34 +00003873 break;
3874 }
3875
3876
3877 /* IEND found */
3878
3879 /*
3880 Finish up reading image data:
3881
3882 o read main image from color_blob.
3883
3884 o close color_blob.
3885
3886 o if (color_type has alpha)
3887 if alpha_encoding is PNG
3888 read secondary image from alpha_blob via ReadPNG
3889 if alpha_encoding is JPEG
3890 read secondary image from alpha_blob via ReadJPEG
3891
3892 o close alpha_blob.
3893
3894 o copy intensity of secondary image into
3895 opacity samples of main image.
3896
3897 o destroy the secondary image.
3898 */
3899
3900 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00003901
cristy3ed852e2009-09-05 21:47:34 +00003902 if (logging != MagickFalse)
3903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3904 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003905
cristy3ed852e2009-09-05 21:47:34 +00003906 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3907 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003908
cristy3ed852e2009-09-05 21:47:34 +00003909 color_image_info->ping=MagickFalse; /* To do: avoid this */
3910 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003911
cristy3ed852e2009-09-05 21:47:34 +00003912 if (jng_image == (Image *) NULL)
3913 return((Image *) NULL);
3914
3915 (void) RelinquishUniqueFileResource(color_image->filename);
3916 color_image=DestroyImage(color_image);
3917 color_image_info=DestroyImageInfo(color_image_info);
3918
3919 if (jng_image == (Image *) NULL)
3920 return((Image *) NULL);
3921
3922 if (logging != MagickFalse)
3923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3924 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00003925
cristy3ed852e2009-09-05 21:47:34 +00003926 image->rows=jng_height;
3927 image->columns=jng_width;
3928 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00003929
cristybb503372010-05-27 20:51:26 +00003930 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003931 {
3932 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3933 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3934 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00003935
cristy3ed852e2009-09-05 21:47:34 +00003936 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3937 break;
3938 }
glennrp0fe50b42010-11-16 03:52:51 +00003939
cristy3ed852e2009-09-05 21:47:34 +00003940 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00003941
cristy3ed852e2009-09-05 21:47:34 +00003942 if (image_info->ping == MagickFalse)
3943 {
3944 if (jng_color_type >= 12)
3945 {
3946 if (jng_alpha_compression_method == 0)
3947 {
3948 png_byte
3949 data[5];
3950 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3951 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00003952 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00003953 (void) WriteBlob(alpha_image,4,data);
3954 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3955 }
glennrp0fe50b42010-11-16 03:52:51 +00003956
cristy3ed852e2009-09-05 21:47:34 +00003957 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00003958
cristy3ed852e2009-09-05 21:47:34 +00003959 if (logging != MagickFalse)
3960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " Reading opacity from alpha_blob.");
3962
3963 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3964 "%s",alpha_image->filename);
3965
3966 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003969 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003970 {
3971 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3972 &image->exception);
3973 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003974
cristy3ed852e2009-09-05 21:47:34 +00003975 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003976 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
glennrp4737d522011-04-29 03:33:42 +00003977 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
3978 GetRedPixelComponent(s));
glennrp0fe50b42010-11-16 03:52:51 +00003979
cristy3ed852e2009-09-05 21:47:34 +00003980 else
cristybb503372010-05-27 20:51:26 +00003981 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003982 {
glennrp7c7b3152011-04-26 04:01:27 +00003983 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
3984 GetRedPixelComponent(s));
3985 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +00003986 image->matte=MagickTrue;
3987 }
glennrp0fe50b42010-11-16 03:52:51 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3990 break;
3991 }
3992 (void) RelinquishUniqueFileResource(alpha_image->filename);
3993 alpha_image=DestroyImage(alpha_image);
3994 alpha_image_info=DestroyImageInfo(alpha_image_info);
3995 if (jng_image != (Image *) NULL)
3996 jng_image=DestroyImage(jng_image);
3997 }
3998 }
3999
glennrp47b9dd52010-11-24 18:12:06 +00004000 /* Read the JNG image. */
4001
cristy3ed852e2009-09-05 21:47:34 +00004002 if (mng_info->mng_type == 0)
4003 {
4004 mng_info->mng_width=jng_width;
4005 mng_info->mng_height=jng_height;
4006 }
glennrp0fe50b42010-11-16 03:52:51 +00004007
cristy3ed852e2009-09-05 21:47:34 +00004008 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004009 {
4010 image->page.width=jng_width;
4011 image->page.height=jng_height;
4012 }
4013
cristy3ed852e2009-09-05 21:47:34 +00004014 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004015 {
4016 image->page.x=mng_info->x_off[mng_info->object_id];
4017 image->page.y=mng_info->y_off[mng_info->object_id];
4018 }
4019
cristy3ed852e2009-09-05 21:47:34 +00004020 else
glennrp0fe50b42010-11-16 03:52:51 +00004021 {
4022 image->page.y=mng_info->y_off[mng_info->object_id];
4023 }
4024
cristy3ed852e2009-09-05 21:47:34 +00004025 mng_info->image_found++;
4026 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4027 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004028
cristy3ed852e2009-09-05 21:47:34 +00004029 if (logging != MagickFalse)
4030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4031 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 return(image);
4034}
4035
4036/*
4037%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4038% %
4039% %
4040% %
4041% R e a d J N G I m a g e %
4042% %
4043% %
4044% %
4045%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4046%
4047% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4048% (including the 8-byte signature) and returns it. It allocates the memory
4049% necessary for the new Image structure and returns a pointer to the new
4050% image.
4051%
4052% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4053%
4054% The format of the ReadJNGImage method is:
4055%
4056% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4057% *exception)
4058%
4059% A description of each parameter follows:
4060%
4061% o image_info: the image info.
4062%
4063% o exception: return any errors or warnings in this structure.
4064%
4065*/
4066
4067static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4068{
4069 Image
4070 *image,
4071 *previous;
4072
4073 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004074 have_mng_structure,
4075 logging,
cristy3ed852e2009-09-05 21:47:34 +00004076 status;
4077
4078 MngInfo
4079 *mng_info;
4080
4081 char
4082 magic_number[MaxTextExtent];
4083
cristy3ed852e2009-09-05 21:47:34 +00004084 size_t
4085 count;
4086
4087 /*
4088 Open image file.
4089 */
4090 assert(image_info != (const ImageInfo *) NULL);
4091 assert(image_info->signature == MagickSignature);
4092 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4093 assert(exception != (ExceptionInfo *) NULL);
4094 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004095 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004096 image=AcquireImage(image_info);
4097 mng_info=(MngInfo *) NULL;
4098 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004099
cristy3ed852e2009-09-05 21:47:34 +00004100 if (status == MagickFalse)
4101 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004102
cristy3ed852e2009-09-05 21:47:34 +00004103 if (LocaleCompare(image_info->magick,"JNG") != 0)
4104 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004105
glennrp47b9dd52010-11-24 18:12:06 +00004106 /* Verify JNG signature. */
4107
cristy3ed852e2009-09-05 21:47:34 +00004108 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004109
glennrp3b8763e2011-02-21 12:08:18 +00004110 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004111 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004112
glennrp47b9dd52010-11-24 18:12:06 +00004113 /* Allocate a MngInfo structure. */
4114
cristy3ed852e2009-09-05 21:47:34 +00004115 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004116 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004117
cristy3ed852e2009-09-05 21:47:34 +00004118 if (mng_info == (MngInfo *) NULL)
4119 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004120
glennrp47b9dd52010-11-24 18:12:06 +00004121 /* Initialize members of the MngInfo structure. */
4122
cristy3ed852e2009-09-05 21:47:34 +00004123 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4124 have_mng_structure=MagickTrue;
4125
4126 mng_info->image=image;
4127 previous=image;
4128 image=ReadOneJNGImage(mng_info,image_info,exception);
4129 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004130
cristy3ed852e2009-09-05 21:47:34 +00004131 if (image == (Image *) NULL)
4132 {
4133 if (IsImageObject(previous) != MagickFalse)
4134 {
4135 (void) CloseBlob(previous);
4136 (void) DestroyImageList(previous);
4137 }
glennrp0fe50b42010-11-16 03:52:51 +00004138
cristy3ed852e2009-09-05 21:47:34 +00004139 if (logging != MagickFalse)
4140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4141 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004142
cristy3ed852e2009-09-05 21:47:34 +00004143 return((Image *) NULL);
4144 }
4145 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004146
cristy3ed852e2009-09-05 21:47:34 +00004147 if (image->columns == 0 || image->rows == 0)
4148 {
4149 if (logging != MagickFalse)
4150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4151 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004152
cristy3ed852e2009-09-05 21:47:34 +00004153 ThrowReaderException(CorruptImageError,"CorruptImage");
4154 }
glennrp0fe50b42010-11-16 03:52:51 +00004155
cristy3ed852e2009-09-05 21:47:34 +00004156 if (logging != MagickFalse)
4157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004158
cristy3ed852e2009-09-05 21:47:34 +00004159 return(image);
4160}
4161#endif
4162
4163static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4164{
4165 char
4166 page_geometry[MaxTextExtent];
4167
4168 Image
4169 *image,
4170 *previous;
4171
cristy4383ec82011-01-05 15:42:32 +00004172 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004173 logging,
4174 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004175
cristy3ed852e2009-09-05 21:47:34 +00004176 volatile int
4177 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004178 object_id,
4179 term_chunk_found,
4180 skip_to_iend;
4181
cristybb503372010-05-27 20:51:26 +00004182 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004183 image_count=0;
4184
4185 MagickBooleanType
4186 status;
4187
4188 MagickOffsetType
4189 offset;
4190
4191 MngInfo
4192 *mng_info;
4193
4194 MngBox
4195 default_fb,
4196 fb,
4197 previous_fb;
4198
4199#if defined(MNG_INSERT_LAYERS)
4200 PixelPacket
4201 mng_background_color;
4202#endif
4203
4204 register unsigned char
4205 *p;
4206
cristybb503372010-05-27 20:51:26 +00004207 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004208 i;
4209
4210 size_t
4211 count;
4212
cristybb503372010-05-27 20:51:26 +00004213 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004214 loop_level;
4215
4216 volatile short
4217 skipping_loop;
4218
4219#if defined(MNG_INSERT_LAYERS)
4220 unsigned int
4221 mandatory_back=0;
4222#endif
4223
4224 volatile unsigned int
4225#ifdef MNG_OBJECT_BUFFERS
4226 mng_background_object=0,
4227#endif
4228 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4229
cristybb503372010-05-27 20:51:26 +00004230 size_t
cristy3ed852e2009-09-05 21:47:34 +00004231 default_frame_timeout,
4232 frame_timeout,
4233#if defined(MNG_INSERT_LAYERS)
4234 image_height,
4235 image_width,
4236#endif
4237 length;
4238
glennrp38ea0832010-06-02 18:50:28 +00004239 /* These delays are all measured in image ticks_per_second,
4240 * not in MNG ticks_per_second
4241 */
cristybb503372010-05-27 20:51:26 +00004242 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004243 default_frame_delay,
4244 final_delay,
4245 final_image_delay,
4246 frame_delay,
4247#if defined(MNG_INSERT_LAYERS)
4248 insert_layers,
4249#endif
4250 mng_iterations=1,
4251 simplicity=0,
4252 subframe_height=0,
4253 subframe_width=0;
4254
4255 previous_fb.top=0;
4256 previous_fb.bottom=0;
4257 previous_fb.left=0;
4258 previous_fb.right=0;
4259 default_fb.top=0;
4260 default_fb.bottom=0;
4261 default_fb.left=0;
4262 default_fb.right=0;
4263
glennrp47b9dd52010-11-24 18:12:06 +00004264 /* Open image file. */
4265
cristy3ed852e2009-09-05 21:47:34 +00004266 assert(image_info != (const ImageInfo *) NULL);
4267 assert(image_info->signature == MagickSignature);
4268 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4269 assert(exception != (ExceptionInfo *) NULL);
4270 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004271 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004272 image=AcquireImage(image_info);
4273 mng_info=(MngInfo *) NULL;
4274 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004275
cristy3ed852e2009-09-05 21:47:34 +00004276 if (status == MagickFalse)
4277 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004278
cristy3ed852e2009-09-05 21:47:34 +00004279 first_mng_object=MagickFalse;
4280 skipping_loop=(-1);
4281 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004282
4283 /* Allocate a MngInfo structure. */
4284
cristy73bd4a52010-10-05 11:24:23 +00004285 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004286
cristy3ed852e2009-09-05 21:47:34 +00004287 if (mng_info == (MngInfo *) NULL)
4288 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004289
glennrp47b9dd52010-11-24 18:12:06 +00004290 /* Initialize members of the MngInfo structure. */
4291
cristy3ed852e2009-09-05 21:47:34 +00004292 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4293 mng_info->image=image;
4294 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004295
4296 if (LocaleCompare(image_info->magick,"MNG") == 0)
4297 {
4298 char
4299 magic_number[MaxTextExtent];
4300
glennrp47b9dd52010-11-24 18:12:06 +00004301 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004302 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4303 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4304 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004305
4306 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004307 for (i=0; i < MNG_MAX_OBJECTS; i++)
4308 {
cristybb503372010-05-27 20:51:26 +00004309 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4310 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004311 }
4312 mng_info->exists[0]=MagickTrue;
4313 }
glennrp47b9dd52010-11-24 18:12:06 +00004314
cristy3ed852e2009-09-05 21:47:34 +00004315 first_mng_object=MagickTrue;
4316 mng_type=0;
4317#if defined(MNG_INSERT_LAYERS)
4318 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4319#endif
4320 default_frame_delay=0;
4321 default_frame_timeout=0;
4322 frame_delay=0;
4323 final_delay=1;
4324 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4325 object_id=0;
4326 skip_to_iend=MagickFalse;
4327 term_chunk_found=MagickFalse;
4328 mng_info->framing_mode=1;
4329#if defined(MNG_INSERT_LAYERS)
4330 mandatory_back=MagickFalse;
4331#endif
4332#if defined(MNG_INSERT_LAYERS)
4333 mng_background_color=image->background_color;
4334#endif
4335 default_fb=mng_info->frame;
4336 previous_fb=mng_info->frame;
4337 do
4338 {
4339 char
4340 type[MaxTextExtent];
4341
4342 if (LocaleCompare(image_info->magick,"MNG") == 0)
4343 {
4344 unsigned char
4345 *chunk;
4346
4347 /*
4348 Read a new chunk.
4349 */
4350 type[0]='\0';
4351 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4352 length=ReadBlobMSBLong(image);
4353 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4354
4355 if (logging != MagickFalse)
4356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004357 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4358 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004359
4360 if (length > PNG_UINT_31_MAX)
4361 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004362
cristy3ed852e2009-09-05 21:47:34 +00004363 if (count == 0)
4364 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004365
cristy3ed852e2009-09-05 21:47:34 +00004366 p=NULL;
4367 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004368
cristy3ed852e2009-09-05 21:47:34 +00004369 if (length)
4370 {
4371 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004372
cristy3ed852e2009-09-05 21:47:34 +00004373 if (chunk == (unsigned char *) NULL)
4374 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004375
cristybb503372010-05-27 20:51:26 +00004376 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004377 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004378
cristy3ed852e2009-09-05 21:47:34 +00004379 p=chunk;
4380 }
glennrp0fe50b42010-11-16 03:52:51 +00004381
cristy3ed852e2009-09-05 21:47:34 +00004382 (void) ReadBlobMSBLong(image); /* read crc word */
4383
4384#if !defined(JNG_SUPPORTED)
4385 if (memcmp(type,mng_JHDR,4) == 0)
4386 {
4387 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004388
cristy3ed852e2009-09-05 21:47:34 +00004389 if (mng_info->jhdr_warning == 0)
4390 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4391 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004392
cristy3ed852e2009-09-05 21:47:34 +00004393 mng_info->jhdr_warning++;
4394 }
4395#endif
4396 if (memcmp(type,mng_DHDR,4) == 0)
4397 {
4398 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004399
cristy3ed852e2009-09-05 21:47:34 +00004400 if (mng_info->dhdr_warning == 0)
4401 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4402 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004403
cristy3ed852e2009-09-05 21:47:34 +00004404 mng_info->dhdr_warning++;
4405 }
4406 if (memcmp(type,mng_MEND,4) == 0)
4407 break;
glennrp47b9dd52010-11-24 18:12:06 +00004408
cristy3ed852e2009-09-05 21:47:34 +00004409 if (skip_to_iend)
4410 {
4411 if (memcmp(type,mng_IEND,4) == 0)
4412 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004413
cristy3ed852e2009-09-05 21:47:34 +00004414 if (length)
4415 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004416
cristy3ed852e2009-09-05 21:47:34 +00004417 if (logging != MagickFalse)
4418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4419 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004420
cristy3ed852e2009-09-05 21:47:34 +00004421 continue;
4422 }
glennrp0fe50b42010-11-16 03:52:51 +00004423
cristy3ed852e2009-09-05 21:47:34 +00004424 if (memcmp(type,mng_MHDR,4) == 0)
4425 {
cristybb503372010-05-27 20:51:26 +00004426 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004427 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004428
cristybb503372010-05-27 20:51:26 +00004429 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004430 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004431
cristy3ed852e2009-09-05 21:47:34 +00004432 if (logging != MagickFalse)
4433 {
4434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004435 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004437 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004438 }
glennrp0fe50b42010-11-16 03:52:51 +00004439
cristy3ed852e2009-09-05 21:47:34 +00004440 p+=8;
cristy8182b072010-05-30 20:10:53 +00004441 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004442
cristy3ed852e2009-09-05 21:47:34 +00004443 if (mng_info->ticks_per_second == 0)
4444 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004445
cristy3ed852e2009-09-05 21:47:34 +00004446 else
4447 default_frame_delay=1UL*image->ticks_per_second/
4448 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004449
cristy3ed852e2009-09-05 21:47:34 +00004450 frame_delay=default_frame_delay;
4451 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004452
cristy3ed852e2009-09-05 21:47:34 +00004453 if (length > 16)
4454 {
4455 p+=16;
cristy8182b072010-05-30 20:10:53 +00004456 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004457 }
glennrp0fe50b42010-11-16 03:52:51 +00004458
cristy3ed852e2009-09-05 21:47:34 +00004459 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004460
cristy3ed852e2009-09-05 21:47:34 +00004461 if ((simplicity != 0) && ((simplicity | 11) == 11))
4462 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004463
cristy3ed852e2009-09-05 21:47:34 +00004464 if ((simplicity != 0) && ((simplicity | 9) == 9))
4465 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004466
cristy3ed852e2009-09-05 21:47:34 +00004467#if defined(MNG_INSERT_LAYERS)
4468 if (mng_type != 3)
4469 insert_layers=MagickTrue;
4470#endif
4471 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4472 {
glennrp47b9dd52010-11-24 18:12:06 +00004473 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004474 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004475
cristy3ed852e2009-09-05 21:47:34 +00004476 if (GetNextImageInList(image) == (Image *) NULL)
4477 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004478
cristy3ed852e2009-09-05 21:47:34 +00004479 image=SyncNextImageInList(image);
4480 mng_info->image=image;
4481 }
4482
4483 if ((mng_info->mng_width > 65535L) ||
4484 (mng_info->mng_height > 65535L))
4485 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004486
cristye8c25f92010-06-03 00:53:06 +00004487 (void) FormatMagickString(page_geometry,MaxTextExtent,
4488 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004489 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004490
cristy3ed852e2009-09-05 21:47:34 +00004491 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004492 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004493 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004494 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004495 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004496
cristy3ed852e2009-09-05 21:47:34 +00004497 for (i=0; i < MNG_MAX_OBJECTS; i++)
4498 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004499
cristy3ed852e2009-09-05 21:47:34 +00004500 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4501 continue;
4502 }
4503
4504 if (memcmp(type,mng_TERM,4) == 0)
4505 {
4506 int
4507 repeat=0;
4508
4509
4510 if (length)
4511 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004512
cristy3ed852e2009-09-05 21:47:34 +00004513 if (repeat == 3)
4514 {
cristy8182b072010-05-30 20:10:53 +00004515 final_delay=(png_uint_32) mng_get_long(&p[2]);
4516 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004517
cristy3ed852e2009-09-05 21:47:34 +00004518 if (mng_iterations == PNG_UINT_31_MAX)
4519 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004520
cristy3ed852e2009-09-05 21:47:34 +00004521 image->iterations=mng_iterations;
4522 term_chunk_found=MagickTrue;
4523 }
glennrp0fe50b42010-11-16 03:52:51 +00004524
cristy3ed852e2009-09-05 21:47:34 +00004525 if (logging != MagickFalse)
4526 {
4527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4528 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004529
cristy3ed852e2009-09-05 21:47:34 +00004530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004531 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004532
cristy3ed852e2009-09-05 21:47:34 +00004533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004534 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004535 }
glennrp0fe50b42010-11-16 03:52:51 +00004536
cristy3ed852e2009-09-05 21:47:34 +00004537 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4538 continue;
4539 }
4540 if (memcmp(type,mng_DEFI,4) == 0)
4541 {
4542 if (mng_type == 3)
4543 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4544 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4545 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004546
cristy3ed852e2009-09-05 21:47:34 +00004547 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004548
cristy3ed852e2009-09-05 21:47:34 +00004549 if (mng_type == 2 && object_id != 0)
4550 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4551 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4552 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004553
cristy3ed852e2009-09-05 21:47:34 +00004554 if (object_id > MNG_MAX_OBJECTS)
4555 {
4556 /*
4557 Instead ofsuing a warning we should allocate a larger
4558 MngInfo structure and continue.
4559 */
4560 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4561 CoderError,"object id too large","`%s'",image->filename);
4562 object_id=MNG_MAX_OBJECTS;
4563 }
glennrp0fe50b42010-11-16 03:52:51 +00004564
cristy3ed852e2009-09-05 21:47:34 +00004565 if (mng_info->exists[object_id])
4566 if (mng_info->frozen[object_id])
4567 {
4568 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4569 (void) ThrowMagickException(&image->exception,
4570 GetMagickModule(),CoderError,
4571 "DEFI cannot redefine a frozen MNG object","`%s'",
4572 image->filename);
4573 continue;
4574 }
glennrp0fe50b42010-11-16 03:52:51 +00004575
cristy3ed852e2009-09-05 21:47:34 +00004576 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004577
cristy3ed852e2009-09-05 21:47:34 +00004578 if (length > 2)
4579 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 /*
4582 Extract object offset info.
4583 */
4584 if (length > 11)
4585 {
glennrp0fe50b42010-11-16 03:52:51 +00004586 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4587 (p[5] << 16) | (p[6] << 8) | p[7]);
4588
4589 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4590 (p[9] << 16) | (p[10] << 8) | p[11]);
4591
cristy3ed852e2009-09-05 21:47:34 +00004592 if (logging != MagickFalse)
4593 {
4594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004595 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004596 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004597
cristy3ed852e2009-09-05 21:47:34 +00004598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004599 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004600 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004601 }
4602 }
glennrp0fe50b42010-11-16 03:52:51 +00004603
cristy3ed852e2009-09-05 21:47:34 +00004604 /*
4605 Extract object clipping info.
4606 */
4607 if (length > 27)
4608 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4609 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004610
cristy3ed852e2009-09-05 21:47:34 +00004611 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4612 continue;
4613 }
4614 if (memcmp(type,mng_bKGD,4) == 0)
4615 {
4616 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004617
cristy3ed852e2009-09-05 21:47:34 +00004618 if (length > 5)
4619 {
4620 mng_info->mng_global_bkgd.red=
4621 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004622
cristy3ed852e2009-09-05 21:47:34 +00004623 mng_info->mng_global_bkgd.green=
4624 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004625
cristy3ed852e2009-09-05 21:47:34 +00004626 mng_info->mng_global_bkgd.blue=
4627 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004628
cristy3ed852e2009-09-05 21:47:34 +00004629 mng_info->have_global_bkgd=MagickTrue;
4630 }
glennrp0fe50b42010-11-16 03:52:51 +00004631
cristy3ed852e2009-09-05 21:47:34 +00004632 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4633 continue;
4634 }
4635 if (memcmp(type,mng_BACK,4) == 0)
4636 {
4637#if defined(MNG_INSERT_LAYERS)
4638 if (length > 6)
4639 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00004640
cristy3ed852e2009-09-05 21:47:34 +00004641 else
4642 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00004643
cristy3ed852e2009-09-05 21:47:34 +00004644 if (mandatory_back && length > 5)
4645 {
4646 mng_background_color.red=
4647 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004648
cristy3ed852e2009-09-05 21:47:34 +00004649 mng_background_color.green=
4650 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004651
cristy3ed852e2009-09-05 21:47:34 +00004652 mng_background_color.blue=
4653 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004654
cristy3ed852e2009-09-05 21:47:34 +00004655 mng_background_color.opacity=OpaqueOpacity;
4656 }
glennrp0fe50b42010-11-16 03:52:51 +00004657
cristy3ed852e2009-09-05 21:47:34 +00004658#ifdef MNG_OBJECT_BUFFERS
4659 if (length > 8)
4660 mng_background_object=(p[7] << 8) | p[8];
4661#endif
4662#endif
4663 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4664 continue;
4665 }
glennrp47b9dd52010-11-24 18:12:06 +00004666
cristy3ed852e2009-09-05 21:47:34 +00004667 if (memcmp(type,mng_PLTE,4) == 0)
4668 {
glennrp47b9dd52010-11-24 18:12:06 +00004669 /* Read global PLTE. */
4670
cristy3ed852e2009-09-05 21:47:34 +00004671 if (length && (length < 769))
4672 {
4673 if (mng_info->global_plte == (png_colorp) NULL)
4674 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4675 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00004676
cristybb503372010-05-27 20:51:26 +00004677 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004678 {
4679 mng_info->global_plte[i].red=p[3*i];
4680 mng_info->global_plte[i].green=p[3*i+1];
4681 mng_info->global_plte[i].blue=p[3*i+2];
4682 }
glennrp0fe50b42010-11-16 03:52:51 +00004683
cristy35ef8242010-06-03 16:24:13 +00004684 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00004685 }
4686#ifdef MNG_LOOSE
4687 for ( ; i < 256; i++)
4688 {
4689 mng_info->global_plte[i].red=i;
4690 mng_info->global_plte[i].green=i;
4691 mng_info->global_plte[i].blue=i;
4692 }
glennrp0fe50b42010-11-16 03:52:51 +00004693
cristy3ed852e2009-09-05 21:47:34 +00004694 if (length)
4695 mng_info->global_plte_length=256;
4696#endif
4697 else
4698 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00004699
cristy3ed852e2009-09-05 21:47:34 +00004700 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4701 continue;
4702 }
glennrp47b9dd52010-11-24 18:12:06 +00004703
cristy3ed852e2009-09-05 21:47:34 +00004704 if (memcmp(type,mng_tRNS,4) == 0)
4705 {
4706 /* read global tRNS */
4707
4708 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004709 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004710 mng_info->global_trns[i]=p[i];
4711
4712#ifdef MNG_LOOSE
4713 for ( ; i < 256; i++)
4714 mng_info->global_trns[i]=255;
4715#endif
cristy12560f32010-06-03 16:51:08 +00004716 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00004717 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4718 continue;
4719 }
4720 if (memcmp(type,mng_gAMA,4) == 0)
4721 {
4722 if (length == 4)
4723 {
cristybb503372010-05-27 20:51:26 +00004724 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004725 igamma;
4726
cristy8182b072010-05-30 20:10:53 +00004727 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004728 mng_info->global_gamma=((float) igamma)*0.00001;
4729 mng_info->have_global_gama=MagickTrue;
4730 }
glennrp0fe50b42010-11-16 03:52:51 +00004731
cristy3ed852e2009-09-05 21:47:34 +00004732 else
4733 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004734
cristy3ed852e2009-09-05 21:47:34 +00004735 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4736 continue;
4737 }
4738
4739 if (memcmp(type,mng_cHRM,4) == 0)
4740 {
glennrp47b9dd52010-11-24 18:12:06 +00004741 /* Read global cHRM */
4742
cristy3ed852e2009-09-05 21:47:34 +00004743 if (length == 32)
4744 {
cristy8182b072010-05-30 20:10:53 +00004745 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4746 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4747 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004748 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004749 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004750 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004751 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004752 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004753 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004754 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004755 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004756 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004757 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004758 mng_info->have_global_chrm=MagickTrue;
4759 }
4760 else
4761 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004762
cristy3ed852e2009-09-05 21:47:34 +00004763 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4764 continue;
4765 }
glennrp47b9dd52010-11-24 18:12:06 +00004766
cristy3ed852e2009-09-05 21:47:34 +00004767 if (memcmp(type,mng_sRGB,4) == 0)
4768 {
4769 /*
4770 Read global sRGB.
4771 */
4772 if (length)
4773 {
glennrpe610a072010-08-05 17:08:46 +00004774 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00004775 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004776 mng_info->have_global_srgb=MagickTrue;
4777 }
4778 else
4779 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004780
cristy3ed852e2009-09-05 21:47:34 +00004781 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4782 continue;
4783 }
glennrp47b9dd52010-11-24 18:12:06 +00004784
cristy3ed852e2009-09-05 21:47:34 +00004785 if (memcmp(type,mng_iCCP,4) == 0)
4786 {
glennrpfd05d622011-02-25 04:10:33 +00004787 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004788
4789 /*
4790 Read global iCCP.
4791 */
4792 if (length)
4793 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004794
cristy3ed852e2009-09-05 21:47:34 +00004795 continue;
4796 }
glennrp47b9dd52010-11-24 18:12:06 +00004797
cristy3ed852e2009-09-05 21:47:34 +00004798 if (memcmp(type,mng_FRAM,4) == 0)
4799 {
4800 if (mng_type == 3)
4801 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4802 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4803 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004804
cristy3ed852e2009-09-05 21:47:34 +00004805 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4806 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004807
cristy3ed852e2009-09-05 21:47:34 +00004808 frame_delay=default_frame_delay;
4809 frame_timeout=default_frame_timeout;
4810 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00004811
cristy3ed852e2009-09-05 21:47:34 +00004812 if (length)
4813 if (p[0])
4814 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004815
cristy3ed852e2009-09-05 21:47:34 +00004816 if (logging != MagickFalse)
4817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4818 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00004819
cristy3ed852e2009-09-05 21:47:34 +00004820 if (length > 6)
4821 {
glennrp47b9dd52010-11-24 18:12:06 +00004822 /* Note the delay and frame clipping boundaries. */
4823
cristy3ed852e2009-09-05 21:47:34 +00004824 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00004825
cristybb503372010-05-27 20:51:26 +00004826 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004827 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00004830
cristybb503372010-05-27 20:51:26 +00004831 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004832 {
4833 int
4834 change_delay,
4835 change_timeout,
4836 change_clipping;
4837
4838 change_delay=(*p++);
4839 change_timeout=(*p++);
4840 change_clipping=(*p++);
4841 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00004842
cristy3ed852e2009-09-05 21:47:34 +00004843 if (change_delay)
4844 {
cristy8182b072010-05-30 20:10:53 +00004845 frame_delay=1UL*image->ticks_per_second*
4846 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy8182b072010-05-30 20:10:53 +00004848 if (mng_info->ticks_per_second != 0)
4849 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004850
glennrpbb010dd2010-06-01 13:07:15 +00004851 else
4852 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854 if (change_delay == 2)
4855 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004856
cristy3ed852e2009-09-05 21:47:34 +00004857 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if (logging != MagickFalse)
4860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004861 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004862 }
glennrp47b9dd52010-11-24 18:12:06 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 if (change_timeout)
4865 {
glennrpbb010dd2010-06-01 13:07:15 +00004866 frame_timeout=1UL*image->ticks_per_second*
4867 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004868
glennrpbb010dd2010-06-01 13:07:15 +00004869 if (mng_info->ticks_per_second != 0)
4870 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004871
glennrpbb010dd2010-06-01 13:07:15 +00004872 else
4873 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004874
cristy3ed852e2009-09-05 21:47:34 +00004875 if (change_delay == 2)
4876 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristy3ed852e2009-09-05 21:47:34 +00004878 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004879
cristy3ed852e2009-09-05 21:47:34 +00004880 if (logging != MagickFalse)
4881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004882 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004883 }
glennrp47b9dd52010-11-24 18:12:06 +00004884
cristy3ed852e2009-09-05 21:47:34 +00004885 if (change_clipping)
4886 {
4887 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4888 p+=17;
4889 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00004890
cristy3ed852e2009-09-05 21:47:34 +00004891 if (logging != MagickFalse)
4892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004893 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004894 (double) fb.left,(double) fb.right,(double) fb.top,
4895 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 if (change_clipping == 2)
4898 default_fb=fb;
4899 }
4900 }
4901 }
4902 mng_info->clip=fb;
4903 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00004904
cristybb503372010-05-27 20:51:26 +00004905 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004906 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00004907
cristybb503372010-05-27 20:51:26 +00004908 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004909 -mng_info->clip.top);
4910 /*
4911 Insert a background layer behind the frame if framing_mode is 4.
4912 */
4913#if defined(MNG_INSERT_LAYERS)
4914 if (logging != MagickFalse)
4915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004916 " subframe_width=%.20g, subframe_height=%.20g",(double)
4917 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00004918
cristy3ed852e2009-09-05 21:47:34 +00004919 if (insert_layers && (mng_info->framing_mode == 4) &&
4920 (subframe_width) && (subframe_height))
4921 {
glennrp47b9dd52010-11-24 18:12:06 +00004922 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004923 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4924 {
4925 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00004926
cristy3ed852e2009-09-05 21:47:34 +00004927 if (GetNextImageInList(image) == (Image *) NULL)
4928 {
4929 image=DestroyImageList(image);
4930 MngInfoFreeStruct(mng_info,&have_mng_structure);
4931 return((Image *) NULL);
4932 }
glennrp47b9dd52010-11-24 18:12:06 +00004933
cristy3ed852e2009-09-05 21:47:34 +00004934 image=SyncNextImageInList(image);
4935 }
glennrp0fe50b42010-11-16 03:52:51 +00004936
cristy3ed852e2009-09-05 21:47:34 +00004937 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00004938
cristy3ed852e2009-09-05 21:47:34 +00004939 if (term_chunk_found)
4940 {
4941 image->start_loop=MagickTrue;
4942 image->iterations=mng_iterations;
4943 term_chunk_found=MagickFalse;
4944 }
glennrp0fe50b42010-11-16 03:52:51 +00004945
cristy3ed852e2009-09-05 21:47:34 +00004946 else
4947 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004948
cristy3ed852e2009-09-05 21:47:34 +00004949 image->columns=subframe_width;
4950 image->rows=subframe_height;
4951 image->page.width=subframe_width;
4952 image->page.height=subframe_height;
4953 image->page.x=mng_info->clip.left;
4954 image->page.y=mng_info->clip.top;
4955 image->background_color=mng_background_color;
4956 image->matte=MagickFalse;
4957 image->delay=0;
4958 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00004959
cristy3ed852e2009-09-05 21:47:34 +00004960 if (logging != MagickFalse)
4961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004962 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004963 (double) mng_info->clip.left,(double) mng_info->clip.right,
4964 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004965 }
4966#endif
4967 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4968 continue;
4969 }
4970 if (memcmp(type,mng_CLIP,4) == 0)
4971 {
4972 unsigned int
4973 first_object,
4974 last_object;
4975
4976 /*
4977 Read CLIP.
4978 */
4979 first_object=(p[0] << 8) | p[1];
4980 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 for (i=(int) first_object; i <= (int) last_object; i++)
4983 {
4984 if (mng_info->exists[i] && !mng_info->frozen[i])
4985 {
4986 MngBox
4987 box;
4988
4989 box=mng_info->object_clip[i];
4990 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4991 }
4992 }
glennrp47b9dd52010-11-24 18:12:06 +00004993
cristy3ed852e2009-09-05 21:47:34 +00004994 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4995 continue;
4996 }
4997 if (memcmp(type,mng_SAVE,4) == 0)
4998 {
4999 for (i=1; i < MNG_MAX_OBJECTS; i++)
5000 if (mng_info->exists[i])
5001 {
5002 mng_info->frozen[i]=MagickTrue;
5003#ifdef MNG_OBJECT_BUFFERS
5004 if (mng_info->ob[i] != (MngBuffer *) NULL)
5005 mng_info->ob[i]->frozen=MagickTrue;
5006#endif
5007 }
glennrp0fe50b42010-11-16 03:52:51 +00005008
cristy3ed852e2009-09-05 21:47:34 +00005009 if (length)
5010 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005011
cristy3ed852e2009-09-05 21:47:34 +00005012 continue;
5013 }
5014
5015 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5016 {
glennrp47b9dd52010-11-24 18:12:06 +00005017 /* Read DISC or SEEK. */
5018
cristy3ed852e2009-09-05 21:47:34 +00005019 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5020 {
5021 for (i=1; i < MNG_MAX_OBJECTS; i++)
5022 MngInfoDiscardObject(mng_info,i);
5023 }
glennrp0fe50b42010-11-16 03:52:51 +00005024
cristy3ed852e2009-09-05 21:47:34 +00005025 else
5026 {
cristybb503372010-05-27 20:51:26 +00005027 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005028 j;
5029
cristybb503372010-05-27 20:51:26 +00005030 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005031 {
5032 i=p[j] << 8 | p[j+1];
5033 MngInfoDiscardObject(mng_info,i);
5034 }
5035 }
glennrp0fe50b42010-11-16 03:52:51 +00005036
cristy3ed852e2009-09-05 21:47:34 +00005037 if (length)
5038 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005039
cristy3ed852e2009-09-05 21:47:34 +00005040 continue;
5041 }
glennrp47b9dd52010-11-24 18:12:06 +00005042
cristy3ed852e2009-09-05 21:47:34 +00005043 if (memcmp(type,mng_MOVE,4) == 0)
5044 {
cristybb503372010-05-27 20:51:26 +00005045 size_t
cristy3ed852e2009-09-05 21:47:34 +00005046 first_object,
5047 last_object;
5048
glennrp47b9dd52010-11-24 18:12:06 +00005049 /* read MOVE */
5050
cristy3ed852e2009-09-05 21:47:34 +00005051 first_object=(p[0] << 8) | p[1];
5052 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005053 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005054 {
5055 if (mng_info->exists[i] && !mng_info->frozen[i])
5056 {
5057 MngPair
5058 new_pair;
5059
5060 MngPair
5061 old_pair;
5062
5063 old_pair.a=mng_info->x_off[i];
5064 old_pair.b=mng_info->y_off[i];
5065 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5066 mng_info->x_off[i]=new_pair.a;
5067 mng_info->y_off[i]=new_pair.b;
5068 }
5069 }
glennrp47b9dd52010-11-24 18:12:06 +00005070
cristy3ed852e2009-09-05 21:47:34 +00005071 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5072 continue;
5073 }
5074
5075 if (memcmp(type,mng_LOOP,4) == 0)
5076 {
cristybb503372010-05-27 20:51:26 +00005077 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005078 loop_level=chunk[0];
5079 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005080
5081 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005082 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005083
cristy3ed852e2009-09-05 21:47:34 +00005084 if (logging != MagickFalse)
5085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005086 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5087 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005088
cristy3ed852e2009-09-05 21:47:34 +00005089 if (loop_iters == 0)
5090 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005091
cristy3ed852e2009-09-05 21:47:34 +00005092 else
5093 {
5094 mng_info->loop_jump[loop_level]=TellBlob(image);
5095 mng_info->loop_count[loop_level]=loop_iters;
5096 }
glennrp0fe50b42010-11-16 03:52:51 +00005097
cristy3ed852e2009-09-05 21:47:34 +00005098 mng_info->loop_iteration[loop_level]=0;
5099 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5100 continue;
5101 }
glennrp47b9dd52010-11-24 18:12:06 +00005102
cristy3ed852e2009-09-05 21:47:34 +00005103 if (memcmp(type,mng_ENDL,4) == 0)
5104 {
5105 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005106
cristy3ed852e2009-09-05 21:47:34 +00005107 if (skipping_loop > 0)
5108 {
5109 if (skipping_loop == loop_level)
5110 {
5111 /*
5112 Found end of zero-iteration loop.
5113 */
5114 skipping_loop=(-1);
5115 mng_info->loop_active[loop_level]=0;
5116 }
5117 }
glennrp47b9dd52010-11-24 18:12:06 +00005118
cristy3ed852e2009-09-05 21:47:34 +00005119 else
5120 {
5121 if (mng_info->loop_active[loop_level] == 1)
5122 {
5123 mng_info->loop_count[loop_level]--;
5124 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005125
cristy3ed852e2009-09-05 21:47:34 +00005126 if (logging != MagickFalse)
5127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005128 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005129 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005130 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005131
cristy3ed852e2009-09-05 21:47:34 +00005132 if (mng_info->loop_count[loop_level] != 0)
5133 {
5134 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5135 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005136
cristy3ed852e2009-09-05 21:47:34 +00005137 if (offset < 0)
5138 ThrowReaderException(CorruptImageError,
5139 "ImproperImageHeader");
5140 }
glennrp47b9dd52010-11-24 18:12:06 +00005141
cristy3ed852e2009-09-05 21:47:34 +00005142 else
5143 {
5144 short
5145 last_level;
5146
5147 /*
5148 Finished loop.
5149 */
5150 mng_info->loop_active[loop_level]=0;
5151 last_level=(-1);
5152 for (i=0; i < loop_level; i++)
5153 if (mng_info->loop_active[i] == 1)
5154 last_level=(short) i;
5155 loop_level=last_level;
5156 }
5157 }
5158 }
glennrp47b9dd52010-11-24 18:12:06 +00005159
cristy3ed852e2009-09-05 21:47:34 +00005160 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5161 continue;
5162 }
glennrp47b9dd52010-11-24 18:12:06 +00005163
cristy3ed852e2009-09-05 21:47:34 +00005164 if (memcmp(type,mng_CLON,4) == 0)
5165 {
5166 if (mng_info->clon_warning == 0)
5167 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5168 CoderError,"CLON is not implemented yet","`%s'",
5169 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005170
cristy3ed852e2009-09-05 21:47:34 +00005171 mng_info->clon_warning++;
5172 }
glennrp47b9dd52010-11-24 18:12:06 +00005173
cristy3ed852e2009-09-05 21:47:34 +00005174 if (memcmp(type,mng_MAGN,4) == 0)
5175 {
5176 png_uint_16
5177 magn_first,
5178 magn_last,
5179 magn_mb,
5180 magn_ml,
5181 magn_mr,
5182 magn_mt,
5183 magn_mx,
5184 magn_my,
5185 magn_methx,
5186 magn_methy;
5187
5188 if (length > 1)
5189 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005190
cristy3ed852e2009-09-05 21:47:34 +00005191 else
5192 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005193
cristy3ed852e2009-09-05 21:47:34 +00005194 if (length > 3)
5195 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005196
cristy3ed852e2009-09-05 21:47:34 +00005197 else
5198 magn_last=magn_first;
5199#ifndef MNG_OBJECT_BUFFERS
5200 if (magn_first || magn_last)
5201 if (mng_info->magn_warning == 0)
5202 {
5203 (void) ThrowMagickException(&image->exception,
5204 GetMagickModule(),CoderError,
5205 "MAGN is not implemented yet for nonzero objects",
5206 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005207
cristy3ed852e2009-09-05 21:47:34 +00005208 mng_info->magn_warning++;
5209 }
5210#endif
5211 if (length > 4)
5212 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 else
5215 magn_methx=0;
5216
5217 if (length > 6)
5218 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005219
cristy3ed852e2009-09-05 21:47:34 +00005220 else
5221 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005222
cristy3ed852e2009-09-05 21:47:34 +00005223 if (magn_mx == 0)
5224 magn_mx=1;
5225
5226 if (length > 8)
5227 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005228
cristy3ed852e2009-09-05 21:47:34 +00005229 else
5230 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005231
cristy3ed852e2009-09-05 21:47:34 +00005232 if (magn_my == 0)
5233 magn_my=1;
5234
5235 if (length > 10)
5236 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005237
cristy3ed852e2009-09-05 21:47:34 +00005238 else
5239 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005240
cristy3ed852e2009-09-05 21:47:34 +00005241 if (magn_ml == 0)
5242 magn_ml=1;
5243
5244 if (length > 12)
5245 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005246
cristy3ed852e2009-09-05 21:47:34 +00005247 else
5248 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005249
cristy3ed852e2009-09-05 21:47:34 +00005250 if (magn_mr == 0)
5251 magn_mr=1;
5252
5253 if (length > 14)
5254 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005255
cristy3ed852e2009-09-05 21:47:34 +00005256 else
5257 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005258
cristy3ed852e2009-09-05 21:47:34 +00005259 if (magn_mt == 0)
5260 magn_mt=1;
5261
5262 if (length > 16)
5263 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005264
cristy3ed852e2009-09-05 21:47:34 +00005265 else
5266 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005267
cristy3ed852e2009-09-05 21:47:34 +00005268 if (magn_mb == 0)
5269 magn_mb=1;
5270
5271 if (length > 17)
5272 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005273
cristy3ed852e2009-09-05 21:47:34 +00005274 else
5275 magn_methy=magn_methx;
5276
glennrp47b9dd52010-11-24 18:12:06 +00005277
cristy3ed852e2009-09-05 21:47:34 +00005278 if (magn_methx > 5 || magn_methy > 5)
5279 if (mng_info->magn_warning == 0)
5280 {
5281 (void) ThrowMagickException(&image->exception,
5282 GetMagickModule(),CoderError,
5283 "Unknown MAGN method in MNG datastream","`%s'",
5284 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005285
cristy3ed852e2009-09-05 21:47:34 +00005286 mng_info->magn_warning++;
5287 }
5288#ifdef MNG_OBJECT_BUFFERS
5289 /* Magnify existing objects in the range magn_first to magn_last */
5290#endif
5291 if (magn_first == 0 || magn_last == 0)
5292 {
5293 /* Save the magnification factors for object 0 */
5294 mng_info->magn_mb=magn_mb;
5295 mng_info->magn_ml=magn_ml;
5296 mng_info->magn_mr=magn_mr;
5297 mng_info->magn_mt=magn_mt;
5298 mng_info->magn_mx=magn_mx;
5299 mng_info->magn_my=magn_my;
5300 mng_info->magn_methx=magn_methx;
5301 mng_info->magn_methy=magn_methy;
5302 }
5303 }
glennrp47b9dd52010-11-24 18:12:06 +00005304
cristy3ed852e2009-09-05 21:47:34 +00005305 if (memcmp(type,mng_PAST,4) == 0)
5306 {
5307 if (mng_info->past_warning == 0)
5308 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5309 CoderError,"PAST is not implemented yet","`%s'",
5310 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005311
cristy3ed852e2009-09-05 21:47:34 +00005312 mng_info->past_warning++;
5313 }
glennrp47b9dd52010-11-24 18:12:06 +00005314
cristy3ed852e2009-09-05 21:47:34 +00005315 if (memcmp(type,mng_SHOW,4) == 0)
5316 {
5317 if (mng_info->show_warning == 0)
5318 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5319 CoderError,"SHOW is not implemented yet","`%s'",
5320 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005321
cristy3ed852e2009-09-05 21:47:34 +00005322 mng_info->show_warning++;
5323 }
glennrp47b9dd52010-11-24 18:12:06 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 if (memcmp(type,mng_sBIT,4) == 0)
5326 {
5327 if (length < 4)
5328 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 else
5331 {
5332 mng_info->global_sbit.gray=p[0];
5333 mng_info->global_sbit.red=p[0];
5334 mng_info->global_sbit.green=p[1];
5335 mng_info->global_sbit.blue=p[2];
5336 mng_info->global_sbit.alpha=p[3];
5337 mng_info->have_global_sbit=MagickTrue;
5338 }
5339 }
5340 if (memcmp(type,mng_pHYs,4) == 0)
5341 {
5342 if (length > 8)
5343 {
5344 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005345 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005346 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005347 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005348 mng_info->global_phys_unit_type=p[8];
5349 mng_info->have_global_phys=MagickTrue;
5350 }
glennrp47b9dd52010-11-24 18:12:06 +00005351
cristy3ed852e2009-09-05 21:47:34 +00005352 else
5353 mng_info->have_global_phys=MagickFalse;
5354 }
5355 if (memcmp(type,mng_pHYg,4) == 0)
5356 {
5357 if (mng_info->phyg_warning == 0)
5358 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5359 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005360
cristy3ed852e2009-09-05 21:47:34 +00005361 mng_info->phyg_warning++;
5362 }
5363 if (memcmp(type,mng_BASI,4) == 0)
5364 {
5365 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005366
cristy3ed852e2009-09-05 21:47:34 +00005367 if (mng_info->basi_warning == 0)
5368 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5369 CoderError,"BASI is not implemented yet","`%s'",
5370 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005371
cristy3ed852e2009-09-05 21:47:34 +00005372 mng_info->basi_warning++;
5373#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005374 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005375 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005376 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005377 (p[6] << 8) | p[7]);
5378 basi_color_type=p[8];
5379 basi_compression_method=p[9];
5380 basi_filter_type=p[10];
5381 basi_interlace_method=p[11];
5382 if (length > 11)
5383 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005384
cristy3ed852e2009-09-05 21:47:34 +00005385 else
5386 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005387
cristy3ed852e2009-09-05 21:47:34 +00005388 if (length > 13)
5389 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005390
cristy3ed852e2009-09-05 21:47:34 +00005391 else
5392 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005393
cristy3ed852e2009-09-05 21:47:34 +00005394 if (length > 15)
5395 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005396
cristy3ed852e2009-09-05 21:47:34 +00005397 else
5398 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005399
cristy3ed852e2009-09-05 21:47:34 +00005400 if (length > 17)
5401 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005402
cristy3ed852e2009-09-05 21:47:34 +00005403 else
5404 {
5405 if (basi_sample_depth == 16)
5406 basi_alpha=65535L;
5407 else
5408 basi_alpha=255;
5409 }
glennrp47b9dd52010-11-24 18:12:06 +00005410
cristy3ed852e2009-09-05 21:47:34 +00005411 if (length > 19)
5412 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005413
cristy3ed852e2009-09-05 21:47:34 +00005414 else
5415 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005416
cristy3ed852e2009-09-05 21:47:34 +00005417#endif
5418 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5419 continue;
5420 }
glennrp47b9dd52010-11-24 18:12:06 +00005421
cristy3ed852e2009-09-05 21:47:34 +00005422 if (memcmp(type,mng_IHDR,4)
5423#if defined(JNG_SUPPORTED)
5424 && memcmp(type,mng_JHDR,4)
5425#endif
5426 )
5427 {
5428 /* Not an IHDR or JHDR chunk */
5429 if (length)
5430 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005431
cristy3ed852e2009-09-05 21:47:34 +00005432 continue;
5433 }
5434/* Process IHDR */
5435 if (logging != MagickFalse)
5436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5437 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005438
cristy3ed852e2009-09-05 21:47:34 +00005439 mng_info->exists[object_id]=MagickTrue;
5440 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005441
cristy3ed852e2009-09-05 21:47:34 +00005442 if (mng_info->invisible[object_id])
5443 {
5444 if (logging != MagickFalse)
5445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5446 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005447
cristy3ed852e2009-09-05 21:47:34 +00005448 skip_to_iend=MagickTrue;
5449 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5450 continue;
5451 }
5452#if defined(MNG_INSERT_LAYERS)
5453 if (length < 8)
5454 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005455
cristy8182b072010-05-30 20:10:53 +00005456 image_width=(size_t) mng_get_long(p);
5457 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005458#endif
5459 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5460
5461 /*
5462 Insert a transparent background layer behind the entire animation
5463 if it is not full screen.
5464 */
5465#if defined(MNG_INSERT_LAYERS)
5466 if (insert_layers && mng_type && first_mng_object)
5467 {
5468 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5469 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005470 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005471 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005472 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005473 {
5474 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5475 {
5476 /*
5477 Allocate next image structure.
5478 */
5479 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005480
cristy3ed852e2009-09-05 21:47:34 +00005481 if (GetNextImageInList(image) == (Image *) NULL)
5482 {
5483 image=DestroyImageList(image);
5484 MngInfoFreeStruct(mng_info,&have_mng_structure);
5485 return((Image *) NULL);
5486 }
glennrp47b9dd52010-11-24 18:12:06 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 image=SyncNextImageInList(image);
5489 }
5490 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005491
cristy3ed852e2009-09-05 21:47:34 +00005492 if (term_chunk_found)
5493 {
5494 image->start_loop=MagickTrue;
5495 image->iterations=mng_iterations;
5496 term_chunk_found=MagickFalse;
5497 }
glennrp47b9dd52010-11-24 18:12:06 +00005498
cristy3ed852e2009-09-05 21:47:34 +00005499 else
5500 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005501
5502 /* Make a background rectangle. */
5503
cristy3ed852e2009-09-05 21:47:34 +00005504 image->delay=0;
5505 image->columns=mng_info->mng_width;
5506 image->rows=mng_info->mng_height;
5507 image->page.width=mng_info->mng_width;
5508 image->page.height=mng_info->mng_height;
5509 image->page.x=0;
5510 image->page.y=0;
5511 image->background_color=mng_background_color;
5512 (void) SetImageBackgroundColor(image);
5513 if (logging != MagickFalse)
5514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005515 " Inserted transparent background layer, W=%.20g, H=%.20g",
5516 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005517 }
5518 }
5519 /*
5520 Insert a background layer behind the upcoming image if
5521 framing_mode is 3, and we haven't already inserted one.
5522 */
5523 if (insert_layers && (mng_info->framing_mode == 3) &&
5524 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5525 (simplicity & 0x08)))
5526 {
5527 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5528 {
5529 /*
5530 Allocate next image structure.
5531 */
5532 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005533
cristy3ed852e2009-09-05 21:47:34 +00005534 if (GetNextImageInList(image) == (Image *) NULL)
5535 {
5536 image=DestroyImageList(image);
5537 MngInfoFreeStruct(mng_info,&have_mng_structure);
5538 return((Image *) NULL);
5539 }
glennrp47b9dd52010-11-24 18:12:06 +00005540
cristy3ed852e2009-09-05 21:47:34 +00005541 image=SyncNextImageInList(image);
5542 }
glennrp0fe50b42010-11-16 03:52:51 +00005543
cristy3ed852e2009-09-05 21:47:34 +00005544 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005545
cristy3ed852e2009-09-05 21:47:34 +00005546 if (term_chunk_found)
5547 {
5548 image->start_loop=MagickTrue;
5549 image->iterations=mng_iterations;
5550 term_chunk_found=MagickFalse;
5551 }
glennrp0fe50b42010-11-16 03:52:51 +00005552
cristy3ed852e2009-09-05 21:47:34 +00005553 else
5554 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005555
cristy3ed852e2009-09-05 21:47:34 +00005556 image->delay=0;
5557 image->columns=subframe_width;
5558 image->rows=subframe_height;
5559 image->page.width=subframe_width;
5560 image->page.height=subframe_height;
5561 image->page.x=mng_info->clip.left;
5562 image->page.y=mng_info->clip.top;
5563 image->background_color=mng_background_color;
5564 image->matte=MagickFalse;
5565 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005566
cristy3ed852e2009-09-05 21:47:34 +00005567 if (logging != MagickFalse)
5568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005569 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005570 (double) mng_info->clip.left,(double) mng_info->clip.right,
5571 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005572 }
5573#endif /* MNG_INSERT_LAYERS */
5574 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5577 {
5578 /*
5579 Allocate next image structure.
5580 */
5581 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005582
cristy3ed852e2009-09-05 21:47:34 +00005583 if (GetNextImageInList(image) == (Image *) NULL)
5584 {
5585 image=DestroyImageList(image);
5586 MngInfoFreeStruct(mng_info,&have_mng_structure);
5587 return((Image *) NULL);
5588 }
glennrp47b9dd52010-11-24 18:12:06 +00005589
cristy3ed852e2009-09-05 21:47:34 +00005590 image=SyncNextImageInList(image);
5591 }
5592 mng_info->image=image;
5593 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5594 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005595
cristy3ed852e2009-09-05 21:47:34 +00005596 if (status == MagickFalse)
5597 break;
glennrp0fe50b42010-11-16 03:52:51 +00005598
cristy3ed852e2009-09-05 21:47:34 +00005599 if (term_chunk_found)
5600 {
5601 image->start_loop=MagickTrue;
5602 term_chunk_found=MagickFalse;
5603 }
glennrp0fe50b42010-11-16 03:52:51 +00005604
cristy3ed852e2009-09-05 21:47:34 +00005605 else
5606 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005607
cristy3ed852e2009-09-05 21:47:34 +00005608 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5609 {
5610 image->delay=frame_delay;
5611 frame_delay=default_frame_delay;
5612 }
glennrp0fe50b42010-11-16 03:52:51 +00005613
cristy3ed852e2009-09-05 21:47:34 +00005614 else
5615 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005616
cristy3ed852e2009-09-05 21:47:34 +00005617 image->page.width=mng_info->mng_width;
5618 image->page.height=mng_info->mng_height;
5619 image->page.x=mng_info->x_off[object_id];
5620 image->page.y=mng_info->y_off[object_id];
5621 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00005622
cristy3ed852e2009-09-05 21:47:34 +00005623 /*
5624 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5625 */
glennrp47b9dd52010-11-24 18:12:06 +00005626
cristy3ed852e2009-09-05 21:47:34 +00005627 if (logging != MagickFalse)
5628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5629 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5630 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005631
cristybb503372010-05-27 20:51:26 +00005632 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00005633
cristy3ed852e2009-09-05 21:47:34 +00005634 if (offset < 0)
5635 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5636 }
5637
5638 previous=image;
5639 mng_info->image=image;
5640 mng_info->mng_type=mng_type;
5641 mng_info->object_id=object_id;
5642
5643 if (memcmp(type,mng_IHDR,4) == 0)
5644 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005645
cristy3ed852e2009-09-05 21:47:34 +00005646#if defined(JNG_SUPPORTED)
5647 else
5648 image=ReadOneJNGImage(mng_info,image_info,exception);
5649#endif
5650
5651 if (image == (Image *) NULL)
5652 {
5653 if (IsImageObject(previous) != MagickFalse)
5654 {
5655 (void) DestroyImageList(previous);
5656 (void) CloseBlob(previous);
5657 }
glennrp47b9dd52010-11-24 18:12:06 +00005658
cristy3ed852e2009-09-05 21:47:34 +00005659 MngInfoFreeStruct(mng_info,&have_mng_structure);
5660 return((Image *) NULL);
5661 }
glennrp0fe50b42010-11-16 03:52:51 +00005662
cristy3ed852e2009-09-05 21:47:34 +00005663 if (image->columns == 0 || image->rows == 0)
5664 {
5665 (void) CloseBlob(image);
5666 image=DestroyImageList(image);
5667 MngInfoFreeStruct(mng_info,&have_mng_structure);
5668 return((Image *) NULL);
5669 }
glennrp0fe50b42010-11-16 03:52:51 +00005670
cristy3ed852e2009-09-05 21:47:34 +00005671 mng_info->image=image;
5672
5673 if (mng_type)
5674 {
5675 MngBox
5676 crop_box;
5677
5678 if (mng_info->magn_methx || mng_info->magn_methy)
5679 {
5680 png_uint_32
5681 magnified_height,
5682 magnified_width;
5683
5684 if (logging != MagickFalse)
5685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5686 " Processing MNG MAGN chunk");
5687
5688 if (mng_info->magn_methx == 1)
5689 {
5690 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 if (image->columns > 1)
5693 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005694
cristy3ed852e2009-09-05 21:47:34 +00005695 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005696 magnified_width += (png_uint_32)
5697 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00005698 }
glennrp47b9dd52010-11-24 18:12:06 +00005699
cristy3ed852e2009-09-05 21:47:34 +00005700 else
5701 {
cristy4e5bc842010-06-09 13:56:01 +00005702 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00005703
cristy3ed852e2009-09-05 21:47:34 +00005704 if (image->columns > 1)
5705 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00005706
cristy3ed852e2009-09-05 21:47:34 +00005707 if (image->columns > 2)
5708 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005711 magnified_width += (png_uint_32)
5712 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00005713 }
glennrp47b9dd52010-11-24 18:12:06 +00005714
cristy3ed852e2009-09-05 21:47:34 +00005715 if (mng_info->magn_methy == 1)
5716 {
5717 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005718
cristy3ed852e2009-09-05 21:47:34 +00005719 if (image->rows > 1)
5720 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005721
cristy3ed852e2009-09-05 21:47:34 +00005722 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005723 magnified_height += (png_uint_32)
5724 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00005725 }
glennrp47b9dd52010-11-24 18:12:06 +00005726
cristy3ed852e2009-09-05 21:47:34 +00005727 else
5728 {
cristy4e5bc842010-06-09 13:56:01 +00005729 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00005730
cristy3ed852e2009-09-05 21:47:34 +00005731 if (image->rows > 1)
5732 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00005733
cristy3ed852e2009-09-05 21:47:34 +00005734 if (image->rows > 2)
5735 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00005736
cristy3ed852e2009-09-05 21:47:34 +00005737 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005738 magnified_height += (png_uint_32)
5739 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00005740 }
glennrp47b9dd52010-11-24 18:12:06 +00005741
cristy3ed852e2009-09-05 21:47:34 +00005742 if (magnified_height > image->rows ||
5743 magnified_width > image->columns)
5744 {
5745 Image
5746 *large_image;
5747
5748 int
5749 yy;
5750
cristybb503372010-05-27 20:51:26 +00005751 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005752 m,
5753 y;
5754
cristybb503372010-05-27 20:51:26 +00005755 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005756 x;
5757
5758 register PixelPacket
5759 *n,
5760 *q;
5761
5762 PixelPacket
5763 *next,
5764 *prev;
5765
5766 png_uint_16
5767 magn_methx,
5768 magn_methy;
5769
glennrp47b9dd52010-11-24 18:12:06 +00005770 /* Allocate next image structure. */
5771
cristy3ed852e2009-09-05 21:47:34 +00005772 if (logging != MagickFalse)
5773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5774 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00005775
cristy3ed852e2009-09-05 21:47:34 +00005776 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005777
cristy3ed852e2009-09-05 21:47:34 +00005778 if (GetNextImageInList(image) == (Image *) NULL)
5779 {
5780 image=DestroyImageList(image);
5781 MngInfoFreeStruct(mng_info,&have_mng_structure);
5782 return((Image *) NULL);
5783 }
5784
5785 large_image=SyncNextImageInList(image);
5786
5787 large_image->columns=magnified_width;
5788 large_image->rows=magnified_height;
5789
5790 magn_methx=mng_info->magn_methx;
5791 magn_methy=mng_info->magn_methy;
5792
glennrp3faa9a32011-04-23 14:00:25 +00005793#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00005794#define QM unsigned short
5795 if (magn_methx != 1 || magn_methy != 1)
5796 {
5797 /*
5798 Scale pixels to unsigned shorts to prevent
5799 overflow of intermediate values of interpolations
5800 */
cristybb503372010-05-27 20:51:26 +00005801 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005802 {
5803 q=GetAuthenticPixels(image,0,y,image->columns,1,
5804 exception);
glennrp47b9dd52010-11-24 18:12:06 +00005805
cristybb503372010-05-27 20:51:26 +00005806 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005807 {
glennrp7c7b3152011-04-26 04:01:27 +00005808 SetRedPixelComponent(q,ScaleQuantumToShort(
5809 GetRedPixelComponent(q));
5810 SetGreenPixelComponent(q,ScaleQuantumToShort(
5811 GetGreenPixelComponent(q));
5812 SetBluePixelComponent(q,ScaleQuantumToShort(
5813 GetBluePixelComponent(q));
5814 SetOpacityPixelComponent(q,ScaleQuantumToShort(
5815 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00005816 q++;
5817 }
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5820 break;
5821 }
5822 }
5823#else
5824#define QM Quantum
5825#endif
5826
5827 if (image->matte != MagickFalse)
5828 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005829
cristy3ed852e2009-09-05 21:47:34 +00005830 else
5831 {
5832 large_image->background_color.opacity=OpaqueOpacity;
5833 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 if (magn_methx == 4)
5836 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 if (magn_methx == 5)
5839 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy3ed852e2009-09-05 21:47:34 +00005841 if (magn_methy == 4)
5842 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 if (magn_methy == 5)
5845 magn_methy=3;
5846 }
5847
5848 /* magnify the rows into the right side of the large image */
5849
5850 if (logging != MagickFalse)
5851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005852 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00005853 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005854 yy=0;
5855 length=(size_t) image->columns;
5856 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5857 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00005858
cristy3ed852e2009-09-05 21:47:34 +00005859 if ((prev == (PixelPacket *) NULL) ||
5860 (next == (PixelPacket *) NULL))
5861 {
5862 image=DestroyImageList(image);
5863 MngInfoFreeStruct(mng_info,&have_mng_structure);
5864 ThrowReaderException(ResourceLimitError,
5865 "MemoryAllocationFailed");
5866 }
glennrp47b9dd52010-11-24 18:12:06 +00005867
cristy3ed852e2009-09-05 21:47:34 +00005868 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5869 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00005870
cristybb503372010-05-27 20:51:26 +00005871 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005872 {
5873 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005874 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005875
cristybb503372010-05-27 20:51:26 +00005876 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5877 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005878
cristybb503372010-05-27 20:51:26 +00005879 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5880 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005881
cristybb503372010-05-27 20:51:26 +00005882 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005883 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005884
cristy3ed852e2009-09-05 21:47:34 +00005885 else
cristybb503372010-05-27 20:51:26 +00005886 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005887
cristy3ed852e2009-09-05 21:47:34 +00005888 n=prev;
5889 prev=next;
5890 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00005891
cristybb503372010-05-27 20:51:26 +00005892 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005893 {
5894 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5895 exception);
5896 (void) CopyMagickMemory(next,n,length);
5897 }
glennrp47b9dd52010-11-24 18:12:06 +00005898
cristy3ed852e2009-09-05 21:47:34 +00005899 for (i=0; i < m; i++, yy++)
5900 {
glennrp7c7b3152011-04-26 04:01:27 +00005901 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00005902 register PixelPacket
5903 *pixels;
5904
cristybb503372010-05-27 20:51:26 +00005905 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005906 pixels=prev;
5907 n=next;
5908 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00005909 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00005910 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00005911
cristybb503372010-05-27 20:51:26 +00005912 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005913 {
glennrpfd05d622011-02-25 04:10:33 +00005914 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005915 /*
5916 if (image->storage_class == PseudoClass)
5917 {
5918 }
5919 */
5920
5921 if (magn_methy <= 1)
5922 {
5923 *q=(*pixels); /* replicate previous */
5924 }
glennrp47b9dd52010-11-24 18:12:06 +00005925
cristy3ed852e2009-09-05 21:47:34 +00005926 else if (magn_methy == 2 || magn_methy == 4)
5927 {
5928 if (i == 0)
5929 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005930
cristy3ed852e2009-09-05 21:47:34 +00005931 else
5932 {
5933 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005934 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5935 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005936 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005937 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5938 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005939 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005940 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5941 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005942 +(*pixels).blue);
glennrp47b9dd52010-11-24 18:12:06 +00005943
cristy3ed852e2009-09-05 21:47:34 +00005944 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005945 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005946 (2*i*((*n).opacity
5947 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005948 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005949 }
glennrp47b9dd52010-11-24 18:12:06 +00005950
cristy3ed852e2009-09-05 21:47:34 +00005951 if (magn_methy == 4)
5952 {
5953 /* Replicate nearest */
5954 if (i <= ((m+1) << 1))
5955 (*q).opacity=(*pixels).opacity+0;
5956 else
5957 (*q).opacity=(*n).opacity+0;
5958 }
5959 }
glennrp47b9dd52010-11-24 18:12:06 +00005960
cristy3ed852e2009-09-05 21:47:34 +00005961 else /* if (magn_methy == 3 || magn_methy == 5) */
5962 {
5963 /* Replicate nearest */
5964 if (i <= ((m+1) << 1))
5965 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005966
cristy3ed852e2009-09-05 21:47:34 +00005967 else
5968 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00005969
cristy3ed852e2009-09-05 21:47:34 +00005970 if (magn_methy == 5)
5971 {
cristybb503372010-05-27 20:51:26 +00005972 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5973 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005974 +(*pixels).opacity);
5975 }
5976 }
5977 n++;
5978 q++;
5979 pixels++;
5980 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00005981
cristy3ed852e2009-09-05 21:47:34 +00005982 if (SyncAuthenticPixels(large_image,exception) == 0)
5983 break;
glennrp47b9dd52010-11-24 18:12:06 +00005984
cristy3ed852e2009-09-05 21:47:34 +00005985 } /* i */
5986 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00005987
cristy3ed852e2009-09-05 21:47:34 +00005988 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5989 next=(PixelPacket *) RelinquishMagickMemory(next);
5990
5991 length=image->columns;
5992
5993 if (logging != MagickFalse)
5994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5995 " Delete original image");
5996
5997 DeleteImageFromList(&image);
5998
5999 image=large_image;
6000
6001 mng_info->image=image;
6002
6003 /* magnify the columns */
6004 if (logging != MagickFalse)
6005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006006 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006007
cristybb503372010-05-27 20:51:26 +00006008 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006009 {
6010 register PixelPacket
6011 *pixels;
6012
6013 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp4737d522011-04-29 03:33:42 +00006014 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00006015 pixels=q+(image->columns-length);
6016 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00006017
cristybb503372010-05-27 20:51:26 +00006018 for (x=(ssize_t) (image->columns-length);
6019 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006020 {
glennrp7c7b3152011-04-26 04:01:27 +00006021 /* To do: Rewrite using Get/Set***PixelComponent() */
6022
cristybb503372010-05-27 20:51:26 +00006023 if (x == (ssize_t) (image->columns-length))
6024 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006025
cristybb503372010-05-27 20:51:26 +00006026 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6027 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006028
cristybb503372010-05-27 20:51:26 +00006029 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6030 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006031
cristybb503372010-05-27 20:51:26 +00006032 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006033 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006034
cristy3ed852e2009-09-05 21:47:34 +00006035 else
cristybb503372010-05-27 20:51:26 +00006036 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006037
cristy3ed852e2009-09-05 21:47:34 +00006038 for (i=0; i < m; i++)
6039 {
6040 if (magn_methx <= 1)
6041 {
6042 /* replicate previous */
glennrp4737d522011-04-29 03:33:42 +00006043 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00006044 *q=(*pixels);
6045 }
glennrp47b9dd52010-11-24 18:12:06 +00006046
cristy3ed852e2009-09-05 21:47:34 +00006047 else if (magn_methx == 2 || magn_methx == 4)
6048 {
6049 if (i == 0)
6050 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006051
cristy3ed852e2009-09-05 21:47:34 +00006052 else
6053 {
6054 /* Interpolate */
6055 (*q).red=(QM) ((2*i*((*n).red
6056 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00006057 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00006058 (*q).green=(QM) ((2*i*((*n).green
6059 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00006060 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00006061 (*q).blue=(QM) ((2*i*((*n).blue
6062 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00006063 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00006064 if (image->matte != MagickFalse)
6065 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006066 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006067 +(*pixels).opacity);
6068 }
glennrp47b9dd52010-11-24 18:12:06 +00006069
cristy3ed852e2009-09-05 21:47:34 +00006070 if (magn_methx == 4)
6071 {
6072 /* Replicate nearest */
6073 if (i <= ((m+1) << 1))
6074 (*q).opacity=(*pixels).opacity+0;
6075 else
6076 (*q).opacity=(*n).opacity+0;
6077 }
6078 }
glennrp47b9dd52010-11-24 18:12:06 +00006079
cristy3ed852e2009-09-05 21:47:34 +00006080 else /* if (magn_methx == 3 || magn_methx == 5) */
6081 {
6082 /* Replicate nearest */
6083 if (i <= ((m+1) << 1))
6084 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006085
cristy3ed852e2009-09-05 21:47:34 +00006086 else
6087 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00006088
cristy3ed852e2009-09-05 21:47:34 +00006089 if (magn_methx == 5)
6090 {
6091 /* Interpolate */
6092 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006093 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006094 +(*pixels).opacity);
6095 }
6096 }
6097 q++;
6098 }
6099 n++;
6100 p++;
6101 }
glennrp47b9dd52010-11-24 18:12:06 +00006102
cristy3ed852e2009-09-05 21:47:34 +00006103 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6104 break;
6105 }
glennrp3faa9a32011-04-23 14:00:25 +00006106#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006107 if (magn_methx != 1 || magn_methy != 1)
6108 {
6109 /*
6110 Rescale pixels to Quantum
6111 */
cristybb503372010-05-27 20:51:26 +00006112 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006113 {
6114 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006115
cristybb503372010-05-27 20:51:26 +00006116 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006117 {
glennrp7c7b3152011-04-26 04:01:27 +00006118 SetRedPixelComponent(q,ScaleShortToQuantum(
6119 GetRedPixelComponent(q));
6120 SetGreenPixelComponent(q,ScaleShortToQuantum(
6121 GetGreenPixelComponent(q));
6122 SetBluePixelComponent(q,ScaleShortToQuantum(
6123 GetBluePixelComponent(q));
6124 SetOpacityPixelComponent(q,ScaleShortToQuantum(
6125 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00006126 q++;
6127 }
glennrp47b9dd52010-11-24 18:12:06 +00006128
cristy3ed852e2009-09-05 21:47:34 +00006129 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6130 break;
6131 }
6132 }
6133#endif
6134 if (logging != MagickFalse)
6135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6136 " Finished MAGN processing");
6137 }
6138 }
6139
6140 /*
6141 Crop_box is with respect to the upper left corner of the MNG.
6142 */
6143 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6144 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6145 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6146 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6147 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6148 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6149 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6150 if ((crop_box.left != (mng_info->image_box.left
6151 +mng_info->x_off[object_id])) ||
6152 (crop_box.right != (mng_info->image_box.right
6153 +mng_info->x_off[object_id])) ||
6154 (crop_box.top != (mng_info->image_box.top
6155 +mng_info->y_off[object_id])) ||
6156 (crop_box.bottom != (mng_info->image_box.bottom
6157 +mng_info->y_off[object_id])))
6158 {
6159 if (logging != MagickFalse)
6160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6161 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006162
cristy3ed852e2009-09-05 21:47:34 +00006163 if ((crop_box.left < crop_box.right) &&
6164 (crop_box.top < crop_box.bottom))
6165 {
6166 Image
6167 *im;
6168
6169 RectangleInfo
6170 crop_info;
6171
6172 /*
6173 Crop_info is with respect to the upper left corner of
6174 the image.
6175 */
6176 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6177 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006178 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6179 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006180 image->page.width=image->columns;
6181 image->page.height=image->rows;
6182 image->page.x=0;
6183 image->page.y=0;
6184 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006185
cristy3ed852e2009-09-05 21:47:34 +00006186 if (im != (Image *) NULL)
6187 {
6188 image->columns=im->columns;
6189 image->rows=im->rows;
6190 im=DestroyImage(im);
6191 image->page.width=image->columns;
6192 image->page.height=image->rows;
6193 image->page.x=crop_box.left;
6194 image->page.y=crop_box.top;
6195 }
6196 }
glennrp47b9dd52010-11-24 18:12:06 +00006197
cristy3ed852e2009-09-05 21:47:34 +00006198 else
6199 {
6200 /*
6201 No pixels in crop area. The MNG spec still requires
6202 a layer, though, so make a single transparent pixel in
6203 the top left corner.
6204 */
6205 image->columns=1;
6206 image->rows=1;
6207 image->colors=2;
6208 (void) SetImageBackgroundColor(image);
6209 image->page.width=1;
6210 image->page.height=1;
6211 image->page.x=0;
6212 image->page.y=0;
6213 }
6214 }
6215#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6216 image=mng_info->image;
6217#endif
6218 }
6219
glennrp2b013e42010-11-24 16:55:50 +00006220#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6221 /* PNG does not handle depths greater than 16 so reduce it even
6222 * if lossy
6223 */
6224 if (image->depth > 16)
6225 image->depth=16;
6226#endif
6227
glennrp3faa9a32011-04-23 14:00:25 +00006228#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006229 if (LosslessReduceDepthOK(image) != MagickFalse)
6230 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006231#endif
glennrpd6afd542010-11-19 01:53:05 +00006232
cristy3ed852e2009-09-05 21:47:34 +00006233 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 if (image_info->number_scenes != 0)
6236 {
6237 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006238 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006239 break;
6240 }
glennrpd6afd542010-11-19 01:53:05 +00006241
cristy3ed852e2009-09-05 21:47:34 +00006242 if (logging != MagickFalse)
6243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6244 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006245
cristy3ed852e2009-09-05 21:47:34 +00006246 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006247
cristy3ed852e2009-09-05 21:47:34 +00006248 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006249
cristy3ed852e2009-09-05 21:47:34 +00006250 if (logging != MagickFalse)
6251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6252 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006253
cristy3ed852e2009-09-05 21:47:34 +00006254#if defined(MNG_INSERT_LAYERS)
6255 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6256 (mng_info->mng_height))
6257 {
6258 /*
6259 Insert a background layer if nothing else was found.
6260 */
6261 if (logging != MagickFalse)
6262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6263 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006264
cristy3ed852e2009-09-05 21:47:34 +00006265 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6266 {
6267 /*
6268 Allocate next image structure.
6269 */
6270 AcquireNextImage(image_info,image);
6271 if (GetNextImageInList(image) == (Image *) NULL)
6272 {
6273 image=DestroyImageList(image);
6274 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristy3ed852e2009-09-05 21:47:34 +00006276 if (logging != MagickFalse)
6277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6278 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006279
cristy3ed852e2009-09-05 21:47:34 +00006280 return((Image *) NULL);
6281 }
6282 image=SyncNextImageInList(image);
6283 }
6284 image->columns=mng_info->mng_width;
6285 image->rows=mng_info->mng_height;
6286 image->page.width=mng_info->mng_width;
6287 image->page.height=mng_info->mng_height;
6288 image->page.x=0;
6289 image->page.y=0;
6290 image->background_color=mng_background_color;
6291 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006292
cristy3ed852e2009-09-05 21:47:34 +00006293 if (image_info->ping == MagickFalse)
6294 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006295
cristy3ed852e2009-09-05 21:47:34 +00006296 mng_info->image_found++;
6297 }
6298#endif
6299 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006300
cristy3ed852e2009-09-05 21:47:34 +00006301 if (mng_iterations == 1)
6302 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006303
cristy3ed852e2009-09-05 21:47:34 +00006304 while (GetPreviousImageInList(image) != (Image *) NULL)
6305 {
6306 image_count++;
6307 if (image_count > 10*mng_info->image_found)
6308 {
6309 if (logging != MagickFalse)
6310 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006311
cristy3ed852e2009-09-05 21:47:34 +00006312 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6313 CoderError,"Linked list is corrupted, beginning of list not found",
6314 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristy3ed852e2009-09-05 21:47:34 +00006316 return((Image *) NULL);
6317 }
glennrp0fe50b42010-11-16 03:52:51 +00006318
cristy3ed852e2009-09-05 21:47:34 +00006319 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006320
cristy3ed852e2009-09-05 21:47:34 +00006321 if (GetNextImageInList(image) == (Image *) NULL)
6322 {
6323 if (logging != MagickFalse)
6324 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006325
cristy3ed852e2009-09-05 21:47:34 +00006326 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6327 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6328 image_info->filename);
6329 }
6330 }
glennrp47b9dd52010-11-24 18:12:06 +00006331
cristy3ed852e2009-09-05 21:47:34 +00006332 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6333 GetNextImageInList(image) ==
6334 (Image *) NULL)
6335 {
6336 if (logging != MagickFalse)
6337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6338 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006339
cristy3ed852e2009-09-05 21:47:34 +00006340 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6341 CoderError,"image->next for first image is NULL but shouldn't be.",
6342 "`%s'",image_info->filename);
6343 }
glennrp47b9dd52010-11-24 18:12:06 +00006344
cristy3ed852e2009-09-05 21:47:34 +00006345 if (mng_info->image_found == 0)
6346 {
6347 if (logging != MagickFalse)
6348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6349 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006350
cristy3ed852e2009-09-05 21:47:34 +00006351 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6352 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006353
cristy3ed852e2009-09-05 21:47:34 +00006354 if (image != (Image *) NULL)
6355 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006356
cristy3ed852e2009-09-05 21:47:34 +00006357 MngInfoFreeStruct(mng_info,&have_mng_structure);
6358 return((Image *) NULL);
6359 }
6360
6361 if (mng_info->ticks_per_second)
6362 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6363 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006364
cristy3ed852e2009-09-05 21:47:34 +00006365 else
6366 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006367
cristy3ed852e2009-09-05 21:47:34 +00006368 /* Find final nonzero image delay */
6369 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006370
cristy3ed852e2009-09-05 21:47:34 +00006371 while (GetNextImageInList(image) != (Image *) NULL)
6372 {
6373 if (image->delay)
6374 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006375
cristy3ed852e2009-09-05 21:47:34 +00006376 image=GetNextImageInList(image);
6377 }
glennrp0fe50b42010-11-16 03:52:51 +00006378
cristy3ed852e2009-09-05 21:47:34 +00006379 if (final_delay < final_image_delay)
6380 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006381
cristy3ed852e2009-09-05 21:47:34 +00006382 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006383
cristy3ed852e2009-09-05 21:47:34 +00006384 if (logging != MagickFalse)
6385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006386 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6387 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006388
cristy3ed852e2009-09-05 21:47:34 +00006389 if (logging != MagickFalse)
6390 {
6391 int
6392 scene;
6393
6394 scene=0;
6395 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006396
cristy3ed852e2009-09-05 21:47:34 +00006397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6398 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006399
cristy3ed852e2009-09-05 21:47:34 +00006400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006401 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006402
cristy3ed852e2009-09-05 21:47:34 +00006403 while (GetNextImageInList(image) != (Image *) NULL)
6404 {
6405 image=GetNextImageInList(image);
6406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006407 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006408 }
6409 }
6410
6411 image=GetFirstImageInList(image);
6412#ifdef MNG_COALESCE_LAYERS
6413 if (insert_layers)
6414 {
6415 Image
6416 *next_image,
6417 *next;
6418
cristybb503372010-05-27 20:51:26 +00006419 size_t
cristy3ed852e2009-09-05 21:47:34 +00006420 scene;
6421
6422 if (logging != MagickFalse)
6423 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006424
cristy3ed852e2009-09-05 21:47:34 +00006425 scene=image->scene;
6426 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006427
cristy3ed852e2009-09-05 21:47:34 +00006428 if (next_image == (Image *) NULL)
6429 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristy3ed852e2009-09-05 21:47:34 +00006431 image=DestroyImageList(image);
6432 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006433
cristy3ed852e2009-09-05 21:47:34 +00006434 for (next=image; next != (Image *) NULL; next=next_image)
6435 {
6436 next->page.width=mng_info->mng_width;
6437 next->page.height=mng_info->mng_height;
6438 next->page.x=0;
6439 next->page.y=0;
6440 next->scene=scene++;
6441 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006442
cristy3ed852e2009-09-05 21:47:34 +00006443 if (next_image == (Image *) NULL)
6444 break;
glennrp47b9dd52010-11-24 18:12:06 +00006445
cristy3ed852e2009-09-05 21:47:34 +00006446 if (next->delay == 0)
6447 {
6448 scene--;
6449 next_image->previous=GetPreviousImageInList(next);
6450 if (GetPreviousImageInList(next) == (Image *) NULL)
6451 image=next_image;
6452 else
6453 next->previous->next=next_image;
6454 next=DestroyImage(next);
6455 }
6456 }
6457 }
6458#endif
6459
6460 while (GetNextImageInList(image) != (Image *) NULL)
6461 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006462
cristy3ed852e2009-09-05 21:47:34 +00006463 image->dispose=BackgroundDispose;
6464
6465 if (logging != MagickFalse)
6466 {
6467 int
6468 scene;
6469
6470 scene=0;
6471 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006472
cristy3ed852e2009-09-05 21:47:34 +00006473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6474 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristy3ed852e2009-09-05 21:47:34 +00006476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006477 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6478 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006479
cristy3ed852e2009-09-05 21:47:34 +00006480 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006481 {
6482 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006483
cristyf2faecf2010-05-28 19:19:36 +00006484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006485 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6486 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006487 }
6488 }
glennrp47b9dd52010-11-24 18:12:06 +00006489
cristy3ed852e2009-09-05 21:47:34 +00006490 image=GetFirstImageInList(image);
6491 MngInfoFreeStruct(mng_info,&have_mng_structure);
6492 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006493
cristy3ed852e2009-09-05 21:47:34 +00006494 if (logging != MagickFalse)
6495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006496
cristy3ed852e2009-09-05 21:47:34 +00006497 return(GetFirstImageInList(image));
6498}
glennrp25c1e2b2010-03-25 01:39:56 +00006499#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006500static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6501{
6502 printf("Your PNG library is too old: You have libpng-%s\n",
6503 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006504
cristy3ed852e2009-09-05 21:47:34 +00006505 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6506 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006507
cristy3ed852e2009-09-05 21:47:34 +00006508 return(Image *) NULL;
6509}
glennrp47b9dd52010-11-24 18:12:06 +00006510
cristy3ed852e2009-09-05 21:47:34 +00006511static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6512{
6513 return(ReadPNGImage(image_info,exception));
6514}
glennrp25c1e2b2010-03-25 01:39:56 +00006515#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006516#endif
6517
6518/*
6519%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6520% %
6521% %
6522% %
6523% R e g i s t e r P N G I m a g e %
6524% %
6525% %
6526% %
6527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6528%
6529% RegisterPNGImage() adds properties for the PNG image format to
6530% the list of supported formats. The properties include the image format
6531% tag, a method to read and/or write the format, whether the format
6532% supports the saving of more than one frame to the same file or blob,
6533% whether the format supports native in-memory I/O, and a brief
6534% description of the format.
6535%
6536% The format of the RegisterPNGImage method is:
6537%
cristybb503372010-05-27 20:51:26 +00006538% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006539%
6540*/
cristybb503372010-05-27 20:51:26 +00006541ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006542{
6543 char
6544 version[MaxTextExtent];
6545
6546 MagickInfo
6547 *entry;
6548
6549 static const char
6550 *PNGNote=
6551 {
6552 "See http://www.libpng.org/ for details about the PNG format."
6553 },
glennrp47b9dd52010-11-24 18:12:06 +00006554
cristy3ed852e2009-09-05 21:47:34 +00006555 *JNGNote=
6556 {
6557 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6558 "format."
6559 },
glennrp47b9dd52010-11-24 18:12:06 +00006560
cristy3ed852e2009-09-05 21:47:34 +00006561 *MNGNote=
6562 {
6563 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6564 "format."
6565 };
6566
6567 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristy3ed852e2009-09-05 21:47:34 +00006569#if defined(PNG_LIBPNG_VER_STRING)
6570 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6571 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006572
cristy3ed852e2009-09-05 21:47:34 +00006573 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6574 {
6575 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6576 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6577 MaxTextExtent);
6578 }
6579#endif
glennrp47b9dd52010-11-24 18:12:06 +00006580
cristy3ed852e2009-09-05 21:47:34 +00006581 entry=SetMagickInfo("MNG");
6582 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00006583
cristy3ed852e2009-09-05 21:47:34 +00006584#if defined(MAGICKCORE_PNG_DELEGATE)
6585 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6586 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6587#endif
glennrp47b9dd52010-11-24 18:12:06 +00006588
cristy3ed852e2009-09-05 21:47:34 +00006589 entry->magick=(IsImageFormatHandler *) IsMNG;
6590 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00006591
cristy3ed852e2009-09-05 21:47:34 +00006592 if (*version != '\0')
6593 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006594
cristy3ed852e2009-09-05 21:47:34 +00006595 entry->module=ConstantString("PNG");
6596 entry->note=ConstantString(MNGNote);
6597 (void) RegisterMagickInfo(entry);
6598
6599 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006600
cristy3ed852e2009-09-05 21:47:34 +00006601#if defined(MAGICKCORE_PNG_DELEGATE)
6602 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6603 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6604#endif
glennrp47b9dd52010-11-24 18:12:06 +00006605
cristy3ed852e2009-09-05 21:47:34 +00006606 entry->magick=(IsImageFormatHandler *) IsPNG;
6607 entry->adjoin=MagickFalse;
6608 entry->description=ConstantString("Portable Network Graphics");
6609 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006610
cristy3ed852e2009-09-05 21:47:34 +00006611 if (*version != '\0')
6612 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006613
cristy3ed852e2009-09-05 21:47:34 +00006614 entry->note=ConstantString(PNGNote);
6615 (void) RegisterMagickInfo(entry);
6616
6617 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00006618
cristy3ed852e2009-09-05 21:47:34 +00006619#if defined(MAGICKCORE_PNG_DELEGATE)
6620 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6621 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6622#endif
glennrp47b9dd52010-11-24 18:12:06 +00006623
cristy3ed852e2009-09-05 21:47:34 +00006624 entry->magick=(IsImageFormatHandler *) IsPNG;
6625 entry->adjoin=MagickFalse;
6626 entry->description=ConstantString(
6627 "8-bit indexed with optional binary transparency");
6628 entry->module=ConstantString("PNG");
6629 (void) RegisterMagickInfo(entry);
6630
6631 entry=SetMagickInfo("PNG24");
6632 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006633
cristy3ed852e2009-09-05 21:47:34 +00006634#if defined(ZLIB_VERSION)
6635 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6636 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006637
cristy3ed852e2009-09-05 21:47:34 +00006638 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6639 {
6640 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6641 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6642 }
6643#endif
glennrp47b9dd52010-11-24 18:12:06 +00006644
cristy3ed852e2009-09-05 21:47:34 +00006645 if (*version != '\0')
6646 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006647
cristy3ed852e2009-09-05 21:47:34 +00006648#if defined(MAGICKCORE_PNG_DELEGATE)
6649 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6650 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6651#endif
glennrp47b9dd52010-11-24 18:12:06 +00006652
cristy3ed852e2009-09-05 21:47:34 +00006653 entry->magick=(IsImageFormatHandler *) IsPNG;
6654 entry->adjoin=MagickFalse;
6655 entry->description=ConstantString("opaque 24-bit RGB");
6656 entry->module=ConstantString("PNG");
6657 (void) RegisterMagickInfo(entry);
6658
6659 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00006660
cristy3ed852e2009-09-05 21:47:34 +00006661#if defined(MAGICKCORE_PNG_DELEGATE)
6662 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6663 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6664#endif
glennrp47b9dd52010-11-24 18:12:06 +00006665
cristy3ed852e2009-09-05 21:47:34 +00006666 entry->magick=(IsImageFormatHandler *) IsPNG;
6667 entry->adjoin=MagickFalse;
6668 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6669 entry->module=ConstantString("PNG");
6670 (void) RegisterMagickInfo(entry);
6671
6672 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006673
cristy3ed852e2009-09-05 21:47:34 +00006674#if defined(JNG_SUPPORTED)
6675#if defined(MAGICKCORE_PNG_DELEGATE)
6676 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6677 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6678#endif
6679#endif
glennrp47b9dd52010-11-24 18:12:06 +00006680
cristy3ed852e2009-09-05 21:47:34 +00006681 entry->magick=(IsImageFormatHandler *) IsJNG;
6682 entry->adjoin=MagickFalse;
6683 entry->description=ConstantString("JPEG Network Graphics");
6684 entry->module=ConstantString("PNG");
6685 entry->note=ConstantString(JNGNote);
6686 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00006687
cristy18b17442009-10-25 18:36:48 +00006688#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006689 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00006690#endif
glennrp47b9dd52010-11-24 18:12:06 +00006691
cristy3ed852e2009-09-05 21:47:34 +00006692 return(MagickImageCoderSignature);
6693}
6694
6695/*
6696%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6697% %
6698% %
6699% %
6700% U n r e g i s t e r P N G I m a g e %
6701% %
6702% %
6703% %
6704%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6705%
6706% UnregisterPNGImage() removes format registrations made by the
6707% PNG module from the list of supported formats.
6708%
6709% The format of the UnregisterPNGImage method is:
6710%
6711% UnregisterPNGImage(void)
6712%
6713*/
6714ModuleExport void UnregisterPNGImage(void)
6715{
6716 (void) UnregisterMagickInfo("MNG");
6717 (void) UnregisterMagickInfo("PNG");
6718 (void) UnregisterMagickInfo("PNG8");
6719 (void) UnregisterMagickInfo("PNG24");
6720 (void) UnregisterMagickInfo("PNG32");
6721 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006722
cristy3ed852e2009-09-05 21:47:34 +00006723#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006724 if (ping_semaphore != (SemaphoreInfo *) NULL)
6725 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006726#endif
6727}
6728
6729#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006730#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006731/*
6732%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6733% %
6734% %
6735% %
6736% W r i t e M N G I m a g e %
6737% %
6738% %
6739% %
6740%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6741%
6742% WriteMNGImage() writes an image in the Portable Network Graphics
6743% Group's "Multiple-image Network Graphics" encoded image format.
6744%
6745% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6746%
6747% The format of the WriteMNGImage method is:
6748%
6749% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6750%
6751% A description of each parameter follows.
6752%
6753% o image_info: the image info.
6754%
6755% o image: The image.
6756%
6757%
6758% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6759% "To do" under ReadPNGImage):
6760%
cristy3ed852e2009-09-05 21:47:34 +00006761% Preserve all unknown and not-yet-handled known chunks found in input
6762% PNG file and copy them into output PNG files according to the PNG
6763% copying rules.
6764%
6765% Write the iCCP chunk at MNG level when (icc profile length > 0)
6766%
6767% Improve selection of color type (use indexed-colour or indexed-colour
6768% with tRNS when 256 or fewer unique RGBA values are present).
6769%
6770% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6771% This will be complicated if we limit ourselves to generating MNG-LC
6772% files. For now we ignore disposal method 3 and simply overlay the next
6773% image on it.
6774%
6775% Check for identical PLTE's or PLTE/tRNS combinations and use a
6776% global MNG PLTE or PLTE/tRNS combination when appropriate.
6777% [mostly done 15 June 1999 but still need to take care of tRNS]
6778%
6779% Check for identical sRGB and replace with a global sRGB (and remove
6780% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6781% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6782% local gAMA/cHRM with local sRGB if appropriate).
6783%
6784% Check for identical sBIT chunks and write global ones.
6785%
6786% Provide option to skip writing the signature tEXt chunks.
6787%
6788% Use signatures to detect identical objects and reuse the first
6789% instance of such objects instead of writing duplicate objects.
6790%
6791% Use a smaller-than-32k value of compression window size when
6792% appropriate.
6793%
6794% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6795% ancillary text chunks and save profiles.
6796%
6797% Provide an option to force LC files (to ensure exact framing rate)
6798% instead of VLC.
6799%
6800% Provide an option to force VLC files instead of LC, even when offsets
6801% are present. This will involve expanding the embedded images with a
6802% transparent region at the top and/or left.
6803*/
6804
cristy3ed852e2009-09-05 21:47:34 +00006805static void
glennrpcf002022011-01-30 02:38:15 +00006806Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00006807 png_info *ping_info, unsigned char *profile_type, unsigned char
6808 *profile_description, unsigned char *profile_data, png_uint_32 length)
6809{
cristy3ed852e2009-09-05 21:47:34 +00006810 png_textp
6811 text;
6812
cristybb503372010-05-27 20:51:26 +00006813 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006814 i;
6815
6816 unsigned char
6817 *sp;
6818
6819 png_charp
6820 dp;
6821
6822 png_uint_32
6823 allocated_length,
6824 description_length;
6825
6826 unsigned char
6827 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006828
cristy3ed852e2009-09-05 21:47:34 +00006829 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6830 return;
6831
6832 if (image_info->verbose)
6833 {
glennrp0fe50b42010-11-16 03:52:51 +00006834 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6835 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006836 }
glennrp0fe50b42010-11-16 03:52:51 +00006837
cristy3ed852e2009-09-05 21:47:34 +00006838 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6839 description_length=(png_uint_32) strlen((const char *) profile_description);
6840 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6841 + description_length);
6842 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6843 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6844 text[0].key[0]='\0';
6845 (void) ConcatenateMagickString(text[0].key,
6846 "Raw profile type ",MaxTextExtent);
6847 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6848 sp=profile_data;
6849 dp=text[0].text;
6850 *dp++='\n';
6851 (void) CopyMagickString(dp,(const char *) profile_description,
6852 allocated_length);
6853 dp+=description_length;
6854 *dp++='\n';
6855 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006856 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006857 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00006858
cristybb503372010-05-27 20:51:26 +00006859 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006860 {
6861 if (i%36 == 0)
6862 *dp++='\n';
6863 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6864 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6865 }
glennrp47b9dd52010-11-24 18:12:06 +00006866
cristy3ed852e2009-09-05 21:47:34 +00006867 *dp++='\n';
6868 *dp='\0';
6869 text[0].text_length=(png_size_t) (dp-text[0].text);
6870 text[0].compression=image_info->compression == NoCompression ||
6871 (image_info->compression == UndefinedCompression &&
6872 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00006873
cristy3ed852e2009-09-05 21:47:34 +00006874 if (text[0].text_length <= allocated_length)
6875 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00006876
cristy3ed852e2009-09-05 21:47:34 +00006877 png_free(ping,text[0].text);
6878 png_free(ping,text[0].key);
6879 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006880}
6881
glennrpcf002022011-01-30 02:38:15 +00006882static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00006883 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00006884{
6885 char
6886 *name;
6887
6888 const StringInfo
6889 *profile;
6890
6891 unsigned char
6892 *data;
6893
6894 png_uint_32 length;
6895
6896 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00006897
6898 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6899 {
cristy3ed852e2009-09-05 21:47:34 +00006900 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00006901
cristy3ed852e2009-09-05 21:47:34 +00006902 if (profile != (const StringInfo *) NULL)
6903 {
6904 StringInfo
glennrpcf002022011-01-30 02:38:15 +00006905 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00006906
glennrp47b9dd52010-11-24 18:12:06 +00006907 if (LocaleNCompare(name,string,11) == 0)
6908 {
6909 if (logging != MagickFalse)
6910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6911 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00006912
glennrpcf002022011-01-30 02:38:15 +00006913 ping_profile=CloneStringInfo(profile);
6914 data=GetStringInfoDatum(ping_profile),
6915 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006916 data[4]=data[3];
6917 data[3]=data[2];
6918 data[2]=data[1];
6919 data[1]=data[0];
6920 (void) WriteBlobMSBULong(image,length-5); /* data length */
6921 (void) WriteBlob(image,length-1,data+1);
6922 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00006923 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006924 }
cristy3ed852e2009-09-05 21:47:34 +00006925 }
glennrp47b9dd52010-11-24 18:12:06 +00006926
cristy3ed852e2009-09-05 21:47:34 +00006927 name=GetNextImageProfile(image);
6928 }
glennrp47b9dd52010-11-24 18:12:06 +00006929
cristy3ed852e2009-09-05 21:47:34 +00006930 return(MagickTrue);
6931}
6932
glennrpb9cfe272010-12-21 15:08:06 +00006933
cristy3ed852e2009-09-05 21:47:34 +00006934/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00006935static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6936 const ImageInfo *IMimage_info,Image *IMimage)
6937{
6938 Image
6939 *image;
6940
6941 ImageInfo
6942 *image_info;
6943
cristy3ed852e2009-09-05 21:47:34 +00006944 char
6945 s[2];
6946
6947 const char
6948 *name,
6949 *property,
6950 *value;
6951
6952 const StringInfo
6953 *profile;
6954
cristy3ed852e2009-09-05 21:47:34 +00006955 int
cristy3ed852e2009-09-05 21:47:34 +00006956 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006957 pass;
6958
glennrpe9c26dc2010-05-30 01:56:35 +00006959 png_byte
6960 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006961
glennrp39992b42010-11-14 00:03:43 +00006962 png_color
6963 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00006964
glennrp5af765f2010-03-30 11:12:18 +00006965 png_color_16
6966 ping_background,
6967 ping_trans_color;
6968
cristy3ed852e2009-09-05 21:47:34 +00006969 png_info
6970 *ping_info;
6971
6972 png_struct
6973 *ping;
6974
glennrp5af765f2010-03-30 11:12:18 +00006975 png_uint_32
6976 ping_height,
6977 ping_width;
6978
cristybb503372010-05-27 20:51:26 +00006979 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006980 y;
6981
6982 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00006983 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00006984 logging,
glennrp58e01762011-01-07 15:28:54 +00006985 matte,
6986
glennrpda8f3a72011-02-27 23:54:12 +00006987 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00006988 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00006989 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00006990 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00006991 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00006992 ping_have_bKGD,
6993 ping_have_pHYs,
6994 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00006995
6996 ping_exclude_bKGD,
6997 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00006998 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00006999 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007000 ping_exclude_gAMA,
7001 ping_exclude_iCCP,
7002 /* ping_exclude_iTXt, */
7003 ping_exclude_oFFs,
7004 ping_exclude_pHYs,
7005 ping_exclude_sRGB,
7006 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007007 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007008 ping_exclude_vpAg,
7009 ping_exclude_zCCP, /* hex-encoded iCCP */
7010 ping_exclude_zTXt,
7011
glennrp8d3d6e52011-04-19 04:39:51 +00007012 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007013 ping_need_colortype_warning,
7014
glennrp82b3c532011-03-22 19:20:54 +00007015 status,
glennrpd3371642011-03-22 19:42:23 +00007016 tried_333,
7017 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007018
7019 QuantumInfo
7020 *quantum_info;
7021
cristybb503372010-05-27 20:51:26 +00007022 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007023 i,
7024 x;
7025
7026 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007027 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007028
glennrp5af765f2010-03-30 11:12:18 +00007029 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007030 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007031 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007032 ping_color_type,
7033 ping_interlace_method,
7034 ping_compression_method,
7035 ping_filter_method,
7036 ping_num_trans;
7037
cristybb503372010-05-27 20:51:26 +00007038 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007039 image_depth,
7040 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007041
cristybb503372010-05-27 20:51:26 +00007042 size_t
cristy3ed852e2009-09-05 21:47:34 +00007043 quality,
7044 rowbytes,
7045 save_image_depth;
7046
glennrpdfd70802010-11-14 01:23:35 +00007047 int
glennrpfd05d622011-02-25 04:10:33 +00007048 j,
glennrpf09bded2011-01-08 01:15:59 +00007049 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007050 number_opaque,
7051 number_semitransparent,
7052 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007053 ping_pHYs_unit_type;
7054
7055 png_uint_32
7056 ping_pHYs_x_resolution,
7057 ping_pHYs_y_resolution;
7058
cristy3ed852e2009-09-05 21:47:34 +00007059 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007060 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007061
glennrpb9cfe272010-12-21 15:08:06 +00007062 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7063 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007064 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007065 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007066
cristy3ed852e2009-09-05 21:47:34 +00007067#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007068 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007069#endif
7070
glennrp5af765f2010-03-30 11:12:18 +00007071 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007072 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007073 ping_color_type=0,
7074 ping_interlace_method=0,
7075 ping_compression_method=0,
7076 ping_filter_method=0,
7077 ping_num_trans = 0;
7078
7079 ping_background.red = 0;
7080 ping_background.green = 0;
7081 ping_background.blue = 0;
7082 ping_background.gray = 0;
7083 ping_background.index = 0;
7084
7085 ping_trans_color.red=0;
7086 ping_trans_color.green=0;
7087 ping_trans_color.blue=0;
7088 ping_trans_color.gray=0;
7089
glennrpdfd70802010-11-14 01:23:35 +00007090 ping_pHYs_unit_type = 0;
7091 ping_pHYs_x_resolution = 0;
7092 ping_pHYs_y_resolution = 0;
7093
glennrpda8f3a72011-02-27 23:54:12 +00007094 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007095 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007096 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007097 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007098 ping_have_bKGD=MagickFalse;
7099 ping_have_pHYs=MagickFalse;
7100 ping_have_tRNS=MagickFalse;
7101
glennrp0e8ea192010-12-24 18:00:33 +00007102 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7103 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007104 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007105 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007106 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007107 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7108 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7109 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7110 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7111 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7112 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007113 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007114 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7115 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7116 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7117
glennrp8d3d6e52011-04-19 04:39:51 +00007118 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007119 ping_need_colortype_warning = MagickFalse;
7120
glennrp8bb3a022010-12-13 20:40:04 +00007121 number_opaque = 0;
7122 number_semitransparent = 0;
7123 number_transparent = 0;
7124
glennrpfd05d622011-02-25 04:10:33 +00007125 if (logging != MagickFalse)
7126 {
7127 if (image->storage_class == UndefinedClass)
7128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7129 " storage_class=UndefinedClass");
7130 if (image->storage_class == DirectClass)
7131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7132 " storage_class=DirectClass");
7133 if (image->storage_class == PseudoClass)
7134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7135 " storage_class=PseudoClass");
7136 }
glennrp28af3712011-04-06 18:07:30 +00007137
glennrpc6c391a2011-04-27 02:23:56 +00007138 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007139 {
glennrpc6c391a2011-04-27 02:23:56 +00007140 if (image->storage_class != PseudoClass && image->colormap != NULL)
7141 {
7142 /* Free the bogus colormap; it can cause trouble later */
7143 if (logging != MagickFalse)
7144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7145 " Freeing bogus colormap");
7146 (void *) RelinquishMagickMemory(image->colormap);
7147 image->colormap=NULL;
7148 }
glennrp28af3712011-04-06 18:07:30 +00007149 }
glennrpfd05d622011-02-25 04:10:33 +00007150
cristy3ed852e2009-09-05 21:47:34 +00007151 if (image->colorspace != RGBColorspace)
7152 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007153
glennrp3241bd02010-12-12 04:36:28 +00007154 /*
7155 Sometimes we get PseudoClass images whose RGB values don't match
7156 the colors in the colormap. This code syncs the RGB values.
7157 */
7158 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7159 (void) SyncImage(image);
7160
glennrpa6a06632011-01-19 15:15:34 +00007161#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7162 if (image->depth > 8)
7163 {
7164 if (logging != MagickFalse)
7165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7166 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7167
7168 image->depth=8;
7169 }
7170#endif
7171
glennrp67b9c1a2011-04-22 18:47:36 +00007172#if 0 /* To do: Option to use the original colormap */
7173 if (ping_preserve_colormap != MagickFalse)
7174 {
7175 }
7176#endif
7177
glennrp3faa9a32011-04-23 14:00:25 +00007178#if 0 /* To do: respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007179 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7180 {
glennrp9d0ea4d2011-04-22 18:35:57 +00007181 }
glennrp67b9c1a2011-04-22 18:47:36 +00007182#endif
glennrp9d0ea4d2011-04-22 18:35:57 +00007183
glennrp67b9c1a2011-04-22 18:47:36 +00007184 /* To do: set to next higher multiple of 8 */
7185 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007186 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007187
glennrp2b013e42010-11-24 16:55:50 +00007188#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7189 /* PNG does not handle depths greater than 16 so reduce it even
7190 * if lossy
7191 */
7192 if (image->depth > 16)
7193 image->depth=16;
7194#endif
7195
glennrp3faa9a32011-04-23 14:00:25 +00007196#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007197 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007198 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007199 image->depth = 8;
7200#endif
7201
glennrpc8c2f062011-02-25 19:00:33 +00007202 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007203 * we reduce the transparency to binary and run again, then if there
7204 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7205 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7206 * palette. The final reduction can only fail if there are still 256
7207 * colors present and one of them has both transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007208 */
glennrp82b3c532011-03-22 19:20:54 +00007209
7210 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007211 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007212
glennrpd3371642011-03-22 19:42:23 +00007213 for (j=0; j<5; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007214 {
7215 /* BUILD_PALETTE
7216 *
7217 * Sometimes we get DirectClass images that have 256 colors or fewer.
7218 * This code will build a colormap.
7219 *
7220 * Also, sometimes we get PseudoClass images with an out-of-date
7221 * colormap. This code will replace the colormap with a new one.
7222 * Sometimes we get PseudoClass images that have more than 256 colors.
7223 * This code will delete the colormap and change the image to
7224 * DirectClass.
7225 *
7226 * If image->matte is MagickFalse, we ignore the opacity channel
7227 * even though it sometimes contains left-over non-opaque values.
7228 *
7229 * Also we gather some information (number of opaque, transparent,
7230 * and semitransparent pixels, and whether the image has any non-gray
7231 * pixels or only black-and-white pixels) that we might need later.
7232 *
7233 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7234 * we need to check for bogus non-opaque values, at least.
7235 */
glennrp3c218112010-11-27 15:31:26 +00007236
glennrpd71e86a2011-02-24 01:28:37 +00007237 ExceptionInfo
7238 *exception;
glennrp3c218112010-11-27 15:31:26 +00007239
glennrpd71e86a2011-02-24 01:28:37 +00007240 int
7241 n;
glennrp3c218112010-11-27 15:31:26 +00007242
glennrpd71e86a2011-02-24 01:28:37 +00007243 PixelPacket
7244 opaque[260],
7245 semitransparent[260],
7246 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007247
glennrpd71e86a2011-02-24 01:28:37 +00007248 register IndexPacket
7249 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007250
glennrpd71e86a2011-02-24 01:28:37 +00007251 register const PixelPacket
7252 *s,
7253 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007254
glennrpfd05d622011-02-25 04:10:33 +00007255 register PixelPacket
7256 *r;
7257
glennrpd71e86a2011-02-24 01:28:37 +00007258 if (logging != MagickFalse)
7259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7260 " Enter BUILD_PALETTE:");
7261
7262 if (logging != MagickFalse)
7263 {
glennrp03812ae2010-12-24 01:31:34 +00007264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007265 " image->columns=%.20g",(double) image->columns);
7266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7267 " image->rows=%.20g",(double) image->rows);
7268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7269 " image->matte=%.20g",(double) image->matte);
7270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7271 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007272
glennrpfd05d622011-02-25 04:10:33 +00007273 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007274 {
7275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007276 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007278 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007279
glennrpd71e86a2011-02-24 01:28:37 +00007280 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007281 {
glennrpd71e86a2011-02-24 01:28:37 +00007282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7283 " %d (%d,%d,%d,%d)",
7284 (int) i,
7285 (int) image->colormap[i].red,
7286 (int) image->colormap[i].green,
7287 (int) image->colormap[i].blue,
7288 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007289 }
glennrp2cc891a2010-12-24 13:44:32 +00007290
glennrpd71e86a2011-02-24 01:28:37 +00007291 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7292 {
7293 if (i > 255)
7294 {
7295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7296 " %d (%d,%d,%d,%d)",
7297 (int) i,
7298 (int) image->colormap[i].red,
7299 (int) image->colormap[i].green,
7300 (int) image->colormap[i].blue,
7301 (int) image->colormap[i].opacity);
7302 }
7303 }
glennrp03812ae2010-12-24 01:31:34 +00007304 }
glennrp7ddcc222010-12-11 05:01:05 +00007305
glennrpd71e86a2011-02-24 01:28:37 +00007306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7307 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007308
glennrpd71e86a2011-02-24 01:28:37 +00007309 if (image->colors == 0)
7310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7311 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007312
glennrp8d3d6e52011-04-19 04:39:51 +00007313 if (ping_preserve_colormap == MagickFalse)
7314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7315 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007316 }
7317
7318 exception=(&image->exception);
7319
7320 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007321 number_opaque = 0;
7322 number_semitransparent = 0;
7323 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007324
7325 for (y=0; y < (ssize_t) image->rows; y++)
7326 {
7327 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7328
7329 if (q == (PixelPacket *) NULL)
7330 break;
7331
7332 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007333 {
glennrp4737d522011-04-29 03:33:42 +00007334 if (image->matte == MagickFalse ||
7335 GetOpacityPixelComponent(q) == OpaqueOpacity)
glennrpd71e86a2011-02-24 01:28:37 +00007336 {
7337 if (number_opaque < 259)
7338 {
7339 if (number_opaque == 0)
7340 {
glennrp8e045c82011-04-27 16:40:27 +00007341 GetRGBPixelComponents(q, opaque[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007342 opaque[0].opacity=OpaqueOpacity;
7343 number_opaque=1;
7344 }
glennrp2cc891a2010-12-24 13:44:32 +00007345
glennrpd71e86a2011-02-24 01:28:37 +00007346 for (i=0; i< (ssize_t) number_opaque; i++)
7347 {
glennrp0e68fac2011-04-26 04:51:47 +00007348 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007349 break;
7350 }
glennrp7ddcc222010-12-11 05:01:05 +00007351
glennrpd71e86a2011-02-24 01:28:37 +00007352 if (i == (ssize_t) number_opaque &&
7353 number_opaque < 259)
7354 {
7355 number_opaque++;
glennrp8e045c82011-04-27 16:40:27 +00007356 GetRGBPixelComponents(q, opaque[i]);
glennrpca7ad3a2011-04-26 04:44:54 +00007357 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00007358 }
7359 }
7360 }
7361 else if (q->opacity == TransparentOpacity)
7362 {
7363 if (number_transparent < 259)
7364 {
7365 if (number_transparent == 0)
7366 {
glennrp8e045c82011-04-27 16:40:27 +00007367 GetRGBOPixelComponents(q, transparent[0]);
glennrpa18d5bc2011-04-23 14:51:34 +00007368 ping_trans_color.red=
7369 (unsigned short) GetRedPixelComponent(q);
7370 ping_trans_color.green=
7371 (unsigned short) GetGreenPixelComponent(q);
7372 ping_trans_color.blue=
7373 (unsigned short) GetBluePixelComponent(q);
7374 ping_trans_color.gray=
7375 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007376 number_transparent = 1;
7377 }
7378
7379 for (i=0; i< (ssize_t) number_transparent; i++)
7380 {
glennrp0e68fac2011-04-26 04:51:47 +00007381 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00007382 break;
7383 }
7384
7385 if (i == (ssize_t) number_transparent &&
7386 number_transparent < 259)
7387 {
7388 number_transparent++;
glennrp8e045c82011-04-27 16:40:27 +00007389 GetRGBOPixelComponents(q, transparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00007390 }
7391 }
7392 }
7393 else
7394 {
7395 if (number_semitransparent < 259)
7396 {
7397 if (number_semitransparent == 0)
7398 {
glennrp8e045c82011-04-27 16:40:27 +00007399 GetRGBOPixelComponents(q, semitransparent[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007400 number_semitransparent = 1;
7401 }
7402
7403 for (i=0; i< (ssize_t) number_semitransparent; i++)
7404 {
glennrp0e68fac2011-04-26 04:51:47 +00007405 if (IsColorEqual(q, semitransparent+i)
glennrpca7ad3a2011-04-26 04:44:54 +00007406 && GetOpacityPixelComponent(q) ==
7407 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00007408 break;
7409 }
7410
7411 if (i == (ssize_t) number_semitransparent &&
7412 number_semitransparent < 259)
7413 {
7414 number_semitransparent++;
glennrp8e045c82011-04-27 16:40:27 +00007415 GetRGBOPixelComponents(q, semitransparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00007416 }
7417 }
7418 }
7419 q++;
7420 }
7421 }
7422
7423 if (ping_exclude_bKGD == MagickFalse)
7424 {
7425 /* Add the background color to the palette, if it
7426 * isn't already there.
7427 */
glennrpc6c391a2011-04-27 02:23:56 +00007428 if (logging != MagickFalse)
7429 {
7430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7431 " Check colormap for background (%d,%d,%d)",
7432 (int) image->background_color.red,
7433 (int) image->background_color.green,
7434 (int) image->background_color.blue);
7435 }
glennrpd71e86a2011-02-24 01:28:37 +00007436 for (i=0; i<number_opaque; i++)
7437 {
glennrpca7ad3a2011-04-26 04:44:54 +00007438 if (opaque[i].red == image->background_color.red &&
7439 opaque[i].green == image->background_color.green &&
7440 opaque[i].blue == image->background_color.blue)
7441 break;
glennrpd71e86a2011-02-24 01:28:37 +00007442 }
glennrpd71e86a2011-02-24 01:28:37 +00007443 if (number_opaque < 259 && i == number_opaque)
7444 {
glennrp8e045c82011-04-27 16:40:27 +00007445 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00007446 ping_background.index = i;
7447 if (logging != MagickFalse)
7448 {
7449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7450 " background_color index is %d",(int) i);
7451 }
7452
glennrpd71e86a2011-02-24 01:28:37 +00007453 }
glennrpa080bc32011-03-11 18:03:44 +00007454 else if (logging != MagickFalse)
7455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7456 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007457 }
7458
7459 image_colors=number_opaque+number_transparent+number_semitransparent;
7460
glennrpa080bc32011-03-11 18:03:44 +00007461 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7462 {
7463 /* No room for the background color; remove it. */
7464 number_opaque--;
7465 image_colors--;
7466 }
7467
glennrpd71e86a2011-02-24 01:28:37 +00007468 if (logging != MagickFalse)
7469 {
7470 if (image_colors > 256)
7471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7472 " image has more than 256 colors");
7473
7474 else
7475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7476 " image has %d colors",image_colors);
7477 }
7478
glennrp8d3d6e52011-04-19 04:39:51 +00007479 if (ping_preserve_colormap != MagickFalse)
7480 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007481
glennrpfd05d622011-02-25 04:10:33 +00007482 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007483 {
7484 ping_have_color=MagickFalse;
7485 ping_have_non_bw=MagickFalse;
7486
7487 if(image_colors > 256)
7488 {
7489 for (y=0; y < (ssize_t) image->rows; y++)
7490 {
7491 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7492
7493 if (q == (PixelPacket *) NULL)
7494 break;
7495
7496 /* Worst case is black-and-white; we are looking at every
7497 * pixel twice.
7498 */
7499
7500 if (ping_have_color == MagickFalse)
7501 {
7502 s=q;
7503 for (x=0; x < (ssize_t) image->columns; x++)
7504 {
glennrpa18d5bc2011-04-23 14:51:34 +00007505 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7506 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00007507 {
7508 ping_have_color=MagickTrue;
7509 ping_have_non_bw=MagickTrue;
7510 break;
7511 }
7512 s++;
7513 }
7514 }
7515
7516 if (ping_have_non_bw == MagickFalse)
7517 {
7518 s=q;
7519 for (x=0; x < (ssize_t) image->columns; x++)
7520 {
glennrpa18d5bc2011-04-23 14:51:34 +00007521 if (GetRedPixelComponent(s) != 0 &&
7522 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00007523 {
7524 ping_have_non_bw=MagickTrue;
7525 }
7526 s++;
7527 }
7528 }
7529 }
7530 }
7531 }
7532
7533 if (image_colors < 257)
7534 {
7535 PixelPacket
7536 colormap[260];
7537
7538 /*
7539 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007540 */
7541
glennrpd71e86a2011-02-24 01:28:37 +00007542 if (logging != MagickFalse)
7543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7544 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007545
glennrpd71e86a2011-02-24 01:28:37 +00007546 /* Sort palette, transparent first */;
7547
7548 n = 0;
7549
7550 for (i=0; i<number_transparent; i++)
7551 colormap[n++] = transparent[i];
7552
7553 for (i=0; i<number_semitransparent; i++)
7554 colormap[n++] = semitransparent[i];
7555
7556 for (i=0; i<number_opaque; i++)
7557 colormap[n++] = opaque[i];
7558
glennrpc6c391a2011-04-27 02:23:56 +00007559 ping_background.index +=
7560 (number_transparent + number_semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00007561
7562 /* image_colors < 257; search the colormap instead of the pixels
7563 * to get ping_have_color and ping_have_non_bw
7564 */
7565 for (i=0; i<n; i++)
7566 {
7567 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007568 {
glennrpd71e86a2011-02-24 01:28:37 +00007569 if (colormap[i].red != colormap[i].green ||
7570 colormap[i].red != colormap[i].blue)
7571 {
7572 ping_have_color=MagickTrue;
7573 ping_have_non_bw=MagickTrue;
7574 break;
7575 }
7576 }
7577
7578 if (ping_have_non_bw == MagickFalse)
7579 {
7580 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007581 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007582 }
glennrp8bb3a022010-12-13 20:40:04 +00007583 }
7584
glennrpd71e86a2011-02-24 01:28:37 +00007585 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7586 (number_transparent == 0 && number_semitransparent == 0)) &&
7587 (((mng_info->write_png_colortype-1) ==
7588 PNG_COLOR_TYPE_PALETTE) ||
7589 (mng_info->write_png_colortype == 0)))
7590 {
glennrp6185c532011-01-14 17:58:40 +00007591 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007592 {
glennrpd71e86a2011-02-24 01:28:37 +00007593 if (n != (ssize_t) image_colors)
7594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7595 " image_colors (%d) and n (%d) don't match",
7596 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007597
glennrpd71e86a2011-02-24 01:28:37 +00007598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7599 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007600 }
glennrp03812ae2010-12-24 01:31:34 +00007601
glennrpd71e86a2011-02-24 01:28:37 +00007602 image->colors = image_colors;
7603
7604 if (AcquireImageColormap(image,image_colors) ==
7605 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007606 ThrowWriterException(ResourceLimitError,
7607 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007608
7609 for (i=0; i< (ssize_t) image_colors; i++)
7610 image->colormap[i] = colormap[i];
7611
7612 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007613 {
glennrpd71e86a2011-02-24 01:28:37 +00007614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7615 " image->colors=%d (%d)",
7616 (int) image->colors, image_colors);
7617
7618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7619 " Update the pixel indexes");
7620 }
glennrp6185c532011-01-14 17:58:40 +00007621
glennrpfd05d622011-02-25 04:10:33 +00007622 /* Sync the pixel indices with the new colormap */
7623
glennrpd71e86a2011-02-24 01:28:37 +00007624 for (y=0; y < (ssize_t) image->rows; y++)
7625 {
7626 q=GetAuthenticPixels(image,0,y,image->columns,1,
7627 exception);
glennrp6185c532011-01-14 17:58:40 +00007628
glennrpd71e86a2011-02-24 01:28:37 +00007629 if (q == (PixelPacket *) NULL)
7630 break;
glennrp6185c532011-01-14 17:58:40 +00007631
glennrpd71e86a2011-02-24 01:28:37 +00007632 indexes=GetAuthenticIndexQueue(image);
7633
7634 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007635 {
glennrpd71e86a2011-02-24 01:28:37 +00007636 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007637 {
glennrpd71e86a2011-02-24 01:28:37 +00007638 if ((image->matte == MagickFalse ||
glennrpca7ad3a2011-04-26 04:44:54 +00007639 image->colormap[i].opacity ==
7640 GetOpacityPixelComponent(q)) &&
7641 image->colormap[i].red ==
7642 GetRedPixelComponent(q) &&
7643 image->colormap[i].green ==
7644 GetGreenPixelComponent(q) &&
7645 image->colormap[i].blue ==
7646 GetBluePixelComponent(q))
glennrp6185c532011-01-14 17:58:40 +00007647 {
cristy9fff7b42011-04-29 01:09:31 +00007648 SetIndexPixelComponent(indexes+x,i);
glennrpd71e86a2011-02-24 01:28:37 +00007649 break;
glennrp6185c532011-01-14 17:58:40 +00007650 }
glennrp6185c532011-01-14 17:58:40 +00007651 }
glennrpd71e86a2011-02-24 01:28:37 +00007652 q++;
7653 }
glennrp6185c532011-01-14 17:58:40 +00007654
glennrpd71e86a2011-02-24 01:28:37 +00007655 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7656 break;
7657 }
7658 }
7659 }
7660
7661 if (logging != MagickFalse)
7662 {
7663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7664 " image->colors=%d", (int) image->colors);
7665
7666 if (image->colormap != NULL)
7667 {
7668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7669 " i (red,green,blue,opacity)");
7670
7671 for (i=0; i < (ssize_t) image->colors; i++)
7672 {
cristy72988482011-03-29 16:34:38 +00007673 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007674 {
7675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7676 " %d (%d,%d,%d,%d)",
7677 (int) i,
7678 (int) image->colormap[i].red,
7679 (int) image->colormap[i].green,
7680 (int) image->colormap[i].blue,
7681 (int) image->colormap[i].opacity);
7682 }
glennrp6185c532011-01-14 17:58:40 +00007683 }
7684 }
glennrp03812ae2010-12-24 01:31:34 +00007685
glennrpd71e86a2011-02-24 01:28:37 +00007686 if (number_transparent < 257)
7687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7688 " number_transparent = %d",
7689 number_transparent);
7690 else
glennrp03812ae2010-12-24 01:31:34 +00007691
glennrpd71e86a2011-02-24 01:28:37 +00007692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7693 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007694
glennrpd71e86a2011-02-24 01:28:37 +00007695 if (number_opaque < 257)
7696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7697 " number_opaque = %d",
7698 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007699
glennrpd71e86a2011-02-24 01:28:37 +00007700 else
7701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7702 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007703
glennrpd71e86a2011-02-24 01:28:37 +00007704 if (number_semitransparent < 257)
7705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7706 " number_semitransparent = %d",
7707 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007708
glennrpd71e86a2011-02-24 01:28:37 +00007709 else
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007712
glennrpd71e86a2011-02-24 01:28:37 +00007713 if (ping_have_non_bw == MagickFalse)
7714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7715 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007716
glennrpd71e86a2011-02-24 01:28:37 +00007717 else if (ping_have_color == MagickFalse)
7718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719 " All pixels and the background are gray");
7720
7721 else
7722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7723 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007724
glennrp03812ae2010-12-24 01:31:34 +00007725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7726 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007727 }
glennrpfd05d622011-02-25 04:10:33 +00007728
glennrpc8c2f062011-02-25 19:00:33 +00007729 if (mng_info->write_png8 == MagickFalse)
7730 break;
glennrpfd05d622011-02-25 04:10:33 +00007731
glennrpc8c2f062011-02-25 19:00:33 +00007732 /* Make any reductions necessary for the PNG8 format */
7733 if (image_colors <= 256 &&
7734 image_colors != 0 && image->colormap != NULL &&
7735 number_semitransparent == 0 &&
7736 number_transparent <= 1)
7737 break;
7738
7739 /* PNG8 can't have semitransparent colors so we threshold the
7740 * opacity to 0 or OpaqueOpacity
7741 */
7742 if (number_semitransparent != 0)
7743 {
7744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7745 " Thresholding the alpha channel to binary");
7746
7747 for (y=0; y < (ssize_t) image->rows; y++)
7748 {
7749 r=GetAuthenticPixels(image,0,y,image->columns,1,
7750 exception);
7751
7752 if (r == (PixelPacket *) NULL)
7753 break;
7754
7755 for (x=0; x < (ssize_t) image->columns; x++)
7756 {
glennrpa18d5bc2011-04-23 14:51:34 +00007757 SetOpacityPixelComponent(r,
7758 (GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7759 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00007760 r++;
7761 }
7762
7763 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7764 break;
7765
7766 if (image_colors != 0 && image_colors <= 256 &&
7767 image->colormap != NULL)
7768 for (i=0; i<image_colors; i++)
7769 image->colormap[i].opacity =
glennrp82b3c532011-03-22 19:20:54 +00007770 image->colormap[i].opacity > TransparentOpacity/2 ?
7771 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007772 }
7773 continue;
7774 }
7775
7776 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007777 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7778 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7779 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007780 */
glennrpd3371642011-03-22 19:42:23 +00007781 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7782 {
7783 if (logging != MagickFalse)
7784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7785 " Quantizing the background color to 4-4-4");
7786
7787 tried_444 = MagickTrue;
7788
7789 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007790 ScaleCharToQuantum(
7791 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7792 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007793 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007794 ScaleCharToQuantum(
7795 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7796 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007797 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007798 ScaleCharToQuantum(
7799 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7800 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007801
7802 if (logging != MagickFalse)
7803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7804 " Quantizing the pixel colors to 4-4-4");
7805
7806 if (image->colormap == NULL)
7807 {
7808 for (y=0; y < (ssize_t) image->rows; y++)
7809 {
7810 r=GetAuthenticPixels(image,0,y,image->columns,1,
7811 exception);
7812
7813 if (r == (PixelPacket *) NULL)
7814 break;
7815
7816 for (x=0; x < (ssize_t) image->columns; x++)
7817 {
glennrp4737d522011-04-29 03:33:42 +00007818 if (GetOpacityPixelComponent(r) == TransparentOpacity)
glennrpd3371642011-03-22 19:42:23 +00007819 {
glennrpb5d5e6d2011-04-27 17:08:19 +00007820 SetRGBPixelComponents(r,image->background_color);
glennrpd3371642011-03-22 19:42:23 +00007821 }
7822 else
7823 {
glennrp4737d522011-04-29 03:33:42 +00007824 SetRedPixelComponent(r,ScaleCharToQuantum(
7825 ScaleQuantumToChar(GetRedPixelComponent(r) & 0xf0) |
7826 ScaleQuantumToChar(GetRedPixelComponent(r) & 0xf0) >> 4));
7827 SetGreenPixelComponent(r,ScaleCharToQuantum(
7828 ScaleQuantumToChar(GetGreenPixelComponent(r) & 0xf0) |
7829 ScaleQuantumToChar(GetGreenPixelComponent(r) & 0xf0) >> 4));
7830 SetBluePixelComponent(r,ScaleCharToQuantum(
7831 ScaleQuantumToChar(GetBluePixelComponent(r) & 0xf0) |
7832 ScaleQuantumToChar(GetBluePixelComponent(r) & 0xf0) >> 4));
glennrpd3371642011-03-22 19:42:23 +00007833 }
7834 r++;
7835 }
7836
7837 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7838 break;
7839 }
7840 }
7841
7842 else /* Should not reach this; colormap already exists and
7843 must be <= 256 */
7844 {
7845 if (logging != MagickFalse)
7846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7847 " Quantizing the colormap to 4-4-4");
7848 for (i=0; i<image_colors; i++)
7849 {
glennrp3faa9a32011-04-23 14:00:25 +00007850 image->colormap[i].red=ScaleCharToQuantum(
7851 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7852 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7853 image->colormap[i].green=ScaleCharToQuantum(
7854 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7855 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7856 image->colormap[i].blue=ScaleCharToQuantum(
7857 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7858 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
glennrpd3371642011-03-22 19:42:23 +00007859 }
7860 }
7861 continue;
7862 }
7863
glennrp82b3c532011-03-22 19:20:54 +00007864 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7865 {
7866 if (logging != MagickFalse)
7867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7868 " Quantizing the background color to 3-3-3");
7869
7870 tried_333 = MagickTrue;
7871
7872 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007873 ScaleCharToQuantum(
7874 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7875 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7876 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007877 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007878 ScaleCharToQuantum(
7879 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7880 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7881 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007882 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007883 ScaleCharToQuantum(
7884 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7885 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7886 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007887
7888 if (logging != MagickFalse)
7889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007890 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007891
7892 if (image->colormap == NULL)
7893 {
7894 for (y=0; y < (ssize_t) image->rows; y++)
7895 {
7896 r=GetAuthenticPixels(image,0,y,image->columns,1,
7897 exception);
7898
7899 if (r == (PixelPacket *) NULL)
7900 break;
7901
7902 for (x=0; x < (ssize_t) image->columns; x++)
7903 {
glennrp4737d522011-04-29 03:33:42 +00007904 if (GetOpacityPixelComponent(r) == TransparentOpacity)
glennrp82b3c532011-03-22 19:20:54 +00007905 {
glennrp4737d522011-04-29 03:33:42 +00007906 SetRGBPixelComponents(r,image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00007907 }
7908 else
7909 {
glennrp4737d522011-04-29 03:33:42 +00007910 SetRedPixelComponent(r,ScaleCharToQuantum(
7911 ScaleQuantumToChar(GetRedPixelComponent(r) & 0xe0) |
7912 ScaleQuantumToChar(GetRedPixelComponent(r) & 0xe0) >> 3 |
7913 ScaleQuantumToChar(GetRedPixelComponent(r) & 0xc0) >> 6));
7914 SetGreenPixelComponent(r,ScaleCharToQuantum(
7915 ScaleQuantumToChar(GetGreenPixelComponent(r) & 0xe0) |
7916 ScaleQuantumToChar(GetGreenPixelComponent(r) & 0xe0) >> 3 |
7917 ScaleQuantumToChar(GetGreenPixelComponent(r) & 0xc0) >> 6));
7918 SetBluePixelComponent(r,ScaleCharToQuantum(
7919 ScaleQuantumToChar(GetBluePixelComponent(r) & 0xe0) |
7920 ScaleQuantumToChar(GetBluePixelComponent(r) & 0xe0) >> 3 |
7921 ScaleQuantumToChar(GetBluePixelComponent(r) & 0xc0) >> 6));
glennrp82b3c532011-03-22 19:20:54 +00007922 }
7923 r++;
7924 }
7925
7926 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7927 break;
7928 }
7929 }
7930
7931 else /* Should not reach this; colormap already exists and
7932 must be <= 256 */
7933 {
7934 if (logging != MagickFalse)
7935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007936 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007937 for (i=0; i<image_colors; i++)
7938 {
glennrp3faa9a32011-04-23 14:00:25 +00007939 image->colormap[i].red=ScaleCharToQuantum(
7940 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7941 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7942 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7943 image->colormap[i].green=ScaleCharToQuantum(
7944 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7945 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7946 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7947 image->colormap[i].blue=ScaleCharToQuantum(
7948 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7949 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7950 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007951 }
glennrpd3371642011-03-22 19:42:23 +00007952 }
7953 continue;
glennrp82b3c532011-03-22 19:20:54 +00007954 }
glennrpc8c2f062011-02-25 19:00:33 +00007955
glennrpc8c2f062011-02-25 19:00:33 +00007956 if (image_colors == 0 || image_colors > 256)
7957 {
7958 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007960 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007961
glennrp3faa9a32011-04-23 14:00:25 +00007962 /* Red and green were already done so we only quantize the blue
7963 * channel
7964 */
7965
7966 image->background_color.blue=ScaleCharToQuantum(
7967 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7968 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7969 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7970 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrpfd05d622011-02-25 04:10:33 +00007971
glennrpc8c2f062011-02-25 19:00:33 +00007972 if (logging != MagickFalse)
7973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007974 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007975
glennrpc8c2f062011-02-25 19:00:33 +00007976 if (image->colormap == NULL)
7977 {
7978 for (y=0; y < (ssize_t) image->rows; y++)
7979 {
7980 r=GetAuthenticPixels(image,0,y,image->columns,1,
7981 exception);
7982
7983 if (r == (PixelPacket *) NULL)
7984 break;
7985
7986 for (x=0; x < (ssize_t) image->columns; x++)
7987 {
glennrp4737d522011-04-29 03:33:42 +00007988 if (GetOpacityPixelComponent(r) == TransparentOpacity)
glennrp82b3c532011-03-22 19:20:54 +00007989 {
glennrpb5d5e6d2011-04-27 17:08:19 +00007990 SetRGBPixelComponents(r,image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00007991 }
7992 else
7993 {
glennrp4737d522011-04-29 03:33:42 +00007994 SetBluePixelComponent(r,ScaleCharToQuantum(
7995 ScaleQuantumToChar(GetBluePixelComponent(r) & 0xc0) |
7996 ScaleQuantumToChar(GetBluePixelComponent(r) & 0xc0) >> 2 |
7997 ScaleQuantumToChar(GetBluePixelComponent(r) & 0xc0) >> 4 |
7998 ScaleQuantumToChar(GetBluePixelComponent(r) & 0xc0) >> 6));
glennrp82b3c532011-03-22 19:20:54 +00007999 }
glennrp52a479c2011-02-26 21:14:38 +00008000 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008001 }
glennrpfd05d622011-02-25 04:10:33 +00008002
glennrpc8c2f062011-02-25 19:00:33 +00008003 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8004 break;
8005 }
8006 }
glennrpfd05d622011-02-25 04:10:33 +00008007
glennrpc8c2f062011-02-25 19:00:33 +00008008 else /* Should not reach this; colormap already exists and
8009 must be <= 256 */
8010 {
8011 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008013 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008014 for (i=0; i<image_colors; i++)
8015 {
glennrp3faa9a32011-04-23 14:00:25 +00008016 image->colormap[i].blue=ScaleCharToQuantum(
8017 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
8018 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
8019 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
8020 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrpc8c2f062011-02-25 19:00:33 +00008021 }
8022 }
8023 continue;
8024 }
8025 break;
glennrpd71e86a2011-02-24 01:28:37 +00008026 }
glennrpfd05d622011-02-25 04:10:33 +00008027 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008028
glennrpfd05d622011-02-25 04:10:33 +00008029 /* If we are excluding the tRNS chunk and there is transparency,
8030 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8031 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008032 */
glennrp0e8ea192010-12-24 18:00:33 +00008033 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8034 (number_transparent != 0 || number_semitransparent != 0))
8035 {
8036 int colortype=mng_info->write_png_colortype;
8037
8038 if (ping_have_color == MagickFalse)
8039 mng_info->write_png_colortype = 5;
8040
8041 else
8042 mng_info->write_png_colortype = 7;
8043
glennrp8d579662011-02-23 02:05:02 +00008044 if (colortype != 0 &&
8045 mng_info->write_png_colortype != (ssize_t) colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008046 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008047
glennrp0e8ea192010-12-24 18:00:33 +00008048 }
8049
glennrpfd05d622011-02-25 04:10:33 +00008050 /* See if cheap transparency is possible. It is only possible
8051 * when there is a single transparent color, no semitransparent
8052 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008053 * as the transparent color. We only need this information if
8054 * we are writing a PNG with colortype 0 or 2, and we have not
8055 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008056 */
glennrp5a39f372011-02-25 04:52:16 +00008057 if (number_transparent == 1 &&
8058 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008059 {
8060 ping_have_cheap_transparency = MagickTrue;
8061
8062 if (number_semitransparent != 0)
8063 ping_have_cheap_transparency = MagickFalse;
8064
8065 else if (image_colors == 0 || image_colors > 256 ||
8066 image->colormap == NULL)
8067 {
8068 ExceptionInfo
8069 *exception;
8070
8071 register const PixelPacket
8072 *q;
8073
8074 exception=(&image->exception);
8075
8076 for (y=0; y < (ssize_t) image->rows; y++)
8077 {
8078 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8079
8080 if (q == (PixelPacket *) NULL)
8081 break;
8082
8083 for (x=0; x < (ssize_t) image->columns; x++)
8084 {
8085 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008086 (unsigned short) GetRedPixelComponent(q) ==
8087 ping_trans_color.red &&
8088 (unsigned short) GetGreenPixelComponent(q) ==
8089 ping_trans_color.green &&
8090 (unsigned short) GetBluePixelComponent(q) ==
8091 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008092 {
8093 ping_have_cheap_transparency = MagickFalse;
8094 break;
8095 }
8096
8097 q++;
8098 }
8099
8100 if (ping_have_cheap_transparency == MagickFalse)
8101 break;
8102 }
8103 }
8104 else
8105 {
glennrp67b9c1a2011-04-22 18:47:36 +00008106 /* Assuming that image->colormap[0] is the one transparent color
8107 * and that all others are opaque.
8108 */
glennrpfd05d622011-02-25 04:10:33 +00008109 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008110 for (i=1; i<image_colors; i++)
8111 if (image->colormap[i].red == image->colormap[0].red &&
8112 image->colormap[i].green == image->colormap[0].green &&
8113 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008114 {
glennrp67b9c1a2011-04-22 18:47:36 +00008115 ping_have_cheap_transparency = MagickFalse;
8116 break;
glennrpfd05d622011-02-25 04:10:33 +00008117 }
8118 }
8119
8120 if (logging != MagickFalse)
8121 {
8122 if (ping_have_cheap_transparency == MagickFalse)
8123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8124 " Cheap transparency is not possible.");
8125
8126 else
8127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8128 " Cheap transparency is possible.");
8129 }
8130 }
8131 else
8132 ping_have_cheap_transparency = MagickFalse;
8133
glennrp8640fb52010-11-23 15:48:26 +00008134 image_depth=image->depth;
8135
glennrp26c990a2010-11-23 02:23:20 +00008136 quantum_info = (QuantumInfo *) NULL;
8137 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008138 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008139 image_matte=image->matte;
8140
glennrp0fe50b42010-11-16 03:52:51 +00008141 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008142 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008143
glennrp52a479c2011-02-26 21:14:38 +00008144 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8145 (image->colors == 0 || image->colormap == NULL))
8146 {
glennrp52a479c2011-02-26 21:14:38 +00008147 image_info=DestroyImageInfo(image_info);
8148 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008149 (void) ThrowMagickException(&IMimage->exception,
8150 GetMagickModule(),CoderError,
8151 "Cannot write PNG8 or color-type 3; colormap is NULL",
8152 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008153#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8154 UnlockSemaphoreInfo(ping_semaphore);
8155#endif
8156 return(MagickFalse);
8157 }
8158
cristy3ed852e2009-09-05 21:47:34 +00008159 /*
8160 Allocate the PNG structures
8161 */
8162#ifdef PNG_USER_MEM_SUPPORTED
8163 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008164 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8165 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008166
cristy3ed852e2009-09-05 21:47:34 +00008167#else
8168 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008169 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008170
cristy3ed852e2009-09-05 21:47:34 +00008171#endif
8172 if (ping == (png_struct *) NULL)
8173 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008174
cristy3ed852e2009-09-05 21:47:34 +00008175 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008176
cristy3ed852e2009-09-05 21:47:34 +00008177 if (ping_info == (png_info *) NULL)
8178 {
8179 png_destroy_write_struct(&ping,(png_info **) NULL);
8180 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8181 }
glennrp0fe50b42010-11-16 03:52:51 +00008182
cristy3ed852e2009-09-05 21:47:34 +00008183 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008184 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008185
glennrp5af765f2010-03-30 11:12:18 +00008186 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008187 {
8188 /*
8189 PNG write failed.
8190 */
8191#ifdef PNG_DEBUG
8192 if (image_info->verbose)
8193 (void) printf("PNG write has failed.\n");
8194#endif
8195 png_destroy_write_struct(&ping,&ping_info);
8196#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008197 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008198#endif
glennrpda8f3a72011-02-27 23:54:12 +00008199 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008200 (void) CloseBlob(image);
8201 image_info=DestroyImageInfo(image_info);
8202 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008203 return(MagickFalse);
8204 }
8205 /*
8206 Prepare PNG for writing.
8207 */
8208#if defined(PNG_MNG_FEATURES_SUPPORTED)
8209 if (mng_info->write_mng)
8210 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008211
cristy3ed852e2009-09-05 21:47:34 +00008212#else
8213# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8214 if (mng_info->write_mng)
8215 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008216
cristy3ed852e2009-09-05 21:47:34 +00008217# endif
8218#endif
glennrp2b013e42010-11-24 16:55:50 +00008219
cristy3ed852e2009-09-05 21:47:34 +00008220 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008221
cristy4e5bc842010-06-09 13:56:01 +00008222 ping_width=(png_uint_32) image->columns;
8223 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008224
cristy3ed852e2009-09-05 21:47:34 +00008225 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8226 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008227
cristy3ed852e2009-09-05 21:47:34 +00008228 if (mng_info->write_png_depth != 0)
8229 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008230
cristy3ed852e2009-09-05 21:47:34 +00008231 /* Adjust requested depth to next higher valid depth if necessary */
8232 if (image_depth > 8)
8233 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008234
cristy3ed852e2009-09-05 21:47:34 +00008235 if ((image_depth > 4) && (image_depth < 8))
8236 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008237
cristy3ed852e2009-09-05 21:47:34 +00008238 if (image_depth == 3)
8239 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008240
cristy3ed852e2009-09-05 21:47:34 +00008241 if (logging != MagickFalse)
8242 {
8243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008244 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008246 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008248 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008250 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008252 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008253 }
glennrp8640fb52010-11-23 15:48:26 +00008254
cristy3ed852e2009-09-05 21:47:34 +00008255 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008256 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008257
glennrp26f37912010-12-23 16:22:42 +00008258
cristy3ed852e2009-09-05 21:47:34 +00008259#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008260 if (ping_exclude_pHYs == MagickFalse)
8261 {
cristy3ed852e2009-09-05 21:47:34 +00008262 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8263 (!mng_info->write_mng || !mng_info->equal_physs))
8264 {
glennrp0fe50b42010-11-16 03:52:51 +00008265 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8267 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008268
8269 if (image->units == PixelsPerInchResolution)
8270 {
glennrpdfd70802010-11-14 01:23:35 +00008271 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008272 ping_pHYs_x_resolution=
8273 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8274 ping_pHYs_y_resolution=
8275 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008276 }
glennrpdfd70802010-11-14 01:23:35 +00008277
cristy3ed852e2009-09-05 21:47:34 +00008278 else if (image->units == PixelsPerCentimeterResolution)
8279 {
glennrpdfd70802010-11-14 01:23:35 +00008280 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008281 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8282 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008283 }
glennrp991d11d2010-11-12 21:55:28 +00008284
cristy3ed852e2009-09-05 21:47:34 +00008285 else
8286 {
glennrpdfd70802010-11-14 01:23:35 +00008287 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8288 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8289 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008290 }
glennrp991d11d2010-11-12 21:55:28 +00008291
glennrp823b55c2011-03-14 18:46:46 +00008292 if (logging != MagickFalse)
8293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8294 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8295 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8296 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008297 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008298 }
glennrp26f37912010-12-23 16:22:42 +00008299 }
cristy3ed852e2009-09-05 21:47:34 +00008300#endif
glennrpa521b2f2010-10-29 04:11:03 +00008301
glennrp26f37912010-12-23 16:22:42 +00008302 if (ping_exclude_bKGD == MagickFalse)
8303 {
glennrpa521b2f2010-10-29 04:11:03 +00008304 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008305 {
glennrpa521b2f2010-10-29 04:11:03 +00008306 unsigned int
8307 mask;
cristy3ed852e2009-09-05 21:47:34 +00008308
glennrpa521b2f2010-10-29 04:11:03 +00008309 mask=0xffff;
8310 if (ping_bit_depth == 8)
8311 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008312
glennrpa521b2f2010-10-29 04:11:03 +00008313 if (ping_bit_depth == 4)
8314 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008315
glennrpa521b2f2010-10-29 04:11:03 +00008316 if (ping_bit_depth == 2)
8317 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008318
glennrpa521b2f2010-10-29 04:11:03 +00008319 if (ping_bit_depth == 1)
8320 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008321
glennrpa521b2f2010-10-29 04:11:03 +00008322 ping_background.red=(png_uint_16)
8323 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008324
glennrpa521b2f2010-10-29 04:11:03 +00008325 ping_background.green=(png_uint_16)
8326 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008327
glennrpa521b2f2010-10-29 04:11:03 +00008328 ping_background.blue=(png_uint_16)
8329 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008330
8331 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008332 }
cristy3ed852e2009-09-05 21:47:34 +00008333
glennrp0fe50b42010-11-16 03:52:51 +00008334 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008335 {
8336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8337 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8339 " background_color index is %d",
8340 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008341
8342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8343 " ping_bit_depth=%d",ping_bit_depth);
8344 }
glennrp0fe50b42010-11-16 03:52:51 +00008345
8346 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008347 }
glennrp0fe50b42010-11-16 03:52:51 +00008348
cristy3ed852e2009-09-05 21:47:34 +00008349 /*
8350 Select the color type.
8351 */
8352 matte=image_matte;
8353 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008354
glennrp1273f7b2011-02-24 03:20:30 +00008355 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008356 {
glennrp0fe50b42010-11-16 03:52:51 +00008357
glennrpfd05d622011-02-25 04:10:33 +00008358 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008359 for reducing the sample depth from 8. */
8360
glennrp0fe50b42010-11-16 03:52:51 +00008361 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008362
glennrp8bb3a022010-12-13 20:40:04 +00008363 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008364
8365 /*
8366 Set image palette.
8367 */
8368 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8369
glennrp0fe50b42010-11-16 03:52:51 +00008370 if (logging != MagickFalse)
8371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8372 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008373 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008374
8375 for (i=0; i < (ssize_t) number_colors; i++)
8376 {
8377 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8378 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8379 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8380 if (logging != MagickFalse)
8381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008382#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008383 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008384#else
8385 " %5ld (%5d,%5d,%5d)",
8386#endif
glennrp0fe50b42010-11-16 03:52:51 +00008387 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8388
8389 }
glennrp2b013e42010-11-24 16:55:50 +00008390
glennrp8bb3a022010-12-13 20:40:04 +00008391 ping_have_PLTE=MagickTrue;
8392 image_depth=ping_bit_depth;
8393 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008394
glennrp58e01762011-01-07 15:28:54 +00008395 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008396 {
glennrp0fe50b42010-11-16 03:52:51 +00008397 /*
8398 Identify which colormap entry is transparent.
8399 */
8400 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008401 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008402
glennrp8bb3a022010-12-13 20:40:04 +00008403 for (i=0; i < (ssize_t) number_transparent; i++)
8404 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008405
glennrp0fe50b42010-11-16 03:52:51 +00008406
glennrp2cc891a2010-12-24 13:44:32 +00008407 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008408 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008409
8410 if (ping_num_trans == 0)
8411 ping_have_tRNS=MagickFalse;
8412
glennrp8bb3a022010-12-13 20:40:04 +00008413 else
8414 ping_have_tRNS=MagickTrue;
8415 }
glennrp0fe50b42010-11-16 03:52:51 +00008416
glennrp1273f7b2011-02-24 03:20:30 +00008417 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008418 {
glennrp1273f7b2011-02-24 03:20:30 +00008419 /*
8420 * Identify which colormap entry is the background color.
8421 */
8422
glennrp4f25bd02011-01-01 18:51:28 +00008423 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8424 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8425 break;
glennrp0fe50b42010-11-16 03:52:51 +00008426
glennrp4f25bd02011-01-01 18:51:28 +00008427 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00008428
8429 if (logging != MagickFalse)
8430 {
8431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8432 " background_color index is %d",
8433 (int) ping_background.index);
8434 }
glennrp4f25bd02011-01-01 18:51:28 +00008435 }
cristy3ed852e2009-09-05 21:47:34 +00008436 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008437
cristy3ed852e2009-09-05 21:47:34 +00008438 else if (mng_info->write_png24)
8439 {
8440 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008441 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008442 }
glennrp0fe50b42010-11-16 03:52:51 +00008443
cristy3ed852e2009-09-05 21:47:34 +00008444 else if (mng_info->write_png32)
8445 {
8446 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008447 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008448 }
glennrp0fe50b42010-11-16 03:52:51 +00008449
glennrp8bb3a022010-12-13 20:40:04 +00008450 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008451 {
glennrp5af765f2010-03-30 11:12:18 +00008452 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008453
glennrp8bb3a022010-12-13 20:40:04 +00008454 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008455 {
glennrp5af765f2010-03-30 11:12:18 +00008456 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008457
glennrp5af765f2010-03-30 11:12:18 +00008458 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8459 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008460 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008461
glennrp8bb3a022010-12-13 20:40:04 +00008462 else
8463 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008464
8465 if (logging != MagickFalse)
8466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8467 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008468 }
glennrp0fe50b42010-11-16 03:52:51 +00008469
glennrp7c4c9e62011-03-21 20:23:32 +00008470 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008471 {
8472 if (logging != MagickFalse)
8473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008474 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008475
glennrpd6bf1612010-12-17 17:28:54 +00008476 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008477 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008478
glennrpd6bf1612010-12-17 17:28:54 +00008479 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008480 {
glennrp5af765f2010-03-30 11:12:18 +00008481 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008482 image_matte=MagickFalse;
8483 }
glennrp0fe50b42010-11-16 03:52:51 +00008484
glennrpd6bf1612010-12-17 17:28:54 +00008485 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008486 {
glennrp5af765f2010-03-30 11:12:18 +00008487 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008488 image_matte=MagickTrue;
8489 }
glennrp0fe50b42010-11-16 03:52:51 +00008490
glennrp5aa37f62011-01-02 03:07:57 +00008491 if (image_info->type == PaletteType ||
8492 image_info->type == PaletteMatteType)
8493 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8494
glennrp7c4c9e62011-03-21 20:23:32 +00008495 if (mng_info->write_png_colortype == 0 &&
8496 (image_info->type == UndefinedType ||
8497 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008498 {
glennrp5aa37f62011-01-02 03:07:57 +00008499 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008500 {
glennrp5aa37f62011-01-02 03:07:57 +00008501 if (image_matte == MagickFalse)
8502 {
8503 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8504 image_matte=MagickFalse;
8505 }
glennrp0fe50b42010-11-16 03:52:51 +00008506
glennrp0b206f52011-01-07 04:55:32 +00008507 else
glennrp5aa37f62011-01-02 03:07:57 +00008508 {
8509 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8510 image_matte=MagickTrue;
8511 }
8512 }
8513 else
glennrp8bb3a022010-12-13 20:40:04 +00008514 {
glennrp5aa37f62011-01-02 03:07:57 +00008515 if (image_matte == MagickFalse)
8516 {
8517 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8518 image_matte=MagickFalse;
8519 }
glennrp8bb3a022010-12-13 20:40:04 +00008520
glennrp0b206f52011-01-07 04:55:32 +00008521 else
glennrp5aa37f62011-01-02 03:07:57 +00008522 {
8523 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8524 image_matte=MagickTrue;
8525 }
8526 }
glennrp0fe50b42010-11-16 03:52:51 +00008527 }
glennrp5aa37f62011-01-02 03:07:57 +00008528
cristy3ed852e2009-09-05 21:47:34 +00008529 }
glennrp0fe50b42010-11-16 03:52:51 +00008530
cristy3ed852e2009-09-05 21:47:34 +00008531 if (logging != MagickFalse)
8532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008533 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008534
glennrp5af765f2010-03-30 11:12:18 +00008535 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008536 {
8537 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8538 ping_color_type == PNG_COLOR_TYPE_RGB ||
8539 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8540 ping_bit_depth=8;
8541 }
cristy3ed852e2009-09-05 21:47:34 +00008542
glennrpd6bf1612010-12-17 17:28:54 +00008543 old_bit_depth=ping_bit_depth;
8544
glennrp5af765f2010-03-30 11:12:18 +00008545 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008546 {
glennrp8d579662011-02-23 02:05:02 +00008547 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8548 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008549 }
glennrp8640fb52010-11-23 15:48:26 +00008550
glennrp5af765f2010-03-30 11:12:18 +00008551 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008552 {
cristy35ef8242010-06-03 16:24:13 +00008553 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008554 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008555
8556 if (image->colors == 0)
8557 {
glennrp0fe50b42010-11-16 03:52:51 +00008558 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008559 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008560 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008561 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008562 }
8563
cristy35ef8242010-06-03 16:24:13 +00008564 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008565 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008566 }
glennrp2b013e42010-11-24 16:55:50 +00008567
glennrpd6bf1612010-12-17 17:28:54 +00008568 if (logging != MagickFalse)
8569 {
8570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8571 " Number of colors: %.20g",(double) image_colors);
8572
8573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8574 " Tentative PNG bit depth: %d",ping_bit_depth);
8575 }
8576
8577 if (ping_bit_depth < (int) mng_info->write_png_depth)
8578 ping_bit_depth = mng_info->write_png_depth;
8579 }
glennrp2cc891a2010-12-24 13:44:32 +00008580
glennrp5af765f2010-03-30 11:12:18 +00008581 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008582
cristy3ed852e2009-09-05 21:47:34 +00008583 if (logging != MagickFalse)
8584 {
8585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008586 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008587
cristy3ed852e2009-09-05 21:47:34 +00008588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008589 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008590
cristy3ed852e2009-09-05 21:47:34 +00008591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008592 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008593
cristy3ed852e2009-09-05 21:47:34 +00008594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008595
glennrp8640fb52010-11-23 15:48:26 +00008596 " image->depth: %.20g",(double) image->depth);
8597
8598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008599 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008600 }
8601
glennrp58e01762011-01-07 15:28:54 +00008602 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008603 {
glennrp4f25bd02011-01-01 18:51:28 +00008604 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008605 {
glennrp7c4c9e62011-03-21 20:23:32 +00008606 if (mng_info->write_png_colortype == 0)
8607 {
8608 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008609
glennrp7c4c9e62011-03-21 20:23:32 +00008610 if (ping_have_color != MagickFalse)
8611 ping_color_type=PNG_COLOR_TYPE_RGBA;
8612 }
glennrp4f25bd02011-01-01 18:51:28 +00008613
8614 /*
8615 * Determine if there is any transparent color.
8616 */
8617 if (number_transparent + number_semitransparent == 0)
8618 {
8619 /*
8620 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8621 */
glennrpa6a06632011-01-19 15:15:34 +00008622
glennrp4f25bd02011-01-01 18:51:28 +00008623 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008624
8625 if (mng_info->write_png_colortype == 0)
8626 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008627 }
8628
8629 else
8630 {
8631 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008632 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008633
8634 mask=0xffff;
8635
8636 if (ping_bit_depth == 8)
8637 mask=0x00ff;
8638
8639 if (ping_bit_depth == 4)
8640 mask=0x000f;
8641
8642 if (ping_bit_depth == 2)
8643 mask=0x0003;
8644
8645 if (ping_bit_depth == 1)
8646 mask=0x0001;
8647
8648 ping_trans_color.red=(png_uint_16)
8649 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8650
8651 ping_trans_color.green=(png_uint_16)
8652 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8653
8654 ping_trans_color.blue=(png_uint_16)
8655 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8656
8657 ping_trans_color.gray=(png_uint_16)
8658 (ScaleQuantumToShort(PixelIntensityToQuantum(
8659 image->colormap)) & mask);
8660
8661 ping_trans_color.index=(png_byte) 0;
8662
8663 ping_have_tRNS=MagickTrue;
8664 }
8665
8666 if (ping_have_tRNS != MagickFalse)
8667 {
8668 /*
glennrpfd05d622011-02-25 04:10:33 +00008669 * Determine if there is one and only one transparent color
8670 * and if so if it is fully transparent.
8671 */
8672 if (ping_have_cheap_transparency == MagickFalse)
8673 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008674 }
8675
8676 if (ping_have_tRNS != MagickFalse)
8677 {
glennrp7c4c9e62011-03-21 20:23:32 +00008678 if (mng_info->write_png_colortype == 0)
8679 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008680
8681 if (image_depth == 8)
8682 {
8683 ping_trans_color.red&=0xff;
8684 ping_trans_color.green&=0xff;
8685 ping_trans_color.blue&=0xff;
8686 ping_trans_color.gray&=0xff;
8687 }
8688 }
8689 }
cristy3ed852e2009-09-05 21:47:34 +00008690 else
8691 {
cristy3ed852e2009-09-05 21:47:34 +00008692 if (image_depth == 8)
8693 {
glennrp5af765f2010-03-30 11:12:18 +00008694 ping_trans_color.red&=0xff;
8695 ping_trans_color.green&=0xff;
8696 ping_trans_color.blue&=0xff;
8697 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008698 }
8699 }
8700 }
glennrp8640fb52010-11-23 15:48:26 +00008701
cristy3ed852e2009-09-05 21:47:34 +00008702 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008703
glennrp2e09f552010-11-14 00:38:48 +00008704 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008705 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008706
glennrp39992b42010-11-14 00:03:43 +00008707 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008708 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008709 ping_have_color == MagickFalse &&
8710 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008711 {
cristy35ef8242010-06-03 16:24:13 +00008712 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008713
cristy3ed852e2009-09-05 21:47:34 +00008714 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008715 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008716
glennrp7c4c9e62011-03-21 20:23:32 +00008717 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008718 {
glennrp5af765f2010-03-30 11:12:18 +00008719 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008720
cristy3ed852e2009-09-05 21:47:34 +00008721 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008722 {
8723 if (logging != MagickFalse)
8724 {
8725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8726 " Scaling ping_trans_color (0)");
8727 }
8728 ping_trans_color.gray*=0x0101;
8729 }
cristy3ed852e2009-09-05 21:47:34 +00008730 }
glennrp0fe50b42010-11-16 03:52:51 +00008731
cristy3ed852e2009-09-05 21:47:34 +00008732 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8733 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008734
glennrp136ee3a2011-04-27 15:47:45 +00008735 if ((image_colors == 0) ||
8736 ((ssize_t) (image_colors-1) > MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008737 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008738
cristy3ed852e2009-09-05 21:47:34 +00008739 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008740 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008741
cristy3ed852e2009-09-05 21:47:34 +00008742 else
8743 {
glennrp5af765f2010-03-30 11:12:18 +00008744 ping_bit_depth=8;
8745 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008746 {
8747 if(!mng_info->write_png_depth)
8748 {
glennrp5af765f2010-03-30 11:12:18 +00008749 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008750
cristy35ef8242010-06-03 16:24:13 +00008751 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008752 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008753 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008754 }
8755 }
glennrp2b013e42010-11-24 16:55:50 +00008756
glennrp0fe50b42010-11-16 03:52:51 +00008757 else if (ping_color_type ==
8758 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008759 mng_info->IsPalette)
8760 {
cristy3ed852e2009-09-05 21:47:34 +00008761 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008762
cristy3ed852e2009-09-05 21:47:34 +00008763 int
8764 depth_4_ok=MagickTrue,
8765 depth_2_ok=MagickTrue,
8766 depth_1_ok=MagickTrue;
8767
cristybb503372010-05-27 20:51:26 +00008768 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008769 {
8770 unsigned char
8771 intensity;
8772
8773 intensity=ScaleQuantumToChar(image->colormap[i].red);
8774
8775 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8776 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8777 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8778 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008779 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008780 depth_1_ok=MagickFalse;
8781 }
glennrp2b013e42010-11-24 16:55:50 +00008782
cristy3ed852e2009-09-05 21:47:34 +00008783 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008784 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008785
cristy3ed852e2009-09-05 21:47:34 +00008786 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008787 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008788
cristy3ed852e2009-09-05 21:47:34 +00008789 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008790 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008791 }
8792 }
glennrp2b013e42010-11-24 16:55:50 +00008793
glennrp5af765f2010-03-30 11:12:18 +00008794 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008795 }
glennrp0fe50b42010-11-16 03:52:51 +00008796
cristy3ed852e2009-09-05 21:47:34 +00008797 else
glennrp0fe50b42010-11-16 03:52:51 +00008798
cristy3ed852e2009-09-05 21:47:34 +00008799 if (mng_info->IsPalette)
8800 {
glennrp17a14852010-05-10 03:01:59 +00008801 number_colors=image_colors;
8802
cristy3ed852e2009-09-05 21:47:34 +00008803 if (image_depth <= 8)
8804 {
cristy3ed852e2009-09-05 21:47:34 +00008805 /*
8806 Set image palette.
8807 */
glennrp5af765f2010-03-30 11:12:18 +00008808 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008809
glennrp58e01762011-01-07 15:28:54 +00008810 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008811 {
glennrp9c1eb072010-06-06 22:19:15 +00008812 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008813
glennrp3b51f0e2010-11-27 18:14:08 +00008814 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8816 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008817 }
glennrp0fe50b42010-11-16 03:52:51 +00008818
cristy3ed852e2009-09-05 21:47:34 +00008819 else
8820 {
cristybb503372010-05-27 20:51:26 +00008821 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008822 {
8823 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8824 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8825 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8826 }
glennrp0fe50b42010-11-16 03:52:51 +00008827
glennrp3b51f0e2010-11-27 18:14:08 +00008828 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008830 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008831 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008832
glennrp39992b42010-11-14 00:03:43 +00008833 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008834 }
glennrp0fe50b42010-11-16 03:52:51 +00008835
cristy3ed852e2009-09-05 21:47:34 +00008836 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008837 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008838 {
cristybefe4d22010-06-07 01:18:58 +00008839 size_t
8840 one;
8841
glennrp5af765f2010-03-30 11:12:18 +00008842 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008843 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008844
cristybefe4d22010-06-07 01:18:58 +00008845 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008846 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008847 }
glennrp0fe50b42010-11-16 03:52:51 +00008848
glennrp5af765f2010-03-30 11:12:18 +00008849 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008850
glennrp58e01762011-01-07 15:28:54 +00008851 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008852 {
glennrp0fe50b42010-11-16 03:52:51 +00008853 /*
glennrpd6bf1612010-12-17 17:28:54 +00008854 * Set up trans_colors array.
8855 */
glennrp0fe50b42010-11-16 03:52:51 +00008856 assert(number_colors <= 256);
8857
glennrpd6bf1612010-12-17 17:28:54 +00008858 ping_num_trans=(unsigned short) (number_transparent +
8859 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008860
8861 if (ping_num_trans == 0)
8862 ping_have_tRNS=MagickFalse;
8863
glennrpd6bf1612010-12-17 17:28:54 +00008864 else
glennrp0fe50b42010-11-16 03:52:51 +00008865 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008866 if (logging != MagickFalse)
8867 {
8868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8869 " Scaling ping_trans_color (1)");
8870 }
glennrpd6bf1612010-12-17 17:28:54 +00008871 ping_have_tRNS=MagickTrue;
8872
8873 for (i=0; i < ping_num_trans; i++)
8874 {
8875 ping_trans_alpha[i]= (png_byte) (255-
8876 ScaleQuantumToChar(image->colormap[i].opacity));
8877 }
glennrp0fe50b42010-11-16 03:52:51 +00008878 }
8879 }
cristy3ed852e2009-09-05 21:47:34 +00008880 }
8881 }
glennrp0fe50b42010-11-16 03:52:51 +00008882
cristy3ed852e2009-09-05 21:47:34 +00008883 else
8884 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008885
cristy3ed852e2009-09-05 21:47:34 +00008886 if (image_depth < 8)
8887 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008888
cristy3ed852e2009-09-05 21:47:34 +00008889 if ((save_image_depth == 16) && (image_depth == 8))
8890 {
glennrp4f25bd02011-01-01 18:51:28 +00008891 if (logging != MagickFalse)
8892 {
8893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8894 " Scaling ping_trans_color from (%d,%d,%d)",
8895 (int) ping_trans_color.red,
8896 (int) ping_trans_color.green,
8897 (int) ping_trans_color.blue);
8898 }
8899
glennrp5af765f2010-03-30 11:12:18 +00008900 ping_trans_color.red*=0x0101;
8901 ping_trans_color.green*=0x0101;
8902 ping_trans_color.blue*=0x0101;
8903 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008904
8905 if (logging != MagickFalse)
8906 {
8907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8908 " to (%d,%d,%d)",
8909 (int) ping_trans_color.red,
8910 (int) ping_trans_color.green,
8911 (int) ping_trans_color.blue);
8912 }
cristy3ed852e2009-09-05 21:47:34 +00008913 }
8914 }
8915
cristy4383ec82011-01-05 15:42:32 +00008916 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8917 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008918
cristy3ed852e2009-09-05 21:47:34 +00008919 /*
8920 Adjust background and transparency samples in sub-8-bit grayscale files.
8921 */
glennrp5af765f2010-03-30 11:12:18 +00008922 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008923 PNG_COLOR_TYPE_GRAY)
8924 {
8925 png_uint_16
8926 maxval;
8927
cristy35ef8242010-06-03 16:24:13 +00008928 size_t
8929 one=1;
8930
cristy22ffd972010-06-03 16:51:47 +00008931 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008932
glennrp4f25bd02011-01-01 18:51:28 +00008933 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008934 {
cristy3ed852e2009-09-05 21:47:34 +00008935
glennrpa521b2f2010-10-29 04:11:03 +00008936 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008937 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8938
8939 if (logging != MagickFalse)
8940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008941 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00008942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8943 " background_color index is %d",
8944 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008945
glennrp991d11d2010-11-12 21:55:28 +00008946 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008947 }
cristy3ed852e2009-09-05 21:47:34 +00008948
glennrp5af765f2010-03-30 11:12:18 +00008949 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8950 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008951 }
glennrp17a14852010-05-10 03:01:59 +00008952
glennrp26f37912010-12-23 16:22:42 +00008953 if (ping_exclude_bKGD == MagickFalse)
8954 {
glennrp1273f7b2011-02-24 03:20:30 +00008955 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008956 {
8957 /*
8958 Identify which colormap entry is the background color.
8959 */
8960
glennrp17a14852010-05-10 03:01:59 +00008961 number_colors=image_colors;
8962
glennrpa521b2f2010-10-29 04:11:03 +00008963 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8964 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008965 break;
8966
8967 ping_background.index=(png_byte) i;
8968
glennrp3b51f0e2010-11-27 18:14:08 +00008969 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008970 {
8971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008972 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008973 }
glennrp0fe50b42010-11-16 03:52:51 +00008974
cristy13d07042010-11-21 20:56:18 +00008975 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008976 {
8977 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008978
8979 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008980 {
8981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8982 " background =(%d,%d,%d)",
8983 (int) ping_background.red,
8984 (int) ping_background.green,
8985 (int) ping_background.blue);
8986 }
8987 }
glennrpa521b2f2010-10-29 04:11:03 +00008988
glennrpd6bf1612010-12-17 17:28:54 +00008989 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008990 {
glennrp3b51f0e2010-11-27 18:14:08 +00008991 if (logging != MagickFalse)
8992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8993 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008994 ping_have_bKGD = MagickFalse;
8995 }
glennrp17a14852010-05-10 03:01:59 +00008996 }
glennrp26f37912010-12-23 16:22:42 +00008997 }
glennrp17a14852010-05-10 03:01:59 +00008998
cristy3ed852e2009-09-05 21:47:34 +00008999 if (logging != MagickFalse)
9000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009001 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009002 /*
9003 Initialize compression level and filtering.
9004 */
9005 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009006 {
9007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9008 " Setting up deflate compression");
9009
9010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011 " Compression buffer size: 32768");
9012 }
9013
cristy3ed852e2009-09-05 21:47:34 +00009014 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009015
cristy3ed852e2009-09-05 21:47:34 +00009016 if (logging != MagickFalse)
9017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9018 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009019
cristy3ed852e2009-09-05 21:47:34 +00009020 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009021
cristy3ed852e2009-09-05 21:47:34 +00009022 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9023 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009024
cristy3ed852e2009-09-05 21:47:34 +00009025 if (quality > 9)
9026 {
9027 int
9028 level;
9029
cristybb503372010-05-27 20:51:26 +00009030 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009031
cristy3ed852e2009-09-05 21:47:34 +00009032 if (logging != MagickFalse)
9033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9034 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00009035
cristy3ed852e2009-09-05 21:47:34 +00009036 png_set_compression_level(ping,level);
9037 }
glennrp0fe50b42010-11-16 03:52:51 +00009038
cristy3ed852e2009-09-05 21:47:34 +00009039 else
9040 {
9041 if (logging != MagickFalse)
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009044
cristy3ed852e2009-09-05 21:47:34 +00009045 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9046 }
glennrp0fe50b42010-11-16 03:52:51 +00009047
cristy3ed852e2009-09-05 21:47:34 +00009048 if (logging != MagickFalse)
9049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9050 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009051
glennrp2b013e42010-11-24 16:55:50 +00009052#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009053 /* This became available in libpng-1.0.9. Output must be a MNG. */
9054 if (mng_info->write_mng && ((quality % 10) == 7))
9055 {
9056 if (logging != MagickFalse)
9057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9058 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009059
glennrp5af765f2010-03-30 11:12:18 +00009060 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009061 }
glennrp0fe50b42010-11-16 03:52:51 +00009062
cristy3ed852e2009-09-05 21:47:34 +00009063 else
9064 if (logging != MagickFalse)
9065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9066 " Filter_type: 0");
9067#endif
glennrp2b013e42010-11-24 16:55:50 +00009068
cristy3ed852e2009-09-05 21:47:34 +00009069 {
9070 int
9071 base_filter;
9072
9073 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009074 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009075
glennrp26c990a2010-11-23 02:23:20 +00009076 else
glennrp8640fb52010-11-23 15:48:26 +00009077 if ((quality % 10) != 5)
9078 base_filter=(int) quality % 10;
9079
9080 else
9081 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9082 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9083 (quality < 50))
9084 base_filter=PNG_NO_FILTERS;
9085
9086 else
9087 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009088
cristy3ed852e2009-09-05 21:47:34 +00009089 if (logging != MagickFalse)
9090 {
9091 if (base_filter == PNG_ALL_FILTERS)
9092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9093 " Base filter method: ADAPTIVE");
9094 else
9095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9096 " Base filter method: NONE");
9097 }
glennrp2b013e42010-11-24 16:55:50 +00009098
cristy3ed852e2009-09-05 21:47:34 +00009099 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9100 }
9101
glennrp823b55c2011-03-14 18:46:46 +00009102 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9103 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009104 {
9105 ResetImageProfileIterator(image);
9106 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009107 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009108 profile=GetImageProfile(image,name);
9109
9110 if (profile != (StringInfo *) NULL)
9111 {
glennrp5af765f2010-03-30 11:12:18 +00009112#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009113 if ((LocaleCompare(name,"ICC") == 0) ||
9114 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009115 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009116
9117 if (ping_exclude_iCCP == MagickFalse)
9118 {
9119 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009120#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009121 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009122#else
9123 (png_const_bytep) GetStringInfoDatum(profile),
9124#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009125 (png_uint_32) GetStringInfoLength(profile));
9126 }
glennrp26f37912010-12-23 16:22:42 +00009127 }
glennrp0fe50b42010-11-16 03:52:51 +00009128
glennrpc8cbc5d2011-01-01 00:12:34 +00009129 else
cristy3ed852e2009-09-05 21:47:34 +00009130#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009131 if (ping_exclude_zCCP == MagickFalse)
9132 {
glennrpcf002022011-01-30 02:38:15 +00009133 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009134 (unsigned char *) name,(unsigned char *) name,
9135 GetStringInfoDatum(profile),
9136 (png_uint_32) GetStringInfoLength(profile));
9137 }
9138 }
glennrp0b206f52011-01-07 04:55:32 +00009139
glennrpc8cbc5d2011-01-01 00:12:34 +00009140 if (logging != MagickFalse)
9141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9142 " Setting up text chunk with %s profile",name);
9143
9144 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009145 }
cristy3ed852e2009-09-05 21:47:34 +00009146 }
9147
9148#if defined(PNG_WRITE_sRGB_SUPPORTED)
9149 if ((mng_info->have_write_global_srgb == 0) &&
9150 ((image->rendering_intent != UndefinedIntent) ||
9151 (image->colorspace == sRGBColorspace)))
9152 {
glennrp26f37912010-12-23 16:22:42 +00009153 if (ping_exclude_sRGB == MagickFalse)
9154 {
9155 /*
9156 Note image rendering intent.
9157 */
9158 if (logging != MagickFalse)
9159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9160 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009161
glennrp26f37912010-12-23 16:22:42 +00009162 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009163 Magick_RenderingIntent_to_PNG_RenderingIntent(
9164 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009165
glennrp26f37912010-12-23 16:22:42 +00009166 if (ping_exclude_gAMA == MagickFalse)
9167 png_set_gAMA(ping,ping_info,0.45455);
9168 }
cristy3ed852e2009-09-05 21:47:34 +00009169 }
glennrp26f37912010-12-23 16:22:42 +00009170
glennrp5af765f2010-03-30 11:12:18 +00009171 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009172#endif
9173 {
glennrp2cc891a2010-12-24 13:44:32 +00009174 if (ping_exclude_gAMA == MagickFalse &&
9175 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009176 (image->gamma < .45 || image->gamma > .46)))
9177 {
cristy3ed852e2009-09-05 21:47:34 +00009178 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9179 {
9180 /*
9181 Note image gamma.
9182 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9183 */
9184 if (logging != MagickFalse)
9185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9186 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009187
cristy3ed852e2009-09-05 21:47:34 +00009188 png_set_gAMA(ping,ping_info,image->gamma);
9189 }
glennrp26f37912010-12-23 16:22:42 +00009190 }
glennrp2b013e42010-11-24 16:55:50 +00009191
glennrp26f37912010-12-23 16:22:42 +00009192 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009193 {
glennrp26f37912010-12-23 16:22:42 +00009194 if ((mng_info->have_write_global_chrm == 0) &&
9195 (image->chromaticity.red_primary.x != 0.0))
9196 {
9197 /*
9198 Note image chromaticity.
9199 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9200 */
9201 PrimaryInfo
9202 bp,
9203 gp,
9204 rp,
9205 wp;
cristy3ed852e2009-09-05 21:47:34 +00009206
glennrp26f37912010-12-23 16:22:42 +00009207 wp=image->chromaticity.white_point;
9208 rp=image->chromaticity.red_primary;
9209 gp=image->chromaticity.green_primary;
9210 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009211
glennrp26f37912010-12-23 16:22:42 +00009212 if (logging != MagickFalse)
9213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9214 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009215
glennrp26f37912010-12-23 16:22:42 +00009216 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9217 bp.x,bp.y);
9218 }
9219 }
cristy3ed852e2009-09-05 21:47:34 +00009220 }
glennrpdfd70802010-11-14 01:23:35 +00009221
glennrp5af765f2010-03-30 11:12:18 +00009222 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009223
9224 if (mng_info->write_mng)
9225 png_set_sig_bytes(ping,8);
9226
9227 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9228
glennrpd6bf1612010-12-17 17:28:54 +00009229 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009230 {
9231 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009232 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009233 {
glennrp5af765f2010-03-30 11:12:18 +00009234 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009235
glennrp5af765f2010-03-30 11:12:18 +00009236 if (ping_bit_depth < 8)
9237 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009238 }
glennrp0fe50b42010-11-16 03:52:51 +00009239
cristy3ed852e2009-09-05 21:47:34 +00009240 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009241 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009242 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009243 }
9244
glennrp0e8ea192010-12-24 18:00:33 +00009245 if (ping_need_colortype_warning != MagickFalse ||
9246 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009247 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009248 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009249 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009250 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009251 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009252 {
9253 if (logging != MagickFalse)
9254 {
glennrp0e8ea192010-12-24 18:00:33 +00009255 if (ping_need_colortype_warning != MagickFalse)
9256 {
9257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9258 " Image has transparency but tRNS chunk was excluded");
9259 }
9260
cristy3ed852e2009-09-05 21:47:34 +00009261 if (mng_info->write_png_depth)
9262 {
9263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9264 " Defined PNG:bit-depth=%u, Computed depth=%u",
9265 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009266 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009267 }
glennrp0e8ea192010-12-24 18:00:33 +00009268
cristy3ed852e2009-09-05 21:47:34 +00009269 if (mng_info->write_png_colortype)
9270 {
9271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9272 " Defined PNG:color-type=%u, Computed color type=%u",
9273 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009274 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009275 }
9276 }
glennrp0e8ea192010-12-24 18:00:33 +00009277
glennrp3bd2e412010-08-10 13:34:52 +00009278 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009279 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9280 }
9281
glennrp58e01762011-01-07 15:28:54 +00009282 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009283 {
9284 /* Add an opaque matte channel */
9285 image->matte = MagickTrue;
9286 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009287
glennrpb4a13412010-05-05 12:47:19 +00009288 if (logging != MagickFalse)
9289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9290 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009291 }
9292
glennrp0e319732011-01-25 21:53:13 +00009293 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009294 {
glennrp991d11d2010-11-12 21:55:28 +00009295 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009296 {
glennrp991d11d2010-11-12 21:55:28 +00009297 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009298 if (logging != MagickFalse)
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300 " Setting ping_have_tRNS=MagickTrue.");
9301 }
glennrpe9c26dc2010-05-30 01:56:35 +00009302 }
9303
cristy3ed852e2009-09-05 21:47:34 +00009304 if (logging != MagickFalse)
9305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9306 " Writing PNG header chunks");
9307
glennrp5af765f2010-03-30 11:12:18 +00009308 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9309 ping_bit_depth,ping_color_type,
9310 ping_interlace_method,ping_compression_method,
9311 ping_filter_method);
9312
glennrp39992b42010-11-14 00:03:43 +00009313 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9314 {
glennrpf09bded2011-01-08 01:15:59 +00009315 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009316
glennrp3b51f0e2010-11-27 18:14:08 +00009317 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009318 {
glennrp8640fb52010-11-23 15:48:26 +00009319 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009320 {
glennrpd6bf1612010-12-17 17:28:54 +00009321 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009323 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9324 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009325 (int) palette[i].red,
9326 (int) palette[i].green,
9327 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009328 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009329 (int) ping_trans_alpha[i]);
9330 else
9331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009332 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009333 (int) i,
9334 (int) palette[i].red,
9335 (int) palette[i].green,
9336 (int) palette[i].blue);
9337 }
glennrp39992b42010-11-14 00:03:43 +00009338 }
glennrp39992b42010-11-14 00:03:43 +00009339 }
9340
glennrp26f37912010-12-23 16:22:42 +00009341 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009342 {
glennrp26f37912010-12-23 16:22:42 +00009343 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009344 {
glennrp26f37912010-12-23 16:22:42 +00009345 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009346 if (logging)
9347 {
9348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9349 " Setting up bKGD chunk");
9350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9351 " background color = (%d,%d,%d)",
9352 (int) ping_background.red,
9353 (int) ping_background.green,
9354 (int) ping_background.blue);
9355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9356 " index = %d, gray=%d",
9357 (int) ping_background.index,
9358 (int) ping_background.gray);
9359 }
9360 }
glennrp26f37912010-12-23 16:22:42 +00009361 }
9362
9363 if (ping_exclude_pHYs == MagickFalse)
9364 {
9365 if (ping_have_pHYs != MagickFalse)
9366 {
9367 png_set_pHYs(ping,ping_info,
9368 ping_pHYs_x_resolution,
9369 ping_pHYs_y_resolution,
9370 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009371
9372 if (logging)
9373 {
9374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9375 " Setting up pHYs chunk");
9376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9377 " x_resolution=%lu",
9378 (unsigned long) ping_pHYs_x_resolution);
9379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9380 " y_resolution=%lu",
9381 (unsigned long) ping_pHYs_y_resolution);
9382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9383 " unit_type=%lu",
9384 (unsigned long) ping_pHYs_unit_type);
9385 }
glennrp26f37912010-12-23 16:22:42 +00009386 }
glennrpdfd70802010-11-14 01:23:35 +00009387 }
9388
9389#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009390 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009391 {
glennrp26f37912010-12-23 16:22:42 +00009392 if (image->page.x || image->page.y)
9393 {
9394 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9395 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009396
glennrp26f37912010-12-23 16:22:42 +00009397 if (logging != MagickFalse)
9398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9399 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9400 (int) image->page.x, (int) image->page.y);
9401 }
glennrpdfd70802010-11-14 01:23:35 +00009402 }
9403#endif
9404
glennrpda8f3a72011-02-27 23:54:12 +00009405 if (mng_info->need_blob != MagickFalse)
9406 {
9407 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9408 MagickFalse)
9409 png_error(ping,"WriteBlob Failed");
9410
9411 ping_have_blob=MagickTrue;
9412 }
9413
cristy3ed852e2009-09-05 21:47:34 +00009414 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009415
glennrp39992b42010-11-14 00:03:43 +00009416 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009417 {
glennrp3b51f0e2010-11-27 18:14:08 +00009418 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009419 {
9420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9421 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9422 }
9423
9424 if (ping_color_type == 3)
9425 (void) png_set_tRNS(ping, ping_info,
9426 ping_trans_alpha,
9427 ping_num_trans,
9428 NULL);
9429
9430 else
9431 {
9432 (void) png_set_tRNS(ping, ping_info,
9433 NULL,
9434 0,
9435 &ping_trans_color);
9436
glennrp3b51f0e2010-11-27 18:14:08 +00009437 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009438 {
9439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009440 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009441 (int) ping_trans_color.red,
9442 (int) ping_trans_color.green,
9443 (int) ping_trans_color.blue);
9444 }
9445 }
glennrp991d11d2010-11-12 21:55:28 +00009446 }
9447
cristy3ed852e2009-09-05 21:47:34 +00009448 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009449 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009450
cristy3ed852e2009-09-05 21:47:34 +00009451 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009452
cristy3ed852e2009-09-05 21:47:34 +00009453 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009454 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009455
glennrp26f37912010-12-23 16:22:42 +00009456 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009457 {
glennrp4f25bd02011-01-01 18:51:28 +00009458 if ((image->page.width != 0 && image->page.width != image->columns) ||
9459 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009460 {
9461 unsigned char
9462 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009463
glennrp26f37912010-12-23 16:22:42 +00009464 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9465 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009466 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009467 PNGLong(chunk+4,(png_uint_32) image->page.width);
9468 PNGLong(chunk+8,(png_uint_32) image->page.height);
9469 chunk[12]=0; /* unit = pixels */
9470 (void) WriteBlob(image,13,chunk);
9471 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9472 }
cristy3ed852e2009-09-05 21:47:34 +00009473 }
9474
9475#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009476 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009477#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009478 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009479#undef PNG_HAVE_IDAT
9480#endif
9481
9482 png_set_packing(ping);
9483 /*
9484 Allocate memory.
9485 */
9486 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009487 if (image_depth > 8)
9488 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009489 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009490 {
glennrpb4a13412010-05-05 12:47:19 +00009491 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009492 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009493 break;
glennrp0fe50b42010-11-16 03:52:51 +00009494
glennrpb4a13412010-05-05 12:47:19 +00009495 case PNG_COLOR_TYPE_GRAY_ALPHA:
9496 rowbytes*=2;
9497 break;
glennrp0fe50b42010-11-16 03:52:51 +00009498
glennrpb4a13412010-05-05 12:47:19 +00009499 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009500 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009501 break;
glennrp0fe50b42010-11-16 03:52:51 +00009502
glennrpb4a13412010-05-05 12:47:19 +00009503 default:
9504 break;
cristy3ed852e2009-09-05 21:47:34 +00009505 }
glennrp3b51f0e2010-11-27 18:14:08 +00009506
9507 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009508 {
9509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9510 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009511
glennrpb4a13412010-05-05 12:47:19 +00009512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009513 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009514 }
glennrpcf002022011-01-30 02:38:15 +00009515 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9516 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009517
glennrpcf002022011-01-30 02:38:15 +00009518 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009519 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009520
cristy3ed852e2009-09-05 21:47:34 +00009521 /*
9522 Initialize image scanlines.
9523 */
glennrp5af765f2010-03-30 11:12:18 +00009524 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009525 {
9526 /*
9527 PNG write failed.
9528 */
9529#ifdef PNG_DEBUG
9530 if (image_info->verbose)
9531 (void) printf("PNG write has failed.\n");
9532#endif
9533 png_destroy_write_struct(&ping,&ping_info);
9534 if (quantum_info != (QuantumInfo *) NULL)
9535 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009536 if (ping_pixels != (unsigned char *) NULL)
9537 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009538#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009539 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009540#endif
glennrpda8f3a72011-02-27 23:54:12 +00009541 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009542 (void) CloseBlob(image);
9543 image_info=DestroyImageInfo(image_info);
9544 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009545 return(MagickFalse);
9546 }
cristyed552522009-10-16 14:04:35 +00009547 quantum_info=AcquireQuantumInfo(image_info,image);
9548 if (quantum_info == (QuantumInfo *) NULL)
9549 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009550 quantum_info->format=UndefinedQuantumFormat;
9551 quantum_info->depth=image_depth;
9552 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009553
cristy3ed852e2009-09-05 21:47:34 +00009554 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009555 !mng_info->write_png32) &&
9556 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009557 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009558 image_matte == MagickFalse &&
9559 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009560 {
glennrp8bb3a022010-12-13 20:40:04 +00009561 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009562 register const PixelPacket
9563 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009564
cristy3ed852e2009-09-05 21:47:34 +00009565 quantum_info->depth=8;
9566 for (pass=0; pass < num_passes; pass++)
9567 {
9568 /*
9569 Convert PseudoClass image to a PNG monochrome image.
9570 */
cristybb503372010-05-27 20:51:26 +00009571 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009572 {
glennrpd71e86a2011-02-24 01:28:37 +00009573 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9575 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009576
cristy3ed852e2009-09-05 21:47:34 +00009577 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009578
cristy3ed852e2009-09-05 21:47:34 +00009579 if (p == (const PixelPacket *) NULL)
9580 break;
glennrp0fe50b42010-11-16 03:52:51 +00009581
cristy3ed852e2009-09-05 21:47:34 +00009582 if (mng_info->IsPalette)
9583 {
9584 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009585 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009586 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9587 mng_info->write_png_depth &&
9588 mng_info->write_png_depth != old_bit_depth)
9589 {
9590 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009591 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009592 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009593 >> (8-old_bit_depth));
9594 }
9595 }
glennrp0fe50b42010-11-16 03:52:51 +00009596
cristy3ed852e2009-09-05 21:47:34 +00009597 else
9598 {
9599 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009600 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009601 }
glennrp0fe50b42010-11-16 03:52:51 +00009602
cristy3ed852e2009-09-05 21:47:34 +00009603 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009604 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009605 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009606 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009607
glennrp3b51f0e2010-11-27 18:14:08 +00009608 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9610 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009611
glennrpcf002022011-01-30 02:38:15 +00009612 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009613 }
9614 if (image->previous == (Image *) NULL)
9615 {
9616 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9617 if (status == MagickFalse)
9618 break;
9619 }
9620 }
9621 }
glennrp0fe50b42010-11-16 03:52:51 +00009622
glennrp8bb3a022010-12-13 20:40:04 +00009623 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009624 {
glennrp0fe50b42010-11-16 03:52:51 +00009625 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009626 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009627 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009628 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009629 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009630 {
glennrp8bb3a022010-12-13 20:40:04 +00009631 register const PixelPacket
9632 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009633
glennrp8bb3a022010-12-13 20:40:04 +00009634 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009635 {
glennrp8bb3a022010-12-13 20:40:04 +00009636
cristybb503372010-05-27 20:51:26 +00009637 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009638 {
9639 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009640
cristy3ed852e2009-09-05 21:47:34 +00009641 if (p == (const PixelPacket *) NULL)
9642 break;
glennrp2cc891a2010-12-24 13:44:32 +00009643
glennrp5af765f2010-03-30 11:12:18 +00009644 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009645 {
glennrp8bb3a022010-12-13 20:40:04 +00009646 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009647 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009648 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009649
glennrp8bb3a022010-12-13 20:40:04 +00009650 else
9651 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009652 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009653
glennrp3b51f0e2010-11-27 18:14:08 +00009654 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009656 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009657 }
glennrp2cc891a2010-12-24 13:44:32 +00009658
glennrp8bb3a022010-12-13 20:40:04 +00009659 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9660 {
9661 if (logging != MagickFalse && y == 0)
9662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9663 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009664
glennrp8bb3a022010-12-13 20:40:04 +00009665 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009666 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009667 }
glennrp2cc891a2010-12-24 13:44:32 +00009668
glennrp3b51f0e2010-11-27 18:14:08 +00009669 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009671 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009672
glennrpcf002022011-01-30 02:38:15 +00009673 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009674 }
glennrp2cc891a2010-12-24 13:44:32 +00009675
glennrp8bb3a022010-12-13 20:40:04 +00009676 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009677 {
glennrp8bb3a022010-12-13 20:40:04 +00009678 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9679 if (status == MagickFalse)
9680 break;
cristy3ed852e2009-09-05 21:47:34 +00009681 }
cristy3ed852e2009-09-05 21:47:34 +00009682 }
9683 }
glennrp8bb3a022010-12-13 20:40:04 +00009684
9685 else
9686 {
9687 register const PixelPacket
9688 *p;
9689
9690 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009691 {
glennrp8bb3a022010-12-13 20:40:04 +00009692 if ((image_depth > 8) || (mng_info->write_png24 ||
9693 mng_info->write_png32 ||
9694 (!mng_info->write_png8 && !mng_info->IsPalette)))
9695 {
9696 for (y=0; y < (ssize_t) image->rows; y++)
9697 {
9698 p=GetVirtualPixels(image,0,y,image->columns,1,
9699 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009700
glennrp8bb3a022010-12-13 20:40:04 +00009701 if (p == (const PixelPacket *) NULL)
9702 break;
glennrp2cc891a2010-12-24 13:44:32 +00009703
glennrp8bb3a022010-12-13 20:40:04 +00009704 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9705 {
9706 if (image->storage_class == DirectClass)
9707 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009708 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009709
glennrp8bb3a022010-12-13 20:40:04 +00009710 else
9711 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009712 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009713 }
glennrp2cc891a2010-12-24 13:44:32 +00009714
glennrp8bb3a022010-12-13 20:40:04 +00009715 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9716 {
9717 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009718 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009719 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009720
glennrp8bb3a022010-12-13 20:40:04 +00009721 if (logging != MagickFalse && y == 0)
9722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9723 " Writing GRAY_ALPHA PNG pixels (3)");
9724 }
glennrp2cc891a2010-12-24 13:44:32 +00009725
glennrp8bb3a022010-12-13 20:40:04 +00009726 else if (image_matte != MagickFalse)
9727 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009728 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009729
glennrp8bb3a022010-12-13 20:40:04 +00009730 else
9731 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009732 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009733
glennrp8bb3a022010-12-13 20:40:04 +00009734 if (logging != MagickFalse && y == 0)
9735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9736 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009737
glennrpcf002022011-01-30 02:38:15 +00009738 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009739 }
9740 }
glennrp2cc891a2010-12-24 13:44:32 +00009741
glennrp8bb3a022010-12-13 20:40:04 +00009742 else
9743 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9744 mng_info->write_png32 ||
9745 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9746 {
9747 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9748 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9749 {
9750 if (logging != MagickFalse)
9751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9752 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009753
glennrp8bb3a022010-12-13 20:40:04 +00009754 quantum_info->depth=8;
9755 image_depth=8;
9756 }
glennrp2cc891a2010-12-24 13:44:32 +00009757
glennrp8bb3a022010-12-13 20:40:04 +00009758 for (y=0; y < (ssize_t) image->rows; y++)
9759 {
9760 if (logging != MagickFalse && y == 0)
9761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9762 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009763
glennrp770d1932011-03-06 22:11:17 +00009764 p=GetVirtualPixels(image,0,y,image->columns,1,
9765 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009766
glennrp8bb3a022010-12-13 20:40:04 +00009767 if (p == (const PixelPacket *) NULL)
9768 break;
glennrp2cc891a2010-12-24 13:44:32 +00009769
glennrp8bb3a022010-12-13 20:40:04 +00009770 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009771 {
glennrp4bf89732011-03-21 13:48:28 +00009772 quantum_info->depth=image->depth;
9773
glennrp44757ab2011-03-17 12:57:03 +00009774 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009775 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009776 }
glennrp2cc891a2010-12-24 13:44:32 +00009777
glennrp8bb3a022010-12-13 20:40:04 +00009778 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9779 {
9780 if (logging != MagickFalse && y == 0)
9781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9782 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009783
glennrp8bb3a022010-12-13 20:40:04 +00009784 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009785 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009786 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009787 }
glennrp2cc891a2010-12-24 13:44:32 +00009788
glennrp8bb3a022010-12-13 20:40:04 +00009789 else
glennrp8bb3a022010-12-13 20:40:04 +00009790 {
glennrp179d0752011-03-17 13:02:10 +00009791 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009792 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9793
9794 if (logging != MagickFalse && y <= 2)
9795 {
9796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009797 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009798
9799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9800 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9801 (int)ping_pixels[0],(int)ping_pixels[1]);
9802 }
glennrp8bb3a022010-12-13 20:40:04 +00009803 }
glennrpcf002022011-01-30 02:38:15 +00009804 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009805 }
9806 }
glennrp2cc891a2010-12-24 13:44:32 +00009807
glennrp8bb3a022010-12-13 20:40:04 +00009808 if (image->previous == (Image *) NULL)
9809 {
9810 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9811 if (status == MagickFalse)
9812 break;
9813 }
cristy3ed852e2009-09-05 21:47:34 +00009814 }
glennrp8bb3a022010-12-13 20:40:04 +00009815 }
9816 }
9817
cristyb32b90a2009-09-07 21:45:48 +00009818 if (quantum_info != (QuantumInfo *) NULL)
9819 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009820
9821 if (logging != MagickFalse)
9822 {
9823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009824 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009825
cristy3ed852e2009-09-05 21:47:34 +00009826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009827 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009828
cristy3ed852e2009-09-05 21:47:34 +00009829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009830 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009831
cristy3ed852e2009-09-05 21:47:34 +00009832 if (mng_info->write_png_depth)
9833 {
9834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9835 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9836 }
glennrp0fe50b42010-11-16 03:52:51 +00009837
cristy3ed852e2009-09-05 21:47:34 +00009838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009839 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009840
cristy3ed852e2009-09-05 21:47:34 +00009841 if (mng_info->write_png_colortype)
9842 {
9843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9844 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9845 }
glennrp0fe50b42010-11-16 03:52:51 +00009846
cristy3ed852e2009-09-05 21:47:34 +00009847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009848 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009849
cristy3ed852e2009-09-05 21:47:34 +00009850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009851 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009852 }
9853 /*
glennrpa0ed0092011-04-18 16:36:29 +00009854 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009855 */
glennrp823b55c2011-03-14 18:46:46 +00009856 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009857 {
glennrp26f37912010-12-23 16:22:42 +00009858 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009859 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009860 while (property != (const char *) NULL)
9861 {
9862 png_textp
9863 text;
glennrp2cc891a2010-12-24 13:44:32 +00009864
glennrp26f37912010-12-23 16:22:42 +00009865 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009866
9867 /* Don't write any "png:" properties; those are just for "identify" */
9868 if (LocaleNCompare(property,"png:",4) != 0 &&
9869
9870 /* Suppress density and units if we wrote a pHYs chunk */
9871 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009872 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009873 LocaleCompare(property,"units") != 0) &&
9874
9875 /* Suppress the IM-generated Date:create and Date:modify */
9876 (ping_exclude_date == MagickFalse ||
9877 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009878 {
glennrpc70af4a2011-03-07 00:08:23 +00009879 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009880 {
glennrpc70af4a2011-03-07 00:08:23 +00009881 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9882 text[0].key=(char *) property;
9883 text[0].text=(char *) value;
9884 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009885
glennrpc70af4a2011-03-07 00:08:23 +00009886 if (ping_exclude_tEXt != MagickFalse)
9887 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9888
9889 else if (ping_exclude_zTXt != MagickFalse)
9890 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9891
9892 else
glennrp26f37912010-12-23 16:22:42 +00009893 {
glennrpc70af4a2011-03-07 00:08:23 +00009894 text[0].compression=image_info->compression == NoCompression ||
9895 (image_info->compression == UndefinedCompression &&
9896 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9897 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009898 }
glennrp2cc891a2010-12-24 13:44:32 +00009899
glennrpc70af4a2011-03-07 00:08:23 +00009900 if (logging != MagickFalse)
9901 {
9902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9903 " Setting up text chunk");
9904
9905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9906 " keyword: %s",text[0].key);
9907 }
9908
9909 png_set_text(ping,ping_info,text,1);
9910 png_free(ping,text);
9911 }
glennrp26f37912010-12-23 16:22:42 +00009912 }
9913 property=GetNextImageProperty(image);
9914 }
cristy3ed852e2009-09-05 21:47:34 +00009915 }
9916
9917 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009918 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009919
9920 if (logging != MagickFalse)
9921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9922 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009923
cristy3ed852e2009-09-05 21:47:34 +00009924 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009925
cristy3ed852e2009-09-05 21:47:34 +00009926 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9927 {
9928 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009929 (ping_width != mng_info->page.width) ||
9930 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009931 {
9932 unsigned char
9933 chunk[32];
9934
9935 /*
9936 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9937 */
9938 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9939 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009940 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009941 chunk[4]=4;
9942 chunk[5]=0; /* frame name separator (no name) */
9943 chunk[6]=1; /* flag for changing delay, for next frame only */
9944 chunk[7]=0; /* flag for changing frame timeout */
9945 chunk[8]=1; /* flag for changing frame clipping for next frame */
9946 chunk[9]=0; /* flag for changing frame sync_id */
9947 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9948 chunk[14]=0; /* clipping boundaries delta type */
9949 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9950 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009951 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009952 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9953 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009954 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009955 (void) WriteBlob(image,31,chunk);
9956 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9957 mng_info->old_framing_mode=4;
9958 mng_info->framing_mode=1;
9959 }
glennrp0fe50b42010-11-16 03:52:51 +00009960
cristy3ed852e2009-09-05 21:47:34 +00009961 else
9962 mng_info->framing_mode=3;
9963 }
9964 if (mng_info->write_mng && !mng_info->need_fram &&
9965 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009966 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009967 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009968 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009969
cristy3ed852e2009-09-05 21:47:34 +00009970 /*
9971 Free PNG resources.
9972 */
glennrp5af765f2010-03-30 11:12:18 +00009973
cristy3ed852e2009-09-05 21:47:34 +00009974 png_destroy_write_struct(&ping,&ping_info);
9975
glennrpcf002022011-01-30 02:38:15 +00009976 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009977
9978#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009979 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009980#endif
9981
glennrpda8f3a72011-02-27 23:54:12 +00009982 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009983 (void) CloseBlob(image);
9984
9985 image_info=DestroyImageInfo(image_info);
9986 image=DestroyImage(image);
9987
9988 /* Store bit depth actually written */
9989 s[0]=(char) ping_bit_depth;
9990 s[1]='\0';
9991
9992 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9993
cristy3ed852e2009-09-05 21:47:34 +00009994 if (logging != MagickFalse)
9995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9996 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009997
cristy3ed852e2009-09-05 21:47:34 +00009998 return(MagickTrue);
9999/* End write one PNG image */
10000}
10001
10002/*
10003%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10004% %
10005% %
10006% %
10007% W r i t e P N G I m a g e %
10008% %
10009% %
10010% %
10011%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10012%
10013% WritePNGImage() writes a Portable Network Graphics (PNG) or
10014% Multiple-image Network Graphics (MNG) image file.
10015%
10016% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10017%
10018% The format of the WritePNGImage method is:
10019%
10020% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10021%
10022% A description of each parameter follows:
10023%
10024% o image_info: the image info.
10025%
10026% o image: The image.
10027%
10028% Returns MagickTrue on success, MagickFalse on failure.
10029%
10030% Communicating with the PNG encoder:
10031%
10032% While the datastream written is always in PNG format and normally would
10033% be given the "png" file extension, this method also writes the following
10034% pseudo-formats which are subsets of PNG:
10035%
glennrp5a39f372011-02-25 04:52:16 +000010036% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10037% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010038% is present, the tRNS chunk must only have values 0 and 255
10039% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010040% transparent). If other values are present they will be
10041% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010042% colors are present, they will be quantized to the 4-4-4-1,
10043% 3-3-3-1, or 3-3-2-1 palette.
10044%
10045% If you want better quantization or dithering of the colors
10046% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010047% PNG encoder. The pixels contain 8-bit indices even if
10048% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010049% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010050% PNG grayscale type might be slightly more efficient. Please
10051% note that writing to the PNG8 format may result in loss
10052% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010053%
10054% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10055% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010056% one of the colors as transparent. The only loss incurred
10057% is reduction of sample depth to 8. If the image has more
10058% than one transparent color, has semitransparent pixels, or
10059% has an opaque pixel with the same RGB components as the
10060% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010061%
10062% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10063% transparency is permitted, i.e., the alpha sample for
10064% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010065% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010066% The only loss in data is the reduction of the sample depth
10067% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010068%
10069% o -define: For more precise control of the PNG output, you can use the
10070% Image options "png:bit-depth" and "png:color-type". These
10071% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010072% from the application programming interfaces. The options
10073% are case-independent and are converted to lowercase before
10074% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010075%
10076% png:color-type can be 0, 2, 3, 4, or 6.
10077%
10078% When png:color-type is 0 (Grayscale), png:bit-depth can
10079% be 1, 2, 4, 8, or 16.
10080%
10081% When png:color-type is 2 (RGB), png:bit-depth can
10082% be 8 or 16.
10083%
10084% When png:color-type is 3 (Indexed), png:bit-depth can
10085% be 1, 2, 4, or 8. This refers to the number of bits
10086% used to store the index. The color samples always have
10087% bit-depth 8 in indexed PNG files.
10088%
10089% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10090% png:bit-depth can be 8 or 16.
10091%
glennrp5a39f372011-02-25 04:52:16 +000010092% If the image cannot be written without loss with the requested bit-depth
10093% and color-type, a PNG file will not be written, and the encoder will
10094% return MagickFalse.
10095%
cristy3ed852e2009-09-05 21:47:34 +000010096% Since image encoders should not be responsible for the "heavy lifting",
10097% the user should make sure that ImageMagick has already reduced the
10098% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010099% transparency prior to attempting to write the image with depth, color,
10100% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010101%
glennrp97cefe22011-04-22 16:17:00 +000010102% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010103%
cristy3ed852e2009-09-05 21:47:34 +000010104% Note that another definition, "png:bit-depth-written" exists, but it
10105% is not intended for external use. It is only used internally by the
10106% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10107%
10108% It is possible to request that the PNG encoder write previously-formatted
10109% ancillary chunks in the output PNG file, using the "-profile" commandline
10110% option as shown below or by setting the profile via a programming
10111% interface:
10112%
10113% -profile PNG-chunk-x:<file>
10114%
10115% where x is a location flag and <file> is a file containing the chunk
10116% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010117% This encoder will compute the chunk length and CRC, so those must not
10118% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010119%
10120% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10121% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10122% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010123% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010124%
glennrpbb8a7332010-11-13 15:17:35 +000010125% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010126%
glennrp3241bd02010-12-12 04:36:28 +000010127% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010128%
glennrpd6afd542010-11-19 01:53:05 +000010129% o 32-bit depth is reduced to 16.
10130% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10131% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010132% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010133% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010134% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010135% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10136% this can be done without loss and a larger bit depth N was not
10137% requested via the "-define PNG:bit-depth=N" option.
10138% o If matte channel is present but only one transparent color is
10139% present, RGB+tRNS is written instead of RGBA
10140% o Opaque matte channel is removed (or added, if color-type 4 or 6
10141% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010142%
cristy3ed852e2009-09-05 21:47:34 +000010143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10144*/
10145static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10146 Image *image)
10147{
10148 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010149 excluding,
10150 logging,
10151 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010152 status;
10153
10154 MngInfo
10155 *mng_info;
10156
10157 const char
10158 *value;
10159
10160 int
glennrp21f0e622011-01-07 16:20:57 +000010161 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010162 source;
10163
cristy3ed852e2009-09-05 21:47:34 +000010164 /*
10165 Open image file.
10166 */
10167 assert(image_info != (const ImageInfo *) NULL);
10168 assert(image_info->signature == MagickSignature);
10169 assert(image != (Image *) NULL);
10170 assert(image->signature == MagickSignature);
10171 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010172 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010173 /*
10174 Allocate a MngInfo structure.
10175 */
10176 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010177 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010178
cristy3ed852e2009-09-05 21:47:34 +000010179 if (mng_info == (MngInfo *) NULL)
10180 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010181
cristy3ed852e2009-09-05 21:47:34 +000010182 /*
10183 Initialize members of the MngInfo structure.
10184 */
10185 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10186 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010187 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010188 have_mng_structure=MagickTrue;
10189
10190 /* See if user has requested a specific PNG subformat */
10191
10192 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10193 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10194 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10195
10196 if (mng_info->write_png8)
10197 {
glennrp9c1eb072010-06-06 22:19:15 +000010198 mng_info->write_png_colortype = /* 3 */ 4;
10199 mng_info->write_png_depth = 8;
10200 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010201 }
10202
10203 if (mng_info->write_png24)
10204 {
glennrp9c1eb072010-06-06 22:19:15 +000010205 mng_info->write_png_colortype = /* 2 */ 3;
10206 mng_info->write_png_depth = 8;
10207 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010208
glennrp9c1eb072010-06-06 22:19:15 +000010209 if (image->matte == MagickTrue)
10210 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010211
glennrp9c1eb072010-06-06 22:19:15 +000010212 else
10213 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010214
glennrp9c1eb072010-06-06 22:19:15 +000010215 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010216 }
10217
10218 if (mng_info->write_png32)
10219 {
glennrp9c1eb072010-06-06 22:19:15 +000010220 mng_info->write_png_colortype = /* 6 */ 7;
10221 mng_info->write_png_depth = 8;
10222 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010223
glennrp9c1eb072010-06-06 22:19:15 +000010224 if (image->matte == MagickTrue)
10225 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010226
glennrp9c1eb072010-06-06 22:19:15 +000010227 else
10228 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010229
glennrp9c1eb072010-06-06 22:19:15 +000010230 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010231 }
10232
10233 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010234
cristy3ed852e2009-09-05 21:47:34 +000010235 if (value != (char *) NULL)
10236 {
10237 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010238 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010239
cristy3ed852e2009-09-05 21:47:34 +000010240 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010241 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010242
cristy3ed852e2009-09-05 21:47:34 +000010243 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010244 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010245
cristy3ed852e2009-09-05 21:47:34 +000010246 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010247 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010248
cristy3ed852e2009-09-05 21:47:34 +000010249 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010250 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010251
glennrpbb8a7332010-11-13 15:17:35 +000010252 else
10253 (void) ThrowMagickException(&image->exception,
10254 GetMagickModule(),CoderWarning,
10255 "ignoring invalid defined png:bit-depth",
10256 "=%s",value);
10257
cristy3ed852e2009-09-05 21:47:34 +000010258 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010260 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010261 }
glennrp0fe50b42010-11-16 03:52:51 +000010262
cristy3ed852e2009-09-05 21:47:34 +000010263 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010264
cristy3ed852e2009-09-05 21:47:34 +000010265 if (value != (char *) NULL)
10266 {
10267 /* We must store colortype+1 because 0 is a valid colortype */
10268 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010269 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010270
cristy3ed852e2009-09-05 21:47:34 +000010271 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010272 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010273
cristy3ed852e2009-09-05 21:47:34 +000010274 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010275 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010276
cristy3ed852e2009-09-05 21:47:34 +000010277 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010278 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010279
cristy3ed852e2009-09-05 21:47:34 +000010280 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010281 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010282
glennrpbb8a7332010-11-13 15:17:35 +000010283 else
10284 (void) ThrowMagickException(&image->exception,
10285 GetMagickModule(),CoderWarning,
10286 "ignoring invalid defined png:color-type",
10287 "=%s",value);
10288
cristy3ed852e2009-09-05 21:47:34 +000010289 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010291 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010292 }
10293
glennrp0e8ea192010-12-24 18:00:33 +000010294 /* Check for chunks to be excluded:
10295 *
glennrp0dff56c2011-01-29 19:10:02 +000010296 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010297 * listed in the "unused_chunks" array, above.
10298 *
10299 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10300 * define (in the image properties or in the image artifacts)
10301 * or via a mng_info member. For convenience, in addition
10302 * to or instead of a comma-separated list of chunks, the
10303 * "exclude-chunk" string can be simply "all" or "none".
10304 *
10305 * The exclude-chunk define takes priority over the mng_info.
10306 *
10307 * A "PNG:include-chunk" define takes priority over both the
10308 * mng_info and the "PNG:exclude-chunk" define. Like the
10309 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010310 * well as a comma-separated list. Chunks that are unknown to
10311 * ImageMagick are always excluded, regardless of their "copy-safe"
10312 * status according to the PNG specification, and even if they
10313 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010314 *
10315 * Finally, all chunks listed in the "unused_chunks" array are
10316 * automatically excluded, regardless of the other instructions
10317 * or lack thereof.
10318 *
10319 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10320 * will not be written and the gAMA chunk will only be written if it
10321 * is not between .45 and .46, or approximately (1.0/2.2).
10322 *
10323 * If you exclude tRNS and the image has transparency, the colortype
10324 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10325 *
10326 * The -strip option causes StripImage() to set the png:include-chunk
10327 * artifact to "none,gama".
10328 */
10329
glennrp26f37912010-12-23 16:22:42 +000010330 mng_info->ping_exclude_bKGD=MagickFalse;
10331 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010332 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010333 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10334 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010335 mng_info->ping_exclude_iCCP=MagickFalse;
10336 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10337 mng_info->ping_exclude_oFFs=MagickFalse;
10338 mng_info->ping_exclude_pHYs=MagickFalse;
10339 mng_info->ping_exclude_sRGB=MagickFalse;
10340 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010341 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010342 mng_info->ping_exclude_vpAg=MagickFalse;
10343 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10344 mng_info->ping_exclude_zTXt=MagickFalse;
10345
glennrp8d3d6e52011-04-19 04:39:51 +000010346 mng_info->ping_preserve_colormap=MagickFalse;
10347
10348 value=GetImageArtifact(image,"png:preserve-colormap");
10349 if (value == NULL)
10350 value=GetImageOption(image_info,"png:preserve-colormap");
10351 if (value != NULL)
10352 mng_info->ping_preserve_colormap=MagickTrue;
10353
glennrp03812ae2010-12-24 01:31:34 +000010354 excluding=MagickFalse;
10355
glennrp5c7cf4e2010-12-24 00:30:00 +000010356 for (source=0; source<1; source++)
10357 {
10358 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010359 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010360 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010361
10362 if (value == NULL)
10363 value=GetImageArtifact(image,"png:exclude-chunks");
10364 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010365 else
glennrpacba0042010-12-24 14:27:26 +000010366 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010367 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010368
glennrpacba0042010-12-24 14:27:26 +000010369 if (value == NULL)
10370 value=GetImageOption(image_info,"png:exclude-chunks");
10371 }
10372
glennrp03812ae2010-12-24 01:31:34 +000010373 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010374 {
glennrp03812ae2010-12-24 01:31:34 +000010375
10376 size_t
10377 last;
10378
10379 excluding=MagickTrue;
10380
10381 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010382 {
10383 if (source == 0)
10384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10385 " png:exclude-chunk=%s found in image artifacts.\n", value);
10386 else
10387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10388 " png:exclude-chunk=%s found in image properties.\n", value);
10389 }
glennrp03812ae2010-12-24 01:31:34 +000010390
10391 last=strlen(value);
10392
10393 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010394 {
glennrp03812ae2010-12-24 01:31:34 +000010395
10396 if (LocaleNCompare(value+i,"all",3) == 0)
10397 {
10398 mng_info->ping_exclude_bKGD=MagickTrue;
10399 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010400 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010401 mng_info->ping_exclude_EXIF=MagickTrue;
10402 mng_info->ping_exclude_gAMA=MagickTrue;
10403 mng_info->ping_exclude_iCCP=MagickTrue;
10404 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10405 mng_info->ping_exclude_oFFs=MagickTrue;
10406 mng_info->ping_exclude_pHYs=MagickTrue;
10407 mng_info->ping_exclude_sRGB=MagickTrue;
10408 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010409 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010410 mng_info->ping_exclude_vpAg=MagickTrue;
10411 mng_info->ping_exclude_zCCP=MagickTrue;
10412 mng_info->ping_exclude_zTXt=MagickTrue;
10413 i--;
10414 }
glennrp2cc891a2010-12-24 13:44:32 +000010415
glennrp03812ae2010-12-24 01:31:34 +000010416 if (LocaleNCompare(value+i,"none",4) == 0)
10417 {
10418 mng_info->ping_exclude_bKGD=MagickFalse;
10419 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010420 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010421 mng_info->ping_exclude_EXIF=MagickFalse;
10422 mng_info->ping_exclude_gAMA=MagickFalse;
10423 mng_info->ping_exclude_iCCP=MagickFalse;
10424 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10425 mng_info->ping_exclude_oFFs=MagickFalse;
10426 mng_info->ping_exclude_pHYs=MagickFalse;
10427 mng_info->ping_exclude_sRGB=MagickFalse;
10428 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010429 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010430 mng_info->ping_exclude_vpAg=MagickFalse;
10431 mng_info->ping_exclude_zCCP=MagickFalse;
10432 mng_info->ping_exclude_zTXt=MagickFalse;
10433 }
glennrp2cc891a2010-12-24 13:44:32 +000010434
glennrp03812ae2010-12-24 01:31:34 +000010435 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10436 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010437
glennrp03812ae2010-12-24 01:31:34 +000010438 if (LocaleNCompare(value+i,"chrm",4) == 0)
10439 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010440
glennrpa0ed0092011-04-18 16:36:29 +000010441 if (LocaleNCompare(value+i,"date",4) == 0)
10442 mng_info->ping_exclude_date=MagickTrue;
10443
glennrp03812ae2010-12-24 01:31:34 +000010444 if (LocaleNCompare(value+i,"exif",4) == 0)
10445 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010446
glennrp03812ae2010-12-24 01:31:34 +000010447 if (LocaleNCompare(value+i,"gama",4) == 0)
10448 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010449
glennrp03812ae2010-12-24 01:31:34 +000010450 if (LocaleNCompare(value+i,"iccp",4) == 0)
10451 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010452
glennrp03812ae2010-12-24 01:31:34 +000010453 /*
10454 if (LocaleNCompare(value+i,"itxt",4) == 0)
10455 mng_info->ping_exclude_iTXt=MagickTrue;
10456 */
glennrp2cc891a2010-12-24 13:44:32 +000010457
glennrp03812ae2010-12-24 01:31:34 +000010458 if (LocaleNCompare(value+i,"gama",4) == 0)
10459 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010460
glennrp03812ae2010-12-24 01:31:34 +000010461 if (LocaleNCompare(value+i,"offs",4) == 0)
10462 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010463
glennrp03812ae2010-12-24 01:31:34 +000010464 if (LocaleNCompare(value+i,"phys",4) == 0)
10465 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010466
glennrpa1e3b7b2010-12-24 16:37:33 +000010467 if (LocaleNCompare(value+i,"srgb",4) == 0)
10468 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010469
glennrp03812ae2010-12-24 01:31:34 +000010470 if (LocaleNCompare(value+i,"text",4) == 0)
10471 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010472
glennrpa1e3b7b2010-12-24 16:37:33 +000010473 if (LocaleNCompare(value+i,"trns",4) == 0)
10474 mng_info->ping_exclude_tRNS=MagickTrue;
10475
glennrp03812ae2010-12-24 01:31:34 +000010476 if (LocaleNCompare(value+i,"vpag",4) == 0)
10477 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010478
glennrp03812ae2010-12-24 01:31:34 +000010479 if (LocaleNCompare(value+i,"zccp",4) == 0)
10480 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010481
glennrp03812ae2010-12-24 01:31:34 +000010482 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10483 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010484
glennrp03812ae2010-12-24 01:31:34 +000010485 }
glennrpce91ed52010-12-23 22:37:49 +000010486 }
glennrp26f37912010-12-23 16:22:42 +000010487 }
10488
glennrp5c7cf4e2010-12-24 00:30:00 +000010489 for (source=0; source<1; source++)
10490 {
10491 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010492 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010493 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010494
10495 if (value == NULL)
10496 value=GetImageArtifact(image,"png:include-chunks");
10497 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010498 else
glennrpacba0042010-12-24 14:27:26 +000010499 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010500 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010501
glennrpacba0042010-12-24 14:27:26 +000010502 if (value == NULL)
10503 value=GetImageOption(image_info,"png:include-chunks");
10504 }
10505
glennrp03812ae2010-12-24 01:31:34 +000010506 if (value != NULL)
10507 {
10508 size_t
10509 last;
glennrp26f37912010-12-23 16:22:42 +000010510
glennrp03812ae2010-12-24 01:31:34 +000010511 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010512
glennrp03812ae2010-12-24 01:31:34 +000010513 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010514 {
10515 if (source == 0)
10516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10517 " png:include-chunk=%s found in image artifacts.\n", value);
10518 else
10519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10520 " png:include-chunk=%s found in image properties.\n", value);
10521 }
glennrp03812ae2010-12-24 01:31:34 +000010522
10523 last=strlen(value);
10524
10525 for (i=0; i<(int) last; i+=5)
10526 {
10527 if (LocaleNCompare(value+i,"all",3) == 0)
10528 {
10529 mng_info->ping_exclude_bKGD=MagickFalse;
10530 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010531 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010532 mng_info->ping_exclude_EXIF=MagickFalse;
10533 mng_info->ping_exclude_gAMA=MagickFalse;
10534 mng_info->ping_exclude_iCCP=MagickFalse;
10535 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10536 mng_info->ping_exclude_oFFs=MagickFalse;
10537 mng_info->ping_exclude_pHYs=MagickFalse;
10538 mng_info->ping_exclude_sRGB=MagickFalse;
10539 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010540 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010541 mng_info->ping_exclude_vpAg=MagickFalse;
10542 mng_info->ping_exclude_zCCP=MagickFalse;
10543 mng_info->ping_exclude_zTXt=MagickFalse;
10544 i--;
10545 }
glennrp2cc891a2010-12-24 13:44:32 +000010546
glennrp03812ae2010-12-24 01:31:34 +000010547 if (LocaleNCompare(value+i,"none",4) == 0)
10548 {
10549 mng_info->ping_exclude_bKGD=MagickTrue;
10550 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010551 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010552 mng_info->ping_exclude_EXIF=MagickTrue;
10553 mng_info->ping_exclude_gAMA=MagickTrue;
10554 mng_info->ping_exclude_iCCP=MagickTrue;
10555 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10556 mng_info->ping_exclude_oFFs=MagickTrue;
10557 mng_info->ping_exclude_pHYs=MagickTrue;
10558 mng_info->ping_exclude_sRGB=MagickTrue;
10559 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010560 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010561 mng_info->ping_exclude_vpAg=MagickTrue;
10562 mng_info->ping_exclude_zCCP=MagickTrue;
10563 mng_info->ping_exclude_zTXt=MagickTrue;
10564 }
glennrp2cc891a2010-12-24 13:44:32 +000010565
glennrp03812ae2010-12-24 01:31:34 +000010566 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10567 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010568
glennrp03812ae2010-12-24 01:31:34 +000010569 if (LocaleNCompare(value+i,"chrm",4) == 0)
10570 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010571
glennrpa0ed0092011-04-18 16:36:29 +000010572 if (LocaleNCompare(value+i,"date",4) == 0)
10573 mng_info->ping_exclude_date=MagickFalse;
10574
glennrp03812ae2010-12-24 01:31:34 +000010575 if (LocaleNCompare(value+i,"exif",4) == 0)
10576 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010577
glennrp03812ae2010-12-24 01:31:34 +000010578 if (LocaleNCompare(value+i,"gama",4) == 0)
10579 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010580
glennrp03812ae2010-12-24 01:31:34 +000010581 if (LocaleNCompare(value+i,"iccp",4) == 0)
10582 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010583
glennrp03812ae2010-12-24 01:31:34 +000010584 /*
10585 if (LocaleNCompare(value+i,"itxt",4) == 0)
10586 mng_info->ping_exclude_iTXt=MagickFalse;
10587 */
glennrp2cc891a2010-12-24 13:44:32 +000010588
glennrp03812ae2010-12-24 01:31:34 +000010589 if (LocaleNCompare(value+i,"gama",4) == 0)
10590 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010591
glennrp03812ae2010-12-24 01:31:34 +000010592 if (LocaleNCompare(value+i,"offs",4) == 0)
10593 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010594
glennrp03812ae2010-12-24 01:31:34 +000010595 if (LocaleNCompare(value+i,"phys",4) == 0)
10596 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010597
glennrpa1e3b7b2010-12-24 16:37:33 +000010598 if (LocaleNCompare(value+i,"srgb",4) == 0)
10599 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010600
glennrp03812ae2010-12-24 01:31:34 +000010601 if (LocaleNCompare(value+i,"text",4) == 0)
10602 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010603
glennrpa1e3b7b2010-12-24 16:37:33 +000010604 if (LocaleNCompare(value+i,"trns",4) == 0)
10605 mng_info->ping_exclude_tRNS=MagickFalse;
10606
glennrp03812ae2010-12-24 01:31:34 +000010607 if (LocaleNCompare(value+i,"vpag",4) == 0)
10608 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010609
glennrp03812ae2010-12-24 01:31:34 +000010610 if (LocaleNCompare(value+i,"zccp",4) == 0)
10611 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010612
glennrp03812ae2010-12-24 01:31:34 +000010613 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10614 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010615
glennrp03812ae2010-12-24 01:31:34 +000010616 }
glennrpce91ed52010-12-23 22:37:49 +000010617 }
glennrp26f37912010-12-23 16:22:42 +000010618 }
10619
glennrp03812ae2010-12-24 01:31:34 +000010620 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010621 {
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10623 " Chunks to be excluded from the output PNG:");
10624 if (mng_info->ping_exclude_bKGD != MagickFalse)
10625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10626 " bKGD");
10627 if (mng_info->ping_exclude_cHRM != MagickFalse)
10628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10629 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010630 if (mng_info->ping_exclude_date != MagickFalse)
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 " date");
glennrp26f37912010-12-23 16:22:42 +000010633 if (mng_info->ping_exclude_EXIF != MagickFalse)
10634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10635 " EXIF");
10636 if (mng_info->ping_exclude_gAMA != MagickFalse)
10637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10638 " gAMA");
10639 if (mng_info->ping_exclude_iCCP != MagickFalse)
10640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10641 " iCCP");
10642/*
10643 if (mng_info->ping_exclude_iTXt != MagickFalse)
10644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10645 " iTXt");
10646*/
10647 if (mng_info->ping_exclude_oFFs != MagickFalse)
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 " oFFs");
10650 if (mng_info->ping_exclude_pHYs != MagickFalse)
10651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652 " pHYs");
10653 if (mng_info->ping_exclude_sRGB != MagickFalse)
10654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10655 " sRGB");
10656 if (mng_info->ping_exclude_tEXt != MagickFalse)
10657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10658 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010659 if (mng_info->ping_exclude_tRNS != MagickFalse)
10660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10661 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010662 if (mng_info->ping_exclude_vpAg != MagickFalse)
10663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10664 " vpAg");
10665 if (mng_info->ping_exclude_zCCP != MagickFalse)
10666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10667 " zCCP");
10668 if (mng_info->ping_exclude_zTXt != MagickFalse)
10669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10670 " zTXt");
10671 }
10672
glennrpb9cfe272010-12-21 15:08:06 +000010673 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010674
glennrpb9cfe272010-12-21 15:08:06 +000010675 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010676
10677 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010678
cristy3ed852e2009-09-05 21:47:34 +000010679 if (logging != MagickFalse)
10680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010681
cristy3ed852e2009-09-05 21:47:34 +000010682 return(status);
10683}
10684
10685#if defined(JNG_SUPPORTED)
10686
10687/* Write one JNG image */
10688static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10689 const ImageInfo *image_info,Image *image)
10690{
10691 Image
10692 *jpeg_image;
10693
10694 ImageInfo
10695 *jpeg_image_info;
10696
10697 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010698 logging,
cristy3ed852e2009-09-05 21:47:34 +000010699 status;
10700
10701 size_t
10702 length;
10703
10704 unsigned char
10705 *blob,
10706 chunk[80],
10707 *p;
10708
10709 unsigned int
10710 jng_alpha_compression_method,
10711 jng_alpha_sample_depth,
10712 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010713 transparent;
10714
cristybb503372010-05-27 20:51:26 +000010715 size_t
cristy3ed852e2009-09-05 21:47:34 +000010716 jng_quality;
10717
10718 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010719 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010720
10721 blob=(unsigned char *) NULL;
10722 jpeg_image=(Image *) NULL;
10723 jpeg_image_info=(ImageInfo *) NULL;
10724
10725 status=MagickTrue;
10726 transparent=image_info->type==GrayscaleMatteType ||
10727 image_info->type==TrueColorMatteType;
10728 jng_color_type=10;
10729 jng_alpha_sample_depth=0;
10730 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10731 jng_alpha_compression_method=0;
10732
10733 if (image->matte != MagickFalse)
10734 {
10735 /* if any pixels are transparent */
10736 transparent=MagickTrue;
10737 if (image_info->compression==JPEGCompression)
10738 jng_alpha_compression_method=8;
10739 }
10740
10741 if (transparent)
10742 {
10743 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010744
cristy3ed852e2009-09-05 21:47:34 +000010745 /* Create JPEG blob, image, and image_info */
10746 if (logging != MagickFalse)
10747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10748 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010749
cristy3ed852e2009-09-05 21:47:34 +000010750 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010751
cristy3ed852e2009-09-05 21:47:34 +000010752 if (jpeg_image_info == (ImageInfo *) NULL)
10753 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010754
cristy3ed852e2009-09-05 21:47:34 +000010755 if (logging != MagickFalse)
10756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10757 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010758
cristy3ed852e2009-09-05 21:47:34 +000010759 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010760
cristy3ed852e2009-09-05 21:47:34 +000010761 if (jpeg_image == (Image *) NULL)
10762 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010763
cristy3ed852e2009-09-05 21:47:34 +000010764 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10765 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10766 status=NegateImage(jpeg_image,MagickFalse);
10767 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010768
cristy3ed852e2009-09-05 21:47:34 +000010769 if (jng_quality >= 1000)
10770 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010771
cristy3ed852e2009-09-05 21:47:34 +000010772 else
10773 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010774
cristy3ed852e2009-09-05 21:47:34 +000010775 jpeg_image_info->type=GrayscaleType;
10776 (void) SetImageType(jpeg_image,GrayscaleType);
10777 (void) AcquireUniqueFilename(jpeg_image->filename);
10778 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10779 "%s",jpeg_image->filename);
10780 }
10781
10782 /* To do: check bit depth of PNG alpha channel */
10783
10784 /* Check if image is grayscale. */
10785 if (image_info->type != TrueColorMatteType && image_info->type !=
10786 TrueColorType && ImageIsGray(image))
10787 jng_color_type-=2;
10788
10789 if (transparent)
10790 {
10791 if (jng_alpha_compression_method==0)
10792 {
10793 const char
10794 *value;
10795
10796 /* Encode opacity as a grayscale PNG blob */
10797 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10798 &image->exception);
10799 if (logging != MagickFalse)
10800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10801 " Creating PNG blob.");
10802 length=0;
10803
10804 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10805 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10806 jpeg_image_info->interlace=NoInterlace;
10807
10808 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10809 &image->exception);
10810
10811 /* Retrieve sample depth used */
10812 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10813 if (value != (char *) NULL)
10814 jng_alpha_sample_depth= (unsigned int) value[0];
10815 }
10816 else
10817 {
10818 /* Encode opacity as a grayscale JPEG blob */
10819
10820 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10821 &image->exception);
10822
10823 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10824 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10825 jpeg_image_info->interlace=NoInterlace;
10826 if (logging != MagickFalse)
10827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10828 " Creating blob.");
10829 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010830 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010831 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010832
cristy3ed852e2009-09-05 21:47:34 +000010833 if (logging != MagickFalse)
10834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010835 " Successfully read jpeg_image into a blob, length=%.20g.",
10836 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010837
10838 }
10839 /* Destroy JPEG image and image_info */
10840 jpeg_image=DestroyImage(jpeg_image);
10841 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10842 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10843 }
10844
10845 /* Write JHDR chunk */
10846 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10847 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010848 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010849 PNGLong(chunk+4,(png_uint_32) image->columns);
10850 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010851 chunk[12]=jng_color_type;
10852 chunk[13]=8; /* sample depth */
10853 chunk[14]=8; /*jng_image_compression_method */
10854 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10855 chunk[16]=jng_alpha_sample_depth;
10856 chunk[17]=jng_alpha_compression_method;
10857 chunk[18]=0; /*jng_alpha_filter_method */
10858 chunk[19]=0; /*jng_alpha_interlace_method */
10859 (void) WriteBlob(image,20,chunk);
10860 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10861 if (logging != MagickFalse)
10862 {
10863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010864 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010865
cristy3ed852e2009-09-05 21:47:34 +000010866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010867 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010868
cristy3ed852e2009-09-05 21:47:34 +000010869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10870 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010871
cristy3ed852e2009-09-05 21:47:34 +000010872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10873 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010874
cristy3ed852e2009-09-05 21:47:34 +000010875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10876 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010877
cristy3ed852e2009-09-05 21:47:34 +000010878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10879 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010880
cristy3ed852e2009-09-05 21:47:34 +000010881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10882 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010883
cristy3ed852e2009-09-05 21:47:34 +000010884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10885 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010886
cristy3ed852e2009-09-05 21:47:34 +000010887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10888 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010889
cristy3ed852e2009-09-05 21:47:34 +000010890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10891 " JNG alpha interlace:%5d",0);
10892 }
10893
glennrp0fe50b42010-11-16 03:52:51 +000010894 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010895 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010896
10897 /*
10898 Write leading ancillary chunks
10899 */
10900
10901 if (transparent)
10902 {
10903 /*
10904 Write JNG bKGD chunk
10905 */
10906
10907 unsigned char
10908 blue,
10909 green,
10910 red;
10911
cristybb503372010-05-27 20:51:26 +000010912 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010913 num_bytes;
10914
10915 if (jng_color_type == 8 || jng_color_type == 12)
10916 num_bytes=6L;
10917 else
10918 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010919 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010920 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010921 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010922 red=ScaleQuantumToChar(image->background_color.red);
10923 green=ScaleQuantumToChar(image->background_color.green);
10924 blue=ScaleQuantumToChar(image->background_color.blue);
10925 *(chunk+4)=0;
10926 *(chunk+5)=red;
10927 *(chunk+6)=0;
10928 *(chunk+7)=green;
10929 *(chunk+8)=0;
10930 *(chunk+9)=blue;
10931 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10932 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10933 }
10934
10935 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10936 {
10937 /*
10938 Write JNG sRGB chunk
10939 */
10940 (void) WriteBlobMSBULong(image,1L);
10941 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010942 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010943
cristy3ed852e2009-09-05 21:47:34 +000010944 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010945 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010946 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010947 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010948
cristy3ed852e2009-09-05 21:47:34 +000010949 else
glennrpe610a072010-08-05 17:08:46 +000010950 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010951 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010952 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010953
cristy3ed852e2009-09-05 21:47:34 +000010954 (void) WriteBlob(image,5,chunk);
10955 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10956 }
10957 else
10958 {
10959 if (image->gamma != 0.0)
10960 {
10961 /*
10962 Write JNG gAMA chunk
10963 */
10964 (void) WriteBlobMSBULong(image,4L);
10965 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010966 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010967 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010968 (void) WriteBlob(image,8,chunk);
10969 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10970 }
glennrp0fe50b42010-11-16 03:52:51 +000010971
cristy3ed852e2009-09-05 21:47:34 +000010972 if ((mng_info->equal_chrms == MagickFalse) &&
10973 (image->chromaticity.red_primary.x != 0.0))
10974 {
10975 PrimaryInfo
10976 primary;
10977
10978 /*
10979 Write JNG cHRM chunk
10980 */
10981 (void) WriteBlobMSBULong(image,32L);
10982 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010983 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010984 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010985 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10986 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010987 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010988 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10989 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010990 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010991 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10992 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010993 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010994 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10995 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010996 (void) WriteBlob(image,36,chunk);
10997 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10998 }
10999 }
glennrp0fe50b42010-11-16 03:52:51 +000011000
cristy3ed852e2009-09-05 21:47:34 +000011001 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11002 {
11003 /*
11004 Write JNG pHYs chunk
11005 */
11006 (void) WriteBlobMSBULong(image,9L);
11007 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011008 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011009 if (image->units == PixelsPerInchResolution)
11010 {
cristy35ef8242010-06-03 16:24:13 +000011011 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011012 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011013
cristy35ef8242010-06-03 16:24:13 +000011014 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011015 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011016
cristy3ed852e2009-09-05 21:47:34 +000011017 chunk[12]=1;
11018 }
glennrp0fe50b42010-11-16 03:52:51 +000011019
cristy3ed852e2009-09-05 21:47:34 +000011020 else
11021 {
11022 if (image->units == PixelsPerCentimeterResolution)
11023 {
cristy35ef8242010-06-03 16:24:13 +000011024 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011025 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011026
cristy35ef8242010-06-03 16:24:13 +000011027 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011028 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011029
cristy3ed852e2009-09-05 21:47:34 +000011030 chunk[12]=1;
11031 }
glennrp0fe50b42010-11-16 03:52:51 +000011032
cristy3ed852e2009-09-05 21:47:34 +000011033 else
11034 {
cristy35ef8242010-06-03 16:24:13 +000011035 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11036 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011037 chunk[12]=0;
11038 }
11039 }
11040 (void) WriteBlob(image,13,chunk);
11041 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11042 }
11043
11044 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11045 {
11046 /*
11047 Write JNG oFFs chunk
11048 */
11049 (void) WriteBlobMSBULong(image,9L);
11050 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011051 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011052 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11053 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011054 chunk[12]=0;
11055 (void) WriteBlob(image,13,chunk);
11056 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11057 }
11058 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11059 {
11060 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11061 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011062 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011063 PNGLong(chunk+4,(png_uint_32) image->page.width);
11064 PNGLong(chunk+8,(png_uint_32) image->page.height);
11065 chunk[12]=0; /* unit = pixels */
11066 (void) WriteBlob(image,13,chunk);
11067 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11068 }
11069
11070
11071 if (transparent)
11072 {
11073 if (jng_alpha_compression_method==0)
11074 {
cristybb503372010-05-27 20:51:26 +000011075 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011076 i;
11077
cristybb503372010-05-27 20:51:26 +000011078 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011079 len;
11080
11081 /* Write IDAT chunk header */
11082 if (logging != MagickFalse)
11083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011084 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011085 length);
cristy3ed852e2009-09-05 21:47:34 +000011086
11087 /* Copy IDAT chunks */
11088 len=0;
11089 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011090 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011091 {
11092 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11093 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011094
cristy3ed852e2009-09-05 21:47:34 +000011095 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11096 {
11097 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011098 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011099 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011100 (void) WriteBlob(image,(size_t) len+4,p);
11101 (void) WriteBlobMSBULong(image,
11102 crc32(0,p,(uInt) len+4));
11103 }
glennrp0fe50b42010-11-16 03:52:51 +000011104
cristy3ed852e2009-09-05 21:47:34 +000011105 else
11106 {
11107 if (logging != MagickFalse)
11108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011109 " Skipping %c%c%c%c chunk, length=%.20g.",
11110 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011111 }
11112 p+=(8+len);
11113 }
11114 }
11115 else
11116 {
11117 /* Write JDAA chunk header */
11118 if (logging != MagickFalse)
11119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011120 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011121 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011122 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011123 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011124 /* Write JDAT chunk(s) data */
11125 (void) WriteBlob(image,4,chunk);
11126 (void) WriteBlob(image,length,blob);
11127 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11128 (uInt) length));
11129 }
11130 blob=(unsigned char *) RelinquishMagickMemory(blob);
11131 }
11132
11133 /* Encode image as a JPEG blob */
11134 if (logging != MagickFalse)
11135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11136 " Creating jpeg_image_info.");
11137 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11138 if (jpeg_image_info == (ImageInfo *) NULL)
11139 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11140
11141 if (logging != MagickFalse)
11142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11143 " Creating jpeg_image.");
11144
11145 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11146 if (jpeg_image == (Image *) NULL)
11147 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11148 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11149
11150 (void) AcquireUniqueFilename(jpeg_image->filename);
11151 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11152 jpeg_image->filename);
11153
11154 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11155 &image->exception);
11156
11157 if (logging != MagickFalse)
11158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011159 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11160 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011161
11162 if (jng_color_type == 8 || jng_color_type == 12)
11163 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011164
cristy3ed852e2009-09-05 21:47:34 +000011165 jpeg_image_info->quality=jng_quality % 1000;
11166 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11167 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011168
cristy3ed852e2009-09-05 21:47:34 +000011169 if (logging != MagickFalse)
11170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11171 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011172
cristy3ed852e2009-09-05 21:47:34 +000011173 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011174
cristy3ed852e2009-09-05 21:47:34 +000011175 if (logging != MagickFalse)
11176 {
11177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011178 " Successfully read jpeg_image into a blob, length=%.20g.",
11179 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011180
11181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011182 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011183 }
glennrp0fe50b42010-11-16 03:52:51 +000011184
cristy3ed852e2009-09-05 21:47:34 +000011185 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011186 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011187 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011188 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011189 (void) WriteBlob(image,4,chunk);
11190 (void) WriteBlob(image,length,blob);
11191 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11192
11193 jpeg_image=DestroyImage(jpeg_image);
11194 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11195 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11196 blob=(unsigned char *) RelinquishMagickMemory(blob);
11197
11198 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011199 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011200
11201 /* Write IEND chunk */
11202 (void) WriteBlobMSBULong(image,0L);
11203 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011204 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011205 (void) WriteBlob(image,4,chunk);
11206 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11207
11208 if (logging != MagickFalse)
11209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11210 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011211
cristy3ed852e2009-09-05 21:47:34 +000011212 return(status);
11213}
11214
11215
11216/*
11217%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11218% %
11219% %
11220% %
11221% W r i t e J N G I m a g e %
11222% %
11223% %
11224% %
11225%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11226%
11227% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11228%
11229% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11230%
11231% The format of the WriteJNGImage method is:
11232%
11233% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11234%
11235% A description of each parameter follows:
11236%
11237% o image_info: the image info.
11238%
11239% o image: The image.
11240%
11241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11242*/
11243static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11244{
11245 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011246 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011247 logging,
cristy3ed852e2009-09-05 21:47:34 +000011248 status;
11249
11250 MngInfo
11251 *mng_info;
11252
cristy3ed852e2009-09-05 21:47:34 +000011253 /*
11254 Open image file.
11255 */
11256 assert(image_info != (const ImageInfo *) NULL);
11257 assert(image_info->signature == MagickSignature);
11258 assert(image != (Image *) NULL);
11259 assert(image->signature == MagickSignature);
11260 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011261 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011262 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11263 if (status == MagickFalse)
11264 return(status);
11265
11266 /*
11267 Allocate a MngInfo structure.
11268 */
11269 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011270 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011271 if (mng_info == (MngInfo *) NULL)
11272 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11273 /*
11274 Initialize members of the MngInfo structure.
11275 */
11276 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11277 mng_info->image=image;
11278 have_mng_structure=MagickTrue;
11279
11280 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11281
11282 status=WriteOneJNGImage(mng_info,image_info,image);
11283 (void) CloseBlob(image);
11284
11285 (void) CatchImageException(image);
11286 MngInfoFreeStruct(mng_info,&have_mng_structure);
11287 if (logging != MagickFalse)
11288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11289 return(status);
11290}
11291#endif
11292
11293
11294
11295static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11296{
11297 const char
11298 *option;
11299
11300 Image
11301 *next_image;
11302
11303 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011304 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011305 status;
11306
glennrp03812ae2010-12-24 01:31:34 +000011307 volatile MagickBooleanType
11308 logging;
11309
cristy3ed852e2009-09-05 21:47:34 +000011310 MngInfo
11311 *mng_info;
11312
11313 int
cristy3ed852e2009-09-05 21:47:34 +000011314 image_count,
11315 need_iterations,
11316 need_matte;
11317
11318 volatile int
11319#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11320 defined(PNG_MNG_FEATURES_SUPPORTED)
11321 need_local_plte,
11322#endif
11323 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011324 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011325 use_global_plte;
11326
cristybb503372010-05-27 20:51:26 +000011327 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011328 i;
11329
11330 unsigned char
11331 chunk[800];
11332
11333 volatile unsigned int
11334 write_jng,
11335 write_mng;
11336
cristybb503372010-05-27 20:51:26 +000011337 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011338 scene;
11339
cristybb503372010-05-27 20:51:26 +000011340 size_t
cristy3ed852e2009-09-05 21:47:34 +000011341 final_delay=0,
11342 initial_delay;
11343
glennrpd5045b42010-03-24 12:40:35 +000011344#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011345 if (image_info->verbose)
11346 printf("Your PNG library (libpng-%s) is rather old.\n",
11347 PNG_LIBPNG_VER_STRING);
11348#endif
11349
11350 /*
11351 Open image file.
11352 */
11353 assert(image_info != (const ImageInfo *) NULL);
11354 assert(image_info->signature == MagickSignature);
11355 assert(image != (Image *) NULL);
11356 assert(image->signature == MagickSignature);
11357 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011358 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011359 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11360 if (status == MagickFalse)
11361 return(status);
11362
11363 /*
11364 Allocate a MngInfo structure.
11365 */
11366 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011367 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011368 if (mng_info == (MngInfo *) NULL)
11369 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11370 /*
11371 Initialize members of the MngInfo structure.
11372 */
11373 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11374 mng_info->image=image;
11375 have_mng_structure=MagickTrue;
11376 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11377
11378 /*
11379 * See if user has requested a specific PNG subformat to be used
11380 * for all of the PNGs in the MNG being written, e.g.,
11381 *
11382 * convert *.png png8:animation.mng
11383 *
11384 * To do: check -define png:bit_depth and png:color_type as well,
11385 * or perhaps use mng:bit_depth and mng:color_type instead for
11386 * global settings.
11387 */
11388
11389 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11390 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11391 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11392
11393 write_jng=MagickFalse;
11394 if (image_info->compression == JPEGCompression)
11395 write_jng=MagickTrue;
11396
11397 mng_info->adjoin=image_info->adjoin &&
11398 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11399
cristy3ed852e2009-09-05 21:47:34 +000011400 if (logging != MagickFalse)
11401 {
11402 /* Log some info about the input */
11403 Image
11404 *p;
11405
11406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11407 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011408
cristy3ed852e2009-09-05 21:47:34 +000011409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011410 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011411
cristy3ed852e2009-09-05 21:47:34 +000011412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11413 " Type: %d",image_info->type);
11414
11415 scene=0;
11416 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11417 {
11418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011419 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011420
cristy3ed852e2009-09-05 21:47:34 +000011421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011422 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011423
cristy3ed852e2009-09-05 21:47:34 +000011424 if (p->matte)
11425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11426 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011427
cristy3ed852e2009-09-05 21:47:34 +000011428 else
11429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11430 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011431
cristy3ed852e2009-09-05 21:47:34 +000011432 if (p->storage_class == PseudoClass)
11433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11434 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011435
cristy3ed852e2009-09-05 21:47:34 +000011436 else
11437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11438 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011439
cristy3ed852e2009-09-05 21:47:34 +000011440 if (p->colors)
11441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011442 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011443
cristy3ed852e2009-09-05 21:47:34 +000011444 else
11445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11446 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011447
cristy3ed852e2009-09-05 21:47:34 +000011448 if (mng_info->adjoin == MagickFalse)
11449 break;
11450 }
11451 }
11452
cristy3ed852e2009-09-05 21:47:34 +000011453 use_global_plte=MagickFalse;
11454 all_images_are_gray=MagickFalse;
11455#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11456 need_local_plte=MagickTrue;
11457#endif
11458 need_defi=MagickFalse;
11459 need_matte=MagickFalse;
11460 mng_info->framing_mode=1;
11461 mng_info->old_framing_mode=1;
11462
11463 if (write_mng)
11464 if (image_info->page != (char *) NULL)
11465 {
11466 /*
11467 Determine image bounding box.
11468 */
11469 SetGeometry(image,&mng_info->page);
11470 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11471 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11472 }
11473 if (write_mng)
11474 {
11475 unsigned int
11476 need_geom;
11477
11478 unsigned short
11479 red,
11480 green,
11481 blue;
11482
11483 mng_info->page=image->page;
11484 need_geom=MagickTrue;
11485 if (mng_info->page.width || mng_info->page.height)
11486 need_geom=MagickFalse;
11487 /*
11488 Check all the scenes.
11489 */
11490 initial_delay=image->delay;
11491 need_iterations=MagickFalse;
11492 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11493 mng_info->equal_physs=MagickTrue,
11494 mng_info->equal_gammas=MagickTrue;
11495 mng_info->equal_srgbs=MagickTrue;
11496 mng_info->equal_backgrounds=MagickTrue;
11497 image_count=0;
11498#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11499 defined(PNG_MNG_FEATURES_SUPPORTED)
11500 all_images_are_gray=MagickTrue;
11501 mng_info->equal_palettes=MagickFalse;
11502 need_local_plte=MagickFalse;
11503#endif
11504 for (next_image=image; next_image != (Image *) NULL; )
11505 {
11506 if (need_geom)
11507 {
11508 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11509 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011510
cristy3ed852e2009-09-05 21:47:34 +000011511 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11512 mng_info->page.height=next_image->rows+next_image->page.y;
11513 }
glennrp0fe50b42010-11-16 03:52:51 +000011514
cristy3ed852e2009-09-05 21:47:34 +000011515 if (next_image->page.x || next_image->page.y)
11516 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011517
cristy3ed852e2009-09-05 21:47:34 +000011518 if (next_image->matte)
11519 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011520
cristy3ed852e2009-09-05 21:47:34 +000011521 if ((int) next_image->dispose >= BackgroundDispose)
11522 if (next_image->matte || next_image->page.x || next_image->page.y ||
11523 ((next_image->columns < mng_info->page.width) &&
11524 (next_image->rows < mng_info->page.height)))
11525 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011526
cristy3ed852e2009-09-05 21:47:34 +000011527 if (next_image->iterations)
11528 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011529
cristy3ed852e2009-09-05 21:47:34 +000011530 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011531
cristy3ed852e2009-09-05 21:47:34 +000011532 if (final_delay != initial_delay || final_delay > 1UL*
11533 next_image->ticks_per_second)
11534 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011535
cristy3ed852e2009-09-05 21:47:34 +000011536#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11537 defined(PNG_MNG_FEATURES_SUPPORTED)
11538 /*
11539 check for global palette possibility.
11540 */
11541 if (image->matte != MagickFalse)
11542 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011543
cristy3ed852e2009-09-05 21:47:34 +000011544 if (need_local_plte == 0)
11545 {
11546 if (ImageIsGray(image) == MagickFalse)
11547 all_images_are_gray=MagickFalse;
11548 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11549 if (use_global_plte == 0)
11550 use_global_plte=mng_info->equal_palettes;
11551 need_local_plte=!mng_info->equal_palettes;
11552 }
11553#endif
11554 if (GetNextImageInList(next_image) != (Image *) NULL)
11555 {
11556 if (next_image->background_color.red !=
11557 next_image->next->background_color.red ||
11558 next_image->background_color.green !=
11559 next_image->next->background_color.green ||
11560 next_image->background_color.blue !=
11561 next_image->next->background_color.blue)
11562 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011563
cristy3ed852e2009-09-05 21:47:34 +000011564 if (next_image->gamma != next_image->next->gamma)
11565 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011566
cristy3ed852e2009-09-05 21:47:34 +000011567 if (next_image->rendering_intent !=
11568 next_image->next->rendering_intent)
11569 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011570
cristy3ed852e2009-09-05 21:47:34 +000011571 if ((next_image->units != next_image->next->units) ||
11572 (next_image->x_resolution != next_image->next->x_resolution) ||
11573 (next_image->y_resolution != next_image->next->y_resolution))
11574 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011575
cristy3ed852e2009-09-05 21:47:34 +000011576 if (mng_info->equal_chrms)
11577 {
11578 if (next_image->chromaticity.red_primary.x !=
11579 next_image->next->chromaticity.red_primary.x ||
11580 next_image->chromaticity.red_primary.y !=
11581 next_image->next->chromaticity.red_primary.y ||
11582 next_image->chromaticity.green_primary.x !=
11583 next_image->next->chromaticity.green_primary.x ||
11584 next_image->chromaticity.green_primary.y !=
11585 next_image->next->chromaticity.green_primary.y ||
11586 next_image->chromaticity.blue_primary.x !=
11587 next_image->next->chromaticity.blue_primary.x ||
11588 next_image->chromaticity.blue_primary.y !=
11589 next_image->next->chromaticity.blue_primary.y ||
11590 next_image->chromaticity.white_point.x !=
11591 next_image->next->chromaticity.white_point.x ||
11592 next_image->chromaticity.white_point.y !=
11593 next_image->next->chromaticity.white_point.y)
11594 mng_info->equal_chrms=MagickFalse;
11595 }
11596 }
11597 image_count++;
11598 next_image=GetNextImageInList(next_image);
11599 }
11600 if (image_count < 2)
11601 {
11602 mng_info->equal_backgrounds=MagickFalse;
11603 mng_info->equal_chrms=MagickFalse;
11604 mng_info->equal_gammas=MagickFalse;
11605 mng_info->equal_srgbs=MagickFalse;
11606 mng_info->equal_physs=MagickFalse;
11607 use_global_plte=MagickFalse;
11608#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11609 need_local_plte=MagickTrue;
11610#endif
11611 need_iterations=MagickFalse;
11612 }
glennrp0fe50b42010-11-16 03:52:51 +000011613
cristy3ed852e2009-09-05 21:47:34 +000011614 if (mng_info->need_fram == MagickFalse)
11615 {
11616 /*
11617 Only certain framing rates 100/n are exactly representable without
11618 the FRAM chunk but we'll allow some slop in VLC files
11619 */
11620 if (final_delay == 0)
11621 {
11622 if (need_iterations != MagickFalse)
11623 {
11624 /*
11625 It's probably a GIF with loop; don't run it *too* fast.
11626 */
glennrp02617122010-07-28 13:07:35 +000011627 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011628 {
11629 final_delay=10;
11630 (void) ThrowMagickException(&image->exception,
11631 GetMagickModule(),CoderWarning,
11632 "input has zero delay between all frames; assuming",
11633 " 10 cs `%s'","");
11634 }
cristy3ed852e2009-09-05 21:47:34 +000011635 }
11636 else
11637 mng_info->ticks_per_second=0;
11638 }
11639 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011640 mng_info->ticks_per_second=(png_uint_32)
11641 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011642 if (final_delay > 50)
11643 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011644
cristy3ed852e2009-09-05 21:47:34 +000011645 if (final_delay > 75)
11646 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011647
cristy3ed852e2009-09-05 21:47:34 +000011648 if (final_delay > 125)
11649 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011650
cristy3ed852e2009-09-05 21:47:34 +000011651 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11652 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11653 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11654 1UL*image->ticks_per_second))
11655 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11656 }
glennrp0fe50b42010-11-16 03:52:51 +000011657
cristy3ed852e2009-09-05 21:47:34 +000011658 if (mng_info->need_fram != MagickFalse)
11659 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11660 /*
11661 If pseudocolor, we should also check to see if all the
11662 palettes are identical and write a global PLTE if they are.
11663 ../glennrp Feb 99.
11664 */
11665 /*
11666 Write the MNG version 1.0 signature and MHDR chunk.
11667 */
11668 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11669 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11670 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011671 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011672 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11673 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011674 PNGLong(chunk+12,mng_info->ticks_per_second);
11675 PNGLong(chunk+16,0L); /* layer count=unknown */
11676 PNGLong(chunk+20,0L); /* frame count=unknown */
11677 PNGLong(chunk+24,0L); /* play time=unknown */
11678 if (write_jng)
11679 {
11680 if (need_matte)
11681 {
11682 if (need_defi || mng_info->need_fram || use_global_plte)
11683 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011684
cristy3ed852e2009-09-05 21:47:34 +000011685 else
11686 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11687 }
glennrp0fe50b42010-11-16 03:52:51 +000011688
cristy3ed852e2009-09-05 21:47:34 +000011689 else
11690 {
11691 if (need_defi || mng_info->need_fram || use_global_plte)
11692 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011693
cristy3ed852e2009-09-05 21:47:34 +000011694 else
11695 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11696 }
11697 }
glennrp0fe50b42010-11-16 03:52:51 +000011698
cristy3ed852e2009-09-05 21:47:34 +000011699 else
11700 {
11701 if (need_matte)
11702 {
11703 if (need_defi || mng_info->need_fram || use_global_plte)
11704 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011705
cristy3ed852e2009-09-05 21:47:34 +000011706 else
11707 PNGLong(chunk+28,9L); /* simplicity=VLC */
11708 }
glennrp0fe50b42010-11-16 03:52:51 +000011709
cristy3ed852e2009-09-05 21:47:34 +000011710 else
11711 {
11712 if (need_defi || mng_info->need_fram || use_global_plte)
11713 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011714
cristy3ed852e2009-09-05 21:47:34 +000011715 else
11716 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11717 }
11718 }
11719 (void) WriteBlob(image,32,chunk);
11720 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11721 option=GetImageOption(image_info,"mng:need-cacheoff");
11722 if (option != (const char *) NULL)
11723 {
11724 size_t
11725 length;
11726
11727 /*
11728 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11729 */
11730 PNGType(chunk,mng_nEED);
11731 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011732 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011733 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011734 length+=4;
11735 (void) WriteBlob(image,length,chunk);
11736 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11737 }
11738 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11739 (GetNextImageInList(image) != (Image *) NULL) &&
11740 (image->iterations != 1))
11741 {
11742 /*
11743 Write MNG TERM chunk
11744 */
11745 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11746 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011747 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011748 chunk[4]=3; /* repeat animation */
11749 chunk[5]=0; /* show last frame when done */
11750 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11751 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011752
cristy3ed852e2009-09-05 21:47:34 +000011753 if (image->iterations == 0)
11754 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011755
cristy3ed852e2009-09-05 21:47:34 +000011756 else
11757 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011758
cristy3ed852e2009-09-05 21:47:34 +000011759 if (logging != MagickFalse)
11760 {
11761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011762 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11763 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011764
cristy3ed852e2009-09-05 21:47:34 +000011765 if (image->iterations == 0)
11766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011767 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011768
cristy3ed852e2009-09-05 21:47:34 +000011769 else
11770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011771 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011772 }
11773 (void) WriteBlob(image,14,chunk);
11774 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11775 }
11776 /*
11777 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11778 */
11779 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11780 mng_info->equal_srgbs)
11781 {
11782 /*
11783 Write MNG sRGB chunk
11784 */
11785 (void) WriteBlobMSBULong(image,1L);
11786 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011787 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011788
cristy3ed852e2009-09-05 21:47:34 +000011789 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011790 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011791 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011792 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011793
cristy3ed852e2009-09-05 21:47:34 +000011794 else
glennrpe610a072010-08-05 17:08:46 +000011795 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011796 Magick_RenderingIntent_to_PNG_RenderingIntent(
11797 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011798
cristy3ed852e2009-09-05 21:47:34 +000011799 (void) WriteBlob(image,5,chunk);
11800 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11801 mng_info->have_write_global_srgb=MagickTrue;
11802 }
glennrp0fe50b42010-11-16 03:52:51 +000011803
cristy3ed852e2009-09-05 21:47:34 +000011804 else
11805 {
11806 if (image->gamma && mng_info->equal_gammas)
11807 {
11808 /*
11809 Write MNG gAMA chunk
11810 */
11811 (void) WriteBlobMSBULong(image,4L);
11812 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011813 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011814 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011815 (void) WriteBlob(image,8,chunk);
11816 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11817 mng_info->have_write_global_gama=MagickTrue;
11818 }
11819 if (mng_info->equal_chrms)
11820 {
11821 PrimaryInfo
11822 primary;
11823
11824 /*
11825 Write MNG cHRM chunk
11826 */
11827 (void) WriteBlobMSBULong(image,32L);
11828 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011829 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011830 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011831 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11832 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011833 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011834 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11835 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011836 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011837 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11838 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011839 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011840 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11841 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011842 (void) WriteBlob(image,36,chunk);
11843 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11844 mng_info->have_write_global_chrm=MagickTrue;
11845 }
11846 }
11847 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11848 {
11849 /*
11850 Write MNG pHYs chunk
11851 */
11852 (void) WriteBlobMSBULong(image,9L);
11853 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011854 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011855
cristy3ed852e2009-09-05 21:47:34 +000011856 if (image->units == PixelsPerInchResolution)
11857 {
cristy35ef8242010-06-03 16:24:13 +000011858 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011859 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011860
cristy35ef8242010-06-03 16:24:13 +000011861 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011862 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011863
cristy3ed852e2009-09-05 21:47:34 +000011864 chunk[12]=1;
11865 }
glennrp0fe50b42010-11-16 03:52:51 +000011866
cristy3ed852e2009-09-05 21:47:34 +000011867 else
11868 {
11869 if (image->units == PixelsPerCentimeterResolution)
11870 {
cristy35ef8242010-06-03 16:24:13 +000011871 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011872 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011873
cristy35ef8242010-06-03 16:24:13 +000011874 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011875 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011876
cristy3ed852e2009-09-05 21:47:34 +000011877 chunk[12]=1;
11878 }
glennrp0fe50b42010-11-16 03:52:51 +000011879
cristy3ed852e2009-09-05 21:47:34 +000011880 else
11881 {
cristy35ef8242010-06-03 16:24:13 +000011882 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11883 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011884 chunk[12]=0;
11885 }
11886 }
11887 (void) WriteBlob(image,13,chunk);
11888 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11889 }
11890 /*
11891 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11892 or does not cover the entire frame.
11893 */
11894 if (write_mng && (image->matte || image->page.x > 0 ||
11895 image->page.y > 0 || (image->page.width &&
11896 (image->page.width+image->page.x < mng_info->page.width))
11897 || (image->page.height && (image->page.height+image->page.y
11898 < mng_info->page.height))))
11899 {
11900 (void) WriteBlobMSBULong(image,6L);
11901 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011902 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011903 red=ScaleQuantumToShort(image->background_color.red);
11904 green=ScaleQuantumToShort(image->background_color.green);
11905 blue=ScaleQuantumToShort(image->background_color.blue);
11906 PNGShort(chunk+4,red);
11907 PNGShort(chunk+6,green);
11908 PNGShort(chunk+8,blue);
11909 (void) WriteBlob(image,10,chunk);
11910 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11911 if (mng_info->equal_backgrounds)
11912 {
11913 (void) WriteBlobMSBULong(image,6L);
11914 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011915 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011916 (void) WriteBlob(image,10,chunk);
11917 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11918 }
11919 }
11920
11921#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11922 if ((need_local_plte == MagickFalse) &&
11923 (image->storage_class == PseudoClass) &&
11924 (all_images_are_gray == MagickFalse))
11925 {
cristybb503372010-05-27 20:51:26 +000011926 size_t
cristy3ed852e2009-09-05 21:47:34 +000011927 data_length;
11928
11929 /*
11930 Write MNG PLTE chunk
11931 */
11932 data_length=3*image->colors;
11933 (void) WriteBlobMSBULong(image,data_length);
11934 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011935 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011936
cristybb503372010-05-27 20:51:26 +000011937 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011938 {
11939 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11940 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11941 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11942 }
glennrp0fe50b42010-11-16 03:52:51 +000011943
cristy3ed852e2009-09-05 21:47:34 +000011944 (void) WriteBlob(image,data_length+4,chunk);
11945 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11946 mng_info->have_write_global_plte=MagickTrue;
11947 }
11948#endif
11949 }
11950 scene=0;
11951 mng_info->delay=0;
11952#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11953 defined(PNG_MNG_FEATURES_SUPPORTED)
11954 mng_info->equal_palettes=MagickFalse;
11955#endif
11956 do
11957 {
11958 if (mng_info->adjoin)
11959 {
11960#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11961 defined(PNG_MNG_FEATURES_SUPPORTED)
11962 /*
11963 If we aren't using a global palette for the entire MNG, check to
11964 see if we can use one for two or more consecutive images.
11965 */
11966 if (need_local_plte && use_global_plte && !all_images_are_gray)
11967 {
11968 if (mng_info->IsPalette)
11969 {
11970 /*
11971 When equal_palettes is true, this image has the same palette
11972 as the previous PseudoClass image
11973 */
11974 mng_info->have_write_global_plte=mng_info->equal_palettes;
11975 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11976 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11977 {
11978 /*
11979 Write MNG PLTE chunk
11980 */
cristybb503372010-05-27 20:51:26 +000011981 size_t
cristy3ed852e2009-09-05 21:47:34 +000011982 data_length;
11983
11984 data_length=3*image->colors;
11985 (void) WriteBlobMSBULong(image,data_length);
11986 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011987 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011988
cristybb503372010-05-27 20:51:26 +000011989 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011990 {
11991 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11992 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11993 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11994 }
glennrp0fe50b42010-11-16 03:52:51 +000011995
cristy3ed852e2009-09-05 21:47:34 +000011996 (void) WriteBlob(image,data_length+4,chunk);
11997 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11998 (uInt) (data_length+4)));
11999 mng_info->have_write_global_plte=MagickTrue;
12000 }
12001 }
12002 else
12003 mng_info->have_write_global_plte=MagickFalse;
12004 }
12005#endif
12006 if (need_defi)
12007 {
cristybb503372010-05-27 20:51:26 +000012008 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012009 previous_x,
12010 previous_y;
12011
12012 if (scene)
12013 {
12014 previous_x=mng_info->page.x;
12015 previous_y=mng_info->page.y;
12016 }
12017 else
12018 {
12019 previous_x=0;
12020 previous_y=0;
12021 }
12022 mng_info->page=image->page;
12023 if ((mng_info->page.x != previous_x) ||
12024 (mng_info->page.y != previous_y))
12025 {
12026 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12027 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012028 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012029 chunk[4]=0; /* object 0 MSB */
12030 chunk[5]=0; /* object 0 LSB */
12031 chunk[6]=0; /* visible */
12032 chunk[7]=0; /* abstract */
12033 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12034 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12035 (void) WriteBlob(image,16,chunk);
12036 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12037 }
12038 }
12039 }
12040
12041 mng_info->write_mng=write_mng;
12042
12043 if ((int) image->dispose >= 3)
12044 mng_info->framing_mode=3;
12045
12046 if (mng_info->need_fram && mng_info->adjoin &&
12047 ((image->delay != mng_info->delay) ||
12048 (mng_info->framing_mode != mng_info->old_framing_mode)))
12049 {
12050 if (image->delay == mng_info->delay)
12051 {
12052 /*
12053 Write a MNG FRAM chunk with the new framing mode.
12054 */
12055 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12056 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012057 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012058 chunk[4]=(unsigned char) mng_info->framing_mode;
12059 (void) WriteBlob(image,5,chunk);
12060 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12061 }
12062 else
12063 {
12064 /*
12065 Write a MNG FRAM chunk with the delay.
12066 */
12067 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12068 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012069 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012070 chunk[4]=(unsigned char) mng_info->framing_mode;
12071 chunk[5]=0; /* frame name separator (no name) */
12072 chunk[6]=2; /* flag for changing default delay */
12073 chunk[7]=0; /* flag for changing frame timeout */
12074 chunk[8]=0; /* flag for changing frame clipping */
12075 chunk[9]=0; /* flag for changing frame sync_id */
12076 PNGLong(chunk+10,(png_uint_32)
12077 ((mng_info->ticks_per_second*
12078 image->delay)/MagickMax(image->ticks_per_second,1)));
12079 (void) WriteBlob(image,14,chunk);
12080 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012081 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012082 }
12083 mng_info->old_framing_mode=mng_info->framing_mode;
12084 }
12085
12086#if defined(JNG_SUPPORTED)
12087 if (image_info->compression == JPEGCompression)
12088 {
12089 ImageInfo
12090 *write_info;
12091
12092 if (logging != MagickFalse)
12093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12094 " Writing JNG object.");
12095 /* To do: specify the desired alpha compression method. */
12096 write_info=CloneImageInfo(image_info);
12097 write_info->compression=UndefinedCompression;
12098 status=WriteOneJNGImage(mng_info,write_info,image);
12099 write_info=DestroyImageInfo(write_info);
12100 }
12101 else
12102#endif
12103 {
12104 if (logging != MagickFalse)
12105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12106 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012107
glennrpb9cfe272010-12-21 15:08:06 +000012108 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012109 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012110
12111 /* We don't want any ancillary chunks written */
12112 mng_info->ping_exclude_bKGD=MagickTrue;
12113 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012114 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012115 mng_info->ping_exclude_EXIF=MagickTrue;
12116 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012117 mng_info->ping_exclude_iCCP=MagickTrue;
12118 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12119 mng_info->ping_exclude_oFFs=MagickTrue;
12120 mng_info->ping_exclude_pHYs=MagickTrue;
12121 mng_info->ping_exclude_sRGB=MagickTrue;
12122 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012123 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012124 mng_info->ping_exclude_vpAg=MagickTrue;
12125 mng_info->ping_exclude_zCCP=MagickTrue;
12126 mng_info->ping_exclude_zTXt=MagickTrue;
12127
cristy3ed852e2009-09-05 21:47:34 +000012128 status=WriteOnePNGImage(mng_info,image_info,image);
12129 }
12130
12131 if (status == MagickFalse)
12132 {
12133 MngInfoFreeStruct(mng_info,&have_mng_structure);
12134 (void) CloseBlob(image);
12135 return(MagickFalse);
12136 }
12137 (void) CatchImageException(image);
12138 if (GetNextImageInList(image) == (Image *) NULL)
12139 break;
12140 image=SyncNextImageInList(image);
12141 status=SetImageProgress(image,SaveImagesTag,scene++,
12142 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012143
cristy3ed852e2009-09-05 21:47:34 +000012144 if (status == MagickFalse)
12145 break;
glennrp0fe50b42010-11-16 03:52:51 +000012146
cristy3ed852e2009-09-05 21:47:34 +000012147 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012148
cristy3ed852e2009-09-05 21:47:34 +000012149 if (write_mng)
12150 {
12151 while (GetPreviousImageInList(image) != (Image *) NULL)
12152 image=GetPreviousImageInList(image);
12153 /*
12154 Write the MEND chunk.
12155 */
12156 (void) WriteBlobMSBULong(image,0x00000000L);
12157 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012158 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012159 (void) WriteBlob(image,4,chunk);
12160 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12161 }
12162 /*
12163 Relinquish resources.
12164 */
12165 (void) CloseBlob(image);
12166 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012167
cristy3ed852e2009-09-05 21:47:34 +000012168 if (logging != MagickFalse)
12169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012170
cristy3ed852e2009-09-05 21:47:34 +000012171 return(MagickTrue);
12172}
glennrpd5045b42010-03-24 12:40:35 +000012173#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012174
cristy3ed852e2009-09-05 21:47:34 +000012175static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12176{
12177 image=image;
12178 printf("Your PNG library is too old: You have libpng-%s\n",
12179 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012180
cristy3ed852e2009-09-05 21:47:34 +000012181 ThrowBinaryException(CoderError,"PNG library is too old",
12182 image_info->filename);
12183}
glennrp39992b42010-11-14 00:03:43 +000012184
cristy3ed852e2009-09-05 21:47:34 +000012185static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12186{
12187 return(WritePNGImage(image_info,image));
12188}
glennrpd5045b42010-03-24 12:40:35 +000012189#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012190#endif