blob: 5f8fefe4967cef82510b918d50db16b65570ec33 [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
glennrp8e045c82011-04-27 16:40:27 +0000128#define GetRGBOPixelComponents(src, dest) \
129 (dest).red = GetRedPixelComponent((src)); \
130 (dest).green = GetGreenPixelComponent((src)); \
131 (dest).red = GetBluePixelComponent((src)); \
132 (dest).opacity = GetOpacityPixelComponent((src)); \
133
134#define SetRGBOPixelComponents(dest, src) \
135 SetRedPixelComponent((src),(dest).red); \
136 SetGreenPixelComponent((src),(dest).green); \
137 SetBluePixelComponent((src),(dest).blue); \
138 SetOpacityPixelComponent((src),(dest).opacity); \
139
140
141#define GetRGBPixelComponents(src, dest) \
142 (dest).red = GetRedPixelComponent((src)); \
143 (dest).green = GetGreenPixelComponent((src)); \
144 (dest).red = GetBluePixelComponent((src));
145
146#define SetRGBPixelComponents(dest, src) \
147 SetRedPixelComponent((src),(dest).red); \
148 SetGreenPixelComponent((src),(dest).green); \
149 SetBluePixelComponent((src),(dest).blue);
150
cristy3ed852e2009-09-05 21:47:34 +0000151/*
152 Establish thread safety.
153 setjmp/longjmp is claimed to be safe on these platforms:
154 setjmp/longjmp is alleged to be unsafe on these platforms:
155*/
156#ifndef SETJMP_IS_THREAD_SAFE
157#define PNG_SETJMP_NOT_THREAD_SAFE
158#endif
159
160#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
161static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000162 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000163#endif
164
165/*
166 This temporary until I set up malloc'ed object attributes array.
167 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
168 waste more memory.
169*/
170#define MNG_MAX_OBJECTS 256
171
172/*
173 If this not defined, spec is interpreted strictly. If it is
174 defined, an attempt will be made to recover from some errors,
175 including
176 o global PLTE too short
177*/
178#undef MNG_LOOSE
179
180/*
181 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
182 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
183 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
184 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
185 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
186 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
187 will be enabled by default in libpng-1.2.0.
188*/
cristy3ed852e2009-09-05 21:47:34 +0000189#ifdef PNG_MNG_FEATURES_SUPPORTED
190# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
191# define PNG_READ_EMPTY_PLTE_SUPPORTED
192# endif
193# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
194# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
195# endif
196#endif
197
198/*
cristybb503372010-05-27 20:51:26 +0000199 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000200 This macro is only defined in libpng-1.0.3 and later.
201 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
202*/
203#ifndef PNG_UINT_31_MAX
204#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
205#endif
206
207/*
208 Constant strings for known chunk types. If you need to add a chunk,
209 add a string holding the name here. To make the code more
210 portable, we use ASCII numbers like this, not characters.
211*/
212
213static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
214static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
215static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
216static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
217static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
218static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
219static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
220static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
221static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
222static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
223static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
224static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
225static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
226static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
227static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
228static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
229static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
230static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
231static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
232static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
233static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
234static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
235static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
236static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
237static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
238static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
239static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
240static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
241static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
242static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
243static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
244static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
245static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
246static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
247
248#if defined(JNG_SUPPORTED)
249static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
250static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
251static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
252static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
253static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
254static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
255#endif
256
257/*
258Other known chunks that are not yet supported by ImageMagick:
259static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
260static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
261static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
262static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
263static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
264static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
265static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
266static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
267*/
268
269typedef struct _MngBox
270{
cristy8182b072010-05-30 20:10:53 +0000271 long
cristy3ed852e2009-09-05 21:47:34 +0000272 left,
273 right,
274 top,
275 bottom;
276} MngBox;
277
278typedef struct _MngPair
279{
cristy8182b072010-05-30 20:10:53 +0000280 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000281 a,
282 b;
283} MngPair;
284
285#ifdef MNG_OBJECT_BUFFERS
286typedef struct _MngBuffer
287{
288
cristybb503372010-05-27 20:51:26 +0000289 size_t
cristy3ed852e2009-09-05 21:47:34 +0000290 height,
291 width;
292
293 Image
294 *image;
295
296 png_color
297 plte[256];
298
299 int
300 reference_count;
301
302 unsigned char
303 alpha_sample_depth,
304 compression_method,
305 color_type,
306 concrete,
307 filter_method,
308 frozen,
309 image_type,
310 interlace_method,
311 pixel_sample_depth,
312 plte_length,
313 sample_depth,
314 viewable;
315} MngBuffer;
316#endif
317
318typedef struct _MngInfo
319{
320
321#ifdef MNG_OBJECT_BUFFERS
322 MngBuffer
323 *ob[MNG_MAX_OBJECTS];
324#endif
325
326 Image *
327 image;
328
329 RectangleInfo
330 page;
331
332 int
333 adjoin,
334#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
335 bytes_in_read_buffer,
336 found_empty_plte,
337#endif
338 equal_backgrounds,
339 equal_chrms,
340 equal_gammas,
341#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
342 defined(PNG_MNG_FEATURES_SUPPORTED)
343 equal_palettes,
344#endif
345 equal_physs,
346 equal_srgbs,
347 framing_mode,
348 have_global_bkgd,
349 have_global_chrm,
350 have_global_gama,
351 have_global_phys,
352 have_global_sbit,
353 have_global_srgb,
354 have_saved_bkgd_index,
355 have_write_global_chrm,
356 have_write_global_gama,
357 have_write_global_plte,
358 have_write_global_srgb,
359 need_fram,
360 object_id,
361 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000362 saved_bkgd_index;
363
364 int
365 new_number_colors;
366
cristybb503372010-05-27 20:51:26 +0000367 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000368 image_found,
369 loop_count[256],
370 loop_iteration[256],
371 scenes_found,
372 x_off[MNG_MAX_OBJECTS],
373 y_off[MNG_MAX_OBJECTS];
374
375 MngBox
376 clip,
377 frame,
378 image_box,
379 object_clip[MNG_MAX_OBJECTS];
380
381 unsigned char
382 /* These flags could be combined into one byte */
383 exists[MNG_MAX_OBJECTS],
384 frozen[MNG_MAX_OBJECTS],
385 loop_active[256],
386 invisible[MNG_MAX_OBJECTS],
387 viewable[MNG_MAX_OBJECTS];
388
389 MagickOffsetType
390 loop_jump[256];
391
392 png_colorp
393 global_plte;
394
395 png_color_8
396 global_sbit;
397
398 png_byte
399#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
400 read_buffer[8],
401#endif
402 global_trns[256];
403
404 float
405 global_gamma;
406
407 ChromaticityInfo
408 global_chrm;
409
410 RenderingIntent
411 global_srgb_intent;
412
cristy35ef8242010-06-03 16:24:13 +0000413 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000414 delay,
415 global_plte_length,
416 global_trns_length,
417 global_x_pixels_per_unit,
418 global_y_pixels_per_unit,
419 mng_width,
420 mng_height,
421 ticks_per_second;
422
glennrpb9cfe272010-12-21 15:08:06 +0000423 MagickBooleanType
424 need_blob;
425
cristy3ed852e2009-09-05 21:47:34 +0000426 unsigned int
427 IsPalette,
428 global_phys_unit_type,
429 basi_warning,
430 clon_warning,
431 dhdr_warning,
432 jhdr_warning,
433 magn_warning,
434 past_warning,
435 phyg_warning,
436 phys_warning,
437 sbit_warning,
438 show_warning,
439 mng_type,
440 write_mng,
441 write_png_colortype,
442 write_png_depth,
443 write_png8,
444 write_png24,
445 write_png32;
446
447#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000448 size_t
cristy3ed852e2009-09-05 21:47:34 +0000449 basi_width,
450 basi_height;
451
452 unsigned int
453 basi_depth,
454 basi_color_type,
455 basi_compression_method,
456 basi_filter_type,
457 basi_interlace_method,
458 basi_red,
459 basi_green,
460 basi_blue,
461 basi_alpha,
462 basi_viewable;
463#endif
464
465 png_uint_16
466 magn_first,
467 magn_last,
468 magn_mb,
469 magn_ml,
470 magn_mr,
471 magn_mt,
472 magn_mx,
473 magn_my,
474 magn_methx,
475 magn_methy;
476
477 PixelPacket
478 mng_global_bkgd;
479
glennrp26f37912010-12-23 16:22:42 +0000480 /* Added at version 6.6.6-7 */
481 MagickBooleanType
482 ping_exclude_bKGD,
483 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000484 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000485 ping_exclude_EXIF,
486 ping_exclude_gAMA,
487 ping_exclude_iCCP,
488 /* ping_exclude_iTXt, */
489 ping_exclude_oFFs,
490 ping_exclude_pHYs,
491 ping_exclude_sRGB,
492 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000493 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000494 ping_exclude_vpAg,
495 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000496 ping_exclude_zTXt,
497 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000498
cristy3ed852e2009-09-05 21:47:34 +0000499} MngInfo;
500#endif /* VER */
501
502/*
503 Forward declarations.
504*/
505static MagickBooleanType
506 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000507
cristy3ed852e2009-09-05 21:47:34 +0000508static MagickBooleanType
509 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000510
cristy3ed852e2009-09-05 21:47:34 +0000511#if defined(JNG_SUPPORTED)
512static MagickBooleanType
513 WriteJNGImage(const ImageInfo *,Image *);
514#endif
515
glennrp0c3e06b2010-11-19 13:45:02 +0000516#if PNG_LIBPNG_VER > 10011
517
glennrpfd05d622011-02-25 04:10:33 +0000518
glennrp0c3e06b2010-11-19 13:45:02 +0000519#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
520static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000521LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000522{
glennrp67b9c1a2011-04-22 18:47:36 +0000523 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
524 *
525 * This is true if the high byte and the next highest byte of
526 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000527 * are equal to each other. We check this by seeing if the samples
528 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000529 *
530 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000531 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000532 */
533
glennrp3faa9a32011-04-23 14:00:25 +0000534#define QuantumToCharToQuantumEqQuantum(quantum) \
535 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
536
glennrp0c3e06b2010-11-19 13:45:02 +0000537 MagickBooleanType
538 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000539
glennrp03e11f62011-04-22 13:30:16 +0000540 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000541 {
542
543 const PixelPacket
544 *p;
545
546 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000547 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
548 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
549 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
550 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000551
552 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
553 {
554 int indx;
555
556 for (indx=0; indx < (ssize_t) image->colors; indx++)
557 {
glennrp3faa9a32011-04-23 14:00:25 +0000558 ok_to_reduce=(
559 QuantumToCharToQuantumEqQuantum(
560 image->colormap[indx].red) &&
561 QuantumToCharToQuantumEqQuantum(
562 image->colormap[indx].green) &&
563 QuantumToCharToQuantumEqQuantum(
564 image->colormap[indx].blue)) ?
565 MagickTrue : MagickFalse;
566
glennrp0c3e06b2010-11-19 13:45:02 +0000567 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000568 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000569 }
570 }
571
572 if ((ok_to_reduce != MagickFalse) &&
573 (image->storage_class != PseudoClass))
574 {
575 ssize_t
576 y;
577
578 register ssize_t
579 x;
580
581 for (y=0; y < (ssize_t) image->rows; y++)
582 {
583 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
584
585 if (p == (const PixelPacket *) NULL)
586 {
587 ok_to_reduce = MagickFalse;
588 break;
589 }
590
591 for (x=(ssize_t) image->columns-1; x >= 0; x--)
592 {
glennrp3faa9a32011-04-23 14:00:25 +0000593 ok_to_reduce=
594 QuantumToCharToQuantumEqQuantum(GetRedPixelComponent(p)) &&
595 QuantumToCharToQuantumEqQuantum(GetGreenPixelComponent(p)) &&
596 QuantumToCharToQuantumEqQuantum(GetBluePixelComponent(p)) ?
597 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000598
599 if (ok_to_reduce == MagickFalse)
600 break;
601
602 p++;
603 }
glennrp8640fb52010-11-23 15:48:26 +0000604 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +0000605 break;
606 }
607 }
608
609 if (ok_to_reduce != MagickFalse)
610 {
glennrp0c3e06b2010-11-19 13:45:02 +0000611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000612 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +0000613 }
glennrpa6a06632011-01-19 15:15:34 +0000614 else
615 {
616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000617 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +0000618 }
glennrp0c3e06b2010-11-19 13:45:02 +0000619 }
620
621 return ok_to_reduce;
622}
623#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
624
glennrpe610a072010-08-05 17:08:46 +0000625static int
glennrpcf002022011-01-30 02:38:15 +0000626Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +0000627{
glennrpe610a072010-08-05 17:08:46 +0000628 switch (intent)
629 {
630 case PerceptualIntent:
631 return 0;
glennrp0fe50b42010-11-16 03:52:51 +0000632
glennrpe610a072010-08-05 17:08:46 +0000633 case RelativeIntent:
634 return 1;
glennrp0fe50b42010-11-16 03:52:51 +0000635
glennrpe610a072010-08-05 17:08:46 +0000636 case SaturationIntent:
637 return 2;
glennrp0fe50b42010-11-16 03:52:51 +0000638
glennrpe610a072010-08-05 17:08:46 +0000639 case AbsoluteIntent:
640 return 3;
glennrp0fe50b42010-11-16 03:52:51 +0000641
glennrpe610a072010-08-05 17:08:46 +0000642 default:
643 return -1;
644 }
645}
646
647static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +0000648Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +0000649{
glennrpcf002022011-01-30 02:38:15 +0000650 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +0000651 {
652 case 0:
653 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000654
glennrpe610a072010-08-05 17:08:46 +0000655 case 1:
656 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000657
glennrpe610a072010-08-05 17:08:46 +0000658 case 2:
659 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000660
glennrpe610a072010-08-05 17:08:46 +0000661 case 3:
662 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000663
glennrpe610a072010-08-05 17:08:46 +0000664 default:
665 return UndefinedIntent;
666 }
667}
668
cristybb503372010-05-27 20:51:26 +0000669static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000670{
671 if (x > y)
672 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000673
cristy3ed852e2009-09-05 21:47:34 +0000674 return(y);
675}
glennrp0c3e06b2010-11-19 13:45:02 +0000676
cristybb503372010-05-27 20:51:26 +0000677static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000678{
679 if (x < y)
680 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000681
cristy3ed852e2009-09-05 21:47:34 +0000682 return(y);
683}
glennrp0c3e06b2010-11-19 13:45:02 +0000684
cristy3ed852e2009-09-05 21:47:34 +0000685
686/*
687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
688% %
689% %
690% %
691% I m a g e I s G r a y %
692% %
693% %
694% %
695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
696% %
697% Like IsGrayImage except does not change DirectClass to PseudoClass %
698% %
699%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
700*/
701static MagickBooleanType ImageIsGray(Image *image)
702{
703 register const PixelPacket
704 *p;
705
cristybb503372010-05-27 20:51:26 +0000706 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000707 i,
708 x,
709 y;
710
711 assert(image != (Image *) NULL);
712 assert(image->signature == MagickSignature);
713 if (image->debug != MagickFalse)
714 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
715
716 if (image->storage_class == PseudoClass)
717 {
cristybb503372010-05-27 20:51:26 +0000718 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000719 if (IsGray(image->colormap+i) == MagickFalse)
720 return(MagickFalse);
721 return(MagickTrue);
722 }
cristybb503372010-05-27 20:51:26 +0000723 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000724 {
725 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
726 if (p == (const PixelPacket *) NULL)
727 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000728 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +0000729 {
730 if (IsGray(p) == MagickFalse)
731 return(MagickFalse);
732 p++;
733 }
734 }
735 return(MagickTrue);
736}
glennrpd5045b42010-03-24 12:40:35 +0000737#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000738#endif /* MAGICKCORE_PNG_DELEGATE */
739
740/*
741%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
742% %
743% %
744% %
745% I s M N G %
746% %
747% %
748% %
749%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
750%
751% IsMNG() returns MagickTrue if the image format type, identified by the
752% magick string, is MNG.
753%
754% The format of the IsMNG method is:
755%
756% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
757%
758% A description of each parameter follows:
759%
760% o magick: compare image format pattern against these bytes.
761%
762% o length: Specifies the length of the magick string.
763%
764%
765*/
766static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
767{
768 if (length < 8)
769 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000770
cristy3ed852e2009-09-05 21:47:34 +0000771 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
772 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000773
cristy3ed852e2009-09-05 21:47:34 +0000774 return(MagickFalse);
775}
776
777/*
778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
779% %
780% %
781% %
782% I s J N G %
783% %
784% %
785% %
786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
787%
788% IsJNG() returns MagickTrue if the image format type, identified by the
789% magick string, is JNG.
790%
791% The format of the IsJNG method is:
792%
793% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
794%
795% A description of each parameter follows:
796%
797% o magick: compare image format pattern against these bytes.
798%
799% o length: Specifies the length of the magick string.
800%
801%
802*/
803static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
804{
805 if (length < 8)
806 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000807
cristy3ed852e2009-09-05 21:47:34 +0000808 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
809 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000810
cristy3ed852e2009-09-05 21:47:34 +0000811 return(MagickFalse);
812}
813
814/*
815%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
816% %
817% %
818% %
819% I s P N G %
820% %
821% %
822% %
823%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
824%
825% IsPNG() returns MagickTrue if the image format type, identified by the
826% magick string, is PNG.
827%
828% The format of the IsPNG method is:
829%
830% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
831%
832% A description of each parameter follows:
833%
834% o magick: compare image format pattern against these bytes.
835%
836% o length: Specifies the length of the magick string.
837%
838*/
839static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
840{
841 if (length < 8)
842 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000843
cristy3ed852e2009-09-05 21:47:34 +0000844 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
845 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000846
cristy3ed852e2009-09-05 21:47:34 +0000847 return(MagickFalse);
848}
849
850#if defined(MAGICKCORE_PNG_DELEGATE)
851#if defined(__cplusplus) || defined(c_plusplus)
852extern "C" {
853#endif
854
glennrpd5045b42010-03-24 12:40:35 +0000855#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +0000856static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +0000857{
858 unsigned char
859 buffer[4];
860
861 assert(image != (Image *) NULL);
862 assert(image->signature == MagickSignature);
863 buffer[0]=(unsigned char) (value >> 24);
864 buffer[1]=(unsigned char) (value >> 16);
865 buffer[2]=(unsigned char) (value >> 8);
866 buffer[3]=(unsigned char) value;
867 return((size_t) WriteBlob(image,4,buffer));
868}
869
870static void PNGLong(png_bytep p,png_uint_32 value)
871{
872 *p++=(png_byte) ((value >> 24) & 0xff);
873 *p++=(png_byte) ((value >> 16) & 0xff);
874 *p++=(png_byte) ((value >> 8) & 0xff);
875 *p++=(png_byte) (value & 0xff);
876}
877
glennrpa521b2f2010-10-29 04:11:03 +0000878#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +0000879static void PNGsLong(png_bytep p,png_int_32 value)
880{
881 *p++=(png_byte) ((value >> 24) & 0xff);
882 *p++=(png_byte) ((value >> 16) & 0xff);
883 *p++=(png_byte) ((value >> 8) & 0xff);
884 *p++=(png_byte) (value & 0xff);
885}
glennrpa521b2f2010-10-29 04:11:03 +0000886#endif
cristy3ed852e2009-09-05 21:47:34 +0000887
888static void PNGShort(png_bytep p,png_uint_16 value)
889{
890 *p++=(png_byte) ((value >> 8) & 0xff);
891 *p++=(png_byte) (value & 0xff);
892}
893
894static void PNGType(png_bytep p,png_bytep type)
895{
896 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
897}
898
glennrp03812ae2010-12-24 01:31:34 +0000899static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
900 size_t length)
cristy3ed852e2009-09-05 21:47:34 +0000901{
902 if (logging != MagickFalse)
903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000904 " Writing %c%c%c%c chunk, length: %.20g",
905 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000906}
glennrpd5045b42010-03-24 12:40:35 +0000907#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000908
909#if defined(__cplusplus) || defined(c_plusplus)
910}
911#endif
912
glennrpd5045b42010-03-24 12:40:35 +0000913#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000914/*
915%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
916% %
917% %
918% %
919% R e a d P N G I m a g e %
920% %
921% %
922% %
923%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
924%
925% ReadPNGImage() reads a Portable Network Graphics (PNG) or
926% Multiple-image Network Graphics (MNG) image file and returns it. It
927% allocates the memory necessary for the new Image structure and returns a
928% pointer to the new image or set of images.
929%
930% MNG support written by Glenn Randers-Pehrson, glennrp@image...
931%
932% The format of the ReadPNGImage method is:
933%
934% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
935%
936% A description of each parameter follows:
937%
938% o image_info: the image info.
939%
940% o exception: return any errors or warnings in this structure.
941%
942% To do, more or less in chronological order (as of version 5.5.2,
943% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
944%
945% Get 16-bit cheap transparency working.
946%
947% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
948%
949% Preserve all unknown and not-yet-handled known chunks found in input
950% PNG file and copy them into output PNG files according to the PNG
951% copying rules.
952%
953% (At this point, PNG encoding should be in full MNG compliance)
954%
955% Provide options for choice of background to use when the MNG BACK
956% chunk is not present or is not mandatory (i.e., leave transparent,
957% user specified, MNG BACK, PNG bKGD)
958%
959% Implement LOOP/ENDL [done, but could do discretionary loops more
960% efficiently by linking in the duplicate frames.].
961%
962% Decode and act on the MHDR simplicity profile (offer option to reject
963% files or attempt to process them anyway when the profile isn't LC or VLC).
964%
965% Upgrade to full MNG without Delta-PNG.
966%
967% o BACK [done a while ago except for background image ID]
968% o MOVE [done 15 May 1999]
969% o CLIP [done 15 May 1999]
970% o DISC [done 19 May 1999]
971% o SAVE [partially done 19 May 1999 (marks objects frozen)]
972% o SEEK [partially done 19 May 1999 (discard function only)]
973% o SHOW
974% o PAST
975% o BASI
976% o MNG-level tEXt/iTXt/zTXt
977% o pHYg
978% o pHYs
979% o sBIT
980% o bKGD
981% o iTXt (wait for libpng implementation).
982%
983% Use the scene signature to discover when an identical scene is
984% being reused, and just point to the original image->exception instead
985% of storing another set of pixels. This not specific to MNG
986% but could be applied generally.
987%
988% Upgrade to full MNG with Delta-PNG.
989%
990% JNG tEXt/iTXt/zTXt
991%
992% We will not attempt to read files containing the CgBI chunk.
993% They are really Xcode files meant for display on the iPhone.
994% These are not valid PNG files and it is impossible to recover
995% the orginal PNG from files that have been converted to Xcode-PNG,
996% since irretrievable loss of color data has occurred due to the
997% use of premultiplied alpha.
998*/
999
1000#if defined(__cplusplus) || defined(c_plusplus)
1001extern "C" {
1002#endif
1003
1004/*
1005 This the function that does the actual reading of data. It is
1006 the same as the one supplied in libpng, except that it receives the
1007 datastream from the ReadBlob() function instead of standard input.
1008*/
1009static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1010{
1011 Image
1012 *image;
1013
1014 image=(Image *) png_get_io_ptr(png_ptr);
1015 if (length)
1016 {
1017 png_size_t
1018 check;
1019
1020 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1021 if (check != length)
1022 {
1023 char
1024 msg[MaxTextExtent];
1025
1026 (void) FormatMagickString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001027 "Expected %.20g bytes; found %.20g bytes",(double) length,
1028 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001029 png_warning(png_ptr,msg);
1030 png_error(png_ptr,"Read Exception");
1031 }
1032 }
1033}
1034
1035#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1036 !defined(PNG_MNG_FEATURES_SUPPORTED)
1037/* We use mng_get_data() instead of png_get_data() if we have a libpng
1038 * older than libpng-1.0.3a, which was the first to allow the empty
1039 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1040 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1041 * encountered after an empty PLTE, so we have to look ahead for bKGD
1042 * chunks and remove them from the datastream that is passed to libpng,
1043 * and store their contents for later use.
1044 */
1045static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1046{
1047 MngInfo
1048 *mng_info;
1049
1050 Image
1051 *image;
1052
1053 png_size_t
1054 check;
1055
cristybb503372010-05-27 20:51:26 +00001056 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001057 i;
1058
1059 i=0;
1060 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1061 image=(Image *) mng_info->image;
1062 while (mng_info->bytes_in_read_buffer && length)
1063 {
1064 data[i]=mng_info->read_buffer[i];
1065 mng_info->bytes_in_read_buffer--;
1066 length--;
1067 i++;
1068 }
1069 if (length)
1070 {
1071 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001072
cristy3ed852e2009-09-05 21:47:34 +00001073 if (check != length)
1074 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001075
cristy3ed852e2009-09-05 21:47:34 +00001076 if (length == 4)
1077 {
1078 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1079 (data[3] == 0))
1080 {
1081 check=(png_size_t) ReadBlob(image,(size_t) length,
1082 (char *) mng_info->read_buffer);
1083 mng_info->read_buffer[4]=0;
1084 mng_info->bytes_in_read_buffer=4;
1085 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1086 mng_info->found_empty_plte=MagickTrue;
1087 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1088 {
1089 mng_info->found_empty_plte=MagickFalse;
1090 mng_info->have_saved_bkgd_index=MagickFalse;
1091 }
1092 }
glennrp0fe50b42010-11-16 03:52:51 +00001093
cristy3ed852e2009-09-05 21:47:34 +00001094 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1095 (data[3] == 1))
1096 {
1097 check=(png_size_t) ReadBlob(image,(size_t) length,
1098 (char *) mng_info->read_buffer);
1099 mng_info->read_buffer[4]=0;
1100 mng_info->bytes_in_read_buffer=4;
1101 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1102 if (mng_info->found_empty_plte)
1103 {
1104 /*
1105 Skip the bKGD data byte and CRC.
1106 */
1107 check=(png_size_t)
1108 ReadBlob(image,5,(char *) mng_info->read_buffer);
1109 check=(png_size_t) ReadBlob(image,(size_t) length,
1110 (char *) mng_info->read_buffer);
1111 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1112 mng_info->have_saved_bkgd_index=MagickTrue;
1113 mng_info->bytes_in_read_buffer=0;
1114 }
1115 }
1116 }
1117 }
1118}
1119#endif
1120
1121static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1122{
1123 Image
1124 *image;
1125
1126 image=(Image *) png_get_io_ptr(png_ptr);
1127 if (length)
1128 {
1129 png_size_t
1130 check;
1131
cristybb503372010-05-27 20:51:26 +00001132 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001133
cristy3ed852e2009-09-05 21:47:34 +00001134 if (check != length)
1135 png_error(png_ptr,"WriteBlob Failed");
1136 }
1137}
1138
1139static void png_flush_data(png_structp png_ptr)
1140{
1141 (void) png_ptr;
1142}
1143
1144#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1145static int PalettesAreEqual(Image *a,Image *b)
1146{
cristybb503372010-05-27 20:51:26 +00001147 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001148 i;
1149
1150 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1151 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001152
cristy3ed852e2009-09-05 21:47:34 +00001153 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1154 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001155
cristy3ed852e2009-09-05 21:47:34 +00001156 if (a->colors != b->colors)
1157 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001158
cristybb503372010-05-27 20:51:26 +00001159 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001160 {
1161 if ((a->colormap[i].red != b->colormap[i].red) ||
1162 (a->colormap[i].green != b->colormap[i].green) ||
1163 (a->colormap[i].blue != b->colormap[i].blue))
1164 return((int) MagickFalse);
1165 }
glennrp0fe50b42010-11-16 03:52:51 +00001166
cristy3ed852e2009-09-05 21:47:34 +00001167 return((int) MagickTrue);
1168}
1169#endif
1170
1171static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1172{
1173 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1174 mng_info->exists[i] && !mng_info->frozen[i])
1175 {
1176#ifdef MNG_OBJECT_BUFFERS
1177 if (mng_info->ob[i] != (MngBuffer *) NULL)
1178 {
1179 if (mng_info->ob[i]->reference_count > 0)
1180 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001181
cristy3ed852e2009-09-05 21:47:34 +00001182 if (mng_info->ob[i]->reference_count == 0)
1183 {
1184 if (mng_info->ob[i]->image != (Image *) NULL)
1185 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001186
cristy3ed852e2009-09-05 21:47:34 +00001187 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1188 }
1189 }
1190 mng_info->ob[i]=(MngBuffer *) NULL;
1191#endif
1192 mng_info->exists[i]=MagickFalse;
1193 mng_info->invisible[i]=MagickFalse;
1194 mng_info->viewable[i]=MagickFalse;
1195 mng_info->frozen[i]=MagickFalse;
1196 mng_info->x_off[i]=0;
1197 mng_info->y_off[i]=0;
1198 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001199 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001200 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001201 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001202 }
1203}
1204
glennrp21f0e622011-01-07 16:20:57 +00001205static void MngInfoFreeStruct(MngInfo *mng_info,
1206 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001207{
glennrp21f0e622011-01-07 16:20:57 +00001208 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001209 {
cristybb503372010-05-27 20:51:26 +00001210 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001211 i;
1212
1213 for (i=1; i < MNG_MAX_OBJECTS; i++)
1214 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001215
cristy3ed852e2009-09-05 21:47:34 +00001216 if (mng_info->global_plte != (png_colorp) NULL)
1217 mng_info->global_plte=(png_colorp)
1218 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001219
cristy3ed852e2009-09-05 21:47:34 +00001220 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1221 *have_mng_structure=MagickFalse;
1222 }
1223}
1224
1225static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1226{
1227 MngBox
1228 box;
1229
1230 box=box1;
1231 if (box.left < box2.left)
1232 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001233
cristy3ed852e2009-09-05 21:47:34 +00001234 if (box.top < box2.top)
1235 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (box.right > box2.right)
1238 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 if (box.bottom > box2.bottom)
1241 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001242
cristy3ed852e2009-09-05 21:47:34 +00001243 return box;
1244}
1245
1246static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1247{
1248 MngBox
1249 box;
1250
1251 /*
1252 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1253 */
cristybb503372010-05-27 20:51:26 +00001254 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1255 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1256 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1257 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001258 if (delta_type != 0)
1259 {
1260 box.left+=previous_box.left;
1261 box.right+=previous_box.right;
1262 box.top+=previous_box.top;
1263 box.bottom+=previous_box.bottom;
1264 }
glennrp0fe50b42010-11-16 03:52:51 +00001265
cristy3ed852e2009-09-05 21:47:34 +00001266 return(box);
1267}
1268
1269static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1270 unsigned char *p)
1271{
1272 MngPair
1273 pair;
1274 /*
cristybb503372010-05-27 20:51:26 +00001275 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001276 */
cristy8182b072010-05-30 20:10:53 +00001277 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1278 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001279
cristy3ed852e2009-09-05 21:47:34 +00001280 if (delta_type != 0)
1281 {
1282 pair.a+=previous_pair.a;
1283 pair.b+=previous_pair.b;
1284 }
glennrp0fe50b42010-11-16 03:52:51 +00001285
cristy3ed852e2009-09-05 21:47:34 +00001286 return(pair);
1287}
1288
cristy8182b072010-05-30 20:10:53 +00001289static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001290{
cristy8182b072010-05-30 20:10:53 +00001291 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001292}
1293
glennrpcf002022011-01-30 02:38:15 +00001294static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001295{
1296 Image
1297 *image;
1298
1299 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001300
cristy3ed852e2009-09-05 21:47:34 +00001301 if (image->debug != MagickFalse)
1302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1303 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001304
cristy3ed852e2009-09-05 21:47:34 +00001305 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1306 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001307
glennrpe4017e32011-01-08 17:16:09 +00001308#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001309 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1310 * are building with libpng-1.4.x and can be ignored.
1311 */
cristy3ed852e2009-09-05 21:47:34 +00001312 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001313#else
1314 png_longjmp(ping,1);
1315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316}
1317
glennrpcf002022011-01-30 02:38:15 +00001318static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001319{
1320 Image
1321 *image;
1322
1323 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1324 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001325
cristy3ed852e2009-09-05 21:47:34 +00001326 image=(Image *) png_get_error_ptr(ping);
1327 if (image->debug != MagickFalse)
1328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001329 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001330
cristy3ed852e2009-09-05 21:47:34 +00001331 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1332 message,"`%s'",image->filename);
1333}
1334
1335#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001336static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001337{
1338#if (PNG_LIBPNG_VER < 10011)
1339 png_voidp
1340 ret;
1341
1342 png_ptr=png_ptr;
1343 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001344
cristy3ed852e2009-09-05 21:47:34 +00001345 if (ret == NULL)
1346 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001347
cristy3ed852e2009-09-05 21:47:34 +00001348 return(ret);
1349#else
1350 png_ptr=png_ptr;
1351 return((png_voidp) AcquireMagickMemory((size_t) size));
1352#endif
1353}
1354
1355/*
1356 Free a pointer. It is removed from the list at the same time.
1357*/
glennrpcf002022011-01-30 02:38:15 +00001358static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001359{
1360 png_ptr=png_ptr;
1361 ptr=RelinquishMagickMemory(ptr);
1362 return((png_free_ptr) NULL);
1363}
1364#endif
1365
1366#if defined(__cplusplus) || defined(c_plusplus)
1367}
1368#endif
1369
1370static int
glennrpcf002022011-01-30 02:38:15 +00001371Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001372 png_textp text,int ii)
1373{
cristybb503372010-05-27 20:51:26 +00001374 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001375 i;
1376
1377 register unsigned char
1378 *dp;
1379
1380 register png_charp
1381 sp;
1382
1383 png_uint_32
1384 length,
1385 nibbles;
1386
1387 StringInfo
1388 *profile;
1389
glennrp0c3e06b2010-11-19 13:45:02 +00001390 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001391 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1392 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1393 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1394 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1395 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1396 13,14,15};
1397
1398 sp=text[ii].text+1;
1399 /* look for newline */
1400 while (*sp != '\n')
1401 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001402
cristy3ed852e2009-09-05 21:47:34 +00001403 /* look for length */
1404 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1405 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001406
cristyf2f27272009-12-17 14:48:46 +00001407 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001408
glennrp97f90e22011-02-22 05:47:58 +00001409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1410 " length: %lu",(unsigned long) length);
1411
cristy3ed852e2009-09-05 21:47:34 +00001412 while (*sp != ' ' && *sp != '\n')
1413 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001414
cristy3ed852e2009-09-05 21:47:34 +00001415 /* allocate space */
1416 if (length == 0)
1417 {
1418 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1419 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1420 return(MagickFalse);
1421 }
glennrp0fe50b42010-11-16 03:52:51 +00001422
cristy3ed852e2009-09-05 21:47:34 +00001423 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001424
cristy3ed852e2009-09-05 21:47:34 +00001425 if (profile == (StringInfo *) NULL)
1426 {
1427 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1428 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1429 "unable to copy profile");
1430 return(MagickFalse);
1431 }
glennrp0fe50b42010-11-16 03:52:51 +00001432
cristy3ed852e2009-09-05 21:47:34 +00001433 /* copy profile, skipping white space and column 1 "=" signs */
1434 dp=GetStringInfoDatum(profile);
1435 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001436
cristybb503372010-05-27 20:51:26 +00001437 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001438 {
1439 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1440 {
1441 if (*sp == '\0')
1442 {
1443 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1444 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1445 profile=DestroyStringInfo(profile);
1446 return(MagickFalse);
1447 }
1448 sp++;
1449 }
glennrp0fe50b42010-11-16 03:52:51 +00001450
cristy3ed852e2009-09-05 21:47:34 +00001451 if (i%2 == 0)
1452 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001453
cristy3ed852e2009-09-05 21:47:34 +00001454 else
1455 (*dp++)+=unhex[(int) *sp++];
1456 }
1457 /*
1458 We have already read "Raw profile type.
1459 */
1460 (void) SetImageProfile(image,&text[ii].key[17],profile);
1461 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001462
cristy3ed852e2009-09-05 21:47:34 +00001463 if (image_info->verbose)
1464 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001465
cristy3ed852e2009-09-05 21:47:34 +00001466 return MagickTrue;
1467}
1468
1469#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1470static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1471{
1472 Image
1473 *image;
1474
1475
1476 /* The unknown chunk structure contains the chunk data:
1477 png_byte name[5];
1478 png_byte *data;
1479 png_size_t size;
1480
1481 Note that libpng has already taken care of the CRC handling.
1482 */
1483
1484
1485 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1486 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1487 return(0); /* Did not recognize */
1488
1489 /* recognized vpAg */
1490
1491 if (chunk->size != 9)
1492 return(-1); /* Error return */
1493
1494 if (chunk->data[8] != 0)
1495 return(0); /* ImageMagick requires pixel units */
1496
1497 image=(Image *) png_get_user_chunk_ptr(ping);
1498
cristybb503372010-05-27 20:51:26 +00001499 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001500 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristybb503372010-05-27 20:51:26 +00001502 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001503 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1504
1505 /* Return one of the following: */
1506 /* return(-n); chunk had an error */
1507 /* return(0); did not recognize */
1508 /* return(n); success */
1509
1510 return(1);
1511
1512}
1513#endif
1514
1515/*
1516%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1517% %
1518% %
1519% %
1520% R e a d O n e P N G I m a g e %
1521% %
1522% %
1523% %
1524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1525%
1526% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1527% (minus the 8-byte signature) and returns it. It allocates the memory
1528% necessary for the new Image structure and returns a pointer to the new
1529% image.
1530%
1531% The format of the ReadOnePNGImage method is:
1532%
1533% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1534% ExceptionInfo *exception)
1535%
1536% A description of each parameter follows:
1537%
1538% o mng_info: Specifies a pointer to a MngInfo structure.
1539%
1540% o image_info: the image info.
1541%
1542% o exception: return any errors or warnings in this structure.
1543%
1544*/
1545static Image *ReadOnePNGImage(MngInfo *mng_info,
1546 const ImageInfo *image_info, ExceptionInfo *exception)
1547{
1548 /* Read one PNG image */
1549
glennrpcc95c3f2011-04-18 16:46:48 +00001550 /* To do: Read the tIME chunk into the date:modify property */
1551 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1552
cristy3ed852e2009-09-05 21:47:34 +00001553 Image
1554 *image;
1555
1556 int
glennrp4eb39312011-03-30 21:34:55 +00001557 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001558 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001559 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001560 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001561 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001562 pass,
1563 ping_bit_depth,
1564 ping_color_type,
1565 ping_interlace_method,
1566 ping_compression_method,
1567 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001568 ping_num_trans,
1569 unit_type;
1570
1571 double
1572 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001573
glennrpa6a06632011-01-19 15:15:34 +00001574 LongPixelPacket
1575 transparent_color;
1576
cristy3ed852e2009-09-05 21:47:34 +00001577 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00001578 logging,
cristy3ed852e2009-09-05 21:47:34 +00001579 status;
1580
glennrpfaa852b2010-03-30 12:17:00 +00001581 png_bytep
1582 ping_trans_alpha;
1583
1584 png_color_16p
1585 ping_background,
1586 ping_trans_color;
1587
cristy3ed852e2009-09-05 21:47:34 +00001588 png_info
1589 *end_info,
1590 *ping_info;
1591
1592 png_struct
1593 *ping;
1594
1595 png_textp
1596 text;
1597
glennrpfaa852b2010-03-30 12:17:00 +00001598 png_uint_32
1599 ping_height,
1600 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00001601 ping_rowbytes,
1602 x_resolution,
1603 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00001604
cristy3ed852e2009-09-05 21:47:34 +00001605 QuantumInfo
1606 *quantum_info;
1607
1608 unsigned char
glennrpcf002022011-01-30 02:38:15 +00001609 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001610
cristybb503372010-05-27 20:51:26 +00001611 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001612 y;
1613
1614 register unsigned char
1615 *p;
1616
1617 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00001618 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00001619
cristybb503372010-05-27 20:51:26 +00001620 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001621 i,
1622 x;
1623
1624 register PixelPacket
1625 *q;
1626
1627 size_t
glennrp39992b42010-11-14 00:03:43 +00001628 length,
cristy3ed852e2009-09-05 21:47:34 +00001629 row_offset;
1630
cristyeb3b22a2011-03-31 20:16:11 +00001631 ssize_t
1632 j;
1633
cristy3ed852e2009-09-05 21:47:34 +00001634#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1635 png_byte unused_chunks[]=
1636 {
1637 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1638 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1639 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1640 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1641 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1642 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1643 };
1644#endif
1645
1646 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001647 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00001648
1649#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001650 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001651#endif
1652
glennrp25c1e2b2010-03-25 01:39:56 +00001653#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00001654 if (image_info->verbose)
1655 printf("Your PNG library (libpng-%s) is rather old.\n",
1656 PNG_LIBPNG_VER_STRING);
1657#endif
1658
glennrp61b4c952009-11-10 20:40:41 +00001659#if (PNG_LIBPNG_VER >= 10400)
1660# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1661 if (image_info->verbose)
1662 {
1663 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1664 PNG_LIBPNG_VER_STRING);
1665 printf("Please update it.\n");
1666 }
1667# endif
1668#endif
1669
1670
cristyed552522009-10-16 14:04:35 +00001671 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00001672 image=mng_info->image;
1673
glennrpa6a06632011-01-19 15:15:34 +00001674 if (logging != MagickFalse)
1675 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1676 " image->matte=%d",(int) image->matte);
1677
glennrp0e319732011-01-25 21:53:13 +00001678 /* Set to an out-of-range color unless tRNS chunk is present */
1679 transparent_color.red=65537;
1680 transparent_color.green=65537;
1681 transparent_color.blue=65537;
1682 transparent_color.opacity=65537;
1683
glennrpcb395ac2011-03-30 19:50:23 +00001684 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00001685 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00001686 num_raw_profiles = 0;
1687
cristy3ed852e2009-09-05 21:47:34 +00001688 /*
1689 Allocate the PNG structures
1690 */
1691#ifdef PNG_USER_MEM_SUPPORTED
1692 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00001693 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1694 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00001695#else
1696 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00001697 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00001698#endif
1699 if (ping == (png_struct *) NULL)
1700 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00001701
cristy3ed852e2009-09-05 21:47:34 +00001702 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001703
cristy3ed852e2009-09-05 21:47:34 +00001704 if (ping_info == (png_info *) NULL)
1705 {
1706 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1707 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1708 }
glennrp0fe50b42010-11-16 03:52:51 +00001709
cristy3ed852e2009-09-05 21:47:34 +00001710 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001711
cristy3ed852e2009-09-05 21:47:34 +00001712 if (end_info == (png_info *) NULL)
1713 {
1714 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1715 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1716 }
glennrp0fe50b42010-11-16 03:52:51 +00001717
glennrpcf002022011-01-30 02:38:15 +00001718 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00001719
glennrpfaa852b2010-03-30 12:17:00 +00001720 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00001721 {
1722 /*
1723 PNG image is corrupt.
1724 */
1725 png_destroy_read_struct(&ping,&ping_info,&end_info);
1726#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001727 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001728#endif
1729 if (logging != MagickFalse)
1730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1731 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00001732
cristy3ed852e2009-09-05 21:47:34 +00001733 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00001734 {
1735 InheritException(exception,&image->exception);
1736 image->columns=0;
1737 }
glennrp0fe50b42010-11-16 03:52:51 +00001738
cristy3ed852e2009-09-05 21:47:34 +00001739 return(GetFirstImageInList(image));
1740 }
1741 /*
1742 Prepare PNG for reading.
1743 */
glennrpfaa852b2010-03-30 12:17:00 +00001744
cristy3ed852e2009-09-05 21:47:34 +00001745 mng_info->image_found++;
1746 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00001747
cristy3ed852e2009-09-05 21:47:34 +00001748 if (LocaleCompare(image_info->magick,"MNG") == 0)
1749 {
1750#if defined(PNG_MNG_FEATURES_SUPPORTED)
1751 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1752 png_set_read_fn(ping,image,png_get_data);
1753#else
1754#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1755 png_permit_empty_plte(ping,MagickTrue);
1756 png_set_read_fn(ping,image,png_get_data);
1757#else
1758 mng_info->image=image;
1759 mng_info->bytes_in_read_buffer=0;
1760 mng_info->found_empty_plte=MagickFalse;
1761 mng_info->have_saved_bkgd_index=MagickFalse;
1762 png_set_read_fn(ping,mng_info,mng_get_data);
1763#endif
1764#endif
1765 }
glennrp0fe50b42010-11-16 03:52:51 +00001766
cristy3ed852e2009-09-05 21:47:34 +00001767 else
1768 png_set_read_fn(ping,image,png_get_data);
1769
1770#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1771 /* Ignore unused chunks and all unknown chunks except for vpAg */
1772 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1773 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1774 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1775 (int)sizeof(unused_chunks)/5);
1776 /* Callback for other unknown chunks */
1777 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1778#endif
1779
glennrp991e92a2010-01-28 03:09:00 +00001780#if (PNG_LIBPNG_VER < 10400)
1781# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1782 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00001783 /* Disable thread-unsafe features of pnggccrd */
1784 if (png_access_version_number() >= 10200)
1785 {
1786 png_uint_32 mmx_disable_mask=0;
1787 png_uint_32 asm_flags;
1788
1789 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1790 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1791 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1792 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1793 asm_flags=png_get_asm_flags(ping);
1794 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1795 }
glennrp991e92a2010-01-28 03:09:00 +00001796# endif
cristy3ed852e2009-09-05 21:47:34 +00001797#endif
1798
1799 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00001800
1801 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1802 &ping_bit_depth,&ping_color_type,
1803 &ping_interlace_method,&ping_compression_method,
1804 &ping_filter_method);
1805
1806 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1807 &ping_trans_color);
1808
1809 (void) png_get_bKGD(ping, ping_info, &ping_background);
1810
1811 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00001812 {
glennrpfaa852b2010-03-30 12:17:00 +00001813 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1814 {
1815 png_set_packing(ping);
1816 ping_bit_depth = 8;
1817 }
cristy3ed852e2009-09-05 21:47:34 +00001818 }
glennrpfaa852b2010-03-30 12:17:00 +00001819
1820 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00001821 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00001822 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00001823 if (logging != MagickFalse)
1824 {
1825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001826 " PNG width: %.20g, height: %.20g",
1827 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00001828
cristy3ed852e2009-09-05 21:47:34 +00001829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1830 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001831 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00001832
cristy3ed852e2009-09-05 21:47:34 +00001833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1834 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001835 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00001836
cristy3ed852e2009-09-05 21:47:34 +00001837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1838 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001839 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00001840 }
1841
glennrpfaa852b2010-03-30 12:17:00 +00001842#ifdef PNG_READ_iCCP_SUPPORTED
1843 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00001844 {
1845 int
1846 compression;
1847
glennrpe4017e32011-01-08 17:16:09 +00001848#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00001849 png_charp
glennrpe4017e32011-01-08 17:16:09 +00001850 info;
1851#else
1852 png_bytep
1853 info;
1854#endif
1855
1856 png_charp
cristy3ed852e2009-09-05 21:47:34 +00001857 name;
1858
1859 png_uint_32
1860 profile_length;
1861
1862 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1863 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00001864
cristy3ed852e2009-09-05 21:47:34 +00001865 if (profile_length != 0)
1866 {
1867 StringInfo
1868 *profile;
1869
1870 if (logging != MagickFalse)
1871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1872 " Reading PNG iCCP chunk.");
1873 profile=AcquireStringInfo(profile_length);
1874 SetStringInfoDatum(profile,(const unsigned char *) info);
1875 (void) SetImageProfile(image,"icc",profile);
1876 profile=DestroyStringInfo(profile);
1877 }
1878 }
1879#endif
1880#if defined(PNG_READ_sRGB_SUPPORTED)
1881 {
cristy3ed852e2009-09-05 21:47:34 +00001882 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00001883 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1884 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00001885
cristy3ed852e2009-09-05 21:47:34 +00001886 if (png_get_sRGB(ping,ping_info,&intent))
1887 {
glennrpcf002022011-01-30 02:38:15 +00001888 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1889 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00001890
cristy3ed852e2009-09-05 21:47:34 +00001891 if (logging != MagickFalse)
1892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00001893 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00001894 }
1895 }
1896#endif
1897 {
glennrpfaa852b2010-03-30 12:17:00 +00001898 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1899 if (mng_info->have_global_gama)
1900 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00001901
cristy3ed852e2009-09-05 21:47:34 +00001902 if (png_get_gAMA(ping,ping_info,&file_gamma))
1903 {
1904 image->gamma=(float) file_gamma;
1905 if (logging != MagickFalse)
1906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1907 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1908 }
1909 }
glennrpfaa852b2010-03-30 12:17:00 +00001910 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1911 {
1912 if (mng_info->have_global_chrm != MagickFalse)
1913 {
1914 (void) png_set_cHRM(ping,ping_info,
1915 mng_info->global_chrm.white_point.x,
1916 mng_info->global_chrm.white_point.y,
1917 mng_info->global_chrm.red_primary.x,
1918 mng_info->global_chrm.red_primary.y,
1919 mng_info->global_chrm.green_primary.x,
1920 mng_info->global_chrm.green_primary.y,
1921 mng_info->global_chrm.blue_primary.x,
1922 mng_info->global_chrm.blue_primary.y);
1923 }
1924 }
glennrp0fe50b42010-11-16 03:52:51 +00001925
glennrpfaa852b2010-03-30 12:17:00 +00001926 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00001927 {
1928 (void) png_get_cHRM(ping,ping_info,
1929 &image->chromaticity.white_point.x,
1930 &image->chromaticity.white_point.y,
1931 &image->chromaticity.red_primary.x,
1932 &image->chromaticity.red_primary.y,
1933 &image->chromaticity.green_primary.x,
1934 &image->chromaticity.green_primary.y,
1935 &image->chromaticity.blue_primary.x,
1936 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00001937
cristy3ed852e2009-09-05 21:47:34 +00001938 if (logging != MagickFalse)
1939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1940 " Reading PNG cHRM chunk.");
1941 }
glennrp0fe50b42010-11-16 03:52:51 +00001942
glennrpe610a072010-08-05 17:08:46 +00001943 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00001944 {
glennrpe610a072010-08-05 17:08:46 +00001945 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00001946 Magick_RenderingIntent_to_PNG_RenderingIntent
1947 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00001948 png_set_gAMA(ping,ping_info,0.45455f);
1949 png_set_cHRM(ping,ping_info,
1950 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1951 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00001952 }
cristy3ed852e2009-09-05 21:47:34 +00001953#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001954 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00001955 {
cristy905ef802011-02-23 00:29:18 +00001956 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1957 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00001958
cristy3ed852e2009-09-05 21:47:34 +00001959 if (logging != MagickFalse)
1960 if (image->page.x || image->page.y)
1961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001962 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1963 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001964 }
1965#endif
1966#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001967 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1968 {
1969 if (mng_info->have_global_phys)
1970 {
1971 png_set_pHYs(ping,ping_info,
1972 mng_info->global_x_pixels_per_unit,
1973 mng_info->global_y_pixels_per_unit,
1974 mng_info->global_phys_unit_type);
1975 }
1976 }
1977
1978 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00001979 {
cristy3ed852e2009-09-05 21:47:34 +00001980 /*
1981 Set image resolution.
1982 */
1983 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00001984 &unit_type);
1985 image->x_resolution=(double) x_resolution;
1986 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00001987
cristy3ed852e2009-09-05 21:47:34 +00001988 if (unit_type == PNG_RESOLUTION_METER)
1989 {
1990 image->units=PixelsPerCentimeterResolution;
1991 image->x_resolution=(double) x_resolution/100.0;
1992 image->y_resolution=(double) y_resolution/100.0;
1993 }
glennrp0fe50b42010-11-16 03:52:51 +00001994
cristy3ed852e2009-09-05 21:47:34 +00001995 if (logging != MagickFalse)
1996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001997 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1998 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00001999 }
cristy3ed852e2009-09-05 21:47:34 +00002000#endif
glennrp823b55c2011-03-14 18:46:46 +00002001
glennrpfaa852b2010-03-30 12:17:00 +00002002 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002003 {
2004 int
2005 number_colors;
2006
2007 png_colorp
2008 palette;
2009
2010 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002011
cristy3ed852e2009-09-05 21:47:34 +00002012 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002013 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002014 {
2015 if (mng_info->global_plte_length)
2016 {
2017 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2018 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002019
glennrpfaa852b2010-03-30 12:17:00 +00002020 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002021 if (mng_info->global_trns_length)
2022 {
2023 if (mng_info->global_trns_length >
2024 mng_info->global_plte_length)
2025 (void) ThrowMagickException(&image->exception,
2026 GetMagickModule(),CoderError,
2027 "global tRNS has more entries than global PLTE",
2028 "`%s'",image_info->filename);
2029 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2030 (int) mng_info->global_trns_length,NULL);
2031 }
glennrpbfd9e612011-04-22 14:02:20 +00002032#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002033 if (
2034#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2035 mng_info->have_saved_bkgd_index ||
2036#endif
glennrpfaa852b2010-03-30 12:17:00 +00002037 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002038 {
2039 png_color_16
2040 background;
2041
2042#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2043 if (mng_info->have_saved_bkgd_index)
2044 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002045#endif
glennrpfaa852b2010-03-30 12:17:00 +00002046 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2047 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002048
cristy3ed852e2009-09-05 21:47:34 +00002049 background.red=(png_uint_16)
2050 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002051
cristy3ed852e2009-09-05 21:47:34 +00002052 background.green=(png_uint_16)
2053 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002054
cristy3ed852e2009-09-05 21:47:34 +00002055 background.blue=(png_uint_16)
2056 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002057
glennrpc6c391a2011-04-27 02:23:56 +00002058 background.gray=(png_uint_16)
2059 mng_info->global_plte[background.index].green;
2060
cristy3ed852e2009-09-05 21:47:34 +00002061 png_set_bKGD(ping,ping_info,&background);
2062 }
2063#endif
2064 }
2065 else
2066 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2067 CoderError,"No global PLTE in file","`%s'",
2068 image_info->filename);
2069 }
2070 }
2071
glennrpbfd9e612011-04-22 14:02:20 +00002072#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002073 if (mng_info->have_global_bkgd &&
2074 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002075 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002076
glennrpfaa852b2010-03-30 12:17:00 +00002077 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002078 {
glennrpbfd9e612011-04-22 14:02:20 +00002079 unsigned int
2080 bkgd_scale;
2081
cristy3ed852e2009-09-05 21:47:34 +00002082 /*
2083 Set image background color.
2084 */
2085 if (logging != MagickFalse)
2086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2087 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002088
glennrpbfd9e612011-04-22 14:02:20 +00002089 /* Scale background components to 16-bit, then scale
2090 * to quantum depth
2091 */
2092 if (logging != MagickFalse)
2093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2094 " raw ping_background=(%d,%d,%d).",ping_background->red,
2095 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002096
glennrpbfd9e612011-04-22 14:02:20 +00002097 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002098
glennrpbfd9e612011-04-22 14:02:20 +00002099 if (ping_bit_depth == 1)
2100 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002101
glennrpbfd9e612011-04-22 14:02:20 +00002102 else if (ping_bit_depth == 2)
2103 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002104
glennrpbfd9e612011-04-22 14:02:20 +00002105 else if (ping_bit_depth == 4)
2106 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002107
glennrpbfd9e612011-04-22 14:02:20 +00002108 if (ping_bit_depth <= 8)
2109 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002110
glennrpbfd9e612011-04-22 14:02:20 +00002111 ping_background->red *= bkgd_scale;
2112 ping_background->green *= bkgd_scale;
2113 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002114
glennrpbfd9e612011-04-22 14:02:20 +00002115 if (logging != MagickFalse)
2116 {
glennrp2cbb4482010-06-02 04:37:24 +00002117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2118 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002119
glennrp2cbb4482010-06-02 04:37:24 +00002120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2121 " ping_background=(%d,%d,%d).",ping_background->red,
2122 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002123 }
glennrp2cbb4482010-06-02 04:37:24 +00002124
glennrpbfd9e612011-04-22 14:02:20 +00002125 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002126 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002127
glennrpbfd9e612011-04-22 14:02:20 +00002128 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002129 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002130
glennrpbfd9e612011-04-22 14:02:20 +00002131 image->background_color.blue=
2132 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002133
glennrpbfd9e612011-04-22 14:02:20 +00002134 image->background_color.opacity=OpaqueOpacity;
glennrp2cbb4482010-06-02 04:37:24 +00002135
glennrpbfd9e612011-04-22 14:02:20 +00002136 if (logging != MagickFalse)
2137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2138 " image->background_color=(%.20g,%.20g,%.20g).",
2139 (double) image->background_color.red,
2140 (double) image->background_color.green,
2141 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002142 }
glennrpbfd9e612011-04-22 14:02:20 +00002143#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002144
glennrpfaa852b2010-03-30 12:17:00 +00002145 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002146 {
2147 /*
glennrpa6a06632011-01-19 15:15:34 +00002148 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002149 */
2150 int
2151 max_sample;
2152
cristy35ef8242010-06-03 16:24:13 +00002153 size_t
2154 one=1;
2155
cristy3ed852e2009-09-05 21:47:34 +00002156 if (logging != MagickFalse)
2157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2158 " Reading PNG tRNS chunk.");
2159
cristyf9cca6a2010-06-04 23:49:28 +00002160 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002161
glennrpfaa852b2010-03-30 12:17:00 +00002162 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2163 (int)ping_trans_color->gray > max_sample) ||
2164 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2165 ((int)ping_trans_color->red > max_sample ||
2166 (int)ping_trans_color->green > max_sample ||
2167 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002168 {
2169 if (logging != MagickFalse)
2170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2171 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002172 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002173 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002174 image->matte=MagickFalse;
2175 }
2176 else
2177 {
glennrpa6a06632011-01-19 15:15:34 +00002178 int
2179 scale_to_short;
2180
2181 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2182
2183 /* Scale transparent_color to short */
2184 transparent_color.red= scale_to_short*ping_trans_color->red;
2185 transparent_color.green= scale_to_short*ping_trans_color->green;
2186 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2187 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002188
glennrpfaa852b2010-03-30 12:17:00 +00002189 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002190 {
glennrp0f111982010-07-07 20:18:33 +00002191 if (logging != MagickFalse)
2192 {
2193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2194 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002195
glennrp0f111982010-07-07 20:18:33 +00002196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2197 " scaled graylevel is %d.",transparent_color.opacity);
2198 }
cristy3ed852e2009-09-05 21:47:34 +00002199 transparent_color.red=transparent_color.opacity;
2200 transparent_color.green=transparent_color.opacity;
2201 transparent_color.blue=transparent_color.opacity;
2202 }
2203 }
2204 }
2205#if defined(PNG_READ_sBIT_SUPPORTED)
2206 if (mng_info->have_global_sbit)
2207 {
glennrpfaa852b2010-03-30 12:17:00 +00002208 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002209 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2210 }
2211#endif
2212 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002213
cristy3ed852e2009-09-05 21:47:34 +00002214 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002215
2216 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2217
cristy3ed852e2009-09-05 21:47:34 +00002218 /*
2219 Initialize image structure.
2220 */
2221 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002222 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002223 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002224 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002225 if (mng_info->mng_type == 0)
2226 {
glennrpfaa852b2010-03-30 12:17:00 +00002227 mng_info->mng_width=ping_width;
2228 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002229 mng_info->frame=mng_info->image_box;
2230 mng_info->clip=mng_info->image_box;
2231 }
glennrp0fe50b42010-11-16 03:52:51 +00002232
cristy3ed852e2009-09-05 21:47:34 +00002233 else
2234 {
2235 image->page.y=mng_info->y_off[mng_info->object_id];
2236 }
glennrp0fe50b42010-11-16 03:52:51 +00002237
cristy3ed852e2009-09-05 21:47:34 +00002238 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002239 image->columns=ping_width;
2240 image->rows=ping_height;
2241 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002242 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002243 {
cristybefe4d22010-06-07 01:18:58 +00002244 size_t
2245 one;
2246
cristy3ed852e2009-09-05 21:47:34 +00002247 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002248 one=1;
2249 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002250#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2251 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002252 image->colors=256;
2253#else
2254 if (image->colors > 65536L)
2255 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002256#endif
glennrpfaa852b2010-03-30 12:17:00 +00002257 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002258 {
2259 int
2260 number_colors;
2261
2262 png_colorp
2263 palette;
2264
2265 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002266 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002267
cristy3ed852e2009-09-05 21:47:34 +00002268 if (logging != MagickFalse)
2269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2270 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2271 }
2272 }
2273
2274 if (image->storage_class == PseudoClass)
2275 {
2276 /*
2277 Initialize image colormap.
2278 */
2279 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2280 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002281
glennrpfaa852b2010-03-30 12:17:00 +00002282 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002283 {
2284 int
2285 number_colors;
2286
2287 png_colorp
2288 palette;
2289
2290 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002291
glennrp6af6cf12011-04-22 13:05:16 +00002292 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002293 {
2294 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2295 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2296 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2297 }
glennrp6af6cf12011-04-22 13:05:16 +00002298
glennrp67b9c1a2011-04-22 18:47:36 +00002299 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002300 {
2301 image->colormap[i].red=0;
2302 image->colormap[i].green=0;
2303 image->colormap[i].blue=0;
2304 }
cristy3ed852e2009-09-05 21:47:34 +00002305 }
glennrp0fe50b42010-11-16 03:52:51 +00002306
cristy3ed852e2009-09-05 21:47:34 +00002307 else
2308 {
cristybb503372010-05-27 20:51:26 +00002309 size_t
cristy3ed852e2009-09-05 21:47:34 +00002310 scale;
2311
glennrpfaa852b2010-03-30 12:17:00 +00002312 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002313
cristy3ed852e2009-09-05 21:47:34 +00002314 if (scale < 1)
2315 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002316
cristybb503372010-05-27 20:51:26 +00002317 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002318 {
2319 image->colormap[i].red=(Quantum) (i*scale);
2320 image->colormap[i].green=(Quantum) (i*scale);
2321 image->colormap[i].blue=(Quantum) (i*scale);
2322 }
2323 }
2324 }
glennrp147bc912011-03-30 18:47:21 +00002325
glennrpcb395ac2011-03-30 19:50:23 +00002326 /* Set some properties for reporting by "identify" */
2327 {
glennrp147bc912011-03-30 18:47:21 +00002328 char
2329 msg[MaxTextExtent];
2330
2331 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2332 ping_interlace_method in value */
2333
glennrp7cdb11c2011-03-31 18:17:25 +00002334 (void) FormatMagickString(msg,MaxTextExtent,
2335 "%d, %d",(int) ping_width, (int) ping_height);
2336 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002337
2338 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2339 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2340
2341 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2342 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2343
2344 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2345 (int) ping_interlace_method);
2346 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002347 }
glennrp147bc912011-03-30 18:47:21 +00002348
cristy3ed852e2009-09-05 21:47:34 +00002349 /*
2350 Read image scanlines.
2351 */
2352 if (image->delay != 0)
2353 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002354
glennrp0ca69b12010-07-26 01:57:52 +00002355 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002356 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2357 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002358 {
2359 if (logging != MagickFalse)
2360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002361 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002362 mng_info->scenes_found-1);
2363 png_destroy_read_struct(&ping,&ping_info,&end_info);
2364#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002365 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002366#endif
2367 if (logging != MagickFalse)
2368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2369 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002370
cristy3ed852e2009-09-05 21:47:34 +00002371 return(image);
2372 }
glennrp0fe50b42010-11-16 03:52:51 +00002373
cristy3ed852e2009-09-05 21:47:34 +00002374 if (logging != MagickFalse)
2375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2376 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002377
cristy3ed852e2009-09-05 21:47:34 +00002378 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002379 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2380 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002381
cristy3ed852e2009-09-05 21:47:34 +00002382 else
glennrpcf002022011-01-30 02:38:15 +00002383 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2384 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002385
glennrpcf002022011-01-30 02:38:15 +00002386 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002387 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002388
cristy3ed852e2009-09-05 21:47:34 +00002389 if (logging != MagickFalse)
2390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2391 " Converting PNG pixels to pixel packets");
2392 /*
2393 Convert PNG pixels to pixel packets.
2394 */
glennrpfaa852b2010-03-30 12:17:00 +00002395 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002396 {
2397 /*
2398 PNG image is corrupt.
2399 */
2400 png_destroy_read_struct(&ping,&ping_info,&end_info);
2401#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002402 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002403#endif
2404 if (quantum_info != (QuantumInfo *) NULL)
2405 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002406
glennrpcf002022011-01-30 02:38:15 +00002407 if (ping_pixels != (unsigned char *) NULL)
2408 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002409
cristy3ed852e2009-09-05 21:47:34 +00002410 if (logging != MagickFalse)
2411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2412 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002413
cristy3ed852e2009-09-05 21:47:34 +00002414 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002415 {
2416 InheritException(exception,&image->exception);
2417 image->columns=0;
2418 }
glennrp0fe50b42010-11-16 03:52:51 +00002419
cristy3ed852e2009-09-05 21:47:34 +00002420 return(GetFirstImageInList(image));
2421 }
glennrp0fe50b42010-11-16 03:52:51 +00002422
cristyed552522009-10-16 14:04:35 +00002423 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002424
cristyed552522009-10-16 14:04:35 +00002425 if (quantum_info == (QuantumInfo *) NULL)
2426 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002427
glennrpc8cbc5d2011-01-01 00:12:34 +00002428 {
2429
2430 MagickBooleanType
2431 found_transparent_pixel;
2432
2433 found_transparent_pixel=MagickFalse;
2434
cristy3ed852e2009-09-05 21:47:34 +00002435 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002436 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002437 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002438 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002439 /*
2440 Convert image to DirectClass pixel packets.
2441 */
glennrp67b9c1a2011-04-22 18:47:36 +00002442#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2443 int
2444 depth;
2445
2446 depth=(ssize_t) ping_bit_depth;
2447#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002448 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2449 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2450 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2451 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002452
glennrpc8cbc5d2011-01-01 00:12:34 +00002453 for (y=0; y < (ssize_t) image->rows; y++)
2454 {
2455 if (num_passes > 1)
2456 row_offset=ping_rowbytes*y;
2457
2458 else
2459 row_offset=0;
2460
glennrpcf002022011-01-30 02:38:15 +00002461 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002462 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2463
2464 if (q == (PixelPacket *) NULL)
2465 break;
2466
glennrpc8cbc5d2011-01-01 00:12:34 +00002467 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2468 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002469 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002470
2471 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2472 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002473 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002474
2475 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2476 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002477 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002478
2479 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2480 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002481 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002482
2483 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2484 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002485 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002486
glennrpc8cbc5d2011-01-01 00:12:34 +00002487 if (found_transparent_pixel == MagickFalse)
2488 {
2489 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002490 if (y== 0 && logging != MagickFalse)
2491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2492 " Looking for cheap transparent pixel");
2493
glennrpc8cbc5d2011-01-01 00:12:34 +00002494 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2495 {
glennrp5aa37f62011-01-02 03:07:57 +00002496 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2497 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
glennrp8b698592011-04-26 03:38:21 +00002498 (GetOpacityPixelComponent(q) != OpaqueOpacity))
glennrpc8cbc5d2011-01-01 00:12:34 +00002499 {
glennrpa6a06632011-01-19 15:15:34 +00002500 if (logging != MagickFalse)
2501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2502 " ...got one.");
2503
glennrpc8cbc5d2011-01-01 00:12:34 +00002504 found_transparent_pixel = MagickTrue;
2505 break;
2506 }
glennrp4f25bd02011-01-01 18:51:28 +00002507 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2508 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp8b698592011-04-26 03:38:21 +00002509 (ScaleQuantumToShort(GetRedPixelComponent(q))
2510 == transparent_color.red &&
2511 ScaleQuantumToShort(GetGreenPixelComponent(q))
2512 == transparent_color.green &&
2513 ScaleQuantumToShort(GetBluePixelComponent(q))
2514 == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002515 {
glennrpa6a06632011-01-19 15:15:34 +00002516 if (logging != MagickFalse)
2517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2518 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002519 found_transparent_pixel = MagickTrue;
2520 break;
2521 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002522 q++;
2523 }
2524 }
2525
2526 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2527 {
2528 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2529 image->rows);
2530
2531 if (status == MagickFalse)
2532 break;
2533 }
2534 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2535 break;
2536 }
2537
2538 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2539 {
2540 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002541 if (status == MagickFalse)
2542 break;
2543 }
cristy3ed852e2009-09-05 21:47:34 +00002544 }
cristy3ed852e2009-09-05 21:47:34 +00002545 }
glennrp0fe50b42010-11-16 03:52:51 +00002546
cristy3ed852e2009-09-05 21:47:34 +00002547 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002548
cristy3ed852e2009-09-05 21:47:34 +00002549 for (pass=0; pass < num_passes; pass++)
2550 {
2551 Quantum
2552 *quantum_scanline;
2553
2554 register Quantum
2555 *r;
2556
2557 /*
2558 Convert grayscale image to PseudoClass pixel packets.
2559 */
glennrpfaa852b2010-03-30 12:17:00 +00002560 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002561 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002562
cristy3ed852e2009-09-05 21:47:34 +00002563 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2564 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002565
cristy3ed852e2009-09-05 21:47:34 +00002566 if (quantum_scanline == (Quantum *) NULL)
2567 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002568
cristybb503372010-05-27 20:51:26 +00002569 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002570 {
2571 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002572 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002573
cristy3ed852e2009-09-05 21:47:34 +00002574 else
2575 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00002576
glennrpcf002022011-01-30 02:38:15 +00002577 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002578 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00002579
cristy3ed852e2009-09-05 21:47:34 +00002580 if (q == (PixelPacket *) NULL)
2581 break;
glennrp0fe50b42010-11-16 03:52:51 +00002582
cristy5c6f7892010-05-05 22:53:29 +00002583 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00002584 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00002585 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00002586
glennrpfaa852b2010-03-30 12:17:00 +00002587 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00002588 {
2589 case 1:
2590 {
cristybb503372010-05-27 20:51:26 +00002591 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002592 bit;
2593
cristybb503372010-05-27 20:51:26 +00002594 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00002595 {
2596 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002597 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002598 p++;
2599 }
glennrp0fe50b42010-11-16 03:52:51 +00002600
cristy3ed852e2009-09-05 21:47:34 +00002601 if ((image->columns % 8) != 0)
2602 {
cristybb503372010-05-27 20:51:26 +00002603 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002604 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002605 }
glennrp0fe50b42010-11-16 03:52:51 +00002606
cristy3ed852e2009-09-05 21:47:34 +00002607 break;
2608 }
glennrp47b9dd52010-11-24 18:12:06 +00002609
cristy3ed852e2009-09-05 21:47:34 +00002610 case 2:
2611 {
cristybb503372010-05-27 20:51:26 +00002612 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002613 {
glennrpa18d5bc2011-04-23 14:51:34 +00002614 *r++=(*p >> 6) & 0x03;
2615 *r++=(*p >> 4) & 0x03;
2616 *r++=(*p >> 2) & 0x03;
2617 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00002618 }
glennrp0fe50b42010-11-16 03:52:51 +00002619
cristy3ed852e2009-09-05 21:47:34 +00002620 if ((image->columns % 4) != 0)
2621 {
cristybb503372010-05-27 20:51:26 +00002622 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00002623 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00002624 }
glennrp0fe50b42010-11-16 03:52:51 +00002625
cristy3ed852e2009-09-05 21:47:34 +00002626 break;
2627 }
glennrp47b9dd52010-11-24 18:12:06 +00002628
cristy3ed852e2009-09-05 21:47:34 +00002629 case 4:
2630 {
cristybb503372010-05-27 20:51:26 +00002631 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00002632 {
glennrpa18d5bc2011-04-23 14:51:34 +00002633 *r++=(*p >> 4) & 0x0f;
2634 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00002635 }
glennrp0fe50b42010-11-16 03:52:51 +00002636
cristy3ed852e2009-09-05 21:47:34 +00002637 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00002638 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00002639
cristy3ed852e2009-09-05 21:47:34 +00002640 break;
2641 }
glennrp47b9dd52010-11-24 18:12:06 +00002642
cristy3ed852e2009-09-05 21:47:34 +00002643 case 8:
2644 {
glennrpfaa852b2010-03-30 12:17:00 +00002645 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00002646 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002647 {
glennrpa18d5bc2011-04-23 14:51:34 +00002648 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00002649 /* In image.h, OpaqueOpacity is 0
2650 * TransparentOpacity is QuantumRange
2651 * In a PNG datastream, Opaque is QuantumRange
2652 * and Transparent is 0.
2653 */
glennrp8b698592011-04-26 03:38:21 +00002654 SetOpacityPixelComponent(q,
2655 ScaleCharToQuantum((unsigned char) (255-(*p++))));
2656 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00002657 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002658 q++;
2659 }
glennrp0fe50b42010-11-16 03:52:51 +00002660
cristy3ed852e2009-09-05 21:47:34 +00002661 else
cristybb503372010-05-27 20:51:26 +00002662 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002663 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00002664
cristy3ed852e2009-09-05 21:47:34 +00002665 break;
2666 }
glennrp47b9dd52010-11-24 18:12:06 +00002667
cristy3ed852e2009-09-05 21:47:34 +00002668 case 16:
2669 {
cristybb503372010-05-27 20:51:26 +00002670 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002671 {
glennrp58f77c72011-04-23 14:09:09 +00002672#if (MAGICKCORE_QUANTUM_DEPTH == 16)
2673 size_t
2674 quantum;
2675
2676 if (image->colors > 256)
2677 *r=((*p++) << 8);
2678
2679 else
2680 *r=0;
2681
2682 quantum=(*r);
2683 quantum|=(*p++);
2684 *r=(Quantum) quantum;
2685 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00002686
2687 if (ping_color_type == 4)
2688 {
glennrp58f77c72011-04-23 14:09:09 +00002689 quantum=((*p++) << 8);
2690 quantum|=(*p++);
glennrp8b698592011-04-26 03:38:21 +00002691 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-quantum));
2692 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002693 found_transparent_pixel = MagickTrue;
2694 q++;
2695 }
2696#else
2697#if (MAGICKCORE_QUANTUM_DEPTH == 32)
2698 size_t
2699 quantum;
2700
2701 if (image->colors > 256)
2702 *r=((*p++) << 8);
2703
2704 else
2705 *r=0;
2706
2707 quantum=(*r);
2708 quantum|=(*p++);
2709 *r=quantum;
2710 r++;
2711
2712 if (ping_color_type == 4)
2713 {
glennrp8b698592011-04-26 03:38:21 +00002714 quantum=(*p << 8) | *(p+1);
2715 quantum*=65537L;
2716 SetOpacityPixelComponent(q,
2717 (Quantum) GetAlphaPixelComponent(q));
2718 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002719 found_transparent_pixel = MagickTrue;
2720 p+=2;
2721 q++;
2722 }
2723
2724#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2725 *r++=(*p++);
2726 p++; /* strip low byte */
2727
2728 if (ping_color_type == 4)
2729 {
glennrp8b698592011-04-26 03:38:21 +00002730 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-(*p++)));
2731 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp9d0ea4d2011-04-22 18:35:57 +00002732 found_transparent_pixel = MagickTrue;
2733 p++;
2734 q++;
2735 }
cristy3ed852e2009-09-05 21:47:34 +00002736#endif
glennrp58f77c72011-04-23 14:09:09 +00002737#endif
glennrpa18d5bc2011-04-23 14:51:34 +00002738 }
glennrp47b9dd52010-11-24 18:12:06 +00002739
cristy3ed852e2009-09-05 21:47:34 +00002740 break;
2741 }
glennrp47b9dd52010-11-24 18:12:06 +00002742
cristy3ed852e2009-09-05 21:47:34 +00002743 default:
2744 break;
2745 }
glennrp3faa9a32011-04-23 14:00:25 +00002746
cristy3ed852e2009-09-05 21:47:34 +00002747 /*
2748 Transfer image scanline.
2749 */
2750 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00002751
cristybb503372010-05-27 20:51:26 +00002752 for (x=0; x < (ssize_t) image->columns; x++)
cristy80ac8b92010-05-08 13:36:57 +00002753 indexes[x]=(IndexPacket) (*r++);
glennrp0fe50b42010-11-16 03:52:51 +00002754
cristy3ed852e2009-09-05 21:47:34 +00002755 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2756 break;
glennrp0fe50b42010-11-16 03:52:51 +00002757
cristy7a287bf2010-02-14 02:18:09 +00002758 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2759 {
cristycee97112010-05-28 00:44:52 +00002760 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2761 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00002762
cristy7a287bf2010-02-14 02:18:09 +00002763 if (status == MagickFalse)
2764 break;
2765 }
cristy3ed852e2009-09-05 21:47:34 +00002766 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002767
cristy7a287bf2010-02-14 02:18:09 +00002768 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002769 {
2770 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00002771
cristy3ed852e2009-09-05 21:47:34 +00002772 if (status == MagickFalse)
2773 break;
2774 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002775
cristy3ed852e2009-09-05 21:47:34 +00002776 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2777 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002778
2779 image->matte=found_transparent_pixel;
2780
2781 if (logging != MagickFalse)
2782 {
2783 if (found_transparent_pixel != MagickFalse)
2784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2785 " Found transparent pixel");
2786 else
glennrp5aa37f62011-01-02 03:07:57 +00002787 {
2788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2789 " No transparent pixel was found");
glennrpa6a06632011-01-19 15:15:34 +00002790
glennrp5aa37f62011-01-02 03:07:57 +00002791 ping_color_type&=0x03;
2792 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002793 }
2794 }
2795
cristyb32b90a2009-09-07 21:45:48 +00002796 if (quantum_info != (QuantumInfo *) NULL)
2797 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002798
cristy5c6f7892010-05-05 22:53:29 +00002799 if (image->storage_class == PseudoClass)
2800 {
cristyaeb2cbc2010-05-07 13:28:58 +00002801 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002802 matte;
2803
2804 matte=image->matte;
2805 image->matte=MagickFalse;
2806 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002807 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002808 }
glennrp47b9dd52010-11-24 18:12:06 +00002809
glennrp4eb39312011-03-30 21:34:55 +00002810 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00002811
2812 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002813 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002814 {
2815 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00002816 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002817 image->colors=2;
2818 (void) SetImageBackgroundColor(image);
2819#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002820 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002821#endif
2822 if (logging != MagickFalse)
2823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2824 " exit ReadOnePNGImage() early.");
2825 return(image);
2826 }
glennrp47b9dd52010-11-24 18:12:06 +00002827
glennrpfaa852b2010-03-30 12:17:00 +00002828 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002829 {
2830 ClassType
2831 storage_class;
2832
2833 /*
2834 Image has a transparent background.
2835 */
2836 storage_class=image->storage_class;
2837 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002838
glennrp3c218112010-11-27 15:31:26 +00002839/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00002840
glennrp0fe50b42010-11-16 03:52:51 +00002841 if (storage_class == PseudoClass)
2842 {
2843 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002844 {
glennrp0fe50b42010-11-16 03:52:51 +00002845 for (x=0; x < ping_num_trans; x++)
2846 {
2847 image->colormap[x].opacity =
2848 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2849 }
glennrpc11cf6a2010-03-20 16:46:19 +00002850 }
glennrp47b9dd52010-11-24 18:12:06 +00002851
glennrp0fe50b42010-11-16 03:52:51 +00002852 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2853 {
2854 for (x=0; x < (int) image->colors; x++)
2855 {
2856 if (ScaleQuantumToShort(image->colormap[x].red) ==
2857 transparent_color.opacity)
2858 {
2859 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2860 }
2861 }
2862 }
2863 (void) SyncImage(image);
2864 }
glennrp47b9dd52010-11-24 18:12:06 +00002865
glennrpa6a06632011-01-19 15:15:34 +00002866#if 1 /* Should have already been done above, but glennrp problem P10
2867 * needs this.
2868 */
glennrp0fe50b42010-11-16 03:52:51 +00002869 else
2870 {
2871 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002872 {
glennrp0fe50b42010-11-16 03:52:51 +00002873 image->storage_class=storage_class;
2874 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2875
2876 if (q == (PixelPacket *) NULL)
2877 break;
2878
2879 indexes=GetAuthenticIndexQueue(image);
2880
glennrpa6a06632011-01-19 15:15:34 +00002881 /* Caution: on a Q8 build, this does not distinguish between
2882 * 16-bit colors that differ only in the low byte
2883 */
glennrp0fe50b42010-11-16 03:52:51 +00002884 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2885 {
glennrp8b698592011-04-26 03:38:21 +00002886 if (ScaleQuantumToShort(GetRedPixelComponent(q))
2887 == transparent_color.red &&
2888 ScaleQuantumToShort(GetGreenPixelComponent(q))
2889 == transparent_color.green &&
2890 ScaleQuantumToShort(GetBluePixelComponent(q))
2891 == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00002892 {
glennrp8b698592011-04-26 03:38:21 +00002893 SetOpacityPixelComponent(q,TransparentOpacity);
glennrp4f25bd02011-01-01 18:51:28 +00002894 }
glennrp0fe50b42010-11-16 03:52:51 +00002895
glennrp67b9c1a2011-04-22 18:47:36 +00002896#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00002897 else
glennrp4f25bd02011-01-01 18:51:28 +00002898 {
2899 q->opacity=(Quantum) OpaqueOpacity;
2900 }
glennrpa6a06632011-01-19 15:15:34 +00002901#endif
glennrp0fe50b42010-11-16 03:52:51 +00002902
2903 q++;
2904 }
2905
2906 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2907 break;
glennrpc11cf6a2010-03-20 16:46:19 +00002908 }
glennrp0fe50b42010-11-16 03:52:51 +00002909 }
glennrpa6a06632011-01-19 15:15:34 +00002910#endif
glennrpc11cf6a2010-03-20 16:46:19 +00002911
cristy3ed852e2009-09-05 21:47:34 +00002912 image->storage_class=DirectClass;
2913 }
glennrp3c218112010-11-27 15:31:26 +00002914
cristyb40fc462010-08-08 00:49:49 +00002915 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2916 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2917 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00002918
cristyeb3b22a2011-03-31 20:16:11 +00002919 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00002920 {
2921 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00002922 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2923 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00002924 else
glennrpa0ed0092011-04-18 16:36:29 +00002925 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2926 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002927
glennrp4eb39312011-03-30 21:34:55 +00002928 if (status != MagickFalse)
2929 for (i=0; i < (ssize_t) num_text; i++)
2930 {
2931 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00002932
glennrp4eb39312011-03-30 21:34:55 +00002933 if (logging != MagickFalse)
2934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2935 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00002936
glennrp4eb39312011-03-30 21:34:55 +00002937 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00002938 {
glennrp4eb39312011-03-30 21:34:55 +00002939 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2940 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00002941 }
glennrp0fe50b42010-11-16 03:52:51 +00002942
glennrp4eb39312011-03-30 21:34:55 +00002943 else
2944 {
2945 char
2946 *value;
2947
2948 length=text[i].text_length;
2949 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2950 sizeof(*value));
2951 if (value == (char *) NULL)
2952 {
2953 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2954 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2955 image->filename);
2956 break;
2957 }
2958 *value='\0';
2959 (void) ConcatenateMagickString(value,text[i].text,length+2);
2960
2961 /* Don't save "density" or "units" property if we have a pHYs
2962 * chunk
2963 */
2964 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2965 (LocaleCompare(text[i].key,"density") != 0 &&
2966 LocaleCompare(text[i].key,"units") != 0))
2967 (void) SetImageProperty(image,text[i].key,value);
2968
2969 if (logging != MagickFalse)
2970 {
2971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2972 " length: %lu",(unsigned long) length);
2973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2974 " Keyword: %s",text[i].key);
2975 }
2976
2977 value=DestroyString(value);
2978 }
2979 }
2980 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00002981 }
glennrp3c218112010-11-27 15:31:26 +00002982
cristy3ed852e2009-09-05 21:47:34 +00002983#ifdef MNG_OBJECT_BUFFERS
2984 /*
2985 Store the object if necessary.
2986 */
2987 if (object_id && !mng_info->frozen[object_id])
2988 {
2989 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2990 {
2991 /*
2992 create a new object buffer.
2993 */
2994 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00002995 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00002996
cristy3ed852e2009-09-05 21:47:34 +00002997 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2998 {
2999 mng_info->ob[object_id]->image=(Image *) NULL;
3000 mng_info->ob[object_id]->reference_count=1;
3001 }
3002 }
glennrp47b9dd52010-11-24 18:12:06 +00003003
cristy3ed852e2009-09-05 21:47:34 +00003004 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3005 mng_info->ob[object_id]->frozen)
3006 {
3007 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3008 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3009 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3010 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003011
cristy3ed852e2009-09-05 21:47:34 +00003012 if (mng_info->ob[object_id]->frozen)
3013 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3014 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3015 "`%s'",image->filename);
3016 }
glennrp0fe50b42010-11-16 03:52:51 +00003017
cristy3ed852e2009-09-05 21:47:34 +00003018 else
3019 {
cristy3ed852e2009-09-05 21:47:34 +00003020
3021 if (mng_info->ob[object_id]->image != (Image *) NULL)
3022 mng_info->ob[object_id]->image=DestroyImage
3023 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003024
cristy3ed852e2009-09-05 21:47:34 +00003025 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3026 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003027
cristy3ed852e2009-09-05 21:47:34 +00003028 if (mng_info->ob[object_id]->image != (Image *) NULL)
3029 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003030
cristy3ed852e2009-09-05 21:47:34 +00003031 else
3032 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3033 ResourceLimitError,"Cloning image for object buffer failed",
3034 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003035
glennrpfaa852b2010-03-30 12:17:00 +00003036 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003037 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003038
glennrpfaa852b2010-03-30 12:17:00 +00003039 mng_info->ob[object_id]->width=ping_width;
3040 mng_info->ob[object_id]->height=ping_height;
3041 mng_info->ob[object_id]->color_type=ping_color_type;
3042 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3043 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3044 mng_info->ob[object_id]->compression_method=
3045 ping_compression_method;
3046 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003047
glennrpfaa852b2010-03-30 12:17:00 +00003048 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003049 {
3050 int
3051 number_colors;
3052
3053 png_colorp
3054 plte;
3055
3056 /*
3057 Copy the PLTE to the object buffer.
3058 */
3059 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3060 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003061
cristy3ed852e2009-09-05 21:47:34 +00003062 for (i=0; i < number_colors; i++)
3063 {
3064 mng_info->ob[object_id]->plte[i]=plte[i];
3065 }
3066 }
glennrp47b9dd52010-11-24 18:12:06 +00003067
cristy3ed852e2009-09-05 21:47:34 +00003068 else
3069 mng_info->ob[object_id]->plte_length=0;
3070 }
3071 }
3072#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003073
3074 /* Set image->matte to MagickTrue if the input colortype supports
3075 * alpha or if a valid tRNS chunk is present, no matter whether there
3076 * is actual transparency present.
3077 */
3078 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3079 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3080 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3081 MagickTrue : MagickFalse;
3082
glennrpcb395ac2011-03-30 19:50:23 +00003083 /* Set more properties for identify to retrieve */
3084 {
3085 char
3086 msg[MaxTextExtent];
3087
glennrp4eb39312011-03-30 21:34:55 +00003088 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003089 {
3090 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3091 (void) FormatMagickString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003092 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003093 (void) SetImageProperty(image,"PNG:text ",msg);
3094 }
3095
3096 if (num_raw_profiles != 0)
3097 {
3098 (void) FormatMagickString(msg,MaxTextExtent,
3099 "%d were found", num_raw_profiles);
3100 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3101 }
3102
glennrpcb395ac2011-03-30 19:50:23 +00003103 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003104 {
3105 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3106 "chunk was found (see Chromaticity, above)");
3107 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3108 }
glennrpcb395ac2011-03-30 19:50:23 +00003109
3110 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003111 {
3112 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3113 "chunk was found (see Background color, above)");
3114 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3115 }
3116
3117 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3118 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003119
3120 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3121 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3122
glennrpcb395ac2011-03-30 19:50:23 +00003123 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3124 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003125
3126#if defined(PNG_sRGB_SUPPORTED)
3127 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3128 {
glennrp07523c72011-03-31 18:12:10 +00003129 (void) FormatMagickString(msg,MaxTextExtent,
3130 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003131 (int) intent);
3132 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3133 }
3134#endif
3135
3136 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3137 {
glennrp07523c72011-03-31 18:12:10 +00003138 (void) FormatMagickString(msg,MaxTextExtent,
3139 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003140 file_gamma);
3141 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3142 }
3143
3144#if defined(PNG_pHYs_SUPPORTED)
3145 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3146 {
glennrp07523c72011-03-31 18:12:10 +00003147 (void) FormatMagickString(msg,MaxTextExtent,
3148 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003149 (double) x_resolution,(double) y_resolution, unit_type);
3150 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3151 }
3152#endif
3153
3154#if defined(PNG_oFFs_SUPPORTED)
3155 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3156 {
3157 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3158 (double) image->page.x,(double) image->page.y);
3159 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3160 }
3161#endif
3162
glennrp07523c72011-03-31 18:12:10 +00003163 if ((image->page.width != 0 && image->page.width != image->columns) ||
3164 (image->page.height != 0 && image->page.height != image->rows))
3165 {
3166 (void) FormatMagickString(msg,MaxTextExtent,
3167 "width=%.20g, height=%.20g",
3168 (double) image->page.width,(double) image->page.height);
3169 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3170 }
glennrpcb395ac2011-03-30 19:50:23 +00003171 }
3172
cristy3ed852e2009-09-05 21:47:34 +00003173 /*
3174 Relinquish resources.
3175 */
3176 png_destroy_read_struct(&ping,&ping_info,&end_info);
3177
glennrpcf002022011-01-30 02:38:15 +00003178 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003179#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003180 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003181#endif
3182
3183 if (logging != MagickFalse)
3184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3185 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003186
cristy3ed852e2009-09-05 21:47:34 +00003187 return(image);
3188
3189/* end of reading one PNG image */
3190}
3191
3192static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3193{
3194 Image
3195 *image,
3196 *previous;
3197
3198 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003199 have_mng_structure,
3200 logging,
cristy3ed852e2009-09-05 21:47:34 +00003201 status;
3202
3203 MngInfo
3204 *mng_info;
3205
3206 char
3207 magic_number[MaxTextExtent];
3208
cristy3ed852e2009-09-05 21:47:34 +00003209 ssize_t
3210 count;
3211
3212 /*
3213 Open image file.
3214 */
3215 assert(image_info != (const ImageInfo *) NULL);
3216 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003217
cristy3ed852e2009-09-05 21:47:34 +00003218 if (image_info->debug != MagickFalse)
3219 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3220 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003221
cristy3ed852e2009-09-05 21:47:34 +00003222 assert(exception != (ExceptionInfo *) NULL);
3223 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003224 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003225 image=AcquireImage(image_info);
3226 mng_info=(MngInfo *) NULL;
3227 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003228
cristy3ed852e2009-09-05 21:47:34 +00003229 if (status == MagickFalse)
3230 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003231
cristy3ed852e2009-09-05 21:47:34 +00003232 /*
3233 Verify PNG signature.
3234 */
3235 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003236
glennrpdde35db2011-02-21 12:06:32 +00003237 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003238 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003239
cristy3ed852e2009-09-05 21:47:34 +00003240 /*
3241 Allocate a MngInfo structure.
3242 */
3243 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003244 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003245
cristy3ed852e2009-09-05 21:47:34 +00003246 if (mng_info == (MngInfo *) NULL)
3247 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003248
cristy3ed852e2009-09-05 21:47:34 +00003249 /*
3250 Initialize members of the MngInfo structure.
3251 */
3252 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3253 mng_info->image=image;
3254 have_mng_structure=MagickTrue;
3255
3256 previous=image;
3257 image=ReadOnePNGImage(mng_info,image_info,exception);
3258 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003259
cristy3ed852e2009-09-05 21:47:34 +00003260 if (image == (Image *) NULL)
3261 {
3262 if (previous != (Image *) NULL)
3263 {
3264 if (previous->signature != MagickSignature)
3265 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003266
cristy3ed852e2009-09-05 21:47:34 +00003267 (void) CloseBlob(previous);
3268 (void) DestroyImageList(previous);
3269 }
glennrp0fe50b42010-11-16 03:52:51 +00003270
cristy3ed852e2009-09-05 21:47:34 +00003271 if (logging != MagickFalse)
3272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3273 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003274
cristy3ed852e2009-09-05 21:47:34 +00003275 return((Image *) NULL);
3276 }
glennrp47b9dd52010-11-24 18:12:06 +00003277
cristy3ed852e2009-09-05 21:47:34 +00003278 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003279
cristy3ed852e2009-09-05 21:47:34 +00003280 if ((image->columns == 0) || (image->rows == 0))
3281 {
3282 if (logging != MagickFalse)
3283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3284 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003285
cristy3ed852e2009-09-05 21:47:34 +00003286 ThrowReaderException(CorruptImageError,"CorruptImage");
3287 }
glennrp47b9dd52010-11-24 18:12:06 +00003288
glennrp3faa9a32011-04-23 14:00:25 +00003289#if 0 /* This is probably redundant now */
cristy3ed852e2009-09-05 21:47:34 +00003290 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3291 {
3292 (void) SetImageType(image,PaletteType);
glennrp0fe50b42010-11-16 03:52:51 +00003293
cristy3ed852e2009-09-05 21:47:34 +00003294 if (image->matte != MagickFalse)
3295 {
3296 /* To do: Reduce to binary transparency */
3297 }
3298 }
glennrp3faa9a32011-04-23 14:00:25 +00003299#endif
glennrp47b9dd52010-11-24 18:12:06 +00003300
cristy3ed852e2009-09-05 21:47:34 +00003301 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3302 {
3303 (void) SetImageType(image,TrueColorType);
3304 image->matte=MagickFalse;
3305 }
glennrp0fe50b42010-11-16 03:52:51 +00003306
cristy3ed852e2009-09-05 21:47:34 +00003307 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3308 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003309
cristy3ed852e2009-09-05 21:47:34 +00003310 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3312 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3313 (double) image->page.width,(double) image->page.height,
3314 (double) image->page.x,(double) image->page.y);
3315
3316 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003318
cristy3ed852e2009-09-05 21:47:34 +00003319 return(image);
3320}
3321
3322
3323
3324#if defined(JNG_SUPPORTED)
3325/*
3326%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3327% %
3328% %
3329% %
3330% R e a d O n e J N G I m a g e %
3331% %
3332% %
3333% %
3334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3335%
3336% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3337% (minus the 8-byte signature) and returns it. It allocates the memory
3338% necessary for the new Image structure and returns a pointer to the new
3339% image.
3340%
3341% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3342%
3343% The format of the ReadOneJNGImage method is:
3344%
3345% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3346% ExceptionInfo *exception)
3347%
3348% A description of each parameter follows:
3349%
3350% o mng_info: Specifies a pointer to a MngInfo structure.
3351%
3352% o image_info: the image info.
3353%
3354% o exception: return any errors or warnings in this structure.
3355%
3356*/
3357static Image *ReadOneJNGImage(MngInfo *mng_info,
3358 const ImageInfo *image_info, ExceptionInfo *exception)
3359{
3360 Image
3361 *alpha_image,
3362 *color_image,
3363 *image,
3364 *jng_image;
3365
3366 ImageInfo
3367 *alpha_image_info,
3368 *color_image_info;
3369
cristy4383ec82011-01-05 15:42:32 +00003370 MagickBooleanType
3371 logging;
3372
cristybb503372010-05-27 20:51:26 +00003373 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003374 y;
3375
3376 MagickBooleanType
3377 status;
3378
3379 png_uint_32
3380 jng_height,
3381 jng_width;
3382
3383 png_byte
3384 jng_color_type,
3385 jng_image_sample_depth,
3386 jng_image_compression_method,
3387 jng_image_interlace_method,
3388 jng_alpha_sample_depth,
3389 jng_alpha_compression_method,
3390 jng_alpha_filter_method,
3391 jng_alpha_interlace_method;
3392
3393 register const PixelPacket
3394 *s;
3395
cristybb503372010-05-27 20:51:26 +00003396 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003397 i,
3398 x;
3399
3400 register PixelPacket
3401 *q;
3402
3403 register unsigned char
3404 *p;
3405
3406 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003407 read_JSEP,
3408 reading_idat,
3409 skip_to_iend;
3410
cristybb503372010-05-27 20:51:26 +00003411 size_t
cristy3ed852e2009-09-05 21:47:34 +00003412 length;
3413
3414 jng_alpha_compression_method=0;
3415 jng_alpha_sample_depth=8;
3416 jng_color_type=0;
3417 jng_height=0;
3418 jng_width=0;
3419 alpha_image=(Image *) NULL;
3420 color_image=(Image *) NULL;
3421 alpha_image_info=(ImageInfo *) NULL;
3422 color_image_info=(ImageInfo *) NULL;
3423
3424 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003425 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003426
3427 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003428
cristy3ed852e2009-09-05 21:47:34 +00003429 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3430 {
3431 /*
3432 Allocate next image structure.
3433 */
3434 if (logging != MagickFalse)
3435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3436 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003437
cristy3ed852e2009-09-05 21:47:34 +00003438 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003439
cristy3ed852e2009-09-05 21:47:34 +00003440 if (GetNextImageInList(image) == (Image *) NULL)
3441 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003442
cristy3ed852e2009-09-05 21:47:34 +00003443 image=SyncNextImageInList(image);
3444 }
3445 mng_info->image=image;
3446
3447 /*
3448 Signature bytes have already been read.
3449 */
3450
3451 read_JSEP=MagickFalse;
3452 reading_idat=MagickFalse;
3453 skip_to_iend=MagickFalse;
3454 for (;;)
3455 {
3456 char
3457 type[MaxTextExtent];
3458
3459 unsigned char
3460 *chunk;
3461
3462 unsigned int
3463 count;
3464
3465 /*
3466 Read a new JNG chunk.
3467 */
3468 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3469 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003470
cristy3ed852e2009-09-05 21:47:34 +00003471 if (status == MagickFalse)
3472 break;
glennrp0fe50b42010-11-16 03:52:51 +00003473
cristy3ed852e2009-09-05 21:47:34 +00003474 type[0]='\0';
3475 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3476 length=ReadBlobMSBLong(image);
3477 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3478
3479 if (logging != MagickFalse)
3480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003481 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3482 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003483
3484 if (length > PNG_UINT_31_MAX || count == 0)
3485 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003486
cristy3ed852e2009-09-05 21:47:34 +00003487 p=NULL;
3488 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003489
cristy3ed852e2009-09-05 21:47:34 +00003490 if (length)
3491 {
3492 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003493
cristy3ed852e2009-09-05 21:47:34 +00003494 if (chunk == (unsigned char *) NULL)
3495 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003496
cristybb503372010-05-27 20:51:26 +00003497 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003498 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003499
cristy3ed852e2009-09-05 21:47:34 +00003500 p=chunk;
3501 }
glennrp47b9dd52010-11-24 18:12:06 +00003502
cristy3ed852e2009-09-05 21:47:34 +00003503 (void) ReadBlobMSBLong(image); /* read crc word */
3504
3505 if (skip_to_iend)
3506 {
3507 if (length)
3508 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003509
cristy3ed852e2009-09-05 21:47:34 +00003510 continue;
3511 }
3512
3513 if (memcmp(type,mng_JHDR,4) == 0)
3514 {
3515 if (length == 16)
3516 {
cristybb503372010-05-27 20:51:26 +00003517 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003518 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003519 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003520 (p[6] << 8) | p[7]);
3521 jng_color_type=p[8];
3522 jng_image_sample_depth=p[9];
3523 jng_image_compression_method=p[10];
3524 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003525
cristy3ed852e2009-09-05 21:47:34 +00003526 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3527 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003528
cristy3ed852e2009-09-05 21:47:34 +00003529 jng_alpha_sample_depth=p[12];
3530 jng_alpha_compression_method=p[13];
3531 jng_alpha_filter_method=p[14];
3532 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003533
cristy3ed852e2009-09-05 21:47:34 +00003534 if (logging != MagickFalse)
3535 {
3536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003537 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003538
cristy3ed852e2009-09-05 21:47:34 +00003539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003540 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003541
cristy3ed852e2009-09-05 21:47:34 +00003542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3543 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003544
cristy3ed852e2009-09-05 21:47:34 +00003545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3546 " jng_image_sample_depth: %3d",
3547 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003548
cristy3ed852e2009-09-05 21:47:34 +00003549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3550 " jng_image_compression_method:%3d",
3551 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003552
cristy3ed852e2009-09-05 21:47:34 +00003553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3554 " jng_image_interlace_method: %3d",
3555 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003556
cristy3ed852e2009-09-05 21:47:34 +00003557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3558 " jng_alpha_sample_depth: %3d",
3559 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003560
cristy3ed852e2009-09-05 21:47:34 +00003561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3562 " jng_alpha_compression_method:%3d",
3563 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003564
cristy3ed852e2009-09-05 21:47:34 +00003565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3566 " jng_alpha_filter_method: %3d",
3567 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003568
cristy3ed852e2009-09-05 21:47:34 +00003569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3570 " jng_alpha_interlace_method: %3d",
3571 jng_alpha_interlace_method);
3572 }
3573 }
glennrp47b9dd52010-11-24 18:12:06 +00003574
cristy3ed852e2009-09-05 21:47:34 +00003575 if (length)
3576 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003577
cristy3ed852e2009-09-05 21:47:34 +00003578 continue;
3579 }
3580
3581
3582 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3583 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3584 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3585 {
3586 /*
3587 o create color_image
3588 o open color_blob, attached to color_image
3589 o if (color type has alpha)
3590 open alpha_blob, attached to alpha_image
3591 */
3592
cristy73bd4a52010-10-05 11:24:23 +00003593 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003594
cristy3ed852e2009-09-05 21:47:34 +00003595 if (color_image_info == (ImageInfo *) NULL)
3596 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003597
cristy3ed852e2009-09-05 21:47:34 +00003598 GetImageInfo(color_image_info);
3599 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003600
cristy3ed852e2009-09-05 21:47:34 +00003601 if (color_image == (Image *) NULL)
3602 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3603
3604 if (logging != MagickFalse)
3605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3606 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003607
cristy3ed852e2009-09-05 21:47:34 +00003608 (void) AcquireUniqueFilename(color_image->filename);
3609 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3610 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003611
cristy3ed852e2009-09-05 21:47:34 +00003612 if (status == MagickFalse)
3613 return((Image *) NULL);
3614
3615 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3616 {
3617 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00003618 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00003619
cristy3ed852e2009-09-05 21:47:34 +00003620 if (alpha_image_info == (ImageInfo *) NULL)
3621 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003622
cristy3ed852e2009-09-05 21:47:34 +00003623 GetImageInfo(alpha_image_info);
3624 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003625
cristy3ed852e2009-09-05 21:47:34 +00003626 if (alpha_image == (Image *) NULL)
3627 {
3628 alpha_image=DestroyImage(alpha_image);
3629 ThrowReaderException(ResourceLimitError,
3630 "MemoryAllocationFailed");
3631 }
glennrp0fe50b42010-11-16 03:52:51 +00003632
cristy3ed852e2009-09-05 21:47:34 +00003633 if (logging != MagickFalse)
3634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3635 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003636
cristy3ed852e2009-09-05 21:47:34 +00003637 (void) AcquireUniqueFilename(alpha_image->filename);
3638 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3639 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003640
cristy3ed852e2009-09-05 21:47:34 +00003641 if (status == MagickFalse)
3642 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003643
cristy3ed852e2009-09-05 21:47:34 +00003644 if (jng_alpha_compression_method == 0)
3645 {
3646 unsigned char
3647 data[18];
3648
3649 if (logging != MagickFalse)
3650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3651 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003652
cristy3ed852e2009-09-05 21:47:34 +00003653 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3654 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00003655
cristy3ed852e2009-09-05 21:47:34 +00003656 (void) WriteBlobMSBULong(alpha_image,13L);
3657 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00003658 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00003659 PNGLong(data+4,jng_width);
3660 PNGLong(data+8,jng_height);
3661 data[12]=jng_alpha_sample_depth;
3662 data[13]=0; /* color_type gray */
3663 data[14]=0; /* compression method 0 */
3664 data[15]=0; /* filter_method 0 */
3665 data[16]=0; /* interlace_method 0 */
3666 (void) WriteBlob(alpha_image,17,data);
3667 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3668 }
3669 }
3670 reading_idat=MagickTrue;
3671 }
3672
3673 if (memcmp(type,mng_JDAT,4) == 0)
3674 {
glennrp47b9dd52010-11-24 18:12:06 +00003675 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003676
3677 if (logging != MagickFalse)
3678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3679 " Copying JDAT chunk data to color_blob.");
3680
3681 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003682
cristy3ed852e2009-09-05 21:47:34 +00003683 if (length)
3684 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003685
cristy3ed852e2009-09-05 21:47:34 +00003686 continue;
3687 }
3688
3689 if (memcmp(type,mng_IDAT,4) == 0)
3690 {
3691 png_byte
3692 data[5];
3693
glennrp47b9dd52010-11-24 18:12:06 +00003694 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003695
3696 if (image_info->ping == MagickFalse)
3697 {
3698 if (logging != MagickFalse)
3699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3700 " Copying IDAT chunk data to alpha_blob.");
3701
cristybb503372010-05-27 20:51:26 +00003702 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003703 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00003704 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00003705 (void) WriteBlob(alpha_image,4,data);
3706 (void) WriteBlob(alpha_image,length,chunk);
3707 (void) WriteBlobMSBULong(alpha_image,
3708 crc32(crc32(0,data,4),chunk,(uInt) length));
3709 }
glennrp0fe50b42010-11-16 03:52:51 +00003710
cristy3ed852e2009-09-05 21:47:34 +00003711 if (length)
3712 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003713
cristy3ed852e2009-09-05 21:47:34 +00003714 continue;
3715 }
3716
3717 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3718 {
glennrp47b9dd52010-11-24 18:12:06 +00003719 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003720
3721 if (image_info->ping == MagickFalse)
3722 {
3723 if (logging != MagickFalse)
3724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3725 " Copying JDAA chunk data to alpha_blob.");
3726
3727 (void) WriteBlob(alpha_image,length,chunk);
3728 }
glennrp0fe50b42010-11-16 03:52:51 +00003729
cristy3ed852e2009-09-05 21:47:34 +00003730 if (length)
3731 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003732
cristy3ed852e2009-09-05 21:47:34 +00003733 continue;
3734 }
3735
3736 if (memcmp(type,mng_JSEP,4) == 0)
3737 {
3738 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00003739
cristy3ed852e2009-09-05 21:47:34 +00003740 if (length)
3741 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003742
cristy3ed852e2009-09-05 21:47:34 +00003743 continue;
3744 }
3745
3746 if (memcmp(type,mng_bKGD,4) == 0)
3747 {
3748 if (length == 2)
3749 {
3750 image->background_color.red=ScaleCharToQuantum(p[1]);
3751 image->background_color.green=image->background_color.red;
3752 image->background_color.blue=image->background_color.red;
3753 }
glennrp0fe50b42010-11-16 03:52:51 +00003754
cristy3ed852e2009-09-05 21:47:34 +00003755 if (length == 6)
3756 {
3757 image->background_color.red=ScaleCharToQuantum(p[1]);
3758 image->background_color.green=ScaleCharToQuantum(p[3]);
3759 image->background_color.blue=ScaleCharToQuantum(p[5]);
3760 }
glennrp0fe50b42010-11-16 03:52:51 +00003761
cristy3ed852e2009-09-05 21:47:34 +00003762 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3763 continue;
3764 }
3765
3766 if (memcmp(type,mng_gAMA,4) == 0)
3767 {
3768 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00003769 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00003770
cristy3ed852e2009-09-05 21:47:34 +00003771 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3772 continue;
3773 }
3774
3775 if (memcmp(type,mng_cHRM,4) == 0)
3776 {
3777 if (length == 32)
3778 {
cristy8182b072010-05-30 20:10:53 +00003779 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3780 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3781 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3782 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3783 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3784 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3785 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3786 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003787 }
glennrp47b9dd52010-11-24 18:12:06 +00003788
cristy3ed852e2009-09-05 21:47:34 +00003789 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3790 continue;
3791 }
3792
3793 if (memcmp(type,mng_sRGB,4) == 0)
3794 {
3795 if (length == 1)
3796 {
glennrpe610a072010-08-05 17:08:46 +00003797 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00003798 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00003799 image->gamma=0.45455f;
3800 image->chromaticity.red_primary.x=0.6400f;
3801 image->chromaticity.red_primary.y=0.3300f;
3802 image->chromaticity.green_primary.x=0.3000f;
3803 image->chromaticity.green_primary.y=0.6000f;
3804 image->chromaticity.blue_primary.x=0.1500f;
3805 image->chromaticity.blue_primary.y=0.0600f;
3806 image->chromaticity.white_point.x=0.3127f;
3807 image->chromaticity.white_point.y=0.3290f;
3808 }
glennrp47b9dd52010-11-24 18:12:06 +00003809
cristy3ed852e2009-09-05 21:47:34 +00003810 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3811 continue;
3812 }
3813
3814 if (memcmp(type,mng_oFFs,4) == 0)
3815 {
3816 if (length > 8)
3817 {
glennrp5eae7602011-02-22 15:21:32 +00003818 image->page.x=(ssize_t) mng_get_long(p);
3819 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00003820
cristy3ed852e2009-09-05 21:47:34 +00003821 if ((int) p[8] != 0)
3822 {
3823 image->page.x/=10000;
3824 image->page.y/=10000;
3825 }
3826 }
glennrp47b9dd52010-11-24 18:12:06 +00003827
cristy3ed852e2009-09-05 21:47:34 +00003828 if (length)
3829 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003830
cristy3ed852e2009-09-05 21:47:34 +00003831 continue;
3832 }
3833
3834 if (memcmp(type,mng_pHYs,4) == 0)
3835 {
3836 if (length > 8)
3837 {
cristy8182b072010-05-30 20:10:53 +00003838 image->x_resolution=(double) mng_get_long(p);
3839 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003840 if ((int) p[8] == PNG_RESOLUTION_METER)
3841 {
3842 image->units=PixelsPerCentimeterResolution;
3843 image->x_resolution=image->x_resolution/100.0f;
3844 image->y_resolution=image->y_resolution/100.0f;
3845 }
3846 }
glennrp0fe50b42010-11-16 03:52:51 +00003847
cristy3ed852e2009-09-05 21:47:34 +00003848 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3849 continue;
3850 }
3851
3852#if 0
3853 if (memcmp(type,mng_iCCP,4) == 0)
3854 {
glennrpfd05d622011-02-25 04:10:33 +00003855 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00003856 if (length)
3857 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003858
cristy3ed852e2009-09-05 21:47:34 +00003859 continue;
3860 }
3861#endif
3862
3863 if (length)
3864 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3865
3866 if (memcmp(type,mng_IEND,4))
3867 continue;
glennrp0fe50b42010-11-16 03:52:51 +00003868
cristy3ed852e2009-09-05 21:47:34 +00003869 break;
3870 }
3871
3872
3873 /* IEND found */
3874
3875 /*
3876 Finish up reading image data:
3877
3878 o read main image from color_blob.
3879
3880 o close color_blob.
3881
3882 o if (color_type has alpha)
3883 if alpha_encoding is PNG
3884 read secondary image from alpha_blob via ReadPNG
3885 if alpha_encoding is JPEG
3886 read secondary image from alpha_blob via ReadJPEG
3887
3888 o close alpha_blob.
3889
3890 o copy intensity of secondary image into
3891 opacity samples of main image.
3892
3893 o destroy the secondary image.
3894 */
3895
3896 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00003897
cristy3ed852e2009-09-05 21:47:34 +00003898 if (logging != MagickFalse)
3899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3900 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003901
cristy3ed852e2009-09-05 21:47:34 +00003902 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3903 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003904
cristy3ed852e2009-09-05 21:47:34 +00003905 color_image_info->ping=MagickFalse; /* To do: avoid this */
3906 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003907
cristy3ed852e2009-09-05 21:47:34 +00003908 if (jng_image == (Image *) NULL)
3909 return((Image *) NULL);
3910
3911 (void) RelinquishUniqueFileResource(color_image->filename);
3912 color_image=DestroyImage(color_image);
3913 color_image_info=DestroyImageInfo(color_image_info);
3914
3915 if (jng_image == (Image *) NULL)
3916 return((Image *) NULL);
3917
3918 if (logging != MagickFalse)
3919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3920 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00003921
cristy3ed852e2009-09-05 21:47:34 +00003922 image->rows=jng_height;
3923 image->columns=jng_width;
3924 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00003925
cristybb503372010-05-27 20:51:26 +00003926 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003927 {
3928 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3929 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3930 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00003931
cristy3ed852e2009-09-05 21:47:34 +00003932 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3933 break;
3934 }
glennrp0fe50b42010-11-16 03:52:51 +00003935
cristy3ed852e2009-09-05 21:47:34 +00003936 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 if (image_info->ping == MagickFalse)
3939 {
3940 if (jng_color_type >= 12)
3941 {
3942 if (jng_alpha_compression_method == 0)
3943 {
3944 png_byte
3945 data[5];
3946 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3947 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00003948 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00003949 (void) WriteBlob(alpha_image,4,data);
3950 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3951 }
glennrp0fe50b42010-11-16 03:52:51 +00003952
cristy3ed852e2009-09-05 21:47:34 +00003953 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00003954
cristy3ed852e2009-09-05 21:47:34 +00003955 if (logging != MagickFalse)
3956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 " Reading opacity from alpha_blob.");
3958
3959 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3960 "%s",alpha_image->filename);
3961
3962 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003963
cristy3ed852e2009-09-05 21:47:34 +00003964 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003965 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003966 {
3967 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3968 &image->exception);
3969 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003970
cristy3ed852e2009-09-05 21:47:34 +00003971 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003972 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
glennrp7c7b3152011-04-26 04:01:27 +00003973 q->opacity=(Quantum) QuantumRange-
3974 GetRedPixelComponent(s);
glennrp0fe50b42010-11-16 03:52:51 +00003975
cristy3ed852e2009-09-05 21:47:34 +00003976 else
cristybb503372010-05-27 20:51:26 +00003977 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003978 {
glennrp7c7b3152011-04-26 04:01:27 +00003979 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
3980 GetRedPixelComponent(s));
3981 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +00003982 image->matte=MagickTrue;
3983 }
glennrp0fe50b42010-11-16 03:52:51 +00003984
cristy3ed852e2009-09-05 21:47:34 +00003985 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3986 break;
3987 }
3988 (void) RelinquishUniqueFileResource(alpha_image->filename);
3989 alpha_image=DestroyImage(alpha_image);
3990 alpha_image_info=DestroyImageInfo(alpha_image_info);
3991 if (jng_image != (Image *) NULL)
3992 jng_image=DestroyImage(jng_image);
3993 }
3994 }
3995
glennrp47b9dd52010-11-24 18:12:06 +00003996 /* Read the JNG image. */
3997
cristy3ed852e2009-09-05 21:47:34 +00003998 if (mng_info->mng_type == 0)
3999 {
4000 mng_info->mng_width=jng_width;
4001 mng_info->mng_height=jng_height;
4002 }
glennrp0fe50b42010-11-16 03:52:51 +00004003
cristy3ed852e2009-09-05 21:47:34 +00004004 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004005 {
4006 image->page.width=jng_width;
4007 image->page.height=jng_height;
4008 }
4009
cristy3ed852e2009-09-05 21:47:34 +00004010 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004011 {
4012 image->page.x=mng_info->x_off[mng_info->object_id];
4013 image->page.y=mng_info->y_off[mng_info->object_id];
4014 }
4015
cristy3ed852e2009-09-05 21:47:34 +00004016 else
glennrp0fe50b42010-11-16 03:52:51 +00004017 {
4018 image->page.y=mng_info->y_off[mng_info->object_id];
4019 }
4020
cristy3ed852e2009-09-05 21:47:34 +00004021 mng_info->image_found++;
4022 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4023 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004024
cristy3ed852e2009-09-05 21:47:34 +00004025 if (logging != MagickFalse)
4026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4027 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004028
cristy3ed852e2009-09-05 21:47:34 +00004029 return(image);
4030}
4031
4032/*
4033%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4034% %
4035% %
4036% %
4037% R e a d J N G I m a g e %
4038% %
4039% %
4040% %
4041%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4042%
4043% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4044% (including the 8-byte signature) and returns it. It allocates the memory
4045% necessary for the new Image structure and returns a pointer to the new
4046% image.
4047%
4048% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4049%
4050% The format of the ReadJNGImage method is:
4051%
4052% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4053% *exception)
4054%
4055% A description of each parameter follows:
4056%
4057% o image_info: the image info.
4058%
4059% o exception: return any errors or warnings in this structure.
4060%
4061*/
4062
4063static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4064{
4065 Image
4066 *image,
4067 *previous;
4068
4069 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004070 have_mng_structure,
4071 logging,
cristy3ed852e2009-09-05 21:47:34 +00004072 status;
4073
4074 MngInfo
4075 *mng_info;
4076
4077 char
4078 magic_number[MaxTextExtent];
4079
cristy3ed852e2009-09-05 21:47:34 +00004080 size_t
4081 count;
4082
4083 /*
4084 Open image file.
4085 */
4086 assert(image_info != (const ImageInfo *) NULL);
4087 assert(image_info->signature == MagickSignature);
4088 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4089 assert(exception != (ExceptionInfo *) NULL);
4090 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004091 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004092 image=AcquireImage(image_info);
4093 mng_info=(MngInfo *) NULL;
4094 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004095
cristy3ed852e2009-09-05 21:47:34 +00004096 if (status == MagickFalse)
4097 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004098
cristy3ed852e2009-09-05 21:47:34 +00004099 if (LocaleCompare(image_info->magick,"JNG") != 0)
4100 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004101
glennrp47b9dd52010-11-24 18:12:06 +00004102 /* Verify JNG signature. */
4103
cristy3ed852e2009-09-05 21:47:34 +00004104 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004105
glennrp3b8763e2011-02-21 12:08:18 +00004106 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004107 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004108
glennrp47b9dd52010-11-24 18:12:06 +00004109 /* Allocate a MngInfo structure. */
4110
cristy3ed852e2009-09-05 21:47:34 +00004111 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004112 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004113
cristy3ed852e2009-09-05 21:47:34 +00004114 if (mng_info == (MngInfo *) NULL)
4115 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004116
glennrp47b9dd52010-11-24 18:12:06 +00004117 /* Initialize members of the MngInfo structure. */
4118
cristy3ed852e2009-09-05 21:47:34 +00004119 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4120 have_mng_structure=MagickTrue;
4121
4122 mng_info->image=image;
4123 previous=image;
4124 image=ReadOneJNGImage(mng_info,image_info,exception);
4125 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004126
cristy3ed852e2009-09-05 21:47:34 +00004127 if (image == (Image *) NULL)
4128 {
4129 if (IsImageObject(previous) != MagickFalse)
4130 {
4131 (void) CloseBlob(previous);
4132 (void) DestroyImageList(previous);
4133 }
glennrp0fe50b42010-11-16 03:52:51 +00004134
cristy3ed852e2009-09-05 21:47:34 +00004135 if (logging != MagickFalse)
4136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4137 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004138
cristy3ed852e2009-09-05 21:47:34 +00004139 return((Image *) NULL);
4140 }
4141 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004142
cristy3ed852e2009-09-05 21:47:34 +00004143 if (image->columns == 0 || image->rows == 0)
4144 {
4145 if (logging != MagickFalse)
4146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4147 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004148
cristy3ed852e2009-09-05 21:47:34 +00004149 ThrowReaderException(CorruptImageError,"CorruptImage");
4150 }
glennrp0fe50b42010-11-16 03:52:51 +00004151
cristy3ed852e2009-09-05 21:47:34 +00004152 if (logging != MagickFalse)
4153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004154
cristy3ed852e2009-09-05 21:47:34 +00004155 return(image);
4156}
4157#endif
4158
4159static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4160{
4161 char
4162 page_geometry[MaxTextExtent];
4163
4164 Image
4165 *image,
4166 *previous;
4167
cristy4383ec82011-01-05 15:42:32 +00004168 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004169 logging,
4170 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004171
cristy3ed852e2009-09-05 21:47:34 +00004172 volatile int
4173 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004174 object_id,
4175 term_chunk_found,
4176 skip_to_iend;
4177
cristybb503372010-05-27 20:51:26 +00004178 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004179 image_count=0;
4180
4181 MagickBooleanType
4182 status;
4183
4184 MagickOffsetType
4185 offset;
4186
4187 MngInfo
4188 *mng_info;
4189
4190 MngBox
4191 default_fb,
4192 fb,
4193 previous_fb;
4194
4195#if defined(MNG_INSERT_LAYERS)
4196 PixelPacket
4197 mng_background_color;
4198#endif
4199
4200 register unsigned char
4201 *p;
4202
cristybb503372010-05-27 20:51:26 +00004203 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004204 i;
4205
4206 size_t
4207 count;
4208
cristybb503372010-05-27 20:51:26 +00004209 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004210 loop_level;
4211
4212 volatile short
4213 skipping_loop;
4214
4215#if defined(MNG_INSERT_LAYERS)
4216 unsigned int
4217 mandatory_back=0;
4218#endif
4219
4220 volatile unsigned int
4221#ifdef MNG_OBJECT_BUFFERS
4222 mng_background_object=0,
4223#endif
4224 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4225
cristybb503372010-05-27 20:51:26 +00004226 size_t
cristy3ed852e2009-09-05 21:47:34 +00004227 default_frame_timeout,
4228 frame_timeout,
4229#if defined(MNG_INSERT_LAYERS)
4230 image_height,
4231 image_width,
4232#endif
4233 length;
4234
glennrp38ea0832010-06-02 18:50:28 +00004235 /* These delays are all measured in image ticks_per_second,
4236 * not in MNG ticks_per_second
4237 */
cristybb503372010-05-27 20:51:26 +00004238 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004239 default_frame_delay,
4240 final_delay,
4241 final_image_delay,
4242 frame_delay,
4243#if defined(MNG_INSERT_LAYERS)
4244 insert_layers,
4245#endif
4246 mng_iterations=1,
4247 simplicity=0,
4248 subframe_height=0,
4249 subframe_width=0;
4250
4251 previous_fb.top=0;
4252 previous_fb.bottom=0;
4253 previous_fb.left=0;
4254 previous_fb.right=0;
4255 default_fb.top=0;
4256 default_fb.bottom=0;
4257 default_fb.left=0;
4258 default_fb.right=0;
4259
glennrp47b9dd52010-11-24 18:12:06 +00004260 /* Open image file. */
4261
cristy3ed852e2009-09-05 21:47:34 +00004262 assert(image_info != (const ImageInfo *) NULL);
4263 assert(image_info->signature == MagickSignature);
4264 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4265 assert(exception != (ExceptionInfo *) NULL);
4266 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004267 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004268 image=AcquireImage(image_info);
4269 mng_info=(MngInfo *) NULL;
4270 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004271
cristy3ed852e2009-09-05 21:47:34 +00004272 if (status == MagickFalse)
4273 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004274
cristy3ed852e2009-09-05 21:47:34 +00004275 first_mng_object=MagickFalse;
4276 skipping_loop=(-1);
4277 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004278
4279 /* Allocate a MngInfo structure. */
4280
cristy73bd4a52010-10-05 11:24:23 +00004281 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004282
cristy3ed852e2009-09-05 21:47:34 +00004283 if (mng_info == (MngInfo *) NULL)
4284 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004285
glennrp47b9dd52010-11-24 18:12:06 +00004286 /* Initialize members of the MngInfo structure. */
4287
cristy3ed852e2009-09-05 21:47:34 +00004288 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4289 mng_info->image=image;
4290 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004291
4292 if (LocaleCompare(image_info->magick,"MNG") == 0)
4293 {
4294 char
4295 magic_number[MaxTextExtent];
4296
glennrp47b9dd52010-11-24 18:12:06 +00004297 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004298 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4299 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4300 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004301
4302 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004303 for (i=0; i < MNG_MAX_OBJECTS; i++)
4304 {
cristybb503372010-05-27 20:51:26 +00004305 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4306 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004307 }
4308 mng_info->exists[0]=MagickTrue;
4309 }
glennrp47b9dd52010-11-24 18:12:06 +00004310
cristy3ed852e2009-09-05 21:47:34 +00004311 first_mng_object=MagickTrue;
4312 mng_type=0;
4313#if defined(MNG_INSERT_LAYERS)
4314 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4315#endif
4316 default_frame_delay=0;
4317 default_frame_timeout=0;
4318 frame_delay=0;
4319 final_delay=1;
4320 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4321 object_id=0;
4322 skip_to_iend=MagickFalse;
4323 term_chunk_found=MagickFalse;
4324 mng_info->framing_mode=1;
4325#if defined(MNG_INSERT_LAYERS)
4326 mandatory_back=MagickFalse;
4327#endif
4328#if defined(MNG_INSERT_LAYERS)
4329 mng_background_color=image->background_color;
4330#endif
4331 default_fb=mng_info->frame;
4332 previous_fb=mng_info->frame;
4333 do
4334 {
4335 char
4336 type[MaxTextExtent];
4337
4338 if (LocaleCompare(image_info->magick,"MNG") == 0)
4339 {
4340 unsigned char
4341 *chunk;
4342
4343 /*
4344 Read a new chunk.
4345 */
4346 type[0]='\0';
4347 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4348 length=ReadBlobMSBLong(image);
4349 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4350
4351 if (logging != MagickFalse)
4352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004353 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4354 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004355
4356 if (length > PNG_UINT_31_MAX)
4357 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004358
cristy3ed852e2009-09-05 21:47:34 +00004359 if (count == 0)
4360 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004361
cristy3ed852e2009-09-05 21:47:34 +00004362 p=NULL;
4363 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004364
cristy3ed852e2009-09-05 21:47:34 +00004365 if (length)
4366 {
4367 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004368
cristy3ed852e2009-09-05 21:47:34 +00004369 if (chunk == (unsigned char *) NULL)
4370 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004371
cristybb503372010-05-27 20:51:26 +00004372 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004373 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004374
cristy3ed852e2009-09-05 21:47:34 +00004375 p=chunk;
4376 }
glennrp0fe50b42010-11-16 03:52:51 +00004377
cristy3ed852e2009-09-05 21:47:34 +00004378 (void) ReadBlobMSBLong(image); /* read crc word */
4379
4380#if !defined(JNG_SUPPORTED)
4381 if (memcmp(type,mng_JHDR,4) == 0)
4382 {
4383 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004384
cristy3ed852e2009-09-05 21:47:34 +00004385 if (mng_info->jhdr_warning == 0)
4386 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4387 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004388
cristy3ed852e2009-09-05 21:47:34 +00004389 mng_info->jhdr_warning++;
4390 }
4391#endif
4392 if (memcmp(type,mng_DHDR,4) == 0)
4393 {
4394 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004395
cristy3ed852e2009-09-05 21:47:34 +00004396 if (mng_info->dhdr_warning == 0)
4397 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4398 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004399
cristy3ed852e2009-09-05 21:47:34 +00004400 mng_info->dhdr_warning++;
4401 }
4402 if (memcmp(type,mng_MEND,4) == 0)
4403 break;
glennrp47b9dd52010-11-24 18:12:06 +00004404
cristy3ed852e2009-09-05 21:47:34 +00004405 if (skip_to_iend)
4406 {
4407 if (memcmp(type,mng_IEND,4) == 0)
4408 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004409
cristy3ed852e2009-09-05 21:47:34 +00004410 if (length)
4411 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004412
cristy3ed852e2009-09-05 21:47:34 +00004413 if (logging != MagickFalse)
4414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4415 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004416
cristy3ed852e2009-09-05 21:47:34 +00004417 continue;
4418 }
glennrp0fe50b42010-11-16 03:52:51 +00004419
cristy3ed852e2009-09-05 21:47:34 +00004420 if (memcmp(type,mng_MHDR,4) == 0)
4421 {
cristybb503372010-05-27 20:51:26 +00004422 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004423 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004424
cristybb503372010-05-27 20:51:26 +00004425 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004426 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004427
cristy3ed852e2009-09-05 21:47:34 +00004428 if (logging != MagickFalse)
4429 {
4430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004431 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004433 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004434 }
glennrp0fe50b42010-11-16 03:52:51 +00004435
cristy3ed852e2009-09-05 21:47:34 +00004436 p+=8;
cristy8182b072010-05-30 20:10:53 +00004437 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004438
cristy3ed852e2009-09-05 21:47:34 +00004439 if (mng_info->ticks_per_second == 0)
4440 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004441
cristy3ed852e2009-09-05 21:47:34 +00004442 else
4443 default_frame_delay=1UL*image->ticks_per_second/
4444 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004445
cristy3ed852e2009-09-05 21:47:34 +00004446 frame_delay=default_frame_delay;
4447 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004448
cristy3ed852e2009-09-05 21:47:34 +00004449 if (length > 16)
4450 {
4451 p+=16;
cristy8182b072010-05-30 20:10:53 +00004452 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004453 }
glennrp0fe50b42010-11-16 03:52:51 +00004454
cristy3ed852e2009-09-05 21:47:34 +00004455 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004456
cristy3ed852e2009-09-05 21:47:34 +00004457 if ((simplicity != 0) && ((simplicity | 11) == 11))
4458 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004459
cristy3ed852e2009-09-05 21:47:34 +00004460 if ((simplicity != 0) && ((simplicity | 9) == 9))
4461 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004462
cristy3ed852e2009-09-05 21:47:34 +00004463#if defined(MNG_INSERT_LAYERS)
4464 if (mng_type != 3)
4465 insert_layers=MagickTrue;
4466#endif
4467 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4468 {
glennrp47b9dd52010-11-24 18:12:06 +00004469 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004470 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004471
cristy3ed852e2009-09-05 21:47:34 +00004472 if (GetNextImageInList(image) == (Image *) NULL)
4473 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004474
cristy3ed852e2009-09-05 21:47:34 +00004475 image=SyncNextImageInList(image);
4476 mng_info->image=image;
4477 }
4478
4479 if ((mng_info->mng_width > 65535L) ||
4480 (mng_info->mng_height > 65535L))
4481 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004482
cristye8c25f92010-06-03 00:53:06 +00004483 (void) FormatMagickString(page_geometry,MaxTextExtent,
4484 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004485 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004486
cristy3ed852e2009-09-05 21:47:34 +00004487 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004488 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004489 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004490 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004491 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004492
cristy3ed852e2009-09-05 21:47:34 +00004493 for (i=0; i < MNG_MAX_OBJECTS; i++)
4494 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004495
cristy3ed852e2009-09-05 21:47:34 +00004496 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4497 continue;
4498 }
4499
4500 if (memcmp(type,mng_TERM,4) == 0)
4501 {
4502 int
4503 repeat=0;
4504
4505
4506 if (length)
4507 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004508
cristy3ed852e2009-09-05 21:47:34 +00004509 if (repeat == 3)
4510 {
cristy8182b072010-05-30 20:10:53 +00004511 final_delay=(png_uint_32) mng_get_long(&p[2]);
4512 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004513
cristy3ed852e2009-09-05 21:47:34 +00004514 if (mng_iterations == PNG_UINT_31_MAX)
4515 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004516
cristy3ed852e2009-09-05 21:47:34 +00004517 image->iterations=mng_iterations;
4518 term_chunk_found=MagickTrue;
4519 }
glennrp0fe50b42010-11-16 03:52:51 +00004520
cristy3ed852e2009-09-05 21:47:34 +00004521 if (logging != MagickFalse)
4522 {
4523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4524 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004525
cristy3ed852e2009-09-05 21:47:34 +00004526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004527 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004528
cristy3ed852e2009-09-05 21:47:34 +00004529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004530 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004531 }
glennrp0fe50b42010-11-16 03:52:51 +00004532
cristy3ed852e2009-09-05 21:47:34 +00004533 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4534 continue;
4535 }
4536 if (memcmp(type,mng_DEFI,4) == 0)
4537 {
4538 if (mng_type == 3)
4539 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4540 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4541 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004542
cristy3ed852e2009-09-05 21:47:34 +00004543 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004544
cristy3ed852e2009-09-05 21:47:34 +00004545 if (mng_type == 2 && object_id != 0)
4546 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4547 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4548 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004549
cristy3ed852e2009-09-05 21:47:34 +00004550 if (object_id > MNG_MAX_OBJECTS)
4551 {
4552 /*
4553 Instead ofsuing a warning we should allocate a larger
4554 MngInfo structure and continue.
4555 */
4556 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4557 CoderError,"object id too large","`%s'",image->filename);
4558 object_id=MNG_MAX_OBJECTS;
4559 }
glennrp0fe50b42010-11-16 03:52:51 +00004560
cristy3ed852e2009-09-05 21:47:34 +00004561 if (mng_info->exists[object_id])
4562 if (mng_info->frozen[object_id])
4563 {
4564 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4565 (void) ThrowMagickException(&image->exception,
4566 GetMagickModule(),CoderError,
4567 "DEFI cannot redefine a frozen MNG object","`%s'",
4568 image->filename);
4569 continue;
4570 }
glennrp0fe50b42010-11-16 03:52:51 +00004571
cristy3ed852e2009-09-05 21:47:34 +00004572 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004573
cristy3ed852e2009-09-05 21:47:34 +00004574 if (length > 2)
4575 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004576
cristy3ed852e2009-09-05 21:47:34 +00004577 /*
4578 Extract object offset info.
4579 */
4580 if (length > 11)
4581 {
glennrp0fe50b42010-11-16 03:52:51 +00004582 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4583 (p[5] << 16) | (p[6] << 8) | p[7]);
4584
4585 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4586 (p[9] << 16) | (p[10] << 8) | p[11]);
4587
cristy3ed852e2009-09-05 21:47:34 +00004588 if (logging != MagickFalse)
4589 {
4590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004591 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004592 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004593
cristy3ed852e2009-09-05 21:47:34 +00004594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004595 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004596 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004597 }
4598 }
glennrp0fe50b42010-11-16 03:52:51 +00004599
cristy3ed852e2009-09-05 21:47:34 +00004600 /*
4601 Extract object clipping info.
4602 */
4603 if (length > 27)
4604 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4605 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004606
cristy3ed852e2009-09-05 21:47:34 +00004607 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4608 continue;
4609 }
4610 if (memcmp(type,mng_bKGD,4) == 0)
4611 {
4612 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004613
cristy3ed852e2009-09-05 21:47:34 +00004614 if (length > 5)
4615 {
4616 mng_info->mng_global_bkgd.red=
4617 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004618
cristy3ed852e2009-09-05 21:47:34 +00004619 mng_info->mng_global_bkgd.green=
4620 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004621
cristy3ed852e2009-09-05 21:47:34 +00004622 mng_info->mng_global_bkgd.blue=
4623 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004624
cristy3ed852e2009-09-05 21:47:34 +00004625 mng_info->have_global_bkgd=MagickTrue;
4626 }
glennrp0fe50b42010-11-16 03:52:51 +00004627
cristy3ed852e2009-09-05 21:47:34 +00004628 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4629 continue;
4630 }
4631 if (memcmp(type,mng_BACK,4) == 0)
4632 {
4633#if defined(MNG_INSERT_LAYERS)
4634 if (length > 6)
4635 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00004636
cristy3ed852e2009-09-05 21:47:34 +00004637 else
4638 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00004639
cristy3ed852e2009-09-05 21:47:34 +00004640 if (mandatory_back && length > 5)
4641 {
4642 mng_background_color.red=
4643 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004644
cristy3ed852e2009-09-05 21:47:34 +00004645 mng_background_color.green=
4646 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004647
cristy3ed852e2009-09-05 21:47:34 +00004648 mng_background_color.blue=
4649 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004650
cristy3ed852e2009-09-05 21:47:34 +00004651 mng_background_color.opacity=OpaqueOpacity;
4652 }
glennrp0fe50b42010-11-16 03:52:51 +00004653
cristy3ed852e2009-09-05 21:47:34 +00004654#ifdef MNG_OBJECT_BUFFERS
4655 if (length > 8)
4656 mng_background_object=(p[7] << 8) | p[8];
4657#endif
4658#endif
4659 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4660 continue;
4661 }
glennrp47b9dd52010-11-24 18:12:06 +00004662
cristy3ed852e2009-09-05 21:47:34 +00004663 if (memcmp(type,mng_PLTE,4) == 0)
4664 {
glennrp47b9dd52010-11-24 18:12:06 +00004665 /* Read global PLTE. */
4666
cristy3ed852e2009-09-05 21:47:34 +00004667 if (length && (length < 769))
4668 {
4669 if (mng_info->global_plte == (png_colorp) NULL)
4670 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4671 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00004672
cristybb503372010-05-27 20:51:26 +00004673 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004674 {
4675 mng_info->global_plte[i].red=p[3*i];
4676 mng_info->global_plte[i].green=p[3*i+1];
4677 mng_info->global_plte[i].blue=p[3*i+2];
4678 }
glennrp0fe50b42010-11-16 03:52:51 +00004679
cristy35ef8242010-06-03 16:24:13 +00004680 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00004681 }
4682#ifdef MNG_LOOSE
4683 for ( ; i < 256; i++)
4684 {
4685 mng_info->global_plte[i].red=i;
4686 mng_info->global_plte[i].green=i;
4687 mng_info->global_plte[i].blue=i;
4688 }
glennrp0fe50b42010-11-16 03:52:51 +00004689
cristy3ed852e2009-09-05 21:47:34 +00004690 if (length)
4691 mng_info->global_plte_length=256;
4692#endif
4693 else
4694 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00004695
cristy3ed852e2009-09-05 21:47:34 +00004696 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4697 continue;
4698 }
glennrp47b9dd52010-11-24 18:12:06 +00004699
cristy3ed852e2009-09-05 21:47:34 +00004700 if (memcmp(type,mng_tRNS,4) == 0)
4701 {
4702 /* read global tRNS */
4703
4704 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004705 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004706 mng_info->global_trns[i]=p[i];
4707
4708#ifdef MNG_LOOSE
4709 for ( ; i < 256; i++)
4710 mng_info->global_trns[i]=255;
4711#endif
cristy12560f32010-06-03 16:51:08 +00004712 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00004713 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4714 continue;
4715 }
4716 if (memcmp(type,mng_gAMA,4) == 0)
4717 {
4718 if (length == 4)
4719 {
cristybb503372010-05-27 20:51:26 +00004720 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004721 igamma;
4722
cristy8182b072010-05-30 20:10:53 +00004723 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004724 mng_info->global_gamma=((float) igamma)*0.00001;
4725 mng_info->have_global_gama=MagickTrue;
4726 }
glennrp0fe50b42010-11-16 03:52:51 +00004727
cristy3ed852e2009-09-05 21:47:34 +00004728 else
4729 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004730
cristy3ed852e2009-09-05 21:47:34 +00004731 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4732 continue;
4733 }
4734
4735 if (memcmp(type,mng_cHRM,4) == 0)
4736 {
glennrp47b9dd52010-11-24 18:12:06 +00004737 /* Read global cHRM */
4738
cristy3ed852e2009-09-05 21:47:34 +00004739 if (length == 32)
4740 {
cristy8182b072010-05-30 20:10:53 +00004741 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4742 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4743 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004744 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004745 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004746 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004747 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004748 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004749 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004750 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004751 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004752 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004753 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004754 mng_info->have_global_chrm=MagickTrue;
4755 }
4756 else
4757 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004758
cristy3ed852e2009-09-05 21:47:34 +00004759 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4760 continue;
4761 }
glennrp47b9dd52010-11-24 18:12:06 +00004762
cristy3ed852e2009-09-05 21:47:34 +00004763 if (memcmp(type,mng_sRGB,4) == 0)
4764 {
4765 /*
4766 Read global sRGB.
4767 */
4768 if (length)
4769 {
glennrpe610a072010-08-05 17:08:46 +00004770 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00004771 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004772 mng_info->have_global_srgb=MagickTrue;
4773 }
4774 else
4775 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004776
cristy3ed852e2009-09-05 21:47:34 +00004777 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4778 continue;
4779 }
glennrp47b9dd52010-11-24 18:12:06 +00004780
cristy3ed852e2009-09-05 21:47:34 +00004781 if (memcmp(type,mng_iCCP,4) == 0)
4782 {
glennrpfd05d622011-02-25 04:10:33 +00004783 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004784
4785 /*
4786 Read global iCCP.
4787 */
4788 if (length)
4789 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004790
cristy3ed852e2009-09-05 21:47:34 +00004791 continue;
4792 }
glennrp47b9dd52010-11-24 18:12:06 +00004793
cristy3ed852e2009-09-05 21:47:34 +00004794 if (memcmp(type,mng_FRAM,4) == 0)
4795 {
4796 if (mng_type == 3)
4797 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4798 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4799 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004800
cristy3ed852e2009-09-05 21:47:34 +00004801 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4802 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004803
cristy3ed852e2009-09-05 21:47:34 +00004804 frame_delay=default_frame_delay;
4805 frame_timeout=default_frame_timeout;
4806 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00004807
cristy3ed852e2009-09-05 21:47:34 +00004808 if (length)
4809 if (p[0])
4810 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004811
cristy3ed852e2009-09-05 21:47:34 +00004812 if (logging != MagickFalse)
4813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4814 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00004815
cristy3ed852e2009-09-05 21:47:34 +00004816 if (length > 6)
4817 {
glennrp47b9dd52010-11-24 18:12:06 +00004818 /* Note the delay and frame clipping boundaries. */
4819
cristy3ed852e2009-09-05 21:47:34 +00004820 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00004821
cristybb503372010-05-27 20:51:26 +00004822 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004823 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00004824
cristy3ed852e2009-09-05 21:47:34 +00004825 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00004826
cristybb503372010-05-27 20:51:26 +00004827 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004828 {
4829 int
4830 change_delay,
4831 change_timeout,
4832 change_clipping;
4833
4834 change_delay=(*p++);
4835 change_timeout=(*p++);
4836 change_clipping=(*p++);
4837 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00004838
cristy3ed852e2009-09-05 21:47:34 +00004839 if (change_delay)
4840 {
cristy8182b072010-05-30 20:10:53 +00004841 frame_delay=1UL*image->ticks_per_second*
4842 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004843
cristy8182b072010-05-30 20:10:53 +00004844 if (mng_info->ticks_per_second != 0)
4845 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004846
glennrpbb010dd2010-06-01 13:07:15 +00004847 else
4848 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004849
cristy3ed852e2009-09-05 21:47:34 +00004850 if (change_delay == 2)
4851 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004852
cristy3ed852e2009-09-05 21:47:34 +00004853 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 if (logging != MagickFalse)
4856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004857 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004858 }
glennrp47b9dd52010-11-24 18:12:06 +00004859
cristy3ed852e2009-09-05 21:47:34 +00004860 if (change_timeout)
4861 {
glennrpbb010dd2010-06-01 13:07:15 +00004862 frame_timeout=1UL*image->ticks_per_second*
4863 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004864
glennrpbb010dd2010-06-01 13:07:15 +00004865 if (mng_info->ticks_per_second != 0)
4866 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004867
glennrpbb010dd2010-06-01 13:07:15 +00004868 else
4869 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004870
cristy3ed852e2009-09-05 21:47:34 +00004871 if (change_delay == 2)
4872 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00004873
cristy3ed852e2009-09-05 21:47:34 +00004874 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004875
cristy3ed852e2009-09-05 21:47:34 +00004876 if (logging != MagickFalse)
4877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004878 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004879 }
glennrp47b9dd52010-11-24 18:12:06 +00004880
cristy3ed852e2009-09-05 21:47:34 +00004881 if (change_clipping)
4882 {
4883 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4884 p+=17;
4885 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00004886
cristy3ed852e2009-09-05 21:47:34 +00004887 if (logging != MagickFalse)
4888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004889 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004890 (double) fb.left,(double) fb.right,(double) fb.top,
4891 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00004892
cristy3ed852e2009-09-05 21:47:34 +00004893 if (change_clipping == 2)
4894 default_fb=fb;
4895 }
4896 }
4897 }
4898 mng_info->clip=fb;
4899 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00004900
cristybb503372010-05-27 20:51:26 +00004901 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004902 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00004903
cristybb503372010-05-27 20:51:26 +00004904 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004905 -mng_info->clip.top);
4906 /*
4907 Insert a background layer behind the frame if framing_mode is 4.
4908 */
4909#if defined(MNG_INSERT_LAYERS)
4910 if (logging != MagickFalse)
4911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004912 " subframe_width=%.20g, subframe_height=%.20g",(double)
4913 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00004914
cristy3ed852e2009-09-05 21:47:34 +00004915 if (insert_layers && (mng_info->framing_mode == 4) &&
4916 (subframe_width) && (subframe_height))
4917 {
glennrp47b9dd52010-11-24 18:12:06 +00004918 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004919 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4920 {
4921 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00004922
cristy3ed852e2009-09-05 21:47:34 +00004923 if (GetNextImageInList(image) == (Image *) NULL)
4924 {
4925 image=DestroyImageList(image);
4926 MngInfoFreeStruct(mng_info,&have_mng_structure);
4927 return((Image *) NULL);
4928 }
glennrp47b9dd52010-11-24 18:12:06 +00004929
cristy3ed852e2009-09-05 21:47:34 +00004930 image=SyncNextImageInList(image);
4931 }
glennrp0fe50b42010-11-16 03:52:51 +00004932
cristy3ed852e2009-09-05 21:47:34 +00004933 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00004934
cristy3ed852e2009-09-05 21:47:34 +00004935 if (term_chunk_found)
4936 {
4937 image->start_loop=MagickTrue;
4938 image->iterations=mng_iterations;
4939 term_chunk_found=MagickFalse;
4940 }
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 else
4943 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004944
cristy3ed852e2009-09-05 21:47:34 +00004945 image->columns=subframe_width;
4946 image->rows=subframe_height;
4947 image->page.width=subframe_width;
4948 image->page.height=subframe_height;
4949 image->page.x=mng_info->clip.left;
4950 image->page.y=mng_info->clip.top;
4951 image->background_color=mng_background_color;
4952 image->matte=MagickFalse;
4953 image->delay=0;
4954 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00004955
cristy3ed852e2009-09-05 21:47:34 +00004956 if (logging != MagickFalse)
4957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004958 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004959 (double) mng_info->clip.left,(double) mng_info->clip.right,
4960 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004961 }
4962#endif
4963 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4964 continue;
4965 }
4966 if (memcmp(type,mng_CLIP,4) == 0)
4967 {
4968 unsigned int
4969 first_object,
4970 last_object;
4971
4972 /*
4973 Read CLIP.
4974 */
4975 first_object=(p[0] << 8) | p[1];
4976 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00004977
cristy3ed852e2009-09-05 21:47:34 +00004978 for (i=(int) first_object; i <= (int) last_object; i++)
4979 {
4980 if (mng_info->exists[i] && !mng_info->frozen[i])
4981 {
4982 MngBox
4983 box;
4984
4985 box=mng_info->object_clip[i];
4986 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4987 }
4988 }
glennrp47b9dd52010-11-24 18:12:06 +00004989
cristy3ed852e2009-09-05 21:47:34 +00004990 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4991 continue;
4992 }
4993 if (memcmp(type,mng_SAVE,4) == 0)
4994 {
4995 for (i=1; i < MNG_MAX_OBJECTS; i++)
4996 if (mng_info->exists[i])
4997 {
4998 mng_info->frozen[i]=MagickTrue;
4999#ifdef MNG_OBJECT_BUFFERS
5000 if (mng_info->ob[i] != (MngBuffer *) NULL)
5001 mng_info->ob[i]->frozen=MagickTrue;
5002#endif
5003 }
glennrp0fe50b42010-11-16 03:52:51 +00005004
cristy3ed852e2009-09-05 21:47:34 +00005005 if (length)
5006 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005007
cristy3ed852e2009-09-05 21:47:34 +00005008 continue;
5009 }
5010
5011 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5012 {
glennrp47b9dd52010-11-24 18:12:06 +00005013 /* Read DISC or SEEK. */
5014
cristy3ed852e2009-09-05 21:47:34 +00005015 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5016 {
5017 for (i=1; i < MNG_MAX_OBJECTS; i++)
5018 MngInfoDiscardObject(mng_info,i);
5019 }
glennrp0fe50b42010-11-16 03:52:51 +00005020
cristy3ed852e2009-09-05 21:47:34 +00005021 else
5022 {
cristybb503372010-05-27 20:51:26 +00005023 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005024 j;
5025
cristybb503372010-05-27 20:51:26 +00005026 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005027 {
5028 i=p[j] << 8 | p[j+1];
5029 MngInfoDiscardObject(mng_info,i);
5030 }
5031 }
glennrp0fe50b42010-11-16 03:52:51 +00005032
cristy3ed852e2009-09-05 21:47:34 +00005033 if (length)
5034 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005035
cristy3ed852e2009-09-05 21:47:34 +00005036 continue;
5037 }
glennrp47b9dd52010-11-24 18:12:06 +00005038
cristy3ed852e2009-09-05 21:47:34 +00005039 if (memcmp(type,mng_MOVE,4) == 0)
5040 {
cristybb503372010-05-27 20:51:26 +00005041 size_t
cristy3ed852e2009-09-05 21:47:34 +00005042 first_object,
5043 last_object;
5044
glennrp47b9dd52010-11-24 18:12:06 +00005045 /* read MOVE */
5046
cristy3ed852e2009-09-05 21:47:34 +00005047 first_object=(p[0] << 8) | p[1];
5048 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005049 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005050 {
5051 if (mng_info->exists[i] && !mng_info->frozen[i])
5052 {
5053 MngPair
5054 new_pair;
5055
5056 MngPair
5057 old_pair;
5058
5059 old_pair.a=mng_info->x_off[i];
5060 old_pair.b=mng_info->y_off[i];
5061 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5062 mng_info->x_off[i]=new_pair.a;
5063 mng_info->y_off[i]=new_pair.b;
5064 }
5065 }
glennrp47b9dd52010-11-24 18:12:06 +00005066
cristy3ed852e2009-09-05 21:47:34 +00005067 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5068 continue;
5069 }
5070
5071 if (memcmp(type,mng_LOOP,4) == 0)
5072 {
cristybb503372010-05-27 20:51:26 +00005073 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005074 loop_level=chunk[0];
5075 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005076
5077 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005078 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005079
cristy3ed852e2009-09-05 21:47:34 +00005080 if (logging != MagickFalse)
5081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005082 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5083 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005084
cristy3ed852e2009-09-05 21:47:34 +00005085 if (loop_iters == 0)
5086 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005087
cristy3ed852e2009-09-05 21:47:34 +00005088 else
5089 {
5090 mng_info->loop_jump[loop_level]=TellBlob(image);
5091 mng_info->loop_count[loop_level]=loop_iters;
5092 }
glennrp0fe50b42010-11-16 03:52:51 +00005093
cristy3ed852e2009-09-05 21:47:34 +00005094 mng_info->loop_iteration[loop_level]=0;
5095 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5096 continue;
5097 }
glennrp47b9dd52010-11-24 18:12:06 +00005098
cristy3ed852e2009-09-05 21:47:34 +00005099 if (memcmp(type,mng_ENDL,4) == 0)
5100 {
5101 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005102
cristy3ed852e2009-09-05 21:47:34 +00005103 if (skipping_loop > 0)
5104 {
5105 if (skipping_loop == loop_level)
5106 {
5107 /*
5108 Found end of zero-iteration loop.
5109 */
5110 skipping_loop=(-1);
5111 mng_info->loop_active[loop_level]=0;
5112 }
5113 }
glennrp47b9dd52010-11-24 18:12:06 +00005114
cristy3ed852e2009-09-05 21:47:34 +00005115 else
5116 {
5117 if (mng_info->loop_active[loop_level] == 1)
5118 {
5119 mng_info->loop_count[loop_level]--;
5120 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005121
cristy3ed852e2009-09-05 21:47:34 +00005122 if (logging != MagickFalse)
5123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005124 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005125 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005126 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005127
cristy3ed852e2009-09-05 21:47:34 +00005128 if (mng_info->loop_count[loop_level] != 0)
5129 {
5130 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5131 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005132
cristy3ed852e2009-09-05 21:47:34 +00005133 if (offset < 0)
5134 ThrowReaderException(CorruptImageError,
5135 "ImproperImageHeader");
5136 }
glennrp47b9dd52010-11-24 18:12:06 +00005137
cristy3ed852e2009-09-05 21:47:34 +00005138 else
5139 {
5140 short
5141 last_level;
5142
5143 /*
5144 Finished loop.
5145 */
5146 mng_info->loop_active[loop_level]=0;
5147 last_level=(-1);
5148 for (i=0; i < loop_level; i++)
5149 if (mng_info->loop_active[i] == 1)
5150 last_level=(short) i;
5151 loop_level=last_level;
5152 }
5153 }
5154 }
glennrp47b9dd52010-11-24 18:12:06 +00005155
cristy3ed852e2009-09-05 21:47:34 +00005156 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5157 continue;
5158 }
glennrp47b9dd52010-11-24 18:12:06 +00005159
cristy3ed852e2009-09-05 21:47:34 +00005160 if (memcmp(type,mng_CLON,4) == 0)
5161 {
5162 if (mng_info->clon_warning == 0)
5163 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5164 CoderError,"CLON is not implemented yet","`%s'",
5165 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005166
cristy3ed852e2009-09-05 21:47:34 +00005167 mng_info->clon_warning++;
5168 }
glennrp47b9dd52010-11-24 18:12:06 +00005169
cristy3ed852e2009-09-05 21:47:34 +00005170 if (memcmp(type,mng_MAGN,4) == 0)
5171 {
5172 png_uint_16
5173 magn_first,
5174 magn_last,
5175 magn_mb,
5176 magn_ml,
5177 magn_mr,
5178 magn_mt,
5179 magn_mx,
5180 magn_my,
5181 magn_methx,
5182 magn_methy;
5183
5184 if (length > 1)
5185 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005186
cristy3ed852e2009-09-05 21:47:34 +00005187 else
5188 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005189
cristy3ed852e2009-09-05 21:47:34 +00005190 if (length > 3)
5191 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005192
cristy3ed852e2009-09-05 21:47:34 +00005193 else
5194 magn_last=magn_first;
5195#ifndef MNG_OBJECT_BUFFERS
5196 if (magn_first || magn_last)
5197 if (mng_info->magn_warning == 0)
5198 {
5199 (void) ThrowMagickException(&image->exception,
5200 GetMagickModule(),CoderError,
5201 "MAGN is not implemented yet for nonzero objects",
5202 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005203
cristy3ed852e2009-09-05 21:47:34 +00005204 mng_info->magn_warning++;
5205 }
5206#endif
5207 if (length > 4)
5208 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 else
5211 magn_methx=0;
5212
5213 if (length > 6)
5214 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005215
cristy3ed852e2009-09-05 21:47:34 +00005216 else
5217 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005218
cristy3ed852e2009-09-05 21:47:34 +00005219 if (magn_mx == 0)
5220 magn_mx=1;
5221
5222 if (length > 8)
5223 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005224
cristy3ed852e2009-09-05 21:47:34 +00005225 else
5226 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005227
cristy3ed852e2009-09-05 21:47:34 +00005228 if (magn_my == 0)
5229 magn_my=1;
5230
5231 if (length > 10)
5232 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005233
cristy3ed852e2009-09-05 21:47:34 +00005234 else
5235 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005236
cristy3ed852e2009-09-05 21:47:34 +00005237 if (magn_ml == 0)
5238 magn_ml=1;
5239
5240 if (length > 12)
5241 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005242
cristy3ed852e2009-09-05 21:47:34 +00005243 else
5244 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005245
cristy3ed852e2009-09-05 21:47:34 +00005246 if (magn_mr == 0)
5247 magn_mr=1;
5248
5249 if (length > 14)
5250 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005251
cristy3ed852e2009-09-05 21:47:34 +00005252 else
5253 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005254
cristy3ed852e2009-09-05 21:47:34 +00005255 if (magn_mt == 0)
5256 magn_mt=1;
5257
5258 if (length > 16)
5259 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005260
cristy3ed852e2009-09-05 21:47:34 +00005261 else
5262 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005263
cristy3ed852e2009-09-05 21:47:34 +00005264 if (magn_mb == 0)
5265 magn_mb=1;
5266
5267 if (length > 17)
5268 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005269
cristy3ed852e2009-09-05 21:47:34 +00005270 else
5271 magn_methy=magn_methx;
5272
glennrp47b9dd52010-11-24 18:12:06 +00005273
cristy3ed852e2009-09-05 21:47:34 +00005274 if (magn_methx > 5 || magn_methy > 5)
5275 if (mng_info->magn_warning == 0)
5276 {
5277 (void) ThrowMagickException(&image->exception,
5278 GetMagickModule(),CoderError,
5279 "Unknown MAGN method in MNG datastream","`%s'",
5280 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005281
cristy3ed852e2009-09-05 21:47:34 +00005282 mng_info->magn_warning++;
5283 }
5284#ifdef MNG_OBJECT_BUFFERS
5285 /* Magnify existing objects in the range magn_first to magn_last */
5286#endif
5287 if (magn_first == 0 || magn_last == 0)
5288 {
5289 /* Save the magnification factors for object 0 */
5290 mng_info->magn_mb=magn_mb;
5291 mng_info->magn_ml=magn_ml;
5292 mng_info->magn_mr=magn_mr;
5293 mng_info->magn_mt=magn_mt;
5294 mng_info->magn_mx=magn_mx;
5295 mng_info->magn_my=magn_my;
5296 mng_info->magn_methx=magn_methx;
5297 mng_info->magn_methy=magn_methy;
5298 }
5299 }
glennrp47b9dd52010-11-24 18:12:06 +00005300
cristy3ed852e2009-09-05 21:47:34 +00005301 if (memcmp(type,mng_PAST,4) == 0)
5302 {
5303 if (mng_info->past_warning == 0)
5304 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5305 CoderError,"PAST is not implemented yet","`%s'",
5306 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005307
cristy3ed852e2009-09-05 21:47:34 +00005308 mng_info->past_warning++;
5309 }
glennrp47b9dd52010-11-24 18:12:06 +00005310
cristy3ed852e2009-09-05 21:47:34 +00005311 if (memcmp(type,mng_SHOW,4) == 0)
5312 {
5313 if (mng_info->show_warning == 0)
5314 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5315 CoderError,"SHOW is not implemented yet","`%s'",
5316 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005317
cristy3ed852e2009-09-05 21:47:34 +00005318 mng_info->show_warning++;
5319 }
glennrp47b9dd52010-11-24 18:12:06 +00005320
cristy3ed852e2009-09-05 21:47:34 +00005321 if (memcmp(type,mng_sBIT,4) == 0)
5322 {
5323 if (length < 4)
5324 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005325
cristy3ed852e2009-09-05 21:47:34 +00005326 else
5327 {
5328 mng_info->global_sbit.gray=p[0];
5329 mng_info->global_sbit.red=p[0];
5330 mng_info->global_sbit.green=p[1];
5331 mng_info->global_sbit.blue=p[2];
5332 mng_info->global_sbit.alpha=p[3];
5333 mng_info->have_global_sbit=MagickTrue;
5334 }
5335 }
5336 if (memcmp(type,mng_pHYs,4) == 0)
5337 {
5338 if (length > 8)
5339 {
5340 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005341 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005342 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005343 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005344 mng_info->global_phys_unit_type=p[8];
5345 mng_info->have_global_phys=MagickTrue;
5346 }
glennrp47b9dd52010-11-24 18:12:06 +00005347
cristy3ed852e2009-09-05 21:47:34 +00005348 else
5349 mng_info->have_global_phys=MagickFalse;
5350 }
5351 if (memcmp(type,mng_pHYg,4) == 0)
5352 {
5353 if (mng_info->phyg_warning == 0)
5354 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5355 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005356
cristy3ed852e2009-09-05 21:47:34 +00005357 mng_info->phyg_warning++;
5358 }
5359 if (memcmp(type,mng_BASI,4) == 0)
5360 {
5361 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005362
cristy3ed852e2009-09-05 21:47:34 +00005363 if (mng_info->basi_warning == 0)
5364 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5365 CoderError,"BASI is not implemented yet","`%s'",
5366 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005367
cristy3ed852e2009-09-05 21:47:34 +00005368 mng_info->basi_warning++;
5369#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005370 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005371 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005372 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005373 (p[6] << 8) | p[7]);
5374 basi_color_type=p[8];
5375 basi_compression_method=p[9];
5376 basi_filter_type=p[10];
5377 basi_interlace_method=p[11];
5378 if (length > 11)
5379 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005380
cristy3ed852e2009-09-05 21:47:34 +00005381 else
5382 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 if (length > 13)
5385 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387 else
5388 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005389
cristy3ed852e2009-09-05 21:47:34 +00005390 if (length > 15)
5391 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005392
cristy3ed852e2009-09-05 21:47:34 +00005393 else
5394 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005395
cristy3ed852e2009-09-05 21:47:34 +00005396 if (length > 17)
5397 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 else
5400 {
5401 if (basi_sample_depth == 16)
5402 basi_alpha=65535L;
5403 else
5404 basi_alpha=255;
5405 }
glennrp47b9dd52010-11-24 18:12:06 +00005406
cristy3ed852e2009-09-05 21:47:34 +00005407 if (length > 19)
5408 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005409
cristy3ed852e2009-09-05 21:47:34 +00005410 else
5411 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005412
cristy3ed852e2009-09-05 21:47:34 +00005413#endif
5414 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5415 continue;
5416 }
glennrp47b9dd52010-11-24 18:12:06 +00005417
cristy3ed852e2009-09-05 21:47:34 +00005418 if (memcmp(type,mng_IHDR,4)
5419#if defined(JNG_SUPPORTED)
5420 && memcmp(type,mng_JHDR,4)
5421#endif
5422 )
5423 {
5424 /* Not an IHDR or JHDR chunk */
5425 if (length)
5426 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005427
cristy3ed852e2009-09-05 21:47:34 +00005428 continue;
5429 }
5430/* Process IHDR */
5431 if (logging != MagickFalse)
5432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5433 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005434
cristy3ed852e2009-09-05 21:47:34 +00005435 mng_info->exists[object_id]=MagickTrue;
5436 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005437
cristy3ed852e2009-09-05 21:47:34 +00005438 if (mng_info->invisible[object_id])
5439 {
5440 if (logging != MagickFalse)
5441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5442 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005443
cristy3ed852e2009-09-05 21:47:34 +00005444 skip_to_iend=MagickTrue;
5445 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5446 continue;
5447 }
5448#if defined(MNG_INSERT_LAYERS)
5449 if (length < 8)
5450 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005451
cristy8182b072010-05-30 20:10:53 +00005452 image_width=(size_t) mng_get_long(p);
5453 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005454#endif
5455 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5456
5457 /*
5458 Insert a transparent background layer behind the entire animation
5459 if it is not full screen.
5460 */
5461#if defined(MNG_INSERT_LAYERS)
5462 if (insert_layers && mng_type && first_mng_object)
5463 {
5464 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5465 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005466 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005467 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005468 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005469 {
5470 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5471 {
5472 /*
5473 Allocate next image structure.
5474 */
5475 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005476
cristy3ed852e2009-09-05 21:47:34 +00005477 if (GetNextImageInList(image) == (Image *) NULL)
5478 {
5479 image=DestroyImageList(image);
5480 MngInfoFreeStruct(mng_info,&have_mng_structure);
5481 return((Image *) NULL);
5482 }
glennrp47b9dd52010-11-24 18:12:06 +00005483
cristy3ed852e2009-09-05 21:47:34 +00005484 image=SyncNextImageInList(image);
5485 }
5486 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 if (term_chunk_found)
5489 {
5490 image->start_loop=MagickTrue;
5491 image->iterations=mng_iterations;
5492 term_chunk_found=MagickFalse;
5493 }
glennrp47b9dd52010-11-24 18:12:06 +00005494
cristy3ed852e2009-09-05 21:47:34 +00005495 else
5496 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005497
5498 /* Make a background rectangle. */
5499
cristy3ed852e2009-09-05 21:47:34 +00005500 image->delay=0;
5501 image->columns=mng_info->mng_width;
5502 image->rows=mng_info->mng_height;
5503 image->page.width=mng_info->mng_width;
5504 image->page.height=mng_info->mng_height;
5505 image->page.x=0;
5506 image->page.y=0;
5507 image->background_color=mng_background_color;
5508 (void) SetImageBackgroundColor(image);
5509 if (logging != MagickFalse)
5510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005511 " Inserted transparent background layer, W=%.20g, H=%.20g",
5512 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005513 }
5514 }
5515 /*
5516 Insert a background layer behind the upcoming image if
5517 framing_mode is 3, and we haven't already inserted one.
5518 */
5519 if (insert_layers && (mng_info->framing_mode == 3) &&
5520 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5521 (simplicity & 0x08)))
5522 {
5523 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5524 {
5525 /*
5526 Allocate next image structure.
5527 */
5528 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005529
cristy3ed852e2009-09-05 21:47:34 +00005530 if (GetNextImageInList(image) == (Image *) NULL)
5531 {
5532 image=DestroyImageList(image);
5533 MngInfoFreeStruct(mng_info,&have_mng_structure);
5534 return((Image *) NULL);
5535 }
glennrp47b9dd52010-11-24 18:12:06 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 image=SyncNextImageInList(image);
5538 }
glennrp0fe50b42010-11-16 03:52:51 +00005539
cristy3ed852e2009-09-05 21:47:34 +00005540 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005541
cristy3ed852e2009-09-05 21:47:34 +00005542 if (term_chunk_found)
5543 {
5544 image->start_loop=MagickTrue;
5545 image->iterations=mng_iterations;
5546 term_chunk_found=MagickFalse;
5547 }
glennrp0fe50b42010-11-16 03:52:51 +00005548
cristy3ed852e2009-09-05 21:47:34 +00005549 else
5550 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005551
cristy3ed852e2009-09-05 21:47:34 +00005552 image->delay=0;
5553 image->columns=subframe_width;
5554 image->rows=subframe_height;
5555 image->page.width=subframe_width;
5556 image->page.height=subframe_height;
5557 image->page.x=mng_info->clip.left;
5558 image->page.y=mng_info->clip.top;
5559 image->background_color=mng_background_color;
5560 image->matte=MagickFalse;
5561 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005562
cristy3ed852e2009-09-05 21:47:34 +00005563 if (logging != MagickFalse)
5564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005565 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005566 (double) mng_info->clip.left,(double) mng_info->clip.right,
5567 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005568 }
5569#endif /* MNG_INSERT_LAYERS */
5570 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005571
cristy3ed852e2009-09-05 21:47:34 +00005572 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5573 {
5574 /*
5575 Allocate next image structure.
5576 */
5577 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005578
cristy3ed852e2009-09-05 21:47:34 +00005579 if (GetNextImageInList(image) == (Image *) NULL)
5580 {
5581 image=DestroyImageList(image);
5582 MngInfoFreeStruct(mng_info,&have_mng_structure);
5583 return((Image *) NULL);
5584 }
glennrp47b9dd52010-11-24 18:12:06 +00005585
cristy3ed852e2009-09-05 21:47:34 +00005586 image=SyncNextImageInList(image);
5587 }
5588 mng_info->image=image;
5589 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5590 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005591
cristy3ed852e2009-09-05 21:47:34 +00005592 if (status == MagickFalse)
5593 break;
glennrp0fe50b42010-11-16 03:52:51 +00005594
cristy3ed852e2009-09-05 21:47:34 +00005595 if (term_chunk_found)
5596 {
5597 image->start_loop=MagickTrue;
5598 term_chunk_found=MagickFalse;
5599 }
glennrp0fe50b42010-11-16 03:52:51 +00005600
cristy3ed852e2009-09-05 21:47:34 +00005601 else
5602 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005603
cristy3ed852e2009-09-05 21:47:34 +00005604 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5605 {
5606 image->delay=frame_delay;
5607 frame_delay=default_frame_delay;
5608 }
glennrp0fe50b42010-11-16 03:52:51 +00005609
cristy3ed852e2009-09-05 21:47:34 +00005610 else
5611 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005612
cristy3ed852e2009-09-05 21:47:34 +00005613 image->page.width=mng_info->mng_width;
5614 image->page.height=mng_info->mng_height;
5615 image->page.x=mng_info->x_off[object_id];
5616 image->page.y=mng_info->y_off[object_id];
5617 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 /*
5620 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5621 */
glennrp47b9dd52010-11-24 18:12:06 +00005622
cristy3ed852e2009-09-05 21:47:34 +00005623 if (logging != MagickFalse)
5624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5625 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5626 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005627
cristybb503372010-05-27 20:51:26 +00005628 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00005629
cristy3ed852e2009-09-05 21:47:34 +00005630 if (offset < 0)
5631 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5632 }
5633
5634 previous=image;
5635 mng_info->image=image;
5636 mng_info->mng_type=mng_type;
5637 mng_info->object_id=object_id;
5638
5639 if (memcmp(type,mng_IHDR,4) == 0)
5640 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005641
cristy3ed852e2009-09-05 21:47:34 +00005642#if defined(JNG_SUPPORTED)
5643 else
5644 image=ReadOneJNGImage(mng_info,image_info,exception);
5645#endif
5646
5647 if (image == (Image *) NULL)
5648 {
5649 if (IsImageObject(previous) != MagickFalse)
5650 {
5651 (void) DestroyImageList(previous);
5652 (void) CloseBlob(previous);
5653 }
glennrp47b9dd52010-11-24 18:12:06 +00005654
cristy3ed852e2009-09-05 21:47:34 +00005655 MngInfoFreeStruct(mng_info,&have_mng_structure);
5656 return((Image *) NULL);
5657 }
glennrp0fe50b42010-11-16 03:52:51 +00005658
cristy3ed852e2009-09-05 21:47:34 +00005659 if (image->columns == 0 || image->rows == 0)
5660 {
5661 (void) CloseBlob(image);
5662 image=DestroyImageList(image);
5663 MngInfoFreeStruct(mng_info,&have_mng_structure);
5664 return((Image *) NULL);
5665 }
glennrp0fe50b42010-11-16 03:52:51 +00005666
cristy3ed852e2009-09-05 21:47:34 +00005667 mng_info->image=image;
5668
5669 if (mng_type)
5670 {
5671 MngBox
5672 crop_box;
5673
5674 if (mng_info->magn_methx || mng_info->magn_methy)
5675 {
5676 png_uint_32
5677 magnified_height,
5678 magnified_width;
5679
5680 if (logging != MagickFalse)
5681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5682 " Processing MNG MAGN chunk");
5683
5684 if (mng_info->magn_methx == 1)
5685 {
5686 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005687
cristy3ed852e2009-09-05 21:47:34 +00005688 if (image->columns > 1)
5689 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005690
cristy3ed852e2009-09-05 21:47:34 +00005691 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005692 magnified_width += (png_uint_32)
5693 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00005694 }
glennrp47b9dd52010-11-24 18:12:06 +00005695
cristy3ed852e2009-09-05 21:47:34 +00005696 else
5697 {
cristy4e5bc842010-06-09 13:56:01 +00005698 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00005699
cristy3ed852e2009-09-05 21:47:34 +00005700 if (image->columns > 1)
5701 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00005702
cristy3ed852e2009-09-05 21:47:34 +00005703 if (image->columns > 2)
5704 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00005705
cristy3ed852e2009-09-05 21:47:34 +00005706 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005707 magnified_width += (png_uint_32)
5708 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00005709 }
glennrp47b9dd52010-11-24 18:12:06 +00005710
cristy3ed852e2009-09-05 21:47:34 +00005711 if (mng_info->magn_methy == 1)
5712 {
5713 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005714
cristy3ed852e2009-09-05 21:47:34 +00005715 if (image->rows > 1)
5716 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005717
cristy3ed852e2009-09-05 21:47:34 +00005718 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005719 magnified_height += (png_uint_32)
5720 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00005721 }
glennrp47b9dd52010-11-24 18:12:06 +00005722
cristy3ed852e2009-09-05 21:47:34 +00005723 else
5724 {
cristy4e5bc842010-06-09 13:56:01 +00005725 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00005726
cristy3ed852e2009-09-05 21:47:34 +00005727 if (image->rows > 1)
5728 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00005729
cristy3ed852e2009-09-05 21:47:34 +00005730 if (image->rows > 2)
5731 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00005732
cristy3ed852e2009-09-05 21:47:34 +00005733 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005734 magnified_height += (png_uint_32)
5735 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00005736 }
glennrp47b9dd52010-11-24 18:12:06 +00005737
cristy3ed852e2009-09-05 21:47:34 +00005738 if (magnified_height > image->rows ||
5739 magnified_width > image->columns)
5740 {
5741 Image
5742 *large_image;
5743
5744 int
5745 yy;
5746
cristybb503372010-05-27 20:51:26 +00005747 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005748 m,
5749 y;
5750
cristybb503372010-05-27 20:51:26 +00005751 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005752 x;
5753
5754 register PixelPacket
5755 *n,
5756 *q;
5757
5758 PixelPacket
5759 *next,
5760 *prev;
5761
5762 png_uint_16
5763 magn_methx,
5764 magn_methy;
5765
glennrp47b9dd52010-11-24 18:12:06 +00005766 /* Allocate next image structure. */
5767
cristy3ed852e2009-09-05 21:47:34 +00005768 if (logging != MagickFalse)
5769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5770 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00005771
cristy3ed852e2009-09-05 21:47:34 +00005772 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005773
cristy3ed852e2009-09-05 21:47:34 +00005774 if (GetNextImageInList(image) == (Image *) NULL)
5775 {
5776 image=DestroyImageList(image);
5777 MngInfoFreeStruct(mng_info,&have_mng_structure);
5778 return((Image *) NULL);
5779 }
5780
5781 large_image=SyncNextImageInList(image);
5782
5783 large_image->columns=magnified_width;
5784 large_image->rows=magnified_height;
5785
5786 magn_methx=mng_info->magn_methx;
5787 magn_methy=mng_info->magn_methy;
5788
glennrp3faa9a32011-04-23 14:00:25 +00005789#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00005790#define QM unsigned short
5791 if (magn_methx != 1 || magn_methy != 1)
5792 {
5793 /*
5794 Scale pixels to unsigned shorts to prevent
5795 overflow of intermediate values of interpolations
5796 */
cristybb503372010-05-27 20:51:26 +00005797 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005798 {
5799 q=GetAuthenticPixels(image,0,y,image->columns,1,
5800 exception);
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristybb503372010-05-27 20:51:26 +00005802 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005803 {
glennrp7c7b3152011-04-26 04:01:27 +00005804 SetRedPixelComponent(q,ScaleQuantumToShort(
5805 GetRedPixelComponent(q));
5806 SetGreenPixelComponent(q,ScaleQuantumToShort(
5807 GetGreenPixelComponent(q));
5808 SetBluePixelComponent(q,ScaleQuantumToShort(
5809 GetBluePixelComponent(q));
5810 SetOpacityPixelComponent(q,ScaleQuantumToShort(
5811 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00005812 q++;
5813 }
glennrp47b9dd52010-11-24 18:12:06 +00005814
cristy3ed852e2009-09-05 21:47:34 +00005815 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5816 break;
5817 }
5818 }
5819#else
5820#define QM Quantum
5821#endif
5822
5823 if (image->matte != MagickFalse)
5824 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005825
cristy3ed852e2009-09-05 21:47:34 +00005826 else
5827 {
5828 large_image->background_color.opacity=OpaqueOpacity;
5829 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005830
cristy3ed852e2009-09-05 21:47:34 +00005831 if (magn_methx == 4)
5832 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00005833
cristy3ed852e2009-09-05 21:47:34 +00005834 if (magn_methx == 5)
5835 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00005836
cristy3ed852e2009-09-05 21:47:34 +00005837 if (magn_methy == 4)
5838 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00005839
cristy3ed852e2009-09-05 21:47:34 +00005840 if (magn_methy == 5)
5841 magn_methy=3;
5842 }
5843
5844 /* magnify the rows into the right side of the large image */
5845
5846 if (logging != MagickFalse)
5847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005848 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00005849 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005850 yy=0;
5851 length=(size_t) image->columns;
5852 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5853 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00005854
cristy3ed852e2009-09-05 21:47:34 +00005855 if ((prev == (PixelPacket *) NULL) ||
5856 (next == (PixelPacket *) NULL))
5857 {
5858 image=DestroyImageList(image);
5859 MngInfoFreeStruct(mng_info,&have_mng_structure);
5860 ThrowReaderException(ResourceLimitError,
5861 "MemoryAllocationFailed");
5862 }
glennrp47b9dd52010-11-24 18:12:06 +00005863
cristy3ed852e2009-09-05 21:47:34 +00005864 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5865 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00005866
cristybb503372010-05-27 20:51:26 +00005867 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005868 {
5869 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005870 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005871
cristybb503372010-05-27 20:51:26 +00005872 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5873 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005874
cristybb503372010-05-27 20:51:26 +00005875 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5876 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005877
cristybb503372010-05-27 20:51:26 +00005878 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005879 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005880
cristy3ed852e2009-09-05 21:47:34 +00005881 else
cristybb503372010-05-27 20:51:26 +00005882 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005883
cristy3ed852e2009-09-05 21:47:34 +00005884 n=prev;
5885 prev=next;
5886 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00005887
cristybb503372010-05-27 20:51:26 +00005888 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005889 {
5890 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5891 exception);
5892 (void) CopyMagickMemory(next,n,length);
5893 }
glennrp47b9dd52010-11-24 18:12:06 +00005894
cristy3ed852e2009-09-05 21:47:34 +00005895 for (i=0; i < m; i++, yy++)
5896 {
glennrp7c7b3152011-04-26 04:01:27 +00005897 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00005898 register PixelPacket
5899 *pixels;
5900
cristybb503372010-05-27 20:51:26 +00005901 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005902 pixels=prev;
5903 n=next;
5904 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5905 1,exception);
5906 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00005907
cristybb503372010-05-27 20:51:26 +00005908 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005909 {
glennrpfd05d622011-02-25 04:10:33 +00005910 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005911 /*
5912 if (image->storage_class == PseudoClass)
5913 {
5914 }
5915 */
5916
5917 if (magn_methy <= 1)
5918 {
5919 *q=(*pixels); /* replicate previous */
5920 }
glennrp47b9dd52010-11-24 18:12:06 +00005921
cristy3ed852e2009-09-05 21:47:34 +00005922 else if (magn_methy == 2 || magn_methy == 4)
5923 {
5924 if (i == 0)
5925 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005926
cristy3ed852e2009-09-05 21:47:34 +00005927 else
5928 {
5929 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005930 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5931 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005932 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005933 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5934 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005935 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005936 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5937 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005938 +(*pixels).blue);
glennrp47b9dd52010-11-24 18:12:06 +00005939
cristy3ed852e2009-09-05 21:47:34 +00005940 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005941 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005942 (2*i*((*n).opacity
5943 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005944 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005945 }
glennrp47b9dd52010-11-24 18:12:06 +00005946
cristy3ed852e2009-09-05 21:47:34 +00005947 if (magn_methy == 4)
5948 {
5949 /* Replicate nearest */
5950 if (i <= ((m+1) << 1))
5951 (*q).opacity=(*pixels).opacity+0;
5952 else
5953 (*q).opacity=(*n).opacity+0;
5954 }
5955 }
glennrp47b9dd52010-11-24 18:12:06 +00005956
cristy3ed852e2009-09-05 21:47:34 +00005957 else /* if (magn_methy == 3 || magn_methy == 5) */
5958 {
5959 /* Replicate nearest */
5960 if (i <= ((m+1) << 1))
5961 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005962
cristy3ed852e2009-09-05 21:47:34 +00005963 else
5964 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00005965
cristy3ed852e2009-09-05 21:47:34 +00005966 if (magn_methy == 5)
5967 {
cristybb503372010-05-27 20:51:26 +00005968 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5969 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005970 +(*pixels).opacity);
5971 }
5972 }
5973 n++;
5974 q++;
5975 pixels++;
5976 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00005977
cristy3ed852e2009-09-05 21:47:34 +00005978 if (SyncAuthenticPixels(large_image,exception) == 0)
5979 break;
glennrp47b9dd52010-11-24 18:12:06 +00005980
cristy3ed852e2009-09-05 21:47:34 +00005981 } /* i */
5982 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00005983
cristy3ed852e2009-09-05 21:47:34 +00005984 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5985 next=(PixelPacket *) RelinquishMagickMemory(next);
5986
5987 length=image->columns;
5988
5989 if (logging != MagickFalse)
5990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5991 " Delete original image");
5992
5993 DeleteImageFromList(&image);
5994
5995 image=large_image;
5996
5997 mng_info->image=image;
5998
5999 /* magnify the columns */
6000 if (logging != MagickFalse)
6001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006002 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006003
cristybb503372010-05-27 20:51:26 +00006004 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006005 {
6006 register PixelPacket
6007 *pixels;
6008
6009 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6010 pixels=q+(image->columns-length);
6011 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00006012
cristybb503372010-05-27 20:51:26 +00006013 for (x=(ssize_t) (image->columns-length);
6014 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006015 {
glennrp7c7b3152011-04-26 04:01:27 +00006016 /* To do: Rewrite using Get/Set***PixelComponent() */
6017
cristybb503372010-05-27 20:51:26 +00006018 if (x == (ssize_t) (image->columns-length))
6019 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristybb503372010-05-27 20:51:26 +00006021 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6022 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006023
cristybb503372010-05-27 20:51:26 +00006024 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6025 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006026
cristybb503372010-05-27 20:51:26 +00006027 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006028 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006029
cristy3ed852e2009-09-05 21:47:34 +00006030 else
cristybb503372010-05-27 20:51:26 +00006031 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006032
cristy3ed852e2009-09-05 21:47:34 +00006033 for (i=0; i < m; i++)
6034 {
6035 if (magn_methx <= 1)
6036 {
6037 /* replicate previous */
6038 *q=(*pixels);
6039 }
glennrp47b9dd52010-11-24 18:12:06 +00006040
cristy3ed852e2009-09-05 21:47:34 +00006041 else if (magn_methx == 2 || magn_methx == 4)
6042 {
6043 if (i == 0)
6044 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006045
cristy3ed852e2009-09-05 21:47:34 +00006046 else
6047 {
6048 /* Interpolate */
6049 (*q).red=(QM) ((2*i*((*n).red
6050 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00006051 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00006052 (*q).green=(QM) ((2*i*((*n).green
6053 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00006054 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00006055 (*q).blue=(QM) ((2*i*((*n).blue
6056 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00006057 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00006058 if (image->matte != MagickFalse)
6059 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006060 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006061 +(*pixels).opacity);
6062 }
glennrp47b9dd52010-11-24 18:12:06 +00006063
cristy3ed852e2009-09-05 21:47:34 +00006064 if (magn_methx == 4)
6065 {
6066 /* Replicate nearest */
6067 if (i <= ((m+1) << 1))
6068 (*q).opacity=(*pixels).opacity+0;
6069 else
6070 (*q).opacity=(*n).opacity+0;
6071 }
6072 }
glennrp47b9dd52010-11-24 18:12:06 +00006073
cristy3ed852e2009-09-05 21:47:34 +00006074 else /* if (magn_methx == 3 || magn_methx == 5) */
6075 {
6076 /* Replicate nearest */
6077 if (i <= ((m+1) << 1))
6078 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006079
cristy3ed852e2009-09-05 21:47:34 +00006080 else
6081 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00006082
cristy3ed852e2009-09-05 21:47:34 +00006083 if (magn_methx == 5)
6084 {
6085 /* Interpolate */
6086 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006087 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006088 +(*pixels).opacity);
6089 }
6090 }
6091 q++;
6092 }
6093 n++;
6094 p++;
6095 }
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6098 break;
6099 }
glennrp3faa9a32011-04-23 14:00:25 +00006100#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006101 if (magn_methx != 1 || magn_methy != 1)
6102 {
6103 /*
6104 Rescale pixels to Quantum
6105 */
cristybb503372010-05-27 20:51:26 +00006106 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006107 {
6108 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006109
cristybb503372010-05-27 20:51:26 +00006110 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006111 {
glennrp7c7b3152011-04-26 04:01:27 +00006112 SetRedPixelComponent(q,ScaleShortToQuantum(
6113 GetRedPixelComponent(q));
6114 SetGreenPixelComponent(q,ScaleShortToQuantum(
6115 GetGreenPixelComponent(q));
6116 SetBluePixelComponent(q,ScaleShortToQuantum(
6117 GetBluePixelComponent(q));
6118 SetOpacityPixelComponent(q,ScaleShortToQuantum(
6119 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00006120 q++;
6121 }
glennrp47b9dd52010-11-24 18:12:06 +00006122
cristy3ed852e2009-09-05 21:47:34 +00006123 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6124 break;
6125 }
6126 }
6127#endif
6128 if (logging != MagickFalse)
6129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6130 " Finished MAGN processing");
6131 }
6132 }
6133
6134 /*
6135 Crop_box is with respect to the upper left corner of the MNG.
6136 */
6137 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6138 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6139 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6140 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6141 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6142 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6143 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6144 if ((crop_box.left != (mng_info->image_box.left
6145 +mng_info->x_off[object_id])) ||
6146 (crop_box.right != (mng_info->image_box.right
6147 +mng_info->x_off[object_id])) ||
6148 (crop_box.top != (mng_info->image_box.top
6149 +mng_info->y_off[object_id])) ||
6150 (crop_box.bottom != (mng_info->image_box.bottom
6151 +mng_info->y_off[object_id])))
6152 {
6153 if (logging != MagickFalse)
6154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6155 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006156
cristy3ed852e2009-09-05 21:47:34 +00006157 if ((crop_box.left < crop_box.right) &&
6158 (crop_box.top < crop_box.bottom))
6159 {
6160 Image
6161 *im;
6162
6163 RectangleInfo
6164 crop_info;
6165
6166 /*
6167 Crop_info is with respect to the upper left corner of
6168 the image.
6169 */
6170 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6171 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006172 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6173 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006174 image->page.width=image->columns;
6175 image->page.height=image->rows;
6176 image->page.x=0;
6177 image->page.y=0;
6178 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006179
cristy3ed852e2009-09-05 21:47:34 +00006180 if (im != (Image *) NULL)
6181 {
6182 image->columns=im->columns;
6183 image->rows=im->rows;
6184 im=DestroyImage(im);
6185 image->page.width=image->columns;
6186 image->page.height=image->rows;
6187 image->page.x=crop_box.left;
6188 image->page.y=crop_box.top;
6189 }
6190 }
glennrp47b9dd52010-11-24 18:12:06 +00006191
cristy3ed852e2009-09-05 21:47:34 +00006192 else
6193 {
6194 /*
6195 No pixels in crop area. The MNG spec still requires
6196 a layer, though, so make a single transparent pixel in
6197 the top left corner.
6198 */
6199 image->columns=1;
6200 image->rows=1;
6201 image->colors=2;
6202 (void) SetImageBackgroundColor(image);
6203 image->page.width=1;
6204 image->page.height=1;
6205 image->page.x=0;
6206 image->page.y=0;
6207 }
6208 }
6209#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6210 image=mng_info->image;
6211#endif
6212 }
6213
glennrp2b013e42010-11-24 16:55:50 +00006214#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6215 /* PNG does not handle depths greater than 16 so reduce it even
6216 * if lossy
6217 */
6218 if (image->depth > 16)
6219 image->depth=16;
6220#endif
6221
glennrp3faa9a32011-04-23 14:00:25 +00006222#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006223 if (LosslessReduceDepthOK(image) != MagickFalse)
6224 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006225#endif
glennrpd6afd542010-11-19 01:53:05 +00006226
cristy3ed852e2009-09-05 21:47:34 +00006227 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006228
cristy3ed852e2009-09-05 21:47:34 +00006229 if (image_info->number_scenes != 0)
6230 {
6231 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006232 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006233 break;
6234 }
glennrpd6afd542010-11-19 01:53:05 +00006235
cristy3ed852e2009-09-05 21:47:34 +00006236 if (logging != MagickFalse)
6237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6238 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006239
cristy3ed852e2009-09-05 21:47:34 +00006240 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006241
cristy3ed852e2009-09-05 21:47:34 +00006242 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006243
cristy3ed852e2009-09-05 21:47:34 +00006244 if (logging != MagickFalse)
6245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6246 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006247
cristy3ed852e2009-09-05 21:47:34 +00006248#if defined(MNG_INSERT_LAYERS)
6249 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6250 (mng_info->mng_height))
6251 {
6252 /*
6253 Insert a background layer if nothing else was found.
6254 */
6255 if (logging != MagickFalse)
6256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6257 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006258
cristy3ed852e2009-09-05 21:47:34 +00006259 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6260 {
6261 /*
6262 Allocate next image structure.
6263 */
6264 AcquireNextImage(image_info,image);
6265 if (GetNextImageInList(image) == (Image *) NULL)
6266 {
6267 image=DestroyImageList(image);
6268 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006269
cristy3ed852e2009-09-05 21:47:34 +00006270 if (logging != MagickFalse)
6271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6272 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006273
cristy3ed852e2009-09-05 21:47:34 +00006274 return((Image *) NULL);
6275 }
6276 image=SyncNextImageInList(image);
6277 }
6278 image->columns=mng_info->mng_width;
6279 image->rows=mng_info->mng_height;
6280 image->page.width=mng_info->mng_width;
6281 image->page.height=mng_info->mng_height;
6282 image->page.x=0;
6283 image->page.y=0;
6284 image->background_color=mng_background_color;
6285 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006286
cristy3ed852e2009-09-05 21:47:34 +00006287 if (image_info->ping == MagickFalse)
6288 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006289
cristy3ed852e2009-09-05 21:47:34 +00006290 mng_info->image_found++;
6291 }
6292#endif
6293 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006294
cristy3ed852e2009-09-05 21:47:34 +00006295 if (mng_iterations == 1)
6296 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006297
cristy3ed852e2009-09-05 21:47:34 +00006298 while (GetPreviousImageInList(image) != (Image *) NULL)
6299 {
6300 image_count++;
6301 if (image_count > 10*mng_info->image_found)
6302 {
6303 if (logging != MagickFalse)
6304 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006305
cristy3ed852e2009-09-05 21:47:34 +00006306 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6307 CoderError,"Linked list is corrupted, beginning of list not found",
6308 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006309
cristy3ed852e2009-09-05 21:47:34 +00006310 return((Image *) NULL);
6311 }
glennrp0fe50b42010-11-16 03:52:51 +00006312
cristy3ed852e2009-09-05 21:47:34 +00006313 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006314
cristy3ed852e2009-09-05 21:47:34 +00006315 if (GetNextImageInList(image) == (Image *) NULL)
6316 {
6317 if (logging != MagickFalse)
6318 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006319
cristy3ed852e2009-09-05 21:47:34 +00006320 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6321 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6322 image_info->filename);
6323 }
6324 }
glennrp47b9dd52010-11-24 18:12:06 +00006325
cristy3ed852e2009-09-05 21:47:34 +00006326 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6327 GetNextImageInList(image) ==
6328 (Image *) NULL)
6329 {
6330 if (logging != MagickFalse)
6331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6332 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006333
cristy3ed852e2009-09-05 21:47:34 +00006334 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6335 CoderError,"image->next for first image is NULL but shouldn't be.",
6336 "`%s'",image_info->filename);
6337 }
glennrp47b9dd52010-11-24 18:12:06 +00006338
cristy3ed852e2009-09-05 21:47:34 +00006339 if (mng_info->image_found == 0)
6340 {
6341 if (logging != MagickFalse)
6342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6343 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006344
cristy3ed852e2009-09-05 21:47:34 +00006345 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6346 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006347
cristy3ed852e2009-09-05 21:47:34 +00006348 if (image != (Image *) NULL)
6349 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006350
cristy3ed852e2009-09-05 21:47:34 +00006351 MngInfoFreeStruct(mng_info,&have_mng_structure);
6352 return((Image *) NULL);
6353 }
6354
6355 if (mng_info->ticks_per_second)
6356 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6357 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006358
cristy3ed852e2009-09-05 21:47:34 +00006359 else
6360 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006361
cristy3ed852e2009-09-05 21:47:34 +00006362 /* Find final nonzero image delay */
6363 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006364
cristy3ed852e2009-09-05 21:47:34 +00006365 while (GetNextImageInList(image) != (Image *) NULL)
6366 {
6367 if (image->delay)
6368 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006369
cristy3ed852e2009-09-05 21:47:34 +00006370 image=GetNextImageInList(image);
6371 }
glennrp0fe50b42010-11-16 03:52:51 +00006372
cristy3ed852e2009-09-05 21:47:34 +00006373 if (final_delay < final_image_delay)
6374 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006375
cristy3ed852e2009-09-05 21:47:34 +00006376 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006377
cristy3ed852e2009-09-05 21:47:34 +00006378 if (logging != MagickFalse)
6379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006380 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6381 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006382
cristy3ed852e2009-09-05 21:47:34 +00006383 if (logging != MagickFalse)
6384 {
6385 int
6386 scene;
6387
6388 scene=0;
6389 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006390
cristy3ed852e2009-09-05 21:47:34 +00006391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6392 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006393
cristy3ed852e2009-09-05 21:47:34 +00006394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006395 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006396
cristy3ed852e2009-09-05 21:47:34 +00006397 while (GetNextImageInList(image) != (Image *) NULL)
6398 {
6399 image=GetNextImageInList(image);
6400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006401 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006402 }
6403 }
6404
6405 image=GetFirstImageInList(image);
6406#ifdef MNG_COALESCE_LAYERS
6407 if (insert_layers)
6408 {
6409 Image
6410 *next_image,
6411 *next;
6412
cristybb503372010-05-27 20:51:26 +00006413 size_t
cristy3ed852e2009-09-05 21:47:34 +00006414 scene;
6415
6416 if (logging != MagickFalse)
6417 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006418
cristy3ed852e2009-09-05 21:47:34 +00006419 scene=image->scene;
6420 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006421
cristy3ed852e2009-09-05 21:47:34 +00006422 if (next_image == (Image *) NULL)
6423 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006424
cristy3ed852e2009-09-05 21:47:34 +00006425 image=DestroyImageList(image);
6426 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006427
cristy3ed852e2009-09-05 21:47:34 +00006428 for (next=image; next != (Image *) NULL; next=next_image)
6429 {
6430 next->page.width=mng_info->mng_width;
6431 next->page.height=mng_info->mng_height;
6432 next->page.x=0;
6433 next->page.y=0;
6434 next->scene=scene++;
6435 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006436
cristy3ed852e2009-09-05 21:47:34 +00006437 if (next_image == (Image *) NULL)
6438 break;
glennrp47b9dd52010-11-24 18:12:06 +00006439
cristy3ed852e2009-09-05 21:47:34 +00006440 if (next->delay == 0)
6441 {
6442 scene--;
6443 next_image->previous=GetPreviousImageInList(next);
6444 if (GetPreviousImageInList(next) == (Image *) NULL)
6445 image=next_image;
6446 else
6447 next->previous->next=next_image;
6448 next=DestroyImage(next);
6449 }
6450 }
6451 }
6452#endif
6453
6454 while (GetNextImageInList(image) != (Image *) NULL)
6455 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006456
cristy3ed852e2009-09-05 21:47:34 +00006457 image->dispose=BackgroundDispose;
6458
6459 if (logging != MagickFalse)
6460 {
6461 int
6462 scene;
6463
6464 scene=0;
6465 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006466
cristy3ed852e2009-09-05 21:47:34 +00006467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6468 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006469
cristy3ed852e2009-09-05 21:47:34 +00006470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006471 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6472 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006473
cristy3ed852e2009-09-05 21:47:34 +00006474 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006475 {
6476 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006477
cristyf2faecf2010-05-28 19:19:36 +00006478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006479 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6480 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006481 }
6482 }
glennrp47b9dd52010-11-24 18:12:06 +00006483
cristy3ed852e2009-09-05 21:47:34 +00006484 image=GetFirstImageInList(image);
6485 MngInfoFreeStruct(mng_info,&have_mng_structure);
6486 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006487
cristy3ed852e2009-09-05 21:47:34 +00006488 if (logging != MagickFalse)
6489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006490
cristy3ed852e2009-09-05 21:47:34 +00006491 return(GetFirstImageInList(image));
6492}
glennrp25c1e2b2010-03-25 01:39:56 +00006493#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006494static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6495{
6496 printf("Your PNG library is too old: You have libpng-%s\n",
6497 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006498
cristy3ed852e2009-09-05 21:47:34 +00006499 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6500 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006501
cristy3ed852e2009-09-05 21:47:34 +00006502 return(Image *) NULL;
6503}
glennrp47b9dd52010-11-24 18:12:06 +00006504
cristy3ed852e2009-09-05 21:47:34 +00006505static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6506{
6507 return(ReadPNGImage(image_info,exception));
6508}
glennrp25c1e2b2010-03-25 01:39:56 +00006509#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006510#endif
6511
6512/*
6513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6514% %
6515% %
6516% %
6517% R e g i s t e r P N G I m a g e %
6518% %
6519% %
6520% %
6521%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6522%
6523% RegisterPNGImage() adds properties for the PNG image format to
6524% the list of supported formats. The properties include the image format
6525% tag, a method to read and/or write the format, whether the format
6526% supports the saving of more than one frame to the same file or blob,
6527% whether the format supports native in-memory I/O, and a brief
6528% description of the format.
6529%
6530% The format of the RegisterPNGImage method is:
6531%
cristybb503372010-05-27 20:51:26 +00006532% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006533%
6534*/
cristybb503372010-05-27 20:51:26 +00006535ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006536{
6537 char
6538 version[MaxTextExtent];
6539
6540 MagickInfo
6541 *entry;
6542
6543 static const char
6544 *PNGNote=
6545 {
6546 "See http://www.libpng.org/ for details about the PNG format."
6547 },
glennrp47b9dd52010-11-24 18:12:06 +00006548
cristy3ed852e2009-09-05 21:47:34 +00006549 *JNGNote=
6550 {
6551 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6552 "format."
6553 },
glennrp47b9dd52010-11-24 18:12:06 +00006554
cristy3ed852e2009-09-05 21:47:34 +00006555 *MNGNote=
6556 {
6557 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6558 "format."
6559 };
6560
6561 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563#if defined(PNG_LIBPNG_VER_STRING)
6564 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6565 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006566
cristy3ed852e2009-09-05 21:47:34 +00006567 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6568 {
6569 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6570 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6571 MaxTextExtent);
6572 }
6573#endif
glennrp47b9dd52010-11-24 18:12:06 +00006574
cristy3ed852e2009-09-05 21:47:34 +00006575 entry=SetMagickInfo("MNG");
6576 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00006577
cristy3ed852e2009-09-05 21:47:34 +00006578#if defined(MAGICKCORE_PNG_DELEGATE)
6579 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6580 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6581#endif
glennrp47b9dd52010-11-24 18:12:06 +00006582
cristy3ed852e2009-09-05 21:47:34 +00006583 entry->magick=(IsImageFormatHandler *) IsMNG;
6584 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00006585
cristy3ed852e2009-09-05 21:47:34 +00006586 if (*version != '\0')
6587 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006588
cristy3ed852e2009-09-05 21:47:34 +00006589 entry->module=ConstantString("PNG");
6590 entry->note=ConstantString(MNGNote);
6591 (void) RegisterMagickInfo(entry);
6592
6593 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006594
cristy3ed852e2009-09-05 21:47:34 +00006595#if defined(MAGICKCORE_PNG_DELEGATE)
6596 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6597 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6598#endif
glennrp47b9dd52010-11-24 18:12:06 +00006599
cristy3ed852e2009-09-05 21:47:34 +00006600 entry->magick=(IsImageFormatHandler *) IsPNG;
6601 entry->adjoin=MagickFalse;
6602 entry->description=ConstantString("Portable Network Graphics");
6603 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006604
cristy3ed852e2009-09-05 21:47:34 +00006605 if (*version != '\0')
6606 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006607
cristy3ed852e2009-09-05 21:47:34 +00006608 entry->note=ConstantString(PNGNote);
6609 (void) RegisterMagickInfo(entry);
6610
6611 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00006612
cristy3ed852e2009-09-05 21:47:34 +00006613#if defined(MAGICKCORE_PNG_DELEGATE)
6614 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6615 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6616#endif
glennrp47b9dd52010-11-24 18:12:06 +00006617
cristy3ed852e2009-09-05 21:47:34 +00006618 entry->magick=(IsImageFormatHandler *) IsPNG;
6619 entry->adjoin=MagickFalse;
6620 entry->description=ConstantString(
6621 "8-bit indexed with optional binary transparency");
6622 entry->module=ConstantString("PNG");
6623 (void) RegisterMagickInfo(entry);
6624
6625 entry=SetMagickInfo("PNG24");
6626 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006627
cristy3ed852e2009-09-05 21:47:34 +00006628#if defined(ZLIB_VERSION)
6629 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6630 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006631
cristy3ed852e2009-09-05 21:47:34 +00006632 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6633 {
6634 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6635 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6636 }
6637#endif
glennrp47b9dd52010-11-24 18:12:06 +00006638
cristy3ed852e2009-09-05 21:47:34 +00006639 if (*version != '\0')
6640 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006641
cristy3ed852e2009-09-05 21:47:34 +00006642#if defined(MAGICKCORE_PNG_DELEGATE)
6643 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6644 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6645#endif
glennrp47b9dd52010-11-24 18:12:06 +00006646
cristy3ed852e2009-09-05 21:47:34 +00006647 entry->magick=(IsImageFormatHandler *) IsPNG;
6648 entry->adjoin=MagickFalse;
6649 entry->description=ConstantString("opaque 24-bit RGB");
6650 entry->module=ConstantString("PNG");
6651 (void) RegisterMagickInfo(entry);
6652
6653 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00006654
cristy3ed852e2009-09-05 21:47:34 +00006655#if defined(MAGICKCORE_PNG_DELEGATE)
6656 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6657 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6658#endif
glennrp47b9dd52010-11-24 18:12:06 +00006659
cristy3ed852e2009-09-05 21:47:34 +00006660 entry->magick=(IsImageFormatHandler *) IsPNG;
6661 entry->adjoin=MagickFalse;
6662 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6663 entry->module=ConstantString("PNG");
6664 (void) RegisterMagickInfo(entry);
6665
6666 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006667
cristy3ed852e2009-09-05 21:47:34 +00006668#if defined(JNG_SUPPORTED)
6669#if defined(MAGICKCORE_PNG_DELEGATE)
6670 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6671 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6672#endif
6673#endif
glennrp47b9dd52010-11-24 18:12:06 +00006674
cristy3ed852e2009-09-05 21:47:34 +00006675 entry->magick=(IsImageFormatHandler *) IsJNG;
6676 entry->adjoin=MagickFalse;
6677 entry->description=ConstantString("JPEG Network Graphics");
6678 entry->module=ConstantString("PNG");
6679 entry->note=ConstantString(JNGNote);
6680 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00006681
cristy18b17442009-10-25 18:36:48 +00006682#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006683 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00006684#endif
glennrp47b9dd52010-11-24 18:12:06 +00006685
cristy3ed852e2009-09-05 21:47:34 +00006686 return(MagickImageCoderSignature);
6687}
6688
6689/*
6690%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6691% %
6692% %
6693% %
6694% U n r e g i s t e r P N G I m a g e %
6695% %
6696% %
6697% %
6698%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6699%
6700% UnregisterPNGImage() removes format registrations made by the
6701% PNG module from the list of supported formats.
6702%
6703% The format of the UnregisterPNGImage method is:
6704%
6705% UnregisterPNGImage(void)
6706%
6707*/
6708ModuleExport void UnregisterPNGImage(void)
6709{
6710 (void) UnregisterMagickInfo("MNG");
6711 (void) UnregisterMagickInfo("PNG");
6712 (void) UnregisterMagickInfo("PNG8");
6713 (void) UnregisterMagickInfo("PNG24");
6714 (void) UnregisterMagickInfo("PNG32");
6715 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006716
cristy3ed852e2009-09-05 21:47:34 +00006717#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006718 if (ping_semaphore != (SemaphoreInfo *) NULL)
6719 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006720#endif
6721}
6722
6723#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006724#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006725/*
6726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6727% %
6728% %
6729% %
6730% W r i t e M N G I m a g e %
6731% %
6732% %
6733% %
6734%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6735%
6736% WriteMNGImage() writes an image in the Portable Network Graphics
6737% Group's "Multiple-image Network Graphics" encoded image format.
6738%
6739% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6740%
6741% The format of the WriteMNGImage method is:
6742%
6743% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6744%
6745% A description of each parameter follows.
6746%
6747% o image_info: the image info.
6748%
6749% o image: The image.
6750%
6751%
6752% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6753% "To do" under ReadPNGImage):
6754%
cristy3ed852e2009-09-05 21:47:34 +00006755% Preserve all unknown and not-yet-handled known chunks found in input
6756% PNG file and copy them into output PNG files according to the PNG
6757% copying rules.
6758%
6759% Write the iCCP chunk at MNG level when (icc profile length > 0)
6760%
6761% Improve selection of color type (use indexed-colour or indexed-colour
6762% with tRNS when 256 or fewer unique RGBA values are present).
6763%
6764% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6765% This will be complicated if we limit ourselves to generating MNG-LC
6766% files. For now we ignore disposal method 3 and simply overlay the next
6767% image on it.
6768%
6769% Check for identical PLTE's or PLTE/tRNS combinations and use a
6770% global MNG PLTE or PLTE/tRNS combination when appropriate.
6771% [mostly done 15 June 1999 but still need to take care of tRNS]
6772%
6773% Check for identical sRGB and replace with a global sRGB (and remove
6774% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6775% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6776% local gAMA/cHRM with local sRGB if appropriate).
6777%
6778% Check for identical sBIT chunks and write global ones.
6779%
6780% Provide option to skip writing the signature tEXt chunks.
6781%
6782% Use signatures to detect identical objects and reuse the first
6783% instance of such objects instead of writing duplicate objects.
6784%
6785% Use a smaller-than-32k value of compression window size when
6786% appropriate.
6787%
6788% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6789% ancillary text chunks and save profiles.
6790%
6791% Provide an option to force LC files (to ensure exact framing rate)
6792% instead of VLC.
6793%
6794% Provide an option to force VLC files instead of LC, even when offsets
6795% are present. This will involve expanding the embedded images with a
6796% transparent region at the top and/or left.
6797*/
6798
cristy3ed852e2009-09-05 21:47:34 +00006799static void
glennrpcf002022011-01-30 02:38:15 +00006800Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00006801 png_info *ping_info, unsigned char *profile_type, unsigned char
6802 *profile_description, unsigned char *profile_data, png_uint_32 length)
6803{
cristy3ed852e2009-09-05 21:47:34 +00006804 png_textp
6805 text;
6806
cristybb503372010-05-27 20:51:26 +00006807 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006808 i;
6809
6810 unsigned char
6811 *sp;
6812
6813 png_charp
6814 dp;
6815
6816 png_uint_32
6817 allocated_length,
6818 description_length;
6819
6820 unsigned char
6821 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006822
cristy3ed852e2009-09-05 21:47:34 +00006823 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6824 return;
6825
6826 if (image_info->verbose)
6827 {
glennrp0fe50b42010-11-16 03:52:51 +00006828 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6829 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006830 }
glennrp0fe50b42010-11-16 03:52:51 +00006831
cristy3ed852e2009-09-05 21:47:34 +00006832 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6833 description_length=(png_uint_32) strlen((const char *) profile_description);
6834 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6835 + description_length);
6836 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6837 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6838 text[0].key[0]='\0';
6839 (void) ConcatenateMagickString(text[0].key,
6840 "Raw profile type ",MaxTextExtent);
6841 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6842 sp=profile_data;
6843 dp=text[0].text;
6844 *dp++='\n';
6845 (void) CopyMagickString(dp,(const char *) profile_description,
6846 allocated_length);
6847 dp+=description_length;
6848 *dp++='\n';
6849 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006850 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006851 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00006852
cristybb503372010-05-27 20:51:26 +00006853 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006854 {
6855 if (i%36 == 0)
6856 *dp++='\n';
6857 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6858 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6859 }
glennrp47b9dd52010-11-24 18:12:06 +00006860
cristy3ed852e2009-09-05 21:47:34 +00006861 *dp++='\n';
6862 *dp='\0';
6863 text[0].text_length=(png_size_t) (dp-text[0].text);
6864 text[0].compression=image_info->compression == NoCompression ||
6865 (image_info->compression == UndefinedCompression &&
6866 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00006867
cristy3ed852e2009-09-05 21:47:34 +00006868 if (text[0].text_length <= allocated_length)
6869 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00006870
cristy3ed852e2009-09-05 21:47:34 +00006871 png_free(ping,text[0].text);
6872 png_free(ping,text[0].key);
6873 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006874}
6875
glennrpcf002022011-01-30 02:38:15 +00006876static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00006877 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00006878{
6879 char
6880 *name;
6881
6882 const StringInfo
6883 *profile;
6884
6885 unsigned char
6886 *data;
6887
6888 png_uint_32 length;
6889
6890 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00006891
6892 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6893 {
cristy3ed852e2009-09-05 21:47:34 +00006894 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00006895
cristy3ed852e2009-09-05 21:47:34 +00006896 if (profile != (const StringInfo *) NULL)
6897 {
6898 StringInfo
glennrpcf002022011-01-30 02:38:15 +00006899 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00006900
glennrp47b9dd52010-11-24 18:12:06 +00006901 if (LocaleNCompare(name,string,11) == 0)
6902 {
6903 if (logging != MagickFalse)
6904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6905 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00006906
glennrpcf002022011-01-30 02:38:15 +00006907 ping_profile=CloneStringInfo(profile);
6908 data=GetStringInfoDatum(ping_profile),
6909 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006910 data[4]=data[3];
6911 data[3]=data[2];
6912 data[2]=data[1];
6913 data[1]=data[0];
6914 (void) WriteBlobMSBULong(image,length-5); /* data length */
6915 (void) WriteBlob(image,length-1,data+1);
6916 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00006917 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006918 }
cristy3ed852e2009-09-05 21:47:34 +00006919 }
glennrp47b9dd52010-11-24 18:12:06 +00006920
cristy3ed852e2009-09-05 21:47:34 +00006921 name=GetNextImageProfile(image);
6922 }
glennrp47b9dd52010-11-24 18:12:06 +00006923
cristy3ed852e2009-09-05 21:47:34 +00006924 return(MagickTrue);
6925}
6926
glennrpb9cfe272010-12-21 15:08:06 +00006927
cristy3ed852e2009-09-05 21:47:34 +00006928/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00006929static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6930 const ImageInfo *IMimage_info,Image *IMimage)
6931{
6932 Image
6933 *image;
6934
6935 ImageInfo
6936 *image_info;
6937
cristy3ed852e2009-09-05 21:47:34 +00006938 char
6939 s[2];
6940
6941 const char
6942 *name,
6943 *property,
6944 *value;
6945
6946 const StringInfo
6947 *profile;
6948
cristy3ed852e2009-09-05 21:47:34 +00006949 int
cristy3ed852e2009-09-05 21:47:34 +00006950 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006951 pass;
6952
glennrpe9c26dc2010-05-30 01:56:35 +00006953 png_byte
6954 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006955
glennrp39992b42010-11-14 00:03:43 +00006956 png_color
6957 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00006958
glennrp5af765f2010-03-30 11:12:18 +00006959 png_color_16
6960 ping_background,
6961 ping_trans_color;
6962
cristy3ed852e2009-09-05 21:47:34 +00006963 png_info
6964 *ping_info;
6965
6966 png_struct
6967 *ping;
6968
glennrp5af765f2010-03-30 11:12:18 +00006969 png_uint_32
6970 ping_height,
6971 ping_width;
6972
cristybb503372010-05-27 20:51:26 +00006973 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006974 y;
6975
6976 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00006977 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00006978 logging,
glennrp58e01762011-01-07 15:28:54 +00006979 matte,
6980
glennrpda8f3a72011-02-27 23:54:12 +00006981 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00006982 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00006983 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00006984 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00006985 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00006986 ping_have_bKGD,
6987 ping_have_pHYs,
6988 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00006989
6990 ping_exclude_bKGD,
6991 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00006992 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00006993 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00006994 ping_exclude_gAMA,
6995 ping_exclude_iCCP,
6996 /* ping_exclude_iTXt, */
6997 ping_exclude_oFFs,
6998 ping_exclude_pHYs,
6999 ping_exclude_sRGB,
7000 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007001 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007002 ping_exclude_vpAg,
7003 ping_exclude_zCCP, /* hex-encoded iCCP */
7004 ping_exclude_zTXt,
7005
glennrp8d3d6e52011-04-19 04:39:51 +00007006 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007007 ping_need_colortype_warning,
7008
glennrp82b3c532011-03-22 19:20:54 +00007009 status,
glennrpd3371642011-03-22 19:42:23 +00007010 tried_333,
7011 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007012
7013 QuantumInfo
7014 *quantum_info;
7015
cristybb503372010-05-27 20:51:26 +00007016 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007017 i,
7018 x;
7019
7020 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007021 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007022
glennrp5af765f2010-03-30 11:12:18 +00007023 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007024 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007025 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007026 ping_color_type,
7027 ping_interlace_method,
7028 ping_compression_method,
7029 ping_filter_method,
7030 ping_num_trans;
7031
cristybb503372010-05-27 20:51:26 +00007032 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007033 image_depth,
7034 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007035
cristybb503372010-05-27 20:51:26 +00007036 size_t
cristy3ed852e2009-09-05 21:47:34 +00007037 quality,
7038 rowbytes,
7039 save_image_depth;
7040
glennrpdfd70802010-11-14 01:23:35 +00007041 int
glennrpfd05d622011-02-25 04:10:33 +00007042 j,
glennrpf09bded2011-01-08 01:15:59 +00007043 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007044 number_opaque,
7045 number_semitransparent,
7046 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007047 ping_pHYs_unit_type;
7048
7049 png_uint_32
7050 ping_pHYs_x_resolution,
7051 ping_pHYs_y_resolution;
7052
cristy3ed852e2009-09-05 21:47:34 +00007053 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007054 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007055
glennrpb9cfe272010-12-21 15:08:06 +00007056 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7057 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007058 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007059 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007060
cristy3ed852e2009-09-05 21:47:34 +00007061#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007062 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007063#endif
7064
glennrp5af765f2010-03-30 11:12:18 +00007065 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007066 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007067 ping_color_type=0,
7068 ping_interlace_method=0,
7069 ping_compression_method=0,
7070 ping_filter_method=0,
7071 ping_num_trans = 0;
7072
7073 ping_background.red = 0;
7074 ping_background.green = 0;
7075 ping_background.blue = 0;
7076 ping_background.gray = 0;
7077 ping_background.index = 0;
7078
7079 ping_trans_color.red=0;
7080 ping_trans_color.green=0;
7081 ping_trans_color.blue=0;
7082 ping_trans_color.gray=0;
7083
glennrpdfd70802010-11-14 01:23:35 +00007084 ping_pHYs_unit_type = 0;
7085 ping_pHYs_x_resolution = 0;
7086 ping_pHYs_y_resolution = 0;
7087
glennrpda8f3a72011-02-27 23:54:12 +00007088 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007089 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007090 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007091 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007092 ping_have_bKGD=MagickFalse;
7093 ping_have_pHYs=MagickFalse;
7094 ping_have_tRNS=MagickFalse;
7095
glennrp0e8ea192010-12-24 18:00:33 +00007096 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7097 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007098 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007099 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007100 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007101 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7102 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7103 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7104 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7105 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7106 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007107 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007108 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7109 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7110 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7111
glennrp8d3d6e52011-04-19 04:39:51 +00007112 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007113 ping_need_colortype_warning = MagickFalse;
7114
glennrp8bb3a022010-12-13 20:40:04 +00007115 number_opaque = 0;
7116 number_semitransparent = 0;
7117 number_transparent = 0;
7118
glennrpfd05d622011-02-25 04:10:33 +00007119 if (logging != MagickFalse)
7120 {
7121 if (image->storage_class == UndefinedClass)
7122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7123 " storage_class=UndefinedClass");
7124 if (image->storage_class == DirectClass)
7125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7126 " storage_class=DirectClass");
7127 if (image->storage_class == PseudoClass)
7128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7129 " storage_class=PseudoClass");
7130 }
glennrp28af3712011-04-06 18:07:30 +00007131
glennrpc6c391a2011-04-27 02:23:56 +00007132 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007133 {
glennrpc6c391a2011-04-27 02:23:56 +00007134 if (image->storage_class != PseudoClass && image->colormap != NULL)
7135 {
7136 /* Free the bogus colormap; it can cause trouble later */
7137 if (logging != MagickFalse)
7138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7139 " Freeing bogus colormap");
7140 (void *) RelinquishMagickMemory(image->colormap);
7141 image->colormap=NULL;
7142 }
glennrp28af3712011-04-06 18:07:30 +00007143 }
glennrpfd05d622011-02-25 04:10:33 +00007144
cristy3ed852e2009-09-05 21:47:34 +00007145 if (image->colorspace != RGBColorspace)
7146 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007147
glennrp3241bd02010-12-12 04:36:28 +00007148 /*
7149 Sometimes we get PseudoClass images whose RGB values don't match
7150 the colors in the colormap. This code syncs the RGB values.
7151 */
7152 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7153 (void) SyncImage(image);
7154
glennrpa6a06632011-01-19 15:15:34 +00007155#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7156 if (image->depth > 8)
7157 {
7158 if (logging != MagickFalse)
7159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7160 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7161
7162 image->depth=8;
7163 }
7164#endif
7165
glennrp67b9c1a2011-04-22 18:47:36 +00007166#if 0 /* To do: Option to use the original colormap */
7167 if (ping_preserve_colormap != MagickFalse)
7168 {
7169 }
7170#endif
7171
glennrp3faa9a32011-04-23 14:00:25 +00007172#if 0 /* To do: respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007173 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7174 {
glennrp9d0ea4d2011-04-22 18:35:57 +00007175 }
glennrp67b9c1a2011-04-22 18:47:36 +00007176#endif
glennrp9d0ea4d2011-04-22 18:35:57 +00007177
glennrp67b9c1a2011-04-22 18:47:36 +00007178 /* To do: set to next higher multiple of 8 */
7179 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007180 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007181
glennrp2b013e42010-11-24 16:55:50 +00007182#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7183 /* PNG does not handle depths greater than 16 so reduce it even
7184 * if lossy
7185 */
7186 if (image->depth > 16)
7187 image->depth=16;
7188#endif
7189
glennrp3faa9a32011-04-23 14:00:25 +00007190#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007191 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007192 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007193 image->depth = 8;
7194#endif
7195
glennrpc8c2f062011-02-25 19:00:33 +00007196 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007197 * we reduce the transparency to binary and run again, then if there
7198 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7199 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7200 * palette. The final reduction can only fail if there are still 256
7201 * colors present and one of them has both transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007202 */
glennrp82b3c532011-03-22 19:20:54 +00007203
7204 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007205 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007206
glennrpd3371642011-03-22 19:42:23 +00007207 for (j=0; j<5; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007208 {
7209 /* BUILD_PALETTE
7210 *
7211 * Sometimes we get DirectClass images that have 256 colors or fewer.
7212 * This code will build a colormap.
7213 *
7214 * Also, sometimes we get PseudoClass images with an out-of-date
7215 * colormap. This code will replace the colormap with a new one.
7216 * Sometimes we get PseudoClass images that have more than 256 colors.
7217 * This code will delete the colormap and change the image to
7218 * DirectClass.
7219 *
7220 * If image->matte is MagickFalse, we ignore the opacity channel
7221 * even though it sometimes contains left-over non-opaque values.
7222 *
7223 * Also we gather some information (number of opaque, transparent,
7224 * and semitransparent pixels, and whether the image has any non-gray
7225 * pixels or only black-and-white pixels) that we might need later.
7226 *
7227 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7228 * we need to check for bogus non-opaque values, at least.
7229 */
glennrp3c218112010-11-27 15:31:26 +00007230
glennrpd71e86a2011-02-24 01:28:37 +00007231 ExceptionInfo
7232 *exception;
glennrp3c218112010-11-27 15:31:26 +00007233
glennrpd71e86a2011-02-24 01:28:37 +00007234 int
7235 n;
glennrp3c218112010-11-27 15:31:26 +00007236
glennrpd71e86a2011-02-24 01:28:37 +00007237 PixelPacket
7238 opaque[260],
7239 semitransparent[260],
7240 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007241
glennrpd71e86a2011-02-24 01:28:37 +00007242 register IndexPacket
7243 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007244
glennrpd71e86a2011-02-24 01:28:37 +00007245 register const PixelPacket
7246 *s,
7247 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007248
glennrpfd05d622011-02-25 04:10:33 +00007249 register PixelPacket
7250 *r;
7251
glennrpd71e86a2011-02-24 01:28:37 +00007252 if (logging != MagickFalse)
7253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7254 " Enter BUILD_PALETTE:");
7255
7256 if (logging != MagickFalse)
7257 {
glennrp03812ae2010-12-24 01:31:34 +00007258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007259 " image->columns=%.20g",(double) image->columns);
7260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7261 " image->rows=%.20g",(double) image->rows);
7262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7263 " image->matte=%.20g",(double) image->matte);
7264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7265 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007266
glennrpfd05d622011-02-25 04:10:33 +00007267 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007268 {
7269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007270 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007272 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007273
glennrpd71e86a2011-02-24 01:28:37 +00007274 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007275 {
glennrpd71e86a2011-02-24 01:28:37 +00007276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7277 " %d (%d,%d,%d,%d)",
7278 (int) i,
7279 (int) image->colormap[i].red,
7280 (int) image->colormap[i].green,
7281 (int) image->colormap[i].blue,
7282 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007283 }
glennrp2cc891a2010-12-24 13:44:32 +00007284
glennrpd71e86a2011-02-24 01:28:37 +00007285 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7286 {
7287 if (i > 255)
7288 {
7289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7290 " %d (%d,%d,%d,%d)",
7291 (int) i,
7292 (int) image->colormap[i].red,
7293 (int) image->colormap[i].green,
7294 (int) image->colormap[i].blue,
7295 (int) image->colormap[i].opacity);
7296 }
7297 }
glennrp03812ae2010-12-24 01:31:34 +00007298 }
glennrp7ddcc222010-12-11 05:01:05 +00007299
glennrpd71e86a2011-02-24 01:28:37 +00007300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7301 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007302
glennrpd71e86a2011-02-24 01:28:37 +00007303 if (image->colors == 0)
7304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7305 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007306
glennrp8d3d6e52011-04-19 04:39:51 +00007307 if (ping_preserve_colormap == MagickFalse)
7308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7309 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007310 }
7311
7312 exception=(&image->exception);
7313
7314 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007315 number_opaque = 0;
7316 number_semitransparent = 0;
7317 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007318
7319 for (y=0; y < (ssize_t) image->rows; y++)
7320 {
7321 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7322
7323 if (q == (PixelPacket *) NULL)
7324 break;
7325
7326 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007327 {
glennrpd71e86a2011-02-24 01:28:37 +00007328 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7329 {
7330 if (number_opaque < 259)
7331 {
7332 if (number_opaque == 0)
7333 {
glennrp8e045c82011-04-27 16:40:27 +00007334 GetRGBPixelComponents(q, opaque[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007335 opaque[0].opacity=OpaqueOpacity;
7336 number_opaque=1;
7337 }
glennrp2cc891a2010-12-24 13:44:32 +00007338
glennrpd71e86a2011-02-24 01:28:37 +00007339 for (i=0; i< (ssize_t) number_opaque; i++)
7340 {
glennrp0e68fac2011-04-26 04:51:47 +00007341 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007342 break;
7343 }
glennrp7ddcc222010-12-11 05:01:05 +00007344
glennrpd71e86a2011-02-24 01:28:37 +00007345 if (i == (ssize_t) number_opaque &&
7346 number_opaque < 259)
7347 {
7348 number_opaque++;
glennrp8e045c82011-04-27 16:40:27 +00007349 GetRGBPixelComponents(q, opaque[i]);
glennrpca7ad3a2011-04-26 04:44:54 +00007350 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00007351 }
7352 }
7353 }
7354 else if (q->opacity == TransparentOpacity)
7355 {
7356 if (number_transparent < 259)
7357 {
7358 if (number_transparent == 0)
7359 {
glennrp8e045c82011-04-27 16:40:27 +00007360 GetRGBOPixelComponents(q, transparent[0]);
glennrpa18d5bc2011-04-23 14:51:34 +00007361 ping_trans_color.red=
7362 (unsigned short) GetRedPixelComponent(q);
7363 ping_trans_color.green=
7364 (unsigned short) GetGreenPixelComponent(q);
7365 ping_trans_color.blue=
7366 (unsigned short) GetBluePixelComponent(q);
7367 ping_trans_color.gray=
7368 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007369 number_transparent = 1;
7370 }
7371
7372 for (i=0; i< (ssize_t) number_transparent; i++)
7373 {
glennrp0e68fac2011-04-26 04:51:47 +00007374 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00007375 break;
7376 }
7377
7378 if (i == (ssize_t) number_transparent &&
7379 number_transparent < 259)
7380 {
7381 number_transparent++;
glennrp8e045c82011-04-27 16:40:27 +00007382 GetRGBOPixelComponents(q, transparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00007383 }
7384 }
7385 }
7386 else
7387 {
7388 if (number_semitransparent < 259)
7389 {
7390 if (number_semitransparent == 0)
7391 {
glennrp8e045c82011-04-27 16:40:27 +00007392 GetRGBOPixelComponents(q, semitransparent[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007393 number_semitransparent = 1;
7394 }
7395
7396 for (i=0; i< (ssize_t) number_semitransparent; i++)
7397 {
glennrp0e68fac2011-04-26 04:51:47 +00007398 if (IsColorEqual(q, semitransparent+i)
glennrpca7ad3a2011-04-26 04:44:54 +00007399 && GetOpacityPixelComponent(q) ==
7400 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00007401 break;
7402 }
7403
7404 if (i == (ssize_t) number_semitransparent &&
7405 number_semitransparent < 259)
7406 {
7407 number_semitransparent++;
glennrp8e045c82011-04-27 16:40:27 +00007408 GetRGBOPixelComponents(q, semitransparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00007409 }
7410 }
7411 }
7412 q++;
7413 }
7414 }
7415
7416 if (ping_exclude_bKGD == MagickFalse)
7417 {
7418 /* Add the background color to the palette, if it
7419 * isn't already there.
7420 */
glennrpc6c391a2011-04-27 02:23:56 +00007421 if (logging != MagickFalse)
7422 {
7423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7424 " Check colormap for background (%d,%d,%d)",
7425 (int) image->background_color.red,
7426 (int) image->background_color.green,
7427 (int) image->background_color.blue);
7428 }
glennrpd71e86a2011-02-24 01:28:37 +00007429 for (i=0; i<number_opaque; i++)
7430 {
glennrpca7ad3a2011-04-26 04:44:54 +00007431 if (opaque[i].red == image->background_color.red &&
7432 opaque[i].green == image->background_color.green &&
7433 opaque[i].blue == image->background_color.blue)
7434 break;
glennrpd71e86a2011-02-24 01:28:37 +00007435 }
glennrpd71e86a2011-02-24 01:28:37 +00007436 if (number_opaque < 259 && i == number_opaque)
7437 {
glennrp8e045c82011-04-27 16:40:27 +00007438 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00007439 ping_background.index = i;
7440 if (logging != MagickFalse)
7441 {
7442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7443 " background_color index is %d",(int) i);
7444 }
7445
glennrpd71e86a2011-02-24 01:28:37 +00007446 }
glennrpa080bc32011-03-11 18:03:44 +00007447 else if (logging != MagickFalse)
7448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7449 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007450 }
7451
7452 image_colors=number_opaque+number_transparent+number_semitransparent;
7453
glennrpa080bc32011-03-11 18:03:44 +00007454 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7455 {
7456 /* No room for the background color; remove it. */
7457 number_opaque--;
7458 image_colors--;
7459 }
7460
glennrpd71e86a2011-02-24 01:28:37 +00007461 if (logging != MagickFalse)
7462 {
7463 if (image_colors > 256)
7464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7465 " image has more than 256 colors");
7466
7467 else
7468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7469 " image has %d colors",image_colors);
7470 }
7471
glennrp8d3d6e52011-04-19 04:39:51 +00007472 if (ping_preserve_colormap != MagickFalse)
7473 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007474
glennrpfd05d622011-02-25 04:10:33 +00007475 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007476 {
7477 ping_have_color=MagickFalse;
7478 ping_have_non_bw=MagickFalse;
7479
7480 if(image_colors > 256)
7481 {
7482 for (y=0; y < (ssize_t) image->rows; y++)
7483 {
7484 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7485
7486 if (q == (PixelPacket *) NULL)
7487 break;
7488
7489 /* Worst case is black-and-white; we are looking at every
7490 * pixel twice.
7491 */
7492
7493 if (ping_have_color == MagickFalse)
7494 {
7495 s=q;
7496 for (x=0; x < (ssize_t) image->columns; x++)
7497 {
glennrpa18d5bc2011-04-23 14:51:34 +00007498 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7499 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00007500 {
7501 ping_have_color=MagickTrue;
7502 ping_have_non_bw=MagickTrue;
7503 break;
7504 }
7505 s++;
7506 }
7507 }
7508
7509 if (ping_have_non_bw == MagickFalse)
7510 {
7511 s=q;
7512 for (x=0; x < (ssize_t) image->columns; x++)
7513 {
glennrpa18d5bc2011-04-23 14:51:34 +00007514 if (GetRedPixelComponent(s) != 0 &&
7515 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00007516 {
7517 ping_have_non_bw=MagickTrue;
7518 }
7519 s++;
7520 }
7521 }
7522 }
7523 }
7524 }
7525
7526 if (image_colors < 257)
7527 {
7528 PixelPacket
7529 colormap[260];
7530
7531 /*
7532 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007533 */
7534
glennrpd71e86a2011-02-24 01:28:37 +00007535 if (logging != MagickFalse)
7536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7537 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007538
glennrpd71e86a2011-02-24 01:28:37 +00007539 /* Sort palette, transparent first */;
7540
7541 n = 0;
7542
7543 for (i=0; i<number_transparent; i++)
7544 colormap[n++] = transparent[i];
7545
7546 for (i=0; i<number_semitransparent; i++)
7547 colormap[n++] = semitransparent[i];
7548
7549 for (i=0; i<number_opaque; i++)
7550 colormap[n++] = opaque[i];
7551
glennrpc6c391a2011-04-27 02:23:56 +00007552 ping_background.index +=
7553 (number_transparent + number_semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00007554
7555 /* image_colors < 257; search the colormap instead of the pixels
7556 * to get ping_have_color and ping_have_non_bw
7557 */
7558 for (i=0; i<n; i++)
7559 {
7560 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007561 {
glennrpd71e86a2011-02-24 01:28:37 +00007562 if (colormap[i].red != colormap[i].green ||
7563 colormap[i].red != colormap[i].blue)
7564 {
7565 ping_have_color=MagickTrue;
7566 ping_have_non_bw=MagickTrue;
7567 break;
7568 }
7569 }
7570
7571 if (ping_have_non_bw == MagickFalse)
7572 {
7573 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007574 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007575 }
glennrp8bb3a022010-12-13 20:40:04 +00007576 }
7577
glennrpd71e86a2011-02-24 01:28:37 +00007578 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7579 (number_transparent == 0 && number_semitransparent == 0)) &&
7580 (((mng_info->write_png_colortype-1) ==
7581 PNG_COLOR_TYPE_PALETTE) ||
7582 (mng_info->write_png_colortype == 0)))
7583 {
glennrp6185c532011-01-14 17:58:40 +00007584 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007585 {
glennrpd71e86a2011-02-24 01:28:37 +00007586 if (n != (ssize_t) image_colors)
7587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7588 " image_colors (%d) and n (%d) don't match",
7589 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007590
glennrpd71e86a2011-02-24 01:28:37 +00007591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7592 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007593 }
glennrp03812ae2010-12-24 01:31:34 +00007594
glennrpd71e86a2011-02-24 01:28:37 +00007595 image->colors = image_colors;
7596
7597 if (AcquireImageColormap(image,image_colors) ==
7598 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007599 ThrowWriterException(ResourceLimitError,
7600 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007601
7602 for (i=0; i< (ssize_t) image_colors; i++)
7603 image->colormap[i] = colormap[i];
7604
7605 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007606 {
glennrpd71e86a2011-02-24 01:28:37 +00007607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7608 " image->colors=%d (%d)",
7609 (int) image->colors, image_colors);
7610
7611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7612 " Update the pixel indexes");
7613 }
glennrp6185c532011-01-14 17:58:40 +00007614
glennrpfd05d622011-02-25 04:10:33 +00007615 /* Sync the pixel indices with the new colormap */
7616
glennrpd71e86a2011-02-24 01:28:37 +00007617 for (y=0; y < (ssize_t) image->rows; y++)
7618 {
7619 q=GetAuthenticPixels(image,0,y,image->columns,1,
7620 exception);
glennrp6185c532011-01-14 17:58:40 +00007621
glennrpd71e86a2011-02-24 01:28:37 +00007622 if (q == (PixelPacket *) NULL)
7623 break;
glennrp6185c532011-01-14 17:58:40 +00007624
glennrpd71e86a2011-02-24 01:28:37 +00007625 indexes=GetAuthenticIndexQueue(image);
7626
7627 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007628 {
glennrpd71e86a2011-02-24 01:28:37 +00007629 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007630 {
glennrpd71e86a2011-02-24 01:28:37 +00007631 if ((image->matte == MagickFalse ||
glennrpca7ad3a2011-04-26 04:44:54 +00007632 image->colormap[i].opacity ==
7633 GetOpacityPixelComponent(q)) &&
7634 image->colormap[i].red ==
7635 GetRedPixelComponent(q) &&
7636 image->colormap[i].green ==
7637 GetGreenPixelComponent(q) &&
7638 image->colormap[i].blue ==
7639 GetBluePixelComponent(q))
glennrp6185c532011-01-14 17:58:40 +00007640 {
glennrpd71e86a2011-02-24 01:28:37 +00007641 indexes[x]=(IndexPacket) i;
7642 break;
glennrp6185c532011-01-14 17:58:40 +00007643 }
glennrp6185c532011-01-14 17:58:40 +00007644 }
glennrpd71e86a2011-02-24 01:28:37 +00007645 q++;
7646 }
glennrp6185c532011-01-14 17:58:40 +00007647
glennrpd71e86a2011-02-24 01:28:37 +00007648 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7649 break;
7650 }
7651 }
7652 }
7653
7654 if (logging != MagickFalse)
7655 {
7656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7657 " image->colors=%d", (int) image->colors);
7658
7659 if (image->colormap != NULL)
7660 {
7661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7662 " i (red,green,blue,opacity)");
7663
7664 for (i=0; i < (ssize_t) image->colors; i++)
7665 {
cristy72988482011-03-29 16:34:38 +00007666 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007667 {
7668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7669 " %d (%d,%d,%d,%d)",
7670 (int) i,
7671 (int) image->colormap[i].red,
7672 (int) image->colormap[i].green,
7673 (int) image->colormap[i].blue,
7674 (int) image->colormap[i].opacity);
7675 }
glennrp6185c532011-01-14 17:58:40 +00007676 }
7677 }
glennrp03812ae2010-12-24 01:31:34 +00007678
glennrpd71e86a2011-02-24 01:28:37 +00007679 if (number_transparent < 257)
7680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7681 " number_transparent = %d",
7682 number_transparent);
7683 else
glennrp03812ae2010-12-24 01:31:34 +00007684
glennrpd71e86a2011-02-24 01:28:37 +00007685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7686 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007687
glennrpd71e86a2011-02-24 01:28:37 +00007688 if (number_opaque < 257)
7689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7690 " number_opaque = %d",
7691 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007692
glennrpd71e86a2011-02-24 01:28:37 +00007693 else
7694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7695 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007696
glennrpd71e86a2011-02-24 01:28:37 +00007697 if (number_semitransparent < 257)
7698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699 " number_semitransparent = %d",
7700 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007701
glennrpd71e86a2011-02-24 01:28:37 +00007702 else
7703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7704 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007705
glennrpd71e86a2011-02-24 01:28:37 +00007706 if (ping_have_non_bw == MagickFalse)
7707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7708 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007709
glennrpd71e86a2011-02-24 01:28:37 +00007710 else if (ping_have_color == MagickFalse)
7711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7712 " All pixels and the background are gray");
7713
7714 else
7715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7716 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007717
glennrp03812ae2010-12-24 01:31:34 +00007718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007720 }
glennrpfd05d622011-02-25 04:10:33 +00007721
glennrpc8c2f062011-02-25 19:00:33 +00007722 if (mng_info->write_png8 == MagickFalse)
7723 break;
glennrpfd05d622011-02-25 04:10:33 +00007724
glennrpc8c2f062011-02-25 19:00:33 +00007725 /* Make any reductions necessary for the PNG8 format */
7726 if (image_colors <= 256 &&
7727 image_colors != 0 && image->colormap != NULL &&
7728 number_semitransparent == 0 &&
7729 number_transparent <= 1)
7730 break;
7731
7732 /* PNG8 can't have semitransparent colors so we threshold the
7733 * opacity to 0 or OpaqueOpacity
7734 */
7735 if (number_semitransparent != 0)
7736 {
7737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7738 " Thresholding the alpha channel to binary");
7739
7740 for (y=0; y < (ssize_t) image->rows; y++)
7741 {
7742 r=GetAuthenticPixels(image,0,y,image->columns,1,
7743 exception);
7744
7745 if (r == (PixelPacket *) NULL)
7746 break;
7747
7748 for (x=0; x < (ssize_t) image->columns; x++)
7749 {
glennrpa18d5bc2011-04-23 14:51:34 +00007750 SetOpacityPixelComponent(r,
7751 (GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7752 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00007753 r++;
7754 }
7755
7756 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7757 break;
7758
7759 if (image_colors != 0 && image_colors <= 256 &&
7760 image->colormap != NULL)
7761 for (i=0; i<image_colors; i++)
7762 image->colormap[i].opacity =
glennrp82b3c532011-03-22 19:20:54 +00007763 image->colormap[i].opacity > TransparentOpacity/2 ?
7764 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007765 }
7766 continue;
7767 }
7768
7769 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007770 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7771 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7772 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007773 */
glennrpd3371642011-03-22 19:42:23 +00007774 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7775 {
7776 if (logging != MagickFalse)
7777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7778 " Quantizing the background color to 4-4-4");
7779
7780 tried_444 = MagickTrue;
7781
7782 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007783 ScaleCharToQuantum(
7784 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7785 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007786 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007787 ScaleCharToQuantum(
7788 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7789 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007790 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007791 ScaleCharToQuantum(
7792 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7793 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007794
7795 if (logging != MagickFalse)
7796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7797 " Quantizing the pixel colors to 4-4-4");
7798
7799 if (image->colormap == NULL)
7800 {
7801 for (y=0; y < (ssize_t) image->rows; y++)
7802 {
7803 r=GetAuthenticPixels(image,0,y,image->columns,1,
7804 exception);
7805
7806 if (r == (PixelPacket *) NULL)
7807 break;
7808
7809 for (x=0; x < (ssize_t) image->columns; x++)
7810 {
7811 if (r->opacity == TransparentOpacity)
7812 {
glennrp8e045c82011-04-27 16:40:27 +00007813 SetRGBPixelComponents(image->background_color, r);
glennrpd3371642011-03-22 19:42:23 +00007814 }
7815 else
7816 {
glennrp3faa9a32011-04-23 14:00:25 +00007817 r->red=ScaleCharToQuantum(
7818 (ScaleQuantumToChar(r->red) & 0xf0) |
7819 (ScaleQuantumToChar(r->red) & 0xf0) >> 4);
7820 r->green=ScaleCharToQuantum(
7821 (ScaleQuantumToChar(r->green) & 0xf0) |
7822 (ScaleQuantumToChar(r->green) & 0xf0) >> 4);
7823 r->blue=ScaleCharToQuantum(
7824 (ScaleQuantumToChar(r->blue) & 0xf0) |
7825 (ScaleQuantumToChar(r->blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007826 }
7827 r++;
7828 }
7829
7830 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7831 break;
7832 }
7833 }
7834
7835 else /* Should not reach this; colormap already exists and
7836 must be <= 256 */
7837 {
7838 if (logging != MagickFalse)
7839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7840 " Quantizing the colormap to 4-4-4");
7841 for (i=0; i<image_colors; i++)
7842 {
glennrp3faa9a32011-04-23 14:00:25 +00007843 image->colormap[i].red=ScaleCharToQuantum(
7844 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7845 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7846 image->colormap[i].green=ScaleCharToQuantum(
7847 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7848 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7849 image->colormap[i].blue=ScaleCharToQuantum(
7850 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7851 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
glennrpd3371642011-03-22 19:42:23 +00007852 }
7853 }
7854 continue;
7855 }
7856
glennrp82b3c532011-03-22 19:20:54 +00007857 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7858 {
7859 if (logging != MagickFalse)
7860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7861 " Quantizing the background color to 3-3-3");
7862
7863 tried_333 = MagickTrue;
7864
7865 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007866 ScaleCharToQuantum(
7867 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7868 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7869 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007870 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007871 ScaleCharToQuantum(
7872 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7873 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7874 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007875 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007876 ScaleCharToQuantum(
7877 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7878 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7879 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007880
7881 if (logging != MagickFalse)
7882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007883 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007884
7885 if (image->colormap == NULL)
7886 {
7887 for (y=0; y < (ssize_t) image->rows; y++)
7888 {
7889 r=GetAuthenticPixels(image,0,y,image->columns,1,
7890 exception);
7891
7892 if (r == (PixelPacket *) NULL)
7893 break;
7894
7895 for (x=0; x < (ssize_t) image->columns; x++)
7896 {
7897 if (r->opacity == TransparentOpacity)
7898 {
7899 r->red = image->background_color.red;
7900 r->green = image->background_color.green;
7901 r->blue = image->background_color.blue;
7902 }
7903 else
7904 {
glennrp3faa9a32011-04-23 14:00:25 +00007905 r->red=ScaleCharToQuantum(
7906 (ScaleQuantumToChar(r->red) & 0xe0) |
7907 (ScaleQuantumToChar(r->red) & 0xe0) >> 3 |
7908 (ScaleQuantumToChar(r->red) & 0xc0) >> 6);
7909 r->green=ScaleCharToQuantum(
7910 (ScaleQuantumToChar(r->green) & 0xe0) |
7911 (ScaleQuantumToChar(r->green) & 0xe0) >> 3 |
7912 (ScaleQuantumToChar(r->green) & 0xc0) >> 6);
7913 r->blue=ScaleCharToQuantum(
7914 (ScaleQuantumToChar(r->blue) & 0xe0) |
7915 (ScaleQuantumToChar(r->blue) & 0xe0) >> 3 |
7916 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007917 }
7918 r++;
7919 }
7920
7921 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7922 break;
7923 }
7924 }
7925
7926 else /* Should not reach this; colormap already exists and
7927 must be <= 256 */
7928 {
7929 if (logging != MagickFalse)
7930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007931 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007932 for (i=0; i<image_colors; i++)
7933 {
glennrp3faa9a32011-04-23 14:00:25 +00007934 image->colormap[i].red=ScaleCharToQuantum(
7935 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7936 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7937 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7938 image->colormap[i].green=ScaleCharToQuantum(
7939 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7940 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7941 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7942 image->colormap[i].blue=ScaleCharToQuantum(
7943 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7944 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7945 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007946 }
glennrpd3371642011-03-22 19:42:23 +00007947 }
7948 continue;
glennrp82b3c532011-03-22 19:20:54 +00007949 }
glennrpc8c2f062011-02-25 19:00:33 +00007950
glennrpc8c2f062011-02-25 19:00:33 +00007951 if (image_colors == 0 || image_colors > 256)
7952 {
7953 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007955 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007956
glennrp3faa9a32011-04-23 14:00:25 +00007957 /* Red and green were already done so we only quantize the blue
7958 * channel
7959 */
7960
7961 image->background_color.blue=ScaleCharToQuantum(
7962 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7963 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7964 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7965 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrpfd05d622011-02-25 04:10:33 +00007966
glennrpc8c2f062011-02-25 19:00:33 +00007967 if (logging != MagickFalse)
7968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007969 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007970
glennrpc8c2f062011-02-25 19:00:33 +00007971 if (image->colormap == NULL)
7972 {
7973 for (y=0; y < (ssize_t) image->rows; y++)
7974 {
7975 r=GetAuthenticPixels(image,0,y,image->columns,1,
7976 exception);
7977
7978 if (r == (PixelPacket *) NULL)
7979 break;
7980
7981 for (x=0; x < (ssize_t) image->columns; x++)
7982 {
glennrp82b3c532011-03-22 19:20:54 +00007983 if (r->opacity == TransparentOpacity)
7984 {
glennrp8e045c82011-04-27 16:40:27 +00007985 SetRGBPixelComponents(image->background_color, r);
glennrp82b3c532011-03-22 19:20:54 +00007986 }
7987 else
7988 {
glennrp3faa9a32011-04-23 14:00:25 +00007989 r->blue=ScaleCharToQuantum(
7990 (ScaleQuantumToChar(r->blue) & 0xc0) |
7991 (ScaleQuantumToChar(r->blue) & 0xc0) >> 2 |
7992 (ScaleQuantumToChar(r->blue) & 0xc0) >> 4 |
7993 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007994 }
glennrp52a479c2011-02-26 21:14:38 +00007995 r++;
glennrpc8c2f062011-02-25 19:00:33 +00007996 }
glennrpfd05d622011-02-25 04:10:33 +00007997
glennrpc8c2f062011-02-25 19:00:33 +00007998 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7999 break;
8000 }
8001 }
glennrpfd05d622011-02-25 04:10:33 +00008002
glennrpc8c2f062011-02-25 19:00:33 +00008003 else /* Should not reach this; colormap already exists and
8004 must be <= 256 */
8005 {
8006 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008008 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008009 for (i=0; i<image_colors; i++)
8010 {
glennrp3faa9a32011-04-23 14:00:25 +00008011 image->colormap[i].blue=ScaleCharToQuantum(
8012 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
8013 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
8014 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
8015 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrpc8c2f062011-02-25 19:00:33 +00008016 }
8017 }
8018 continue;
8019 }
8020 break;
glennrpd71e86a2011-02-24 01:28:37 +00008021 }
glennrpfd05d622011-02-25 04:10:33 +00008022 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008023
glennrpfd05d622011-02-25 04:10:33 +00008024 /* If we are excluding the tRNS chunk and there is transparency,
8025 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8026 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008027 */
glennrp0e8ea192010-12-24 18:00:33 +00008028 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8029 (number_transparent != 0 || number_semitransparent != 0))
8030 {
8031 int colortype=mng_info->write_png_colortype;
8032
8033 if (ping_have_color == MagickFalse)
8034 mng_info->write_png_colortype = 5;
8035
8036 else
8037 mng_info->write_png_colortype = 7;
8038
glennrp8d579662011-02-23 02:05:02 +00008039 if (colortype != 0 &&
8040 mng_info->write_png_colortype != (ssize_t) colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008041 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008042
glennrp0e8ea192010-12-24 18:00:33 +00008043 }
8044
glennrpfd05d622011-02-25 04:10:33 +00008045 /* See if cheap transparency is possible. It is only possible
8046 * when there is a single transparent color, no semitransparent
8047 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008048 * as the transparent color. We only need this information if
8049 * we are writing a PNG with colortype 0 or 2, and we have not
8050 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008051 */
glennrp5a39f372011-02-25 04:52:16 +00008052 if (number_transparent == 1 &&
8053 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008054 {
8055 ping_have_cheap_transparency = MagickTrue;
8056
8057 if (number_semitransparent != 0)
8058 ping_have_cheap_transparency = MagickFalse;
8059
8060 else if (image_colors == 0 || image_colors > 256 ||
8061 image->colormap == NULL)
8062 {
8063 ExceptionInfo
8064 *exception;
8065
8066 register const PixelPacket
8067 *q;
8068
8069 exception=(&image->exception);
8070
8071 for (y=0; y < (ssize_t) image->rows; y++)
8072 {
8073 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8074
8075 if (q == (PixelPacket *) NULL)
8076 break;
8077
8078 for (x=0; x < (ssize_t) image->columns; x++)
8079 {
8080 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008081 (unsigned short) GetRedPixelComponent(q) ==
8082 ping_trans_color.red &&
8083 (unsigned short) GetGreenPixelComponent(q) ==
8084 ping_trans_color.green &&
8085 (unsigned short) GetBluePixelComponent(q) ==
8086 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008087 {
8088 ping_have_cheap_transparency = MagickFalse;
8089 break;
8090 }
8091
8092 q++;
8093 }
8094
8095 if (ping_have_cheap_transparency == MagickFalse)
8096 break;
8097 }
8098 }
8099 else
8100 {
glennrp67b9c1a2011-04-22 18:47:36 +00008101 /* Assuming that image->colormap[0] is the one transparent color
8102 * and that all others are opaque.
8103 */
glennrpfd05d622011-02-25 04:10:33 +00008104 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008105 for (i=1; i<image_colors; i++)
8106 if (image->colormap[i].red == image->colormap[0].red &&
8107 image->colormap[i].green == image->colormap[0].green &&
8108 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008109 {
glennrp67b9c1a2011-04-22 18:47:36 +00008110 ping_have_cheap_transparency = MagickFalse;
8111 break;
glennrpfd05d622011-02-25 04:10:33 +00008112 }
8113 }
8114
8115 if (logging != MagickFalse)
8116 {
8117 if (ping_have_cheap_transparency == MagickFalse)
8118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8119 " Cheap transparency is not possible.");
8120
8121 else
8122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8123 " Cheap transparency is possible.");
8124 }
8125 }
8126 else
8127 ping_have_cheap_transparency = MagickFalse;
8128
glennrp8640fb52010-11-23 15:48:26 +00008129 image_depth=image->depth;
8130
glennrp26c990a2010-11-23 02:23:20 +00008131 quantum_info = (QuantumInfo *) NULL;
8132 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008133 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008134 image_matte=image->matte;
8135
glennrp0fe50b42010-11-16 03:52:51 +00008136 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008137 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008138
glennrp52a479c2011-02-26 21:14:38 +00008139 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8140 (image->colors == 0 || image->colormap == NULL))
8141 {
glennrp52a479c2011-02-26 21:14:38 +00008142 image_info=DestroyImageInfo(image_info);
8143 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008144 (void) ThrowMagickException(&IMimage->exception,
8145 GetMagickModule(),CoderError,
8146 "Cannot write PNG8 or color-type 3; colormap is NULL",
8147 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008148#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8149 UnlockSemaphoreInfo(ping_semaphore);
8150#endif
8151 return(MagickFalse);
8152 }
8153
cristy3ed852e2009-09-05 21:47:34 +00008154 /*
8155 Allocate the PNG structures
8156 */
8157#ifdef PNG_USER_MEM_SUPPORTED
8158 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008159 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8160 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008161
cristy3ed852e2009-09-05 21:47:34 +00008162#else
8163 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008164 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008165
cristy3ed852e2009-09-05 21:47:34 +00008166#endif
8167 if (ping == (png_struct *) NULL)
8168 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008169
cristy3ed852e2009-09-05 21:47:34 +00008170 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008171
cristy3ed852e2009-09-05 21:47:34 +00008172 if (ping_info == (png_info *) NULL)
8173 {
8174 png_destroy_write_struct(&ping,(png_info **) NULL);
8175 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8176 }
glennrp0fe50b42010-11-16 03:52:51 +00008177
cristy3ed852e2009-09-05 21:47:34 +00008178 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008179 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008180
glennrp5af765f2010-03-30 11:12:18 +00008181 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008182 {
8183 /*
8184 PNG write failed.
8185 */
8186#ifdef PNG_DEBUG
8187 if (image_info->verbose)
8188 (void) printf("PNG write has failed.\n");
8189#endif
8190 png_destroy_write_struct(&ping,&ping_info);
8191#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008192 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008193#endif
glennrpda8f3a72011-02-27 23:54:12 +00008194 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008195 (void) CloseBlob(image);
8196 image_info=DestroyImageInfo(image_info);
8197 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008198 return(MagickFalse);
8199 }
8200 /*
8201 Prepare PNG for writing.
8202 */
8203#if defined(PNG_MNG_FEATURES_SUPPORTED)
8204 if (mng_info->write_mng)
8205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008206
cristy3ed852e2009-09-05 21:47:34 +00008207#else
8208# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8209 if (mng_info->write_mng)
8210 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008211
cristy3ed852e2009-09-05 21:47:34 +00008212# endif
8213#endif
glennrp2b013e42010-11-24 16:55:50 +00008214
cristy3ed852e2009-09-05 21:47:34 +00008215 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008216
cristy4e5bc842010-06-09 13:56:01 +00008217 ping_width=(png_uint_32) image->columns;
8218 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008219
cristy3ed852e2009-09-05 21:47:34 +00008220 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8221 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008222
cristy3ed852e2009-09-05 21:47:34 +00008223 if (mng_info->write_png_depth != 0)
8224 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008225
cristy3ed852e2009-09-05 21:47:34 +00008226 /* Adjust requested depth to next higher valid depth if necessary */
8227 if (image_depth > 8)
8228 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008229
cristy3ed852e2009-09-05 21:47:34 +00008230 if ((image_depth > 4) && (image_depth < 8))
8231 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008232
cristy3ed852e2009-09-05 21:47:34 +00008233 if (image_depth == 3)
8234 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008235
cristy3ed852e2009-09-05 21:47:34 +00008236 if (logging != MagickFalse)
8237 {
8238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008239 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008241 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008243 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008245 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008247 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008248 }
glennrp8640fb52010-11-23 15:48:26 +00008249
cristy3ed852e2009-09-05 21:47:34 +00008250 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008251 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008252
glennrp26f37912010-12-23 16:22:42 +00008253
cristy3ed852e2009-09-05 21:47:34 +00008254#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008255 if (ping_exclude_pHYs == MagickFalse)
8256 {
cristy3ed852e2009-09-05 21:47:34 +00008257 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8258 (!mng_info->write_mng || !mng_info->equal_physs))
8259 {
glennrp0fe50b42010-11-16 03:52:51 +00008260 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8262 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008263
8264 if (image->units == PixelsPerInchResolution)
8265 {
glennrpdfd70802010-11-14 01:23:35 +00008266 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008267 ping_pHYs_x_resolution=
8268 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8269 ping_pHYs_y_resolution=
8270 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008271 }
glennrpdfd70802010-11-14 01:23:35 +00008272
cristy3ed852e2009-09-05 21:47:34 +00008273 else if (image->units == PixelsPerCentimeterResolution)
8274 {
glennrpdfd70802010-11-14 01:23:35 +00008275 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008276 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8277 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008278 }
glennrp991d11d2010-11-12 21:55:28 +00008279
cristy3ed852e2009-09-05 21:47:34 +00008280 else
8281 {
glennrpdfd70802010-11-14 01:23:35 +00008282 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8283 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8284 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008285 }
glennrp991d11d2010-11-12 21:55:28 +00008286
glennrp823b55c2011-03-14 18:46:46 +00008287 if (logging != MagickFalse)
8288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8289 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8290 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8291 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008292 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008293 }
glennrp26f37912010-12-23 16:22:42 +00008294 }
cristy3ed852e2009-09-05 21:47:34 +00008295#endif
glennrpa521b2f2010-10-29 04:11:03 +00008296
glennrp26f37912010-12-23 16:22:42 +00008297 if (ping_exclude_bKGD == MagickFalse)
8298 {
glennrpa521b2f2010-10-29 04:11:03 +00008299 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008300 {
glennrpa521b2f2010-10-29 04:11:03 +00008301 unsigned int
8302 mask;
cristy3ed852e2009-09-05 21:47:34 +00008303
glennrpa521b2f2010-10-29 04:11:03 +00008304 mask=0xffff;
8305 if (ping_bit_depth == 8)
8306 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008307
glennrpa521b2f2010-10-29 04:11:03 +00008308 if (ping_bit_depth == 4)
8309 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008310
glennrpa521b2f2010-10-29 04:11:03 +00008311 if (ping_bit_depth == 2)
8312 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008313
glennrpa521b2f2010-10-29 04:11:03 +00008314 if (ping_bit_depth == 1)
8315 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008316
glennrpa521b2f2010-10-29 04:11:03 +00008317 ping_background.red=(png_uint_16)
8318 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008319
glennrpa521b2f2010-10-29 04:11:03 +00008320 ping_background.green=(png_uint_16)
8321 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008322
glennrpa521b2f2010-10-29 04:11:03 +00008323 ping_background.blue=(png_uint_16)
8324 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008325
8326 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008327 }
cristy3ed852e2009-09-05 21:47:34 +00008328
glennrp0fe50b42010-11-16 03:52:51 +00008329 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008330 {
8331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8332 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8334 " background_color index is %d",
8335 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008336
8337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8338 " ping_bit_depth=%d",ping_bit_depth);
8339 }
glennrp0fe50b42010-11-16 03:52:51 +00008340
8341 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008342 }
glennrp0fe50b42010-11-16 03:52:51 +00008343
cristy3ed852e2009-09-05 21:47:34 +00008344 /*
8345 Select the color type.
8346 */
8347 matte=image_matte;
8348 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008349
glennrp1273f7b2011-02-24 03:20:30 +00008350 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008351 {
glennrp0fe50b42010-11-16 03:52:51 +00008352
glennrpfd05d622011-02-25 04:10:33 +00008353 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008354 for reducing the sample depth from 8. */
8355
glennrp0fe50b42010-11-16 03:52:51 +00008356 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008357
glennrp8bb3a022010-12-13 20:40:04 +00008358 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008359
8360 /*
8361 Set image palette.
8362 */
8363 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8364
glennrp0fe50b42010-11-16 03:52:51 +00008365 if (logging != MagickFalse)
8366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8367 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008368 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008369
8370 for (i=0; i < (ssize_t) number_colors; i++)
8371 {
8372 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8373 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8374 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8375 if (logging != MagickFalse)
8376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008377#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008378 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008379#else
8380 " %5ld (%5d,%5d,%5d)",
8381#endif
glennrp0fe50b42010-11-16 03:52:51 +00008382 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8383
8384 }
glennrp2b013e42010-11-24 16:55:50 +00008385
glennrp8bb3a022010-12-13 20:40:04 +00008386 ping_have_PLTE=MagickTrue;
8387 image_depth=ping_bit_depth;
8388 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008389
glennrp58e01762011-01-07 15:28:54 +00008390 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008391 {
glennrp0fe50b42010-11-16 03:52:51 +00008392 /*
8393 Identify which colormap entry is transparent.
8394 */
8395 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008396 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008397
glennrp8bb3a022010-12-13 20:40:04 +00008398 for (i=0; i < (ssize_t) number_transparent; i++)
8399 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008400
glennrp0fe50b42010-11-16 03:52:51 +00008401
glennrp2cc891a2010-12-24 13:44:32 +00008402 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008403 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008404
8405 if (ping_num_trans == 0)
8406 ping_have_tRNS=MagickFalse;
8407
glennrp8bb3a022010-12-13 20:40:04 +00008408 else
8409 ping_have_tRNS=MagickTrue;
8410 }
glennrp0fe50b42010-11-16 03:52:51 +00008411
glennrp1273f7b2011-02-24 03:20:30 +00008412 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008413 {
glennrp1273f7b2011-02-24 03:20:30 +00008414 /*
8415 * Identify which colormap entry is the background color.
8416 */
8417
glennrp4f25bd02011-01-01 18:51:28 +00008418 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8419 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8420 break;
glennrp0fe50b42010-11-16 03:52:51 +00008421
glennrp4f25bd02011-01-01 18:51:28 +00008422 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00008423
8424 if (logging != MagickFalse)
8425 {
8426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8427 " background_color index is %d",
8428 (int) ping_background.index);
8429 }
glennrp4f25bd02011-01-01 18:51:28 +00008430 }
cristy3ed852e2009-09-05 21:47:34 +00008431 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008432
cristy3ed852e2009-09-05 21:47:34 +00008433 else if (mng_info->write_png24)
8434 {
8435 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008436 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008437 }
glennrp0fe50b42010-11-16 03:52:51 +00008438
cristy3ed852e2009-09-05 21:47:34 +00008439 else if (mng_info->write_png32)
8440 {
8441 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008442 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008443 }
glennrp0fe50b42010-11-16 03:52:51 +00008444
glennrp8bb3a022010-12-13 20:40:04 +00008445 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008446 {
glennrp5af765f2010-03-30 11:12:18 +00008447 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008448
glennrp8bb3a022010-12-13 20:40:04 +00008449 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008450 {
glennrp5af765f2010-03-30 11:12:18 +00008451 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008452
glennrp5af765f2010-03-30 11:12:18 +00008453 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8454 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008455 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008456
glennrp8bb3a022010-12-13 20:40:04 +00008457 else
8458 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008459
8460 if (logging != MagickFalse)
8461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8462 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008463 }
glennrp0fe50b42010-11-16 03:52:51 +00008464
glennrp7c4c9e62011-03-21 20:23:32 +00008465 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008466 {
8467 if (logging != MagickFalse)
8468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008469 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008470
glennrpd6bf1612010-12-17 17:28:54 +00008471 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008472 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008473
glennrpd6bf1612010-12-17 17:28:54 +00008474 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008475 {
glennrp5af765f2010-03-30 11:12:18 +00008476 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008477 image_matte=MagickFalse;
8478 }
glennrp0fe50b42010-11-16 03:52:51 +00008479
glennrpd6bf1612010-12-17 17:28:54 +00008480 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008481 {
glennrp5af765f2010-03-30 11:12:18 +00008482 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008483 image_matte=MagickTrue;
8484 }
glennrp0fe50b42010-11-16 03:52:51 +00008485
glennrp5aa37f62011-01-02 03:07:57 +00008486 if (image_info->type == PaletteType ||
8487 image_info->type == PaletteMatteType)
8488 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8489
glennrp7c4c9e62011-03-21 20:23:32 +00008490 if (mng_info->write_png_colortype == 0 &&
8491 (image_info->type == UndefinedType ||
8492 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008493 {
glennrp5aa37f62011-01-02 03:07:57 +00008494 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008495 {
glennrp5aa37f62011-01-02 03:07:57 +00008496 if (image_matte == MagickFalse)
8497 {
8498 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8499 image_matte=MagickFalse;
8500 }
glennrp0fe50b42010-11-16 03:52:51 +00008501
glennrp0b206f52011-01-07 04:55:32 +00008502 else
glennrp5aa37f62011-01-02 03:07:57 +00008503 {
8504 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8505 image_matte=MagickTrue;
8506 }
8507 }
8508 else
glennrp8bb3a022010-12-13 20:40:04 +00008509 {
glennrp5aa37f62011-01-02 03:07:57 +00008510 if (image_matte == MagickFalse)
8511 {
8512 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8513 image_matte=MagickFalse;
8514 }
glennrp8bb3a022010-12-13 20:40:04 +00008515
glennrp0b206f52011-01-07 04:55:32 +00008516 else
glennrp5aa37f62011-01-02 03:07:57 +00008517 {
8518 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8519 image_matte=MagickTrue;
8520 }
8521 }
glennrp0fe50b42010-11-16 03:52:51 +00008522 }
glennrp5aa37f62011-01-02 03:07:57 +00008523
cristy3ed852e2009-09-05 21:47:34 +00008524 }
glennrp0fe50b42010-11-16 03:52:51 +00008525
cristy3ed852e2009-09-05 21:47:34 +00008526 if (logging != MagickFalse)
8527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008528 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008529
glennrp5af765f2010-03-30 11:12:18 +00008530 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008531 {
8532 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8533 ping_color_type == PNG_COLOR_TYPE_RGB ||
8534 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8535 ping_bit_depth=8;
8536 }
cristy3ed852e2009-09-05 21:47:34 +00008537
glennrpd6bf1612010-12-17 17:28:54 +00008538 old_bit_depth=ping_bit_depth;
8539
glennrp5af765f2010-03-30 11:12:18 +00008540 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008541 {
glennrp8d579662011-02-23 02:05:02 +00008542 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8543 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008544 }
glennrp8640fb52010-11-23 15:48:26 +00008545
glennrp5af765f2010-03-30 11:12:18 +00008546 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008547 {
cristy35ef8242010-06-03 16:24:13 +00008548 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008549 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008550
8551 if (image->colors == 0)
8552 {
glennrp0fe50b42010-11-16 03:52:51 +00008553 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008554 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008555 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008556 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008557 }
8558
cristy35ef8242010-06-03 16:24:13 +00008559 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008560 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008561 }
glennrp2b013e42010-11-24 16:55:50 +00008562
glennrpd6bf1612010-12-17 17:28:54 +00008563 if (logging != MagickFalse)
8564 {
8565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8566 " Number of colors: %.20g",(double) image_colors);
8567
8568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8569 " Tentative PNG bit depth: %d",ping_bit_depth);
8570 }
8571
8572 if (ping_bit_depth < (int) mng_info->write_png_depth)
8573 ping_bit_depth = mng_info->write_png_depth;
8574 }
glennrp2cc891a2010-12-24 13:44:32 +00008575
glennrp5af765f2010-03-30 11:12:18 +00008576 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008577
cristy3ed852e2009-09-05 21:47:34 +00008578 if (logging != MagickFalse)
8579 {
8580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008581 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008582
cristy3ed852e2009-09-05 21:47:34 +00008583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008584 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008585
cristy3ed852e2009-09-05 21:47:34 +00008586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008587 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008588
cristy3ed852e2009-09-05 21:47:34 +00008589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008590
glennrp8640fb52010-11-23 15:48:26 +00008591 " image->depth: %.20g",(double) image->depth);
8592
8593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008594 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008595 }
8596
glennrp58e01762011-01-07 15:28:54 +00008597 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008598 {
glennrp4f25bd02011-01-01 18:51:28 +00008599 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008600 {
glennrp7c4c9e62011-03-21 20:23:32 +00008601 if (mng_info->write_png_colortype == 0)
8602 {
8603 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008604
glennrp7c4c9e62011-03-21 20:23:32 +00008605 if (ping_have_color != MagickFalse)
8606 ping_color_type=PNG_COLOR_TYPE_RGBA;
8607 }
glennrp4f25bd02011-01-01 18:51:28 +00008608
8609 /*
8610 * Determine if there is any transparent color.
8611 */
8612 if (number_transparent + number_semitransparent == 0)
8613 {
8614 /*
8615 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8616 */
glennrpa6a06632011-01-19 15:15:34 +00008617
glennrp4f25bd02011-01-01 18:51:28 +00008618 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008619
8620 if (mng_info->write_png_colortype == 0)
8621 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008622 }
8623
8624 else
8625 {
8626 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008627 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008628
8629 mask=0xffff;
8630
8631 if (ping_bit_depth == 8)
8632 mask=0x00ff;
8633
8634 if (ping_bit_depth == 4)
8635 mask=0x000f;
8636
8637 if (ping_bit_depth == 2)
8638 mask=0x0003;
8639
8640 if (ping_bit_depth == 1)
8641 mask=0x0001;
8642
8643 ping_trans_color.red=(png_uint_16)
8644 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8645
8646 ping_trans_color.green=(png_uint_16)
8647 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8648
8649 ping_trans_color.blue=(png_uint_16)
8650 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8651
8652 ping_trans_color.gray=(png_uint_16)
8653 (ScaleQuantumToShort(PixelIntensityToQuantum(
8654 image->colormap)) & mask);
8655
8656 ping_trans_color.index=(png_byte) 0;
8657
8658 ping_have_tRNS=MagickTrue;
8659 }
8660
8661 if (ping_have_tRNS != MagickFalse)
8662 {
8663 /*
glennrpfd05d622011-02-25 04:10:33 +00008664 * Determine if there is one and only one transparent color
8665 * and if so if it is fully transparent.
8666 */
8667 if (ping_have_cheap_transparency == MagickFalse)
8668 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008669 }
8670
8671 if (ping_have_tRNS != MagickFalse)
8672 {
glennrp7c4c9e62011-03-21 20:23:32 +00008673 if (mng_info->write_png_colortype == 0)
8674 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008675
8676 if (image_depth == 8)
8677 {
8678 ping_trans_color.red&=0xff;
8679 ping_trans_color.green&=0xff;
8680 ping_trans_color.blue&=0xff;
8681 ping_trans_color.gray&=0xff;
8682 }
8683 }
8684 }
cristy3ed852e2009-09-05 21:47:34 +00008685 else
8686 {
cristy3ed852e2009-09-05 21:47:34 +00008687 if (image_depth == 8)
8688 {
glennrp5af765f2010-03-30 11:12:18 +00008689 ping_trans_color.red&=0xff;
8690 ping_trans_color.green&=0xff;
8691 ping_trans_color.blue&=0xff;
8692 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008693 }
8694 }
8695 }
glennrp8640fb52010-11-23 15:48:26 +00008696
cristy3ed852e2009-09-05 21:47:34 +00008697 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008698
glennrp2e09f552010-11-14 00:38:48 +00008699 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008700 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008701
glennrp39992b42010-11-14 00:03:43 +00008702 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008703 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008704 ping_have_color == MagickFalse &&
8705 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008706 {
cristy35ef8242010-06-03 16:24:13 +00008707 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008708
cristy3ed852e2009-09-05 21:47:34 +00008709 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008710 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008711
glennrp7c4c9e62011-03-21 20:23:32 +00008712 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008713 {
glennrp5af765f2010-03-30 11:12:18 +00008714 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008715
cristy3ed852e2009-09-05 21:47:34 +00008716 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008717 {
8718 if (logging != MagickFalse)
8719 {
8720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8721 " Scaling ping_trans_color (0)");
8722 }
8723 ping_trans_color.gray*=0x0101;
8724 }
cristy3ed852e2009-09-05 21:47:34 +00008725 }
glennrp0fe50b42010-11-16 03:52:51 +00008726
cristy3ed852e2009-09-05 21:47:34 +00008727 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8728 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008729
glennrp136ee3a2011-04-27 15:47:45 +00008730 if ((image_colors == 0) ||
8731 ((ssize_t) (image_colors-1) > MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008732 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008733
cristy3ed852e2009-09-05 21:47:34 +00008734 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008735 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008736
cristy3ed852e2009-09-05 21:47:34 +00008737 else
8738 {
glennrp5af765f2010-03-30 11:12:18 +00008739 ping_bit_depth=8;
8740 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008741 {
8742 if(!mng_info->write_png_depth)
8743 {
glennrp5af765f2010-03-30 11:12:18 +00008744 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008745
cristy35ef8242010-06-03 16:24:13 +00008746 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008747 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008748 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008749 }
8750 }
glennrp2b013e42010-11-24 16:55:50 +00008751
glennrp0fe50b42010-11-16 03:52:51 +00008752 else if (ping_color_type ==
8753 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008754 mng_info->IsPalette)
8755 {
cristy3ed852e2009-09-05 21:47:34 +00008756 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008757
cristy3ed852e2009-09-05 21:47:34 +00008758 int
8759 depth_4_ok=MagickTrue,
8760 depth_2_ok=MagickTrue,
8761 depth_1_ok=MagickTrue;
8762
cristybb503372010-05-27 20:51:26 +00008763 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008764 {
8765 unsigned char
8766 intensity;
8767
8768 intensity=ScaleQuantumToChar(image->colormap[i].red);
8769
8770 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8771 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8772 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8773 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008774 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008775 depth_1_ok=MagickFalse;
8776 }
glennrp2b013e42010-11-24 16:55:50 +00008777
cristy3ed852e2009-09-05 21:47:34 +00008778 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008779 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008780
cristy3ed852e2009-09-05 21:47:34 +00008781 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008782 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008783
cristy3ed852e2009-09-05 21:47:34 +00008784 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008785 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008786 }
8787 }
glennrp2b013e42010-11-24 16:55:50 +00008788
glennrp5af765f2010-03-30 11:12:18 +00008789 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008790 }
glennrp0fe50b42010-11-16 03:52:51 +00008791
cristy3ed852e2009-09-05 21:47:34 +00008792 else
glennrp0fe50b42010-11-16 03:52:51 +00008793
cristy3ed852e2009-09-05 21:47:34 +00008794 if (mng_info->IsPalette)
8795 {
glennrp17a14852010-05-10 03:01:59 +00008796 number_colors=image_colors;
8797
cristy3ed852e2009-09-05 21:47:34 +00008798 if (image_depth <= 8)
8799 {
cristy3ed852e2009-09-05 21:47:34 +00008800 /*
8801 Set image palette.
8802 */
glennrp5af765f2010-03-30 11:12:18 +00008803 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008804
glennrp58e01762011-01-07 15:28:54 +00008805 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008806 {
glennrp9c1eb072010-06-06 22:19:15 +00008807 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008808
glennrp3b51f0e2010-11-27 18:14:08 +00008809 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8811 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008812 }
glennrp0fe50b42010-11-16 03:52:51 +00008813
cristy3ed852e2009-09-05 21:47:34 +00008814 else
8815 {
cristybb503372010-05-27 20:51:26 +00008816 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008817 {
8818 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8819 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8820 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8821 }
glennrp0fe50b42010-11-16 03:52:51 +00008822
glennrp3b51f0e2010-11-27 18:14:08 +00008823 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008825 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008826 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008827
glennrp39992b42010-11-14 00:03:43 +00008828 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008829 }
glennrp0fe50b42010-11-16 03:52:51 +00008830
cristy3ed852e2009-09-05 21:47:34 +00008831 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008832 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008833 {
cristybefe4d22010-06-07 01:18:58 +00008834 size_t
8835 one;
8836
glennrp5af765f2010-03-30 11:12:18 +00008837 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008838 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008839
cristybefe4d22010-06-07 01:18:58 +00008840 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008841 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008842 }
glennrp0fe50b42010-11-16 03:52:51 +00008843
glennrp5af765f2010-03-30 11:12:18 +00008844 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008845
glennrp58e01762011-01-07 15:28:54 +00008846 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008847 {
glennrp0fe50b42010-11-16 03:52:51 +00008848 /*
glennrpd6bf1612010-12-17 17:28:54 +00008849 * Set up trans_colors array.
8850 */
glennrp0fe50b42010-11-16 03:52:51 +00008851 assert(number_colors <= 256);
8852
glennrpd6bf1612010-12-17 17:28:54 +00008853 ping_num_trans=(unsigned short) (number_transparent +
8854 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008855
8856 if (ping_num_trans == 0)
8857 ping_have_tRNS=MagickFalse;
8858
glennrpd6bf1612010-12-17 17:28:54 +00008859 else
glennrp0fe50b42010-11-16 03:52:51 +00008860 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008861 if (logging != MagickFalse)
8862 {
8863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8864 " Scaling ping_trans_color (1)");
8865 }
glennrpd6bf1612010-12-17 17:28:54 +00008866 ping_have_tRNS=MagickTrue;
8867
8868 for (i=0; i < ping_num_trans; i++)
8869 {
8870 ping_trans_alpha[i]= (png_byte) (255-
8871 ScaleQuantumToChar(image->colormap[i].opacity));
8872 }
glennrp0fe50b42010-11-16 03:52:51 +00008873 }
8874 }
cristy3ed852e2009-09-05 21:47:34 +00008875 }
8876 }
glennrp0fe50b42010-11-16 03:52:51 +00008877
cristy3ed852e2009-09-05 21:47:34 +00008878 else
8879 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008880
cristy3ed852e2009-09-05 21:47:34 +00008881 if (image_depth < 8)
8882 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008883
cristy3ed852e2009-09-05 21:47:34 +00008884 if ((save_image_depth == 16) && (image_depth == 8))
8885 {
glennrp4f25bd02011-01-01 18:51:28 +00008886 if (logging != MagickFalse)
8887 {
8888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8889 " Scaling ping_trans_color from (%d,%d,%d)",
8890 (int) ping_trans_color.red,
8891 (int) ping_trans_color.green,
8892 (int) ping_trans_color.blue);
8893 }
8894
glennrp5af765f2010-03-30 11:12:18 +00008895 ping_trans_color.red*=0x0101;
8896 ping_trans_color.green*=0x0101;
8897 ping_trans_color.blue*=0x0101;
8898 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008899
8900 if (logging != MagickFalse)
8901 {
8902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8903 " to (%d,%d,%d)",
8904 (int) ping_trans_color.red,
8905 (int) ping_trans_color.green,
8906 (int) ping_trans_color.blue);
8907 }
cristy3ed852e2009-09-05 21:47:34 +00008908 }
8909 }
8910
cristy4383ec82011-01-05 15:42:32 +00008911 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8912 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008913
cristy3ed852e2009-09-05 21:47:34 +00008914 /*
8915 Adjust background and transparency samples in sub-8-bit grayscale files.
8916 */
glennrp5af765f2010-03-30 11:12:18 +00008917 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008918 PNG_COLOR_TYPE_GRAY)
8919 {
8920 png_uint_16
8921 maxval;
8922
cristy35ef8242010-06-03 16:24:13 +00008923 size_t
8924 one=1;
8925
cristy22ffd972010-06-03 16:51:47 +00008926 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008927
glennrp4f25bd02011-01-01 18:51:28 +00008928 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008929 {
cristy3ed852e2009-09-05 21:47:34 +00008930
glennrpa521b2f2010-10-29 04:11:03 +00008931 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008932 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8933
8934 if (logging != MagickFalse)
8935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008936 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00008937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8938 " background_color index is %d",
8939 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008940
glennrp991d11d2010-11-12 21:55:28 +00008941 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008942 }
cristy3ed852e2009-09-05 21:47:34 +00008943
glennrp5af765f2010-03-30 11:12:18 +00008944 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8945 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008946 }
glennrp17a14852010-05-10 03:01:59 +00008947
glennrp26f37912010-12-23 16:22:42 +00008948 if (ping_exclude_bKGD == MagickFalse)
8949 {
glennrp1273f7b2011-02-24 03:20:30 +00008950 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008951 {
8952 /*
8953 Identify which colormap entry is the background color.
8954 */
8955
glennrp17a14852010-05-10 03:01:59 +00008956 number_colors=image_colors;
8957
glennrpa521b2f2010-10-29 04:11:03 +00008958 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8959 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008960 break;
8961
8962 ping_background.index=(png_byte) i;
8963
glennrp3b51f0e2010-11-27 18:14:08 +00008964 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008965 {
8966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008967 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008968 }
glennrp0fe50b42010-11-16 03:52:51 +00008969
cristy13d07042010-11-21 20:56:18 +00008970 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008971 {
8972 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008973
8974 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008975 {
8976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8977 " background =(%d,%d,%d)",
8978 (int) ping_background.red,
8979 (int) ping_background.green,
8980 (int) ping_background.blue);
8981 }
8982 }
glennrpa521b2f2010-10-29 04:11:03 +00008983
glennrpd6bf1612010-12-17 17:28:54 +00008984 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008985 {
glennrp3b51f0e2010-11-27 18:14:08 +00008986 if (logging != MagickFalse)
8987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8988 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008989 ping_have_bKGD = MagickFalse;
8990 }
glennrp17a14852010-05-10 03:01:59 +00008991 }
glennrp26f37912010-12-23 16:22:42 +00008992 }
glennrp17a14852010-05-10 03:01:59 +00008993
cristy3ed852e2009-09-05 21:47:34 +00008994 if (logging != MagickFalse)
8995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00008996 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008997 /*
8998 Initialize compression level and filtering.
8999 */
9000 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009001 {
9002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9003 " Setting up deflate compression");
9004
9005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9006 " Compression buffer size: 32768");
9007 }
9008
cristy3ed852e2009-09-05 21:47:34 +00009009 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009010
cristy3ed852e2009-09-05 21:47:34 +00009011 if (logging != MagickFalse)
9012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9013 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009014
cristy3ed852e2009-09-05 21:47:34 +00009015 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009016
cristy3ed852e2009-09-05 21:47:34 +00009017 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9018 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009019
cristy3ed852e2009-09-05 21:47:34 +00009020 if (quality > 9)
9021 {
9022 int
9023 level;
9024
cristybb503372010-05-27 20:51:26 +00009025 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009026
cristy3ed852e2009-09-05 21:47:34 +00009027 if (logging != MagickFalse)
9028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00009030
cristy3ed852e2009-09-05 21:47:34 +00009031 png_set_compression_level(ping,level);
9032 }
glennrp0fe50b42010-11-16 03:52:51 +00009033
cristy3ed852e2009-09-05 21:47:34 +00009034 else
9035 {
9036 if (logging != MagickFalse)
9037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9038 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009039
cristy3ed852e2009-09-05 21:47:34 +00009040 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9041 }
glennrp0fe50b42010-11-16 03:52:51 +00009042
cristy3ed852e2009-09-05 21:47:34 +00009043 if (logging != MagickFalse)
9044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9045 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009046
glennrp2b013e42010-11-24 16:55:50 +00009047#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009048 /* This became available in libpng-1.0.9. Output must be a MNG. */
9049 if (mng_info->write_mng && ((quality % 10) == 7))
9050 {
9051 if (logging != MagickFalse)
9052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9053 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009054
glennrp5af765f2010-03-30 11:12:18 +00009055 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009056 }
glennrp0fe50b42010-11-16 03:52:51 +00009057
cristy3ed852e2009-09-05 21:47:34 +00009058 else
9059 if (logging != MagickFalse)
9060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9061 " Filter_type: 0");
9062#endif
glennrp2b013e42010-11-24 16:55:50 +00009063
cristy3ed852e2009-09-05 21:47:34 +00009064 {
9065 int
9066 base_filter;
9067
9068 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009069 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009070
glennrp26c990a2010-11-23 02:23:20 +00009071 else
glennrp8640fb52010-11-23 15:48:26 +00009072 if ((quality % 10) != 5)
9073 base_filter=(int) quality % 10;
9074
9075 else
9076 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9077 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9078 (quality < 50))
9079 base_filter=PNG_NO_FILTERS;
9080
9081 else
9082 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009083
cristy3ed852e2009-09-05 21:47:34 +00009084 if (logging != MagickFalse)
9085 {
9086 if (base_filter == PNG_ALL_FILTERS)
9087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9088 " Base filter method: ADAPTIVE");
9089 else
9090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9091 " Base filter method: NONE");
9092 }
glennrp2b013e42010-11-24 16:55:50 +00009093
cristy3ed852e2009-09-05 21:47:34 +00009094 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9095 }
9096
glennrp823b55c2011-03-14 18:46:46 +00009097 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9098 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009099 {
9100 ResetImageProfileIterator(image);
9101 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009102 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009103 profile=GetImageProfile(image,name);
9104
9105 if (profile != (StringInfo *) NULL)
9106 {
glennrp5af765f2010-03-30 11:12:18 +00009107#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009108 if ((LocaleCompare(name,"ICC") == 0) ||
9109 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009110 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009111
9112 if (ping_exclude_iCCP == MagickFalse)
9113 {
9114 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009115#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009116 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009117#else
9118 (png_const_bytep) GetStringInfoDatum(profile),
9119#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009120 (png_uint_32) GetStringInfoLength(profile));
9121 }
glennrp26f37912010-12-23 16:22:42 +00009122 }
glennrp0fe50b42010-11-16 03:52:51 +00009123
glennrpc8cbc5d2011-01-01 00:12:34 +00009124 else
cristy3ed852e2009-09-05 21:47:34 +00009125#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009126 if (ping_exclude_zCCP == MagickFalse)
9127 {
glennrpcf002022011-01-30 02:38:15 +00009128 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009129 (unsigned char *) name,(unsigned char *) name,
9130 GetStringInfoDatum(profile),
9131 (png_uint_32) GetStringInfoLength(profile));
9132 }
9133 }
glennrp0b206f52011-01-07 04:55:32 +00009134
glennrpc8cbc5d2011-01-01 00:12:34 +00009135 if (logging != MagickFalse)
9136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9137 " Setting up text chunk with %s profile",name);
9138
9139 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009140 }
cristy3ed852e2009-09-05 21:47:34 +00009141 }
9142
9143#if defined(PNG_WRITE_sRGB_SUPPORTED)
9144 if ((mng_info->have_write_global_srgb == 0) &&
9145 ((image->rendering_intent != UndefinedIntent) ||
9146 (image->colorspace == sRGBColorspace)))
9147 {
glennrp26f37912010-12-23 16:22:42 +00009148 if (ping_exclude_sRGB == MagickFalse)
9149 {
9150 /*
9151 Note image rendering intent.
9152 */
9153 if (logging != MagickFalse)
9154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9155 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009156
glennrp26f37912010-12-23 16:22:42 +00009157 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009158 Magick_RenderingIntent_to_PNG_RenderingIntent(
9159 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009160
glennrp26f37912010-12-23 16:22:42 +00009161 if (ping_exclude_gAMA == MagickFalse)
9162 png_set_gAMA(ping,ping_info,0.45455);
9163 }
cristy3ed852e2009-09-05 21:47:34 +00009164 }
glennrp26f37912010-12-23 16:22:42 +00009165
glennrp5af765f2010-03-30 11:12:18 +00009166 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009167#endif
9168 {
glennrp2cc891a2010-12-24 13:44:32 +00009169 if (ping_exclude_gAMA == MagickFalse &&
9170 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009171 (image->gamma < .45 || image->gamma > .46)))
9172 {
cristy3ed852e2009-09-05 21:47:34 +00009173 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9174 {
9175 /*
9176 Note image gamma.
9177 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9178 */
9179 if (logging != MagickFalse)
9180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9181 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009182
cristy3ed852e2009-09-05 21:47:34 +00009183 png_set_gAMA(ping,ping_info,image->gamma);
9184 }
glennrp26f37912010-12-23 16:22:42 +00009185 }
glennrp2b013e42010-11-24 16:55:50 +00009186
glennrp26f37912010-12-23 16:22:42 +00009187 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009188 {
glennrp26f37912010-12-23 16:22:42 +00009189 if ((mng_info->have_write_global_chrm == 0) &&
9190 (image->chromaticity.red_primary.x != 0.0))
9191 {
9192 /*
9193 Note image chromaticity.
9194 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9195 */
9196 PrimaryInfo
9197 bp,
9198 gp,
9199 rp,
9200 wp;
cristy3ed852e2009-09-05 21:47:34 +00009201
glennrp26f37912010-12-23 16:22:42 +00009202 wp=image->chromaticity.white_point;
9203 rp=image->chromaticity.red_primary;
9204 gp=image->chromaticity.green_primary;
9205 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009206
glennrp26f37912010-12-23 16:22:42 +00009207 if (logging != MagickFalse)
9208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9209 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009210
glennrp26f37912010-12-23 16:22:42 +00009211 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9212 bp.x,bp.y);
9213 }
9214 }
cristy3ed852e2009-09-05 21:47:34 +00009215 }
glennrpdfd70802010-11-14 01:23:35 +00009216
glennrp5af765f2010-03-30 11:12:18 +00009217 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009218
9219 if (mng_info->write_mng)
9220 png_set_sig_bytes(ping,8);
9221
9222 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9223
glennrpd6bf1612010-12-17 17:28:54 +00009224 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009225 {
9226 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009227 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009228 {
glennrp5af765f2010-03-30 11:12:18 +00009229 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009230
glennrp5af765f2010-03-30 11:12:18 +00009231 if (ping_bit_depth < 8)
9232 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009233 }
glennrp0fe50b42010-11-16 03:52:51 +00009234
cristy3ed852e2009-09-05 21:47:34 +00009235 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009236 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009237 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009238 }
9239
glennrp0e8ea192010-12-24 18:00:33 +00009240 if (ping_need_colortype_warning != MagickFalse ||
9241 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009242 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009243 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009244 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009245 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009246 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009247 {
9248 if (logging != MagickFalse)
9249 {
glennrp0e8ea192010-12-24 18:00:33 +00009250 if (ping_need_colortype_warning != MagickFalse)
9251 {
9252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9253 " Image has transparency but tRNS chunk was excluded");
9254 }
9255
cristy3ed852e2009-09-05 21:47:34 +00009256 if (mng_info->write_png_depth)
9257 {
9258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9259 " Defined PNG:bit-depth=%u, Computed depth=%u",
9260 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009261 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009262 }
glennrp0e8ea192010-12-24 18:00:33 +00009263
cristy3ed852e2009-09-05 21:47:34 +00009264 if (mng_info->write_png_colortype)
9265 {
9266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9267 " Defined PNG:color-type=%u, Computed color type=%u",
9268 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009269 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009270 }
9271 }
glennrp0e8ea192010-12-24 18:00:33 +00009272
glennrp3bd2e412010-08-10 13:34:52 +00009273 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009274 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9275 }
9276
glennrp58e01762011-01-07 15:28:54 +00009277 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009278 {
9279 /* Add an opaque matte channel */
9280 image->matte = MagickTrue;
9281 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009282
glennrpb4a13412010-05-05 12:47:19 +00009283 if (logging != MagickFalse)
9284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9285 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009286 }
9287
glennrp0e319732011-01-25 21:53:13 +00009288 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009289 {
glennrp991d11d2010-11-12 21:55:28 +00009290 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009291 {
glennrp991d11d2010-11-12 21:55:28 +00009292 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009293 if (logging != MagickFalse)
9294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9295 " Setting ping_have_tRNS=MagickTrue.");
9296 }
glennrpe9c26dc2010-05-30 01:56:35 +00009297 }
9298
cristy3ed852e2009-09-05 21:47:34 +00009299 if (logging != MagickFalse)
9300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9301 " Writing PNG header chunks");
9302
glennrp5af765f2010-03-30 11:12:18 +00009303 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9304 ping_bit_depth,ping_color_type,
9305 ping_interlace_method,ping_compression_method,
9306 ping_filter_method);
9307
glennrp39992b42010-11-14 00:03:43 +00009308 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9309 {
glennrpf09bded2011-01-08 01:15:59 +00009310 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009311
glennrp3b51f0e2010-11-27 18:14:08 +00009312 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009313 {
glennrp8640fb52010-11-23 15:48:26 +00009314 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009315 {
glennrpd6bf1612010-12-17 17:28:54 +00009316 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009318 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9319 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009320 (int) palette[i].red,
9321 (int) palette[i].green,
9322 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009323 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009324 (int) ping_trans_alpha[i]);
9325 else
9326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009327 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009328 (int) i,
9329 (int) palette[i].red,
9330 (int) palette[i].green,
9331 (int) palette[i].blue);
9332 }
glennrp39992b42010-11-14 00:03:43 +00009333 }
glennrp39992b42010-11-14 00:03:43 +00009334 }
9335
glennrp26f37912010-12-23 16:22:42 +00009336 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009337 {
glennrp26f37912010-12-23 16:22:42 +00009338 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009339 {
glennrp26f37912010-12-23 16:22:42 +00009340 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009341 if (logging)
9342 {
9343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9344 " Setting up bKGD chunk");
9345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9346 " background color = (%d,%d,%d)",
9347 (int) ping_background.red,
9348 (int) ping_background.green,
9349 (int) ping_background.blue);
9350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9351 " index = %d, gray=%d",
9352 (int) ping_background.index,
9353 (int) ping_background.gray);
9354 }
9355 }
glennrp26f37912010-12-23 16:22:42 +00009356 }
9357
9358 if (ping_exclude_pHYs == MagickFalse)
9359 {
9360 if (ping_have_pHYs != MagickFalse)
9361 {
9362 png_set_pHYs(ping,ping_info,
9363 ping_pHYs_x_resolution,
9364 ping_pHYs_y_resolution,
9365 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009366
9367 if (logging)
9368 {
9369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9370 " Setting up pHYs chunk");
9371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9372 " x_resolution=%lu",
9373 (unsigned long) ping_pHYs_x_resolution);
9374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9375 " y_resolution=%lu",
9376 (unsigned long) ping_pHYs_y_resolution);
9377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9378 " unit_type=%lu",
9379 (unsigned long) ping_pHYs_unit_type);
9380 }
glennrp26f37912010-12-23 16:22:42 +00009381 }
glennrpdfd70802010-11-14 01:23:35 +00009382 }
9383
9384#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009385 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009386 {
glennrp26f37912010-12-23 16:22:42 +00009387 if (image->page.x || image->page.y)
9388 {
9389 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9390 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009391
glennrp26f37912010-12-23 16:22:42 +00009392 if (logging != MagickFalse)
9393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9394 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9395 (int) image->page.x, (int) image->page.y);
9396 }
glennrpdfd70802010-11-14 01:23:35 +00009397 }
9398#endif
9399
glennrpda8f3a72011-02-27 23:54:12 +00009400 if (mng_info->need_blob != MagickFalse)
9401 {
9402 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9403 MagickFalse)
9404 png_error(ping,"WriteBlob Failed");
9405
9406 ping_have_blob=MagickTrue;
9407 }
9408
cristy3ed852e2009-09-05 21:47:34 +00009409 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009410
glennrp39992b42010-11-14 00:03:43 +00009411 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009412 {
glennrp3b51f0e2010-11-27 18:14:08 +00009413 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009414 {
9415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9416 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9417 }
9418
9419 if (ping_color_type == 3)
9420 (void) png_set_tRNS(ping, ping_info,
9421 ping_trans_alpha,
9422 ping_num_trans,
9423 NULL);
9424
9425 else
9426 {
9427 (void) png_set_tRNS(ping, ping_info,
9428 NULL,
9429 0,
9430 &ping_trans_color);
9431
glennrp3b51f0e2010-11-27 18:14:08 +00009432 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009433 {
9434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009435 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009436 (int) ping_trans_color.red,
9437 (int) ping_trans_color.green,
9438 (int) ping_trans_color.blue);
9439 }
9440 }
glennrp991d11d2010-11-12 21:55:28 +00009441 }
9442
cristy3ed852e2009-09-05 21:47:34 +00009443 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009444 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009445
cristy3ed852e2009-09-05 21:47:34 +00009446 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009447
cristy3ed852e2009-09-05 21:47:34 +00009448 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009449 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009450
glennrp26f37912010-12-23 16:22:42 +00009451 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009452 {
glennrp4f25bd02011-01-01 18:51:28 +00009453 if ((image->page.width != 0 && image->page.width != image->columns) ||
9454 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009455 {
9456 unsigned char
9457 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009458
glennrp26f37912010-12-23 16:22:42 +00009459 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9460 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009461 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009462 PNGLong(chunk+4,(png_uint_32) image->page.width);
9463 PNGLong(chunk+8,(png_uint_32) image->page.height);
9464 chunk[12]=0; /* unit = pixels */
9465 (void) WriteBlob(image,13,chunk);
9466 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9467 }
cristy3ed852e2009-09-05 21:47:34 +00009468 }
9469
9470#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009471 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009472#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009473 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009474#undef PNG_HAVE_IDAT
9475#endif
9476
9477 png_set_packing(ping);
9478 /*
9479 Allocate memory.
9480 */
9481 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009482 if (image_depth > 8)
9483 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009484 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009485 {
glennrpb4a13412010-05-05 12:47:19 +00009486 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009487 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009488 break;
glennrp0fe50b42010-11-16 03:52:51 +00009489
glennrpb4a13412010-05-05 12:47:19 +00009490 case PNG_COLOR_TYPE_GRAY_ALPHA:
9491 rowbytes*=2;
9492 break;
glennrp0fe50b42010-11-16 03:52:51 +00009493
glennrpb4a13412010-05-05 12:47:19 +00009494 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009495 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009496 break;
glennrp0fe50b42010-11-16 03:52:51 +00009497
glennrpb4a13412010-05-05 12:47:19 +00009498 default:
9499 break;
cristy3ed852e2009-09-05 21:47:34 +00009500 }
glennrp3b51f0e2010-11-27 18:14:08 +00009501
9502 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009503 {
9504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9505 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009506
glennrpb4a13412010-05-05 12:47:19 +00009507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009508 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009509 }
glennrpcf002022011-01-30 02:38:15 +00009510 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9511 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009512
glennrpcf002022011-01-30 02:38:15 +00009513 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009514 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009515
cristy3ed852e2009-09-05 21:47:34 +00009516 /*
9517 Initialize image scanlines.
9518 */
glennrp5af765f2010-03-30 11:12:18 +00009519 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009520 {
9521 /*
9522 PNG write failed.
9523 */
9524#ifdef PNG_DEBUG
9525 if (image_info->verbose)
9526 (void) printf("PNG write has failed.\n");
9527#endif
9528 png_destroy_write_struct(&ping,&ping_info);
9529 if (quantum_info != (QuantumInfo *) NULL)
9530 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009531 if (ping_pixels != (unsigned char *) NULL)
9532 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009533#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009534 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009535#endif
glennrpda8f3a72011-02-27 23:54:12 +00009536 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009537 (void) CloseBlob(image);
9538 image_info=DestroyImageInfo(image_info);
9539 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009540 return(MagickFalse);
9541 }
cristyed552522009-10-16 14:04:35 +00009542 quantum_info=AcquireQuantumInfo(image_info,image);
9543 if (quantum_info == (QuantumInfo *) NULL)
9544 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009545 quantum_info->format=UndefinedQuantumFormat;
9546 quantum_info->depth=image_depth;
9547 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009548
cristy3ed852e2009-09-05 21:47:34 +00009549 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009550 !mng_info->write_png32) &&
9551 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009552 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009553 image_matte == MagickFalse &&
9554 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009555 {
glennrp8bb3a022010-12-13 20:40:04 +00009556 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009557 register const PixelPacket
9558 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009559
cristy3ed852e2009-09-05 21:47:34 +00009560 quantum_info->depth=8;
9561 for (pass=0; pass < num_passes; pass++)
9562 {
9563 /*
9564 Convert PseudoClass image to a PNG monochrome image.
9565 */
cristybb503372010-05-27 20:51:26 +00009566 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009567 {
glennrpd71e86a2011-02-24 01:28:37 +00009568 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9570 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009571
cristy3ed852e2009-09-05 21:47:34 +00009572 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009573
cristy3ed852e2009-09-05 21:47:34 +00009574 if (p == (const PixelPacket *) NULL)
9575 break;
glennrp0fe50b42010-11-16 03:52:51 +00009576
cristy3ed852e2009-09-05 21:47:34 +00009577 if (mng_info->IsPalette)
9578 {
9579 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009580 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009581 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9582 mng_info->write_png_depth &&
9583 mng_info->write_png_depth != old_bit_depth)
9584 {
9585 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009586 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009587 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009588 >> (8-old_bit_depth));
9589 }
9590 }
glennrp0fe50b42010-11-16 03:52:51 +00009591
cristy3ed852e2009-09-05 21:47:34 +00009592 else
9593 {
9594 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009595 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009596 }
glennrp0fe50b42010-11-16 03:52:51 +00009597
cristy3ed852e2009-09-05 21:47:34 +00009598 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009599 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009600 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009601 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009602
glennrp3b51f0e2010-11-27 18:14:08 +00009603 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9605 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009606
glennrpcf002022011-01-30 02:38:15 +00009607 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009608 }
9609 if (image->previous == (Image *) NULL)
9610 {
9611 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9612 if (status == MagickFalse)
9613 break;
9614 }
9615 }
9616 }
glennrp0fe50b42010-11-16 03:52:51 +00009617
glennrp8bb3a022010-12-13 20:40:04 +00009618 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009619 {
glennrp0fe50b42010-11-16 03:52:51 +00009620 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009621 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009622 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009623 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009624 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009625 {
glennrp8bb3a022010-12-13 20:40:04 +00009626 register const PixelPacket
9627 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009628
glennrp8bb3a022010-12-13 20:40:04 +00009629 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009630 {
glennrp8bb3a022010-12-13 20:40:04 +00009631
cristybb503372010-05-27 20:51:26 +00009632 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009633 {
9634 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009635
cristy3ed852e2009-09-05 21:47:34 +00009636 if (p == (const PixelPacket *) NULL)
9637 break;
glennrp2cc891a2010-12-24 13:44:32 +00009638
glennrp5af765f2010-03-30 11:12:18 +00009639 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009640 {
glennrp8bb3a022010-12-13 20:40:04 +00009641 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009642 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009643 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009644
glennrp8bb3a022010-12-13 20:40:04 +00009645 else
9646 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009647 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009648
glennrp3b51f0e2010-11-27 18:14:08 +00009649 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009651 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009652 }
glennrp2cc891a2010-12-24 13:44:32 +00009653
glennrp8bb3a022010-12-13 20:40:04 +00009654 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9655 {
9656 if (logging != MagickFalse && y == 0)
9657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9658 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009659
glennrp8bb3a022010-12-13 20:40:04 +00009660 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009661 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009662 }
glennrp2cc891a2010-12-24 13:44:32 +00009663
glennrp3b51f0e2010-11-27 18:14:08 +00009664 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009666 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009667
glennrpcf002022011-01-30 02:38:15 +00009668 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009669 }
glennrp2cc891a2010-12-24 13:44:32 +00009670
glennrp8bb3a022010-12-13 20:40:04 +00009671 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009672 {
glennrp8bb3a022010-12-13 20:40:04 +00009673 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9674 if (status == MagickFalse)
9675 break;
cristy3ed852e2009-09-05 21:47:34 +00009676 }
cristy3ed852e2009-09-05 21:47:34 +00009677 }
9678 }
glennrp8bb3a022010-12-13 20:40:04 +00009679
9680 else
9681 {
9682 register const PixelPacket
9683 *p;
9684
9685 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009686 {
glennrp8bb3a022010-12-13 20:40:04 +00009687 if ((image_depth > 8) || (mng_info->write_png24 ||
9688 mng_info->write_png32 ||
9689 (!mng_info->write_png8 && !mng_info->IsPalette)))
9690 {
9691 for (y=0; y < (ssize_t) image->rows; y++)
9692 {
9693 p=GetVirtualPixels(image,0,y,image->columns,1,
9694 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009695
glennrp8bb3a022010-12-13 20:40:04 +00009696 if (p == (const PixelPacket *) NULL)
9697 break;
glennrp2cc891a2010-12-24 13:44:32 +00009698
glennrp8bb3a022010-12-13 20:40:04 +00009699 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9700 {
9701 if (image->storage_class == DirectClass)
9702 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009703 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009704
glennrp8bb3a022010-12-13 20:40:04 +00009705 else
9706 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009707 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009708 }
glennrp2cc891a2010-12-24 13:44:32 +00009709
glennrp8bb3a022010-12-13 20:40:04 +00009710 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9711 {
9712 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009713 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009714 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009715
glennrp8bb3a022010-12-13 20:40:04 +00009716 if (logging != MagickFalse && y == 0)
9717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9718 " Writing GRAY_ALPHA PNG pixels (3)");
9719 }
glennrp2cc891a2010-12-24 13:44:32 +00009720
glennrp8bb3a022010-12-13 20:40:04 +00009721 else if (image_matte != MagickFalse)
9722 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009723 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009724
glennrp8bb3a022010-12-13 20:40:04 +00009725 else
9726 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009727 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009728
glennrp8bb3a022010-12-13 20:40:04 +00009729 if (logging != MagickFalse && y == 0)
9730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9731 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009732
glennrpcf002022011-01-30 02:38:15 +00009733 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009734 }
9735 }
glennrp2cc891a2010-12-24 13:44:32 +00009736
glennrp8bb3a022010-12-13 20:40:04 +00009737 else
9738 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9739 mng_info->write_png32 ||
9740 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9741 {
9742 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9743 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9744 {
9745 if (logging != MagickFalse)
9746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9747 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009748
glennrp8bb3a022010-12-13 20:40:04 +00009749 quantum_info->depth=8;
9750 image_depth=8;
9751 }
glennrp2cc891a2010-12-24 13:44:32 +00009752
glennrp8bb3a022010-12-13 20:40:04 +00009753 for (y=0; y < (ssize_t) image->rows; y++)
9754 {
9755 if (logging != MagickFalse && y == 0)
9756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9757 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009758
glennrp770d1932011-03-06 22:11:17 +00009759 p=GetVirtualPixels(image,0,y,image->columns,1,
9760 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009761
glennrp8bb3a022010-12-13 20:40:04 +00009762 if (p == (const PixelPacket *) NULL)
9763 break;
glennrp2cc891a2010-12-24 13:44:32 +00009764
glennrp8bb3a022010-12-13 20:40:04 +00009765 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009766 {
glennrp4bf89732011-03-21 13:48:28 +00009767 quantum_info->depth=image->depth;
9768
glennrp44757ab2011-03-17 12:57:03 +00009769 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009770 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009771 }
glennrp2cc891a2010-12-24 13:44:32 +00009772
glennrp8bb3a022010-12-13 20:40:04 +00009773 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9774 {
9775 if (logging != MagickFalse && y == 0)
9776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9777 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009778
glennrp8bb3a022010-12-13 20:40:04 +00009779 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009780 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009781 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009782 }
glennrp2cc891a2010-12-24 13:44:32 +00009783
glennrp8bb3a022010-12-13 20:40:04 +00009784 else
glennrp8bb3a022010-12-13 20:40:04 +00009785 {
glennrp179d0752011-03-17 13:02:10 +00009786 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009787 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9788
9789 if (logging != MagickFalse && y <= 2)
9790 {
9791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009792 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009793
9794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9795 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9796 (int)ping_pixels[0],(int)ping_pixels[1]);
9797 }
glennrp8bb3a022010-12-13 20:40:04 +00009798 }
glennrpcf002022011-01-30 02:38:15 +00009799 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009800 }
9801 }
glennrp2cc891a2010-12-24 13:44:32 +00009802
glennrp8bb3a022010-12-13 20:40:04 +00009803 if (image->previous == (Image *) NULL)
9804 {
9805 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9806 if (status == MagickFalse)
9807 break;
9808 }
cristy3ed852e2009-09-05 21:47:34 +00009809 }
glennrp8bb3a022010-12-13 20:40:04 +00009810 }
9811 }
9812
cristyb32b90a2009-09-07 21:45:48 +00009813 if (quantum_info != (QuantumInfo *) NULL)
9814 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009815
9816 if (logging != MagickFalse)
9817 {
9818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009819 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009820
cristy3ed852e2009-09-05 21:47:34 +00009821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009822 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009823
cristy3ed852e2009-09-05 21:47:34 +00009824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009825 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009826
cristy3ed852e2009-09-05 21:47:34 +00009827 if (mng_info->write_png_depth)
9828 {
9829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9831 }
glennrp0fe50b42010-11-16 03:52:51 +00009832
cristy3ed852e2009-09-05 21:47:34 +00009833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009834 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009835
cristy3ed852e2009-09-05 21:47:34 +00009836 if (mng_info->write_png_colortype)
9837 {
9838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9839 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9840 }
glennrp0fe50b42010-11-16 03:52:51 +00009841
cristy3ed852e2009-09-05 21:47:34 +00009842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009843 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009844
cristy3ed852e2009-09-05 21:47:34 +00009845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009846 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009847 }
9848 /*
glennrpa0ed0092011-04-18 16:36:29 +00009849 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009850 */
glennrp823b55c2011-03-14 18:46:46 +00009851 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009852 {
glennrp26f37912010-12-23 16:22:42 +00009853 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009854 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009855 while (property != (const char *) NULL)
9856 {
9857 png_textp
9858 text;
glennrp2cc891a2010-12-24 13:44:32 +00009859
glennrp26f37912010-12-23 16:22:42 +00009860 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009861
9862 /* Don't write any "png:" properties; those are just for "identify" */
9863 if (LocaleNCompare(property,"png:",4) != 0 &&
9864
9865 /* Suppress density and units if we wrote a pHYs chunk */
9866 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009867 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009868 LocaleCompare(property,"units") != 0) &&
9869
9870 /* Suppress the IM-generated Date:create and Date:modify */
9871 (ping_exclude_date == MagickFalse ||
9872 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009873 {
glennrpc70af4a2011-03-07 00:08:23 +00009874 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009875 {
glennrpc70af4a2011-03-07 00:08:23 +00009876 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9877 text[0].key=(char *) property;
9878 text[0].text=(char *) value;
9879 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009880
glennrpc70af4a2011-03-07 00:08:23 +00009881 if (ping_exclude_tEXt != MagickFalse)
9882 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9883
9884 else if (ping_exclude_zTXt != MagickFalse)
9885 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9886
9887 else
glennrp26f37912010-12-23 16:22:42 +00009888 {
glennrpc70af4a2011-03-07 00:08:23 +00009889 text[0].compression=image_info->compression == NoCompression ||
9890 (image_info->compression == UndefinedCompression &&
9891 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9892 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009893 }
glennrp2cc891a2010-12-24 13:44:32 +00009894
glennrpc70af4a2011-03-07 00:08:23 +00009895 if (logging != MagickFalse)
9896 {
9897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9898 " Setting up text chunk");
9899
9900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9901 " keyword: %s",text[0].key);
9902 }
9903
9904 png_set_text(ping,ping_info,text,1);
9905 png_free(ping,text);
9906 }
glennrp26f37912010-12-23 16:22:42 +00009907 }
9908 property=GetNextImageProperty(image);
9909 }
cristy3ed852e2009-09-05 21:47:34 +00009910 }
9911
9912 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009913 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009914
9915 if (logging != MagickFalse)
9916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9917 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009918
cristy3ed852e2009-09-05 21:47:34 +00009919 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009920
cristy3ed852e2009-09-05 21:47:34 +00009921 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9922 {
9923 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009924 (ping_width != mng_info->page.width) ||
9925 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009926 {
9927 unsigned char
9928 chunk[32];
9929
9930 /*
9931 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9932 */
9933 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9934 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009935 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009936 chunk[4]=4;
9937 chunk[5]=0; /* frame name separator (no name) */
9938 chunk[6]=1; /* flag for changing delay, for next frame only */
9939 chunk[7]=0; /* flag for changing frame timeout */
9940 chunk[8]=1; /* flag for changing frame clipping for next frame */
9941 chunk[9]=0; /* flag for changing frame sync_id */
9942 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9943 chunk[14]=0; /* clipping boundaries delta type */
9944 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9945 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009946 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009947 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9948 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009949 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009950 (void) WriteBlob(image,31,chunk);
9951 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9952 mng_info->old_framing_mode=4;
9953 mng_info->framing_mode=1;
9954 }
glennrp0fe50b42010-11-16 03:52:51 +00009955
cristy3ed852e2009-09-05 21:47:34 +00009956 else
9957 mng_info->framing_mode=3;
9958 }
9959 if (mng_info->write_mng && !mng_info->need_fram &&
9960 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009961 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009962 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009963 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009964
cristy3ed852e2009-09-05 21:47:34 +00009965 /*
9966 Free PNG resources.
9967 */
glennrp5af765f2010-03-30 11:12:18 +00009968
cristy3ed852e2009-09-05 21:47:34 +00009969 png_destroy_write_struct(&ping,&ping_info);
9970
glennrpcf002022011-01-30 02:38:15 +00009971 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009972
9973#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009974 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009975#endif
9976
glennrpda8f3a72011-02-27 23:54:12 +00009977 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009978 (void) CloseBlob(image);
9979
9980 image_info=DestroyImageInfo(image_info);
9981 image=DestroyImage(image);
9982
9983 /* Store bit depth actually written */
9984 s[0]=(char) ping_bit_depth;
9985 s[1]='\0';
9986
9987 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9988
cristy3ed852e2009-09-05 21:47:34 +00009989 if (logging != MagickFalse)
9990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9991 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009992
cristy3ed852e2009-09-05 21:47:34 +00009993 return(MagickTrue);
9994/* End write one PNG image */
9995}
9996
9997/*
9998%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9999% %
10000% %
10001% %
10002% W r i t e P N G I m a g e %
10003% %
10004% %
10005% %
10006%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10007%
10008% WritePNGImage() writes a Portable Network Graphics (PNG) or
10009% Multiple-image Network Graphics (MNG) image file.
10010%
10011% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10012%
10013% The format of the WritePNGImage method is:
10014%
10015% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10016%
10017% A description of each parameter follows:
10018%
10019% o image_info: the image info.
10020%
10021% o image: The image.
10022%
10023% Returns MagickTrue on success, MagickFalse on failure.
10024%
10025% Communicating with the PNG encoder:
10026%
10027% While the datastream written is always in PNG format and normally would
10028% be given the "png" file extension, this method also writes the following
10029% pseudo-formats which are subsets of PNG:
10030%
glennrp5a39f372011-02-25 04:52:16 +000010031% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10032% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010033% is present, the tRNS chunk must only have values 0 and 255
10034% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010035% transparent). If other values are present they will be
10036% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010037% colors are present, they will be quantized to the 4-4-4-1,
10038% 3-3-3-1, or 3-3-2-1 palette.
10039%
10040% If you want better quantization or dithering of the colors
10041% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010042% PNG encoder. The pixels contain 8-bit indices even if
10043% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010044% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010045% PNG grayscale type might be slightly more efficient. Please
10046% note that writing to the PNG8 format may result in loss
10047% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010048%
10049% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10050% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010051% one of the colors as transparent. The only loss incurred
10052% is reduction of sample depth to 8. If the image has more
10053% than one transparent color, has semitransparent pixels, or
10054% has an opaque pixel with the same RGB components as the
10055% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010056%
10057% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10058% transparency is permitted, i.e., the alpha sample for
10059% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010060% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010061% The only loss in data is the reduction of the sample depth
10062% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010063%
10064% o -define: For more precise control of the PNG output, you can use the
10065% Image options "png:bit-depth" and "png:color-type". These
10066% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010067% from the application programming interfaces. The options
10068% are case-independent and are converted to lowercase before
10069% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010070%
10071% png:color-type can be 0, 2, 3, 4, or 6.
10072%
10073% When png:color-type is 0 (Grayscale), png:bit-depth can
10074% be 1, 2, 4, 8, or 16.
10075%
10076% When png:color-type is 2 (RGB), png:bit-depth can
10077% be 8 or 16.
10078%
10079% When png:color-type is 3 (Indexed), png:bit-depth can
10080% be 1, 2, 4, or 8. This refers to the number of bits
10081% used to store the index. The color samples always have
10082% bit-depth 8 in indexed PNG files.
10083%
10084% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10085% png:bit-depth can be 8 or 16.
10086%
glennrp5a39f372011-02-25 04:52:16 +000010087% If the image cannot be written without loss with the requested bit-depth
10088% and color-type, a PNG file will not be written, and the encoder will
10089% return MagickFalse.
10090%
cristy3ed852e2009-09-05 21:47:34 +000010091% Since image encoders should not be responsible for the "heavy lifting",
10092% the user should make sure that ImageMagick has already reduced the
10093% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010094% transparency prior to attempting to write the image with depth, color,
10095% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010096%
glennrp97cefe22011-04-22 16:17:00 +000010097% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010098%
cristy3ed852e2009-09-05 21:47:34 +000010099% Note that another definition, "png:bit-depth-written" exists, but it
10100% is not intended for external use. It is only used internally by the
10101% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10102%
10103% It is possible to request that the PNG encoder write previously-formatted
10104% ancillary chunks in the output PNG file, using the "-profile" commandline
10105% option as shown below or by setting the profile via a programming
10106% interface:
10107%
10108% -profile PNG-chunk-x:<file>
10109%
10110% where x is a location flag and <file> is a file containing the chunk
10111% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010112% This encoder will compute the chunk length and CRC, so those must not
10113% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010114%
10115% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10116% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10117% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010118% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010119%
glennrpbb8a7332010-11-13 15:17:35 +000010120% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010121%
glennrp3241bd02010-12-12 04:36:28 +000010122% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010123%
glennrpd6afd542010-11-19 01:53:05 +000010124% o 32-bit depth is reduced to 16.
10125% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10126% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010127% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010128% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010129% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010130% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10131% this can be done without loss and a larger bit depth N was not
10132% requested via the "-define PNG:bit-depth=N" option.
10133% o If matte channel is present but only one transparent color is
10134% present, RGB+tRNS is written instead of RGBA
10135% o Opaque matte channel is removed (or added, if color-type 4 or 6
10136% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010137%
cristy3ed852e2009-09-05 21:47:34 +000010138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10139*/
10140static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10141 Image *image)
10142{
10143 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010144 excluding,
10145 logging,
10146 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010147 status;
10148
10149 MngInfo
10150 *mng_info;
10151
10152 const char
10153 *value;
10154
10155 int
glennrp21f0e622011-01-07 16:20:57 +000010156 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010157 source;
10158
cristy3ed852e2009-09-05 21:47:34 +000010159 /*
10160 Open image file.
10161 */
10162 assert(image_info != (const ImageInfo *) NULL);
10163 assert(image_info->signature == MagickSignature);
10164 assert(image != (Image *) NULL);
10165 assert(image->signature == MagickSignature);
10166 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010167 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010168 /*
10169 Allocate a MngInfo structure.
10170 */
10171 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010172 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010173
cristy3ed852e2009-09-05 21:47:34 +000010174 if (mng_info == (MngInfo *) NULL)
10175 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010176
cristy3ed852e2009-09-05 21:47:34 +000010177 /*
10178 Initialize members of the MngInfo structure.
10179 */
10180 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10181 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010182 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010183 have_mng_structure=MagickTrue;
10184
10185 /* See if user has requested a specific PNG subformat */
10186
10187 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10188 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10189 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10190
10191 if (mng_info->write_png8)
10192 {
glennrp9c1eb072010-06-06 22:19:15 +000010193 mng_info->write_png_colortype = /* 3 */ 4;
10194 mng_info->write_png_depth = 8;
10195 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010196 }
10197
10198 if (mng_info->write_png24)
10199 {
glennrp9c1eb072010-06-06 22:19:15 +000010200 mng_info->write_png_colortype = /* 2 */ 3;
10201 mng_info->write_png_depth = 8;
10202 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010203
glennrp9c1eb072010-06-06 22:19:15 +000010204 if (image->matte == MagickTrue)
10205 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010206
glennrp9c1eb072010-06-06 22:19:15 +000010207 else
10208 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010209
glennrp9c1eb072010-06-06 22:19:15 +000010210 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010211 }
10212
10213 if (mng_info->write_png32)
10214 {
glennrp9c1eb072010-06-06 22:19:15 +000010215 mng_info->write_png_colortype = /* 6 */ 7;
10216 mng_info->write_png_depth = 8;
10217 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010218
glennrp9c1eb072010-06-06 22:19:15 +000010219 if (image->matte == MagickTrue)
10220 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010221
glennrp9c1eb072010-06-06 22:19:15 +000010222 else
10223 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010224
glennrp9c1eb072010-06-06 22:19:15 +000010225 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010226 }
10227
10228 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010229
cristy3ed852e2009-09-05 21:47:34 +000010230 if (value != (char *) NULL)
10231 {
10232 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010233 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010234
cristy3ed852e2009-09-05 21:47:34 +000010235 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010236 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010237
cristy3ed852e2009-09-05 21:47:34 +000010238 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010239 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010240
cristy3ed852e2009-09-05 21:47:34 +000010241 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010242 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010243
cristy3ed852e2009-09-05 21:47:34 +000010244 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010245 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010246
glennrpbb8a7332010-11-13 15:17:35 +000010247 else
10248 (void) ThrowMagickException(&image->exception,
10249 GetMagickModule(),CoderWarning,
10250 "ignoring invalid defined png:bit-depth",
10251 "=%s",value);
10252
cristy3ed852e2009-09-05 21:47:34 +000010253 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010255 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010256 }
glennrp0fe50b42010-11-16 03:52:51 +000010257
cristy3ed852e2009-09-05 21:47:34 +000010258 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010259
cristy3ed852e2009-09-05 21:47:34 +000010260 if (value != (char *) NULL)
10261 {
10262 /* We must store colortype+1 because 0 is a valid colortype */
10263 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010264 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010265
cristy3ed852e2009-09-05 21:47:34 +000010266 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010267 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010268
cristy3ed852e2009-09-05 21:47:34 +000010269 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010270 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010271
cristy3ed852e2009-09-05 21:47:34 +000010272 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010273 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010274
cristy3ed852e2009-09-05 21:47:34 +000010275 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010276 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010277
glennrpbb8a7332010-11-13 15:17:35 +000010278 else
10279 (void) ThrowMagickException(&image->exception,
10280 GetMagickModule(),CoderWarning,
10281 "ignoring invalid defined png:color-type",
10282 "=%s",value);
10283
cristy3ed852e2009-09-05 21:47:34 +000010284 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010286 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010287 }
10288
glennrp0e8ea192010-12-24 18:00:33 +000010289 /* Check for chunks to be excluded:
10290 *
glennrp0dff56c2011-01-29 19:10:02 +000010291 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010292 * listed in the "unused_chunks" array, above.
10293 *
10294 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10295 * define (in the image properties or in the image artifacts)
10296 * or via a mng_info member. For convenience, in addition
10297 * to or instead of a comma-separated list of chunks, the
10298 * "exclude-chunk" string can be simply "all" or "none".
10299 *
10300 * The exclude-chunk define takes priority over the mng_info.
10301 *
10302 * A "PNG:include-chunk" define takes priority over both the
10303 * mng_info and the "PNG:exclude-chunk" define. Like the
10304 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010305 * well as a comma-separated list. Chunks that are unknown to
10306 * ImageMagick are always excluded, regardless of their "copy-safe"
10307 * status according to the PNG specification, and even if they
10308 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010309 *
10310 * Finally, all chunks listed in the "unused_chunks" array are
10311 * automatically excluded, regardless of the other instructions
10312 * or lack thereof.
10313 *
10314 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10315 * will not be written and the gAMA chunk will only be written if it
10316 * is not between .45 and .46, or approximately (1.0/2.2).
10317 *
10318 * If you exclude tRNS and the image has transparency, the colortype
10319 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10320 *
10321 * The -strip option causes StripImage() to set the png:include-chunk
10322 * artifact to "none,gama".
10323 */
10324
glennrp26f37912010-12-23 16:22:42 +000010325 mng_info->ping_exclude_bKGD=MagickFalse;
10326 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010327 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010328 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10329 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010330 mng_info->ping_exclude_iCCP=MagickFalse;
10331 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10332 mng_info->ping_exclude_oFFs=MagickFalse;
10333 mng_info->ping_exclude_pHYs=MagickFalse;
10334 mng_info->ping_exclude_sRGB=MagickFalse;
10335 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010336 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010337 mng_info->ping_exclude_vpAg=MagickFalse;
10338 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10339 mng_info->ping_exclude_zTXt=MagickFalse;
10340
glennrp8d3d6e52011-04-19 04:39:51 +000010341 mng_info->ping_preserve_colormap=MagickFalse;
10342
10343 value=GetImageArtifact(image,"png:preserve-colormap");
10344 if (value == NULL)
10345 value=GetImageOption(image_info,"png:preserve-colormap");
10346 if (value != NULL)
10347 mng_info->ping_preserve_colormap=MagickTrue;
10348
glennrp03812ae2010-12-24 01:31:34 +000010349 excluding=MagickFalse;
10350
glennrp5c7cf4e2010-12-24 00:30:00 +000010351 for (source=0; source<1; source++)
10352 {
10353 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010354 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010355 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010356
10357 if (value == NULL)
10358 value=GetImageArtifact(image,"png:exclude-chunks");
10359 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010360 else
glennrpacba0042010-12-24 14:27:26 +000010361 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010362 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010363
glennrpacba0042010-12-24 14:27:26 +000010364 if (value == NULL)
10365 value=GetImageOption(image_info,"png:exclude-chunks");
10366 }
10367
glennrp03812ae2010-12-24 01:31:34 +000010368 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010369 {
glennrp03812ae2010-12-24 01:31:34 +000010370
10371 size_t
10372 last;
10373
10374 excluding=MagickTrue;
10375
10376 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010377 {
10378 if (source == 0)
10379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10380 " png:exclude-chunk=%s found in image artifacts.\n", value);
10381 else
10382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10383 " png:exclude-chunk=%s found in image properties.\n", value);
10384 }
glennrp03812ae2010-12-24 01:31:34 +000010385
10386 last=strlen(value);
10387
10388 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010389 {
glennrp03812ae2010-12-24 01:31:34 +000010390
10391 if (LocaleNCompare(value+i,"all",3) == 0)
10392 {
10393 mng_info->ping_exclude_bKGD=MagickTrue;
10394 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010395 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010396 mng_info->ping_exclude_EXIF=MagickTrue;
10397 mng_info->ping_exclude_gAMA=MagickTrue;
10398 mng_info->ping_exclude_iCCP=MagickTrue;
10399 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10400 mng_info->ping_exclude_oFFs=MagickTrue;
10401 mng_info->ping_exclude_pHYs=MagickTrue;
10402 mng_info->ping_exclude_sRGB=MagickTrue;
10403 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010404 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010405 mng_info->ping_exclude_vpAg=MagickTrue;
10406 mng_info->ping_exclude_zCCP=MagickTrue;
10407 mng_info->ping_exclude_zTXt=MagickTrue;
10408 i--;
10409 }
glennrp2cc891a2010-12-24 13:44:32 +000010410
glennrp03812ae2010-12-24 01:31:34 +000010411 if (LocaleNCompare(value+i,"none",4) == 0)
10412 {
10413 mng_info->ping_exclude_bKGD=MagickFalse;
10414 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010415 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010416 mng_info->ping_exclude_EXIF=MagickFalse;
10417 mng_info->ping_exclude_gAMA=MagickFalse;
10418 mng_info->ping_exclude_iCCP=MagickFalse;
10419 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10420 mng_info->ping_exclude_oFFs=MagickFalse;
10421 mng_info->ping_exclude_pHYs=MagickFalse;
10422 mng_info->ping_exclude_sRGB=MagickFalse;
10423 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010424 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010425 mng_info->ping_exclude_vpAg=MagickFalse;
10426 mng_info->ping_exclude_zCCP=MagickFalse;
10427 mng_info->ping_exclude_zTXt=MagickFalse;
10428 }
glennrp2cc891a2010-12-24 13:44:32 +000010429
glennrp03812ae2010-12-24 01:31:34 +000010430 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10431 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010432
glennrp03812ae2010-12-24 01:31:34 +000010433 if (LocaleNCompare(value+i,"chrm",4) == 0)
10434 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010435
glennrpa0ed0092011-04-18 16:36:29 +000010436 if (LocaleNCompare(value+i,"date",4) == 0)
10437 mng_info->ping_exclude_date=MagickTrue;
10438
glennrp03812ae2010-12-24 01:31:34 +000010439 if (LocaleNCompare(value+i,"exif",4) == 0)
10440 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010441
glennrp03812ae2010-12-24 01:31:34 +000010442 if (LocaleNCompare(value+i,"gama",4) == 0)
10443 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010444
glennrp03812ae2010-12-24 01:31:34 +000010445 if (LocaleNCompare(value+i,"iccp",4) == 0)
10446 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010447
glennrp03812ae2010-12-24 01:31:34 +000010448 /*
10449 if (LocaleNCompare(value+i,"itxt",4) == 0)
10450 mng_info->ping_exclude_iTXt=MagickTrue;
10451 */
glennrp2cc891a2010-12-24 13:44:32 +000010452
glennrp03812ae2010-12-24 01:31:34 +000010453 if (LocaleNCompare(value+i,"gama",4) == 0)
10454 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010455
glennrp03812ae2010-12-24 01:31:34 +000010456 if (LocaleNCompare(value+i,"offs",4) == 0)
10457 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010458
glennrp03812ae2010-12-24 01:31:34 +000010459 if (LocaleNCompare(value+i,"phys",4) == 0)
10460 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010461
glennrpa1e3b7b2010-12-24 16:37:33 +000010462 if (LocaleNCompare(value+i,"srgb",4) == 0)
10463 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010464
glennrp03812ae2010-12-24 01:31:34 +000010465 if (LocaleNCompare(value+i,"text",4) == 0)
10466 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010467
glennrpa1e3b7b2010-12-24 16:37:33 +000010468 if (LocaleNCompare(value+i,"trns",4) == 0)
10469 mng_info->ping_exclude_tRNS=MagickTrue;
10470
glennrp03812ae2010-12-24 01:31:34 +000010471 if (LocaleNCompare(value+i,"vpag",4) == 0)
10472 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010473
glennrp03812ae2010-12-24 01:31:34 +000010474 if (LocaleNCompare(value+i,"zccp",4) == 0)
10475 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010476
glennrp03812ae2010-12-24 01:31:34 +000010477 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10478 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010479
glennrp03812ae2010-12-24 01:31:34 +000010480 }
glennrpce91ed52010-12-23 22:37:49 +000010481 }
glennrp26f37912010-12-23 16:22:42 +000010482 }
10483
glennrp5c7cf4e2010-12-24 00:30:00 +000010484 for (source=0; source<1; source++)
10485 {
10486 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010487 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010488 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010489
10490 if (value == NULL)
10491 value=GetImageArtifact(image,"png:include-chunks");
10492 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010493 else
glennrpacba0042010-12-24 14:27:26 +000010494 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010495 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010496
glennrpacba0042010-12-24 14:27:26 +000010497 if (value == NULL)
10498 value=GetImageOption(image_info,"png:include-chunks");
10499 }
10500
glennrp03812ae2010-12-24 01:31:34 +000010501 if (value != NULL)
10502 {
10503 size_t
10504 last;
glennrp26f37912010-12-23 16:22:42 +000010505
glennrp03812ae2010-12-24 01:31:34 +000010506 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010507
glennrp03812ae2010-12-24 01:31:34 +000010508 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010509 {
10510 if (source == 0)
10511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10512 " png:include-chunk=%s found in image artifacts.\n", value);
10513 else
10514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10515 " png:include-chunk=%s found in image properties.\n", value);
10516 }
glennrp03812ae2010-12-24 01:31:34 +000010517
10518 last=strlen(value);
10519
10520 for (i=0; i<(int) last; i+=5)
10521 {
10522 if (LocaleNCompare(value+i,"all",3) == 0)
10523 {
10524 mng_info->ping_exclude_bKGD=MagickFalse;
10525 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010526 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010527 mng_info->ping_exclude_EXIF=MagickFalse;
10528 mng_info->ping_exclude_gAMA=MagickFalse;
10529 mng_info->ping_exclude_iCCP=MagickFalse;
10530 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10531 mng_info->ping_exclude_oFFs=MagickFalse;
10532 mng_info->ping_exclude_pHYs=MagickFalse;
10533 mng_info->ping_exclude_sRGB=MagickFalse;
10534 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010535 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010536 mng_info->ping_exclude_vpAg=MagickFalse;
10537 mng_info->ping_exclude_zCCP=MagickFalse;
10538 mng_info->ping_exclude_zTXt=MagickFalse;
10539 i--;
10540 }
glennrp2cc891a2010-12-24 13:44:32 +000010541
glennrp03812ae2010-12-24 01:31:34 +000010542 if (LocaleNCompare(value+i,"none",4) == 0)
10543 {
10544 mng_info->ping_exclude_bKGD=MagickTrue;
10545 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010546 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010547 mng_info->ping_exclude_EXIF=MagickTrue;
10548 mng_info->ping_exclude_gAMA=MagickTrue;
10549 mng_info->ping_exclude_iCCP=MagickTrue;
10550 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10551 mng_info->ping_exclude_oFFs=MagickTrue;
10552 mng_info->ping_exclude_pHYs=MagickTrue;
10553 mng_info->ping_exclude_sRGB=MagickTrue;
10554 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010555 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010556 mng_info->ping_exclude_vpAg=MagickTrue;
10557 mng_info->ping_exclude_zCCP=MagickTrue;
10558 mng_info->ping_exclude_zTXt=MagickTrue;
10559 }
glennrp2cc891a2010-12-24 13:44:32 +000010560
glennrp03812ae2010-12-24 01:31:34 +000010561 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10562 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010563
glennrp03812ae2010-12-24 01:31:34 +000010564 if (LocaleNCompare(value+i,"chrm",4) == 0)
10565 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010566
glennrpa0ed0092011-04-18 16:36:29 +000010567 if (LocaleNCompare(value+i,"date",4) == 0)
10568 mng_info->ping_exclude_date=MagickFalse;
10569
glennrp03812ae2010-12-24 01:31:34 +000010570 if (LocaleNCompare(value+i,"exif",4) == 0)
10571 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010572
glennrp03812ae2010-12-24 01:31:34 +000010573 if (LocaleNCompare(value+i,"gama",4) == 0)
10574 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010575
glennrp03812ae2010-12-24 01:31:34 +000010576 if (LocaleNCompare(value+i,"iccp",4) == 0)
10577 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010578
glennrp03812ae2010-12-24 01:31:34 +000010579 /*
10580 if (LocaleNCompare(value+i,"itxt",4) == 0)
10581 mng_info->ping_exclude_iTXt=MagickFalse;
10582 */
glennrp2cc891a2010-12-24 13:44:32 +000010583
glennrp03812ae2010-12-24 01:31:34 +000010584 if (LocaleNCompare(value+i,"gama",4) == 0)
10585 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010586
glennrp03812ae2010-12-24 01:31:34 +000010587 if (LocaleNCompare(value+i,"offs",4) == 0)
10588 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010589
glennrp03812ae2010-12-24 01:31:34 +000010590 if (LocaleNCompare(value+i,"phys",4) == 0)
10591 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010592
glennrpa1e3b7b2010-12-24 16:37:33 +000010593 if (LocaleNCompare(value+i,"srgb",4) == 0)
10594 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010595
glennrp03812ae2010-12-24 01:31:34 +000010596 if (LocaleNCompare(value+i,"text",4) == 0)
10597 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010598
glennrpa1e3b7b2010-12-24 16:37:33 +000010599 if (LocaleNCompare(value+i,"trns",4) == 0)
10600 mng_info->ping_exclude_tRNS=MagickFalse;
10601
glennrp03812ae2010-12-24 01:31:34 +000010602 if (LocaleNCompare(value+i,"vpag",4) == 0)
10603 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010604
glennrp03812ae2010-12-24 01:31:34 +000010605 if (LocaleNCompare(value+i,"zccp",4) == 0)
10606 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010607
glennrp03812ae2010-12-24 01:31:34 +000010608 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10609 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010610
glennrp03812ae2010-12-24 01:31:34 +000010611 }
glennrpce91ed52010-12-23 22:37:49 +000010612 }
glennrp26f37912010-12-23 16:22:42 +000010613 }
10614
glennrp03812ae2010-12-24 01:31:34 +000010615 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010616 {
10617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10618 " Chunks to be excluded from the output PNG:");
10619 if (mng_info->ping_exclude_bKGD != MagickFalse)
10620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10621 " bKGD");
10622 if (mng_info->ping_exclude_cHRM != MagickFalse)
10623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10624 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010625 if (mng_info->ping_exclude_date != MagickFalse)
10626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10627 " date");
glennrp26f37912010-12-23 16:22:42 +000010628 if (mng_info->ping_exclude_EXIF != MagickFalse)
10629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10630 " EXIF");
10631 if (mng_info->ping_exclude_gAMA != MagickFalse)
10632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633 " gAMA");
10634 if (mng_info->ping_exclude_iCCP != MagickFalse)
10635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 " iCCP");
10637/*
10638 if (mng_info->ping_exclude_iTXt != MagickFalse)
10639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 " iTXt");
10641*/
10642 if (mng_info->ping_exclude_oFFs != MagickFalse)
10643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10644 " oFFs");
10645 if (mng_info->ping_exclude_pHYs != MagickFalse)
10646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10647 " pHYs");
10648 if (mng_info->ping_exclude_sRGB != MagickFalse)
10649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10650 " sRGB");
10651 if (mng_info->ping_exclude_tEXt != MagickFalse)
10652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10653 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010654 if (mng_info->ping_exclude_tRNS != MagickFalse)
10655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10656 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010657 if (mng_info->ping_exclude_vpAg != MagickFalse)
10658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10659 " vpAg");
10660 if (mng_info->ping_exclude_zCCP != MagickFalse)
10661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10662 " zCCP");
10663 if (mng_info->ping_exclude_zTXt != MagickFalse)
10664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10665 " zTXt");
10666 }
10667
glennrpb9cfe272010-12-21 15:08:06 +000010668 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010669
glennrpb9cfe272010-12-21 15:08:06 +000010670 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010671
10672 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010673
cristy3ed852e2009-09-05 21:47:34 +000010674 if (logging != MagickFalse)
10675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010676
cristy3ed852e2009-09-05 21:47:34 +000010677 return(status);
10678}
10679
10680#if defined(JNG_SUPPORTED)
10681
10682/* Write one JNG image */
10683static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10684 const ImageInfo *image_info,Image *image)
10685{
10686 Image
10687 *jpeg_image;
10688
10689 ImageInfo
10690 *jpeg_image_info;
10691
10692 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010693 logging,
cristy3ed852e2009-09-05 21:47:34 +000010694 status;
10695
10696 size_t
10697 length;
10698
10699 unsigned char
10700 *blob,
10701 chunk[80],
10702 *p;
10703
10704 unsigned int
10705 jng_alpha_compression_method,
10706 jng_alpha_sample_depth,
10707 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010708 transparent;
10709
cristybb503372010-05-27 20:51:26 +000010710 size_t
cristy3ed852e2009-09-05 21:47:34 +000010711 jng_quality;
10712
10713 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010714 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010715
10716 blob=(unsigned char *) NULL;
10717 jpeg_image=(Image *) NULL;
10718 jpeg_image_info=(ImageInfo *) NULL;
10719
10720 status=MagickTrue;
10721 transparent=image_info->type==GrayscaleMatteType ||
10722 image_info->type==TrueColorMatteType;
10723 jng_color_type=10;
10724 jng_alpha_sample_depth=0;
10725 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10726 jng_alpha_compression_method=0;
10727
10728 if (image->matte != MagickFalse)
10729 {
10730 /* if any pixels are transparent */
10731 transparent=MagickTrue;
10732 if (image_info->compression==JPEGCompression)
10733 jng_alpha_compression_method=8;
10734 }
10735
10736 if (transparent)
10737 {
10738 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010739
cristy3ed852e2009-09-05 21:47:34 +000010740 /* Create JPEG blob, image, and image_info */
10741 if (logging != MagickFalse)
10742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10743 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010744
cristy3ed852e2009-09-05 21:47:34 +000010745 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010746
cristy3ed852e2009-09-05 21:47:34 +000010747 if (jpeg_image_info == (ImageInfo *) NULL)
10748 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010749
cristy3ed852e2009-09-05 21:47:34 +000010750 if (logging != MagickFalse)
10751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10752 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010753
cristy3ed852e2009-09-05 21:47:34 +000010754 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010755
cristy3ed852e2009-09-05 21:47:34 +000010756 if (jpeg_image == (Image *) NULL)
10757 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010758
cristy3ed852e2009-09-05 21:47:34 +000010759 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10760 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10761 status=NegateImage(jpeg_image,MagickFalse);
10762 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010763
cristy3ed852e2009-09-05 21:47:34 +000010764 if (jng_quality >= 1000)
10765 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010766
cristy3ed852e2009-09-05 21:47:34 +000010767 else
10768 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010769
cristy3ed852e2009-09-05 21:47:34 +000010770 jpeg_image_info->type=GrayscaleType;
10771 (void) SetImageType(jpeg_image,GrayscaleType);
10772 (void) AcquireUniqueFilename(jpeg_image->filename);
10773 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10774 "%s",jpeg_image->filename);
10775 }
10776
10777 /* To do: check bit depth of PNG alpha channel */
10778
10779 /* Check if image is grayscale. */
10780 if (image_info->type != TrueColorMatteType && image_info->type !=
10781 TrueColorType && ImageIsGray(image))
10782 jng_color_type-=2;
10783
10784 if (transparent)
10785 {
10786 if (jng_alpha_compression_method==0)
10787 {
10788 const char
10789 *value;
10790
10791 /* Encode opacity as a grayscale PNG blob */
10792 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10793 &image->exception);
10794 if (logging != MagickFalse)
10795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10796 " Creating PNG blob.");
10797 length=0;
10798
10799 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10800 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10801 jpeg_image_info->interlace=NoInterlace;
10802
10803 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10804 &image->exception);
10805
10806 /* Retrieve sample depth used */
10807 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10808 if (value != (char *) NULL)
10809 jng_alpha_sample_depth= (unsigned int) value[0];
10810 }
10811 else
10812 {
10813 /* Encode opacity as a grayscale JPEG blob */
10814
10815 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10816 &image->exception);
10817
10818 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10819 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10820 jpeg_image_info->interlace=NoInterlace;
10821 if (logging != MagickFalse)
10822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10823 " Creating blob.");
10824 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010825 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010826 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010827
cristy3ed852e2009-09-05 21:47:34 +000010828 if (logging != MagickFalse)
10829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010830 " Successfully read jpeg_image into a blob, length=%.20g.",
10831 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010832
10833 }
10834 /* Destroy JPEG image and image_info */
10835 jpeg_image=DestroyImage(jpeg_image);
10836 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10837 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10838 }
10839
10840 /* Write JHDR chunk */
10841 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10842 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010843 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010844 PNGLong(chunk+4,(png_uint_32) image->columns);
10845 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010846 chunk[12]=jng_color_type;
10847 chunk[13]=8; /* sample depth */
10848 chunk[14]=8; /*jng_image_compression_method */
10849 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10850 chunk[16]=jng_alpha_sample_depth;
10851 chunk[17]=jng_alpha_compression_method;
10852 chunk[18]=0; /*jng_alpha_filter_method */
10853 chunk[19]=0; /*jng_alpha_interlace_method */
10854 (void) WriteBlob(image,20,chunk);
10855 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10856 if (logging != MagickFalse)
10857 {
10858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010859 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010860
cristy3ed852e2009-09-05 21:47:34 +000010861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010862 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010863
cristy3ed852e2009-09-05 21:47:34 +000010864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10865 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010866
cristy3ed852e2009-09-05 21:47:34 +000010867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10868 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010869
cristy3ed852e2009-09-05 21:47:34 +000010870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10871 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010872
cristy3ed852e2009-09-05 21:47:34 +000010873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10874 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010875
cristy3ed852e2009-09-05 21:47:34 +000010876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10877 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010878
cristy3ed852e2009-09-05 21:47:34 +000010879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10880 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010881
cristy3ed852e2009-09-05 21:47:34 +000010882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10883 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010884
cristy3ed852e2009-09-05 21:47:34 +000010885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10886 " JNG alpha interlace:%5d",0);
10887 }
10888
glennrp0fe50b42010-11-16 03:52:51 +000010889 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010890 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010891
10892 /*
10893 Write leading ancillary chunks
10894 */
10895
10896 if (transparent)
10897 {
10898 /*
10899 Write JNG bKGD chunk
10900 */
10901
10902 unsigned char
10903 blue,
10904 green,
10905 red;
10906
cristybb503372010-05-27 20:51:26 +000010907 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010908 num_bytes;
10909
10910 if (jng_color_type == 8 || jng_color_type == 12)
10911 num_bytes=6L;
10912 else
10913 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010914 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010915 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010916 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010917 red=ScaleQuantumToChar(image->background_color.red);
10918 green=ScaleQuantumToChar(image->background_color.green);
10919 blue=ScaleQuantumToChar(image->background_color.blue);
10920 *(chunk+4)=0;
10921 *(chunk+5)=red;
10922 *(chunk+6)=0;
10923 *(chunk+7)=green;
10924 *(chunk+8)=0;
10925 *(chunk+9)=blue;
10926 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10927 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10928 }
10929
10930 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10931 {
10932 /*
10933 Write JNG sRGB chunk
10934 */
10935 (void) WriteBlobMSBULong(image,1L);
10936 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010937 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010938
cristy3ed852e2009-09-05 21:47:34 +000010939 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010940 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010941 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010942 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010943
cristy3ed852e2009-09-05 21:47:34 +000010944 else
glennrpe610a072010-08-05 17:08:46 +000010945 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010946 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010947 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010948
cristy3ed852e2009-09-05 21:47:34 +000010949 (void) WriteBlob(image,5,chunk);
10950 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10951 }
10952 else
10953 {
10954 if (image->gamma != 0.0)
10955 {
10956 /*
10957 Write JNG gAMA chunk
10958 */
10959 (void) WriteBlobMSBULong(image,4L);
10960 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010961 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010962 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010963 (void) WriteBlob(image,8,chunk);
10964 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10965 }
glennrp0fe50b42010-11-16 03:52:51 +000010966
cristy3ed852e2009-09-05 21:47:34 +000010967 if ((mng_info->equal_chrms == MagickFalse) &&
10968 (image->chromaticity.red_primary.x != 0.0))
10969 {
10970 PrimaryInfo
10971 primary;
10972
10973 /*
10974 Write JNG cHRM chunk
10975 */
10976 (void) WriteBlobMSBULong(image,32L);
10977 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010978 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010979 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010980 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10981 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010982 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010983 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10984 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010985 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010986 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10987 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010988 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010989 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10990 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010991 (void) WriteBlob(image,36,chunk);
10992 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10993 }
10994 }
glennrp0fe50b42010-11-16 03:52:51 +000010995
cristy3ed852e2009-09-05 21:47:34 +000010996 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10997 {
10998 /*
10999 Write JNG pHYs chunk
11000 */
11001 (void) WriteBlobMSBULong(image,9L);
11002 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011003 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011004 if (image->units == PixelsPerInchResolution)
11005 {
cristy35ef8242010-06-03 16:24:13 +000011006 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011007 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011008
cristy35ef8242010-06-03 16:24:13 +000011009 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011010 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011011
cristy3ed852e2009-09-05 21:47:34 +000011012 chunk[12]=1;
11013 }
glennrp0fe50b42010-11-16 03:52:51 +000011014
cristy3ed852e2009-09-05 21:47:34 +000011015 else
11016 {
11017 if (image->units == PixelsPerCentimeterResolution)
11018 {
cristy35ef8242010-06-03 16:24:13 +000011019 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011020 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011021
cristy35ef8242010-06-03 16:24:13 +000011022 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011023 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011024
cristy3ed852e2009-09-05 21:47:34 +000011025 chunk[12]=1;
11026 }
glennrp0fe50b42010-11-16 03:52:51 +000011027
cristy3ed852e2009-09-05 21:47:34 +000011028 else
11029 {
cristy35ef8242010-06-03 16:24:13 +000011030 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11031 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011032 chunk[12]=0;
11033 }
11034 }
11035 (void) WriteBlob(image,13,chunk);
11036 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11037 }
11038
11039 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11040 {
11041 /*
11042 Write JNG oFFs chunk
11043 */
11044 (void) WriteBlobMSBULong(image,9L);
11045 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011046 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011047 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11048 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011049 chunk[12]=0;
11050 (void) WriteBlob(image,13,chunk);
11051 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11052 }
11053 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11054 {
11055 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11056 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011057 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011058 PNGLong(chunk+4,(png_uint_32) image->page.width);
11059 PNGLong(chunk+8,(png_uint_32) image->page.height);
11060 chunk[12]=0; /* unit = pixels */
11061 (void) WriteBlob(image,13,chunk);
11062 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11063 }
11064
11065
11066 if (transparent)
11067 {
11068 if (jng_alpha_compression_method==0)
11069 {
cristybb503372010-05-27 20:51:26 +000011070 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011071 i;
11072
cristybb503372010-05-27 20:51:26 +000011073 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011074 len;
11075
11076 /* Write IDAT chunk header */
11077 if (logging != MagickFalse)
11078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011079 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011080 length);
cristy3ed852e2009-09-05 21:47:34 +000011081
11082 /* Copy IDAT chunks */
11083 len=0;
11084 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011085 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011086 {
11087 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11088 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011089
cristy3ed852e2009-09-05 21:47:34 +000011090 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11091 {
11092 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011093 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011094 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011095 (void) WriteBlob(image,(size_t) len+4,p);
11096 (void) WriteBlobMSBULong(image,
11097 crc32(0,p,(uInt) len+4));
11098 }
glennrp0fe50b42010-11-16 03:52:51 +000011099
cristy3ed852e2009-09-05 21:47:34 +000011100 else
11101 {
11102 if (logging != MagickFalse)
11103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011104 " Skipping %c%c%c%c chunk, length=%.20g.",
11105 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011106 }
11107 p+=(8+len);
11108 }
11109 }
11110 else
11111 {
11112 /* Write JDAA chunk header */
11113 if (logging != MagickFalse)
11114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011115 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011116 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011117 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011118 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011119 /* Write JDAT chunk(s) data */
11120 (void) WriteBlob(image,4,chunk);
11121 (void) WriteBlob(image,length,blob);
11122 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11123 (uInt) length));
11124 }
11125 blob=(unsigned char *) RelinquishMagickMemory(blob);
11126 }
11127
11128 /* Encode image as a JPEG blob */
11129 if (logging != MagickFalse)
11130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11131 " Creating jpeg_image_info.");
11132 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11133 if (jpeg_image_info == (ImageInfo *) NULL)
11134 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11135
11136 if (logging != MagickFalse)
11137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11138 " Creating jpeg_image.");
11139
11140 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11141 if (jpeg_image == (Image *) NULL)
11142 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11143 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11144
11145 (void) AcquireUniqueFilename(jpeg_image->filename);
11146 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11147 jpeg_image->filename);
11148
11149 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11150 &image->exception);
11151
11152 if (logging != MagickFalse)
11153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011154 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11155 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011156
11157 if (jng_color_type == 8 || jng_color_type == 12)
11158 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011159
cristy3ed852e2009-09-05 21:47:34 +000011160 jpeg_image_info->quality=jng_quality % 1000;
11161 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11162 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011163
cristy3ed852e2009-09-05 21:47:34 +000011164 if (logging != MagickFalse)
11165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11166 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011167
cristy3ed852e2009-09-05 21:47:34 +000011168 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011169
cristy3ed852e2009-09-05 21:47:34 +000011170 if (logging != MagickFalse)
11171 {
11172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011173 " Successfully read jpeg_image into a blob, length=%.20g.",
11174 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011175
11176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011177 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011178 }
glennrp0fe50b42010-11-16 03:52:51 +000011179
cristy3ed852e2009-09-05 21:47:34 +000011180 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011181 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011182 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011183 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011184 (void) WriteBlob(image,4,chunk);
11185 (void) WriteBlob(image,length,blob);
11186 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11187
11188 jpeg_image=DestroyImage(jpeg_image);
11189 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11190 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11191 blob=(unsigned char *) RelinquishMagickMemory(blob);
11192
11193 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011194 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011195
11196 /* Write IEND chunk */
11197 (void) WriteBlobMSBULong(image,0L);
11198 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011199 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011200 (void) WriteBlob(image,4,chunk);
11201 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11202
11203 if (logging != MagickFalse)
11204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11205 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011206
cristy3ed852e2009-09-05 21:47:34 +000011207 return(status);
11208}
11209
11210
11211/*
11212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11213% %
11214% %
11215% %
11216% W r i t e J N G I m a g e %
11217% %
11218% %
11219% %
11220%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11221%
11222% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11223%
11224% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11225%
11226% The format of the WriteJNGImage method is:
11227%
11228% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11229%
11230% A description of each parameter follows:
11231%
11232% o image_info: the image info.
11233%
11234% o image: The image.
11235%
11236%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11237*/
11238static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11239{
11240 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011241 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011242 logging,
cristy3ed852e2009-09-05 21:47:34 +000011243 status;
11244
11245 MngInfo
11246 *mng_info;
11247
cristy3ed852e2009-09-05 21:47:34 +000011248 /*
11249 Open image file.
11250 */
11251 assert(image_info != (const ImageInfo *) NULL);
11252 assert(image_info->signature == MagickSignature);
11253 assert(image != (Image *) NULL);
11254 assert(image->signature == MagickSignature);
11255 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011256 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011257 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11258 if (status == MagickFalse)
11259 return(status);
11260
11261 /*
11262 Allocate a MngInfo structure.
11263 */
11264 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011265 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011266 if (mng_info == (MngInfo *) NULL)
11267 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11268 /*
11269 Initialize members of the MngInfo structure.
11270 */
11271 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11272 mng_info->image=image;
11273 have_mng_structure=MagickTrue;
11274
11275 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11276
11277 status=WriteOneJNGImage(mng_info,image_info,image);
11278 (void) CloseBlob(image);
11279
11280 (void) CatchImageException(image);
11281 MngInfoFreeStruct(mng_info,&have_mng_structure);
11282 if (logging != MagickFalse)
11283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11284 return(status);
11285}
11286#endif
11287
11288
11289
11290static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11291{
11292 const char
11293 *option;
11294
11295 Image
11296 *next_image;
11297
11298 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011299 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011300 status;
11301
glennrp03812ae2010-12-24 01:31:34 +000011302 volatile MagickBooleanType
11303 logging;
11304
cristy3ed852e2009-09-05 21:47:34 +000011305 MngInfo
11306 *mng_info;
11307
11308 int
cristy3ed852e2009-09-05 21:47:34 +000011309 image_count,
11310 need_iterations,
11311 need_matte;
11312
11313 volatile int
11314#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11315 defined(PNG_MNG_FEATURES_SUPPORTED)
11316 need_local_plte,
11317#endif
11318 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011319 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011320 use_global_plte;
11321
cristybb503372010-05-27 20:51:26 +000011322 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011323 i;
11324
11325 unsigned char
11326 chunk[800];
11327
11328 volatile unsigned int
11329 write_jng,
11330 write_mng;
11331
cristybb503372010-05-27 20:51:26 +000011332 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011333 scene;
11334
cristybb503372010-05-27 20:51:26 +000011335 size_t
cristy3ed852e2009-09-05 21:47:34 +000011336 final_delay=0,
11337 initial_delay;
11338
glennrpd5045b42010-03-24 12:40:35 +000011339#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011340 if (image_info->verbose)
11341 printf("Your PNG library (libpng-%s) is rather old.\n",
11342 PNG_LIBPNG_VER_STRING);
11343#endif
11344
11345 /*
11346 Open image file.
11347 */
11348 assert(image_info != (const ImageInfo *) NULL);
11349 assert(image_info->signature == MagickSignature);
11350 assert(image != (Image *) NULL);
11351 assert(image->signature == MagickSignature);
11352 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011353 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011354 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11355 if (status == MagickFalse)
11356 return(status);
11357
11358 /*
11359 Allocate a MngInfo structure.
11360 */
11361 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011362 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011363 if (mng_info == (MngInfo *) NULL)
11364 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11365 /*
11366 Initialize members of the MngInfo structure.
11367 */
11368 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11369 mng_info->image=image;
11370 have_mng_structure=MagickTrue;
11371 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11372
11373 /*
11374 * See if user has requested a specific PNG subformat to be used
11375 * for all of the PNGs in the MNG being written, e.g.,
11376 *
11377 * convert *.png png8:animation.mng
11378 *
11379 * To do: check -define png:bit_depth and png:color_type as well,
11380 * or perhaps use mng:bit_depth and mng:color_type instead for
11381 * global settings.
11382 */
11383
11384 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11385 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11386 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11387
11388 write_jng=MagickFalse;
11389 if (image_info->compression == JPEGCompression)
11390 write_jng=MagickTrue;
11391
11392 mng_info->adjoin=image_info->adjoin &&
11393 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11394
cristy3ed852e2009-09-05 21:47:34 +000011395 if (logging != MagickFalse)
11396 {
11397 /* Log some info about the input */
11398 Image
11399 *p;
11400
11401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11402 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011403
cristy3ed852e2009-09-05 21:47:34 +000011404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011405 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011406
cristy3ed852e2009-09-05 21:47:34 +000011407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11408 " Type: %d",image_info->type);
11409
11410 scene=0;
11411 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11412 {
11413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011414 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011415
cristy3ed852e2009-09-05 21:47:34 +000011416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011417 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011418
cristy3ed852e2009-09-05 21:47:34 +000011419 if (p->matte)
11420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11421 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011422
cristy3ed852e2009-09-05 21:47:34 +000011423 else
11424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11425 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011426
cristy3ed852e2009-09-05 21:47:34 +000011427 if (p->storage_class == PseudoClass)
11428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11429 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011430
cristy3ed852e2009-09-05 21:47:34 +000011431 else
11432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11433 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011434
cristy3ed852e2009-09-05 21:47:34 +000011435 if (p->colors)
11436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011437 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011438
cristy3ed852e2009-09-05 21:47:34 +000011439 else
11440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11441 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011442
cristy3ed852e2009-09-05 21:47:34 +000011443 if (mng_info->adjoin == MagickFalse)
11444 break;
11445 }
11446 }
11447
cristy3ed852e2009-09-05 21:47:34 +000011448 use_global_plte=MagickFalse;
11449 all_images_are_gray=MagickFalse;
11450#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11451 need_local_plte=MagickTrue;
11452#endif
11453 need_defi=MagickFalse;
11454 need_matte=MagickFalse;
11455 mng_info->framing_mode=1;
11456 mng_info->old_framing_mode=1;
11457
11458 if (write_mng)
11459 if (image_info->page != (char *) NULL)
11460 {
11461 /*
11462 Determine image bounding box.
11463 */
11464 SetGeometry(image,&mng_info->page);
11465 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11466 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11467 }
11468 if (write_mng)
11469 {
11470 unsigned int
11471 need_geom;
11472
11473 unsigned short
11474 red,
11475 green,
11476 blue;
11477
11478 mng_info->page=image->page;
11479 need_geom=MagickTrue;
11480 if (mng_info->page.width || mng_info->page.height)
11481 need_geom=MagickFalse;
11482 /*
11483 Check all the scenes.
11484 */
11485 initial_delay=image->delay;
11486 need_iterations=MagickFalse;
11487 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11488 mng_info->equal_physs=MagickTrue,
11489 mng_info->equal_gammas=MagickTrue;
11490 mng_info->equal_srgbs=MagickTrue;
11491 mng_info->equal_backgrounds=MagickTrue;
11492 image_count=0;
11493#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11494 defined(PNG_MNG_FEATURES_SUPPORTED)
11495 all_images_are_gray=MagickTrue;
11496 mng_info->equal_palettes=MagickFalse;
11497 need_local_plte=MagickFalse;
11498#endif
11499 for (next_image=image; next_image != (Image *) NULL; )
11500 {
11501 if (need_geom)
11502 {
11503 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11504 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011505
cristy3ed852e2009-09-05 21:47:34 +000011506 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11507 mng_info->page.height=next_image->rows+next_image->page.y;
11508 }
glennrp0fe50b42010-11-16 03:52:51 +000011509
cristy3ed852e2009-09-05 21:47:34 +000011510 if (next_image->page.x || next_image->page.y)
11511 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011512
cristy3ed852e2009-09-05 21:47:34 +000011513 if (next_image->matte)
11514 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011515
cristy3ed852e2009-09-05 21:47:34 +000011516 if ((int) next_image->dispose >= BackgroundDispose)
11517 if (next_image->matte || next_image->page.x || next_image->page.y ||
11518 ((next_image->columns < mng_info->page.width) &&
11519 (next_image->rows < mng_info->page.height)))
11520 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011521
cristy3ed852e2009-09-05 21:47:34 +000011522 if (next_image->iterations)
11523 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011524
cristy3ed852e2009-09-05 21:47:34 +000011525 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011526
cristy3ed852e2009-09-05 21:47:34 +000011527 if (final_delay != initial_delay || final_delay > 1UL*
11528 next_image->ticks_per_second)
11529 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011530
cristy3ed852e2009-09-05 21:47:34 +000011531#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11532 defined(PNG_MNG_FEATURES_SUPPORTED)
11533 /*
11534 check for global palette possibility.
11535 */
11536 if (image->matte != MagickFalse)
11537 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011538
cristy3ed852e2009-09-05 21:47:34 +000011539 if (need_local_plte == 0)
11540 {
11541 if (ImageIsGray(image) == MagickFalse)
11542 all_images_are_gray=MagickFalse;
11543 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11544 if (use_global_plte == 0)
11545 use_global_plte=mng_info->equal_palettes;
11546 need_local_plte=!mng_info->equal_palettes;
11547 }
11548#endif
11549 if (GetNextImageInList(next_image) != (Image *) NULL)
11550 {
11551 if (next_image->background_color.red !=
11552 next_image->next->background_color.red ||
11553 next_image->background_color.green !=
11554 next_image->next->background_color.green ||
11555 next_image->background_color.blue !=
11556 next_image->next->background_color.blue)
11557 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011558
cristy3ed852e2009-09-05 21:47:34 +000011559 if (next_image->gamma != next_image->next->gamma)
11560 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011561
cristy3ed852e2009-09-05 21:47:34 +000011562 if (next_image->rendering_intent !=
11563 next_image->next->rendering_intent)
11564 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011565
cristy3ed852e2009-09-05 21:47:34 +000011566 if ((next_image->units != next_image->next->units) ||
11567 (next_image->x_resolution != next_image->next->x_resolution) ||
11568 (next_image->y_resolution != next_image->next->y_resolution))
11569 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011570
cristy3ed852e2009-09-05 21:47:34 +000011571 if (mng_info->equal_chrms)
11572 {
11573 if (next_image->chromaticity.red_primary.x !=
11574 next_image->next->chromaticity.red_primary.x ||
11575 next_image->chromaticity.red_primary.y !=
11576 next_image->next->chromaticity.red_primary.y ||
11577 next_image->chromaticity.green_primary.x !=
11578 next_image->next->chromaticity.green_primary.x ||
11579 next_image->chromaticity.green_primary.y !=
11580 next_image->next->chromaticity.green_primary.y ||
11581 next_image->chromaticity.blue_primary.x !=
11582 next_image->next->chromaticity.blue_primary.x ||
11583 next_image->chromaticity.blue_primary.y !=
11584 next_image->next->chromaticity.blue_primary.y ||
11585 next_image->chromaticity.white_point.x !=
11586 next_image->next->chromaticity.white_point.x ||
11587 next_image->chromaticity.white_point.y !=
11588 next_image->next->chromaticity.white_point.y)
11589 mng_info->equal_chrms=MagickFalse;
11590 }
11591 }
11592 image_count++;
11593 next_image=GetNextImageInList(next_image);
11594 }
11595 if (image_count < 2)
11596 {
11597 mng_info->equal_backgrounds=MagickFalse;
11598 mng_info->equal_chrms=MagickFalse;
11599 mng_info->equal_gammas=MagickFalse;
11600 mng_info->equal_srgbs=MagickFalse;
11601 mng_info->equal_physs=MagickFalse;
11602 use_global_plte=MagickFalse;
11603#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11604 need_local_plte=MagickTrue;
11605#endif
11606 need_iterations=MagickFalse;
11607 }
glennrp0fe50b42010-11-16 03:52:51 +000011608
cristy3ed852e2009-09-05 21:47:34 +000011609 if (mng_info->need_fram == MagickFalse)
11610 {
11611 /*
11612 Only certain framing rates 100/n are exactly representable without
11613 the FRAM chunk but we'll allow some slop in VLC files
11614 */
11615 if (final_delay == 0)
11616 {
11617 if (need_iterations != MagickFalse)
11618 {
11619 /*
11620 It's probably a GIF with loop; don't run it *too* fast.
11621 */
glennrp02617122010-07-28 13:07:35 +000011622 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011623 {
11624 final_delay=10;
11625 (void) ThrowMagickException(&image->exception,
11626 GetMagickModule(),CoderWarning,
11627 "input has zero delay between all frames; assuming",
11628 " 10 cs `%s'","");
11629 }
cristy3ed852e2009-09-05 21:47:34 +000011630 }
11631 else
11632 mng_info->ticks_per_second=0;
11633 }
11634 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011635 mng_info->ticks_per_second=(png_uint_32)
11636 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011637 if (final_delay > 50)
11638 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011639
cristy3ed852e2009-09-05 21:47:34 +000011640 if (final_delay > 75)
11641 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011642
cristy3ed852e2009-09-05 21:47:34 +000011643 if (final_delay > 125)
11644 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011645
cristy3ed852e2009-09-05 21:47:34 +000011646 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11647 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11648 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11649 1UL*image->ticks_per_second))
11650 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11651 }
glennrp0fe50b42010-11-16 03:52:51 +000011652
cristy3ed852e2009-09-05 21:47:34 +000011653 if (mng_info->need_fram != MagickFalse)
11654 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11655 /*
11656 If pseudocolor, we should also check to see if all the
11657 palettes are identical and write a global PLTE if they are.
11658 ../glennrp Feb 99.
11659 */
11660 /*
11661 Write the MNG version 1.0 signature and MHDR chunk.
11662 */
11663 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11664 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11665 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011666 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011667 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11668 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011669 PNGLong(chunk+12,mng_info->ticks_per_second);
11670 PNGLong(chunk+16,0L); /* layer count=unknown */
11671 PNGLong(chunk+20,0L); /* frame count=unknown */
11672 PNGLong(chunk+24,0L); /* play time=unknown */
11673 if (write_jng)
11674 {
11675 if (need_matte)
11676 {
11677 if (need_defi || mng_info->need_fram || use_global_plte)
11678 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011679
cristy3ed852e2009-09-05 21:47:34 +000011680 else
11681 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11682 }
glennrp0fe50b42010-11-16 03:52:51 +000011683
cristy3ed852e2009-09-05 21:47:34 +000011684 else
11685 {
11686 if (need_defi || mng_info->need_fram || use_global_plte)
11687 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011688
cristy3ed852e2009-09-05 21:47:34 +000011689 else
11690 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11691 }
11692 }
glennrp0fe50b42010-11-16 03:52:51 +000011693
cristy3ed852e2009-09-05 21:47:34 +000011694 else
11695 {
11696 if (need_matte)
11697 {
11698 if (need_defi || mng_info->need_fram || use_global_plte)
11699 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011700
cristy3ed852e2009-09-05 21:47:34 +000011701 else
11702 PNGLong(chunk+28,9L); /* simplicity=VLC */
11703 }
glennrp0fe50b42010-11-16 03:52:51 +000011704
cristy3ed852e2009-09-05 21:47:34 +000011705 else
11706 {
11707 if (need_defi || mng_info->need_fram || use_global_plte)
11708 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011709
cristy3ed852e2009-09-05 21:47:34 +000011710 else
11711 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11712 }
11713 }
11714 (void) WriteBlob(image,32,chunk);
11715 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11716 option=GetImageOption(image_info,"mng:need-cacheoff");
11717 if (option != (const char *) NULL)
11718 {
11719 size_t
11720 length;
11721
11722 /*
11723 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11724 */
11725 PNGType(chunk,mng_nEED);
11726 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011727 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011728 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011729 length+=4;
11730 (void) WriteBlob(image,length,chunk);
11731 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11732 }
11733 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11734 (GetNextImageInList(image) != (Image *) NULL) &&
11735 (image->iterations != 1))
11736 {
11737 /*
11738 Write MNG TERM chunk
11739 */
11740 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11741 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011742 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011743 chunk[4]=3; /* repeat animation */
11744 chunk[5]=0; /* show last frame when done */
11745 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11746 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011747
cristy3ed852e2009-09-05 21:47:34 +000011748 if (image->iterations == 0)
11749 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011750
cristy3ed852e2009-09-05 21:47:34 +000011751 else
11752 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011753
cristy3ed852e2009-09-05 21:47:34 +000011754 if (logging != MagickFalse)
11755 {
11756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011757 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11758 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011759
cristy3ed852e2009-09-05 21:47:34 +000011760 if (image->iterations == 0)
11761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011762 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011763
cristy3ed852e2009-09-05 21:47:34 +000011764 else
11765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011766 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011767 }
11768 (void) WriteBlob(image,14,chunk);
11769 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11770 }
11771 /*
11772 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11773 */
11774 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11775 mng_info->equal_srgbs)
11776 {
11777 /*
11778 Write MNG sRGB chunk
11779 */
11780 (void) WriteBlobMSBULong(image,1L);
11781 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011782 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011783
cristy3ed852e2009-09-05 21:47:34 +000011784 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011785 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011786 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011787 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011788
cristy3ed852e2009-09-05 21:47:34 +000011789 else
glennrpe610a072010-08-05 17:08:46 +000011790 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011791 Magick_RenderingIntent_to_PNG_RenderingIntent(
11792 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011793
cristy3ed852e2009-09-05 21:47:34 +000011794 (void) WriteBlob(image,5,chunk);
11795 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11796 mng_info->have_write_global_srgb=MagickTrue;
11797 }
glennrp0fe50b42010-11-16 03:52:51 +000011798
cristy3ed852e2009-09-05 21:47:34 +000011799 else
11800 {
11801 if (image->gamma && mng_info->equal_gammas)
11802 {
11803 /*
11804 Write MNG gAMA chunk
11805 */
11806 (void) WriteBlobMSBULong(image,4L);
11807 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011808 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011809 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011810 (void) WriteBlob(image,8,chunk);
11811 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11812 mng_info->have_write_global_gama=MagickTrue;
11813 }
11814 if (mng_info->equal_chrms)
11815 {
11816 PrimaryInfo
11817 primary;
11818
11819 /*
11820 Write MNG cHRM chunk
11821 */
11822 (void) WriteBlobMSBULong(image,32L);
11823 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011824 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011825 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011826 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11827 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011828 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011829 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11830 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011831 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011832 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11833 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011834 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011835 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11836 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011837 (void) WriteBlob(image,36,chunk);
11838 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11839 mng_info->have_write_global_chrm=MagickTrue;
11840 }
11841 }
11842 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11843 {
11844 /*
11845 Write MNG pHYs chunk
11846 */
11847 (void) WriteBlobMSBULong(image,9L);
11848 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011849 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011850
cristy3ed852e2009-09-05 21:47:34 +000011851 if (image->units == PixelsPerInchResolution)
11852 {
cristy35ef8242010-06-03 16:24:13 +000011853 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011854 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011855
cristy35ef8242010-06-03 16:24:13 +000011856 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011857 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011858
cristy3ed852e2009-09-05 21:47:34 +000011859 chunk[12]=1;
11860 }
glennrp0fe50b42010-11-16 03:52:51 +000011861
cristy3ed852e2009-09-05 21:47:34 +000011862 else
11863 {
11864 if (image->units == PixelsPerCentimeterResolution)
11865 {
cristy35ef8242010-06-03 16:24:13 +000011866 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011867 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011868
cristy35ef8242010-06-03 16:24:13 +000011869 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011870 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011871
cristy3ed852e2009-09-05 21:47:34 +000011872 chunk[12]=1;
11873 }
glennrp0fe50b42010-11-16 03:52:51 +000011874
cristy3ed852e2009-09-05 21:47:34 +000011875 else
11876 {
cristy35ef8242010-06-03 16:24:13 +000011877 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11878 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011879 chunk[12]=0;
11880 }
11881 }
11882 (void) WriteBlob(image,13,chunk);
11883 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11884 }
11885 /*
11886 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11887 or does not cover the entire frame.
11888 */
11889 if (write_mng && (image->matte || image->page.x > 0 ||
11890 image->page.y > 0 || (image->page.width &&
11891 (image->page.width+image->page.x < mng_info->page.width))
11892 || (image->page.height && (image->page.height+image->page.y
11893 < mng_info->page.height))))
11894 {
11895 (void) WriteBlobMSBULong(image,6L);
11896 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011897 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011898 red=ScaleQuantumToShort(image->background_color.red);
11899 green=ScaleQuantumToShort(image->background_color.green);
11900 blue=ScaleQuantumToShort(image->background_color.blue);
11901 PNGShort(chunk+4,red);
11902 PNGShort(chunk+6,green);
11903 PNGShort(chunk+8,blue);
11904 (void) WriteBlob(image,10,chunk);
11905 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11906 if (mng_info->equal_backgrounds)
11907 {
11908 (void) WriteBlobMSBULong(image,6L);
11909 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011910 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011911 (void) WriteBlob(image,10,chunk);
11912 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11913 }
11914 }
11915
11916#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11917 if ((need_local_plte == MagickFalse) &&
11918 (image->storage_class == PseudoClass) &&
11919 (all_images_are_gray == MagickFalse))
11920 {
cristybb503372010-05-27 20:51:26 +000011921 size_t
cristy3ed852e2009-09-05 21:47:34 +000011922 data_length;
11923
11924 /*
11925 Write MNG PLTE chunk
11926 */
11927 data_length=3*image->colors;
11928 (void) WriteBlobMSBULong(image,data_length);
11929 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011930 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011931
cristybb503372010-05-27 20:51:26 +000011932 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011933 {
11934 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11935 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11936 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11937 }
glennrp0fe50b42010-11-16 03:52:51 +000011938
cristy3ed852e2009-09-05 21:47:34 +000011939 (void) WriteBlob(image,data_length+4,chunk);
11940 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11941 mng_info->have_write_global_plte=MagickTrue;
11942 }
11943#endif
11944 }
11945 scene=0;
11946 mng_info->delay=0;
11947#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11948 defined(PNG_MNG_FEATURES_SUPPORTED)
11949 mng_info->equal_palettes=MagickFalse;
11950#endif
11951 do
11952 {
11953 if (mng_info->adjoin)
11954 {
11955#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11956 defined(PNG_MNG_FEATURES_SUPPORTED)
11957 /*
11958 If we aren't using a global palette for the entire MNG, check to
11959 see if we can use one for two or more consecutive images.
11960 */
11961 if (need_local_plte && use_global_plte && !all_images_are_gray)
11962 {
11963 if (mng_info->IsPalette)
11964 {
11965 /*
11966 When equal_palettes is true, this image has the same palette
11967 as the previous PseudoClass image
11968 */
11969 mng_info->have_write_global_plte=mng_info->equal_palettes;
11970 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11971 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11972 {
11973 /*
11974 Write MNG PLTE chunk
11975 */
cristybb503372010-05-27 20:51:26 +000011976 size_t
cristy3ed852e2009-09-05 21:47:34 +000011977 data_length;
11978
11979 data_length=3*image->colors;
11980 (void) WriteBlobMSBULong(image,data_length);
11981 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011982 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011983
cristybb503372010-05-27 20:51:26 +000011984 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011985 {
11986 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11987 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11988 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11989 }
glennrp0fe50b42010-11-16 03:52:51 +000011990
cristy3ed852e2009-09-05 21:47:34 +000011991 (void) WriteBlob(image,data_length+4,chunk);
11992 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11993 (uInt) (data_length+4)));
11994 mng_info->have_write_global_plte=MagickTrue;
11995 }
11996 }
11997 else
11998 mng_info->have_write_global_plte=MagickFalse;
11999 }
12000#endif
12001 if (need_defi)
12002 {
cristybb503372010-05-27 20:51:26 +000012003 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012004 previous_x,
12005 previous_y;
12006
12007 if (scene)
12008 {
12009 previous_x=mng_info->page.x;
12010 previous_y=mng_info->page.y;
12011 }
12012 else
12013 {
12014 previous_x=0;
12015 previous_y=0;
12016 }
12017 mng_info->page=image->page;
12018 if ((mng_info->page.x != previous_x) ||
12019 (mng_info->page.y != previous_y))
12020 {
12021 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12022 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012023 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012024 chunk[4]=0; /* object 0 MSB */
12025 chunk[5]=0; /* object 0 LSB */
12026 chunk[6]=0; /* visible */
12027 chunk[7]=0; /* abstract */
12028 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12029 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12030 (void) WriteBlob(image,16,chunk);
12031 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12032 }
12033 }
12034 }
12035
12036 mng_info->write_mng=write_mng;
12037
12038 if ((int) image->dispose >= 3)
12039 mng_info->framing_mode=3;
12040
12041 if (mng_info->need_fram && mng_info->adjoin &&
12042 ((image->delay != mng_info->delay) ||
12043 (mng_info->framing_mode != mng_info->old_framing_mode)))
12044 {
12045 if (image->delay == mng_info->delay)
12046 {
12047 /*
12048 Write a MNG FRAM chunk with the new framing mode.
12049 */
12050 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12051 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012052 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012053 chunk[4]=(unsigned char) mng_info->framing_mode;
12054 (void) WriteBlob(image,5,chunk);
12055 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12056 }
12057 else
12058 {
12059 /*
12060 Write a MNG FRAM chunk with the delay.
12061 */
12062 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12063 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012064 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012065 chunk[4]=(unsigned char) mng_info->framing_mode;
12066 chunk[5]=0; /* frame name separator (no name) */
12067 chunk[6]=2; /* flag for changing default delay */
12068 chunk[7]=0; /* flag for changing frame timeout */
12069 chunk[8]=0; /* flag for changing frame clipping */
12070 chunk[9]=0; /* flag for changing frame sync_id */
12071 PNGLong(chunk+10,(png_uint_32)
12072 ((mng_info->ticks_per_second*
12073 image->delay)/MagickMax(image->ticks_per_second,1)));
12074 (void) WriteBlob(image,14,chunk);
12075 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012076 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012077 }
12078 mng_info->old_framing_mode=mng_info->framing_mode;
12079 }
12080
12081#if defined(JNG_SUPPORTED)
12082 if (image_info->compression == JPEGCompression)
12083 {
12084 ImageInfo
12085 *write_info;
12086
12087 if (logging != MagickFalse)
12088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12089 " Writing JNG object.");
12090 /* To do: specify the desired alpha compression method. */
12091 write_info=CloneImageInfo(image_info);
12092 write_info->compression=UndefinedCompression;
12093 status=WriteOneJNGImage(mng_info,write_info,image);
12094 write_info=DestroyImageInfo(write_info);
12095 }
12096 else
12097#endif
12098 {
12099 if (logging != MagickFalse)
12100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12101 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012102
glennrpb9cfe272010-12-21 15:08:06 +000012103 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012104 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012105
12106 /* We don't want any ancillary chunks written */
12107 mng_info->ping_exclude_bKGD=MagickTrue;
12108 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012109 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012110 mng_info->ping_exclude_EXIF=MagickTrue;
12111 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012112 mng_info->ping_exclude_iCCP=MagickTrue;
12113 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12114 mng_info->ping_exclude_oFFs=MagickTrue;
12115 mng_info->ping_exclude_pHYs=MagickTrue;
12116 mng_info->ping_exclude_sRGB=MagickTrue;
12117 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012118 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012119 mng_info->ping_exclude_vpAg=MagickTrue;
12120 mng_info->ping_exclude_zCCP=MagickTrue;
12121 mng_info->ping_exclude_zTXt=MagickTrue;
12122
cristy3ed852e2009-09-05 21:47:34 +000012123 status=WriteOnePNGImage(mng_info,image_info,image);
12124 }
12125
12126 if (status == MagickFalse)
12127 {
12128 MngInfoFreeStruct(mng_info,&have_mng_structure);
12129 (void) CloseBlob(image);
12130 return(MagickFalse);
12131 }
12132 (void) CatchImageException(image);
12133 if (GetNextImageInList(image) == (Image *) NULL)
12134 break;
12135 image=SyncNextImageInList(image);
12136 status=SetImageProgress(image,SaveImagesTag,scene++,
12137 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012138
cristy3ed852e2009-09-05 21:47:34 +000012139 if (status == MagickFalse)
12140 break;
glennrp0fe50b42010-11-16 03:52:51 +000012141
cristy3ed852e2009-09-05 21:47:34 +000012142 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012143
cristy3ed852e2009-09-05 21:47:34 +000012144 if (write_mng)
12145 {
12146 while (GetPreviousImageInList(image) != (Image *) NULL)
12147 image=GetPreviousImageInList(image);
12148 /*
12149 Write the MEND chunk.
12150 */
12151 (void) WriteBlobMSBULong(image,0x00000000L);
12152 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012153 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012154 (void) WriteBlob(image,4,chunk);
12155 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12156 }
12157 /*
12158 Relinquish resources.
12159 */
12160 (void) CloseBlob(image);
12161 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012162
cristy3ed852e2009-09-05 21:47:34 +000012163 if (logging != MagickFalse)
12164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012165
cristy3ed852e2009-09-05 21:47:34 +000012166 return(MagickTrue);
12167}
glennrpd5045b42010-03-24 12:40:35 +000012168#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012169
cristy3ed852e2009-09-05 21:47:34 +000012170static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12171{
12172 image=image;
12173 printf("Your PNG library is too old: You have libpng-%s\n",
12174 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012175
cristy3ed852e2009-09-05 21:47:34 +000012176 ThrowBinaryException(CoderError,"PNG library is too old",
12177 image_info->filename);
12178}
glennrp39992b42010-11-14 00:03:43 +000012179
cristy3ed852e2009-09-05 21:47:34 +000012180static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12181{
12182 return(WritePNGImage(image_info,image));
12183}
glennrpd5045b42010-03-24 12:40:35 +000012184#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012185#endif