blob: 6ff291679625aa5ac702cd38bc485db9888c17b9 [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)); \
glennrp77110c32011-05-03 05:25:16 +0000135 (packet).blue = GetBluePixelComponent((pixel)); \
glennrpb5d5e6d2011-04-27 17:08:19 +0000136 (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)); \
glennrp77110c32011-05-03 05:25:16 +0000148 (packet).blue = 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
glennrp77110c32011-05-03 05:25:16 +00007258 unsigned char
7259 hi4,
7260 hi3,
7261 hi2;
7262
glennrpd71e86a2011-02-24 01:28:37 +00007263 if (logging != MagickFalse)
7264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7265 " Enter BUILD_PALETTE:");
7266
7267 if (logging != MagickFalse)
7268 {
glennrp03812ae2010-12-24 01:31:34 +00007269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007270 " image->columns=%.20g",(double) image->columns);
7271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7272 " image->rows=%.20g",(double) image->rows);
7273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7274 " image->matte=%.20g",(double) image->matte);
7275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7276 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007277
glennrpfd05d622011-02-25 04:10:33 +00007278 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007279 {
7280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007281 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007283 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007284
glennrpd71e86a2011-02-24 01:28:37 +00007285 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007286 {
glennrpd71e86a2011-02-24 01:28:37 +00007287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7288 " %d (%d,%d,%d,%d)",
7289 (int) i,
7290 (int) image->colormap[i].red,
7291 (int) image->colormap[i].green,
7292 (int) image->colormap[i].blue,
7293 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007294 }
glennrp2cc891a2010-12-24 13:44:32 +00007295
glennrpd71e86a2011-02-24 01:28:37 +00007296 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7297 {
7298 if (i > 255)
7299 {
7300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7301 " %d (%d,%d,%d,%d)",
7302 (int) i,
7303 (int) image->colormap[i].red,
7304 (int) image->colormap[i].green,
7305 (int) image->colormap[i].blue,
7306 (int) image->colormap[i].opacity);
7307 }
7308 }
glennrp03812ae2010-12-24 01:31:34 +00007309 }
glennrp7ddcc222010-12-11 05:01:05 +00007310
glennrpd71e86a2011-02-24 01:28:37 +00007311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7312 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007313
glennrpd71e86a2011-02-24 01:28:37 +00007314 if (image->colors == 0)
7315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7316 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007317
glennrp8d3d6e52011-04-19 04:39:51 +00007318 if (ping_preserve_colormap == MagickFalse)
7319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7320 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007321 }
7322
7323 exception=(&image->exception);
7324
7325 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007326 number_opaque = 0;
7327 number_semitransparent = 0;
7328 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007329
7330 for (y=0; y < (ssize_t) image->rows; y++)
7331 {
7332 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7333
7334 if (q == (PixelPacket *) NULL)
7335 break;
7336
7337 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007338 {
glennrp4737d522011-04-29 03:33:42 +00007339 if (image->matte == MagickFalse ||
7340 GetOpacityPixelComponent(q) == OpaqueOpacity)
glennrpd71e86a2011-02-24 01:28:37 +00007341 {
7342 if (number_opaque < 259)
7343 {
7344 if (number_opaque == 0)
7345 {
glennrp8e045c82011-04-27 16:40:27 +00007346 GetRGBPixelComponents(q, opaque[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007347 opaque[0].opacity=OpaqueOpacity;
7348 number_opaque=1;
7349 }
glennrp2cc891a2010-12-24 13:44:32 +00007350
glennrpd71e86a2011-02-24 01:28:37 +00007351 for (i=0; i< (ssize_t) number_opaque; i++)
7352 {
glennrp0e68fac2011-04-26 04:51:47 +00007353 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007354 break;
7355 }
glennrp7ddcc222010-12-11 05:01:05 +00007356
glennrpd71e86a2011-02-24 01:28:37 +00007357 if (i == (ssize_t) number_opaque &&
7358 number_opaque < 259)
7359 {
7360 number_opaque++;
glennrp8e045c82011-04-27 16:40:27 +00007361 GetRGBPixelComponents(q, opaque[i]);
glennrpca7ad3a2011-04-26 04:44:54 +00007362 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00007363 }
7364 }
7365 }
7366 else if (q->opacity == TransparentOpacity)
7367 {
7368 if (number_transparent < 259)
7369 {
7370 if (number_transparent == 0)
7371 {
glennrp8e045c82011-04-27 16:40:27 +00007372 GetRGBOPixelComponents(q, transparent[0]);
glennrpa18d5bc2011-04-23 14:51:34 +00007373 ping_trans_color.red=
7374 (unsigned short) GetRedPixelComponent(q);
7375 ping_trans_color.green=
7376 (unsigned short) GetGreenPixelComponent(q);
7377 ping_trans_color.blue=
7378 (unsigned short) GetBluePixelComponent(q);
7379 ping_trans_color.gray=
7380 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007381 number_transparent = 1;
7382 }
7383
7384 for (i=0; i< (ssize_t) number_transparent; i++)
7385 {
glennrp0e68fac2011-04-26 04:51:47 +00007386 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00007387 break;
7388 }
7389
7390 if (i == (ssize_t) number_transparent &&
7391 number_transparent < 259)
7392 {
7393 number_transparent++;
glennrp8e045c82011-04-27 16:40:27 +00007394 GetRGBOPixelComponents(q, transparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00007395 }
7396 }
7397 }
7398 else
7399 {
7400 if (number_semitransparent < 259)
7401 {
7402 if (number_semitransparent == 0)
7403 {
glennrp8e045c82011-04-27 16:40:27 +00007404 GetRGBOPixelComponents(q, semitransparent[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007405 number_semitransparent = 1;
7406 }
7407
7408 for (i=0; i< (ssize_t) number_semitransparent; i++)
7409 {
glennrp0e68fac2011-04-26 04:51:47 +00007410 if (IsColorEqual(q, semitransparent+i)
glennrpca7ad3a2011-04-26 04:44:54 +00007411 && GetOpacityPixelComponent(q) ==
7412 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00007413 break;
7414 }
7415
7416 if (i == (ssize_t) number_semitransparent &&
7417 number_semitransparent < 259)
7418 {
7419 number_semitransparent++;
glennrp8e045c82011-04-27 16:40:27 +00007420 GetRGBOPixelComponents(q, semitransparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00007421 }
7422 }
7423 }
7424 q++;
7425 }
7426 }
7427
7428 if (ping_exclude_bKGD == MagickFalse)
7429 {
7430 /* Add the background color to the palette, if it
7431 * isn't already there.
7432 */
glennrpc6c391a2011-04-27 02:23:56 +00007433 if (logging != MagickFalse)
7434 {
7435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7436 " Check colormap for background (%d,%d,%d)",
7437 (int) image->background_color.red,
7438 (int) image->background_color.green,
7439 (int) image->background_color.blue);
7440 }
glennrpd71e86a2011-02-24 01:28:37 +00007441 for (i=0; i<number_opaque; i++)
7442 {
glennrpca7ad3a2011-04-26 04:44:54 +00007443 if (opaque[i].red == image->background_color.red &&
7444 opaque[i].green == image->background_color.green &&
7445 opaque[i].blue == image->background_color.blue)
7446 break;
glennrpd71e86a2011-02-24 01:28:37 +00007447 }
glennrpd71e86a2011-02-24 01:28:37 +00007448 if (number_opaque < 259 && i == number_opaque)
7449 {
glennrp8e045c82011-04-27 16:40:27 +00007450 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00007451 ping_background.index = i;
7452 if (logging != MagickFalse)
7453 {
7454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7455 " background_color index is %d",(int) i);
7456 }
7457
glennrpd71e86a2011-02-24 01:28:37 +00007458 }
glennrpa080bc32011-03-11 18:03:44 +00007459 else if (logging != MagickFalse)
7460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7461 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007462 }
7463
7464 image_colors=number_opaque+number_transparent+number_semitransparent;
7465
glennrpa080bc32011-03-11 18:03:44 +00007466 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7467 {
7468 /* No room for the background color; remove it. */
7469 number_opaque--;
7470 image_colors--;
7471 }
7472
glennrpd71e86a2011-02-24 01:28:37 +00007473 if (logging != MagickFalse)
7474 {
7475 if (image_colors > 256)
7476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7477 " image has more than 256 colors");
7478
7479 else
7480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7481 " image has %d colors",image_colors);
7482 }
7483
glennrp8d3d6e52011-04-19 04:39:51 +00007484 if (ping_preserve_colormap != MagickFalse)
7485 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007486
glennrpfd05d622011-02-25 04:10:33 +00007487 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007488 {
7489 ping_have_color=MagickFalse;
7490 ping_have_non_bw=MagickFalse;
7491
7492 if(image_colors > 256)
7493 {
7494 for (y=0; y < (ssize_t) image->rows; y++)
7495 {
7496 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7497
7498 if (q == (PixelPacket *) NULL)
7499 break;
7500
7501 /* Worst case is black-and-white; we are looking at every
7502 * pixel twice.
7503 */
7504
7505 if (ping_have_color == MagickFalse)
7506 {
7507 s=q;
7508 for (x=0; x < (ssize_t) image->columns; x++)
7509 {
glennrpa18d5bc2011-04-23 14:51:34 +00007510 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7511 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00007512 {
7513 ping_have_color=MagickTrue;
7514 ping_have_non_bw=MagickTrue;
7515 break;
7516 }
7517 s++;
7518 }
7519 }
7520
7521 if (ping_have_non_bw == MagickFalse)
7522 {
7523 s=q;
7524 for (x=0; x < (ssize_t) image->columns; x++)
7525 {
glennrpa18d5bc2011-04-23 14:51:34 +00007526 if (GetRedPixelComponent(s) != 0 &&
7527 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00007528 {
7529 ping_have_non_bw=MagickTrue;
7530 }
7531 s++;
7532 }
7533 }
7534 }
7535 }
7536 }
7537
7538 if (image_colors < 257)
7539 {
7540 PixelPacket
7541 colormap[260];
7542
7543 /*
7544 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007545 */
7546
glennrpd71e86a2011-02-24 01:28:37 +00007547 if (logging != MagickFalse)
7548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7549 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007550
glennrpd71e86a2011-02-24 01:28:37 +00007551 /* Sort palette, transparent first */;
7552
7553 n = 0;
7554
7555 for (i=0; i<number_transparent; i++)
7556 colormap[n++] = transparent[i];
7557
7558 for (i=0; i<number_semitransparent; i++)
7559 colormap[n++] = semitransparent[i];
7560
7561 for (i=0; i<number_opaque; i++)
7562 colormap[n++] = opaque[i];
7563
glennrpc6c391a2011-04-27 02:23:56 +00007564 ping_background.index +=
7565 (number_transparent + number_semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00007566
7567 /* image_colors < 257; search the colormap instead of the pixels
7568 * to get ping_have_color and ping_have_non_bw
7569 */
7570 for (i=0; i<n; i++)
7571 {
7572 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007573 {
glennrpd71e86a2011-02-24 01:28:37 +00007574 if (colormap[i].red != colormap[i].green ||
7575 colormap[i].red != colormap[i].blue)
7576 {
7577 ping_have_color=MagickTrue;
7578 ping_have_non_bw=MagickTrue;
7579 break;
7580 }
7581 }
7582
7583 if (ping_have_non_bw == MagickFalse)
7584 {
7585 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007586 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007587 }
glennrp8bb3a022010-12-13 20:40:04 +00007588 }
7589
glennrpd71e86a2011-02-24 01:28:37 +00007590 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7591 (number_transparent == 0 && number_semitransparent == 0)) &&
7592 (((mng_info->write_png_colortype-1) ==
7593 PNG_COLOR_TYPE_PALETTE) ||
7594 (mng_info->write_png_colortype == 0)))
7595 {
glennrp6185c532011-01-14 17:58:40 +00007596 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007597 {
glennrpd71e86a2011-02-24 01:28:37 +00007598 if (n != (ssize_t) image_colors)
7599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7600 " image_colors (%d) and n (%d) don't match",
7601 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007602
glennrpd71e86a2011-02-24 01:28:37 +00007603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7604 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007605 }
glennrp03812ae2010-12-24 01:31:34 +00007606
glennrpd71e86a2011-02-24 01:28:37 +00007607 image->colors = image_colors;
7608
7609 if (AcquireImageColormap(image,image_colors) ==
7610 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007611 ThrowWriterException(ResourceLimitError,
7612 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007613
7614 for (i=0; i< (ssize_t) image_colors; i++)
7615 image->colormap[i] = colormap[i];
7616
7617 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007618 {
glennrpd71e86a2011-02-24 01:28:37 +00007619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7620 " image->colors=%d (%d)",
7621 (int) image->colors, image_colors);
7622
7623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7624 " Update the pixel indexes");
7625 }
glennrp6185c532011-01-14 17:58:40 +00007626
glennrpfd05d622011-02-25 04:10:33 +00007627 /* Sync the pixel indices with the new colormap */
7628
glennrpd71e86a2011-02-24 01:28:37 +00007629 for (y=0; y < (ssize_t) image->rows; y++)
7630 {
7631 q=GetAuthenticPixels(image,0,y,image->columns,1,
7632 exception);
glennrp6185c532011-01-14 17:58:40 +00007633
glennrpd71e86a2011-02-24 01:28:37 +00007634 if (q == (PixelPacket *) NULL)
7635 break;
glennrp6185c532011-01-14 17:58:40 +00007636
glennrpd71e86a2011-02-24 01:28:37 +00007637 indexes=GetAuthenticIndexQueue(image);
7638
7639 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007640 {
glennrpd71e86a2011-02-24 01:28:37 +00007641 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007642 {
glennrpd71e86a2011-02-24 01:28:37 +00007643 if ((image->matte == MagickFalse ||
glennrpca7ad3a2011-04-26 04:44:54 +00007644 image->colormap[i].opacity ==
7645 GetOpacityPixelComponent(q)) &&
7646 image->colormap[i].red ==
7647 GetRedPixelComponent(q) &&
7648 image->colormap[i].green ==
7649 GetGreenPixelComponent(q) &&
7650 image->colormap[i].blue ==
7651 GetBluePixelComponent(q))
glennrp6185c532011-01-14 17:58:40 +00007652 {
cristy9fff7b42011-04-29 01:09:31 +00007653 SetIndexPixelComponent(indexes+x,i);
glennrpd71e86a2011-02-24 01:28:37 +00007654 break;
glennrp6185c532011-01-14 17:58:40 +00007655 }
glennrp6185c532011-01-14 17:58:40 +00007656 }
glennrpd71e86a2011-02-24 01:28:37 +00007657 q++;
7658 }
glennrp6185c532011-01-14 17:58:40 +00007659
glennrpd71e86a2011-02-24 01:28:37 +00007660 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7661 break;
7662 }
7663 }
7664 }
7665
7666 if (logging != MagickFalse)
7667 {
7668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7669 " image->colors=%d", (int) image->colors);
7670
7671 if (image->colormap != NULL)
7672 {
7673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7674 " i (red,green,blue,opacity)");
7675
7676 for (i=0; i < (ssize_t) image->colors; i++)
7677 {
cristy72988482011-03-29 16:34:38 +00007678 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007679 {
7680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7681 " %d (%d,%d,%d,%d)",
7682 (int) i,
7683 (int) image->colormap[i].red,
7684 (int) image->colormap[i].green,
7685 (int) image->colormap[i].blue,
7686 (int) image->colormap[i].opacity);
7687 }
glennrp6185c532011-01-14 17:58:40 +00007688 }
7689 }
glennrp03812ae2010-12-24 01:31:34 +00007690
glennrpd71e86a2011-02-24 01:28:37 +00007691 if (number_transparent < 257)
7692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7693 " number_transparent = %d",
7694 number_transparent);
7695 else
glennrp03812ae2010-12-24 01:31:34 +00007696
glennrpd71e86a2011-02-24 01:28:37 +00007697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7698 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007699
glennrpd71e86a2011-02-24 01:28:37 +00007700 if (number_opaque < 257)
7701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7702 " number_opaque = %d",
7703 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007704
glennrpd71e86a2011-02-24 01:28:37 +00007705 else
7706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7707 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007708
glennrpd71e86a2011-02-24 01:28:37 +00007709 if (number_semitransparent < 257)
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " number_semitransparent = %d",
7712 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007713
glennrpd71e86a2011-02-24 01:28:37 +00007714 else
7715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7716 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007717
glennrpd71e86a2011-02-24 01:28:37 +00007718 if (ping_have_non_bw == MagickFalse)
7719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7720 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007721
glennrpd71e86a2011-02-24 01:28:37 +00007722 else if (ping_have_color == MagickFalse)
7723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7724 " All pixels and the background are gray");
7725
7726 else
7727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7728 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007729
glennrp03812ae2010-12-24 01:31:34 +00007730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7731 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007732 }
glennrpfd05d622011-02-25 04:10:33 +00007733
glennrpc8c2f062011-02-25 19:00:33 +00007734 if (mng_info->write_png8 == MagickFalse)
7735 break;
glennrpfd05d622011-02-25 04:10:33 +00007736
glennrpc8c2f062011-02-25 19:00:33 +00007737 /* Make any reductions necessary for the PNG8 format */
7738 if (image_colors <= 256 &&
7739 image_colors != 0 && image->colormap != NULL &&
7740 number_semitransparent == 0 &&
7741 number_transparent <= 1)
7742 break;
7743
7744 /* PNG8 can't have semitransparent colors so we threshold the
7745 * opacity to 0 or OpaqueOpacity
7746 */
7747 if (number_semitransparent != 0)
7748 {
7749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7750 " Thresholding the alpha channel to binary");
7751
7752 for (y=0; y < (ssize_t) image->rows; y++)
7753 {
7754 r=GetAuthenticPixels(image,0,y,image->columns,1,
7755 exception);
7756
7757 if (r == (PixelPacket *) NULL)
7758 break;
7759
7760 for (x=0; x < (ssize_t) image->columns; x++)
7761 {
glennrpa18d5bc2011-04-23 14:51:34 +00007762 SetOpacityPixelComponent(r,
glennrp77110c32011-05-03 05:25:16 +00007763 ((GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7764 TransparentOpacity : OpaqueOpacity));
glennrpc8c2f062011-02-25 19:00:33 +00007765 r++;
7766 }
7767
7768 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7769 break;
7770
7771 if (image_colors != 0 && image_colors <= 256 &&
7772 image->colormap != NULL)
7773 for (i=0; i<image_colors; i++)
7774 image->colormap[i].opacity =
glennrp77110c32011-05-03 05:25:16 +00007775 (image->colormap[i].opacity > TransparentOpacity/2 ?
7776 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00007777 }
7778 continue;
7779 }
7780
7781 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007782 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7783 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7784 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007785 */
glennrpd3371642011-03-22 19:42:23 +00007786 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7787 {
7788 if (logging != MagickFalse)
7789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7790 " Quantizing the background color to 4-4-4");
7791
7792 tried_444 = MagickTrue;
7793
glennrp77110c32011-05-03 05:25:16 +00007794 hi4=ScaleQuantumToChar(image->background_color.red) & 0xf0;
7795 image->background_color.red=ScaleCharToQuantum((hi4 | (hi4 >> 4)));
7796 hi4=ScaleQuantumToChar(image->background_color.green) & 0xf0;
7797 image->background_color.green=ScaleCharToQuantum((hi4 | (hi4 >> 4)));
7798 hi4=ScaleQuantumToChar(image->background_color.blue) & 0xf0;
7799 image->background_color.blue=ScaleCharToQuantum((hi4 | (hi4 >> 4)));
glennrpd3371642011-03-22 19:42:23 +00007800
7801 if (logging != MagickFalse)
7802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7803 " Quantizing the pixel colors to 4-4-4");
7804
7805 if (image->colormap == NULL)
7806 {
7807 for (y=0; y < (ssize_t) image->rows; y++)
7808 {
7809 r=GetAuthenticPixels(image,0,y,image->columns,1,
7810 exception);
7811
7812 if (r == (PixelPacket *) NULL)
7813 break;
7814
7815 for (x=0; x < (ssize_t) image->columns; x++)
7816 {
glennrp4737d522011-04-29 03:33:42 +00007817 if (GetOpacityPixelComponent(r) == TransparentOpacity)
glennrpd3371642011-03-22 19:42:23 +00007818 {
glennrpb5d5e6d2011-04-27 17:08:19 +00007819 SetRGBPixelComponents(r,image->background_color);
glennrpd3371642011-03-22 19:42:23 +00007820 }
7821 else
7822 {
glennrp77110c32011-05-03 05:25:16 +00007823 hi4=ScaleQuantumToChar(GetRedPixelComponent(r)) & 0xf0;
7824 SetRedPixelComponent(r,ScaleCharToQuantum((hi4 | (hi4 >>
7825 4))));
7826 hi4=ScaleQuantumToChar(GetGreenPixelComponent(r)) & 0xf0;
7827 SetGreenPixelComponent(r,ScaleCharToQuantum((hi4 | (hi4 >>
7828 4))));
7829 hi4=ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xf0;
7830 SetBluePixelComponent(r,ScaleCharToQuantum((hi4 | (hi4 >>
7831 4))));
glennrpd3371642011-03-22 19:42:23 +00007832 }
7833 r++;
7834 }
7835
7836 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7837 break;
7838 }
7839 }
7840
7841 else /* Should not reach this; colormap already exists and
7842 must be <= 256 */
7843 {
7844 if (logging != MagickFalse)
7845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7846 " Quantizing the colormap to 4-4-4");
7847 for (i=0; i<image_colors; i++)
7848 {
glennrp77110c32011-05-03 05:25:16 +00007849 hi4=ScaleQuantumToChar(image->colormap[i].red) & 0xf0;
7850 image->colormap[i].red=ScaleCharToQuantum((hi4 | (hi4 >> 4)));
7851 hi4=ScaleQuantumToChar(image->colormap[i].green) & 0xf0;
7852 image->colormap[i].green=ScaleCharToQuantum((hi4 | (hi4 >> 4)));
7853 hi4=ScaleQuantumToChar(image->colormap[i].blue) & 0xf0;
7854 image->colormap[i].blue=ScaleCharToQuantum((hi4 | (hi4 >> 4)));
glennrpd3371642011-03-22 19:42:23 +00007855 }
7856 }
7857 continue;
7858 }
7859
glennrp82b3c532011-03-22 19:20:54 +00007860 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7861 {
7862 if (logging != MagickFalse)
7863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7864 " Quantizing the background color to 3-3-3");
7865
7866 tried_333 = MagickTrue;
7867
glennrp77110c32011-05-03 05:25:16 +00007868 hi3=ScaleQuantumToChar(image->background_color.red) & 0xe0;
7869 hi2=hi3 & 0xc0;
7870 image->background_color.red=ScaleCharToQuantum(
7871 (hi3 | (hi3 >> 3) | (hi2 >> 6)));
7872 hi3=ScaleQuantumToChar(image->background_color.green) & 0xe0;
7873 hi2=hi3 & 0xc0;
7874 image->background_color.green=ScaleCharToQuantum(
7875 (hi3 | (hi3 >> 3) | (hi2 >> 6)));
7876 hi3=ScaleQuantumToChar(image->background_color.blue) & 0xe0;
7877 hi2=hi3 & 0xc0;
7878 image->background_color.blue=ScaleCharToQuantum(
7879 (hi3 | (hi3 >> 3) | (hi2 >> 6)));
7880
glennrp82b3c532011-03-22 19:20:54 +00007881
7882 if (logging != MagickFalse)
7883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007884 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007885
7886 if (image->colormap == NULL)
7887 {
7888 for (y=0; y < (ssize_t) image->rows; y++)
7889 {
7890 r=GetAuthenticPixels(image,0,y,image->columns,1,
7891 exception);
7892
7893 if (r == (PixelPacket *) NULL)
7894 break;
7895
7896 for (x=0; x < (ssize_t) image->columns; x++)
7897 {
glennrp4737d522011-04-29 03:33:42 +00007898 if (GetOpacityPixelComponent(r) == TransparentOpacity)
glennrp82b3c532011-03-22 19:20:54 +00007899 {
glennrp4737d522011-04-29 03:33:42 +00007900 SetRGBPixelComponents(r,image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00007901 }
7902 else
7903 {
glennrp77110c32011-05-03 05:25:16 +00007904 hi3=ScaleQuantumToChar(GetRedPixelComponent(r)) & 0xe0;
7905 hi2=hi3 & 0xc0;
glennrp4737d522011-04-29 03:33:42 +00007906 SetRedPixelComponent(r,ScaleCharToQuantum(
glennrp77110c32011-05-03 05:25:16 +00007907 (hi3 | (hi3 >> 3) | (hi2 >> 6))));
7908 hi3=ScaleQuantumToChar(GetGreenPixelComponent(r)) & 0xe0;
7909 hi2=hi3 & 0xc0;
glennrp4737d522011-04-29 03:33:42 +00007910 SetGreenPixelComponent(r,ScaleCharToQuantum(
glennrp77110c32011-05-03 05:25:16 +00007911 (hi3 | (hi3 >> 3) | (hi2 >> 6))));
7912 hi3=ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xe0;
7913 hi2=hi3 & 0xc0;
glennrp4737d522011-04-29 03:33:42 +00007914 SetBluePixelComponent(r,ScaleCharToQuantum(
glennrp77110c32011-05-03 05:25:16 +00007915 (hi3 | (hi3 >> 3) | (hi2 >> 6))));
glennrp82b3c532011-03-22 19:20:54 +00007916 }
7917 r++;
7918 }
7919
7920 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7921 break;
7922 }
7923 }
7924
7925 else /* Should not reach this; colormap already exists and
7926 must be <= 256 */
7927 {
7928 if (logging != MagickFalse)
7929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007930 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007931 for (i=0; i<image_colors; i++)
7932 {
glennrp77110c32011-05-03 05:25:16 +00007933 hi3=ScaleQuantumToChar(image->colormap[i].red) & 0xe0;
7934 hi2=hi3 & 0xc0;
glennrp3faa9a32011-04-23 14:00:25 +00007935 image->colormap[i].red=ScaleCharToQuantum(
glennrp77110c32011-05-03 05:25:16 +00007936 (hi3 | (hi3 >> 3) | (hi2 >> 6)));
7937 hi3=ScaleQuantumToChar(image->colormap[i].green) & 0xe0;
7938 hi2=hi3 & 0xc0;
glennrp3faa9a32011-04-23 14:00:25 +00007939 image->colormap[i].green=ScaleCharToQuantum(
glennrp77110c32011-05-03 05:25:16 +00007940 (hi3 | (hi3 >> 3) | (hi2 >> 6)));
7941 hi3=ScaleQuantumToChar(image->colormap[i].blue) & 0xe0;
7942 hi2=hi3 & 0xc0;
glennrp3faa9a32011-04-23 14:00:25 +00007943 image->colormap[i].blue=ScaleCharToQuantum(
glennrp77110c32011-05-03 05:25:16 +00007944 (hi3 | (hi3 >> 3) | (hi2 >> 6)));
glennrp82b3c532011-03-22 19:20:54 +00007945 }
glennrpd3371642011-03-22 19:42:23 +00007946 }
7947 continue;
glennrp82b3c532011-03-22 19:20:54 +00007948 }
glennrpc8c2f062011-02-25 19:00:33 +00007949
glennrpc8c2f062011-02-25 19:00:33 +00007950 if (image_colors == 0 || image_colors > 256)
7951 {
7952 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007954 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007955
glennrp3faa9a32011-04-23 14:00:25 +00007956 /* Red and green were already done so we only quantize the blue
7957 * channel
7958 */
7959
glennrp77110c32011-05-03 05:25:16 +00007960 hi2=ScaleQuantumToChar(image->background_color.blue) & 0xc0;
glennrp3faa9a32011-04-23 14:00:25 +00007961 image->background_color.blue=ScaleCharToQuantum(
glennrp77110c32011-05-03 05:25:16 +00007962 (hi2 | (hi2 >> 2) | (hi2 >> 4) | (hi2 >> 6)));
glennrpfd05d622011-02-25 04:10:33 +00007963
glennrpc8c2f062011-02-25 19:00:33 +00007964 if (logging != MagickFalse)
7965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007966 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007967
glennrpc8c2f062011-02-25 19:00:33 +00007968 if (image->colormap == NULL)
7969 {
7970 for (y=0; y < (ssize_t) image->rows; y++)
7971 {
7972 r=GetAuthenticPixels(image,0,y,image->columns,1,
7973 exception);
7974
7975 if (r == (PixelPacket *) NULL)
7976 break;
7977
7978 for (x=0; x < (ssize_t) image->columns; x++)
7979 {
glennrp4737d522011-04-29 03:33:42 +00007980 if (GetOpacityPixelComponent(r) == TransparentOpacity)
glennrp82b3c532011-03-22 19:20:54 +00007981 {
glennrpb5d5e6d2011-04-27 17:08:19 +00007982 SetRGBPixelComponents(r,image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00007983 }
7984 else
7985 {
glennrp77110c32011-05-03 05:25:16 +00007986 hi2=ScaleQuantumToChar(GetBluePixelComponent(r)) & 0xc0;
glennrp4737d522011-04-29 03:33:42 +00007987 SetBluePixelComponent(r,ScaleCharToQuantum(
glennrp77110c32011-05-03 05:25:16 +00007988 (hi2 | (hi2 >> 2) | (hi2 >> 4) | (hi2 >> 6))));
glennrp82b3c532011-03-22 19:20:54 +00007989 }
glennrp52a479c2011-02-26 21:14:38 +00007990 r++;
glennrpc8c2f062011-02-25 19:00:33 +00007991 }
glennrpfd05d622011-02-25 04:10:33 +00007992
glennrpc8c2f062011-02-25 19:00:33 +00007993 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7994 break;
7995 }
7996 }
glennrpfd05d622011-02-25 04:10:33 +00007997
glennrpc8c2f062011-02-25 19:00:33 +00007998 else /* Should not reach this; colormap already exists and
7999 must be <= 256 */
8000 {
8001 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008003 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008004 for (i=0; i<image_colors; i++)
8005 {
glennrp77110c32011-05-03 05:25:16 +00008006 hi2=ScaleQuantumToChar(image->colormap[i].blue) & 0xc0;
glennrp3faa9a32011-04-23 14:00:25 +00008007 image->colormap[i].blue=ScaleCharToQuantum(
glennrp77110c32011-05-03 05:25:16 +00008008 (hi2 | (hi2 >> 2) | (hi2 >> 4) | (hi2 >> 6)));
glennrpc8c2f062011-02-25 19:00:33 +00008009 }
8010 }
8011 continue;
8012 }
8013 break;
glennrpd71e86a2011-02-24 01:28:37 +00008014 }
glennrpfd05d622011-02-25 04:10:33 +00008015 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008016
glennrpfd05d622011-02-25 04:10:33 +00008017 /* If we are excluding the tRNS chunk and there is transparency,
8018 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8019 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008020 */
glennrp0e8ea192010-12-24 18:00:33 +00008021 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8022 (number_transparent != 0 || number_semitransparent != 0))
8023 {
glennrpd17915c2011-04-29 14:24:22 +00008024 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008025
8026 if (ping_have_color == MagickFalse)
8027 mng_info->write_png_colortype = 5;
8028
8029 else
8030 mng_info->write_png_colortype = 7;
8031
glennrp8d579662011-02-23 02:05:02 +00008032 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008033 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008034 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008035
glennrp0e8ea192010-12-24 18:00:33 +00008036 }
8037
glennrpfd05d622011-02-25 04:10:33 +00008038 /* See if cheap transparency is possible. It is only possible
8039 * when there is a single transparent color, no semitransparent
8040 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008041 * as the transparent color. We only need this information if
8042 * we are writing a PNG with colortype 0 or 2, and we have not
8043 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008044 */
glennrp5a39f372011-02-25 04:52:16 +00008045 if (number_transparent == 1 &&
8046 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008047 {
8048 ping_have_cheap_transparency = MagickTrue;
8049
8050 if (number_semitransparent != 0)
8051 ping_have_cheap_transparency = MagickFalse;
8052
8053 else if (image_colors == 0 || image_colors > 256 ||
8054 image->colormap == NULL)
8055 {
8056 ExceptionInfo
8057 *exception;
8058
8059 register const PixelPacket
8060 *q;
8061
8062 exception=(&image->exception);
8063
8064 for (y=0; y < (ssize_t) image->rows; y++)
8065 {
8066 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8067
8068 if (q == (PixelPacket *) NULL)
8069 break;
8070
8071 for (x=0; x < (ssize_t) image->columns; x++)
8072 {
8073 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008074 (unsigned short) GetRedPixelComponent(q) ==
8075 ping_trans_color.red &&
8076 (unsigned short) GetGreenPixelComponent(q) ==
8077 ping_trans_color.green &&
8078 (unsigned short) GetBluePixelComponent(q) ==
8079 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008080 {
8081 ping_have_cheap_transparency = MagickFalse;
8082 break;
8083 }
8084
8085 q++;
8086 }
8087
8088 if (ping_have_cheap_transparency == MagickFalse)
8089 break;
8090 }
8091 }
8092 else
8093 {
glennrp67b9c1a2011-04-22 18:47:36 +00008094 /* Assuming that image->colormap[0] is the one transparent color
8095 * and that all others are opaque.
8096 */
glennrpfd05d622011-02-25 04:10:33 +00008097 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008098 for (i=1; i<image_colors; i++)
8099 if (image->colormap[i].red == image->colormap[0].red &&
8100 image->colormap[i].green == image->colormap[0].green &&
8101 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008102 {
glennrp67b9c1a2011-04-22 18:47:36 +00008103 ping_have_cheap_transparency = MagickFalse;
8104 break;
glennrpfd05d622011-02-25 04:10:33 +00008105 }
8106 }
8107
8108 if (logging != MagickFalse)
8109 {
8110 if (ping_have_cheap_transparency == MagickFalse)
8111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8112 " Cheap transparency is not possible.");
8113
8114 else
8115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8116 " Cheap transparency is possible.");
8117 }
8118 }
8119 else
8120 ping_have_cheap_transparency = MagickFalse;
8121
glennrp8640fb52010-11-23 15:48:26 +00008122 image_depth=image->depth;
8123
glennrp26c990a2010-11-23 02:23:20 +00008124 quantum_info = (QuantumInfo *) NULL;
8125 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008126 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008127 image_matte=image->matte;
8128
glennrp0fe50b42010-11-16 03:52:51 +00008129 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008130 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008131
glennrp52a479c2011-02-26 21:14:38 +00008132 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8133 (image->colors == 0 || image->colormap == NULL))
8134 {
glennrp52a479c2011-02-26 21:14:38 +00008135 image_info=DestroyImageInfo(image_info);
8136 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008137 (void) ThrowMagickException(&IMimage->exception,
8138 GetMagickModule(),CoderError,
8139 "Cannot write PNG8 or color-type 3; colormap is NULL",
8140 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008141#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8142 UnlockSemaphoreInfo(ping_semaphore);
8143#endif
8144 return(MagickFalse);
8145 }
8146
cristy3ed852e2009-09-05 21:47:34 +00008147 /*
8148 Allocate the PNG structures
8149 */
8150#ifdef PNG_USER_MEM_SUPPORTED
8151 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008152 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8153 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008154
cristy3ed852e2009-09-05 21:47:34 +00008155#else
8156 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008157 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008158
cristy3ed852e2009-09-05 21:47:34 +00008159#endif
8160 if (ping == (png_struct *) NULL)
8161 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008162
cristy3ed852e2009-09-05 21:47:34 +00008163 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008164
cristy3ed852e2009-09-05 21:47:34 +00008165 if (ping_info == (png_info *) NULL)
8166 {
8167 png_destroy_write_struct(&ping,(png_info **) NULL);
8168 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8169 }
glennrp0fe50b42010-11-16 03:52:51 +00008170
cristy3ed852e2009-09-05 21:47:34 +00008171 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008172 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008173
glennrp5af765f2010-03-30 11:12:18 +00008174 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008175 {
8176 /*
8177 PNG write failed.
8178 */
8179#ifdef PNG_DEBUG
8180 if (image_info->verbose)
8181 (void) printf("PNG write has failed.\n");
8182#endif
8183 png_destroy_write_struct(&ping,&ping_info);
8184#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008185 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008186#endif
glennrpda8f3a72011-02-27 23:54:12 +00008187 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008188 (void) CloseBlob(image);
8189 image_info=DestroyImageInfo(image_info);
8190 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008191 return(MagickFalse);
8192 }
8193 /*
8194 Prepare PNG for writing.
8195 */
8196#if defined(PNG_MNG_FEATURES_SUPPORTED)
8197 if (mng_info->write_mng)
8198 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008199
cristy3ed852e2009-09-05 21:47:34 +00008200#else
8201# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8202 if (mng_info->write_mng)
8203 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008204
cristy3ed852e2009-09-05 21:47:34 +00008205# endif
8206#endif
glennrp2b013e42010-11-24 16:55:50 +00008207
cristy3ed852e2009-09-05 21:47:34 +00008208 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008209
cristy4e5bc842010-06-09 13:56:01 +00008210 ping_width=(png_uint_32) image->columns;
8211 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008212
cristy3ed852e2009-09-05 21:47:34 +00008213 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8214 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008215
cristy3ed852e2009-09-05 21:47:34 +00008216 if (mng_info->write_png_depth != 0)
8217 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008218
cristy3ed852e2009-09-05 21:47:34 +00008219 /* Adjust requested depth to next higher valid depth if necessary */
8220 if (image_depth > 8)
8221 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008222
cristy3ed852e2009-09-05 21:47:34 +00008223 if ((image_depth > 4) && (image_depth < 8))
8224 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008225
cristy3ed852e2009-09-05 21:47:34 +00008226 if (image_depth == 3)
8227 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008228
cristy3ed852e2009-09-05 21:47:34 +00008229 if (logging != MagickFalse)
8230 {
8231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008232 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008234 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008236 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008238 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008240 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008241 }
glennrp8640fb52010-11-23 15:48:26 +00008242
cristy3ed852e2009-09-05 21:47:34 +00008243 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008244 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008245
glennrp26f37912010-12-23 16:22:42 +00008246
cristy3ed852e2009-09-05 21:47:34 +00008247#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008248 if (ping_exclude_pHYs == MagickFalse)
8249 {
cristy3ed852e2009-09-05 21:47:34 +00008250 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8251 (!mng_info->write_mng || !mng_info->equal_physs))
8252 {
glennrp0fe50b42010-11-16 03:52:51 +00008253 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8255 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008256
8257 if (image->units == PixelsPerInchResolution)
8258 {
glennrpdfd70802010-11-14 01:23:35 +00008259 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008260 ping_pHYs_x_resolution=
8261 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8262 ping_pHYs_y_resolution=
8263 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008264 }
glennrpdfd70802010-11-14 01:23:35 +00008265
cristy3ed852e2009-09-05 21:47:34 +00008266 else if (image->units == PixelsPerCentimeterResolution)
8267 {
glennrpdfd70802010-11-14 01:23:35 +00008268 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008269 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8270 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008271 }
glennrp991d11d2010-11-12 21:55:28 +00008272
cristy3ed852e2009-09-05 21:47:34 +00008273 else
8274 {
glennrpdfd70802010-11-14 01:23:35 +00008275 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8276 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8277 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008278 }
glennrp991d11d2010-11-12 21:55:28 +00008279
glennrp823b55c2011-03-14 18:46:46 +00008280 if (logging != MagickFalse)
8281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8282 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8283 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8284 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008285 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008286 }
glennrp26f37912010-12-23 16:22:42 +00008287 }
cristy3ed852e2009-09-05 21:47:34 +00008288#endif
glennrpa521b2f2010-10-29 04:11:03 +00008289
glennrp26f37912010-12-23 16:22:42 +00008290 if (ping_exclude_bKGD == MagickFalse)
8291 {
glennrpa521b2f2010-10-29 04:11:03 +00008292 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008293 {
glennrpa521b2f2010-10-29 04:11:03 +00008294 unsigned int
8295 mask;
cristy3ed852e2009-09-05 21:47:34 +00008296
glennrpa521b2f2010-10-29 04:11:03 +00008297 mask=0xffff;
8298 if (ping_bit_depth == 8)
8299 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008300
glennrpa521b2f2010-10-29 04:11:03 +00008301 if (ping_bit_depth == 4)
8302 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008303
glennrpa521b2f2010-10-29 04:11:03 +00008304 if (ping_bit_depth == 2)
8305 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008306
glennrpa521b2f2010-10-29 04:11:03 +00008307 if (ping_bit_depth == 1)
8308 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008309
glennrpa521b2f2010-10-29 04:11:03 +00008310 ping_background.red=(png_uint_16)
8311 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008312
glennrpa521b2f2010-10-29 04:11:03 +00008313 ping_background.green=(png_uint_16)
8314 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008315
glennrpa521b2f2010-10-29 04:11:03 +00008316 ping_background.blue=(png_uint_16)
8317 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008318
8319 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008320 }
cristy3ed852e2009-09-05 21:47:34 +00008321
glennrp0fe50b42010-11-16 03:52:51 +00008322 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008323 {
8324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8325 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8327 " background_color index is %d",
8328 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008329
8330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8331 " ping_bit_depth=%d",ping_bit_depth);
8332 }
glennrp0fe50b42010-11-16 03:52:51 +00008333
8334 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008335 }
glennrp0fe50b42010-11-16 03:52:51 +00008336
cristy3ed852e2009-09-05 21:47:34 +00008337 /*
8338 Select the color type.
8339 */
8340 matte=image_matte;
8341 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008342
glennrp1273f7b2011-02-24 03:20:30 +00008343 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008344 {
glennrp0fe50b42010-11-16 03:52:51 +00008345
glennrpfd05d622011-02-25 04:10:33 +00008346 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008347 for reducing the sample depth from 8. */
8348
glennrp0fe50b42010-11-16 03:52:51 +00008349 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008350
glennrp8bb3a022010-12-13 20:40:04 +00008351 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008352
8353 /*
8354 Set image palette.
8355 */
8356 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8357
glennrp0fe50b42010-11-16 03:52:51 +00008358 if (logging != MagickFalse)
8359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8360 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008361 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008362
8363 for (i=0; i < (ssize_t) number_colors; i++)
8364 {
8365 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8366 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8367 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8368 if (logging != MagickFalse)
8369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008370#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008371 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008372#else
8373 " %5ld (%5d,%5d,%5d)",
8374#endif
glennrp0fe50b42010-11-16 03:52:51 +00008375 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8376
8377 }
glennrp2b013e42010-11-24 16:55:50 +00008378
glennrp8bb3a022010-12-13 20:40:04 +00008379 ping_have_PLTE=MagickTrue;
8380 image_depth=ping_bit_depth;
8381 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008382
glennrp58e01762011-01-07 15:28:54 +00008383 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008384 {
glennrp0fe50b42010-11-16 03:52:51 +00008385 /*
8386 Identify which colormap entry is transparent.
8387 */
8388 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008389 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008390
glennrp8bb3a022010-12-13 20:40:04 +00008391 for (i=0; i < (ssize_t) number_transparent; i++)
8392 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008393
glennrp0fe50b42010-11-16 03:52:51 +00008394
glennrp2cc891a2010-12-24 13:44:32 +00008395 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008396 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008397
8398 if (ping_num_trans == 0)
8399 ping_have_tRNS=MagickFalse;
8400
glennrp8bb3a022010-12-13 20:40:04 +00008401 else
8402 ping_have_tRNS=MagickTrue;
8403 }
glennrp0fe50b42010-11-16 03:52:51 +00008404
glennrp1273f7b2011-02-24 03:20:30 +00008405 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008406 {
glennrp1273f7b2011-02-24 03:20:30 +00008407 /*
8408 * Identify which colormap entry is the background color.
8409 */
8410
glennrp4f25bd02011-01-01 18:51:28 +00008411 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8412 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8413 break;
glennrp0fe50b42010-11-16 03:52:51 +00008414
glennrp4f25bd02011-01-01 18:51:28 +00008415 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00008416
8417 if (logging != MagickFalse)
8418 {
8419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8420 " background_color index is %d",
8421 (int) ping_background.index);
8422 }
glennrp4f25bd02011-01-01 18:51:28 +00008423 }
cristy3ed852e2009-09-05 21:47:34 +00008424 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008425
cristy3ed852e2009-09-05 21:47:34 +00008426 else if (mng_info->write_png24)
8427 {
8428 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008429 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008430 }
glennrp0fe50b42010-11-16 03:52:51 +00008431
cristy3ed852e2009-09-05 21:47:34 +00008432 else if (mng_info->write_png32)
8433 {
8434 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008435 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008436 }
glennrp0fe50b42010-11-16 03:52:51 +00008437
glennrp8bb3a022010-12-13 20:40:04 +00008438 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008439 {
glennrp5af765f2010-03-30 11:12:18 +00008440 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008441
glennrp8bb3a022010-12-13 20:40:04 +00008442 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008443 {
glennrp5af765f2010-03-30 11:12:18 +00008444 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008445
glennrp5af765f2010-03-30 11:12:18 +00008446 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8447 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008448 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008449
glennrp8bb3a022010-12-13 20:40:04 +00008450 else
8451 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008452
8453 if (logging != MagickFalse)
8454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8455 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008456 }
glennrp0fe50b42010-11-16 03:52:51 +00008457
glennrp7c4c9e62011-03-21 20:23:32 +00008458 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008459 {
8460 if (logging != MagickFalse)
8461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008462 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008463
glennrpd6bf1612010-12-17 17:28:54 +00008464 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008465 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008466
glennrpd6bf1612010-12-17 17:28:54 +00008467 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008468 {
glennrp5af765f2010-03-30 11:12:18 +00008469 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008470 image_matte=MagickFalse;
8471 }
glennrp0fe50b42010-11-16 03:52:51 +00008472
glennrpd6bf1612010-12-17 17:28:54 +00008473 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008474 {
glennrp5af765f2010-03-30 11:12:18 +00008475 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008476 image_matte=MagickTrue;
8477 }
glennrp0fe50b42010-11-16 03:52:51 +00008478
glennrp5aa37f62011-01-02 03:07:57 +00008479 if (image_info->type == PaletteType ||
8480 image_info->type == PaletteMatteType)
8481 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8482
glennrp7c4c9e62011-03-21 20:23:32 +00008483 if (mng_info->write_png_colortype == 0 &&
8484 (image_info->type == UndefinedType ||
8485 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008486 {
glennrp5aa37f62011-01-02 03:07:57 +00008487 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008488 {
glennrp5aa37f62011-01-02 03:07:57 +00008489 if (image_matte == MagickFalse)
8490 {
8491 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8492 image_matte=MagickFalse;
8493 }
glennrp0fe50b42010-11-16 03:52:51 +00008494
glennrp0b206f52011-01-07 04:55:32 +00008495 else
glennrp5aa37f62011-01-02 03:07:57 +00008496 {
8497 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8498 image_matte=MagickTrue;
8499 }
8500 }
8501 else
glennrp8bb3a022010-12-13 20:40:04 +00008502 {
glennrp5aa37f62011-01-02 03:07:57 +00008503 if (image_matte == MagickFalse)
8504 {
8505 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8506 image_matte=MagickFalse;
8507 }
glennrp8bb3a022010-12-13 20:40:04 +00008508
glennrp0b206f52011-01-07 04:55:32 +00008509 else
glennrp5aa37f62011-01-02 03:07:57 +00008510 {
8511 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8512 image_matte=MagickTrue;
8513 }
8514 }
glennrp0fe50b42010-11-16 03:52:51 +00008515 }
glennrp5aa37f62011-01-02 03:07:57 +00008516
cristy3ed852e2009-09-05 21:47:34 +00008517 }
glennrp0fe50b42010-11-16 03:52:51 +00008518
cristy3ed852e2009-09-05 21:47:34 +00008519 if (logging != MagickFalse)
8520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008521 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008522
glennrp5af765f2010-03-30 11:12:18 +00008523 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008524 {
8525 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8526 ping_color_type == PNG_COLOR_TYPE_RGB ||
8527 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8528 ping_bit_depth=8;
8529 }
cristy3ed852e2009-09-05 21:47:34 +00008530
glennrpd6bf1612010-12-17 17:28:54 +00008531 old_bit_depth=ping_bit_depth;
8532
glennrp5af765f2010-03-30 11:12:18 +00008533 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008534 {
glennrp8d579662011-02-23 02:05:02 +00008535 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8536 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008537 }
glennrp8640fb52010-11-23 15:48:26 +00008538
glennrp5af765f2010-03-30 11:12:18 +00008539 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008540 {
cristy35ef8242010-06-03 16:24:13 +00008541 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008542 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008543
8544 if (image->colors == 0)
8545 {
glennrp0fe50b42010-11-16 03:52:51 +00008546 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008547 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008548 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008549 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008550 }
8551
cristy35ef8242010-06-03 16:24:13 +00008552 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008553 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008554 }
glennrp2b013e42010-11-24 16:55:50 +00008555
glennrpd6bf1612010-12-17 17:28:54 +00008556 if (logging != MagickFalse)
8557 {
8558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8559 " Number of colors: %.20g",(double) image_colors);
8560
8561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8562 " Tentative PNG bit depth: %d",ping_bit_depth);
8563 }
8564
8565 if (ping_bit_depth < (int) mng_info->write_png_depth)
8566 ping_bit_depth = mng_info->write_png_depth;
8567 }
glennrp2cc891a2010-12-24 13:44:32 +00008568
glennrp5af765f2010-03-30 11:12:18 +00008569 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008570
cristy3ed852e2009-09-05 21:47:34 +00008571 if (logging != MagickFalse)
8572 {
8573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008574 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008575
cristy3ed852e2009-09-05 21:47:34 +00008576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008577 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008578
cristy3ed852e2009-09-05 21:47:34 +00008579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008580 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008581
cristy3ed852e2009-09-05 21:47:34 +00008582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008583
glennrp8640fb52010-11-23 15:48:26 +00008584 " image->depth: %.20g",(double) image->depth);
8585
8586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008587 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008588 }
8589
glennrp58e01762011-01-07 15:28:54 +00008590 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008591 {
glennrp4f25bd02011-01-01 18:51:28 +00008592 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008593 {
glennrp7c4c9e62011-03-21 20:23:32 +00008594 if (mng_info->write_png_colortype == 0)
8595 {
8596 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008597
glennrp7c4c9e62011-03-21 20:23:32 +00008598 if (ping_have_color != MagickFalse)
8599 ping_color_type=PNG_COLOR_TYPE_RGBA;
8600 }
glennrp4f25bd02011-01-01 18:51:28 +00008601
8602 /*
8603 * Determine if there is any transparent color.
8604 */
8605 if (number_transparent + number_semitransparent == 0)
8606 {
8607 /*
8608 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8609 */
glennrpa6a06632011-01-19 15:15:34 +00008610
glennrp4f25bd02011-01-01 18:51:28 +00008611 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008612
8613 if (mng_info->write_png_colortype == 0)
8614 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008615 }
8616
8617 else
8618 {
8619 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008620 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008621
8622 mask=0xffff;
8623
8624 if (ping_bit_depth == 8)
8625 mask=0x00ff;
8626
8627 if (ping_bit_depth == 4)
8628 mask=0x000f;
8629
8630 if (ping_bit_depth == 2)
8631 mask=0x0003;
8632
8633 if (ping_bit_depth == 1)
8634 mask=0x0001;
8635
8636 ping_trans_color.red=(png_uint_16)
8637 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8638
8639 ping_trans_color.green=(png_uint_16)
8640 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8641
8642 ping_trans_color.blue=(png_uint_16)
8643 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8644
8645 ping_trans_color.gray=(png_uint_16)
8646 (ScaleQuantumToShort(PixelIntensityToQuantum(
8647 image->colormap)) & mask);
8648
8649 ping_trans_color.index=(png_byte) 0;
8650
8651 ping_have_tRNS=MagickTrue;
8652 }
8653
8654 if (ping_have_tRNS != MagickFalse)
8655 {
8656 /*
glennrpfd05d622011-02-25 04:10:33 +00008657 * Determine if there is one and only one transparent color
8658 * and if so if it is fully transparent.
8659 */
8660 if (ping_have_cheap_transparency == MagickFalse)
8661 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008662 }
8663
8664 if (ping_have_tRNS != MagickFalse)
8665 {
glennrp7c4c9e62011-03-21 20:23:32 +00008666 if (mng_info->write_png_colortype == 0)
8667 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008668
8669 if (image_depth == 8)
8670 {
8671 ping_trans_color.red&=0xff;
8672 ping_trans_color.green&=0xff;
8673 ping_trans_color.blue&=0xff;
8674 ping_trans_color.gray&=0xff;
8675 }
8676 }
8677 }
cristy3ed852e2009-09-05 21:47:34 +00008678 else
8679 {
cristy3ed852e2009-09-05 21:47:34 +00008680 if (image_depth == 8)
8681 {
glennrp5af765f2010-03-30 11:12:18 +00008682 ping_trans_color.red&=0xff;
8683 ping_trans_color.green&=0xff;
8684 ping_trans_color.blue&=0xff;
8685 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008686 }
8687 }
8688 }
glennrp8640fb52010-11-23 15:48:26 +00008689
cristy3ed852e2009-09-05 21:47:34 +00008690 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008691
glennrp2e09f552010-11-14 00:38:48 +00008692 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008693 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008694
glennrp39992b42010-11-14 00:03:43 +00008695 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008696 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008697 ping_have_color == MagickFalse &&
8698 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008699 {
cristy35ef8242010-06-03 16:24:13 +00008700 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008701
cristy3ed852e2009-09-05 21:47:34 +00008702 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008703 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008704
glennrp7c4c9e62011-03-21 20:23:32 +00008705 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008706 {
glennrp5af765f2010-03-30 11:12:18 +00008707 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008708
cristy3ed852e2009-09-05 21:47:34 +00008709 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008710 {
8711 if (logging != MagickFalse)
8712 {
8713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8714 " Scaling ping_trans_color (0)");
8715 }
8716 ping_trans_color.gray*=0x0101;
8717 }
cristy3ed852e2009-09-05 21:47:34 +00008718 }
glennrp0fe50b42010-11-16 03:52:51 +00008719
cristy3ed852e2009-09-05 21:47:34 +00008720 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8721 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008722
glennrp136ee3a2011-04-27 15:47:45 +00008723 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00008724 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008725 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008726
cristy3ed852e2009-09-05 21:47:34 +00008727 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008728 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008729
cristy3ed852e2009-09-05 21:47:34 +00008730 else
8731 {
glennrp5af765f2010-03-30 11:12:18 +00008732 ping_bit_depth=8;
8733 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008734 {
8735 if(!mng_info->write_png_depth)
8736 {
glennrp5af765f2010-03-30 11:12:18 +00008737 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008738
cristy35ef8242010-06-03 16:24:13 +00008739 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008740 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008741 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008742 }
8743 }
glennrp2b013e42010-11-24 16:55:50 +00008744
glennrp0fe50b42010-11-16 03:52:51 +00008745 else if (ping_color_type ==
8746 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008747 mng_info->IsPalette)
8748 {
cristy3ed852e2009-09-05 21:47:34 +00008749 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008750
cristy3ed852e2009-09-05 21:47:34 +00008751 int
8752 depth_4_ok=MagickTrue,
8753 depth_2_ok=MagickTrue,
8754 depth_1_ok=MagickTrue;
8755
cristybb503372010-05-27 20:51:26 +00008756 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008757 {
8758 unsigned char
8759 intensity;
8760
8761 intensity=ScaleQuantumToChar(image->colormap[i].red);
8762
8763 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8764 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8765 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8766 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008767 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008768 depth_1_ok=MagickFalse;
8769 }
glennrp2b013e42010-11-24 16:55:50 +00008770
cristy3ed852e2009-09-05 21:47:34 +00008771 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008772 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008773
cristy3ed852e2009-09-05 21:47:34 +00008774 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008775 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008776
cristy3ed852e2009-09-05 21:47:34 +00008777 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008778 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008779 }
8780 }
glennrp2b013e42010-11-24 16:55:50 +00008781
glennrp5af765f2010-03-30 11:12:18 +00008782 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008783 }
glennrp0fe50b42010-11-16 03:52:51 +00008784
cristy3ed852e2009-09-05 21:47:34 +00008785 else
glennrp0fe50b42010-11-16 03:52:51 +00008786
cristy3ed852e2009-09-05 21:47:34 +00008787 if (mng_info->IsPalette)
8788 {
glennrp17a14852010-05-10 03:01:59 +00008789 number_colors=image_colors;
8790
cristy3ed852e2009-09-05 21:47:34 +00008791 if (image_depth <= 8)
8792 {
cristy3ed852e2009-09-05 21:47:34 +00008793 /*
8794 Set image palette.
8795 */
glennrp5af765f2010-03-30 11:12:18 +00008796 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008797
glennrp58e01762011-01-07 15:28:54 +00008798 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008799 {
glennrp9c1eb072010-06-06 22:19:15 +00008800 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008801
glennrp3b51f0e2010-11-27 18:14:08 +00008802 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8804 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008805 }
glennrp0fe50b42010-11-16 03:52:51 +00008806
cristy3ed852e2009-09-05 21:47:34 +00008807 else
8808 {
cristybb503372010-05-27 20:51:26 +00008809 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008810 {
8811 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8812 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8813 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8814 }
glennrp0fe50b42010-11-16 03:52:51 +00008815
glennrp3b51f0e2010-11-27 18:14:08 +00008816 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008818 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008819 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008820
glennrp39992b42010-11-14 00:03:43 +00008821 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008822 }
glennrp0fe50b42010-11-16 03:52:51 +00008823
cristy3ed852e2009-09-05 21:47:34 +00008824 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008825 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008826 {
cristybefe4d22010-06-07 01:18:58 +00008827 size_t
8828 one;
8829
glennrp5af765f2010-03-30 11:12:18 +00008830 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008831 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008832
glennrpd17915c2011-04-29 14:24:22 +00008833 while ((one << ping_bit_depth) < (ssize_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008834 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008835 }
glennrp0fe50b42010-11-16 03:52:51 +00008836
glennrp5af765f2010-03-30 11:12:18 +00008837 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008838
glennrp58e01762011-01-07 15:28:54 +00008839 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008840 {
glennrp0fe50b42010-11-16 03:52:51 +00008841 /*
glennrpd6bf1612010-12-17 17:28:54 +00008842 * Set up trans_colors array.
8843 */
glennrp0fe50b42010-11-16 03:52:51 +00008844 assert(number_colors <= 256);
8845
glennrpd6bf1612010-12-17 17:28:54 +00008846 ping_num_trans=(unsigned short) (number_transparent +
8847 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008848
8849 if (ping_num_trans == 0)
8850 ping_have_tRNS=MagickFalse;
8851
glennrpd6bf1612010-12-17 17:28:54 +00008852 else
glennrp0fe50b42010-11-16 03:52:51 +00008853 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008854 if (logging != MagickFalse)
8855 {
8856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8857 " Scaling ping_trans_color (1)");
8858 }
glennrpd6bf1612010-12-17 17:28:54 +00008859 ping_have_tRNS=MagickTrue;
8860
8861 for (i=0; i < ping_num_trans; i++)
8862 {
8863 ping_trans_alpha[i]= (png_byte) (255-
8864 ScaleQuantumToChar(image->colormap[i].opacity));
8865 }
glennrp0fe50b42010-11-16 03:52:51 +00008866 }
8867 }
cristy3ed852e2009-09-05 21:47:34 +00008868 }
8869 }
glennrp0fe50b42010-11-16 03:52:51 +00008870
cristy3ed852e2009-09-05 21:47:34 +00008871 else
8872 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008873
cristy3ed852e2009-09-05 21:47:34 +00008874 if (image_depth < 8)
8875 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008876
cristy3ed852e2009-09-05 21:47:34 +00008877 if ((save_image_depth == 16) && (image_depth == 8))
8878 {
glennrp4f25bd02011-01-01 18:51:28 +00008879 if (logging != MagickFalse)
8880 {
8881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8882 " Scaling ping_trans_color from (%d,%d,%d)",
8883 (int) ping_trans_color.red,
8884 (int) ping_trans_color.green,
8885 (int) ping_trans_color.blue);
8886 }
8887
glennrp5af765f2010-03-30 11:12:18 +00008888 ping_trans_color.red*=0x0101;
8889 ping_trans_color.green*=0x0101;
8890 ping_trans_color.blue*=0x0101;
8891 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008892
8893 if (logging != MagickFalse)
8894 {
8895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8896 " to (%d,%d,%d)",
8897 (int) ping_trans_color.red,
8898 (int) ping_trans_color.green,
8899 (int) ping_trans_color.blue);
8900 }
cristy3ed852e2009-09-05 21:47:34 +00008901 }
8902 }
8903
cristy4383ec82011-01-05 15:42:32 +00008904 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8905 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008906
cristy3ed852e2009-09-05 21:47:34 +00008907 /*
8908 Adjust background and transparency samples in sub-8-bit grayscale files.
8909 */
glennrp5af765f2010-03-30 11:12:18 +00008910 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008911 PNG_COLOR_TYPE_GRAY)
8912 {
8913 png_uint_16
8914 maxval;
8915
cristy35ef8242010-06-03 16:24:13 +00008916 size_t
8917 one=1;
8918
cristy22ffd972010-06-03 16:51:47 +00008919 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008920
glennrp4f25bd02011-01-01 18:51:28 +00008921 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008922 {
cristy3ed852e2009-09-05 21:47:34 +00008923
glennrpa521b2f2010-10-29 04:11:03 +00008924 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008925 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8926
8927 if (logging != MagickFalse)
8928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008929 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00008930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8931 " background_color index is %d",
8932 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008933
glennrp991d11d2010-11-12 21:55:28 +00008934 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008935 }
cristy3ed852e2009-09-05 21:47:34 +00008936
glennrp5af765f2010-03-30 11:12:18 +00008937 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8938 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008939 }
glennrp17a14852010-05-10 03:01:59 +00008940
glennrp26f37912010-12-23 16:22:42 +00008941 if (ping_exclude_bKGD == MagickFalse)
8942 {
glennrp1273f7b2011-02-24 03:20:30 +00008943 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008944 {
8945 /*
8946 Identify which colormap entry is the background color.
8947 */
8948
glennrp17a14852010-05-10 03:01:59 +00008949 number_colors=image_colors;
8950
glennrpa521b2f2010-10-29 04:11:03 +00008951 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8952 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008953 break;
8954
8955 ping_background.index=(png_byte) i;
8956
glennrp3b51f0e2010-11-27 18:14:08 +00008957 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008958 {
8959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008960 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008961 }
glennrp0fe50b42010-11-16 03:52:51 +00008962
cristy13d07042010-11-21 20:56:18 +00008963 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008964 {
8965 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008966
8967 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008968 {
8969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8970 " background =(%d,%d,%d)",
8971 (int) ping_background.red,
8972 (int) ping_background.green,
8973 (int) ping_background.blue);
8974 }
8975 }
glennrpa521b2f2010-10-29 04:11:03 +00008976
glennrpd6bf1612010-12-17 17:28:54 +00008977 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008978 {
glennrp3b51f0e2010-11-27 18:14:08 +00008979 if (logging != MagickFalse)
8980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8981 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008982 ping_have_bKGD = MagickFalse;
8983 }
glennrp17a14852010-05-10 03:01:59 +00008984 }
glennrp26f37912010-12-23 16:22:42 +00008985 }
glennrp17a14852010-05-10 03:01:59 +00008986
cristy3ed852e2009-09-05 21:47:34 +00008987 if (logging != MagickFalse)
8988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00008989 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008990 /*
8991 Initialize compression level and filtering.
8992 */
8993 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008994 {
8995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8996 " Setting up deflate compression");
8997
8998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8999 " Compression buffer size: 32768");
9000 }
9001
cristy3ed852e2009-09-05 21:47:34 +00009002 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009003
cristy3ed852e2009-09-05 21:47:34 +00009004 if (logging != MagickFalse)
9005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9006 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009007
cristy3ed852e2009-09-05 21:47:34 +00009008 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009009
cristy3ed852e2009-09-05 21:47:34 +00009010 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9011 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009012
cristy3ed852e2009-09-05 21:47:34 +00009013 if (quality > 9)
9014 {
9015 int
9016 level;
9017
cristybb503372010-05-27 20:51:26 +00009018 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009019
cristy3ed852e2009-09-05 21:47:34 +00009020 if (logging != MagickFalse)
9021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9022 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00009023
cristy3ed852e2009-09-05 21:47:34 +00009024 png_set_compression_level(ping,level);
9025 }
glennrp0fe50b42010-11-16 03:52:51 +00009026
cristy3ed852e2009-09-05 21:47:34 +00009027 else
9028 {
9029 if (logging != MagickFalse)
9030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9031 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009032
cristy3ed852e2009-09-05 21:47:34 +00009033 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9034 }
glennrp0fe50b42010-11-16 03:52:51 +00009035
cristy3ed852e2009-09-05 21:47:34 +00009036 if (logging != MagickFalse)
9037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9038 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009039
glennrp2b013e42010-11-24 16:55:50 +00009040#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009041 /* This became available in libpng-1.0.9. Output must be a MNG. */
9042 if (mng_info->write_mng && ((quality % 10) == 7))
9043 {
9044 if (logging != MagickFalse)
9045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9046 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009047
glennrp5af765f2010-03-30 11:12:18 +00009048 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009049 }
glennrp0fe50b42010-11-16 03:52:51 +00009050
cristy3ed852e2009-09-05 21:47:34 +00009051 else
9052 if (logging != MagickFalse)
9053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9054 " Filter_type: 0");
9055#endif
glennrp2b013e42010-11-24 16:55:50 +00009056
cristy3ed852e2009-09-05 21:47:34 +00009057 {
9058 int
9059 base_filter;
9060
9061 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009062 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009063
glennrp26c990a2010-11-23 02:23:20 +00009064 else
glennrp8640fb52010-11-23 15:48:26 +00009065 if ((quality % 10) != 5)
9066 base_filter=(int) quality % 10;
9067
9068 else
9069 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9070 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9071 (quality < 50))
9072 base_filter=PNG_NO_FILTERS;
9073
9074 else
9075 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009076
cristy3ed852e2009-09-05 21:47:34 +00009077 if (logging != MagickFalse)
9078 {
9079 if (base_filter == PNG_ALL_FILTERS)
9080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9081 " Base filter method: ADAPTIVE");
9082 else
9083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9084 " Base filter method: NONE");
9085 }
glennrp2b013e42010-11-24 16:55:50 +00009086
cristy3ed852e2009-09-05 21:47:34 +00009087 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9088 }
9089
glennrp823b55c2011-03-14 18:46:46 +00009090 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9091 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009092 {
9093 ResetImageProfileIterator(image);
9094 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009095 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009096 profile=GetImageProfile(image,name);
9097
9098 if (profile != (StringInfo *) NULL)
9099 {
glennrp5af765f2010-03-30 11:12:18 +00009100#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009101 if ((LocaleCompare(name,"ICC") == 0) ||
9102 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009103 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009104
9105 if (ping_exclude_iCCP == MagickFalse)
9106 {
9107 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009108#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009109 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009110#else
9111 (png_const_bytep) GetStringInfoDatum(profile),
9112#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009113 (png_uint_32) GetStringInfoLength(profile));
9114 }
glennrp26f37912010-12-23 16:22:42 +00009115 }
glennrp0fe50b42010-11-16 03:52:51 +00009116
glennrpc8cbc5d2011-01-01 00:12:34 +00009117 else
cristy3ed852e2009-09-05 21:47:34 +00009118#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009119 if (ping_exclude_zCCP == MagickFalse)
9120 {
glennrpcf002022011-01-30 02:38:15 +00009121 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009122 (unsigned char *) name,(unsigned char *) name,
9123 GetStringInfoDatum(profile),
9124 (png_uint_32) GetStringInfoLength(profile));
9125 }
9126 }
glennrp0b206f52011-01-07 04:55:32 +00009127
glennrpc8cbc5d2011-01-01 00:12:34 +00009128 if (logging != MagickFalse)
9129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9130 " Setting up text chunk with %s profile",name);
9131
9132 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009133 }
cristy3ed852e2009-09-05 21:47:34 +00009134 }
9135
9136#if defined(PNG_WRITE_sRGB_SUPPORTED)
9137 if ((mng_info->have_write_global_srgb == 0) &&
9138 ((image->rendering_intent != UndefinedIntent) ||
9139 (image->colorspace == sRGBColorspace)))
9140 {
glennrp26f37912010-12-23 16:22:42 +00009141 if (ping_exclude_sRGB == MagickFalse)
9142 {
9143 /*
9144 Note image rendering intent.
9145 */
9146 if (logging != MagickFalse)
9147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9148 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009149
glennrp26f37912010-12-23 16:22:42 +00009150 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009151 Magick_RenderingIntent_to_PNG_RenderingIntent(
9152 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009153
glennrp26f37912010-12-23 16:22:42 +00009154 if (ping_exclude_gAMA == MagickFalse)
9155 png_set_gAMA(ping,ping_info,0.45455);
9156 }
cristy3ed852e2009-09-05 21:47:34 +00009157 }
glennrp26f37912010-12-23 16:22:42 +00009158
glennrp5af765f2010-03-30 11:12:18 +00009159 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009160#endif
9161 {
glennrp2cc891a2010-12-24 13:44:32 +00009162 if (ping_exclude_gAMA == MagickFalse &&
9163 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009164 (image->gamma < .45 || image->gamma > .46)))
9165 {
cristy3ed852e2009-09-05 21:47:34 +00009166 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9167 {
9168 /*
9169 Note image gamma.
9170 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9171 */
9172 if (logging != MagickFalse)
9173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9174 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009175
cristy3ed852e2009-09-05 21:47:34 +00009176 png_set_gAMA(ping,ping_info,image->gamma);
9177 }
glennrp26f37912010-12-23 16:22:42 +00009178 }
glennrp2b013e42010-11-24 16:55:50 +00009179
glennrp26f37912010-12-23 16:22:42 +00009180 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009181 {
glennrp26f37912010-12-23 16:22:42 +00009182 if ((mng_info->have_write_global_chrm == 0) &&
9183 (image->chromaticity.red_primary.x != 0.0))
9184 {
9185 /*
9186 Note image chromaticity.
9187 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9188 */
9189 PrimaryInfo
9190 bp,
9191 gp,
9192 rp,
9193 wp;
cristy3ed852e2009-09-05 21:47:34 +00009194
glennrp26f37912010-12-23 16:22:42 +00009195 wp=image->chromaticity.white_point;
9196 rp=image->chromaticity.red_primary;
9197 gp=image->chromaticity.green_primary;
9198 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009199
glennrp26f37912010-12-23 16:22:42 +00009200 if (logging != MagickFalse)
9201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9202 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009203
glennrp26f37912010-12-23 16:22:42 +00009204 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9205 bp.x,bp.y);
9206 }
9207 }
cristy3ed852e2009-09-05 21:47:34 +00009208 }
glennrpdfd70802010-11-14 01:23:35 +00009209
glennrp5af765f2010-03-30 11:12:18 +00009210 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009211
9212 if (mng_info->write_mng)
9213 png_set_sig_bytes(ping,8);
9214
9215 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9216
glennrpd6bf1612010-12-17 17:28:54 +00009217 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009218 {
9219 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009220 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009221 {
glennrp5af765f2010-03-30 11:12:18 +00009222 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009223
glennrp5af765f2010-03-30 11:12:18 +00009224 if (ping_bit_depth < 8)
9225 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009226 }
glennrp0fe50b42010-11-16 03:52:51 +00009227
cristy3ed852e2009-09-05 21:47:34 +00009228 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009229 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009230 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009231 }
9232
glennrp0e8ea192010-12-24 18:00:33 +00009233 if (ping_need_colortype_warning != MagickFalse ||
9234 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009235 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009236 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009237 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009238 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009239 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009240 {
9241 if (logging != MagickFalse)
9242 {
glennrp0e8ea192010-12-24 18:00:33 +00009243 if (ping_need_colortype_warning != MagickFalse)
9244 {
9245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9246 " Image has transparency but tRNS chunk was excluded");
9247 }
9248
cristy3ed852e2009-09-05 21:47:34 +00009249 if (mng_info->write_png_depth)
9250 {
9251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9252 " Defined PNG:bit-depth=%u, Computed depth=%u",
9253 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009254 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009255 }
glennrp0e8ea192010-12-24 18:00:33 +00009256
cristy3ed852e2009-09-05 21:47:34 +00009257 if (mng_info->write_png_colortype)
9258 {
9259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9260 " Defined PNG:color-type=%u, Computed color type=%u",
9261 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009262 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009263 }
9264 }
glennrp0e8ea192010-12-24 18:00:33 +00009265
glennrp3bd2e412010-08-10 13:34:52 +00009266 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009267 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9268 }
9269
glennrp58e01762011-01-07 15:28:54 +00009270 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009271 {
9272 /* Add an opaque matte channel */
9273 image->matte = MagickTrue;
9274 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009275
glennrpb4a13412010-05-05 12:47:19 +00009276 if (logging != MagickFalse)
9277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9278 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009279 }
9280
glennrp0e319732011-01-25 21:53:13 +00009281 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009282 {
glennrp991d11d2010-11-12 21:55:28 +00009283 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009284 {
glennrp991d11d2010-11-12 21:55:28 +00009285 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009286 if (logging != MagickFalse)
9287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9288 " Setting ping_have_tRNS=MagickTrue.");
9289 }
glennrpe9c26dc2010-05-30 01:56:35 +00009290 }
9291
cristy3ed852e2009-09-05 21:47:34 +00009292 if (logging != MagickFalse)
9293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9294 " Writing PNG header chunks");
9295
glennrp5af765f2010-03-30 11:12:18 +00009296 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9297 ping_bit_depth,ping_color_type,
9298 ping_interlace_method,ping_compression_method,
9299 ping_filter_method);
9300
glennrp39992b42010-11-14 00:03:43 +00009301 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9302 {
glennrpf09bded2011-01-08 01:15:59 +00009303 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009304
glennrp3b51f0e2010-11-27 18:14:08 +00009305 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009306 {
glennrp8640fb52010-11-23 15:48:26 +00009307 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009308 {
glennrpd6bf1612010-12-17 17:28:54 +00009309 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009311 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9312 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009313 (int) palette[i].red,
9314 (int) palette[i].green,
9315 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009316 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009317 (int) ping_trans_alpha[i]);
9318 else
9319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009320 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009321 (int) i,
9322 (int) palette[i].red,
9323 (int) palette[i].green,
9324 (int) palette[i].blue);
9325 }
glennrp39992b42010-11-14 00:03:43 +00009326 }
glennrp39992b42010-11-14 00:03:43 +00009327 }
9328
glennrp26f37912010-12-23 16:22:42 +00009329 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009330 {
glennrp26f37912010-12-23 16:22:42 +00009331 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009332 {
glennrp26f37912010-12-23 16:22:42 +00009333 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009334 if (logging)
9335 {
9336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9337 " Setting up bKGD chunk");
9338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9339 " background color = (%d,%d,%d)",
9340 (int) ping_background.red,
9341 (int) ping_background.green,
9342 (int) ping_background.blue);
9343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9344 " index = %d, gray=%d",
9345 (int) ping_background.index,
9346 (int) ping_background.gray);
9347 }
9348 }
glennrp26f37912010-12-23 16:22:42 +00009349 }
9350
9351 if (ping_exclude_pHYs == MagickFalse)
9352 {
9353 if (ping_have_pHYs != MagickFalse)
9354 {
9355 png_set_pHYs(ping,ping_info,
9356 ping_pHYs_x_resolution,
9357 ping_pHYs_y_resolution,
9358 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009359
9360 if (logging)
9361 {
9362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9363 " Setting up pHYs chunk");
9364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9365 " x_resolution=%lu",
9366 (unsigned long) ping_pHYs_x_resolution);
9367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9368 " y_resolution=%lu",
9369 (unsigned long) ping_pHYs_y_resolution);
9370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9371 " unit_type=%lu",
9372 (unsigned long) ping_pHYs_unit_type);
9373 }
glennrp26f37912010-12-23 16:22:42 +00009374 }
glennrpdfd70802010-11-14 01:23:35 +00009375 }
9376
9377#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009378 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009379 {
glennrp26f37912010-12-23 16:22:42 +00009380 if (image->page.x || image->page.y)
9381 {
9382 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9383 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009384
glennrp26f37912010-12-23 16:22:42 +00009385 if (logging != MagickFalse)
9386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9387 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9388 (int) image->page.x, (int) image->page.y);
9389 }
glennrpdfd70802010-11-14 01:23:35 +00009390 }
9391#endif
9392
glennrpda8f3a72011-02-27 23:54:12 +00009393 if (mng_info->need_blob != MagickFalse)
9394 {
9395 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9396 MagickFalse)
9397 png_error(ping,"WriteBlob Failed");
9398
9399 ping_have_blob=MagickTrue;
9400 }
9401
cristy3ed852e2009-09-05 21:47:34 +00009402 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009403
glennrp39992b42010-11-14 00:03:43 +00009404 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009405 {
glennrp3b51f0e2010-11-27 18:14:08 +00009406 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009407 {
9408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9409 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9410 }
9411
9412 if (ping_color_type == 3)
9413 (void) png_set_tRNS(ping, ping_info,
9414 ping_trans_alpha,
9415 ping_num_trans,
9416 NULL);
9417
9418 else
9419 {
9420 (void) png_set_tRNS(ping, ping_info,
9421 NULL,
9422 0,
9423 &ping_trans_color);
9424
glennrp3b51f0e2010-11-27 18:14:08 +00009425 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009426 {
9427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009428 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009429 (int) ping_trans_color.red,
9430 (int) ping_trans_color.green,
9431 (int) ping_trans_color.blue);
9432 }
9433 }
glennrp991d11d2010-11-12 21:55:28 +00009434 }
9435
cristy3ed852e2009-09-05 21:47:34 +00009436 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009437 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009438
cristy3ed852e2009-09-05 21:47:34 +00009439 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009440
cristy3ed852e2009-09-05 21:47:34 +00009441 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009442 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009443
glennrp26f37912010-12-23 16:22:42 +00009444 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009445 {
glennrp4f25bd02011-01-01 18:51:28 +00009446 if ((image->page.width != 0 && image->page.width != image->columns) ||
9447 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009448 {
9449 unsigned char
9450 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009451
glennrp26f37912010-12-23 16:22:42 +00009452 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9453 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009454 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009455 PNGLong(chunk+4,(png_uint_32) image->page.width);
9456 PNGLong(chunk+8,(png_uint_32) image->page.height);
9457 chunk[12]=0; /* unit = pixels */
9458 (void) WriteBlob(image,13,chunk);
9459 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9460 }
cristy3ed852e2009-09-05 21:47:34 +00009461 }
9462
9463#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009464 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009465#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009466 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009467#undef PNG_HAVE_IDAT
9468#endif
9469
9470 png_set_packing(ping);
9471 /*
9472 Allocate memory.
9473 */
9474 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009475 if (image_depth > 8)
9476 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009477 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009478 {
glennrpb4a13412010-05-05 12:47:19 +00009479 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009480 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009481 break;
glennrp0fe50b42010-11-16 03:52:51 +00009482
glennrpb4a13412010-05-05 12:47:19 +00009483 case PNG_COLOR_TYPE_GRAY_ALPHA:
9484 rowbytes*=2;
9485 break;
glennrp0fe50b42010-11-16 03:52:51 +00009486
glennrpb4a13412010-05-05 12:47:19 +00009487 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009488 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009489 break;
glennrp0fe50b42010-11-16 03:52:51 +00009490
glennrpb4a13412010-05-05 12:47:19 +00009491 default:
9492 break;
cristy3ed852e2009-09-05 21:47:34 +00009493 }
glennrp3b51f0e2010-11-27 18:14:08 +00009494
9495 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009496 {
9497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9498 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009499
glennrpb4a13412010-05-05 12:47:19 +00009500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009501 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009502 }
glennrpcf002022011-01-30 02:38:15 +00009503 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9504 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009505
glennrpcf002022011-01-30 02:38:15 +00009506 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009507 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009508
cristy3ed852e2009-09-05 21:47:34 +00009509 /*
9510 Initialize image scanlines.
9511 */
glennrp5af765f2010-03-30 11:12:18 +00009512 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009513 {
9514 /*
9515 PNG write failed.
9516 */
9517#ifdef PNG_DEBUG
9518 if (image_info->verbose)
9519 (void) printf("PNG write has failed.\n");
9520#endif
9521 png_destroy_write_struct(&ping,&ping_info);
9522 if (quantum_info != (QuantumInfo *) NULL)
9523 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009524 if (ping_pixels != (unsigned char *) NULL)
9525 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009526#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009527 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009528#endif
glennrpda8f3a72011-02-27 23:54:12 +00009529 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009530 (void) CloseBlob(image);
9531 image_info=DestroyImageInfo(image_info);
9532 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009533 return(MagickFalse);
9534 }
cristyed552522009-10-16 14:04:35 +00009535 quantum_info=AcquireQuantumInfo(image_info,image);
9536 if (quantum_info == (QuantumInfo *) NULL)
9537 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009538 quantum_info->format=UndefinedQuantumFormat;
9539 quantum_info->depth=image_depth;
9540 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009541
cristy3ed852e2009-09-05 21:47:34 +00009542 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009543 !mng_info->write_png32) &&
9544 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009545 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009546 image_matte == MagickFalse &&
9547 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009548 {
glennrp8bb3a022010-12-13 20:40:04 +00009549 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009550 register const PixelPacket
9551 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009552
cristy3ed852e2009-09-05 21:47:34 +00009553 quantum_info->depth=8;
9554 for (pass=0; pass < num_passes; pass++)
9555 {
9556 /*
9557 Convert PseudoClass image to a PNG monochrome image.
9558 */
cristybb503372010-05-27 20:51:26 +00009559 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009560 {
glennrpd71e86a2011-02-24 01:28:37 +00009561 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9563 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009564
cristy3ed852e2009-09-05 21:47:34 +00009565 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009566
cristy3ed852e2009-09-05 21:47:34 +00009567 if (p == (const PixelPacket *) NULL)
9568 break;
glennrp0fe50b42010-11-16 03:52:51 +00009569
cristy3ed852e2009-09-05 21:47:34 +00009570 if (mng_info->IsPalette)
9571 {
9572 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009573 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009574 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9575 mng_info->write_png_depth &&
9576 mng_info->write_png_depth != old_bit_depth)
9577 {
9578 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009579 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009580 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009581 >> (8-old_bit_depth));
9582 }
9583 }
glennrp0fe50b42010-11-16 03:52:51 +00009584
cristy3ed852e2009-09-05 21:47:34 +00009585 else
9586 {
9587 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009588 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009589 }
glennrp0fe50b42010-11-16 03:52:51 +00009590
cristy3ed852e2009-09-05 21:47:34 +00009591 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009592 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009593 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009594 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009595
glennrp3b51f0e2010-11-27 18:14:08 +00009596 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9598 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009599
glennrpcf002022011-01-30 02:38:15 +00009600 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009601 }
9602 if (image->previous == (Image *) NULL)
9603 {
9604 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9605 if (status == MagickFalse)
9606 break;
9607 }
9608 }
9609 }
glennrp0fe50b42010-11-16 03:52:51 +00009610
glennrp8bb3a022010-12-13 20:40:04 +00009611 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009612 {
glennrp0fe50b42010-11-16 03:52:51 +00009613 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009614 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009615 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009616 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009617 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009618 {
glennrp8bb3a022010-12-13 20:40:04 +00009619 register const PixelPacket
9620 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009621
glennrp8bb3a022010-12-13 20:40:04 +00009622 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009623 {
glennrp8bb3a022010-12-13 20:40:04 +00009624
cristybb503372010-05-27 20:51:26 +00009625 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009626 {
9627 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009628
cristy3ed852e2009-09-05 21:47:34 +00009629 if (p == (const PixelPacket *) NULL)
9630 break;
glennrp2cc891a2010-12-24 13:44:32 +00009631
glennrp5af765f2010-03-30 11:12:18 +00009632 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009633 {
glennrp8bb3a022010-12-13 20:40:04 +00009634 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009635 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009636 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009637
glennrp8bb3a022010-12-13 20:40:04 +00009638 else
9639 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009640 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009641
glennrp3b51f0e2010-11-27 18:14:08 +00009642 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009644 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009645 }
glennrp2cc891a2010-12-24 13:44:32 +00009646
glennrp8bb3a022010-12-13 20:40:04 +00009647 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9648 {
9649 if (logging != MagickFalse && y == 0)
9650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9651 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009652
glennrp8bb3a022010-12-13 20:40:04 +00009653 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009654 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009655 }
glennrp2cc891a2010-12-24 13:44:32 +00009656
glennrp3b51f0e2010-11-27 18:14:08 +00009657 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009659 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009660
glennrpcf002022011-01-30 02:38:15 +00009661 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009662 }
glennrp2cc891a2010-12-24 13:44:32 +00009663
glennrp8bb3a022010-12-13 20:40:04 +00009664 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009665 {
glennrp8bb3a022010-12-13 20:40:04 +00009666 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9667 if (status == MagickFalse)
9668 break;
cristy3ed852e2009-09-05 21:47:34 +00009669 }
cristy3ed852e2009-09-05 21:47:34 +00009670 }
9671 }
glennrp8bb3a022010-12-13 20:40:04 +00009672
9673 else
9674 {
9675 register const PixelPacket
9676 *p;
9677
9678 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009679 {
glennrp8bb3a022010-12-13 20:40:04 +00009680 if ((image_depth > 8) || (mng_info->write_png24 ||
9681 mng_info->write_png32 ||
9682 (!mng_info->write_png8 && !mng_info->IsPalette)))
9683 {
9684 for (y=0; y < (ssize_t) image->rows; y++)
9685 {
9686 p=GetVirtualPixels(image,0,y,image->columns,1,
9687 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009688
glennrp8bb3a022010-12-13 20:40:04 +00009689 if (p == (const PixelPacket *) NULL)
9690 break;
glennrp2cc891a2010-12-24 13:44:32 +00009691
glennrp8bb3a022010-12-13 20:40:04 +00009692 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9693 {
9694 if (image->storage_class == DirectClass)
9695 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009696 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009697
glennrp8bb3a022010-12-13 20:40:04 +00009698 else
9699 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009700 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009701 }
glennrp2cc891a2010-12-24 13:44:32 +00009702
glennrp8bb3a022010-12-13 20:40:04 +00009703 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9704 {
9705 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009706 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009707 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009708
glennrp8bb3a022010-12-13 20:40:04 +00009709 if (logging != MagickFalse && y == 0)
9710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9711 " Writing GRAY_ALPHA PNG pixels (3)");
9712 }
glennrp2cc891a2010-12-24 13:44:32 +00009713
glennrp8bb3a022010-12-13 20:40:04 +00009714 else if (image_matte != MagickFalse)
9715 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009716 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009717
glennrp8bb3a022010-12-13 20:40:04 +00009718 else
9719 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009720 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009721
glennrp8bb3a022010-12-13 20:40:04 +00009722 if (logging != MagickFalse && y == 0)
9723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9724 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009725
glennrpcf002022011-01-30 02:38:15 +00009726 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009727 }
9728 }
glennrp2cc891a2010-12-24 13:44:32 +00009729
glennrp8bb3a022010-12-13 20:40:04 +00009730 else
9731 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9732 mng_info->write_png32 ||
9733 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9734 {
9735 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9736 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9737 {
9738 if (logging != MagickFalse)
9739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9740 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009741
glennrp8bb3a022010-12-13 20:40:04 +00009742 quantum_info->depth=8;
9743 image_depth=8;
9744 }
glennrp2cc891a2010-12-24 13:44:32 +00009745
glennrp8bb3a022010-12-13 20:40:04 +00009746 for (y=0; y < (ssize_t) image->rows; y++)
9747 {
9748 if (logging != MagickFalse && y == 0)
9749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9750 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009751
glennrp770d1932011-03-06 22:11:17 +00009752 p=GetVirtualPixels(image,0,y,image->columns,1,
9753 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009754
glennrp8bb3a022010-12-13 20:40:04 +00009755 if (p == (const PixelPacket *) NULL)
9756 break;
glennrp2cc891a2010-12-24 13:44:32 +00009757
glennrp8bb3a022010-12-13 20:40:04 +00009758 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009759 {
glennrp4bf89732011-03-21 13:48:28 +00009760 quantum_info->depth=image->depth;
9761
glennrp44757ab2011-03-17 12:57:03 +00009762 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009763 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009764 }
glennrp2cc891a2010-12-24 13:44:32 +00009765
glennrp8bb3a022010-12-13 20:40:04 +00009766 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9767 {
9768 if (logging != MagickFalse && y == 0)
9769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9770 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009771
glennrp8bb3a022010-12-13 20:40:04 +00009772 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009773 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009774 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009775 }
glennrp2cc891a2010-12-24 13:44:32 +00009776
glennrp8bb3a022010-12-13 20:40:04 +00009777 else
glennrp8bb3a022010-12-13 20:40:04 +00009778 {
glennrp179d0752011-03-17 13:02:10 +00009779 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009780 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9781
9782 if (logging != MagickFalse && y <= 2)
9783 {
9784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009785 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009786
9787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9788 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9789 (int)ping_pixels[0],(int)ping_pixels[1]);
9790 }
glennrp8bb3a022010-12-13 20:40:04 +00009791 }
glennrpcf002022011-01-30 02:38:15 +00009792 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009793 }
9794 }
glennrp2cc891a2010-12-24 13:44:32 +00009795
glennrp8bb3a022010-12-13 20:40:04 +00009796 if (image->previous == (Image *) NULL)
9797 {
9798 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9799 if (status == MagickFalse)
9800 break;
9801 }
cristy3ed852e2009-09-05 21:47:34 +00009802 }
glennrp8bb3a022010-12-13 20:40:04 +00009803 }
9804 }
9805
cristyb32b90a2009-09-07 21:45:48 +00009806 if (quantum_info != (QuantumInfo *) NULL)
9807 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009808
9809 if (logging != MagickFalse)
9810 {
9811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009812 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009813
cristy3ed852e2009-09-05 21:47:34 +00009814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009815 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009816
cristy3ed852e2009-09-05 21:47:34 +00009817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009818 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009819
cristy3ed852e2009-09-05 21:47:34 +00009820 if (mng_info->write_png_depth)
9821 {
9822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9823 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9824 }
glennrp0fe50b42010-11-16 03:52:51 +00009825
cristy3ed852e2009-09-05 21:47:34 +00009826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009827 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009828
cristy3ed852e2009-09-05 21:47:34 +00009829 if (mng_info->write_png_colortype)
9830 {
9831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9832 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9833 }
glennrp0fe50b42010-11-16 03:52:51 +00009834
cristy3ed852e2009-09-05 21:47:34 +00009835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009836 " PNG color-type written: %d",ping_color_type);
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 Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009840 }
9841 /*
glennrpa0ed0092011-04-18 16:36:29 +00009842 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009843 */
glennrp823b55c2011-03-14 18:46:46 +00009844 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009845 {
glennrp26f37912010-12-23 16:22:42 +00009846 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009847 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009848 while (property != (const char *) NULL)
9849 {
9850 png_textp
9851 text;
glennrp2cc891a2010-12-24 13:44:32 +00009852
glennrp26f37912010-12-23 16:22:42 +00009853 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009854
9855 /* Don't write any "png:" properties; those are just for "identify" */
9856 if (LocaleNCompare(property,"png:",4) != 0 &&
9857
9858 /* Suppress density and units if we wrote a pHYs chunk */
9859 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009860 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009861 LocaleCompare(property,"units") != 0) &&
9862
9863 /* Suppress the IM-generated Date:create and Date:modify */
9864 (ping_exclude_date == MagickFalse ||
9865 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009866 {
glennrpc70af4a2011-03-07 00:08:23 +00009867 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009868 {
glennrpc70af4a2011-03-07 00:08:23 +00009869 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9870 text[0].key=(char *) property;
9871 text[0].text=(char *) value;
9872 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009873
glennrpc70af4a2011-03-07 00:08:23 +00009874 if (ping_exclude_tEXt != MagickFalse)
9875 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9876
9877 else if (ping_exclude_zTXt != MagickFalse)
9878 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9879
9880 else
glennrp26f37912010-12-23 16:22:42 +00009881 {
glennrpc70af4a2011-03-07 00:08:23 +00009882 text[0].compression=image_info->compression == NoCompression ||
9883 (image_info->compression == UndefinedCompression &&
9884 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9885 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009886 }
glennrp2cc891a2010-12-24 13:44:32 +00009887
glennrpc70af4a2011-03-07 00:08:23 +00009888 if (logging != MagickFalse)
9889 {
9890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9891 " Setting up text chunk");
9892
9893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9894 " keyword: %s",text[0].key);
9895 }
9896
9897 png_set_text(ping,ping_info,text,1);
9898 png_free(ping,text);
9899 }
glennrp26f37912010-12-23 16:22:42 +00009900 }
9901 property=GetNextImageProperty(image);
9902 }
cristy3ed852e2009-09-05 21:47:34 +00009903 }
9904
9905 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009906 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009907
9908 if (logging != MagickFalse)
9909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9910 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009911
cristy3ed852e2009-09-05 21:47:34 +00009912 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009913
cristy3ed852e2009-09-05 21:47:34 +00009914 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9915 {
9916 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009917 (ping_width != mng_info->page.width) ||
9918 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009919 {
9920 unsigned char
9921 chunk[32];
9922
9923 /*
9924 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9925 */
9926 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9927 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009928 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009929 chunk[4]=4;
9930 chunk[5]=0; /* frame name separator (no name) */
9931 chunk[6]=1; /* flag for changing delay, for next frame only */
9932 chunk[7]=0; /* flag for changing frame timeout */
9933 chunk[8]=1; /* flag for changing frame clipping for next frame */
9934 chunk[9]=0; /* flag for changing frame sync_id */
9935 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9936 chunk[14]=0; /* clipping boundaries delta type */
9937 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9938 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009939 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009940 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9941 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009942 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009943 (void) WriteBlob(image,31,chunk);
9944 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9945 mng_info->old_framing_mode=4;
9946 mng_info->framing_mode=1;
9947 }
glennrp0fe50b42010-11-16 03:52:51 +00009948
cristy3ed852e2009-09-05 21:47:34 +00009949 else
9950 mng_info->framing_mode=3;
9951 }
9952 if (mng_info->write_mng && !mng_info->need_fram &&
9953 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009954 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009955 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009956 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009957
cristy3ed852e2009-09-05 21:47:34 +00009958 /*
9959 Free PNG resources.
9960 */
glennrp5af765f2010-03-30 11:12:18 +00009961
cristy3ed852e2009-09-05 21:47:34 +00009962 png_destroy_write_struct(&ping,&ping_info);
9963
glennrpcf002022011-01-30 02:38:15 +00009964 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009965
9966#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009967 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009968#endif
9969
glennrpda8f3a72011-02-27 23:54:12 +00009970 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009971 (void) CloseBlob(image);
9972
9973 image_info=DestroyImageInfo(image_info);
9974 image=DestroyImage(image);
9975
9976 /* Store bit depth actually written */
9977 s[0]=(char) ping_bit_depth;
9978 s[1]='\0';
9979
9980 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9981
cristy3ed852e2009-09-05 21:47:34 +00009982 if (logging != MagickFalse)
9983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9984 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009985
cristy3ed852e2009-09-05 21:47:34 +00009986 return(MagickTrue);
9987/* End write one PNG image */
9988}
9989
9990/*
9991%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9992% %
9993% %
9994% %
9995% W r i t e P N G I m a g e %
9996% %
9997% %
9998% %
9999%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10000%
10001% WritePNGImage() writes a Portable Network Graphics (PNG) or
10002% Multiple-image Network Graphics (MNG) image file.
10003%
10004% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10005%
10006% The format of the WritePNGImage method is:
10007%
10008% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10009%
10010% A description of each parameter follows:
10011%
10012% o image_info: the image info.
10013%
10014% o image: The image.
10015%
10016% Returns MagickTrue on success, MagickFalse on failure.
10017%
10018% Communicating with the PNG encoder:
10019%
10020% While the datastream written is always in PNG format and normally would
10021% be given the "png" file extension, this method also writes the following
10022% pseudo-formats which are subsets of PNG:
10023%
glennrp5a39f372011-02-25 04:52:16 +000010024% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10025% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010026% is present, the tRNS chunk must only have values 0 and 255
10027% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010028% transparent). If other values are present they will be
10029% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010030% colors are present, they will be quantized to the 4-4-4-1,
10031% 3-3-3-1, or 3-3-2-1 palette.
10032%
10033% If you want better quantization or dithering of the colors
10034% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010035% PNG encoder. The pixels contain 8-bit indices even if
10036% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010037% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010038% PNG grayscale type might be slightly more efficient. Please
10039% note that writing to the PNG8 format may result in loss
10040% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010041%
10042% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10043% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010044% one of the colors as transparent. The only loss incurred
10045% is reduction of sample depth to 8. If the image has more
10046% than one transparent color, has semitransparent pixels, or
10047% has an opaque pixel with the same RGB components as the
10048% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010049%
10050% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10051% transparency is permitted, i.e., the alpha sample for
10052% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010053% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010054% The only loss in data is the reduction of the sample depth
10055% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010056%
10057% o -define: For more precise control of the PNG output, you can use the
10058% Image options "png:bit-depth" and "png:color-type". These
10059% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010060% from the application programming interfaces. The options
10061% are case-independent and are converted to lowercase before
10062% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010063%
10064% png:color-type can be 0, 2, 3, 4, or 6.
10065%
10066% When png:color-type is 0 (Grayscale), png:bit-depth can
10067% be 1, 2, 4, 8, or 16.
10068%
10069% When png:color-type is 2 (RGB), png:bit-depth can
10070% be 8 or 16.
10071%
10072% When png:color-type is 3 (Indexed), png:bit-depth can
10073% be 1, 2, 4, or 8. This refers to the number of bits
10074% used to store the index. The color samples always have
10075% bit-depth 8 in indexed PNG files.
10076%
10077% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10078% png:bit-depth can be 8 or 16.
10079%
glennrp5a39f372011-02-25 04:52:16 +000010080% If the image cannot be written without loss with the requested bit-depth
10081% and color-type, a PNG file will not be written, and the encoder will
10082% return MagickFalse.
10083%
cristy3ed852e2009-09-05 21:47:34 +000010084% Since image encoders should not be responsible for the "heavy lifting",
10085% the user should make sure that ImageMagick has already reduced the
10086% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010087% transparency prior to attempting to write the image with depth, color,
10088% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010089%
glennrp97cefe22011-04-22 16:17:00 +000010090% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010091%
cristy3ed852e2009-09-05 21:47:34 +000010092% Note that another definition, "png:bit-depth-written" exists, but it
10093% is not intended for external use. It is only used internally by the
10094% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10095%
10096% It is possible to request that the PNG encoder write previously-formatted
10097% ancillary chunks in the output PNG file, using the "-profile" commandline
10098% option as shown below or by setting the profile via a programming
10099% interface:
10100%
10101% -profile PNG-chunk-x:<file>
10102%
10103% where x is a location flag and <file> is a file containing the chunk
10104% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010105% This encoder will compute the chunk length and CRC, so those must not
10106% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010107%
10108% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10109% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10110% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010111% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010112%
glennrpbb8a7332010-11-13 15:17:35 +000010113% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010114%
glennrp3241bd02010-12-12 04:36:28 +000010115% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010116%
glennrpd6afd542010-11-19 01:53:05 +000010117% o 32-bit depth is reduced to 16.
10118% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10119% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010120% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010121% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010122% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010123% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10124% this can be done without loss and a larger bit depth N was not
10125% requested via the "-define PNG:bit-depth=N" option.
10126% o If matte channel is present but only one transparent color is
10127% present, RGB+tRNS is written instead of RGBA
10128% o Opaque matte channel is removed (or added, if color-type 4 or 6
10129% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010130%
cristy3ed852e2009-09-05 21:47:34 +000010131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10132*/
10133static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10134 Image *image)
10135{
10136 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010137 excluding,
10138 logging,
10139 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010140 status;
10141
10142 MngInfo
10143 *mng_info;
10144
10145 const char
10146 *value;
10147
10148 int
glennrp21f0e622011-01-07 16:20:57 +000010149 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010150 source;
10151
cristy3ed852e2009-09-05 21:47:34 +000010152 /*
10153 Open image file.
10154 */
10155 assert(image_info != (const ImageInfo *) NULL);
10156 assert(image_info->signature == MagickSignature);
10157 assert(image != (Image *) NULL);
10158 assert(image->signature == MagickSignature);
10159 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010160 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010161 /*
10162 Allocate a MngInfo structure.
10163 */
10164 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010165 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010166
cristy3ed852e2009-09-05 21:47:34 +000010167 if (mng_info == (MngInfo *) NULL)
10168 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010169
cristy3ed852e2009-09-05 21:47:34 +000010170 /*
10171 Initialize members of the MngInfo structure.
10172 */
10173 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10174 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010175 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010176 have_mng_structure=MagickTrue;
10177
10178 /* See if user has requested a specific PNG subformat */
10179
10180 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10181 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10182 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10183
10184 if (mng_info->write_png8)
10185 {
glennrp9c1eb072010-06-06 22:19:15 +000010186 mng_info->write_png_colortype = /* 3 */ 4;
10187 mng_info->write_png_depth = 8;
10188 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010189 }
10190
10191 if (mng_info->write_png24)
10192 {
glennrp9c1eb072010-06-06 22:19:15 +000010193 mng_info->write_png_colortype = /* 2 */ 3;
10194 mng_info->write_png_depth = 8;
10195 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010196
glennrp9c1eb072010-06-06 22:19:15 +000010197 if (image->matte == MagickTrue)
10198 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010199
glennrp9c1eb072010-06-06 22:19:15 +000010200 else
10201 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010202
glennrp9c1eb072010-06-06 22:19:15 +000010203 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010204 }
10205
10206 if (mng_info->write_png32)
10207 {
glennrp9c1eb072010-06-06 22:19:15 +000010208 mng_info->write_png_colortype = /* 6 */ 7;
10209 mng_info->write_png_depth = 8;
10210 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010211
glennrp9c1eb072010-06-06 22:19:15 +000010212 if (image->matte == MagickTrue)
10213 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010214
glennrp9c1eb072010-06-06 22:19:15 +000010215 else
10216 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010217
glennrp9c1eb072010-06-06 22:19:15 +000010218 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010219 }
10220
10221 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010222
cristy3ed852e2009-09-05 21:47:34 +000010223 if (value != (char *) NULL)
10224 {
10225 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010226 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010227
cristy3ed852e2009-09-05 21:47:34 +000010228 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010229 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010230
cristy3ed852e2009-09-05 21:47:34 +000010231 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010232 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010233
cristy3ed852e2009-09-05 21:47:34 +000010234 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010235 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010236
cristy3ed852e2009-09-05 21:47:34 +000010237 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010238 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010239
glennrpbb8a7332010-11-13 15:17:35 +000010240 else
10241 (void) ThrowMagickException(&image->exception,
10242 GetMagickModule(),CoderWarning,
10243 "ignoring invalid defined png:bit-depth",
10244 "=%s",value);
10245
cristy3ed852e2009-09-05 21:47:34 +000010246 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010248 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010249 }
glennrp0fe50b42010-11-16 03:52:51 +000010250
cristy3ed852e2009-09-05 21:47:34 +000010251 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010252
cristy3ed852e2009-09-05 21:47:34 +000010253 if (value != (char *) NULL)
10254 {
10255 /* We must store colortype+1 because 0 is a valid colortype */
10256 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010257 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010258
cristy3ed852e2009-09-05 21:47:34 +000010259 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010260 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010261
cristy3ed852e2009-09-05 21:47:34 +000010262 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010263 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010264
cristy3ed852e2009-09-05 21:47:34 +000010265 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010266 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010267
cristy3ed852e2009-09-05 21:47:34 +000010268 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010269 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010270
glennrpbb8a7332010-11-13 15:17:35 +000010271 else
10272 (void) ThrowMagickException(&image->exception,
10273 GetMagickModule(),CoderWarning,
10274 "ignoring invalid defined png:color-type",
10275 "=%s",value);
10276
cristy3ed852e2009-09-05 21:47:34 +000010277 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010279 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010280 }
10281
glennrp0e8ea192010-12-24 18:00:33 +000010282 /* Check for chunks to be excluded:
10283 *
glennrp0dff56c2011-01-29 19:10:02 +000010284 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010285 * listed in the "unused_chunks" array, above.
10286 *
10287 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10288 * define (in the image properties or in the image artifacts)
10289 * or via a mng_info member. For convenience, in addition
10290 * to or instead of a comma-separated list of chunks, the
10291 * "exclude-chunk" string can be simply "all" or "none".
10292 *
10293 * The exclude-chunk define takes priority over the mng_info.
10294 *
10295 * A "PNG:include-chunk" define takes priority over both the
10296 * mng_info and the "PNG:exclude-chunk" define. Like the
10297 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010298 * well as a comma-separated list. Chunks that are unknown to
10299 * ImageMagick are always excluded, regardless of their "copy-safe"
10300 * status according to the PNG specification, and even if they
10301 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010302 *
10303 * Finally, all chunks listed in the "unused_chunks" array are
10304 * automatically excluded, regardless of the other instructions
10305 * or lack thereof.
10306 *
10307 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10308 * will not be written and the gAMA chunk will only be written if it
10309 * is not between .45 and .46, or approximately (1.0/2.2).
10310 *
10311 * If you exclude tRNS and the image has transparency, the colortype
10312 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10313 *
10314 * The -strip option causes StripImage() to set the png:include-chunk
10315 * artifact to "none,gama".
10316 */
10317
glennrp26f37912010-12-23 16:22:42 +000010318 mng_info->ping_exclude_bKGD=MagickFalse;
10319 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010320 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010321 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10322 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010323 mng_info->ping_exclude_iCCP=MagickFalse;
10324 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10325 mng_info->ping_exclude_oFFs=MagickFalse;
10326 mng_info->ping_exclude_pHYs=MagickFalse;
10327 mng_info->ping_exclude_sRGB=MagickFalse;
10328 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010329 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010330 mng_info->ping_exclude_vpAg=MagickFalse;
10331 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10332 mng_info->ping_exclude_zTXt=MagickFalse;
10333
glennrp8d3d6e52011-04-19 04:39:51 +000010334 mng_info->ping_preserve_colormap=MagickFalse;
10335
10336 value=GetImageArtifact(image,"png:preserve-colormap");
10337 if (value == NULL)
10338 value=GetImageOption(image_info,"png:preserve-colormap");
10339 if (value != NULL)
10340 mng_info->ping_preserve_colormap=MagickTrue;
10341
glennrp03812ae2010-12-24 01:31:34 +000010342 excluding=MagickFalse;
10343
glennrp5c7cf4e2010-12-24 00:30:00 +000010344 for (source=0; source<1; source++)
10345 {
10346 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010347 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010348 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010349
10350 if (value == NULL)
10351 value=GetImageArtifact(image,"png:exclude-chunks");
10352 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010353 else
glennrpacba0042010-12-24 14:27:26 +000010354 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010355 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010356
glennrpacba0042010-12-24 14:27:26 +000010357 if (value == NULL)
10358 value=GetImageOption(image_info,"png:exclude-chunks");
10359 }
10360
glennrp03812ae2010-12-24 01:31:34 +000010361 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010362 {
glennrp03812ae2010-12-24 01:31:34 +000010363
10364 size_t
10365 last;
10366
10367 excluding=MagickTrue;
10368
10369 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010370 {
10371 if (source == 0)
10372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10373 " png:exclude-chunk=%s found in image artifacts.\n", value);
10374 else
10375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10376 " png:exclude-chunk=%s found in image properties.\n", value);
10377 }
glennrp03812ae2010-12-24 01:31:34 +000010378
10379 last=strlen(value);
10380
10381 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010382 {
glennrp03812ae2010-12-24 01:31:34 +000010383
10384 if (LocaleNCompare(value+i,"all",3) == 0)
10385 {
10386 mng_info->ping_exclude_bKGD=MagickTrue;
10387 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010388 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010389 mng_info->ping_exclude_EXIF=MagickTrue;
10390 mng_info->ping_exclude_gAMA=MagickTrue;
10391 mng_info->ping_exclude_iCCP=MagickTrue;
10392 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10393 mng_info->ping_exclude_oFFs=MagickTrue;
10394 mng_info->ping_exclude_pHYs=MagickTrue;
10395 mng_info->ping_exclude_sRGB=MagickTrue;
10396 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010397 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010398 mng_info->ping_exclude_vpAg=MagickTrue;
10399 mng_info->ping_exclude_zCCP=MagickTrue;
10400 mng_info->ping_exclude_zTXt=MagickTrue;
10401 i--;
10402 }
glennrp2cc891a2010-12-24 13:44:32 +000010403
glennrp03812ae2010-12-24 01:31:34 +000010404 if (LocaleNCompare(value+i,"none",4) == 0)
10405 {
10406 mng_info->ping_exclude_bKGD=MagickFalse;
10407 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010408 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010409 mng_info->ping_exclude_EXIF=MagickFalse;
10410 mng_info->ping_exclude_gAMA=MagickFalse;
10411 mng_info->ping_exclude_iCCP=MagickFalse;
10412 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10413 mng_info->ping_exclude_oFFs=MagickFalse;
10414 mng_info->ping_exclude_pHYs=MagickFalse;
10415 mng_info->ping_exclude_sRGB=MagickFalse;
10416 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010417 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010418 mng_info->ping_exclude_vpAg=MagickFalse;
10419 mng_info->ping_exclude_zCCP=MagickFalse;
10420 mng_info->ping_exclude_zTXt=MagickFalse;
10421 }
glennrp2cc891a2010-12-24 13:44:32 +000010422
glennrp03812ae2010-12-24 01:31:34 +000010423 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10424 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010425
glennrp03812ae2010-12-24 01:31:34 +000010426 if (LocaleNCompare(value+i,"chrm",4) == 0)
10427 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010428
glennrpa0ed0092011-04-18 16:36:29 +000010429 if (LocaleNCompare(value+i,"date",4) == 0)
10430 mng_info->ping_exclude_date=MagickTrue;
10431
glennrp03812ae2010-12-24 01:31:34 +000010432 if (LocaleNCompare(value+i,"exif",4) == 0)
10433 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010434
glennrp03812ae2010-12-24 01:31:34 +000010435 if (LocaleNCompare(value+i,"gama",4) == 0)
10436 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010437
glennrp03812ae2010-12-24 01:31:34 +000010438 if (LocaleNCompare(value+i,"iccp",4) == 0)
10439 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010440
glennrp03812ae2010-12-24 01:31:34 +000010441 /*
10442 if (LocaleNCompare(value+i,"itxt",4) == 0)
10443 mng_info->ping_exclude_iTXt=MagickTrue;
10444 */
glennrp2cc891a2010-12-24 13:44:32 +000010445
glennrp03812ae2010-12-24 01:31:34 +000010446 if (LocaleNCompare(value+i,"gama",4) == 0)
10447 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010448
glennrp03812ae2010-12-24 01:31:34 +000010449 if (LocaleNCompare(value+i,"offs",4) == 0)
10450 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010451
glennrp03812ae2010-12-24 01:31:34 +000010452 if (LocaleNCompare(value+i,"phys",4) == 0)
10453 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010454
glennrpa1e3b7b2010-12-24 16:37:33 +000010455 if (LocaleNCompare(value+i,"srgb",4) == 0)
10456 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010457
glennrp03812ae2010-12-24 01:31:34 +000010458 if (LocaleNCompare(value+i,"text",4) == 0)
10459 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010460
glennrpa1e3b7b2010-12-24 16:37:33 +000010461 if (LocaleNCompare(value+i,"trns",4) == 0)
10462 mng_info->ping_exclude_tRNS=MagickTrue;
10463
glennrp03812ae2010-12-24 01:31:34 +000010464 if (LocaleNCompare(value+i,"vpag",4) == 0)
10465 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010466
glennrp03812ae2010-12-24 01:31:34 +000010467 if (LocaleNCompare(value+i,"zccp",4) == 0)
10468 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010469
glennrp03812ae2010-12-24 01:31:34 +000010470 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10471 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010472
glennrp03812ae2010-12-24 01:31:34 +000010473 }
glennrpce91ed52010-12-23 22:37:49 +000010474 }
glennrp26f37912010-12-23 16:22:42 +000010475 }
10476
glennrp5c7cf4e2010-12-24 00:30:00 +000010477 for (source=0; source<1; source++)
10478 {
10479 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010480 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010481 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010482
10483 if (value == NULL)
10484 value=GetImageArtifact(image,"png:include-chunks");
10485 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010486 else
glennrpacba0042010-12-24 14:27:26 +000010487 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010488 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010489
glennrpacba0042010-12-24 14:27:26 +000010490 if (value == NULL)
10491 value=GetImageOption(image_info,"png:include-chunks");
10492 }
10493
glennrp03812ae2010-12-24 01:31:34 +000010494 if (value != NULL)
10495 {
10496 size_t
10497 last;
glennrp26f37912010-12-23 16:22:42 +000010498
glennrp03812ae2010-12-24 01:31:34 +000010499 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010500
glennrp03812ae2010-12-24 01:31:34 +000010501 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010502 {
10503 if (source == 0)
10504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10505 " png:include-chunk=%s found in image artifacts.\n", value);
10506 else
10507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10508 " png:include-chunk=%s found in image properties.\n", value);
10509 }
glennrp03812ae2010-12-24 01:31:34 +000010510
10511 last=strlen(value);
10512
10513 for (i=0; i<(int) last; i+=5)
10514 {
10515 if (LocaleNCompare(value+i,"all",3) == 0)
10516 {
10517 mng_info->ping_exclude_bKGD=MagickFalse;
10518 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010519 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010520 mng_info->ping_exclude_EXIF=MagickFalse;
10521 mng_info->ping_exclude_gAMA=MagickFalse;
10522 mng_info->ping_exclude_iCCP=MagickFalse;
10523 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10524 mng_info->ping_exclude_oFFs=MagickFalse;
10525 mng_info->ping_exclude_pHYs=MagickFalse;
10526 mng_info->ping_exclude_sRGB=MagickFalse;
10527 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010528 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010529 mng_info->ping_exclude_vpAg=MagickFalse;
10530 mng_info->ping_exclude_zCCP=MagickFalse;
10531 mng_info->ping_exclude_zTXt=MagickFalse;
10532 i--;
10533 }
glennrp2cc891a2010-12-24 13:44:32 +000010534
glennrp03812ae2010-12-24 01:31:34 +000010535 if (LocaleNCompare(value+i,"none",4) == 0)
10536 {
10537 mng_info->ping_exclude_bKGD=MagickTrue;
10538 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010539 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010540 mng_info->ping_exclude_EXIF=MagickTrue;
10541 mng_info->ping_exclude_gAMA=MagickTrue;
10542 mng_info->ping_exclude_iCCP=MagickTrue;
10543 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10544 mng_info->ping_exclude_oFFs=MagickTrue;
10545 mng_info->ping_exclude_pHYs=MagickTrue;
10546 mng_info->ping_exclude_sRGB=MagickTrue;
10547 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010548 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010549 mng_info->ping_exclude_vpAg=MagickTrue;
10550 mng_info->ping_exclude_zCCP=MagickTrue;
10551 mng_info->ping_exclude_zTXt=MagickTrue;
10552 }
glennrp2cc891a2010-12-24 13:44:32 +000010553
glennrp03812ae2010-12-24 01:31:34 +000010554 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10555 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010556
glennrp03812ae2010-12-24 01:31:34 +000010557 if (LocaleNCompare(value+i,"chrm",4) == 0)
10558 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010559
glennrpa0ed0092011-04-18 16:36:29 +000010560 if (LocaleNCompare(value+i,"date",4) == 0)
10561 mng_info->ping_exclude_date=MagickFalse;
10562
glennrp03812ae2010-12-24 01:31:34 +000010563 if (LocaleNCompare(value+i,"exif",4) == 0)
10564 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010565
glennrp03812ae2010-12-24 01:31:34 +000010566 if (LocaleNCompare(value+i,"gama",4) == 0)
10567 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010568
glennrp03812ae2010-12-24 01:31:34 +000010569 if (LocaleNCompare(value+i,"iccp",4) == 0)
10570 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010571
glennrp03812ae2010-12-24 01:31:34 +000010572 /*
10573 if (LocaleNCompare(value+i,"itxt",4) == 0)
10574 mng_info->ping_exclude_iTXt=MagickFalse;
10575 */
glennrp2cc891a2010-12-24 13:44:32 +000010576
glennrp03812ae2010-12-24 01:31:34 +000010577 if (LocaleNCompare(value+i,"gama",4) == 0)
10578 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010579
glennrp03812ae2010-12-24 01:31:34 +000010580 if (LocaleNCompare(value+i,"offs",4) == 0)
10581 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010582
glennrp03812ae2010-12-24 01:31:34 +000010583 if (LocaleNCompare(value+i,"phys",4) == 0)
10584 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010585
glennrpa1e3b7b2010-12-24 16:37:33 +000010586 if (LocaleNCompare(value+i,"srgb",4) == 0)
10587 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010588
glennrp03812ae2010-12-24 01:31:34 +000010589 if (LocaleNCompare(value+i,"text",4) == 0)
10590 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010591
glennrpa1e3b7b2010-12-24 16:37:33 +000010592 if (LocaleNCompare(value+i,"trns",4) == 0)
10593 mng_info->ping_exclude_tRNS=MagickFalse;
10594
glennrp03812ae2010-12-24 01:31:34 +000010595 if (LocaleNCompare(value+i,"vpag",4) == 0)
10596 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010597
glennrp03812ae2010-12-24 01:31:34 +000010598 if (LocaleNCompare(value+i,"zccp",4) == 0)
10599 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010600
glennrp03812ae2010-12-24 01:31:34 +000010601 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10602 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010603
glennrp03812ae2010-12-24 01:31:34 +000010604 }
glennrpce91ed52010-12-23 22:37:49 +000010605 }
glennrp26f37912010-12-23 16:22:42 +000010606 }
10607
glennrp03812ae2010-12-24 01:31:34 +000010608 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010609 {
10610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10611 " Chunks to be excluded from the output PNG:");
10612 if (mng_info->ping_exclude_bKGD != MagickFalse)
10613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10614 " bKGD");
10615 if (mng_info->ping_exclude_cHRM != MagickFalse)
10616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10617 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010618 if (mng_info->ping_exclude_date != MagickFalse)
10619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10620 " date");
glennrp26f37912010-12-23 16:22:42 +000010621 if (mng_info->ping_exclude_EXIF != MagickFalse)
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10623 " EXIF");
10624 if (mng_info->ping_exclude_gAMA != MagickFalse)
10625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10626 " gAMA");
10627 if (mng_info->ping_exclude_iCCP != MagickFalse)
10628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10629 " iCCP");
10630/*
10631 if (mng_info->ping_exclude_iTXt != MagickFalse)
10632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633 " iTXt");
10634*/
10635 if (mng_info->ping_exclude_oFFs != MagickFalse)
10636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10637 " oFFs");
10638 if (mng_info->ping_exclude_pHYs != MagickFalse)
10639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 " pHYs");
10641 if (mng_info->ping_exclude_sRGB != MagickFalse)
10642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10643 " sRGB");
10644 if (mng_info->ping_exclude_tEXt != MagickFalse)
10645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10646 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010647 if (mng_info->ping_exclude_tRNS != MagickFalse)
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010650 if (mng_info->ping_exclude_vpAg != MagickFalse)
10651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652 " vpAg");
10653 if (mng_info->ping_exclude_zCCP != MagickFalse)
10654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10655 " zCCP");
10656 if (mng_info->ping_exclude_zTXt != MagickFalse)
10657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10658 " zTXt");
10659 }
10660
glennrpb9cfe272010-12-21 15:08:06 +000010661 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010662
glennrpb9cfe272010-12-21 15:08:06 +000010663 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010664
10665 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010666
cristy3ed852e2009-09-05 21:47:34 +000010667 if (logging != MagickFalse)
10668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010669
cristy3ed852e2009-09-05 21:47:34 +000010670 return(status);
10671}
10672
10673#if defined(JNG_SUPPORTED)
10674
10675/* Write one JNG image */
10676static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10677 const ImageInfo *image_info,Image *image)
10678{
10679 Image
10680 *jpeg_image;
10681
10682 ImageInfo
10683 *jpeg_image_info;
10684
10685 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010686 logging,
cristy3ed852e2009-09-05 21:47:34 +000010687 status;
10688
10689 size_t
10690 length;
10691
10692 unsigned char
10693 *blob,
10694 chunk[80],
10695 *p;
10696
10697 unsigned int
10698 jng_alpha_compression_method,
10699 jng_alpha_sample_depth,
10700 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010701 transparent;
10702
cristybb503372010-05-27 20:51:26 +000010703 size_t
cristy3ed852e2009-09-05 21:47:34 +000010704 jng_quality;
10705
10706 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010707 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010708
10709 blob=(unsigned char *) NULL;
10710 jpeg_image=(Image *) NULL;
10711 jpeg_image_info=(ImageInfo *) NULL;
10712
10713 status=MagickTrue;
10714 transparent=image_info->type==GrayscaleMatteType ||
10715 image_info->type==TrueColorMatteType;
10716 jng_color_type=10;
10717 jng_alpha_sample_depth=0;
10718 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10719 jng_alpha_compression_method=0;
10720
10721 if (image->matte != MagickFalse)
10722 {
10723 /* if any pixels are transparent */
10724 transparent=MagickTrue;
10725 if (image_info->compression==JPEGCompression)
10726 jng_alpha_compression_method=8;
10727 }
10728
10729 if (transparent)
10730 {
10731 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010732
cristy3ed852e2009-09-05 21:47:34 +000010733 /* Create JPEG blob, image, and image_info */
10734 if (logging != MagickFalse)
10735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10736 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010737
cristy3ed852e2009-09-05 21:47:34 +000010738 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010739
cristy3ed852e2009-09-05 21:47:34 +000010740 if (jpeg_image_info == (ImageInfo *) NULL)
10741 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010742
cristy3ed852e2009-09-05 21:47:34 +000010743 if (logging != MagickFalse)
10744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10745 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010746
cristy3ed852e2009-09-05 21:47:34 +000010747 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010748
cristy3ed852e2009-09-05 21:47:34 +000010749 if (jpeg_image == (Image *) NULL)
10750 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010751
cristy3ed852e2009-09-05 21:47:34 +000010752 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10753 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10754 status=NegateImage(jpeg_image,MagickFalse);
10755 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010756
cristy3ed852e2009-09-05 21:47:34 +000010757 if (jng_quality >= 1000)
10758 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010759
cristy3ed852e2009-09-05 21:47:34 +000010760 else
10761 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010762
cristy3ed852e2009-09-05 21:47:34 +000010763 jpeg_image_info->type=GrayscaleType;
10764 (void) SetImageType(jpeg_image,GrayscaleType);
10765 (void) AcquireUniqueFilename(jpeg_image->filename);
10766 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10767 "%s",jpeg_image->filename);
10768 }
10769
10770 /* To do: check bit depth of PNG alpha channel */
10771
10772 /* Check if image is grayscale. */
10773 if (image_info->type != TrueColorMatteType && image_info->type !=
10774 TrueColorType && ImageIsGray(image))
10775 jng_color_type-=2;
10776
10777 if (transparent)
10778 {
10779 if (jng_alpha_compression_method==0)
10780 {
10781 const char
10782 *value;
10783
10784 /* Encode opacity as a grayscale PNG blob */
10785 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10786 &image->exception);
10787 if (logging != MagickFalse)
10788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10789 " Creating PNG blob.");
10790 length=0;
10791
10792 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10793 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10794 jpeg_image_info->interlace=NoInterlace;
10795
10796 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10797 &image->exception);
10798
10799 /* Retrieve sample depth used */
10800 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10801 if (value != (char *) NULL)
10802 jng_alpha_sample_depth= (unsigned int) value[0];
10803 }
10804 else
10805 {
10806 /* Encode opacity as a grayscale JPEG blob */
10807
10808 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10809 &image->exception);
10810
10811 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10812 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10813 jpeg_image_info->interlace=NoInterlace;
10814 if (logging != MagickFalse)
10815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10816 " Creating blob.");
10817 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010818 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010819 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010820
cristy3ed852e2009-09-05 21:47:34 +000010821 if (logging != MagickFalse)
10822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010823 " Successfully read jpeg_image into a blob, length=%.20g.",
10824 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010825
10826 }
10827 /* Destroy JPEG image and image_info */
10828 jpeg_image=DestroyImage(jpeg_image);
10829 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10830 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10831 }
10832
10833 /* Write JHDR chunk */
10834 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10835 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010836 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010837 PNGLong(chunk+4,(png_uint_32) image->columns);
10838 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010839 chunk[12]=jng_color_type;
10840 chunk[13]=8; /* sample depth */
10841 chunk[14]=8; /*jng_image_compression_method */
10842 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10843 chunk[16]=jng_alpha_sample_depth;
10844 chunk[17]=jng_alpha_compression_method;
10845 chunk[18]=0; /*jng_alpha_filter_method */
10846 chunk[19]=0; /*jng_alpha_interlace_method */
10847 (void) WriteBlob(image,20,chunk);
10848 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10849 if (logging != MagickFalse)
10850 {
10851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010852 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010853
cristy3ed852e2009-09-05 21:47:34 +000010854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010855 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010856
cristy3ed852e2009-09-05 21:47:34 +000010857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10858 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010859
cristy3ed852e2009-09-05 21:47:34 +000010860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10861 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010862
cristy3ed852e2009-09-05 21:47:34 +000010863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10864 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010865
cristy3ed852e2009-09-05 21:47:34 +000010866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10867 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010868
cristy3ed852e2009-09-05 21:47:34 +000010869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10870 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010871
cristy3ed852e2009-09-05 21:47:34 +000010872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10873 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010874
cristy3ed852e2009-09-05 21:47:34 +000010875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10876 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010877
cristy3ed852e2009-09-05 21:47:34 +000010878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10879 " JNG alpha interlace:%5d",0);
10880 }
10881
glennrp0fe50b42010-11-16 03:52:51 +000010882 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010883 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010884
10885 /*
10886 Write leading ancillary chunks
10887 */
10888
10889 if (transparent)
10890 {
10891 /*
10892 Write JNG bKGD chunk
10893 */
10894
10895 unsigned char
10896 blue,
10897 green,
10898 red;
10899
cristybb503372010-05-27 20:51:26 +000010900 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010901 num_bytes;
10902
10903 if (jng_color_type == 8 || jng_color_type == 12)
10904 num_bytes=6L;
10905 else
10906 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010907 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010908 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010909 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010910 red=ScaleQuantumToChar(image->background_color.red);
10911 green=ScaleQuantumToChar(image->background_color.green);
10912 blue=ScaleQuantumToChar(image->background_color.blue);
10913 *(chunk+4)=0;
10914 *(chunk+5)=red;
10915 *(chunk+6)=0;
10916 *(chunk+7)=green;
10917 *(chunk+8)=0;
10918 *(chunk+9)=blue;
10919 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10920 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10921 }
10922
10923 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10924 {
10925 /*
10926 Write JNG sRGB chunk
10927 */
10928 (void) WriteBlobMSBULong(image,1L);
10929 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010930 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010931
cristy3ed852e2009-09-05 21:47:34 +000010932 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010933 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010934 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010935 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010936
cristy3ed852e2009-09-05 21:47:34 +000010937 else
glennrpe610a072010-08-05 17:08:46 +000010938 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010939 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010940 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010941
cristy3ed852e2009-09-05 21:47:34 +000010942 (void) WriteBlob(image,5,chunk);
10943 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10944 }
10945 else
10946 {
10947 if (image->gamma != 0.0)
10948 {
10949 /*
10950 Write JNG gAMA chunk
10951 */
10952 (void) WriteBlobMSBULong(image,4L);
10953 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010954 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010955 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010956 (void) WriteBlob(image,8,chunk);
10957 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10958 }
glennrp0fe50b42010-11-16 03:52:51 +000010959
cristy3ed852e2009-09-05 21:47:34 +000010960 if ((mng_info->equal_chrms == MagickFalse) &&
10961 (image->chromaticity.red_primary.x != 0.0))
10962 {
10963 PrimaryInfo
10964 primary;
10965
10966 /*
10967 Write JNG cHRM chunk
10968 */
10969 (void) WriteBlobMSBULong(image,32L);
10970 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010971 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010972 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010973 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10974 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010975 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010976 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10977 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010978 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010979 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10980 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010981 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010982 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10983 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010984 (void) WriteBlob(image,36,chunk);
10985 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10986 }
10987 }
glennrp0fe50b42010-11-16 03:52:51 +000010988
cristy3ed852e2009-09-05 21:47:34 +000010989 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10990 {
10991 /*
10992 Write JNG pHYs chunk
10993 */
10994 (void) WriteBlobMSBULong(image,9L);
10995 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000010996 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000010997 if (image->units == PixelsPerInchResolution)
10998 {
cristy35ef8242010-06-03 16:24:13 +000010999 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011000 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011001
cristy35ef8242010-06-03 16:24:13 +000011002 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011003 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011004
cristy3ed852e2009-09-05 21:47:34 +000011005 chunk[12]=1;
11006 }
glennrp0fe50b42010-11-16 03:52:51 +000011007
cristy3ed852e2009-09-05 21:47:34 +000011008 else
11009 {
11010 if (image->units == PixelsPerCentimeterResolution)
11011 {
cristy35ef8242010-06-03 16:24:13 +000011012 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011013 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011014
cristy35ef8242010-06-03 16:24:13 +000011015 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011016 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011017
cristy3ed852e2009-09-05 21:47:34 +000011018 chunk[12]=1;
11019 }
glennrp0fe50b42010-11-16 03:52:51 +000011020
cristy3ed852e2009-09-05 21:47:34 +000011021 else
11022 {
cristy35ef8242010-06-03 16:24:13 +000011023 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11024 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011025 chunk[12]=0;
11026 }
11027 }
11028 (void) WriteBlob(image,13,chunk);
11029 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11030 }
11031
11032 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11033 {
11034 /*
11035 Write JNG oFFs chunk
11036 */
11037 (void) WriteBlobMSBULong(image,9L);
11038 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011039 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011040 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11041 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011042 chunk[12]=0;
11043 (void) WriteBlob(image,13,chunk);
11044 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11045 }
11046 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11047 {
11048 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11049 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011050 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011051 PNGLong(chunk+4,(png_uint_32) image->page.width);
11052 PNGLong(chunk+8,(png_uint_32) image->page.height);
11053 chunk[12]=0; /* unit = pixels */
11054 (void) WriteBlob(image,13,chunk);
11055 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11056 }
11057
11058
11059 if (transparent)
11060 {
11061 if (jng_alpha_compression_method==0)
11062 {
cristybb503372010-05-27 20:51:26 +000011063 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011064 i;
11065
cristybb503372010-05-27 20:51:26 +000011066 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011067 len;
11068
11069 /* Write IDAT chunk header */
11070 if (logging != MagickFalse)
11071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011072 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011073 length);
cristy3ed852e2009-09-05 21:47:34 +000011074
11075 /* Copy IDAT chunks */
11076 len=0;
11077 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011078 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011079 {
11080 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11081 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011082
cristy3ed852e2009-09-05 21:47:34 +000011083 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11084 {
11085 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011086 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011087 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011088 (void) WriteBlob(image,(size_t) len+4,p);
11089 (void) WriteBlobMSBULong(image,
11090 crc32(0,p,(uInt) len+4));
11091 }
glennrp0fe50b42010-11-16 03:52:51 +000011092
cristy3ed852e2009-09-05 21:47:34 +000011093 else
11094 {
11095 if (logging != MagickFalse)
11096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011097 " Skipping %c%c%c%c chunk, length=%.20g.",
11098 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011099 }
11100 p+=(8+len);
11101 }
11102 }
11103 else
11104 {
11105 /* Write JDAA chunk header */
11106 if (logging != MagickFalse)
11107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011108 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011109 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011110 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011111 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011112 /* Write JDAT chunk(s) data */
11113 (void) WriteBlob(image,4,chunk);
11114 (void) WriteBlob(image,length,blob);
11115 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11116 (uInt) length));
11117 }
11118 blob=(unsigned char *) RelinquishMagickMemory(blob);
11119 }
11120
11121 /* Encode image as a JPEG blob */
11122 if (logging != MagickFalse)
11123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11124 " Creating jpeg_image_info.");
11125 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11126 if (jpeg_image_info == (ImageInfo *) NULL)
11127 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11128
11129 if (logging != MagickFalse)
11130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11131 " Creating jpeg_image.");
11132
11133 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11134 if (jpeg_image == (Image *) NULL)
11135 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11136 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11137
11138 (void) AcquireUniqueFilename(jpeg_image->filename);
11139 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11140 jpeg_image->filename);
11141
11142 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11143 &image->exception);
11144
11145 if (logging != MagickFalse)
11146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011147 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11148 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011149
11150 if (jng_color_type == 8 || jng_color_type == 12)
11151 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011152
cristy3ed852e2009-09-05 21:47:34 +000011153 jpeg_image_info->quality=jng_quality % 1000;
11154 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11155 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011156
cristy3ed852e2009-09-05 21:47:34 +000011157 if (logging != MagickFalse)
11158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11159 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011160
cristy3ed852e2009-09-05 21:47:34 +000011161 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011162
cristy3ed852e2009-09-05 21:47:34 +000011163 if (logging != MagickFalse)
11164 {
11165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011166 " Successfully read jpeg_image into a blob, length=%.20g.",
11167 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011168
11169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011170 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011171 }
glennrp0fe50b42010-11-16 03:52:51 +000011172
cristy3ed852e2009-09-05 21:47:34 +000011173 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011174 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011175 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011176 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011177 (void) WriteBlob(image,4,chunk);
11178 (void) WriteBlob(image,length,blob);
11179 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11180
11181 jpeg_image=DestroyImage(jpeg_image);
11182 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11183 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11184 blob=(unsigned char *) RelinquishMagickMemory(blob);
11185
11186 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011187 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011188
11189 /* Write IEND chunk */
11190 (void) WriteBlobMSBULong(image,0L);
11191 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011192 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011193 (void) WriteBlob(image,4,chunk);
11194 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11195
11196 if (logging != MagickFalse)
11197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11198 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011199
cristy3ed852e2009-09-05 21:47:34 +000011200 return(status);
11201}
11202
11203
11204/*
11205%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11206% %
11207% %
11208% %
11209% W r i t e J N G I m a g e %
11210% %
11211% %
11212% %
11213%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11214%
11215% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11216%
11217% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11218%
11219% The format of the WriteJNGImage method is:
11220%
11221% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11222%
11223% A description of each parameter follows:
11224%
11225% o image_info: the image info.
11226%
11227% o image: The image.
11228%
11229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11230*/
11231static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11232{
11233 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011234 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011235 logging,
cristy3ed852e2009-09-05 21:47:34 +000011236 status;
11237
11238 MngInfo
11239 *mng_info;
11240
cristy3ed852e2009-09-05 21:47:34 +000011241 /*
11242 Open image file.
11243 */
11244 assert(image_info != (const ImageInfo *) NULL);
11245 assert(image_info->signature == MagickSignature);
11246 assert(image != (Image *) NULL);
11247 assert(image->signature == MagickSignature);
11248 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011249 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011250 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11251 if (status == MagickFalse)
11252 return(status);
11253
11254 /*
11255 Allocate a MngInfo structure.
11256 */
11257 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011258 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011259 if (mng_info == (MngInfo *) NULL)
11260 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11261 /*
11262 Initialize members of the MngInfo structure.
11263 */
11264 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11265 mng_info->image=image;
11266 have_mng_structure=MagickTrue;
11267
11268 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11269
11270 status=WriteOneJNGImage(mng_info,image_info,image);
11271 (void) CloseBlob(image);
11272
11273 (void) CatchImageException(image);
11274 MngInfoFreeStruct(mng_info,&have_mng_structure);
11275 if (logging != MagickFalse)
11276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11277 return(status);
11278}
11279#endif
11280
11281
11282
11283static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11284{
11285 const char
11286 *option;
11287
11288 Image
11289 *next_image;
11290
11291 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011292 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011293 status;
11294
glennrp03812ae2010-12-24 01:31:34 +000011295 volatile MagickBooleanType
11296 logging;
11297
cristy3ed852e2009-09-05 21:47:34 +000011298 MngInfo
11299 *mng_info;
11300
11301 int
cristy3ed852e2009-09-05 21:47:34 +000011302 image_count,
11303 need_iterations,
11304 need_matte;
11305
11306 volatile int
11307#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11308 defined(PNG_MNG_FEATURES_SUPPORTED)
11309 need_local_plte,
11310#endif
11311 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011312 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011313 use_global_plte;
11314
cristybb503372010-05-27 20:51:26 +000011315 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011316 i;
11317
11318 unsigned char
11319 chunk[800];
11320
11321 volatile unsigned int
11322 write_jng,
11323 write_mng;
11324
cristybb503372010-05-27 20:51:26 +000011325 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011326 scene;
11327
cristybb503372010-05-27 20:51:26 +000011328 size_t
cristy3ed852e2009-09-05 21:47:34 +000011329 final_delay=0,
11330 initial_delay;
11331
glennrpd5045b42010-03-24 12:40:35 +000011332#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011333 if (image_info->verbose)
11334 printf("Your PNG library (libpng-%s) is rather old.\n",
11335 PNG_LIBPNG_VER_STRING);
11336#endif
11337
11338 /*
11339 Open image file.
11340 */
11341 assert(image_info != (const ImageInfo *) NULL);
11342 assert(image_info->signature == MagickSignature);
11343 assert(image != (Image *) NULL);
11344 assert(image->signature == MagickSignature);
11345 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011346 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011347 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11348 if (status == MagickFalse)
11349 return(status);
11350
11351 /*
11352 Allocate a MngInfo structure.
11353 */
11354 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011355 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011356 if (mng_info == (MngInfo *) NULL)
11357 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11358 /*
11359 Initialize members of the MngInfo structure.
11360 */
11361 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11362 mng_info->image=image;
11363 have_mng_structure=MagickTrue;
11364 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11365
11366 /*
11367 * See if user has requested a specific PNG subformat to be used
11368 * for all of the PNGs in the MNG being written, e.g.,
11369 *
11370 * convert *.png png8:animation.mng
11371 *
11372 * To do: check -define png:bit_depth and png:color_type as well,
11373 * or perhaps use mng:bit_depth and mng:color_type instead for
11374 * global settings.
11375 */
11376
11377 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11378 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11379 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11380
11381 write_jng=MagickFalse;
11382 if (image_info->compression == JPEGCompression)
11383 write_jng=MagickTrue;
11384
11385 mng_info->adjoin=image_info->adjoin &&
11386 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11387
cristy3ed852e2009-09-05 21:47:34 +000011388 if (logging != MagickFalse)
11389 {
11390 /* Log some info about the input */
11391 Image
11392 *p;
11393
11394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11395 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011396
cristy3ed852e2009-09-05 21:47:34 +000011397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011398 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011399
cristy3ed852e2009-09-05 21:47:34 +000011400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11401 " Type: %d",image_info->type);
11402
11403 scene=0;
11404 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11405 {
11406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011407 " Scene: %.20g",(double) scene++);
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 depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011411
cristy3ed852e2009-09-05 21:47:34 +000011412 if (p->matte)
11413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11414 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011415
cristy3ed852e2009-09-05 21:47:34 +000011416 else
11417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11418 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011419
cristy3ed852e2009-09-05 21:47:34 +000011420 if (p->storage_class == PseudoClass)
11421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11422 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011423
cristy3ed852e2009-09-05 21:47:34 +000011424 else
11425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11426 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011427
cristy3ed852e2009-09-05 21:47:34 +000011428 if (p->colors)
11429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011430 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011431
cristy3ed852e2009-09-05 21:47:34 +000011432 else
11433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11434 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011435
cristy3ed852e2009-09-05 21:47:34 +000011436 if (mng_info->adjoin == MagickFalse)
11437 break;
11438 }
11439 }
11440
cristy3ed852e2009-09-05 21:47:34 +000011441 use_global_plte=MagickFalse;
11442 all_images_are_gray=MagickFalse;
11443#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11444 need_local_plte=MagickTrue;
11445#endif
11446 need_defi=MagickFalse;
11447 need_matte=MagickFalse;
11448 mng_info->framing_mode=1;
11449 mng_info->old_framing_mode=1;
11450
11451 if (write_mng)
11452 if (image_info->page != (char *) NULL)
11453 {
11454 /*
11455 Determine image bounding box.
11456 */
11457 SetGeometry(image,&mng_info->page);
11458 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11459 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11460 }
11461 if (write_mng)
11462 {
11463 unsigned int
11464 need_geom;
11465
11466 unsigned short
11467 red,
11468 green,
11469 blue;
11470
11471 mng_info->page=image->page;
11472 need_geom=MagickTrue;
11473 if (mng_info->page.width || mng_info->page.height)
11474 need_geom=MagickFalse;
11475 /*
11476 Check all the scenes.
11477 */
11478 initial_delay=image->delay;
11479 need_iterations=MagickFalse;
11480 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11481 mng_info->equal_physs=MagickTrue,
11482 mng_info->equal_gammas=MagickTrue;
11483 mng_info->equal_srgbs=MagickTrue;
11484 mng_info->equal_backgrounds=MagickTrue;
11485 image_count=0;
11486#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11487 defined(PNG_MNG_FEATURES_SUPPORTED)
11488 all_images_are_gray=MagickTrue;
11489 mng_info->equal_palettes=MagickFalse;
11490 need_local_plte=MagickFalse;
11491#endif
11492 for (next_image=image; next_image != (Image *) NULL; )
11493 {
11494 if (need_geom)
11495 {
11496 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11497 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011498
cristy3ed852e2009-09-05 21:47:34 +000011499 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11500 mng_info->page.height=next_image->rows+next_image->page.y;
11501 }
glennrp0fe50b42010-11-16 03:52:51 +000011502
cristy3ed852e2009-09-05 21:47:34 +000011503 if (next_image->page.x || next_image->page.y)
11504 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011505
cristy3ed852e2009-09-05 21:47:34 +000011506 if (next_image->matte)
11507 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011508
cristy3ed852e2009-09-05 21:47:34 +000011509 if ((int) next_image->dispose >= BackgroundDispose)
11510 if (next_image->matte || next_image->page.x || next_image->page.y ||
11511 ((next_image->columns < mng_info->page.width) &&
11512 (next_image->rows < mng_info->page.height)))
11513 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011514
cristy3ed852e2009-09-05 21:47:34 +000011515 if (next_image->iterations)
11516 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011517
cristy3ed852e2009-09-05 21:47:34 +000011518 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011519
cristy3ed852e2009-09-05 21:47:34 +000011520 if (final_delay != initial_delay || final_delay > 1UL*
11521 next_image->ticks_per_second)
11522 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011523
cristy3ed852e2009-09-05 21:47:34 +000011524#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11525 defined(PNG_MNG_FEATURES_SUPPORTED)
11526 /*
11527 check for global palette possibility.
11528 */
11529 if (image->matte != MagickFalse)
11530 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011531
cristy3ed852e2009-09-05 21:47:34 +000011532 if (need_local_plte == 0)
11533 {
11534 if (ImageIsGray(image) == MagickFalse)
11535 all_images_are_gray=MagickFalse;
11536 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11537 if (use_global_plte == 0)
11538 use_global_plte=mng_info->equal_palettes;
11539 need_local_plte=!mng_info->equal_palettes;
11540 }
11541#endif
11542 if (GetNextImageInList(next_image) != (Image *) NULL)
11543 {
11544 if (next_image->background_color.red !=
11545 next_image->next->background_color.red ||
11546 next_image->background_color.green !=
11547 next_image->next->background_color.green ||
11548 next_image->background_color.blue !=
11549 next_image->next->background_color.blue)
11550 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011551
cristy3ed852e2009-09-05 21:47:34 +000011552 if (next_image->gamma != next_image->next->gamma)
11553 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011554
cristy3ed852e2009-09-05 21:47:34 +000011555 if (next_image->rendering_intent !=
11556 next_image->next->rendering_intent)
11557 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011558
cristy3ed852e2009-09-05 21:47:34 +000011559 if ((next_image->units != next_image->next->units) ||
11560 (next_image->x_resolution != next_image->next->x_resolution) ||
11561 (next_image->y_resolution != next_image->next->y_resolution))
11562 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011563
cristy3ed852e2009-09-05 21:47:34 +000011564 if (mng_info->equal_chrms)
11565 {
11566 if (next_image->chromaticity.red_primary.x !=
11567 next_image->next->chromaticity.red_primary.x ||
11568 next_image->chromaticity.red_primary.y !=
11569 next_image->next->chromaticity.red_primary.y ||
11570 next_image->chromaticity.green_primary.x !=
11571 next_image->next->chromaticity.green_primary.x ||
11572 next_image->chromaticity.green_primary.y !=
11573 next_image->next->chromaticity.green_primary.y ||
11574 next_image->chromaticity.blue_primary.x !=
11575 next_image->next->chromaticity.blue_primary.x ||
11576 next_image->chromaticity.blue_primary.y !=
11577 next_image->next->chromaticity.blue_primary.y ||
11578 next_image->chromaticity.white_point.x !=
11579 next_image->next->chromaticity.white_point.x ||
11580 next_image->chromaticity.white_point.y !=
11581 next_image->next->chromaticity.white_point.y)
11582 mng_info->equal_chrms=MagickFalse;
11583 }
11584 }
11585 image_count++;
11586 next_image=GetNextImageInList(next_image);
11587 }
11588 if (image_count < 2)
11589 {
11590 mng_info->equal_backgrounds=MagickFalse;
11591 mng_info->equal_chrms=MagickFalse;
11592 mng_info->equal_gammas=MagickFalse;
11593 mng_info->equal_srgbs=MagickFalse;
11594 mng_info->equal_physs=MagickFalse;
11595 use_global_plte=MagickFalse;
11596#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11597 need_local_plte=MagickTrue;
11598#endif
11599 need_iterations=MagickFalse;
11600 }
glennrp0fe50b42010-11-16 03:52:51 +000011601
cristy3ed852e2009-09-05 21:47:34 +000011602 if (mng_info->need_fram == MagickFalse)
11603 {
11604 /*
11605 Only certain framing rates 100/n are exactly representable without
11606 the FRAM chunk but we'll allow some slop in VLC files
11607 */
11608 if (final_delay == 0)
11609 {
11610 if (need_iterations != MagickFalse)
11611 {
11612 /*
11613 It's probably a GIF with loop; don't run it *too* fast.
11614 */
glennrp02617122010-07-28 13:07:35 +000011615 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011616 {
11617 final_delay=10;
11618 (void) ThrowMagickException(&image->exception,
11619 GetMagickModule(),CoderWarning,
11620 "input has zero delay between all frames; assuming",
11621 " 10 cs `%s'","");
11622 }
cristy3ed852e2009-09-05 21:47:34 +000011623 }
11624 else
11625 mng_info->ticks_per_second=0;
11626 }
11627 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011628 mng_info->ticks_per_second=(png_uint_32)
11629 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011630 if (final_delay > 50)
11631 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011632
cristy3ed852e2009-09-05 21:47:34 +000011633 if (final_delay > 75)
11634 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011635
cristy3ed852e2009-09-05 21:47:34 +000011636 if (final_delay > 125)
11637 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011638
cristy3ed852e2009-09-05 21:47:34 +000011639 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11640 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11641 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11642 1UL*image->ticks_per_second))
11643 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11644 }
glennrp0fe50b42010-11-16 03:52:51 +000011645
cristy3ed852e2009-09-05 21:47:34 +000011646 if (mng_info->need_fram != MagickFalse)
11647 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11648 /*
11649 If pseudocolor, we should also check to see if all the
11650 palettes are identical and write a global PLTE if they are.
11651 ../glennrp Feb 99.
11652 */
11653 /*
11654 Write the MNG version 1.0 signature and MHDR chunk.
11655 */
11656 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11657 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11658 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011659 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011660 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11661 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011662 PNGLong(chunk+12,mng_info->ticks_per_second);
11663 PNGLong(chunk+16,0L); /* layer count=unknown */
11664 PNGLong(chunk+20,0L); /* frame count=unknown */
11665 PNGLong(chunk+24,0L); /* play time=unknown */
11666 if (write_jng)
11667 {
11668 if (need_matte)
11669 {
11670 if (need_defi || mng_info->need_fram || use_global_plte)
11671 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011672
cristy3ed852e2009-09-05 21:47:34 +000011673 else
11674 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11675 }
glennrp0fe50b42010-11-16 03:52:51 +000011676
cristy3ed852e2009-09-05 21:47:34 +000011677 else
11678 {
11679 if (need_defi || mng_info->need_fram || use_global_plte)
11680 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011681
cristy3ed852e2009-09-05 21:47:34 +000011682 else
11683 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11684 }
11685 }
glennrp0fe50b42010-11-16 03:52:51 +000011686
cristy3ed852e2009-09-05 21:47:34 +000011687 else
11688 {
11689 if (need_matte)
11690 {
11691 if (need_defi || mng_info->need_fram || use_global_plte)
11692 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011693
cristy3ed852e2009-09-05 21:47:34 +000011694 else
11695 PNGLong(chunk+28,9L); /* simplicity=VLC */
11696 }
glennrp0fe50b42010-11-16 03:52:51 +000011697
cristy3ed852e2009-09-05 21:47:34 +000011698 else
11699 {
11700 if (need_defi || mng_info->need_fram || use_global_plte)
11701 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011702
cristy3ed852e2009-09-05 21:47:34 +000011703 else
11704 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11705 }
11706 }
11707 (void) WriteBlob(image,32,chunk);
11708 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11709 option=GetImageOption(image_info,"mng:need-cacheoff");
11710 if (option != (const char *) NULL)
11711 {
11712 size_t
11713 length;
11714
11715 /*
11716 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11717 */
11718 PNGType(chunk,mng_nEED);
11719 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011720 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011721 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011722 length+=4;
11723 (void) WriteBlob(image,length,chunk);
11724 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11725 }
11726 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11727 (GetNextImageInList(image) != (Image *) NULL) &&
11728 (image->iterations != 1))
11729 {
11730 /*
11731 Write MNG TERM chunk
11732 */
11733 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11734 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011735 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011736 chunk[4]=3; /* repeat animation */
11737 chunk[5]=0; /* show last frame when done */
11738 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11739 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011740
cristy3ed852e2009-09-05 21:47:34 +000011741 if (image->iterations == 0)
11742 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011743
cristy3ed852e2009-09-05 21:47:34 +000011744 else
11745 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011746
cristy3ed852e2009-09-05 21:47:34 +000011747 if (logging != MagickFalse)
11748 {
11749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011750 " TERM delay: %.20g",(double) (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 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011755 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011756
cristy3ed852e2009-09-05 21:47:34 +000011757 else
11758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011759 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011760 }
11761 (void) WriteBlob(image,14,chunk);
11762 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11763 }
11764 /*
11765 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11766 */
11767 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11768 mng_info->equal_srgbs)
11769 {
11770 /*
11771 Write MNG sRGB chunk
11772 */
11773 (void) WriteBlobMSBULong(image,1L);
11774 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011775 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011776
cristy3ed852e2009-09-05 21:47:34 +000011777 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011778 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011779 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011780 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011781
cristy3ed852e2009-09-05 21:47:34 +000011782 else
glennrpe610a072010-08-05 17:08:46 +000011783 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011784 Magick_RenderingIntent_to_PNG_RenderingIntent(
11785 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011786
cristy3ed852e2009-09-05 21:47:34 +000011787 (void) WriteBlob(image,5,chunk);
11788 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11789 mng_info->have_write_global_srgb=MagickTrue;
11790 }
glennrp0fe50b42010-11-16 03:52:51 +000011791
cristy3ed852e2009-09-05 21:47:34 +000011792 else
11793 {
11794 if (image->gamma && mng_info->equal_gammas)
11795 {
11796 /*
11797 Write MNG gAMA chunk
11798 */
11799 (void) WriteBlobMSBULong(image,4L);
11800 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011801 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011802 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011803 (void) WriteBlob(image,8,chunk);
11804 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11805 mng_info->have_write_global_gama=MagickTrue;
11806 }
11807 if (mng_info->equal_chrms)
11808 {
11809 PrimaryInfo
11810 primary;
11811
11812 /*
11813 Write MNG cHRM chunk
11814 */
11815 (void) WriteBlobMSBULong(image,32L);
11816 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011817 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011818 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011819 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11820 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011821 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011822 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11823 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011824 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011825 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11826 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011827 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011828 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11829 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011830 (void) WriteBlob(image,36,chunk);
11831 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11832 mng_info->have_write_global_chrm=MagickTrue;
11833 }
11834 }
11835 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11836 {
11837 /*
11838 Write MNG pHYs chunk
11839 */
11840 (void) WriteBlobMSBULong(image,9L);
11841 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011842 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011843
cristy3ed852e2009-09-05 21:47:34 +000011844 if (image->units == PixelsPerInchResolution)
11845 {
cristy35ef8242010-06-03 16:24:13 +000011846 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011847 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011848
cristy35ef8242010-06-03 16:24:13 +000011849 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011850 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011851
cristy3ed852e2009-09-05 21:47:34 +000011852 chunk[12]=1;
11853 }
glennrp0fe50b42010-11-16 03:52:51 +000011854
cristy3ed852e2009-09-05 21:47:34 +000011855 else
11856 {
11857 if (image->units == PixelsPerCentimeterResolution)
11858 {
cristy35ef8242010-06-03 16:24:13 +000011859 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011860 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011861
cristy35ef8242010-06-03 16:24:13 +000011862 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011863 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011864
cristy3ed852e2009-09-05 21:47:34 +000011865 chunk[12]=1;
11866 }
glennrp0fe50b42010-11-16 03:52:51 +000011867
cristy3ed852e2009-09-05 21:47:34 +000011868 else
11869 {
cristy35ef8242010-06-03 16:24:13 +000011870 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11871 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011872 chunk[12]=0;
11873 }
11874 }
11875 (void) WriteBlob(image,13,chunk);
11876 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11877 }
11878 /*
11879 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11880 or does not cover the entire frame.
11881 */
11882 if (write_mng && (image->matte || image->page.x > 0 ||
11883 image->page.y > 0 || (image->page.width &&
11884 (image->page.width+image->page.x < mng_info->page.width))
11885 || (image->page.height && (image->page.height+image->page.y
11886 < mng_info->page.height))))
11887 {
11888 (void) WriteBlobMSBULong(image,6L);
11889 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011890 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011891 red=ScaleQuantumToShort(image->background_color.red);
11892 green=ScaleQuantumToShort(image->background_color.green);
11893 blue=ScaleQuantumToShort(image->background_color.blue);
11894 PNGShort(chunk+4,red);
11895 PNGShort(chunk+6,green);
11896 PNGShort(chunk+8,blue);
11897 (void) WriteBlob(image,10,chunk);
11898 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11899 if (mng_info->equal_backgrounds)
11900 {
11901 (void) WriteBlobMSBULong(image,6L);
11902 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011903 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011904 (void) WriteBlob(image,10,chunk);
11905 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11906 }
11907 }
11908
11909#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11910 if ((need_local_plte == MagickFalse) &&
11911 (image->storage_class == PseudoClass) &&
11912 (all_images_are_gray == MagickFalse))
11913 {
cristybb503372010-05-27 20:51:26 +000011914 size_t
cristy3ed852e2009-09-05 21:47:34 +000011915 data_length;
11916
11917 /*
11918 Write MNG PLTE chunk
11919 */
11920 data_length=3*image->colors;
11921 (void) WriteBlobMSBULong(image,data_length);
11922 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011923 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011924
cristybb503372010-05-27 20:51:26 +000011925 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011926 {
11927 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11928 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11929 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11930 }
glennrp0fe50b42010-11-16 03:52:51 +000011931
cristy3ed852e2009-09-05 21:47:34 +000011932 (void) WriteBlob(image,data_length+4,chunk);
11933 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11934 mng_info->have_write_global_plte=MagickTrue;
11935 }
11936#endif
11937 }
11938 scene=0;
11939 mng_info->delay=0;
11940#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11941 defined(PNG_MNG_FEATURES_SUPPORTED)
11942 mng_info->equal_palettes=MagickFalse;
11943#endif
11944 do
11945 {
11946 if (mng_info->adjoin)
11947 {
11948#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11949 defined(PNG_MNG_FEATURES_SUPPORTED)
11950 /*
11951 If we aren't using a global palette for the entire MNG, check to
11952 see if we can use one for two or more consecutive images.
11953 */
11954 if (need_local_plte && use_global_plte && !all_images_are_gray)
11955 {
11956 if (mng_info->IsPalette)
11957 {
11958 /*
11959 When equal_palettes is true, this image has the same palette
11960 as the previous PseudoClass image
11961 */
11962 mng_info->have_write_global_plte=mng_info->equal_palettes;
11963 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11964 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11965 {
11966 /*
11967 Write MNG PLTE chunk
11968 */
cristybb503372010-05-27 20:51:26 +000011969 size_t
cristy3ed852e2009-09-05 21:47:34 +000011970 data_length;
11971
11972 data_length=3*image->colors;
11973 (void) WriteBlobMSBULong(image,data_length);
11974 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011975 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011976
cristybb503372010-05-27 20:51:26 +000011977 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011978 {
11979 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11980 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11981 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11982 }
glennrp0fe50b42010-11-16 03:52:51 +000011983
cristy3ed852e2009-09-05 21:47:34 +000011984 (void) WriteBlob(image,data_length+4,chunk);
11985 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11986 (uInt) (data_length+4)));
11987 mng_info->have_write_global_plte=MagickTrue;
11988 }
11989 }
11990 else
11991 mng_info->have_write_global_plte=MagickFalse;
11992 }
11993#endif
11994 if (need_defi)
11995 {
cristybb503372010-05-27 20:51:26 +000011996 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011997 previous_x,
11998 previous_y;
11999
12000 if (scene)
12001 {
12002 previous_x=mng_info->page.x;
12003 previous_y=mng_info->page.y;
12004 }
12005 else
12006 {
12007 previous_x=0;
12008 previous_y=0;
12009 }
12010 mng_info->page=image->page;
12011 if ((mng_info->page.x != previous_x) ||
12012 (mng_info->page.y != previous_y))
12013 {
12014 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12015 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012016 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012017 chunk[4]=0; /* object 0 MSB */
12018 chunk[5]=0; /* object 0 LSB */
12019 chunk[6]=0; /* visible */
12020 chunk[7]=0; /* abstract */
12021 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12022 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12023 (void) WriteBlob(image,16,chunk);
12024 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12025 }
12026 }
12027 }
12028
12029 mng_info->write_mng=write_mng;
12030
12031 if ((int) image->dispose >= 3)
12032 mng_info->framing_mode=3;
12033
12034 if (mng_info->need_fram && mng_info->adjoin &&
12035 ((image->delay != mng_info->delay) ||
12036 (mng_info->framing_mode != mng_info->old_framing_mode)))
12037 {
12038 if (image->delay == mng_info->delay)
12039 {
12040 /*
12041 Write a MNG FRAM chunk with the new framing mode.
12042 */
12043 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12044 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012045 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012046 chunk[4]=(unsigned char) mng_info->framing_mode;
12047 (void) WriteBlob(image,5,chunk);
12048 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12049 }
12050 else
12051 {
12052 /*
12053 Write a MNG FRAM chunk with the delay.
12054 */
12055 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12056 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012057 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012058 chunk[4]=(unsigned char) mng_info->framing_mode;
12059 chunk[5]=0; /* frame name separator (no name) */
12060 chunk[6]=2; /* flag for changing default delay */
12061 chunk[7]=0; /* flag for changing frame timeout */
12062 chunk[8]=0; /* flag for changing frame clipping */
12063 chunk[9]=0; /* flag for changing frame sync_id */
12064 PNGLong(chunk+10,(png_uint_32)
12065 ((mng_info->ticks_per_second*
12066 image->delay)/MagickMax(image->ticks_per_second,1)));
12067 (void) WriteBlob(image,14,chunk);
12068 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012069 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012070 }
12071 mng_info->old_framing_mode=mng_info->framing_mode;
12072 }
12073
12074#if defined(JNG_SUPPORTED)
12075 if (image_info->compression == JPEGCompression)
12076 {
12077 ImageInfo
12078 *write_info;
12079
12080 if (logging != MagickFalse)
12081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12082 " Writing JNG object.");
12083 /* To do: specify the desired alpha compression method. */
12084 write_info=CloneImageInfo(image_info);
12085 write_info->compression=UndefinedCompression;
12086 status=WriteOneJNGImage(mng_info,write_info,image);
12087 write_info=DestroyImageInfo(write_info);
12088 }
12089 else
12090#endif
12091 {
12092 if (logging != MagickFalse)
12093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12094 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012095
glennrpb9cfe272010-12-21 15:08:06 +000012096 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012097 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012098
12099 /* We don't want any ancillary chunks written */
12100 mng_info->ping_exclude_bKGD=MagickTrue;
12101 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012102 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012103 mng_info->ping_exclude_EXIF=MagickTrue;
12104 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012105 mng_info->ping_exclude_iCCP=MagickTrue;
12106 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12107 mng_info->ping_exclude_oFFs=MagickTrue;
12108 mng_info->ping_exclude_pHYs=MagickTrue;
12109 mng_info->ping_exclude_sRGB=MagickTrue;
12110 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012111 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012112 mng_info->ping_exclude_vpAg=MagickTrue;
12113 mng_info->ping_exclude_zCCP=MagickTrue;
12114 mng_info->ping_exclude_zTXt=MagickTrue;
12115
cristy3ed852e2009-09-05 21:47:34 +000012116 status=WriteOnePNGImage(mng_info,image_info,image);
12117 }
12118
12119 if (status == MagickFalse)
12120 {
12121 MngInfoFreeStruct(mng_info,&have_mng_structure);
12122 (void) CloseBlob(image);
12123 return(MagickFalse);
12124 }
12125 (void) CatchImageException(image);
12126 if (GetNextImageInList(image) == (Image *) NULL)
12127 break;
12128 image=SyncNextImageInList(image);
12129 status=SetImageProgress(image,SaveImagesTag,scene++,
12130 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012131
cristy3ed852e2009-09-05 21:47:34 +000012132 if (status == MagickFalse)
12133 break;
glennrp0fe50b42010-11-16 03:52:51 +000012134
cristy3ed852e2009-09-05 21:47:34 +000012135 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012136
cristy3ed852e2009-09-05 21:47:34 +000012137 if (write_mng)
12138 {
12139 while (GetPreviousImageInList(image) != (Image *) NULL)
12140 image=GetPreviousImageInList(image);
12141 /*
12142 Write the MEND chunk.
12143 */
12144 (void) WriteBlobMSBULong(image,0x00000000L);
12145 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012146 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012147 (void) WriteBlob(image,4,chunk);
12148 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12149 }
12150 /*
12151 Relinquish resources.
12152 */
12153 (void) CloseBlob(image);
12154 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012155
cristy3ed852e2009-09-05 21:47:34 +000012156 if (logging != MagickFalse)
12157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012158
cristy3ed852e2009-09-05 21:47:34 +000012159 return(MagickTrue);
12160}
glennrpd5045b42010-03-24 12:40:35 +000012161#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012162
cristy3ed852e2009-09-05 21:47:34 +000012163static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12164{
12165 image=image;
12166 printf("Your PNG library is too old: You have libpng-%s\n",
12167 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012168
cristy3ed852e2009-09-05 21:47:34 +000012169 ThrowBinaryException(CoderError,"PNG library is too old",
12170 image_info->filename);
12171}
glennrp39992b42010-11-14 00:03:43 +000012172
cristy3ed852e2009-09-05 21:47:34 +000012173static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12174{
12175 return(WritePNGImage(image_info,image));
12176}
glennrpd5045b42010-03-24 12:40:35 +000012177#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012178#endif