blob: 902100fe149a90f7b4d223646dc8aeb1f605c5e9 [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) \
123 (((color).red == (target).red) && \
124 ((color).green == (target).green) && \
125 ((color).blue == (target).blue))
126#endif
127
128/*
129 Establish thread safety.
130 setjmp/longjmp is claimed to be safe on these platforms:
131 setjmp/longjmp is alleged to be unsafe on these platforms:
132*/
133#ifndef SETJMP_IS_THREAD_SAFE
134#define PNG_SETJMP_NOT_THREAD_SAFE
135#endif
136
137#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
138static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000139 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000140#endif
141
142/*
143 This temporary until I set up malloc'ed object attributes array.
144 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
145 waste more memory.
146*/
147#define MNG_MAX_OBJECTS 256
148
149/*
150 If this not defined, spec is interpreted strictly. If it is
151 defined, an attempt will be made to recover from some errors,
152 including
153 o global PLTE too short
154*/
155#undef MNG_LOOSE
156
157/*
158 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
159 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
160 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
161 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
162 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
163 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
164 will be enabled by default in libpng-1.2.0.
165*/
cristy3ed852e2009-09-05 21:47:34 +0000166#ifdef PNG_MNG_FEATURES_SUPPORTED
167# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
168# define PNG_READ_EMPTY_PLTE_SUPPORTED
169# endif
170# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
171# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
172# endif
173#endif
174
175/*
cristybb503372010-05-27 20:51:26 +0000176 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000177 This macro is only defined in libpng-1.0.3 and later.
178 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
179*/
180#ifndef PNG_UINT_31_MAX
181#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
182#endif
183
184/*
185 Constant strings for known chunk types. If you need to add a chunk,
186 add a string holding the name here. To make the code more
187 portable, we use ASCII numbers like this, not characters.
188*/
189
190static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
191static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
192static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
193static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
194static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
195static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
196static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
197static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
198static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
199static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
200static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
201static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
202static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
203static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
204static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
205static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
206static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
207static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
208static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
209static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
210static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
211static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
212static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
213static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
214static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
215static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
216static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
217static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
218static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
219static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
220static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
221static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
222static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
223static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
224
225#if defined(JNG_SUPPORTED)
226static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
227static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
228static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
229static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
230static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
231static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
232#endif
233
234/*
235Other known chunks that are not yet supported by ImageMagick:
236static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
237static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
238static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
239static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
240static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
241static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
242static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
243static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
244*/
245
246typedef struct _MngBox
247{
cristy8182b072010-05-30 20:10:53 +0000248 long
cristy3ed852e2009-09-05 21:47:34 +0000249 left,
250 right,
251 top,
252 bottom;
253} MngBox;
254
255typedef struct _MngPair
256{
cristy8182b072010-05-30 20:10:53 +0000257 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000258 a,
259 b;
260} MngPair;
261
262#ifdef MNG_OBJECT_BUFFERS
263typedef struct _MngBuffer
264{
265
cristybb503372010-05-27 20:51:26 +0000266 size_t
cristy3ed852e2009-09-05 21:47:34 +0000267 height,
268 width;
269
270 Image
271 *image;
272
273 png_color
274 plte[256];
275
276 int
277 reference_count;
278
279 unsigned char
280 alpha_sample_depth,
281 compression_method,
282 color_type,
283 concrete,
284 filter_method,
285 frozen,
286 image_type,
287 interlace_method,
288 pixel_sample_depth,
289 plte_length,
290 sample_depth,
291 viewable;
292} MngBuffer;
293#endif
294
295typedef struct _MngInfo
296{
297
298#ifdef MNG_OBJECT_BUFFERS
299 MngBuffer
300 *ob[MNG_MAX_OBJECTS];
301#endif
302
303 Image *
304 image;
305
306 RectangleInfo
307 page;
308
309 int
310 adjoin,
311#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
312 bytes_in_read_buffer,
313 found_empty_plte,
314#endif
315 equal_backgrounds,
316 equal_chrms,
317 equal_gammas,
318#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
319 defined(PNG_MNG_FEATURES_SUPPORTED)
320 equal_palettes,
321#endif
322 equal_physs,
323 equal_srgbs,
324 framing_mode,
325 have_global_bkgd,
326 have_global_chrm,
327 have_global_gama,
328 have_global_phys,
329 have_global_sbit,
330 have_global_srgb,
331 have_saved_bkgd_index,
332 have_write_global_chrm,
333 have_write_global_gama,
334 have_write_global_plte,
335 have_write_global_srgb,
336 need_fram,
337 object_id,
338 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000339 saved_bkgd_index;
340
341 int
342 new_number_colors;
343
cristybb503372010-05-27 20:51:26 +0000344 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000345 image_found,
346 loop_count[256],
347 loop_iteration[256],
348 scenes_found,
349 x_off[MNG_MAX_OBJECTS],
350 y_off[MNG_MAX_OBJECTS];
351
352 MngBox
353 clip,
354 frame,
355 image_box,
356 object_clip[MNG_MAX_OBJECTS];
357
358 unsigned char
359 /* These flags could be combined into one byte */
360 exists[MNG_MAX_OBJECTS],
361 frozen[MNG_MAX_OBJECTS],
362 loop_active[256],
363 invisible[MNG_MAX_OBJECTS],
364 viewable[MNG_MAX_OBJECTS];
365
366 MagickOffsetType
367 loop_jump[256];
368
369 png_colorp
370 global_plte;
371
372 png_color_8
373 global_sbit;
374
375 png_byte
376#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
377 read_buffer[8],
378#endif
379 global_trns[256];
380
381 float
382 global_gamma;
383
384 ChromaticityInfo
385 global_chrm;
386
387 RenderingIntent
388 global_srgb_intent;
389
cristy35ef8242010-06-03 16:24:13 +0000390 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000391 delay,
392 global_plte_length,
393 global_trns_length,
394 global_x_pixels_per_unit,
395 global_y_pixels_per_unit,
396 mng_width,
397 mng_height,
398 ticks_per_second;
399
glennrpb9cfe272010-12-21 15:08:06 +0000400 MagickBooleanType
401 need_blob;
402
cristy3ed852e2009-09-05 21:47:34 +0000403 unsigned int
404 IsPalette,
405 global_phys_unit_type,
406 basi_warning,
407 clon_warning,
408 dhdr_warning,
409 jhdr_warning,
410 magn_warning,
411 past_warning,
412 phyg_warning,
413 phys_warning,
414 sbit_warning,
415 show_warning,
416 mng_type,
417 write_mng,
418 write_png_colortype,
419 write_png_depth,
420 write_png8,
421 write_png24,
422 write_png32;
423
424#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000425 size_t
cristy3ed852e2009-09-05 21:47:34 +0000426 basi_width,
427 basi_height;
428
429 unsigned int
430 basi_depth,
431 basi_color_type,
432 basi_compression_method,
433 basi_filter_type,
434 basi_interlace_method,
435 basi_red,
436 basi_green,
437 basi_blue,
438 basi_alpha,
439 basi_viewable;
440#endif
441
442 png_uint_16
443 magn_first,
444 magn_last,
445 magn_mb,
446 magn_ml,
447 magn_mr,
448 magn_mt,
449 magn_mx,
450 magn_my,
451 magn_methx,
452 magn_methy;
453
454 PixelPacket
455 mng_global_bkgd;
456
glennrp26f37912010-12-23 16:22:42 +0000457 /* Added at version 6.6.6-7 */
458 MagickBooleanType
459 ping_exclude_bKGD,
460 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000461 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000462 ping_exclude_EXIF,
463 ping_exclude_gAMA,
464 ping_exclude_iCCP,
465 /* ping_exclude_iTXt, */
466 ping_exclude_oFFs,
467 ping_exclude_pHYs,
468 ping_exclude_sRGB,
469 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000470 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000471 ping_exclude_vpAg,
472 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000473 ping_exclude_zTXt,
474 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000475
cristy3ed852e2009-09-05 21:47:34 +0000476} MngInfo;
477#endif /* VER */
478
479/*
480 Forward declarations.
481*/
482static MagickBooleanType
483 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000484
cristy3ed852e2009-09-05 21:47:34 +0000485static MagickBooleanType
486 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000487
cristy3ed852e2009-09-05 21:47:34 +0000488#if defined(JNG_SUPPORTED)
489static MagickBooleanType
490 WriteJNGImage(const ImageInfo *,Image *);
491#endif
492
glennrp0c3e06b2010-11-19 13:45:02 +0000493#if PNG_LIBPNG_VER > 10011
494
glennrpfd05d622011-02-25 04:10:33 +0000495
glennrp0c3e06b2010-11-19 13:45:02 +0000496#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
497static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000498LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000499{
glennrp67b9c1a2011-04-22 18:47:36 +0000500 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
501 *
502 * This is true if the high byte and the next highest byte of
503 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000504 * are equal to each other. We check this by seeing if the samples
505 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000506 *
507 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000508 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000509 */
510
glennrp3faa9a32011-04-23 14:00:25 +0000511#define QuantumToCharToQuantumEqQuantum(quantum) \
512 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
513
glennrp0c3e06b2010-11-19 13:45:02 +0000514 MagickBooleanType
515 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000516
glennrp03e11f62011-04-22 13:30:16 +0000517 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000518 {
519
520 const PixelPacket
521 *p;
522
523 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000524 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
525 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
526 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
527 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000528
529 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
530 {
531 int indx;
532
533 for (indx=0; indx < (ssize_t) image->colors; indx++)
534 {
glennrp3faa9a32011-04-23 14:00:25 +0000535 ok_to_reduce=(
536 QuantumToCharToQuantumEqQuantum(
537 image->colormap[indx].red) &&
538 QuantumToCharToQuantumEqQuantum(
539 image->colormap[indx].green) &&
540 QuantumToCharToQuantumEqQuantum(
541 image->colormap[indx].blue)) ?
542 MagickTrue : MagickFalse;
543
glennrp0c3e06b2010-11-19 13:45:02 +0000544 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000545 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000546 }
547 }
548
549 if ((ok_to_reduce != MagickFalse) &&
550 (image->storage_class != PseudoClass))
551 {
552 ssize_t
553 y;
554
555 register ssize_t
556 x;
557
558 for (y=0; y < (ssize_t) image->rows; y++)
559 {
560 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
561
562 if (p == (const PixelPacket *) NULL)
563 {
564 ok_to_reduce = MagickFalse;
565 break;
566 }
567
568 for (x=(ssize_t) image->columns-1; x >= 0; x--)
569 {
glennrp3faa9a32011-04-23 14:00:25 +0000570 ok_to_reduce=
571 QuantumToCharToQuantumEqQuantum(GetRedPixelComponent(p)) &&
572 QuantumToCharToQuantumEqQuantum(GetGreenPixelComponent(p)) &&
573 QuantumToCharToQuantumEqQuantum(GetBluePixelComponent(p)) ?
574 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000575
576 if (ok_to_reduce == MagickFalse)
577 break;
578
579 p++;
580 }
glennrp8640fb52010-11-23 15:48:26 +0000581 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +0000582 break;
583 }
584 }
585
586 if (ok_to_reduce != MagickFalse)
587 {
glennrp0c3e06b2010-11-19 13:45:02 +0000588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000589 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +0000590 }
glennrpa6a06632011-01-19 15:15:34 +0000591 else
592 {
593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000594 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +0000595 }
glennrp0c3e06b2010-11-19 13:45:02 +0000596 }
597
598 return ok_to_reduce;
599}
600#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
601
glennrpe610a072010-08-05 17:08:46 +0000602static int
glennrpcf002022011-01-30 02:38:15 +0000603Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +0000604{
glennrpe610a072010-08-05 17:08:46 +0000605 switch (intent)
606 {
607 case PerceptualIntent:
608 return 0;
glennrp0fe50b42010-11-16 03:52:51 +0000609
glennrpe610a072010-08-05 17:08:46 +0000610 case RelativeIntent:
611 return 1;
glennrp0fe50b42010-11-16 03:52:51 +0000612
glennrpe610a072010-08-05 17:08:46 +0000613 case SaturationIntent:
614 return 2;
glennrp0fe50b42010-11-16 03:52:51 +0000615
glennrpe610a072010-08-05 17:08:46 +0000616 case AbsoluteIntent:
617 return 3;
glennrp0fe50b42010-11-16 03:52:51 +0000618
glennrpe610a072010-08-05 17:08:46 +0000619 default:
620 return -1;
621 }
622}
623
624static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +0000625Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +0000626{
glennrpcf002022011-01-30 02:38:15 +0000627 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +0000628 {
629 case 0:
630 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000631
glennrpe610a072010-08-05 17:08:46 +0000632 case 1:
633 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000634
glennrpe610a072010-08-05 17:08:46 +0000635 case 2:
636 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000637
glennrpe610a072010-08-05 17:08:46 +0000638 case 3:
639 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000640
glennrpe610a072010-08-05 17:08:46 +0000641 default:
642 return UndefinedIntent;
643 }
644}
645
cristybb503372010-05-27 20:51:26 +0000646static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000647{
648 if (x > y)
649 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000650
cristy3ed852e2009-09-05 21:47:34 +0000651 return(y);
652}
glennrp0c3e06b2010-11-19 13:45:02 +0000653
cristybb503372010-05-27 20:51:26 +0000654static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000655{
656 if (x < y)
657 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000658
cristy3ed852e2009-09-05 21:47:34 +0000659 return(y);
660}
glennrp0c3e06b2010-11-19 13:45:02 +0000661
cristy3ed852e2009-09-05 21:47:34 +0000662
663/*
664%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
665% %
666% %
667% %
668% I m a g e I s G r a y %
669% %
670% %
671% %
672%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
673% %
674% Like IsGrayImage except does not change DirectClass to PseudoClass %
675% %
676%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
677*/
678static MagickBooleanType ImageIsGray(Image *image)
679{
680 register const PixelPacket
681 *p;
682
cristybb503372010-05-27 20:51:26 +0000683 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000684 i,
685 x,
686 y;
687
688 assert(image != (Image *) NULL);
689 assert(image->signature == MagickSignature);
690 if (image->debug != MagickFalse)
691 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
692
693 if (image->storage_class == PseudoClass)
694 {
cristybb503372010-05-27 20:51:26 +0000695 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000696 if (IsGray(image->colormap+i) == MagickFalse)
697 return(MagickFalse);
698 return(MagickTrue);
699 }
cristybb503372010-05-27 20:51:26 +0000700 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000701 {
702 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
703 if (p == (const PixelPacket *) NULL)
704 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000705 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +0000706 {
707 if (IsGray(p) == MagickFalse)
708 return(MagickFalse);
709 p++;
710 }
711 }
712 return(MagickTrue);
713}
glennrpd5045b42010-03-24 12:40:35 +0000714#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000715#endif /* MAGICKCORE_PNG_DELEGATE */
716
717/*
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719% %
720% %
721% %
722% I s M N G %
723% %
724% %
725% %
726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727%
728% IsMNG() returns MagickTrue if the image format type, identified by the
729% magick string, is MNG.
730%
731% The format of the IsMNG method is:
732%
733% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
734%
735% A description of each parameter follows:
736%
737% o magick: compare image format pattern against these bytes.
738%
739% o length: Specifies the length of the magick string.
740%
741%
742*/
743static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
744{
745 if (length < 8)
746 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000747
cristy3ed852e2009-09-05 21:47:34 +0000748 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
749 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000750
cristy3ed852e2009-09-05 21:47:34 +0000751 return(MagickFalse);
752}
753
754/*
755%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756% %
757% %
758% %
759% I s J N G %
760% %
761% %
762% %
763%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
764%
765% IsJNG() returns MagickTrue if the image format type, identified by the
766% magick string, is JNG.
767%
768% The format of the IsJNG method is:
769%
770% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
771%
772% A description of each parameter follows:
773%
774% o magick: compare image format pattern against these bytes.
775%
776% o length: Specifies the length of the magick string.
777%
778%
779*/
780static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
781{
782 if (length < 8)
783 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000784
cristy3ed852e2009-09-05 21:47:34 +0000785 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
786 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000787
cristy3ed852e2009-09-05 21:47:34 +0000788 return(MagickFalse);
789}
790
791/*
792%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
793% %
794% %
795% %
796% I s P N G %
797% %
798% %
799% %
800%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
801%
802% IsPNG() returns MagickTrue if the image format type, identified by the
803% magick string, is PNG.
804%
805% The format of the IsPNG method is:
806%
807% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
808%
809% A description of each parameter follows:
810%
811% o magick: compare image format pattern against these bytes.
812%
813% o length: Specifies the length of the magick string.
814%
815*/
816static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
817{
818 if (length < 8)
819 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000820
cristy3ed852e2009-09-05 21:47:34 +0000821 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
822 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000823
cristy3ed852e2009-09-05 21:47:34 +0000824 return(MagickFalse);
825}
826
827#if defined(MAGICKCORE_PNG_DELEGATE)
828#if defined(__cplusplus) || defined(c_plusplus)
829extern "C" {
830#endif
831
glennrpd5045b42010-03-24 12:40:35 +0000832#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +0000833static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +0000834{
835 unsigned char
836 buffer[4];
837
838 assert(image != (Image *) NULL);
839 assert(image->signature == MagickSignature);
840 buffer[0]=(unsigned char) (value >> 24);
841 buffer[1]=(unsigned char) (value >> 16);
842 buffer[2]=(unsigned char) (value >> 8);
843 buffer[3]=(unsigned char) value;
844 return((size_t) WriteBlob(image,4,buffer));
845}
846
847static void PNGLong(png_bytep p,png_uint_32 value)
848{
849 *p++=(png_byte) ((value >> 24) & 0xff);
850 *p++=(png_byte) ((value >> 16) & 0xff);
851 *p++=(png_byte) ((value >> 8) & 0xff);
852 *p++=(png_byte) (value & 0xff);
853}
854
glennrpa521b2f2010-10-29 04:11:03 +0000855#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +0000856static void PNGsLong(png_bytep p,png_int_32 value)
857{
858 *p++=(png_byte) ((value >> 24) & 0xff);
859 *p++=(png_byte) ((value >> 16) & 0xff);
860 *p++=(png_byte) ((value >> 8) & 0xff);
861 *p++=(png_byte) (value & 0xff);
862}
glennrpa521b2f2010-10-29 04:11:03 +0000863#endif
cristy3ed852e2009-09-05 21:47:34 +0000864
865static void PNGShort(png_bytep p,png_uint_16 value)
866{
867 *p++=(png_byte) ((value >> 8) & 0xff);
868 *p++=(png_byte) (value & 0xff);
869}
870
871static void PNGType(png_bytep p,png_bytep type)
872{
873 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
874}
875
glennrp03812ae2010-12-24 01:31:34 +0000876static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
877 size_t length)
cristy3ed852e2009-09-05 21:47:34 +0000878{
879 if (logging != MagickFalse)
880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000881 " Writing %c%c%c%c chunk, length: %.20g",
882 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000883}
glennrpd5045b42010-03-24 12:40:35 +0000884#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000885
886#if defined(__cplusplus) || defined(c_plusplus)
887}
888#endif
889
glennrpd5045b42010-03-24 12:40:35 +0000890#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000891/*
892%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
893% %
894% %
895% %
896% R e a d P N G I m a g e %
897% %
898% %
899% %
900%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
901%
902% ReadPNGImage() reads a Portable Network Graphics (PNG) or
903% Multiple-image Network Graphics (MNG) image file and returns it. It
904% allocates the memory necessary for the new Image structure and returns a
905% pointer to the new image or set of images.
906%
907% MNG support written by Glenn Randers-Pehrson, glennrp@image...
908%
909% The format of the ReadPNGImage method is:
910%
911% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
912%
913% A description of each parameter follows:
914%
915% o image_info: the image info.
916%
917% o exception: return any errors or warnings in this structure.
918%
919% To do, more or less in chronological order (as of version 5.5.2,
920% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
921%
922% Get 16-bit cheap transparency working.
923%
924% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
925%
926% Preserve all unknown and not-yet-handled known chunks found in input
927% PNG file and copy them into output PNG files according to the PNG
928% copying rules.
929%
930% (At this point, PNG encoding should be in full MNG compliance)
931%
932% Provide options for choice of background to use when the MNG BACK
933% chunk is not present or is not mandatory (i.e., leave transparent,
934% user specified, MNG BACK, PNG bKGD)
935%
936% Implement LOOP/ENDL [done, but could do discretionary loops more
937% efficiently by linking in the duplicate frames.].
938%
939% Decode and act on the MHDR simplicity profile (offer option to reject
940% files or attempt to process them anyway when the profile isn't LC or VLC).
941%
942% Upgrade to full MNG without Delta-PNG.
943%
944% o BACK [done a while ago except for background image ID]
945% o MOVE [done 15 May 1999]
946% o CLIP [done 15 May 1999]
947% o DISC [done 19 May 1999]
948% o SAVE [partially done 19 May 1999 (marks objects frozen)]
949% o SEEK [partially done 19 May 1999 (discard function only)]
950% o SHOW
951% o PAST
952% o BASI
953% o MNG-level tEXt/iTXt/zTXt
954% o pHYg
955% o pHYs
956% o sBIT
957% o bKGD
958% o iTXt (wait for libpng implementation).
959%
960% Use the scene signature to discover when an identical scene is
961% being reused, and just point to the original image->exception instead
962% of storing another set of pixels. This not specific to MNG
963% but could be applied generally.
964%
965% Upgrade to full MNG with Delta-PNG.
966%
967% JNG tEXt/iTXt/zTXt
968%
969% We will not attempt to read files containing the CgBI chunk.
970% They are really Xcode files meant for display on the iPhone.
971% These are not valid PNG files and it is impossible to recover
972% the orginal PNG from files that have been converted to Xcode-PNG,
973% since irretrievable loss of color data has occurred due to the
974% use of premultiplied alpha.
975*/
976
977#if defined(__cplusplus) || defined(c_plusplus)
978extern "C" {
979#endif
980
981/*
982 This the function that does the actual reading of data. It is
983 the same as the one supplied in libpng, except that it receives the
984 datastream from the ReadBlob() function instead of standard input.
985*/
986static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
987{
988 Image
989 *image;
990
991 image=(Image *) png_get_io_ptr(png_ptr);
992 if (length)
993 {
994 png_size_t
995 check;
996
997 check=(png_size_t) ReadBlob(image,(size_t) length,data);
998 if (check != length)
999 {
1000 char
1001 msg[MaxTextExtent];
1002
1003 (void) FormatMagickString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001004 "Expected %.20g bytes; found %.20g bytes",(double) length,
1005 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001006 png_warning(png_ptr,msg);
1007 png_error(png_ptr,"Read Exception");
1008 }
1009 }
1010}
1011
1012#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1013 !defined(PNG_MNG_FEATURES_SUPPORTED)
1014/* We use mng_get_data() instead of png_get_data() if we have a libpng
1015 * older than libpng-1.0.3a, which was the first to allow the empty
1016 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1017 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1018 * encountered after an empty PLTE, so we have to look ahead for bKGD
1019 * chunks and remove them from the datastream that is passed to libpng,
1020 * and store their contents for later use.
1021 */
1022static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1023{
1024 MngInfo
1025 *mng_info;
1026
1027 Image
1028 *image;
1029
1030 png_size_t
1031 check;
1032
cristybb503372010-05-27 20:51:26 +00001033 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001034 i;
1035
1036 i=0;
1037 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1038 image=(Image *) mng_info->image;
1039 while (mng_info->bytes_in_read_buffer && length)
1040 {
1041 data[i]=mng_info->read_buffer[i];
1042 mng_info->bytes_in_read_buffer--;
1043 length--;
1044 i++;
1045 }
1046 if (length)
1047 {
1048 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001049
cristy3ed852e2009-09-05 21:47:34 +00001050 if (check != length)
1051 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001052
cristy3ed852e2009-09-05 21:47:34 +00001053 if (length == 4)
1054 {
1055 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1056 (data[3] == 0))
1057 {
1058 check=(png_size_t) ReadBlob(image,(size_t) length,
1059 (char *) mng_info->read_buffer);
1060 mng_info->read_buffer[4]=0;
1061 mng_info->bytes_in_read_buffer=4;
1062 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1063 mng_info->found_empty_plte=MagickTrue;
1064 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1065 {
1066 mng_info->found_empty_plte=MagickFalse;
1067 mng_info->have_saved_bkgd_index=MagickFalse;
1068 }
1069 }
glennrp0fe50b42010-11-16 03:52:51 +00001070
cristy3ed852e2009-09-05 21:47:34 +00001071 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1072 (data[3] == 1))
1073 {
1074 check=(png_size_t) ReadBlob(image,(size_t) length,
1075 (char *) mng_info->read_buffer);
1076 mng_info->read_buffer[4]=0;
1077 mng_info->bytes_in_read_buffer=4;
1078 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1079 if (mng_info->found_empty_plte)
1080 {
1081 /*
1082 Skip the bKGD data byte and CRC.
1083 */
1084 check=(png_size_t)
1085 ReadBlob(image,5,(char *) mng_info->read_buffer);
1086 check=(png_size_t) ReadBlob(image,(size_t) length,
1087 (char *) mng_info->read_buffer);
1088 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1089 mng_info->have_saved_bkgd_index=MagickTrue;
1090 mng_info->bytes_in_read_buffer=0;
1091 }
1092 }
1093 }
1094 }
1095}
1096#endif
1097
1098static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1099{
1100 Image
1101 *image;
1102
1103 image=(Image *) png_get_io_ptr(png_ptr);
1104 if (length)
1105 {
1106 png_size_t
1107 check;
1108
cristybb503372010-05-27 20:51:26 +00001109 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 if (check != length)
1112 png_error(png_ptr,"WriteBlob Failed");
1113 }
1114}
1115
1116static void png_flush_data(png_structp png_ptr)
1117{
1118 (void) png_ptr;
1119}
1120
1121#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1122static int PalettesAreEqual(Image *a,Image *b)
1123{
cristybb503372010-05-27 20:51:26 +00001124 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001125 i;
1126
1127 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1128 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001129
cristy3ed852e2009-09-05 21:47:34 +00001130 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1131 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001132
cristy3ed852e2009-09-05 21:47:34 +00001133 if (a->colors != b->colors)
1134 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001135
cristybb503372010-05-27 20:51:26 +00001136 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001137 {
1138 if ((a->colormap[i].red != b->colormap[i].red) ||
1139 (a->colormap[i].green != b->colormap[i].green) ||
1140 (a->colormap[i].blue != b->colormap[i].blue))
1141 return((int) MagickFalse);
1142 }
glennrp0fe50b42010-11-16 03:52:51 +00001143
cristy3ed852e2009-09-05 21:47:34 +00001144 return((int) MagickTrue);
1145}
1146#endif
1147
1148static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1149{
1150 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1151 mng_info->exists[i] && !mng_info->frozen[i])
1152 {
1153#ifdef MNG_OBJECT_BUFFERS
1154 if (mng_info->ob[i] != (MngBuffer *) NULL)
1155 {
1156 if (mng_info->ob[i]->reference_count > 0)
1157 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001158
cristy3ed852e2009-09-05 21:47:34 +00001159 if (mng_info->ob[i]->reference_count == 0)
1160 {
1161 if (mng_info->ob[i]->image != (Image *) NULL)
1162 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001163
cristy3ed852e2009-09-05 21:47:34 +00001164 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1165 }
1166 }
1167 mng_info->ob[i]=(MngBuffer *) NULL;
1168#endif
1169 mng_info->exists[i]=MagickFalse;
1170 mng_info->invisible[i]=MagickFalse;
1171 mng_info->viewable[i]=MagickFalse;
1172 mng_info->frozen[i]=MagickFalse;
1173 mng_info->x_off[i]=0;
1174 mng_info->y_off[i]=0;
1175 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001176 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001177 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001178 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001179 }
1180}
1181
glennrp21f0e622011-01-07 16:20:57 +00001182static void MngInfoFreeStruct(MngInfo *mng_info,
1183 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001184{
glennrp21f0e622011-01-07 16:20:57 +00001185 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001186 {
cristybb503372010-05-27 20:51:26 +00001187 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001188 i;
1189
1190 for (i=1; i < MNG_MAX_OBJECTS; i++)
1191 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001192
cristy3ed852e2009-09-05 21:47:34 +00001193 if (mng_info->global_plte != (png_colorp) NULL)
1194 mng_info->global_plte=(png_colorp)
1195 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001196
cristy3ed852e2009-09-05 21:47:34 +00001197 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1198 *have_mng_structure=MagickFalse;
1199 }
1200}
1201
1202static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1203{
1204 MngBox
1205 box;
1206
1207 box=box1;
1208 if (box.left < box2.left)
1209 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001210
cristy3ed852e2009-09-05 21:47:34 +00001211 if (box.top < box2.top)
1212 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001213
cristy3ed852e2009-09-05 21:47:34 +00001214 if (box.right > box2.right)
1215 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001216
cristy3ed852e2009-09-05 21:47:34 +00001217 if (box.bottom > box2.bottom)
1218 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001219
cristy3ed852e2009-09-05 21:47:34 +00001220 return box;
1221}
1222
1223static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1224{
1225 MngBox
1226 box;
1227
1228 /*
1229 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1230 */
cristybb503372010-05-27 20:51:26 +00001231 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1232 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1233 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1234 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001235 if (delta_type != 0)
1236 {
1237 box.left+=previous_box.left;
1238 box.right+=previous_box.right;
1239 box.top+=previous_box.top;
1240 box.bottom+=previous_box.bottom;
1241 }
glennrp0fe50b42010-11-16 03:52:51 +00001242
cristy3ed852e2009-09-05 21:47:34 +00001243 return(box);
1244}
1245
1246static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1247 unsigned char *p)
1248{
1249 MngPair
1250 pair;
1251 /*
cristybb503372010-05-27 20:51:26 +00001252 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001253 */
cristy8182b072010-05-30 20:10:53 +00001254 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1255 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001256
cristy3ed852e2009-09-05 21:47:34 +00001257 if (delta_type != 0)
1258 {
1259 pair.a+=previous_pair.a;
1260 pair.b+=previous_pair.b;
1261 }
glennrp0fe50b42010-11-16 03:52:51 +00001262
cristy3ed852e2009-09-05 21:47:34 +00001263 return(pair);
1264}
1265
cristy8182b072010-05-30 20:10:53 +00001266static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001267{
cristy8182b072010-05-30 20:10:53 +00001268 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001269}
1270
glennrpcf002022011-01-30 02:38:15 +00001271static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001272{
1273 Image
1274 *image;
1275
1276 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001277
cristy3ed852e2009-09-05 21:47:34 +00001278 if (image->debug != MagickFalse)
1279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1280 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001281
cristy3ed852e2009-09-05 21:47:34 +00001282 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1283 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001284
glennrpe4017e32011-01-08 17:16:09 +00001285#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001286 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1287 * are building with libpng-1.4.x and can be ignored.
1288 */
cristy3ed852e2009-09-05 21:47:34 +00001289 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001290#else
1291 png_longjmp(ping,1);
1292#endif
cristy3ed852e2009-09-05 21:47:34 +00001293}
1294
glennrpcf002022011-01-30 02:38:15 +00001295static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001296{
1297 Image
1298 *image;
1299
1300 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1301 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001302
cristy3ed852e2009-09-05 21:47:34 +00001303 image=(Image *) png_get_error_ptr(ping);
1304 if (image->debug != MagickFalse)
1305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001306 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001307
cristy3ed852e2009-09-05 21:47:34 +00001308 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1309 message,"`%s'",image->filename);
1310}
1311
1312#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001313static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001314{
1315#if (PNG_LIBPNG_VER < 10011)
1316 png_voidp
1317 ret;
1318
1319 png_ptr=png_ptr;
1320 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001321
cristy3ed852e2009-09-05 21:47:34 +00001322 if (ret == NULL)
1323 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001324
cristy3ed852e2009-09-05 21:47:34 +00001325 return(ret);
1326#else
1327 png_ptr=png_ptr;
1328 return((png_voidp) AcquireMagickMemory((size_t) size));
1329#endif
1330}
1331
1332/*
1333 Free a pointer. It is removed from the list at the same time.
1334*/
glennrpcf002022011-01-30 02:38:15 +00001335static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001336{
1337 png_ptr=png_ptr;
1338 ptr=RelinquishMagickMemory(ptr);
1339 return((png_free_ptr) NULL);
1340}
1341#endif
1342
1343#if defined(__cplusplus) || defined(c_plusplus)
1344}
1345#endif
1346
1347static int
glennrpcf002022011-01-30 02:38:15 +00001348Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001349 png_textp text,int ii)
1350{
cristybb503372010-05-27 20:51:26 +00001351 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001352 i;
1353
1354 register unsigned char
1355 *dp;
1356
1357 register png_charp
1358 sp;
1359
1360 png_uint_32
1361 length,
1362 nibbles;
1363
1364 StringInfo
1365 *profile;
1366
glennrp0c3e06b2010-11-19 13:45:02 +00001367 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001368 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1369 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1370 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1371 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1372 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1373 13,14,15};
1374
1375 sp=text[ii].text+1;
1376 /* look for newline */
1377 while (*sp != '\n')
1378 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001379
cristy3ed852e2009-09-05 21:47:34 +00001380 /* look for length */
1381 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1382 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001383
cristyf2f27272009-12-17 14:48:46 +00001384 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001385
glennrp97f90e22011-02-22 05:47:58 +00001386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1387 " length: %lu",(unsigned long) length);
1388
cristy3ed852e2009-09-05 21:47:34 +00001389 while (*sp != ' ' && *sp != '\n')
1390 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001391
cristy3ed852e2009-09-05 21:47:34 +00001392 /* allocate space */
1393 if (length == 0)
1394 {
1395 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1396 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1397 return(MagickFalse);
1398 }
glennrp0fe50b42010-11-16 03:52:51 +00001399
cristy3ed852e2009-09-05 21:47:34 +00001400 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001401
cristy3ed852e2009-09-05 21:47:34 +00001402 if (profile == (StringInfo *) NULL)
1403 {
1404 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1405 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1406 "unable to copy profile");
1407 return(MagickFalse);
1408 }
glennrp0fe50b42010-11-16 03:52:51 +00001409
cristy3ed852e2009-09-05 21:47:34 +00001410 /* copy profile, skipping white space and column 1 "=" signs */
1411 dp=GetStringInfoDatum(profile);
1412 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001413
cristybb503372010-05-27 20:51:26 +00001414 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001415 {
1416 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1417 {
1418 if (*sp == '\0')
1419 {
1420 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1421 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1422 profile=DestroyStringInfo(profile);
1423 return(MagickFalse);
1424 }
1425 sp++;
1426 }
glennrp0fe50b42010-11-16 03:52:51 +00001427
cristy3ed852e2009-09-05 21:47:34 +00001428 if (i%2 == 0)
1429 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001430
cristy3ed852e2009-09-05 21:47:34 +00001431 else
1432 (*dp++)+=unhex[(int) *sp++];
1433 }
1434 /*
1435 We have already read "Raw profile type.
1436 */
1437 (void) SetImageProfile(image,&text[ii].key[17],profile);
1438 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001439
cristy3ed852e2009-09-05 21:47:34 +00001440 if (image_info->verbose)
1441 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001442
cristy3ed852e2009-09-05 21:47:34 +00001443 return MagickTrue;
1444}
1445
1446#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1447static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1448{
1449 Image
1450 *image;
1451
1452
1453 /* The unknown chunk structure contains the chunk data:
1454 png_byte name[5];
1455 png_byte *data;
1456 png_size_t size;
1457
1458 Note that libpng has already taken care of the CRC handling.
1459 */
1460
1461
1462 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1463 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1464 return(0); /* Did not recognize */
1465
1466 /* recognized vpAg */
1467
1468 if (chunk->size != 9)
1469 return(-1); /* Error return */
1470
1471 if (chunk->data[8] != 0)
1472 return(0); /* ImageMagick requires pixel units */
1473
1474 image=(Image *) png_get_user_chunk_ptr(ping);
1475
cristybb503372010-05-27 20:51:26 +00001476 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001477 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001478
cristybb503372010-05-27 20:51:26 +00001479 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001480 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1481
1482 /* Return one of the following: */
1483 /* return(-n); chunk had an error */
1484 /* return(0); did not recognize */
1485 /* return(n); success */
1486
1487 return(1);
1488
1489}
1490#endif
1491
1492/*
1493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1494% %
1495% %
1496% %
1497% R e a d O n e P N G I m a g e %
1498% %
1499% %
1500% %
1501%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1502%
1503% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1504% (minus the 8-byte signature) and returns it. It allocates the memory
1505% necessary for the new Image structure and returns a pointer to the new
1506% image.
1507%
1508% The format of the ReadOnePNGImage method is:
1509%
1510% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1511% ExceptionInfo *exception)
1512%
1513% A description of each parameter follows:
1514%
1515% o mng_info: Specifies a pointer to a MngInfo structure.
1516%
1517% o image_info: the image info.
1518%
1519% o exception: return any errors or warnings in this structure.
1520%
1521*/
1522static Image *ReadOnePNGImage(MngInfo *mng_info,
1523 const ImageInfo *image_info, ExceptionInfo *exception)
1524{
1525 /* Read one PNG image */
1526
glennrpcc95c3f2011-04-18 16:46:48 +00001527 /* To do: Read the tIME chunk into the date:modify property */
1528 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1529
cristy3ed852e2009-09-05 21:47:34 +00001530 Image
1531 *image;
1532
1533 int
glennrp4eb39312011-03-30 21:34:55 +00001534 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001535 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001536 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001537 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001538 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001539 pass,
1540 ping_bit_depth,
1541 ping_color_type,
1542 ping_interlace_method,
1543 ping_compression_method,
1544 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001545 ping_num_trans,
1546 unit_type;
1547
1548 double
1549 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001550
glennrpa6a06632011-01-19 15:15:34 +00001551 LongPixelPacket
1552 transparent_color;
1553
cristy3ed852e2009-09-05 21:47:34 +00001554 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00001555 logging,
cristy3ed852e2009-09-05 21:47:34 +00001556 status;
1557
glennrpfaa852b2010-03-30 12:17:00 +00001558 png_bytep
1559 ping_trans_alpha;
1560
1561 png_color_16p
1562 ping_background,
1563 ping_trans_color;
1564
cristy3ed852e2009-09-05 21:47:34 +00001565 png_info
1566 *end_info,
1567 *ping_info;
1568
1569 png_struct
1570 *ping;
1571
1572 png_textp
1573 text;
1574
glennrpfaa852b2010-03-30 12:17:00 +00001575 png_uint_32
1576 ping_height,
1577 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00001578 ping_rowbytes,
1579 x_resolution,
1580 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 QuantumInfo
1583 *quantum_info;
1584
1585 unsigned char
glennrpcf002022011-01-30 02:38:15 +00001586 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001587
cristybb503372010-05-27 20:51:26 +00001588 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001589 y;
1590
1591 register unsigned char
1592 *p;
1593
1594 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00001595 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00001596
cristybb503372010-05-27 20:51:26 +00001597 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001598 i,
1599 x;
1600
1601 register PixelPacket
1602 *q;
1603
1604 size_t
glennrp39992b42010-11-14 00:03:43 +00001605 length,
cristy3ed852e2009-09-05 21:47:34 +00001606 row_offset;
1607
cristyeb3b22a2011-03-31 20:16:11 +00001608 ssize_t
1609 j;
1610
cristy3ed852e2009-09-05 21:47:34 +00001611#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1612 png_byte unused_chunks[]=
1613 {
1614 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1615 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1616 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1617 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1618 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1619 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1620 };
1621#endif
1622
1623 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001624 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00001625
1626#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001627 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001628#endif
1629
glennrp25c1e2b2010-03-25 01:39:56 +00001630#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00001631 if (image_info->verbose)
1632 printf("Your PNG library (libpng-%s) is rather old.\n",
1633 PNG_LIBPNG_VER_STRING);
1634#endif
1635
glennrp61b4c952009-11-10 20:40:41 +00001636#if (PNG_LIBPNG_VER >= 10400)
1637# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1638 if (image_info->verbose)
1639 {
1640 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1641 PNG_LIBPNG_VER_STRING);
1642 printf("Please update it.\n");
1643 }
1644# endif
1645#endif
1646
1647
cristyed552522009-10-16 14:04:35 +00001648 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00001649 image=mng_info->image;
1650
glennrpa6a06632011-01-19 15:15:34 +00001651 if (logging != MagickFalse)
1652 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1653 " image->matte=%d",(int) image->matte);
1654
glennrp0e319732011-01-25 21:53:13 +00001655 /* Set to an out-of-range color unless tRNS chunk is present */
1656 transparent_color.red=65537;
1657 transparent_color.green=65537;
1658 transparent_color.blue=65537;
1659 transparent_color.opacity=65537;
1660
glennrpcb395ac2011-03-30 19:50:23 +00001661 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00001662 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00001663 num_raw_profiles = 0;
1664
cristy3ed852e2009-09-05 21:47:34 +00001665 /*
1666 Allocate the PNG structures
1667 */
1668#ifdef PNG_USER_MEM_SUPPORTED
1669 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00001670 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1671 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00001672#else
1673 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00001674 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00001675#endif
1676 if (ping == (png_struct *) NULL)
1677 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00001678
cristy3ed852e2009-09-05 21:47:34 +00001679 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001680
cristy3ed852e2009-09-05 21:47:34 +00001681 if (ping_info == (png_info *) NULL)
1682 {
1683 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1684 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1685 }
glennrp0fe50b42010-11-16 03:52:51 +00001686
cristy3ed852e2009-09-05 21:47:34 +00001687 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001688
cristy3ed852e2009-09-05 21:47:34 +00001689 if (end_info == (png_info *) NULL)
1690 {
1691 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1692 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
glennrpcf002022011-01-30 02:38:15 +00001695 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00001696
glennrpfaa852b2010-03-30 12:17:00 +00001697 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00001698 {
1699 /*
1700 PNG image is corrupt.
1701 */
1702 png_destroy_read_struct(&ping,&ping_info,&end_info);
1703#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001704 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001705#endif
1706 if (logging != MagickFalse)
1707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1708 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00001709
cristy3ed852e2009-09-05 21:47:34 +00001710 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00001711 {
1712 InheritException(exception,&image->exception);
1713 image->columns=0;
1714 }
glennrp0fe50b42010-11-16 03:52:51 +00001715
cristy3ed852e2009-09-05 21:47:34 +00001716 return(GetFirstImageInList(image));
1717 }
1718 /*
1719 Prepare PNG for reading.
1720 */
glennrpfaa852b2010-03-30 12:17:00 +00001721
cristy3ed852e2009-09-05 21:47:34 +00001722 mng_info->image_found++;
1723 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00001724
cristy3ed852e2009-09-05 21:47:34 +00001725 if (LocaleCompare(image_info->magick,"MNG") == 0)
1726 {
1727#if defined(PNG_MNG_FEATURES_SUPPORTED)
1728 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1729 png_set_read_fn(ping,image,png_get_data);
1730#else
1731#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1732 png_permit_empty_plte(ping,MagickTrue);
1733 png_set_read_fn(ping,image,png_get_data);
1734#else
1735 mng_info->image=image;
1736 mng_info->bytes_in_read_buffer=0;
1737 mng_info->found_empty_plte=MagickFalse;
1738 mng_info->have_saved_bkgd_index=MagickFalse;
1739 png_set_read_fn(ping,mng_info,mng_get_data);
1740#endif
1741#endif
1742 }
glennrp0fe50b42010-11-16 03:52:51 +00001743
cristy3ed852e2009-09-05 21:47:34 +00001744 else
1745 png_set_read_fn(ping,image,png_get_data);
1746
1747#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1748 /* Ignore unused chunks and all unknown chunks except for vpAg */
1749 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1750 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1751 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1752 (int)sizeof(unused_chunks)/5);
1753 /* Callback for other unknown chunks */
1754 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1755#endif
1756
glennrp991e92a2010-01-28 03:09:00 +00001757#if (PNG_LIBPNG_VER < 10400)
1758# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1759 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00001760 /* Disable thread-unsafe features of pnggccrd */
1761 if (png_access_version_number() >= 10200)
1762 {
1763 png_uint_32 mmx_disable_mask=0;
1764 png_uint_32 asm_flags;
1765
1766 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1767 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1768 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1769 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1770 asm_flags=png_get_asm_flags(ping);
1771 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1772 }
glennrp991e92a2010-01-28 03:09:00 +00001773# endif
cristy3ed852e2009-09-05 21:47:34 +00001774#endif
1775
1776 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00001777
1778 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1779 &ping_bit_depth,&ping_color_type,
1780 &ping_interlace_method,&ping_compression_method,
1781 &ping_filter_method);
1782
1783 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1784 &ping_trans_color);
1785
1786 (void) png_get_bKGD(ping, ping_info, &ping_background);
1787
1788 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00001789 {
glennrpfaa852b2010-03-30 12:17:00 +00001790 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1791 {
1792 png_set_packing(ping);
1793 ping_bit_depth = 8;
1794 }
cristy3ed852e2009-09-05 21:47:34 +00001795 }
glennrpfaa852b2010-03-30 12:17:00 +00001796
1797 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00001798 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00001799 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00001800 if (logging != MagickFalse)
1801 {
1802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001803 " PNG width: %.20g, height: %.20g",
1804 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00001805
cristy3ed852e2009-09-05 21:47:34 +00001806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1807 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001808 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00001809
cristy3ed852e2009-09-05 21:47:34 +00001810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1811 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001812 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00001813
cristy3ed852e2009-09-05 21:47:34 +00001814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1815 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001816 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00001817 }
1818
glennrpfaa852b2010-03-30 12:17:00 +00001819#ifdef PNG_READ_iCCP_SUPPORTED
1820 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00001821 {
1822 int
1823 compression;
1824
glennrpe4017e32011-01-08 17:16:09 +00001825#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00001826 png_charp
glennrpe4017e32011-01-08 17:16:09 +00001827 info;
1828#else
1829 png_bytep
1830 info;
1831#endif
1832
1833 png_charp
cristy3ed852e2009-09-05 21:47:34 +00001834 name;
1835
1836 png_uint_32
1837 profile_length;
1838
1839 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1840 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00001841
cristy3ed852e2009-09-05 21:47:34 +00001842 if (profile_length != 0)
1843 {
1844 StringInfo
1845 *profile;
1846
1847 if (logging != MagickFalse)
1848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1849 " Reading PNG iCCP chunk.");
1850 profile=AcquireStringInfo(profile_length);
1851 SetStringInfoDatum(profile,(const unsigned char *) info);
1852 (void) SetImageProfile(image,"icc",profile);
1853 profile=DestroyStringInfo(profile);
1854 }
1855 }
1856#endif
1857#if defined(PNG_READ_sRGB_SUPPORTED)
1858 {
cristy3ed852e2009-09-05 21:47:34 +00001859 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00001860 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1861 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00001862
cristy3ed852e2009-09-05 21:47:34 +00001863 if (png_get_sRGB(ping,ping_info,&intent))
1864 {
glennrpcf002022011-01-30 02:38:15 +00001865 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1866 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00001867
cristy3ed852e2009-09-05 21:47:34 +00001868 if (logging != MagickFalse)
1869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00001870 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00001871 }
1872 }
1873#endif
1874 {
glennrpfaa852b2010-03-30 12:17:00 +00001875 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1876 if (mng_info->have_global_gama)
1877 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00001878
cristy3ed852e2009-09-05 21:47:34 +00001879 if (png_get_gAMA(ping,ping_info,&file_gamma))
1880 {
1881 image->gamma=(float) file_gamma;
1882 if (logging != MagickFalse)
1883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1884 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1885 }
1886 }
glennrpfaa852b2010-03-30 12:17:00 +00001887 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1888 {
1889 if (mng_info->have_global_chrm != MagickFalse)
1890 {
1891 (void) png_set_cHRM(ping,ping_info,
1892 mng_info->global_chrm.white_point.x,
1893 mng_info->global_chrm.white_point.y,
1894 mng_info->global_chrm.red_primary.x,
1895 mng_info->global_chrm.red_primary.y,
1896 mng_info->global_chrm.green_primary.x,
1897 mng_info->global_chrm.green_primary.y,
1898 mng_info->global_chrm.blue_primary.x,
1899 mng_info->global_chrm.blue_primary.y);
1900 }
1901 }
glennrp0fe50b42010-11-16 03:52:51 +00001902
glennrpfaa852b2010-03-30 12:17:00 +00001903 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00001904 {
1905 (void) png_get_cHRM(ping,ping_info,
1906 &image->chromaticity.white_point.x,
1907 &image->chromaticity.white_point.y,
1908 &image->chromaticity.red_primary.x,
1909 &image->chromaticity.red_primary.y,
1910 &image->chromaticity.green_primary.x,
1911 &image->chromaticity.green_primary.y,
1912 &image->chromaticity.blue_primary.x,
1913 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy3ed852e2009-09-05 21:47:34 +00001915 if (logging != MagickFalse)
1916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1917 " Reading PNG cHRM chunk.");
1918 }
glennrp0fe50b42010-11-16 03:52:51 +00001919
glennrpe610a072010-08-05 17:08:46 +00001920 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00001921 {
glennrpe610a072010-08-05 17:08:46 +00001922 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00001923 Magick_RenderingIntent_to_PNG_RenderingIntent
1924 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00001925 png_set_gAMA(ping,ping_info,0.45455f);
1926 png_set_cHRM(ping,ping_info,
1927 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1928 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00001929 }
cristy3ed852e2009-09-05 21:47:34 +00001930#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001931 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00001932 {
cristy905ef802011-02-23 00:29:18 +00001933 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1934 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00001935
cristy3ed852e2009-09-05 21:47:34 +00001936 if (logging != MagickFalse)
1937 if (image->page.x || image->page.y)
1938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001939 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1940 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001941 }
1942#endif
1943#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001944 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1945 {
1946 if (mng_info->have_global_phys)
1947 {
1948 png_set_pHYs(ping,ping_info,
1949 mng_info->global_x_pixels_per_unit,
1950 mng_info->global_y_pixels_per_unit,
1951 mng_info->global_phys_unit_type);
1952 }
1953 }
1954
1955 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00001956 {
cristy3ed852e2009-09-05 21:47:34 +00001957 /*
1958 Set image resolution.
1959 */
1960 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00001961 &unit_type);
1962 image->x_resolution=(double) x_resolution;
1963 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00001964
cristy3ed852e2009-09-05 21:47:34 +00001965 if (unit_type == PNG_RESOLUTION_METER)
1966 {
1967 image->units=PixelsPerCentimeterResolution;
1968 image->x_resolution=(double) x_resolution/100.0;
1969 image->y_resolution=(double) y_resolution/100.0;
1970 }
glennrp0fe50b42010-11-16 03:52:51 +00001971
cristy3ed852e2009-09-05 21:47:34 +00001972 if (logging != MagickFalse)
1973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001974 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1975 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00001976 }
cristy3ed852e2009-09-05 21:47:34 +00001977#endif
glennrp823b55c2011-03-14 18:46:46 +00001978
glennrpfaa852b2010-03-30 12:17:00 +00001979 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00001980 {
1981 int
1982 number_colors;
1983
1984 png_colorp
1985 palette;
1986
1987 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00001988
cristy3ed852e2009-09-05 21:47:34 +00001989 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00001990 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00001991 {
1992 if (mng_info->global_plte_length)
1993 {
1994 png_set_PLTE(ping,ping_info,mng_info->global_plte,
1995 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00001996
glennrpfaa852b2010-03-30 12:17:00 +00001997 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00001998 if (mng_info->global_trns_length)
1999 {
2000 if (mng_info->global_trns_length >
2001 mng_info->global_plte_length)
2002 (void) ThrowMagickException(&image->exception,
2003 GetMagickModule(),CoderError,
2004 "global tRNS has more entries than global PLTE",
2005 "`%s'",image_info->filename);
2006 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2007 (int) mng_info->global_trns_length,NULL);
2008 }
glennrpbfd9e612011-04-22 14:02:20 +00002009#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002010 if (
2011#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2012 mng_info->have_saved_bkgd_index ||
2013#endif
glennrpfaa852b2010-03-30 12:17:00 +00002014 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002015 {
2016 png_color_16
2017 background;
2018
2019#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2020 if (mng_info->have_saved_bkgd_index)
2021 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002022#endif
glennrpfaa852b2010-03-30 12:17:00 +00002023 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2024 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002025
cristy3ed852e2009-09-05 21:47:34 +00002026 background.red=(png_uint_16)
2027 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002028
cristy3ed852e2009-09-05 21:47:34 +00002029 background.green=(png_uint_16)
2030 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002031
cristy3ed852e2009-09-05 21:47:34 +00002032 background.blue=(png_uint_16)
2033 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002034
glennrpc6c391a2011-04-27 02:23:56 +00002035 background.gray=(png_uint_16)
2036 mng_info->global_plte[background.index].green;
2037
cristy3ed852e2009-09-05 21:47:34 +00002038 png_set_bKGD(ping,ping_info,&background);
2039 }
2040#endif
2041 }
2042 else
2043 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2044 CoderError,"No global PLTE in file","`%s'",
2045 image_info->filename);
2046 }
2047 }
2048
glennrpbfd9e612011-04-22 14:02:20 +00002049#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002050 if (mng_info->have_global_bkgd &&
2051 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002052 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002053
glennrpfaa852b2010-03-30 12:17:00 +00002054 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002055 {
glennrpbfd9e612011-04-22 14:02:20 +00002056 unsigned int
2057 bkgd_scale;
2058
cristy3ed852e2009-09-05 21:47:34 +00002059 /*
2060 Set image background color.
2061 */
2062 if (logging != MagickFalse)
2063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2064 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002065
glennrpbfd9e612011-04-22 14:02:20 +00002066 /* Scale background components to 16-bit, then scale
2067 * to quantum depth
2068 */
2069 if (logging != MagickFalse)
2070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2071 " raw ping_background=(%d,%d,%d).",ping_background->red,
2072 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002073
glennrpbfd9e612011-04-22 14:02:20 +00002074 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002075
glennrpbfd9e612011-04-22 14:02:20 +00002076 if (ping_bit_depth == 1)
2077 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002078
glennrpbfd9e612011-04-22 14:02:20 +00002079 else if (ping_bit_depth == 2)
2080 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002081
glennrpbfd9e612011-04-22 14:02:20 +00002082 else if (ping_bit_depth == 4)
2083 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002084
glennrpbfd9e612011-04-22 14:02:20 +00002085 if (ping_bit_depth <= 8)
2086 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002087
glennrpbfd9e612011-04-22 14:02:20 +00002088 ping_background->red *= bkgd_scale;
2089 ping_background->green *= bkgd_scale;
2090 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002091
glennrpbfd9e612011-04-22 14:02:20 +00002092 if (logging != MagickFalse)
2093 {
glennrp2cbb4482010-06-02 04:37:24 +00002094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2095 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002096
glennrp2cbb4482010-06-02 04:37:24 +00002097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2098 " ping_background=(%d,%d,%d).",ping_background->red,
2099 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002100 }
glennrp2cbb4482010-06-02 04:37:24 +00002101
glennrpbfd9e612011-04-22 14:02:20 +00002102 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002103 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002104
glennrpbfd9e612011-04-22 14:02:20 +00002105 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002106 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002107
glennrpbfd9e612011-04-22 14:02:20 +00002108 image->background_color.blue=
2109 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002110
glennrpbfd9e612011-04-22 14:02:20 +00002111 image->background_color.opacity=OpaqueOpacity;
glennrp2cbb4482010-06-02 04:37:24 +00002112
glennrpbfd9e612011-04-22 14:02:20 +00002113 if (logging != MagickFalse)
2114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2115 " image->background_color=(%.20g,%.20g,%.20g).",
2116 (double) image->background_color.red,
2117 (double) image->background_color.green,
2118 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002119 }
glennrpbfd9e612011-04-22 14:02:20 +00002120#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002121
glennrpfaa852b2010-03-30 12:17:00 +00002122 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002123 {
2124 /*
glennrpa6a06632011-01-19 15:15:34 +00002125 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002126 */
2127 int
2128 max_sample;
2129
cristy35ef8242010-06-03 16:24:13 +00002130 size_t
2131 one=1;
2132
cristy3ed852e2009-09-05 21:47:34 +00002133 if (logging != MagickFalse)
2134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2135 " Reading PNG tRNS chunk.");
2136
cristyf9cca6a2010-06-04 23:49:28 +00002137 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002138
glennrpfaa852b2010-03-30 12:17:00 +00002139 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2140 (int)ping_trans_color->gray > max_sample) ||
2141 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2142 ((int)ping_trans_color->red > max_sample ||
2143 (int)ping_trans_color->green > max_sample ||
2144 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002145 {
2146 if (logging != MagickFalse)
2147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2148 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002149 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002150 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002151 image->matte=MagickFalse;
2152 }
2153 else
2154 {
glennrpa6a06632011-01-19 15:15:34 +00002155 int
2156 scale_to_short;
2157
2158 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2159
2160 /* Scale transparent_color to short */
2161 transparent_color.red= scale_to_short*ping_trans_color->red;
2162 transparent_color.green= scale_to_short*ping_trans_color->green;
2163 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2164 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002165
glennrpfaa852b2010-03-30 12:17:00 +00002166 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002167 {
glennrp0f111982010-07-07 20:18:33 +00002168 if (logging != MagickFalse)
2169 {
2170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2171 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002172
glennrp0f111982010-07-07 20:18:33 +00002173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2174 " scaled graylevel is %d.",transparent_color.opacity);
2175 }
cristy3ed852e2009-09-05 21:47:34 +00002176 transparent_color.red=transparent_color.opacity;
2177 transparent_color.green=transparent_color.opacity;
2178 transparent_color.blue=transparent_color.opacity;
2179 }
2180 }
2181 }
2182#if defined(PNG_READ_sBIT_SUPPORTED)
2183 if (mng_info->have_global_sbit)
2184 {
glennrpfaa852b2010-03-30 12:17:00 +00002185 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002186 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2187 }
2188#endif
2189 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002190
cristy3ed852e2009-09-05 21:47:34 +00002191 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002192
2193 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2194
cristy3ed852e2009-09-05 21:47:34 +00002195 /*
2196 Initialize image structure.
2197 */
2198 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002199 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002200 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002201 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002202 if (mng_info->mng_type == 0)
2203 {
glennrpfaa852b2010-03-30 12:17:00 +00002204 mng_info->mng_width=ping_width;
2205 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002206 mng_info->frame=mng_info->image_box;
2207 mng_info->clip=mng_info->image_box;
2208 }
glennrp0fe50b42010-11-16 03:52:51 +00002209
cristy3ed852e2009-09-05 21:47:34 +00002210 else
2211 {
2212 image->page.y=mng_info->y_off[mng_info->object_id];
2213 }
glennrp0fe50b42010-11-16 03:52:51 +00002214
cristy3ed852e2009-09-05 21:47:34 +00002215 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002216 image->columns=ping_width;
2217 image->rows=ping_height;
2218 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002219 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002220 {
cristybefe4d22010-06-07 01:18:58 +00002221 size_t
2222 one;
2223
cristy3ed852e2009-09-05 21:47:34 +00002224 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002225 one=1;
2226 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002227#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2228 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002229 image->colors=256;
2230#else
2231 if (image->colors > 65536L)
2232 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002233#endif
glennrpfaa852b2010-03-30 12:17:00 +00002234 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002235 {
2236 int
2237 number_colors;
2238
2239 png_colorp
2240 palette;
2241
2242 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002243 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002244
cristy3ed852e2009-09-05 21:47:34 +00002245 if (logging != MagickFalse)
2246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2247 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2248 }
2249 }
2250
2251 if (image->storage_class == PseudoClass)
2252 {
2253 /*
2254 Initialize image colormap.
2255 */
2256 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2257 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002258
glennrpfaa852b2010-03-30 12:17:00 +00002259 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002260 {
2261 int
2262 number_colors;
2263
2264 png_colorp
2265 palette;
2266
2267 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002268
glennrp6af6cf12011-04-22 13:05:16 +00002269 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002270 {
2271 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2272 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2273 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2274 }
glennrp6af6cf12011-04-22 13:05:16 +00002275
glennrp67b9c1a2011-04-22 18:47:36 +00002276 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002277 {
2278 image->colormap[i].red=0;
2279 image->colormap[i].green=0;
2280 image->colormap[i].blue=0;
2281 }
cristy3ed852e2009-09-05 21:47:34 +00002282 }
glennrp0fe50b42010-11-16 03:52:51 +00002283
cristy3ed852e2009-09-05 21:47:34 +00002284 else
2285 {
cristybb503372010-05-27 20:51:26 +00002286 size_t
cristy3ed852e2009-09-05 21:47:34 +00002287 scale;
2288
glennrpfaa852b2010-03-30 12:17:00 +00002289 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 if (scale < 1)
2292 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002293
cristybb503372010-05-27 20:51:26 +00002294 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002295 {
2296 image->colormap[i].red=(Quantum) (i*scale);
2297 image->colormap[i].green=(Quantum) (i*scale);
2298 image->colormap[i].blue=(Quantum) (i*scale);
2299 }
2300 }
2301 }
glennrp147bc912011-03-30 18:47:21 +00002302
glennrpcb395ac2011-03-30 19:50:23 +00002303 /* Set some properties for reporting by "identify" */
2304 {
glennrp147bc912011-03-30 18:47:21 +00002305 char
2306 msg[MaxTextExtent];
2307
2308 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2309 ping_interlace_method in value */
2310
glennrp7cdb11c2011-03-31 18:17:25 +00002311 (void) FormatMagickString(msg,MaxTextExtent,
2312 "%d, %d",(int) ping_width, (int) ping_height);
2313 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002314
2315 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2316 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2317
2318 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2319 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2320
2321 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2322 (int) ping_interlace_method);
2323 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002324 }
glennrp147bc912011-03-30 18:47:21 +00002325
cristy3ed852e2009-09-05 21:47:34 +00002326 /*
2327 Read image scanlines.
2328 */
2329 if (image->delay != 0)
2330 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002331
glennrp0ca69b12010-07-26 01:57:52 +00002332 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002333 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2334 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002335 {
2336 if (logging != MagickFalse)
2337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002338 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002339 mng_info->scenes_found-1);
2340 png_destroy_read_struct(&ping,&ping_info,&end_info);
2341#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002342 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002343#endif
2344 if (logging != MagickFalse)
2345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2346 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002347
cristy3ed852e2009-09-05 21:47:34 +00002348 return(image);
2349 }
glennrp0fe50b42010-11-16 03:52:51 +00002350
cristy3ed852e2009-09-05 21:47:34 +00002351 if (logging != MagickFalse)
2352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2353 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002354
cristy3ed852e2009-09-05 21:47:34 +00002355 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002356 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2357 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002358
cristy3ed852e2009-09-05 21:47:34 +00002359 else
glennrpcf002022011-01-30 02:38:15 +00002360 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2361 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002362
glennrpcf002022011-01-30 02:38:15 +00002363 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002364 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002365
cristy3ed852e2009-09-05 21:47:34 +00002366 if (logging != MagickFalse)
2367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2368 " Converting PNG pixels to pixel packets");
2369 /*
2370 Convert PNG pixels to pixel packets.
2371 */
glennrpfaa852b2010-03-30 12:17:00 +00002372 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002373 {
2374 /*
2375 PNG image is corrupt.
2376 */
2377 png_destroy_read_struct(&ping,&ping_info,&end_info);
2378#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002379 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002380#endif
2381 if (quantum_info != (QuantumInfo *) NULL)
2382 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002383
glennrpcf002022011-01-30 02:38:15 +00002384 if (ping_pixels != (unsigned char *) NULL)
2385 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002386
cristy3ed852e2009-09-05 21:47:34 +00002387 if (logging != MagickFalse)
2388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2389 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002390
cristy3ed852e2009-09-05 21:47:34 +00002391 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002392 {
2393 InheritException(exception,&image->exception);
2394 image->columns=0;
2395 }
glennrp0fe50b42010-11-16 03:52:51 +00002396
cristy3ed852e2009-09-05 21:47:34 +00002397 return(GetFirstImageInList(image));
2398 }
glennrp0fe50b42010-11-16 03:52:51 +00002399
cristyed552522009-10-16 14:04:35 +00002400 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002401
cristyed552522009-10-16 14:04:35 +00002402 if (quantum_info == (QuantumInfo *) NULL)
2403 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002404
glennrpc8cbc5d2011-01-01 00:12:34 +00002405 {
2406
2407 MagickBooleanType
2408 found_transparent_pixel;
2409
2410 found_transparent_pixel=MagickFalse;
2411
cristy3ed852e2009-09-05 21:47:34 +00002412 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002413 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002414 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002415 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002416 /*
2417 Convert image to DirectClass pixel packets.
2418 */
glennrp67b9c1a2011-04-22 18:47:36 +00002419#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2420 int
2421 depth;
2422
2423 depth=(ssize_t) ping_bit_depth;
2424#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002425 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2426 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2427 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2428 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002429
glennrpc8cbc5d2011-01-01 00:12:34 +00002430 for (y=0; y < (ssize_t) image->rows; y++)
2431 {
2432 if (num_passes > 1)
2433 row_offset=ping_rowbytes*y;
2434
2435 else
2436 row_offset=0;
2437
glennrpcf002022011-01-30 02:38:15 +00002438 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002439 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2440
2441 if (q == (PixelPacket *) NULL)
2442 break;
2443
glennrpc8cbc5d2011-01-01 00:12:34 +00002444 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2445 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002446 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002447
2448 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2449 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002450 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002451
2452 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2453 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002454 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002455
2456 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2457 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002458 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002459
2460 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2461 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002462 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002463
glennrpc8cbc5d2011-01-01 00:12:34 +00002464 if (found_transparent_pixel == MagickFalse)
2465 {
2466 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002467 if (y== 0 && logging != MagickFalse)
2468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2469 " Looking for cheap transparent pixel");
2470
glennrpc8cbc5d2011-01-01 00:12:34 +00002471 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2472 {
glennrp5aa37f62011-01-02 03:07:57 +00002473 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2474 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
glennrp8b698592011-04-26 03:38:21 +00002475 (GetOpacityPixelComponent(q) != OpaqueOpacity))
glennrpc8cbc5d2011-01-01 00:12:34 +00002476 {
glennrpa6a06632011-01-19 15:15:34 +00002477 if (logging != MagickFalse)
2478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2479 " ...got one.");
2480
glennrpc8cbc5d2011-01-01 00:12:34 +00002481 found_transparent_pixel = MagickTrue;
2482 break;
2483 }
glennrp4f25bd02011-01-01 18:51:28 +00002484 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2485 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp8b698592011-04-26 03:38:21 +00002486 (ScaleQuantumToShort(GetRedPixelComponent(q))
2487 == transparent_color.red &&
2488 ScaleQuantumToShort(GetGreenPixelComponent(q))
2489 == transparent_color.green &&
2490 ScaleQuantumToShort(GetBluePixelComponent(q))
2491 == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002492 {
glennrpa6a06632011-01-19 15:15:34 +00002493 if (logging != MagickFalse)
2494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2495 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002496 found_transparent_pixel = MagickTrue;
2497 break;
2498 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002499 q++;
2500 }
2501 }
2502
2503 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2504 {
2505 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2506 image->rows);
2507
2508 if (status == MagickFalse)
2509 break;
2510 }
2511 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2512 break;
2513 }
2514
2515 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2516 {
2517 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002518 if (status == MagickFalse)
2519 break;
2520 }
cristy3ed852e2009-09-05 21:47:34 +00002521 }
cristy3ed852e2009-09-05 21:47:34 +00002522 }
glennrp0fe50b42010-11-16 03:52:51 +00002523
cristy3ed852e2009-09-05 21:47:34 +00002524 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002525
cristy3ed852e2009-09-05 21:47:34 +00002526 for (pass=0; pass < num_passes; pass++)
2527 {
2528 Quantum
2529 *quantum_scanline;
2530
2531 register Quantum
2532 *r;
2533
2534 /*
2535 Convert grayscale image to PseudoClass pixel packets.
2536 */
glennrpfaa852b2010-03-30 12:17:00 +00002537 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002538 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002539
cristy3ed852e2009-09-05 21:47:34 +00002540 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2541 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002542
cristy3ed852e2009-09-05 21:47:34 +00002543 if (quantum_scanline == (Quantum *) NULL)
2544 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002545
cristybb503372010-05-27 20:51:26 +00002546 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002547 {
2548 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002549 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002550
cristy3ed852e2009-09-05 21:47:34 +00002551 else
2552 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00002553
glennrpcf002022011-01-30 02:38:15 +00002554 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002555 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00002556
cristy3ed852e2009-09-05 21:47:34 +00002557 if (q == (PixelPacket *) NULL)
2558 break;
glennrp0fe50b42010-11-16 03:52:51 +00002559
cristy5c6f7892010-05-05 22:53:29 +00002560 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00002561 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00002562 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00002563
glennrpfaa852b2010-03-30 12:17:00 +00002564 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00002565 {
2566 case 1:
2567 {
cristybb503372010-05-27 20:51:26 +00002568 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002569 bit;
2570
cristybb503372010-05-27 20:51:26 +00002571 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00002572 {
2573 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002574 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002575 p++;
2576 }
glennrp0fe50b42010-11-16 03:52:51 +00002577
cristy3ed852e2009-09-05 21:47:34 +00002578 if ((image->columns % 8) != 0)
2579 {
cristybb503372010-05-27 20:51:26 +00002580 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002581 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002582 }
glennrp0fe50b42010-11-16 03:52:51 +00002583
cristy3ed852e2009-09-05 21:47:34 +00002584 break;
2585 }
glennrp47b9dd52010-11-24 18:12:06 +00002586
cristy3ed852e2009-09-05 21:47:34 +00002587 case 2:
2588 {
cristybb503372010-05-27 20:51:26 +00002589 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002590 {
glennrpa18d5bc2011-04-23 14:51:34 +00002591 *r++=(*p >> 6) & 0x03;
2592 *r++=(*p >> 4) & 0x03;
2593 *r++=(*p >> 2) & 0x03;
2594 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00002595 }
glennrp0fe50b42010-11-16 03:52:51 +00002596
cristy3ed852e2009-09-05 21:47:34 +00002597 if ((image->columns % 4) != 0)
2598 {
cristybb503372010-05-27 20:51:26 +00002599 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00002600 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00002601 }
glennrp0fe50b42010-11-16 03:52:51 +00002602
cristy3ed852e2009-09-05 21:47:34 +00002603 break;
2604 }
glennrp47b9dd52010-11-24 18:12:06 +00002605
cristy3ed852e2009-09-05 21:47:34 +00002606 case 4:
2607 {
cristybb503372010-05-27 20:51:26 +00002608 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00002609 {
glennrpa18d5bc2011-04-23 14:51:34 +00002610 *r++=(*p >> 4) & 0x0f;
2611 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00002612 }
glennrp0fe50b42010-11-16 03:52:51 +00002613
cristy3ed852e2009-09-05 21:47:34 +00002614 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00002615 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00002616
cristy3ed852e2009-09-05 21:47:34 +00002617 break;
2618 }
glennrp47b9dd52010-11-24 18:12:06 +00002619
cristy3ed852e2009-09-05 21:47:34 +00002620 case 8:
2621 {
glennrpfaa852b2010-03-30 12:17:00 +00002622 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00002623 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002624 {
glennrpa18d5bc2011-04-23 14:51:34 +00002625 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00002626 /* In image.h, OpaqueOpacity is 0
2627 * TransparentOpacity is QuantumRange
2628 * In a PNG datastream, Opaque is QuantumRange
2629 * and Transparent is 0.
2630 */
glennrp8b698592011-04-26 03:38:21 +00002631 SetOpacityPixelComponent(q,
2632 ScaleCharToQuantum((unsigned char) (255-(*p++))));
2633 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00002634 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002635 q++;
2636 }
glennrp0fe50b42010-11-16 03:52:51 +00002637
cristy3ed852e2009-09-05 21:47:34 +00002638 else
cristybb503372010-05-27 20:51:26 +00002639 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002640 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00002641
cristy3ed852e2009-09-05 21:47:34 +00002642 break;
2643 }
glennrp47b9dd52010-11-24 18:12:06 +00002644
cristy3ed852e2009-09-05 21:47:34 +00002645 case 16:
2646 {
cristybb503372010-05-27 20:51:26 +00002647 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002648 {
glennrp58f77c72011-04-23 14:09:09 +00002649#if (MAGICKCORE_QUANTUM_DEPTH == 16)
2650 size_t
2651 quantum;
2652
2653 if (image->colors > 256)
2654 *r=((*p++) << 8);
2655
2656 else
2657 *r=0;
2658
2659 quantum=(*r);
2660 quantum|=(*p++);
2661 *r=(Quantum) quantum;
2662 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00002663
2664 if (ping_color_type == 4)
2665 {
glennrp58f77c72011-04-23 14:09:09 +00002666 quantum=((*p++) << 8);
2667 quantum|=(*p++);
glennrp8b698592011-04-26 03:38:21 +00002668 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-quantum));
2669 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002670 found_transparent_pixel = MagickTrue;
2671 q++;
2672 }
2673#else
2674#if (MAGICKCORE_QUANTUM_DEPTH == 32)
2675 size_t
2676 quantum;
2677
2678 if (image->colors > 256)
2679 *r=((*p++) << 8);
2680
2681 else
2682 *r=0;
2683
2684 quantum=(*r);
2685 quantum|=(*p++);
2686 *r=quantum;
2687 r++;
2688
2689 if (ping_color_type == 4)
2690 {
glennrp8b698592011-04-26 03:38:21 +00002691 quantum=(*p << 8) | *(p+1);
2692 quantum*=65537L;
2693 SetOpacityPixelComponent(q,
2694 (Quantum) GetAlphaPixelComponent(q));
2695 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002696 found_transparent_pixel = MagickTrue;
2697 p+=2;
2698 q++;
2699 }
2700
2701#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2702 *r++=(*p++);
2703 p++; /* strip low byte */
2704
2705 if (ping_color_type == 4)
2706 {
glennrp8b698592011-04-26 03:38:21 +00002707 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-(*p++)));
2708 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp9d0ea4d2011-04-22 18:35:57 +00002709 found_transparent_pixel = MagickTrue;
2710 p++;
2711 q++;
2712 }
cristy3ed852e2009-09-05 21:47:34 +00002713#endif
glennrp58f77c72011-04-23 14:09:09 +00002714#endif
glennrpa18d5bc2011-04-23 14:51:34 +00002715 }
glennrp47b9dd52010-11-24 18:12:06 +00002716
cristy3ed852e2009-09-05 21:47:34 +00002717 break;
2718 }
glennrp47b9dd52010-11-24 18:12:06 +00002719
cristy3ed852e2009-09-05 21:47:34 +00002720 default:
2721 break;
2722 }
glennrp3faa9a32011-04-23 14:00:25 +00002723
cristy3ed852e2009-09-05 21:47:34 +00002724 /*
2725 Transfer image scanline.
2726 */
2727 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00002728
cristybb503372010-05-27 20:51:26 +00002729 for (x=0; x < (ssize_t) image->columns; x++)
cristy80ac8b92010-05-08 13:36:57 +00002730 indexes[x]=(IndexPacket) (*r++);
glennrp0fe50b42010-11-16 03:52:51 +00002731
cristy3ed852e2009-09-05 21:47:34 +00002732 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2733 break;
glennrp0fe50b42010-11-16 03:52:51 +00002734
cristy7a287bf2010-02-14 02:18:09 +00002735 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2736 {
cristycee97112010-05-28 00:44:52 +00002737 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2738 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00002739
cristy7a287bf2010-02-14 02:18:09 +00002740 if (status == MagickFalse)
2741 break;
2742 }
cristy3ed852e2009-09-05 21:47:34 +00002743 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002744
cristy7a287bf2010-02-14 02:18:09 +00002745 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002746 {
2747 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00002748
cristy3ed852e2009-09-05 21:47:34 +00002749 if (status == MagickFalse)
2750 break;
2751 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002752
cristy3ed852e2009-09-05 21:47:34 +00002753 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2754 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002755
2756 image->matte=found_transparent_pixel;
2757
2758 if (logging != MagickFalse)
2759 {
2760 if (found_transparent_pixel != MagickFalse)
2761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2762 " Found transparent pixel");
2763 else
glennrp5aa37f62011-01-02 03:07:57 +00002764 {
2765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2766 " No transparent pixel was found");
glennrpa6a06632011-01-19 15:15:34 +00002767
glennrp5aa37f62011-01-02 03:07:57 +00002768 ping_color_type&=0x03;
2769 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002770 }
2771 }
2772
cristyb32b90a2009-09-07 21:45:48 +00002773 if (quantum_info != (QuantumInfo *) NULL)
2774 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002775
cristy5c6f7892010-05-05 22:53:29 +00002776 if (image->storage_class == PseudoClass)
2777 {
cristyaeb2cbc2010-05-07 13:28:58 +00002778 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002779 matte;
2780
2781 matte=image->matte;
2782 image->matte=MagickFalse;
2783 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002784 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002785 }
glennrp47b9dd52010-11-24 18:12:06 +00002786
glennrp4eb39312011-03-30 21:34:55 +00002787 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00002788
2789 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002790 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002791 {
2792 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00002793 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002794 image->colors=2;
2795 (void) SetImageBackgroundColor(image);
2796#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002797 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002798#endif
2799 if (logging != MagickFalse)
2800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2801 " exit ReadOnePNGImage() early.");
2802 return(image);
2803 }
glennrp47b9dd52010-11-24 18:12:06 +00002804
glennrpfaa852b2010-03-30 12:17:00 +00002805 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002806 {
2807 ClassType
2808 storage_class;
2809
2810 /*
2811 Image has a transparent background.
2812 */
2813 storage_class=image->storage_class;
2814 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002815
glennrp3c218112010-11-27 15:31:26 +00002816/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00002817
glennrp0fe50b42010-11-16 03:52:51 +00002818 if (storage_class == PseudoClass)
2819 {
2820 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002821 {
glennrp0fe50b42010-11-16 03:52:51 +00002822 for (x=0; x < ping_num_trans; x++)
2823 {
2824 image->colormap[x].opacity =
2825 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2826 }
glennrpc11cf6a2010-03-20 16:46:19 +00002827 }
glennrp47b9dd52010-11-24 18:12:06 +00002828
glennrp0fe50b42010-11-16 03:52:51 +00002829 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2830 {
2831 for (x=0; x < (int) image->colors; x++)
2832 {
2833 if (ScaleQuantumToShort(image->colormap[x].red) ==
2834 transparent_color.opacity)
2835 {
2836 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2837 }
2838 }
2839 }
2840 (void) SyncImage(image);
2841 }
glennrp47b9dd52010-11-24 18:12:06 +00002842
glennrpa6a06632011-01-19 15:15:34 +00002843#if 1 /* Should have already been done above, but glennrp problem P10
2844 * needs this.
2845 */
glennrp0fe50b42010-11-16 03:52:51 +00002846 else
2847 {
2848 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002849 {
glennrp0fe50b42010-11-16 03:52:51 +00002850 image->storage_class=storage_class;
2851 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2852
2853 if (q == (PixelPacket *) NULL)
2854 break;
2855
2856 indexes=GetAuthenticIndexQueue(image);
2857
glennrpa6a06632011-01-19 15:15:34 +00002858 /* Caution: on a Q8 build, this does not distinguish between
2859 * 16-bit colors that differ only in the low byte
2860 */
glennrp0fe50b42010-11-16 03:52:51 +00002861 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2862 {
glennrp8b698592011-04-26 03:38:21 +00002863 if (ScaleQuantumToShort(GetRedPixelComponent(q))
2864 == transparent_color.red &&
2865 ScaleQuantumToShort(GetGreenPixelComponent(q))
2866 == transparent_color.green &&
2867 ScaleQuantumToShort(GetBluePixelComponent(q))
2868 == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00002869 {
glennrp8b698592011-04-26 03:38:21 +00002870 SetOpacityPixelComponent(q,TransparentOpacity);
glennrp4f25bd02011-01-01 18:51:28 +00002871 }
glennrp0fe50b42010-11-16 03:52:51 +00002872
glennrp67b9c1a2011-04-22 18:47:36 +00002873#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00002874 else
glennrp4f25bd02011-01-01 18:51:28 +00002875 {
2876 q->opacity=(Quantum) OpaqueOpacity;
2877 }
glennrpa6a06632011-01-19 15:15:34 +00002878#endif
glennrp0fe50b42010-11-16 03:52:51 +00002879
2880 q++;
2881 }
2882
2883 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2884 break;
glennrpc11cf6a2010-03-20 16:46:19 +00002885 }
glennrp0fe50b42010-11-16 03:52:51 +00002886 }
glennrpa6a06632011-01-19 15:15:34 +00002887#endif
glennrpc11cf6a2010-03-20 16:46:19 +00002888
cristy3ed852e2009-09-05 21:47:34 +00002889 image->storage_class=DirectClass;
2890 }
glennrp3c218112010-11-27 15:31:26 +00002891
cristyb40fc462010-08-08 00:49:49 +00002892 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2893 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2894 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00002895
cristyeb3b22a2011-03-31 20:16:11 +00002896 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00002897 {
2898 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00002899 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2900 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00002901 else
glennrpa0ed0092011-04-18 16:36:29 +00002902 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2903 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002904
glennrp4eb39312011-03-30 21:34:55 +00002905 if (status != MagickFalse)
2906 for (i=0; i < (ssize_t) num_text; i++)
2907 {
2908 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00002909
glennrp4eb39312011-03-30 21:34:55 +00002910 if (logging != MagickFalse)
2911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2912 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00002913
glennrp4eb39312011-03-30 21:34:55 +00002914 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00002915 {
glennrp4eb39312011-03-30 21:34:55 +00002916 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2917 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00002918 }
glennrp0fe50b42010-11-16 03:52:51 +00002919
glennrp4eb39312011-03-30 21:34:55 +00002920 else
2921 {
2922 char
2923 *value;
2924
2925 length=text[i].text_length;
2926 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2927 sizeof(*value));
2928 if (value == (char *) NULL)
2929 {
2930 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2931 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2932 image->filename);
2933 break;
2934 }
2935 *value='\0';
2936 (void) ConcatenateMagickString(value,text[i].text,length+2);
2937
2938 /* Don't save "density" or "units" property if we have a pHYs
2939 * chunk
2940 */
2941 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2942 (LocaleCompare(text[i].key,"density") != 0 &&
2943 LocaleCompare(text[i].key,"units") != 0))
2944 (void) SetImageProperty(image,text[i].key,value);
2945
2946 if (logging != MagickFalse)
2947 {
2948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2949 " length: %lu",(unsigned long) length);
2950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2951 " Keyword: %s",text[i].key);
2952 }
2953
2954 value=DestroyString(value);
2955 }
2956 }
2957 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00002958 }
glennrp3c218112010-11-27 15:31:26 +00002959
cristy3ed852e2009-09-05 21:47:34 +00002960#ifdef MNG_OBJECT_BUFFERS
2961 /*
2962 Store the object if necessary.
2963 */
2964 if (object_id && !mng_info->frozen[object_id])
2965 {
2966 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2967 {
2968 /*
2969 create a new object buffer.
2970 */
2971 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00002972 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00002973
cristy3ed852e2009-09-05 21:47:34 +00002974 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2975 {
2976 mng_info->ob[object_id]->image=(Image *) NULL;
2977 mng_info->ob[object_id]->reference_count=1;
2978 }
2979 }
glennrp47b9dd52010-11-24 18:12:06 +00002980
cristy3ed852e2009-09-05 21:47:34 +00002981 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2982 mng_info->ob[object_id]->frozen)
2983 {
2984 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2985 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2986 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2987 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00002988
cristy3ed852e2009-09-05 21:47:34 +00002989 if (mng_info->ob[object_id]->frozen)
2990 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2991 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2992 "`%s'",image->filename);
2993 }
glennrp0fe50b42010-11-16 03:52:51 +00002994
cristy3ed852e2009-09-05 21:47:34 +00002995 else
2996 {
cristy3ed852e2009-09-05 21:47:34 +00002997
2998 if (mng_info->ob[object_id]->image != (Image *) NULL)
2999 mng_info->ob[object_id]->image=DestroyImage
3000 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003001
cristy3ed852e2009-09-05 21:47:34 +00003002 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3003 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003004
cristy3ed852e2009-09-05 21:47:34 +00003005 if (mng_info->ob[object_id]->image != (Image *) NULL)
3006 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003007
cristy3ed852e2009-09-05 21:47:34 +00003008 else
3009 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3010 ResourceLimitError,"Cloning image for object buffer failed",
3011 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003012
glennrpfaa852b2010-03-30 12:17:00 +00003013 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003014 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003015
glennrpfaa852b2010-03-30 12:17:00 +00003016 mng_info->ob[object_id]->width=ping_width;
3017 mng_info->ob[object_id]->height=ping_height;
3018 mng_info->ob[object_id]->color_type=ping_color_type;
3019 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3020 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3021 mng_info->ob[object_id]->compression_method=
3022 ping_compression_method;
3023 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003024
glennrpfaa852b2010-03-30 12:17:00 +00003025 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003026 {
3027 int
3028 number_colors;
3029
3030 png_colorp
3031 plte;
3032
3033 /*
3034 Copy the PLTE to the object buffer.
3035 */
3036 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3037 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003038
cristy3ed852e2009-09-05 21:47:34 +00003039 for (i=0; i < number_colors; i++)
3040 {
3041 mng_info->ob[object_id]->plte[i]=plte[i];
3042 }
3043 }
glennrp47b9dd52010-11-24 18:12:06 +00003044
cristy3ed852e2009-09-05 21:47:34 +00003045 else
3046 mng_info->ob[object_id]->plte_length=0;
3047 }
3048 }
3049#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003050
3051 /* Set image->matte to MagickTrue if the input colortype supports
3052 * alpha or if a valid tRNS chunk is present, no matter whether there
3053 * is actual transparency present.
3054 */
3055 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3056 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3057 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3058 MagickTrue : MagickFalse;
3059
glennrpcb395ac2011-03-30 19:50:23 +00003060 /* Set more properties for identify to retrieve */
3061 {
3062 char
3063 msg[MaxTextExtent];
3064
glennrp4eb39312011-03-30 21:34:55 +00003065 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003066 {
3067 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3068 (void) FormatMagickString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003069 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003070 (void) SetImageProperty(image,"PNG:text ",msg);
3071 }
3072
3073 if (num_raw_profiles != 0)
3074 {
3075 (void) FormatMagickString(msg,MaxTextExtent,
3076 "%d were found", num_raw_profiles);
3077 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3078 }
3079
glennrpcb395ac2011-03-30 19:50:23 +00003080 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003081 {
3082 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3083 "chunk was found (see Chromaticity, above)");
3084 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3085 }
glennrpcb395ac2011-03-30 19:50:23 +00003086
3087 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003088 {
3089 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3090 "chunk was found (see Background color, above)");
3091 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3092 }
3093
3094 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3095 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003096
3097 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3098 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3099
glennrpcb395ac2011-03-30 19:50:23 +00003100 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3101 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003102
3103#if defined(PNG_sRGB_SUPPORTED)
3104 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3105 {
glennrp07523c72011-03-31 18:12:10 +00003106 (void) FormatMagickString(msg,MaxTextExtent,
3107 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003108 (int) intent);
3109 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3110 }
3111#endif
3112
3113 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3114 {
glennrp07523c72011-03-31 18:12:10 +00003115 (void) FormatMagickString(msg,MaxTextExtent,
3116 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003117 file_gamma);
3118 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3119 }
3120
3121#if defined(PNG_pHYs_SUPPORTED)
3122 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3123 {
glennrp07523c72011-03-31 18:12:10 +00003124 (void) FormatMagickString(msg,MaxTextExtent,
3125 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003126 (double) x_resolution,(double) y_resolution, unit_type);
3127 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3128 }
3129#endif
3130
3131#if defined(PNG_oFFs_SUPPORTED)
3132 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3133 {
3134 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3135 (double) image->page.x,(double) image->page.y);
3136 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3137 }
3138#endif
3139
glennrp07523c72011-03-31 18:12:10 +00003140 if ((image->page.width != 0 && image->page.width != image->columns) ||
3141 (image->page.height != 0 && image->page.height != image->rows))
3142 {
3143 (void) FormatMagickString(msg,MaxTextExtent,
3144 "width=%.20g, height=%.20g",
3145 (double) image->page.width,(double) image->page.height);
3146 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3147 }
glennrpcb395ac2011-03-30 19:50:23 +00003148 }
3149
cristy3ed852e2009-09-05 21:47:34 +00003150 /*
3151 Relinquish resources.
3152 */
3153 png_destroy_read_struct(&ping,&ping_info,&end_info);
3154
glennrpcf002022011-01-30 02:38:15 +00003155 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003156#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003157 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003158#endif
3159
3160 if (logging != MagickFalse)
3161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3162 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003163
cristy3ed852e2009-09-05 21:47:34 +00003164 return(image);
3165
3166/* end of reading one PNG image */
3167}
3168
3169static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3170{
3171 Image
3172 *image,
3173 *previous;
3174
3175 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003176 have_mng_structure,
3177 logging,
cristy3ed852e2009-09-05 21:47:34 +00003178 status;
3179
3180 MngInfo
3181 *mng_info;
3182
3183 char
3184 magic_number[MaxTextExtent];
3185
cristy3ed852e2009-09-05 21:47:34 +00003186 ssize_t
3187 count;
3188
3189 /*
3190 Open image file.
3191 */
3192 assert(image_info != (const ImageInfo *) NULL);
3193 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003194
cristy3ed852e2009-09-05 21:47:34 +00003195 if (image_info->debug != MagickFalse)
3196 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3197 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003198
cristy3ed852e2009-09-05 21:47:34 +00003199 assert(exception != (ExceptionInfo *) NULL);
3200 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003201 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003202 image=AcquireImage(image_info);
3203 mng_info=(MngInfo *) NULL;
3204 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003205
cristy3ed852e2009-09-05 21:47:34 +00003206 if (status == MagickFalse)
3207 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003208
cristy3ed852e2009-09-05 21:47:34 +00003209 /*
3210 Verify PNG signature.
3211 */
3212 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003213
glennrpdde35db2011-02-21 12:06:32 +00003214 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003215 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003216
cristy3ed852e2009-09-05 21:47:34 +00003217 /*
3218 Allocate a MngInfo structure.
3219 */
3220 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003221 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003222
cristy3ed852e2009-09-05 21:47:34 +00003223 if (mng_info == (MngInfo *) NULL)
3224 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003225
cristy3ed852e2009-09-05 21:47:34 +00003226 /*
3227 Initialize members of the MngInfo structure.
3228 */
3229 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3230 mng_info->image=image;
3231 have_mng_structure=MagickTrue;
3232
3233 previous=image;
3234 image=ReadOnePNGImage(mng_info,image_info,exception);
3235 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003236
cristy3ed852e2009-09-05 21:47:34 +00003237 if (image == (Image *) NULL)
3238 {
3239 if (previous != (Image *) NULL)
3240 {
3241 if (previous->signature != MagickSignature)
3242 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003243
cristy3ed852e2009-09-05 21:47:34 +00003244 (void) CloseBlob(previous);
3245 (void) DestroyImageList(previous);
3246 }
glennrp0fe50b42010-11-16 03:52:51 +00003247
cristy3ed852e2009-09-05 21:47:34 +00003248 if (logging != MagickFalse)
3249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3250 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003251
cristy3ed852e2009-09-05 21:47:34 +00003252 return((Image *) NULL);
3253 }
glennrp47b9dd52010-11-24 18:12:06 +00003254
cristy3ed852e2009-09-05 21:47:34 +00003255 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003256
cristy3ed852e2009-09-05 21:47:34 +00003257 if ((image->columns == 0) || (image->rows == 0))
3258 {
3259 if (logging != MagickFalse)
3260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3261 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003262
cristy3ed852e2009-09-05 21:47:34 +00003263 ThrowReaderException(CorruptImageError,"CorruptImage");
3264 }
glennrp47b9dd52010-11-24 18:12:06 +00003265
glennrp3faa9a32011-04-23 14:00:25 +00003266#if 0 /* This is probably redundant now */
cristy3ed852e2009-09-05 21:47:34 +00003267 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3268 {
3269 (void) SetImageType(image,PaletteType);
glennrp0fe50b42010-11-16 03:52:51 +00003270
cristy3ed852e2009-09-05 21:47:34 +00003271 if (image->matte != MagickFalse)
3272 {
3273 /* To do: Reduce to binary transparency */
3274 }
3275 }
glennrp3faa9a32011-04-23 14:00:25 +00003276#endif
glennrp47b9dd52010-11-24 18:12:06 +00003277
cristy3ed852e2009-09-05 21:47:34 +00003278 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3279 {
3280 (void) SetImageType(image,TrueColorType);
3281 image->matte=MagickFalse;
3282 }
glennrp0fe50b42010-11-16 03:52:51 +00003283
cristy3ed852e2009-09-05 21:47:34 +00003284 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3285 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003286
cristy3ed852e2009-09-05 21:47:34 +00003287 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3289 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3290 (double) image->page.width,(double) image->page.height,
3291 (double) image->page.x,(double) image->page.y);
3292
3293 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003295
cristy3ed852e2009-09-05 21:47:34 +00003296 return(image);
3297}
3298
3299
3300
3301#if defined(JNG_SUPPORTED)
3302/*
3303%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3304% %
3305% %
3306% %
3307% R e a d O n e J N G I m a g e %
3308% %
3309% %
3310% %
3311%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3312%
3313% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3314% (minus the 8-byte signature) and returns it. It allocates the memory
3315% necessary for the new Image structure and returns a pointer to the new
3316% image.
3317%
3318% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3319%
3320% The format of the ReadOneJNGImage method is:
3321%
3322% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3323% ExceptionInfo *exception)
3324%
3325% A description of each parameter follows:
3326%
3327% o mng_info: Specifies a pointer to a MngInfo structure.
3328%
3329% o image_info: the image info.
3330%
3331% o exception: return any errors or warnings in this structure.
3332%
3333*/
3334static Image *ReadOneJNGImage(MngInfo *mng_info,
3335 const ImageInfo *image_info, ExceptionInfo *exception)
3336{
3337 Image
3338 *alpha_image,
3339 *color_image,
3340 *image,
3341 *jng_image;
3342
3343 ImageInfo
3344 *alpha_image_info,
3345 *color_image_info;
3346
cristy4383ec82011-01-05 15:42:32 +00003347 MagickBooleanType
3348 logging;
3349
cristybb503372010-05-27 20:51:26 +00003350 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003351 y;
3352
3353 MagickBooleanType
3354 status;
3355
3356 png_uint_32
3357 jng_height,
3358 jng_width;
3359
3360 png_byte
3361 jng_color_type,
3362 jng_image_sample_depth,
3363 jng_image_compression_method,
3364 jng_image_interlace_method,
3365 jng_alpha_sample_depth,
3366 jng_alpha_compression_method,
3367 jng_alpha_filter_method,
3368 jng_alpha_interlace_method;
3369
3370 register const PixelPacket
3371 *s;
3372
cristybb503372010-05-27 20:51:26 +00003373 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003374 i,
3375 x;
3376
3377 register PixelPacket
3378 *q;
3379
3380 register unsigned char
3381 *p;
3382
3383 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003384 read_JSEP,
3385 reading_idat,
3386 skip_to_iend;
3387
cristybb503372010-05-27 20:51:26 +00003388 size_t
cristy3ed852e2009-09-05 21:47:34 +00003389 length;
3390
3391 jng_alpha_compression_method=0;
3392 jng_alpha_sample_depth=8;
3393 jng_color_type=0;
3394 jng_height=0;
3395 jng_width=0;
3396 alpha_image=(Image *) NULL;
3397 color_image=(Image *) NULL;
3398 alpha_image_info=(ImageInfo *) NULL;
3399 color_image_info=(ImageInfo *) NULL;
3400
3401 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003402 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003403
3404 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003405
cristy3ed852e2009-09-05 21:47:34 +00003406 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3407 {
3408 /*
3409 Allocate next image structure.
3410 */
3411 if (logging != MagickFalse)
3412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3413 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003414
cristy3ed852e2009-09-05 21:47:34 +00003415 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003416
cristy3ed852e2009-09-05 21:47:34 +00003417 if (GetNextImageInList(image) == (Image *) NULL)
3418 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003419
cristy3ed852e2009-09-05 21:47:34 +00003420 image=SyncNextImageInList(image);
3421 }
3422 mng_info->image=image;
3423
3424 /*
3425 Signature bytes have already been read.
3426 */
3427
3428 read_JSEP=MagickFalse;
3429 reading_idat=MagickFalse;
3430 skip_to_iend=MagickFalse;
3431 for (;;)
3432 {
3433 char
3434 type[MaxTextExtent];
3435
3436 unsigned char
3437 *chunk;
3438
3439 unsigned int
3440 count;
3441
3442 /*
3443 Read a new JNG chunk.
3444 */
3445 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3446 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003447
cristy3ed852e2009-09-05 21:47:34 +00003448 if (status == MagickFalse)
3449 break;
glennrp0fe50b42010-11-16 03:52:51 +00003450
cristy3ed852e2009-09-05 21:47:34 +00003451 type[0]='\0';
3452 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3453 length=ReadBlobMSBLong(image);
3454 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3455
3456 if (logging != MagickFalse)
3457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003458 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3459 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003460
3461 if (length > PNG_UINT_31_MAX || count == 0)
3462 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003463
cristy3ed852e2009-09-05 21:47:34 +00003464 p=NULL;
3465 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003466
cristy3ed852e2009-09-05 21:47:34 +00003467 if (length)
3468 {
3469 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003470
cristy3ed852e2009-09-05 21:47:34 +00003471 if (chunk == (unsigned char *) NULL)
3472 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003473
cristybb503372010-05-27 20:51:26 +00003474 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003475 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003476
cristy3ed852e2009-09-05 21:47:34 +00003477 p=chunk;
3478 }
glennrp47b9dd52010-11-24 18:12:06 +00003479
cristy3ed852e2009-09-05 21:47:34 +00003480 (void) ReadBlobMSBLong(image); /* read crc word */
3481
3482 if (skip_to_iend)
3483 {
3484 if (length)
3485 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003486
cristy3ed852e2009-09-05 21:47:34 +00003487 continue;
3488 }
3489
3490 if (memcmp(type,mng_JHDR,4) == 0)
3491 {
3492 if (length == 16)
3493 {
cristybb503372010-05-27 20:51:26 +00003494 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003495 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003496 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003497 (p[6] << 8) | p[7]);
3498 jng_color_type=p[8];
3499 jng_image_sample_depth=p[9];
3500 jng_image_compression_method=p[10];
3501 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003502
cristy3ed852e2009-09-05 21:47:34 +00003503 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3504 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003505
cristy3ed852e2009-09-05 21:47:34 +00003506 jng_alpha_sample_depth=p[12];
3507 jng_alpha_compression_method=p[13];
3508 jng_alpha_filter_method=p[14];
3509 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003510
cristy3ed852e2009-09-05 21:47:34 +00003511 if (logging != MagickFalse)
3512 {
3513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003514 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003515
cristy3ed852e2009-09-05 21:47:34 +00003516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003517 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003518
cristy3ed852e2009-09-05 21:47:34 +00003519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3520 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003521
cristy3ed852e2009-09-05 21:47:34 +00003522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3523 " jng_image_sample_depth: %3d",
3524 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003525
cristy3ed852e2009-09-05 21:47:34 +00003526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3527 " jng_image_compression_method:%3d",
3528 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003529
cristy3ed852e2009-09-05 21:47:34 +00003530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3531 " jng_image_interlace_method: %3d",
3532 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003533
cristy3ed852e2009-09-05 21:47:34 +00003534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3535 " jng_alpha_sample_depth: %3d",
3536 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003537
cristy3ed852e2009-09-05 21:47:34 +00003538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3539 " jng_alpha_compression_method:%3d",
3540 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003541
cristy3ed852e2009-09-05 21:47:34 +00003542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3543 " jng_alpha_filter_method: %3d",
3544 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003545
cristy3ed852e2009-09-05 21:47:34 +00003546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3547 " jng_alpha_interlace_method: %3d",
3548 jng_alpha_interlace_method);
3549 }
3550 }
glennrp47b9dd52010-11-24 18:12:06 +00003551
cristy3ed852e2009-09-05 21:47:34 +00003552 if (length)
3553 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003554
cristy3ed852e2009-09-05 21:47:34 +00003555 continue;
3556 }
3557
3558
3559 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3560 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3561 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3562 {
3563 /*
3564 o create color_image
3565 o open color_blob, attached to color_image
3566 o if (color type has alpha)
3567 open alpha_blob, attached to alpha_image
3568 */
3569
cristy73bd4a52010-10-05 11:24:23 +00003570 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003571
cristy3ed852e2009-09-05 21:47:34 +00003572 if (color_image_info == (ImageInfo *) NULL)
3573 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003574
cristy3ed852e2009-09-05 21:47:34 +00003575 GetImageInfo(color_image_info);
3576 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003577
cristy3ed852e2009-09-05 21:47:34 +00003578 if (color_image == (Image *) NULL)
3579 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3580
3581 if (logging != MagickFalse)
3582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3583 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003584
cristy3ed852e2009-09-05 21:47:34 +00003585 (void) AcquireUniqueFilename(color_image->filename);
3586 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3587 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003588
cristy3ed852e2009-09-05 21:47:34 +00003589 if (status == MagickFalse)
3590 return((Image *) NULL);
3591
3592 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3593 {
3594 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00003595 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00003596
cristy3ed852e2009-09-05 21:47:34 +00003597 if (alpha_image_info == (ImageInfo *) NULL)
3598 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003599
cristy3ed852e2009-09-05 21:47:34 +00003600 GetImageInfo(alpha_image_info);
3601 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003602
cristy3ed852e2009-09-05 21:47:34 +00003603 if (alpha_image == (Image *) NULL)
3604 {
3605 alpha_image=DestroyImage(alpha_image);
3606 ThrowReaderException(ResourceLimitError,
3607 "MemoryAllocationFailed");
3608 }
glennrp0fe50b42010-11-16 03:52:51 +00003609
cristy3ed852e2009-09-05 21:47:34 +00003610 if (logging != MagickFalse)
3611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3612 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003613
cristy3ed852e2009-09-05 21:47:34 +00003614 (void) AcquireUniqueFilename(alpha_image->filename);
3615 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3616 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003617
cristy3ed852e2009-09-05 21:47:34 +00003618 if (status == MagickFalse)
3619 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003620
cristy3ed852e2009-09-05 21:47:34 +00003621 if (jng_alpha_compression_method == 0)
3622 {
3623 unsigned char
3624 data[18];
3625
3626 if (logging != MagickFalse)
3627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3628 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003629
cristy3ed852e2009-09-05 21:47:34 +00003630 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3631 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00003632
cristy3ed852e2009-09-05 21:47:34 +00003633 (void) WriteBlobMSBULong(alpha_image,13L);
3634 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00003635 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00003636 PNGLong(data+4,jng_width);
3637 PNGLong(data+8,jng_height);
3638 data[12]=jng_alpha_sample_depth;
3639 data[13]=0; /* color_type gray */
3640 data[14]=0; /* compression method 0 */
3641 data[15]=0; /* filter_method 0 */
3642 data[16]=0; /* interlace_method 0 */
3643 (void) WriteBlob(alpha_image,17,data);
3644 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3645 }
3646 }
3647 reading_idat=MagickTrue;
3648 }
3649
3650 if (memcmp(type,mng_JDAT,4) == 0)
3651 {
glennrp47b9dd52010-11-24 18:12:06 +00003652 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003653
3654 if (logging != MagickFalse)
3655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3656 " Copying JDAT chunk data to color_blob.");
3657
3658 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003659
cristy3ed852e2009-09-05 21:47:34 +00003660 if (length)
3661 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003662
cristy3ed852e2009-09-05 21:47:34 +00003663 continue;
3664 }
3665
3666 if (memcmp(type,mng_IDAT,4) == 0)
3667 {
3668 png_byte
3669 data[5];
3670
glennrp47b9dd52010-11-24 18:12:06 +00003671 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003672
3673 if (image_info->ping == MagickFalse)
3674 {
3675 if (logging != MagickFalse)
3676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3677 " Copying IDAT chunk data to alpha_blob.");
3678
cristybb503372010-05-27 20:51:26 +00003679 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003680 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00003681 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00003682 (void) WriteBlob(alpha_image,4,data);
3683 (void) WriteBlob(alpha_image,length,chunk);
3684 (void) WriteBlobMSBULong(alpha_image,
3685 crc32(crc32(0,data,4),chunk,(uInt) length));
3686 }
glennrp0fe50b42010-11-16 03:52:51 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688 if (length)
3689 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003690
cristy3ed852e2009-09-05 21:47:34 +00003691 continue;
3692 }
3693
3694 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3695 {
glennrp47b9dd52010-11-24 18:12:06 +00003696 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003697
3698 if (image_info->ping == MagickFalse)
3699 {
3700 if (logging != MagickFalse)
3701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3702 " Copying JDAA chunk data to alpha_blob.");
3703
3704 (void) WriteBlob(alpha_image,length,chunk);
3705 }
glennrp0fe50b42010-11-16 03:52:51 +00003706
cristy3ed852e2009-09-05 21:47:34 +00003707 if (length)
3708 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003709
cristy3ed852e2009-09-05 21:47:34 +00003710 continue;
3711 }
3712
3713 if (memcmp(type,mng_JSEP,4) == 0)
3714 {
3715 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00003716
cristy3ed852e2009-09-05 21:47:34 +00003717 if (length)
3718 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003719
cristy3ed852e2009-09-05 21:47:34 +00003720 continue;
3721 }
3722
3723 if (memcmp(type,mng_bKGD,4) == 0)
3724 {
3725 if (length == 2)
3726 {
3727 image->background_color.red=ScaleCharToQuantum(p[1]);
3728 image->background_color.green=image->background_color.red;
3729 image->background_color.blue=image->background_color.red;
3730 }
glennrp0fe50b42010-11-16 03:52:51 +00003731
cristy3ed852e2009-09-05 21:47:34 +00003732 if (length == 6)
3733 {
3734 image->background_color.red=ScaleCharToQuantum(p[1]);
3735 image->background_color.green=ScaleCharToQuantum(p[3]);
3736 image->background_color.blue=ScaleCharToQuantum(p[5]);
3737 }
glennrp0fe50b42010-11-16 03:52:51 +00003738
cristy3ed852e2009-09-05 21:47:34 +00003739 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3740 continue;
3741 }
3742
3743 if (memcmp(type,mng_gAMA,4) == 0)
3744 {
3745 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00003746 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00003747
cristy3ed852e2009-09-05 21:47:34 +00003748 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3749 continue;
3750 }
3751
3752 if (memcmp(type,mng_cHRM,4) == 0)
3753 {
3754 if (length == 32)
3755 {
cristy8182b072010-05-30 20:10:53 +00003756 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3757 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3758 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3759 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3760 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3761 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3762 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3763 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003764 }
glennrp47b9dd52010-11-24 18:12:06 +00003765
cristy3ed852e2009-09-05 21:47:34 +00003766 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3767 continue;
3768 }
3769
3770 if (memcmp(type,mng_sRGB,4) == 0)
3771 {
3772 if (length == 1)
3773 {
glennrpe610a072010-08-05 17:08:46 +00003774 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00003775 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00003776 image->gamma=0.45455f;
3777 image->chromaticity.red_primary.x=0.6400f;
3778 image->chromaticity.red_primary.y=0.3300f;
3779 image->chromaticity.green_primary.x=0.3000f;
3780 image->chromaticity.green_primary.y=0.6000f;
3781 image->chromaticity.blue_primary.x=0.1500f;
3782 image->chromaticity.blue_primary.y=0.0600f;
3783 image->chromaticity.white_point.x=0.3127f;
3784 image->chromaticity.white_point.y=0.3290f;
3785 }
glennrp47b9dd52010-11-24 18:12:06 +00003786
cristy3ed852e2009-09-05 21:47:34 +00003787 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3788 continue;
3789 }
3790
3791 if (memcmp(type,mng_oFFs,4) == 0)
3792 {
3793 if (length > 8)
3794 {
glennrp5eae7602011-02-22 15:21:32 +00003795 image->page.x=(ssize_t) mng_get_long(p);
3796 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00003797
cristy3ed852e2009-09-05 21:47:34 +00003798 if ((int) p[8] != 0)
3799 {
3800 image->page.x/=10000;
3801 image->page.y/=10000;
3802 }
3803 }
glennrp47b9dd52010-11-24 18:12:06 +00003804
cristy3ed852e2009-09-05 21:47:34 +00003805 if (length)
3806 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003807
cristy3ed852e2009-09-05 21:47:34 +00003808 continue;
3809 }
3810
3811 if (memcmp(type,mng_pHYs,4) == 0)
3812 {
3813 if (length > 8)
3814 {
cristy8182b072010-05-30 20:10:53 +00003815 image->x_resolution=(double) mng_get_long(p);
3816 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003817 if ((int) p[8] == PNG_RESOLUTION_METER)
3818 {
3819 image->units=PixelsPerCentimeterResolution;
3820 image->x_resolution=image->x_resolution/100.0f;
3821 image->y_resolution=image->y_resolution/100.0f;
3822 }
3823 }
glennrp0fe50b42010-11-16 03:52:51 +00003824
cristy3ed852e2009-09-05 21:47:34 +00003825 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3826 continue;
3827 }
3828
3829#if 0
3830 if (memcmp(type,mng_iCCP,4) == 0)
3831 {
glennrpfd05d622011-02-25 04:10:33 +00003832 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00003833 if (length)
3834 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003835
cristy3ed852e2009-09-05 21:47:34 +00003836 continue;
3837 }
3838#endif
3839
3840 if (length)
3841 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3842
3843 if (memcmp(type,mng_IEND,4))
3844 continue;
glennrp0fe50b42010-11-16 03:52:51 +00003845
cristy3ed852e2009-09-05 21:47:34 +00003846 break;
3847 }
3848
3849
3850 /* IEND found */
3851
3852 /*
3853 Finish up reading image data:
3854
3855 o read main image from color_blob.
3856
3857 o close color_blob.
3858
3859 o if (color_type has alpha)
3860 if alpha_encoding is PNG
3861 read secondary image from alpha_blob via ReadPNG
3862 if alpha_encoding is JPEG
3863 read secondary image from alpha_blob via ReadJPEG
3864
3865 o close alpha_blob.
3866
3867 o copy intensity of secondary image into
3868 opacity samples of main image.
3869
3870 o destroy the secondary image.
3871 */
3872
3873 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00003874
cristy3ed852e2009-09-05 21:47:34 +00003875 if (logging != MagickFalse)
3876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3877 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003878
cristy3ed852e2009-09-05 21:47:34 +00003879 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3880 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 color_image_info->ping=MagickFalse; /* To do: avoid this */
3883 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003884
cristy3ed852e2009-09-05 21:47:34 +00003885 if (jng_image == (Image *) NULL)
3886 return((Image *) NULL);
3887
3888 (void) RelinquishUniqueFileResource(color_image->filename);
3889 color_image=DestroyImage(color_image);
3890 color_image_info=DestroyImageInfo(color_image_info);
3891
3892 if (jng_image == (Image *) NULL)
3893 return((Image *) NULL);
3894
3895 if (logging != MagickFalse)
3896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3897 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00003898
cristy3ed852e2009-09-05 21:47:34 +00003899 image->rows=jng_height;
3900 image->columns=jng_width;
3901 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00003902
cristybb503372010-05-27 20:51:26 +00003903 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003904 {
3905 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3906 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3907 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00003908
cristy3ed852e2009-09-05 21:47:34 +00003909 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3910 break;
3911 }
glennrp0fe50b42010-11-16 03:52:51 +00003912
cristy3ed852e2009-09-05 21:47:34 +00003913 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00003914
cristy3ed852e2009-09-05 21:47:34 +00003915 if (image_info->ping == MagickFalse)
3916 {
3917 if (jng_color_type >= 12)
3918 {
3919 if (jng_alpha_compression_method == 0)
3920 {
3921 png_byte
3922 data[5];
3923 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3924 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00003925 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00003926 (void) WriteBlob(alpha_image,4,data);
3927 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3928 }
glennrp0fe50b42010-11-16 03:52:51 +00003929
cristy3ed852e2009-09-05 21:47:34 +00003930 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00003931
cristy3ed852e2009-09-05 21:47:34 +00003932 if (logging != MagickFalse)
3933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3934 " Reading opacity from alpha_blob.");
3935
3936 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3937 "%s",alpha_image->filename);
3938
3939 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003940
cristy3ed852e2009-09-05 21:47:34 +00003941 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003942 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003943 {
3944 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3945 &image->exception);
3946 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003947
cristy3ed852e2009-09-05 21:47:34 +00003948 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003949 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
glennrp7c7b3152011-04-26 04:01:27 +00003950 q->opacity=(Quantum) QuantumRange-
3951 GetRedPixelComponent(s);
glennrp0fe50b42010-11-16 03:52:51 +00003952
cristy3ed852e2009-09-05 21:47:34 +00003953 else
cristybb503372010-05-27 20:51:26 +00003954 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003955 {
glennrp7c7b3152011-04-26 04:01:27 +00003956 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
3957 GetRedPixelComponent(s));
3958 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +00003959 image->matte=MagickTrue;
3960 }
glennrp0fe50b42010-11-16 03:52:51 +00003961
cristy3ed852e2009-09-05 21:47:34 +00003962 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3963 break;
3964 }
3965 (void) RelinquishUniqueFileResource(alpha_image->filename);
3966 alpha_image=DestroyImage(alpha_image);
3967 alpha_image_info=DestroyImageInfo(alpha_image_info);
3968 if (jng_image != (Image *) NULL)
3969 jng_image=DestroyImage(jng_image);
3970 }
3971 }
3972
glennrp47b9dd52010-11-24 18:12:06 +00003973 /* Read the JNG image. */
3974
cristy3ed852e2009-09-05 21:47:34 +00003975 if (mng_info->mng_type == 0)
3976 {
3977 mng_info->mng_width=jng_width;
3978 mng_info->mng_height=jng_height;
3979 }
glennrp0fe50b42010-11-16 03:52:51 +00003980
cristy3ed852e2009-09-05 21:47:34 +00003981 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003982 {
3983 image->page.width=jng_width;
3984 image->page.height=jng_height;
3985 }
3986
cristy3ed852e2009-09-05 21:47:34 +00003987 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003988 {
3989 image->page.x=mng_info->x_off[mng_info->object_id];
3990 image->page.y=mng_info->y_off[mng_info->object_id];
3991 }
3992
cristy3ed852e2009-09-05 21:47:34 +00003993 else
glennrp0fe50b42010-11-16 03:52:51 +00003994 {
3995 image->page.y=mng_info->y_off[mng_info->object_id];
3996 }
3997
cristy3ed852e2009-09-05 21:47:34 +00003998 mng_info->image_found++;
3999 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4000 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004001
cristy3ed852e2009-09-05 21:47:34 +00004002 if (logging != MagickFalse)
4003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4004 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004005
cristy3ed852e2009-09-05 21:47:34 +00004006 return(image);
4007}
4008
4009/*
4010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4011% %
4012% %
4013% %
4014% R e a d J N G I m a g e %
4015% %
4016% %
4017% %
4018%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4019%
4020% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4021% (including the 8-byte signature) and returns it. It allocates the memory
4022% necessary for the new Image structure and returns a pointer to the new
4023% image.
4024%
4025% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4026%
4027% The format of the ReadJNGImage method is:
4028%
4029% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4030% *exception)
4031%
4032% A description of each parameter follows:
4033%
4034% o image_info: the image info.
4035%
4036% o exception: return any errors or warnings in this structure.
4037%
4038*/
4039
4040static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4041{
4042 Image
4043 *image,
4044 *previous;
4045
4046 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004047 have_mng_structure,
4048 logging,
cristy3ed852e2009-09-05 21:47:34 +00004049 status;
4050
4051 MngInfo
4052 *mng_info;
4053
4054 char
4055 magic_number[MaxTextExtent];
4056
cristy3ed852e2009-09-05 21:47:34 +00004057 size_t
4058 count;
4059
4060 /*
4061 Open image file.
4062 */
4063 assert(image_info != (const ImageInfo *) NULL);
4064 assert(image_info->signature == MagickSignature);
4065 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4066 assert(exception != (ExceptionInfo *) NULL);
4067 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004068 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004069 image=AcquireImage(image_info);
4070 mng_info=(MngInfo *) NULL;
4071 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004072
cristy3ed852e2009-09-05 21:47:34 +00004073 if (status == MagickFalse)
4074 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004075
cristy3ed852e2009-09-05 21:47:34 +00004076 if (LocaleCompare(image_info->magick,"JNG") != 0)
4077 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004078
glennrp47b9dd52010-11-24 18:12:06 +00004079 /* Verify JNG signature. */
4080
cristy3ed852e2009-09-05 21:47:34 +00004081 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004082
glennrp3b8763e2011-02-21 12:08:18 +00004083 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004084 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004085
glennrp47b9dd52010-11-24 18:12:06 +00004086 /* Allocate a MngInfo structure. */
4087
cristy3ed852e2009-09-05 21:47:34 +00004088 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004089 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004090
cristy3ed852e2009-09-05 21:47:34 +00004091 if (mng_info == (MngInfo *) NULL)
4092 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004093
glennrp47b9dd52010-11-24 18:12:06 +00004094 /* Initialize members of the MngInfo structure. */
4095
cristy3ed852e2009-09-05 21:47:34 +00004096 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4097 have_mng_structure=MagickTrue;
4098
4099 mng_info->image=image;
4100 previous=image;
4101 image=ReadOneJNGImage(mng_info,image_info,exception);
4102 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004103
cristy3ed852e2009-09-05 21:47:34 +00004104 if (image == (Image *) NULL)
4105 {
4106 if (IsImageObject(previous) != MagickFalse)
4107 {
4108 (void) CloseBlob(previous);
4109 (void) DestroyImageList(previous);
4110 }
glennrp0fe50b42010-11-16 03:52:51 +00004111
cristy3ed852e2009-09-05 21:47:34 +00004112 if (logging != MagickFalse)
4113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4114 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004115
cristy3ed852e2009-09-05 21:47:34 +00004116 return((Image *) NULL);
4117 }
4118 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004119
cristy3ed852e2009-09-05 21:47:34 +00004120 if (image->columns == 0 || image->rows == 0)
4121 {
4122 if (logging != MagickFalse)
4123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4124 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004125
cristy3ed852e2009-09-05 21:47:34 +00004126 ThrowReaderException(CorruptImageError,"CorruptImage");
4127 }
glennrp0fe50b42010-11-16 03:52:51 +00004128
cristy3ed852e2009-09-05 21:47:34 +00004129 if (logging != MagickFalse)
4130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004131
cristy3ed852e2009-09-05 21:47:34 +00004132 return(image);
4133}
4134#endif
4135
4136static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4137{
4138 char
4139 page_geometry[MaxTextExtent];
4140
4141 Image
4142 *image,
4143 *previous;
4144
cristy4383ec82011-01-05 15:42:32 +00004145 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004146 logging,
4147 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004148
cristy3ed852e2009-09-05 21:47:34 +00004149 volatile int
4150 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004151 object_id,
4152 term_chunk_found,
4153 skip_to_iend;
4154
cristybb503372010-05-27 20:51:26 +00004155 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004156 image_count=0;
4157
4158 MagickBooleanType
4159 status;
4160
4161 MagickOffsetType
4162 offset;
4163
4164 MngInfo
4165 *mng_info;
4166
4167 MngBox
4168 default_fb,
4169 fb,
4170 previous_fb;
4171
4172#if defined(MNG_INSERT_LAYERS)
4173 PixelPacket
4174 mng_background_color;
4175#endif
4176
4177 register unsigned char
4178 *p;
4179
cristybb503372010-05-27 20:51:26 +00004180 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004181 i;
4182
4183 size_t
4184 count;
4185
cristybb503372010-05-27 20:51:26 +00004186 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004187 loop_level;
4188
4189 volatile short
4190 skipping_loop;
4191
4192#if defined(MNG_INSERT_LAYERS)
4193 unsigned int
4194 mandatory_back=0;
4195#endif
4196
4197 volatile unsigned int
4198#ifdef MNG_OBJECT_BUFFERS
4199 mng_background_object=0,
4200#endif
4201 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4202
cristybb503372010-05-27 20:51:26 +00004203 size_t
cristy3ed852e2009-09-05 21:47:34 +00004204 default_frame_timeout,
4205 frame_timeout,
4206#if defined(MNG_INSERT_LAYERS)
4207 image_height,
4208 image_width,
4209#endif
4210 length;
4211
glennrp38ea0832010-06-02 18:50:28 +00004212 /* These delays are all measured in image ticks_per_second,
4213 * not in MNG ticks_per_second
4214 */
cristybb503372010-05-27 20:51:26 +00004215 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004216 default_frame_delay,
4217 final_delay,
4218 final_image_delay,
4219 frame_delay,
4220#if defined(MNG_INSERT_LAYERS)
4221 insert_layers,
4222#endif
4223 mng_iterations=1,
4224 simplicity=0,
4225 subframe_height=0,
4226 subframe_width=0;
4227
4228 previous_fb.top=0;
4229 previous_fb.bottom=0;
4230 previous_fb.left=0;
4231 previous_fb.right=0;
4232 default_fb.top=0;
4233 default_fb.bottom=0;
4234 default_fb.left=0;
4235 default_fb.right=0;
4236
glennrp47b9dd52010-11-24 18:12:06 +00004237 /* Open image file. */
4238
cristy3ed852e2009-09-05 21:47:34 +00004239 assert(image_info != (const ImageInfo *) NULL);
4240 assert(image_info->signature == MagickSignature);
4241 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4242 assert(exception != (ExceptionInfo *) NULL);
4243 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004244 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004245 image=AcquireImage(image_info);
4246 mng_info=(MngInfo *) NULL;
4247 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004248
cristy3ed852e2009-09-05 21:47:34 +00004249 if (status == MagickFalse)
4250 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004251
cristy3ed852e2009-09-05 21:47:34 +00004252 first_mng_object=MagickFalse;
4253 skipping_loop=(-1);
4254 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004255
4256 /* Allocate a MngInfo structure. */
4257
cristy73bd4a52010-10-05 11:24:23 +00004258 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004259
cristy3ed852e2009-09-05 21:47:34 +00004260 if (mng_info == (MngInfo *) NULL)
4261 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004262
glennrp47b9dd52010-11-24 18:12:06 +00004263 /* Initialize members of the MngInfo structure. */
4264
cristy3ed852e2009-09-05 21:47:34 +00004265 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4266 mng_info->image=image;
4267 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004268
4269 if (LocaleCompare(image_info->magick,"MNG") == 0)
4270 {
4271 char
4272 magic_number[MaxTextExtent];
4273
glennrp47b9dd52010-11-24 18:12:06 +00004274 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004275 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4276 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4277 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004278
4279 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004280 for (i=0; i < MNG_MAX_OBJECTS; i++)
4281 {
cristybb503372010-05-27 20:51:26 +00004282 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4283 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004284 }
4285 mng_info->exists[0]=MagickTrue;
4286 }
glennrp47b9dd52010-11-24 18:12:06 +00004287
cristy3ed852e2009-09-05 21:47:34 +00004288 first_mng_object=MagickTrue;
4289 mng_type=0;
4290#if defined(MNG_INSERT_LAYERS)
4291 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4292#endif
4293 default_frame_delay=0;
4294 default_frame_timeout=0;
4295 frame_delay=0;
4296 final_delay=1;
4297 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4298 object_id=0;
4299 skip_to_iend=MagickFalse;
4300 term_chunk_found=MagickFalse;
4301 mng_info->framing_mode=1;
4302#if defined(MNG_INSERT_LAYERS)
4303 mandatory_back=MagickFalse;
4304#endif
4305#if defined(MNG_INSERT_LAYERS)
4306 mng_background_color=image->background_color;
4307#endif
4308 default_fb=mng_info->frame;
4309 previous_fb=mng_info->frame;
4310 do
4311 {
4312 char
4313 type[MaxTextExtent];
4314
4315 if (LocaleCompare(image_info->magick,"MNG") == 0)
4316 {
4317 unsigned char
4318 *chunk;
4319
4320 /*
4321 Read a new chunk.
4322 */
4323 type[0]='\0';
4324 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4325 length=ReadBlobMSBLong(image);
4326 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4327
4328 if (logging != MagickFalse)
4329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004330 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4331 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004332
4333 if (length > PNG_UINT_31_MAX)
4334 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004335
cristy3ed852e2009-09-05 21:47:34 +00004336 if (count == 0)
4337 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004338
cristy3ed852e2009-09-05 21:47:34 +00004339 p=NULL;
4340 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004341
cristy3ed852e2009-09-05 21:47:34 +00004342 if (length)
4343 {
4344 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004345
cristy3ed852e2009-09-05 21:47:34 +00004346 if (chunk == (unsigned char *) NULL)
4347 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004348
cristybb503372010-05-27 20:51:26 +00004349 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004350 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004351
cristy3ed852e2009-09-05 21:47:34 +00004352 p=chunk;
4353 }
glennrp0fe50b42010-11-16 03:52:51 +00004354
cristy3ed852e2009-09-05 21:47:34 +00004355 (void) ReadBlobMSBLong(image); /* read crc word */
4356
4357#if !defined(JNG_SUPPORTED)
4358 if (memcmp(type,mng_JHDR,4) == 0)
4359 {
4360 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004361
cristy3ed852e2009-09-05 21:47:34 +00004362 if (mng_info->jhdr_warning == 0)
4363 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4364 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004365
cristy3ed852e2009-09-05 21:47:34 +00004366 mng_info->jhdr_warning++;
4367 }
4368#endif
4369 if (memcmp(type,mng_DHDR,4) == 0)
4370 {
4371 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004372
cristy3ed852e2009-09-05 21:47:34 +00004373 if (mng_info->dhdr_warning == 0)
4374 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4375 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004376
cristy3ed852e2009-09-05 21:47:34 +00004377 mng_info->dhdr_warning++;
4378 }
4379 if (memcmp(type,mng_MEND,4) == 0)
4380 break;
glennrp47b9dd52010-11-24 18:12:06 +00004381
cristy3ed852e2009-09-05 21:47:34 +00004382 if (skip_to_iend)
4383 {
4384 if (memcmp(type,mng_IEND,4) == 0)
4385 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004386
cristy3ed852e2009-09-05 21:47:34 +00004387 if (length)
4388 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004389
cristy3ed852e2009-09-05 21:47:34 +00004390 if (logging != MagickFalse)
4391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4392 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004393
cristy3ed852e2009-09-05 21:47:34 +00004394 continue;
4395 }
glennrp0fe50b42010-11-16 03:52:51 +00004396
cristy3ed852e2009-09-05 21:47:34 +00004397 if (memcmp(type,mng_MHDR,4) == 0)
4398 {
cristybb503372010-05-27 20:51:26 +00004399 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004400 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004401
cristybb503372010-05-27 20:51:26 +00004402 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004403 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004404
cristy3ed852e2009-09-05 21:47:34 +00004405 if (logging != MagickFalse)
4406 {
4407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004408 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004410 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004411 }
glennrp0fe50b42010-11-16 03:52:51 +00004412
cristy3ed852e2009-09-05 21:47:34 +00004413 p+=8;
cristy8182b072010-05-30 20:10:53 +00004414 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004415
cristy3ed852e2009-09-05 21:47:34 +00004416 if (mng_info->ticks_per_second == 0)
4417 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004418
cristy3ed852e2009-09-05 21:47:34 +00004419 else
4420 default_frame_delay=1UL*image->ticks_per_second/
4421 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004422
cristy3ed852e2009-09-05 21:47:34 +00004423 frame_delay=default_frame_delay;
4424 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004425
cristy3ed852e2009-09-05 21:47:34 +00004426 if (length > 16)
4427 {
4428 p+=16;
cristy8182b072010-05-30 20:10:53 +00004429 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004430 }
glennrp0fe50b42010-11-16 03:52:51 +00004431
cristy3ed852e2009-09-05 21:47:34 +00004432 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004433
cristy3ed852e2009-09-05 21:47:34 +00004434 if ((simplicity != 0) && ((simplicity | 11) == 11))
4435 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004436
cristy3ed852e2009-09-05 21:47:34 +00004437 if ((simplicity != 0) && ((simplicity | 9) == 9))
4438 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004439
cristy3ed852e2009-09-05 21:47:34 +00004440#if defined(MNG_INSERT_LAYERS)
4441 if (mng_type != 3)
4442 insert_layers=MagickTrue;
4443#endif
4444 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4445 {
glennrp47b9dd52010-11-24 18:12:06 +00004446 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004447 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004448
cristy3ed852e2009-09-05 21:47:34 +00004449 if (GetNextImageInList(image) == (Image *) NULL)
4450 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004451
cristy3ed852e2009-09-05 21:47:34 +00004452 image=SyncNextImageInList(image);
4453 mng_info->image=image;
4454 }
4455
4456 if ((mng_info->mng_width > 65535L) ||
4457 (mng_info->mng_height > 65535L))
4458 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004459
cristye8c25f92010-06-03 00:53:06 +00004460 (void) FormatMagickString(page_geometry,MaxTextExtent,
4461 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004462 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004463
cristy3ed852e2009-09-05 21:47:34 +00004464 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004465 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004466 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004467 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004468 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004469
cristy3ed852e2009-09-05 21:47:34 +00004470 for (i=0; i < MNG_MAX_OBJECTS; i++)
4471 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004472
cristy3ed852e2009-09-05 21:47:34 +00004473 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4474 continue;
4475 }
4476
4477 if (memcmp(type,mng_TERM,4) == 0)
4478 {
4479 int
4480 repeat=0;
4481
4482
4483 if (length)
4484 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004485
cristy3ed852e2009-09-05 21:47:34 +00004486 if (repeat == 3)
4487 {
cristy8182b072010-05-30 20:10:53 +00004488 final_delay=(png_uint_32) mng_get_long(&p[2]);
4489 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004490
cristy3ed852e2009-09-05 21:47:34 +00004491 if (mng_iterations == PNG_UINT_31_MAX)
4492 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004493
cristy3ed852e2009-09-05 21:47:34 +00004494 image->iterations=mng_iterations;
4495 term_chunk_found=MagickTrue;
4496 }
glennrp0fe50b42010-11-16 03:52:51 +00004497
cristy3ed852e2009-09-05 21:47:34 +00004498 if (logging != MagickFalse)
4499 {
4500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4501 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004502
cristy3ed852e2009-09-05 21:47:34 +00004503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004504 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004505
cristy3ed852e2009-09-05 21:47:34 +00004506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004507 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004508 }
glennrp0fe50b42010-11-16 03:52:51 +00004509
cristy3ed852e2009-09-05 21:47:34 +00004510 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4511 continue;
4512 }
4513 if (memcmp(type,mng_DEFI,4) == 0)
4514 {
4515 if (mng_type == 3)
4516 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4517 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4518 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004519
cristy3ed852e2009-09-05 21:47:34 +00004520 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004521
cristy3ed852e2009-09-05 21:47:34 +00004522 if (mng_type == 2 && object_id != 0)
4523 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4524 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4525 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004526
cristy3ed852e2009-09-05 21:47:34 +00004527 if (object_id > MNG_MAX_OBJECTS)
4528 {
4529 /*
4530 Instead ofsuing a warning we should allocate a larger
4531 MngInfo structure and continue.
4532 */
4533 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4534 CoderError,"object id too large","`%s'",image->filename);
4535 object_id=MNG_MAX_OBJECTS;
4536 }
glennrp0fe50b42010-11-16 03:52:51 +00004537
cristy3ed852e2009-09-05 21:47:34 +00004538 if (mng_info->exists[object_id])
4539 if (mng_info->frozen[object_id])
4540 {
4541 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4542 (void) ThrowMagickException(&image->exception,
4543 GetMagickModule(),CoderError,
4544 "DEFI cannot redefine a frozen MNG object","`%s'",
4545 image->filename);
4546 continue;
4547 }
glennrp0fe50b42010-11-16 03:52:51 +00004548
cristy3ed852e2009-09-05 21:47:34 +00004549 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004550
cristy3ed852e2009-09-05 21:47:34 +00004551 if (length > 2)
4552 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004553
cristy3ed852e2009-09-05 21:47:34 +00004554 /*
4555 Extract object offset info.
4556 */
4557 if (length > 11)
4558 {
glennrp0fe50b42010-11-16 03:52:51 +00004559 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4560 (p[5] << 16) | (p[6] << 8) | p[7]);
4561
4562 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4563 (p[9] << 16) | (p[10] << 8) | p[11]);
4564
cristy3ed852e2009-09-05 21:47:34 +00004565 if (logging != MagickFalse)
4566 {
4567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004568 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004569 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004570
cristy3ed852e2009-09-05 21:47:34 +00004571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004572 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004573 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004574 }
4575 }
glennrp0fe50b42010-11-16 03:52:51 +00004576
cristy3ed852e2009-09-05 21:47:34 +00004577 /*
4578 Extract object clipping info.
4579 */
4580 if (length > 27)
4581 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4582 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004583
cristy3ed852e2009-09-05 21:47:34 +00004584 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4585 continue;
4586 }
4587 if (memcmp(type,mng_bKGD,4) == 0)
4588 {
4589 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004590
cristy3ed852e2009-09-05 21:47:34 +00004591 if (length > 5)
4592 {
4593 mng_info->mng_global_bkgd.red=
4594 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004595
cristy3ed852e2009-09-05 21:47:34 +00004596 mng_info->mng_global_bkgd.green=
4597 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004598
cristy3ed852e2009-09-05 21:47:34 +00004599 mng_info->mng_global_bkgd.blue=
4600 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004601
cristy3ed852e2009-09-05 21:47:34 +00004602 mng_info->have_global_bkgd=MagickTrue;
4603 }
glennrp0fe50b42010-11-16 03:52:51 +00004604
cristy3ed852e2009-09-05 21:47:34 +00004605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4606 continue;
4607 }
4608 if (memcmp(type,mng_BACK,4) == 0)
4609 {
4610#if defined(MNG_INSERT_LAYERS)
4611 if (length > 6)
4612 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00004613
cristy3ed852e2009-09-05 21:47:34 +00004614 else
4615 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00004616
cristy3ed852e2009-09-05 21:47:34 +00004617 if (mandatory_back && length > 5)
4618 {
4619 mng_background_color.red=
4620 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004621
cristy3ed852e2009-09-05 21:47:34 +00004622 mng_background_color.green=
4623 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004624
cristy3ed852e2009-09-05 21:47:34 +00004625 mng_background_color.blue=
4626 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004627
cristy3ed852e2009-09-05 21:47:34 +00004628 mng_background_color.opacity=OpaqueOpacity;
4629 }
glennrp0fe50b42010-11-16 03:52:51 +00004630
cristy3ed852e2009-09-05 21:47:34 +00004631#ifdef MNG_OBJECT_BUFFERS
4632 if (length > 8)
4633 mng_background_object=(p[7] << 8) | p[8];
4634#endif
4635#endif
4636 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4637 continue;
4638 }
glennrp47b9dd52010-11-24 18:12:06 +00004639
cristy3ed852e2009-09-05 21:47:34 +00004640 if (memcmp(type,mng_PLTE,4) == 0)
4641 {
glennrp47b9dd52010-11-24 18:12:06 +00004642 /* Read global PLTE. */
4643
cristy3ed852e2009-09-05 21:47:34 +00004644 if (length && (length < 769))
4645 {
4646 if (mng_info->global_plte == (png_colorp) NULL)
4647 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4648 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00004649
cristybb503372010-05-27 20:51:26 +00004650 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004651 {
4652 mng_info->global_plte[i].red=p[3*i];
4653 mng_info->global_plte[i].green=p[3*i+1];
4654 mng_info->global_plte[i].blue=p[3*i+2];
4655 }
glennrp0fe50b42010-11-16 03:52:51 +00004656
cristy35ef8242010-06-03 16:24:13 +00004657 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00004658 }
4659#ifdef MNG_LOOSE
4660 for ( ; i < 256; i++)
4661 {
4662 mng_info->global_plte[i].red=i;
4663 mng_info->global_plte[i].green=i;
4664 mng_info->global_plte[i].blue=i;
4665 }
glennrp0fe50b42010-11-16 03:52:51 +00004666
cristy3ed852e2009-09-05 21:47:34 +00004667 if (length)
4668 mng_info->global_plte_length=256;
4669#endif
4670 else
4671 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00004672
cristy3ed852e2009-09-05 21:47:34 +00004673 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4674 continue;
4675 }
glennrp47b9dd52010-11-24 18:12:06 +00004676
cristy3ed852e2009-09-05 21:47:34 +00004677 if (memcmp(type,mng_tRNS,4) == 0)
4678 {
4679 /* read global tRNS */
4680
4681 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004682 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004683 mng_info->global_trns[i]=p[i];
4684
4685#ifdef MNG_LOOSE
4686 for ( ; i < 256; i++)
4687 mng_info->global_trns[i]=255;
4688#endif
cristy12560f32010-06-03 16:51:08 +00004689 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00004690 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4691 continue;
4692 }
4693 if (memcmp(type,mng_gAMA,4) == 0)
4694 {
4695 if (length == 4)
4696 {
cristybb503372010-05-27 20:51:26 +00004697 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004698 igamma;
4699
cristy8182b072010-05-30 20:10:53 +00004700 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004701 mng_info->global_gamma=((float) igamma)*0.00001;
4702 mng_info->have_global_gama=MagickTrue;
4703 }
glennrp0fe50b42010-11-16 03:52:51 +00004704
cristy3ed852e2009-09-05 21:47:34 +00004705 else
4706 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004707
cristy3ed852e2009-09-05 21:47:34 +00004708 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4709 continue;
4710 }
4711
4712 if (memcmp(type,mng_cHRM,4) == 0)
4713 {
glennrp47b9dd52010-11-24 18:12:06 +00004714 /* Read global cHRM */
4715
cristy3ed852e2009-09-05 21:47:34 +00004716 if (length == 32)
4717 {
cristy8182b072010-05-30 20:10:53 +00004718 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4719 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4720 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004721 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004722 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004723 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004724 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004725 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004726 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004727 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004728 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004729 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004730 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004731 mng_info->have_global_chrm=MagickTrue;
4732 }
4733 else
4734 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004735
cristy3ed852e2009-09-05 21:47:34 +00004736 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4737 continue;
4738 }
glennrp47b9dd52010-11-24 18:12:06 +00004739
cristy3ed852e2009-09-05 21:47:34 +00004740 if (memcmp(type,mng_sRGB,4) == 0)
4741 {
4742 /*
4743 Read global sRGB.
4744 */
4745 if (length)
4746 {
glennrpe610a072010-08-05 17:08:46 +00004747 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00004748 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004749 mng_info->have_global_srgb=MagickTrue;
4750 }
4751 else
4752 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004753
cristy3ed852e2009-09-05 21:47:34 +00004754 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4755 continue;
4756 }
glennrp47b9dd52010-11-24 18:12:06 +00004757
cristy3ed852e2009-09-05 21:47:34 +00004758 if (memcmp(type,mng_iCCP,4) == 0)
4759 {
glennrpfd05d622011-02-25 04:10:33 +00004760 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004761
4762 /*
4763 Read global iCCP.
4764 */
4765 if (length)
4766 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004767
cristy3ed852e2009-09-05 21:47:34 +00004768 continue;
4769 }
glennrp47b9dd52010-11-24 18:12:06 +00004770
cristy3ed852e2009-09-05 21:47:34 +00004771 if (memcmp(type,mng_FRAM,4) == 0)
4772 {
4773 if (mng_type == 3)
4774 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4775 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4776 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004777
cristy3ed852e2009-09-05 21:47:34 +00004778 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4779 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004780
cristy3ed852e2009-09-05 21:47:34 +00004781 frame_delay=default_frame_delay;
4782 frame_timeout=default_frame_timeout;
4783 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00004784
cristy3ed852e2009-09-05 21:47:34 +00004785 if (length)
4786 if (p[0])
4787 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004788
cristy3ed852e2009-09-05 21:47:34 +00004789 if (logging != MagickFalse)
4790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4791 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00004792
cristy3ed852e2009-09-05 21:47:34 +00004793 if (length > 6)
4794 {
glennrp47b9dd52010-11-24 18:12:06 +00004795 /* Note the delay and frame clipping boundaries. */
4796
cristy3ed852e2009-09-05 21:47:34 +00004797 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00004798
cristybb503372010-05-27 20:51:26 +00004799 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004800 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00004801
cristy3ed852e2009-09-05 21:47:34 +00004802 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00004803
cristybb503372010-05-27 20:51:26 +00004804 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004805 {
4806 int
4807 change_delay,
4808 change_timeout,
4809 change_clipping;
4810
4811 change_delay=(*p++);
4812 change_timeout=(*p++);
4813 change_clipping=(*p++);
4814 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00004815
cristy3ed852e2009-09-05 21:47:34 +00004816 if (change_delay)
4817 {
cristy8182b072010-05-30 20:10:53 +00004818 frame_delay=1UL*image->ticks_per_second*
4819 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004820
cristy8182b072010-05-30 20:10:53 +00004821 if (mng_info->ticks_per_second != 0)
4822 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004823
glennrpbb010dd2010-06-01 13:07:15 +00004824 else
4825 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 if (change_delay == 2)
4828 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004829
cristy3ed852e2009-09-05 21:47:34 +00004830 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004831
cristy3ed852e2009-09-05 21:47:34 +00004832 if (logging != MagickFalse)
4833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004834 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004835 }
glennrp47b9dd52010-11-24 18:12:06 +00004836
cristy3ed852e2009-09-05 21:47:34 +00004837 if (change_timeout)
4838 {
glennrpbb010dd2010-06-01 13:07:15 +00004839 frame_timeout=1UL*image->ticks_per_second*
4840 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004841
glennrpbb010dd2010-06-01 13:07:15 +00004842 if (mng_info->ticks_per_second != 0)
4843 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004844
glennrpbb010dd2010-06-01 13:07:15 +00004845 else
4846 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 if (change_delay == 2)
4849 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00004850
cristy3ed852e2009-09-05 21:47:34 +00004851 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004852
cristy3ed852e2009-09-05 21:47:34 +00004853 if (logging != MagickFalse)
4854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004855 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004856 }
glennrp47b9dd52010-11-24 18:12:06 +00004857
cristy3ed852e2009-09-05 21:47:34 +00004858 if (change_clipping)
4859 {
4860 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4861 p+=17;
4862 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 if (logging != MagickFalse)
4865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004866 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004867 (double) fb.left,(double) fb.right,(double) fb.top,
4868 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00004869
cristy3ed852e2009-09-05 21:47:34 +00004870 if (change_clipping == 2)
4871 default_fb=fb;
4872 }
4873 }
4874 }
4875 mng_info->clip=fb;
4876 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristybb503372010-05-27 20:51:26 +00004878 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004879 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00004880
cristybb503372010-05-27 20:51:26 +00004881 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004882 -mng_info->clip.top);
4883 /*
4884 Insert a background layer behind the frame if framing_mode is 4.
4885 */
4886#if defined(MNG_INSERT_LAYERS)
4887 if (logging != MagickFalse)
4888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004889 " subframe_width=%.20g, subframe_height=%.20g",(double)
4890 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00004891
cristy3ed852e2009-09-05 21:47:34 +00004892 if (insert_layers && (mng_info->framing_mode == 4) &&
4893 (subframe_width) && (subframe_height))
4894 {
glennrp47b9dd52010-11-24 18:12:06 +00004895 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004896 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4897 {
4898 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00004899
cristy3ed852e2009-09-05 21:47:34 +00004900 if (GetNextImageInList(image) == (Image *) NULL)
4901 {
4902 image=DestroyImageList(image);
4903 MngInfoFreeStruct(mng_info,&have_mng_structure);
4904 return((Image *) NULL);
4905 }
glennrp47b9dd52010-11-24 18:12:06 +00004906
cristy3ed852e2009-09-05 21:47:34 +00004907 image=SyncNextImageInList(image);
4908 }
glennrp0fe50b42010-11-16 03:52:51 +00004909
cristy3ed852e2009-09-05 21:47:34 +00004910 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00004911
cristy3ed852e2009-09-05 21:47:34 +00004912 if (term_chunk_found)
4913 {
4914 image->start_loop=MagickTrue;
4915 image->iterations=mng_iterations;
4916 term_chunk_found=MagickFalse;
4917 }
glennrp0fe50b42010-11-16 03:52:51 +00004918
cristy3ed852e2009-09-05 21:47:34 +00004919 else
4920 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004921
cristy3ed852e2009-09-05 21:47:34 +00004922 image->columns=subframe_width;
4923 image->rows=subframe_height;
4924 image->page.width=subframe_width;
4925 image->page.height=subframe_height;
4926 image->page.x=mng_info->clip.left;
4927 image->page.y=mng_info->clip.top;
4928 image->background_color=mng_background_color;
4929 image->matte=MagickFalse;
4930 image->delay=0;
4931 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00004932
cristy3ed852e2009-09-05 21:47:34 +00004933 if (logging != MagickFalse)
4934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004935 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004936 (double) mng_info->clip.left,(double) mng_info->clip.right,
4937 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004938 }
4939#endif
4940 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4941 continue;
4942 }
4943 if (memcmp(type,mng_CLIP,4) == 0)
4944 {
4945 unsigned int
4946 first_object,
4947 last_object;
4948
4949 /*
4950 Read CLIP.
4951 */
4952 first_object=(p[0] << 8) | p[1];
4953 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00004954
cristy3ed852e2009-09-05 21:47:34 +00004955 for (i=(int) first_object; i <= (int) last_object; i++)
4956 {
4957 if (mng_info->exists[i] && !mng_info->frozen[i])
4958 {
4959 MngBox
4960 box;
4961
4962 box=mng_info->object_clip[i];
4963 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4964 }
4965 }
glennrp47b9dd52010-11-24 18:12:06 +00004966
cristy3ed852e2009-09-05 21:47:34 +00004967 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4968 continue;
4969 }
4970 if (memcmp(type,mng_SAVE,4) == 0)
4971 {
4972 for (i=1; i < MNG_MAX_OBJECTS; i++)
4973 if (mng_info->exists[i])
4974 {
4975 mng_info->frozen[i]=MagickTrue;
4976#ifdef MNG_OBJECT_BUFFERS
4977 if (mng_info->ob[i] != (MngBuffer *) NULL)
4978 mng_info->ob[i]->frozen=MagickTrue;
4979#endif
4980 }
glennrp0fe50b42010-11-16 03:52:51 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 if (length)
4983 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004984
cristy3ed852e2009-09-05 21:47:34 +00004985 continue;
4986 }
4987
4988 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4989 {
glennrp47b9dd52010-11-24 18:12:06 +00004990 /* Read DISC or SEEK. */
4991
cristy3ed852e2009-09-05 21:47:34 +00004992 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4993 {
4994 for (i=1; i < MNG_MAX_OBJECTS; i++)
4995 MngInfoDiscardObject(mng_info,i);
4996 }
glennrp0fe50b42010-11-16 03:52:51 +00004997
cristy3ed852e2009-09-05 21:47:34 +00004998 else
4999 {
cristybb503372010-05-27 20:51:26 +00005000 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005001 j;
5002
cristybb503372010-05-27 20:51:26 +00005003 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005004 {
5005 i=p[j] << 8 | p[j+1];
5006 MngInfoDiscardObject(mng_info,i);
5007 }
5008 }
glennrp0fe50b42010-11-16 03:52:51 +00005009
cristy3ed852e2009-09-05 21:47:34 +00005010 if (length)
5011 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005012
cristy3ed852e2009-09-05 21:47:34 +00005013 continue;
5014 }
glennrp47b9dd52010-11-24 18:12:06 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016 if (memcmp(type,mng_MOVE,4) == 0)
5017 {
cristybb503372010-05-27 20:51:26 +00005018 size_t
cristy3ed852e2009-09-05 21:47:34 +00005019 first_object,
5020 last_object;
5021
glennrp47b9dd52010-11-24 18:12:06 +00005022 /* read MOVE */
5023
cristy3ed852e2009-09-05 21:47:34 +00005024 first_object=(p[0] << 8) | p[1];
5025 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005026 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005027 {
5028 if (mng_info->exists[i] && !mng_info->frozen[i])
5029 {
5030 MngPair
5031 new_pair;
5032
5033 MngPair
5034 old_pair;
5035
5036 old_pair.a=mng_info->x_off[i];
5037 old_pair.b=mng_info->y_off[i];
5038 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5039 mng_info->x_off[i]=new_pair.a;
5040 mng_info->y_off[i]=new_pair.b;
5041 }
5042 }
glennrp47b9dd52010-11-24 18:12:06 +00005043
cristy3ed852e2009-09-05 21:47:34 +00005044 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5045 continue;
5046 }
5047
5048 if (memcmp(type,mng_LOOP,4) == 0)
5049 {
cristybb503372010-05-27 20:51:26 +00005050 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005051 loop_level=chunk[0];
5052 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005053
5054 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005055 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005056
cristy3ed852e2009-09-05 21:47:34 +00005057 if (logging != MagickFalse)
5058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005059 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5060 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005061
cristy3ed852e2009-09-05 21:47:34 +00005062 if (loop_iters == 0)
5063 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005064
cristy3ed852e2009-09-05 21:47:34 +00005065 else
5066 {
5067 mng_info->loop_jump[loop_level]=TellBlob(image);
5068 mng_info->loop_count[loop_level]=loop_iters;
5069 }
glennrp0fe50b42010-11-16 03:52:51 +00005070
cristy3ed852e2009-09-05 21:47:34 +00005071 mng_info->loop_iteration[loop_level]=0;
5072 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5073 continue;
5074 }
glennrp47b9dd52010-11-24 18:12:06 +00005075
cristy3ed852e2009-09-05 21:47:34 +00005076 if (memcmp(type,mng_ENDL,4) == 0)
5077 {
5078 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005079
cristy3ed852e2009-09-05 21:47:34 +00005080 if (skipping_loop > 0)
5081 {
5082 if (skipping_loop == loop_level)
5083 {
5084 /*
5085 Found end of zero-iteration loop.
5086 */
5087 skipping_loop=(-1);
5088 mng_info->loop_active[loop_level]=0;
5089 }
5090 }
glennrp47b9dd52010-11-24 18:12:06 +00005091
cristy3ed852e2009-09-05 21:47:34 +00005092 else
5093 {
5094 if (mng_info->loop_active[loop_level] == 1)
5095 {
5096 mng_info->loop_count[loop_level]--;
5097 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005098
cristy3ed852e2009-09-05 21:47:34 +00005099 if (logging != MagickFalse)
5100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005101 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005102 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005103 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005104
cristy3ed852e2009-09-05 21:47:34 +00005105 if (mng_info->loop_count[loop_level] != 0)
5106 {
5107 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5108 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005109
cristy3ed852e2009-09-05 21:47:34 +00005110 if (offset < 0)
5111 ThrowReaderException(CorruptImageError,
5112 "ImproperImageHeader");
5113 }
glennrp47b9dd52010-11-24 18:12:06 +00005114
cristy3ed852e2009-09-05 21:47:34 +00005115 else
5116 {
5117 short
5118 last_level;
5119
5120 /*
5121 Finished loop.
5122 */
5123 mng_info->loop_active[loop_level]=0;
5124 last_level=(-1);
5125 for (i=0; i < loop_level; i++)
5126 if (mng_info->loop_active[i] == 1)
5127 last_level=(short) i;
5128 loop_level=last_level;
5129 }
5130 }
5131 }
glennrp47b9dd52010-11-24 18:12:06 +00005132
cristy3ed852e2009-09-05 21:47:34 +00005133 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5134 continue;
5135 }
glennrp47b9dd52010-11-24 18:12:06 +00005136
cristy3ed852e2009-09-05 21:47:34 +00005137 if (memcmp(type,mng_CLON,4) == 0)
5138 {
5139 if (mng_info->clon_warning == 0)
5140 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5141 CoderError,"CLON is not implemented yet","`%s'",
5142 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005143
cristy3ed852e2009-09-05 21:47:34 +00005144 mng_info->clon_warning++;
5145 }
glennrp47b9dd52010-11-24 18:12:06 +00005146
cristy3ed852e2009-09-05 21:47:34 +00005147 if (memcmp(type,mng_MAGN,4) == 0)
5148 {
5149 png_uint_16
5150 magn_first,
5151 magn_last,
5152 magn_mb,
5153 magn_ml,
5154 magn_mr,
5155 magn_mt,
5156 magn_mx,
5157 magn_my,
5158 magn_methx,
5159 magn_methy;
5160
5161 if (length > 1)
5162 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005163
cristy3ed852e2009-09-05 21:47:34 +00005164 else
5165 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005166
cristy3ed852e2009-09-05 21:47:34 +00005167 if (length > 3)
5168 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005169
cristy3ed852e2009-09-05 21:47:34 +00005170 else
5171 magn_last=magn_first;
5172#ifndef MNG_OBJECT_BUFFERS
5173 if (magn_first || magn_last)
5174 if (mng_info->magn_warning == 0)
5175 {
5176 (void) ThrowMagickException(&image->exception,
5177 GetMagickModule(),CoderError,
5178 "MAGN is not implemented yet for nonzero objects",
5179 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005180
cristy3ed852e2009-09-05 21:47:34 +00005181 mng_info->magn_warning++;
5182 }
5183#endif
5184 if (length > 4)
5185 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005186
cristy3ed852e2009-09-05 21:47:34 +00005187 else
5188 magn_methx=0;
5189
5190 if (length > 6)
5191 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005192
cristy3ed852e2009-09-05 21:47:34 +00005193 else
5194 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005195
cristy3ed852e2009-09-05 21:47:34 +00005196 if (magn_mx == 0)
5197 magn_mx=1;
5198
5199 if (length > 8)
5200 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005201
cristy3ed852e2009-09-05 21:47:34 +00005202 else
5203 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005204
cristy3ed852e2009-09-05 21:47:34 +00005205 if (magn_my == 0)
5206 magn_my=1;
5207
5208 if (length > 10)
5209 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005210
cristy3ed852e2009-09-05 21:47:34 +00005211 else
5212 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 if (magn_ml == 0)
5215 magn_ml=1;
5216
5217 if (length > 12)
5218 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005219
cristy3ed852e2009-09-05 21:47:34 +00005220 else
5221 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005222
cristy3ed852e2009-09-05 21:47:34 +00005223 if (magn_mr == 0)
5224 magn_mr=1;
5225
5226 if (length > 14)
5227 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005228
cristy3ed852e2009-09-05 21:47:34 +00005229 else
5230 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005231
cristy3ed852e2009-09-05 21:47:34 +00005232 if (magn_mt == 0)
5233 magn_mt=1;
5234
5235 if (length > 16)
5236 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005237
cristy3ed852e2009-09-05 21:47:34 +00005238 else
5239 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005240
cristy3ed852e2009-09-05 21:47:34 +00005241 if (magn_mb == 0)
5242 magn_mb=1;
5243
5244 if (length > 17)
5245 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005246
cristy3ed852e2009-09-05 21:47:34 +00005247 else
5248 magn_methy=magn_methx;
5249
glennrp47b9dd52010-11-24 18:12:06 +00005250
cristy3ed852e2009-09-05 21:47:34 +00005251 if (magn_methx > 5 || magn_methy > 5)
5252 if (mng_info->magn_warning == 0)
5253 {
5254 (void) ThrowMagickException(&image->exception,
5255 GetMagickModule(),CoderError,
5256 "Unknown MAGN method in MNG datastream","`%s'",
5257 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005258
cristy3ed852e2009-09-05 21:47:34 +00005259 mng_info->magn_warning++;
5260 }
5261#ifdef MNG_OBJECT_BUFFERS
5262 /* Magnify existing objects in the range magn_first to magn_last */
5263#endif
5264 if (magn_first == 0 || magn_last == 0)
5265 {
5266 /* Save the magnification factors for object 0 */
5267 mng_info->magn_mb=magn_mb;
5268 mng_info->magn_ml=magn_ml;
5269 mng_info->magn_mr=magn_mr;
5270 mng_info->magn_mt=magn_mt;
5271 mng_info->magn_mx=magn_mx;
5272 mng_info->magn_my=magn_my;
5273 mng_info->magn_methx=magn_methx;
5274 mng_info->magn_methy=magn_methy;
5275 }
5276 }
glennrp47b9dd52010-11-24 18:12:06 +00005277
cristy3ed852e2009-09-05 21:47:34 +00005278 if (memcmp(type,mng_PAST,4) == 0)
5279 {
5280 if (mng_info->past_warning == 0)
5281 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5282 CoderError,"PAST is not implemented yet","`%s'",
5283 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005284
cristy3ed852e2009-09-05 21:47:34 +00005285 mng_info->past_warning++;
5286 }
glennrp47b9dd52010-11-24 18:12:06 +00005287
cristy3ed852e2009-09-05 21:47:34 +00005288 if (memcmp(type,mng_SHOW,4) == 0)
5289 {
5290 if (mng_info->show_warning == 0)
5291 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5292 CoderError,"SHOW is not implemented yet","`%s'",
5293 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005294
cristy3ed852e2009-09-05 21:47:34 +00005295 mng_info->show_warning++;
5296 }
glennrp47b9dd52010-11-24 18:12:06 +00005297
cristy3ed852e2009-09-05 21:47:34 +00005298 if (memcmp(type,mng_sBIT,4) == 0)
5299 {
5300 if (length < 4)
5301 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005302
cristy3ed852e2009-09-05 21:47:34 +00005303 else
5304 {
5305 mng_info->global_sbit.gray=p[0];
5306 mng_info->global_sbit.red=p[0];
5307 mng_info->global_sbit.green=p[1];
5308 mng_info->global_sbit.blue=p[2];
5309 mng_info->global_sbit.alpha=p[3];
5310 mng_info->have_global_sbit=MagickTrue;
5311 }
5312 }
5313 if (memcmp(type,mng_pHYs,4) == 0)
5314 {
5315 if (length > 8)
5316 {
5317 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005318 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005319 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005320 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005321 mng_info->global_phys_unit_type=p[8];
5322 mng_info->have_global_phys=MagickTrue;
5323 }
glennrp47b9dd52010-11-24 18:12:06 +00005324
cristy3ed852e2009-09-05 21:47:34 +00005325 else
5326 mng_info->have_global_phys=MagickFalse;
5327 }
5328 if (memcmp(type,mng_pHYg,4) == 0)
5329 {
5330 if (mng_info->phyg_warning == 0)
5331 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5332 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005333
cristy3ed852e2009-09-05 21:47:34 +00005334 mng_info->phyg_warning++;
5335 }
5336 if (memcmp(type,mng_BASI,4) == 0)
5337 {
5338 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005339
cristy3ed852e2009-09-05 21:47:34 +00005340 if (mng_info->basi_warning == 0)
5341 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5342 CoderError,"BASI is not implemented yet","`%s'",
5343 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005344
cristy3ed852e2009-09-05 21:47:34 +00005345 mng_info->basi_warning++;
5346#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005347 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005348 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005349 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005350 (p[6] << 8) | p[7]);
5351 basi_color_type=p[8];
5352 basi_compression_method=p[9];
5353 basi_filter_type=p[10];
5354 basi_interlace_method=p[11];
5355 if (length > 11)
5356 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005357
cristy3ed852e2009-09-05 21:47:34 +00005358 else
5359 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005360
cristy3ed852e2009-09-05 21:47:34 +00005361 if (length > 13)
5362 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 else
5365 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005366
cristy3ed852e2009-09-05 21:47:34 +00005367 if (length > 15)
5368 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005369
cristy3ed852e2009-09-05 21:47:34 +00005370 else
5371 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005372
cristy3ed852e2009-09-05 21:47:34 +00005373 if (length > 17)
5374 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005375
cristy3ed852e2009-09-05 21:47:34 +00005376 else
5377 {
5378 if (basi_sample_depth == 16)
5379 basi_alpha=65535L;
5380 else
5381 basi_alpha=255;
5382 }
glennrp47b9dd52010-11-24 18:12:06 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 if (length > 19)
5385 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387 else
5388 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005389
cristy3ed852e2009-09-05 21:47:34 +00005390#endif
5391 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5392 continue;
5393 }
glennrp47b9dd52010-11-24 18:12:06 +00005394
cristy3ed852e2009-09-05 21:47:34 +00005395 if (memcmp(type,mng_IHDR,4)
5396#if defined(JNG_SUPPORTED)
5397 && memcmp(type,mng_JHDR,4)
5398#endif
5399 )
5400 {
5401 /* Not an IHDR or JHDR chunk */
5402 if (length)
5403 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005404
cristy3ed852e2009-09-05 21:47:34 +00005405 continue;
5406 }
5407/* Process IHDR */
5408 if (logging != MagickFalse)
5409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5410 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005411
cristy3ed852e2009-09-05 21:47:34 +00005412 mng_info->exists[object_id]=MagickTrue;
5413 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005414
cristy3ed852e2009-09-05 21:47:34 +00005415 if (mng_info->invisible[object_id])
5416 {
5417 if (logging != MagickFalse)
5418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5419 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005420
cristy3ed852e2009-09-05 21:47:34 +00005421 skip_to_iend=MagickTrue;
5422 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5423 continue;
5424 }
5425#if defined(MNG_INSERT_LAYERS)
5426 if (length < 8)
5427 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005428
cristy8182b072010-05-30 20:10:53 +00005429 image_width=(size_t) mng_get_long(p);
5430 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005431#endif
5432 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5433
5434 /*
5435 Insert a transparent background layer behind the entire animation
5436 if it is not full screen.
5437 */
5438#if defined(MNG_INSERT_LAYERS)
5439 if (insert_layers && mng_type && first_mng_object)
5440 {
5441 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5442 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005443 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005444 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005445 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005446 {
5447 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5448 {
5449 /*
5450 Allocate next image structure.
5451 */
5452 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005453
cristy3ed852e2009-09-05 21:47:34 +00005454 if (GetNextImageInList(image) == (Image *) NULL)
5455 {
5456 image=DestroyImageList(image);
5457 MngInfoFreeStruct(mng_info,&have_mng_structure);
5458 return((Image *) NULL);
5459 }
glennrp47b9dd52010-11-24 18:12:06 +00005460
cristy3ed852e2009-09-05 21:47:34 +00005461 image=SyncNextImageInList(image);
5462 }
5463 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005464
cristy3ed852e2009-09-05 21:47:34 +00005465 if (term_chunk_found)
5466 {
5467 image->start_loop=MagickTrue;
5468 image->iterations=mng_iterations;
5469 term_chunk_found=MagickFalse;
5470 }
glennrp47b9dd52010-11-24 18:12:06 +00005471
cristy3ed852e2009-09-05 21:47:34 +00005472 else
5473 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005474
5475 /* Make a background rectangle. */
5476
cristy3ed852e2009-09-05 21:47:34 +00005477 image->delay=0;
5478 image->columns=mng_info->mng_width;
5479 image->rows=mng_info->mng_height;
5480 image->page.width=mng_info->mng_width;
5481 image->page.height=mng_info->mng_height;
5482 image->page.x=0;
5483 image->page.y=0;
5484 image->background_color=mng_background_color;
5485 (void) SetImageBackgroundColor(image);
5486 if (logging != MagickFalse)
5487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005488 " Inserted transparent background layer, W=%.20g, H=%.20g",
5489 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005490 }
5491 }
5492 /*
5493 Insert a background layer behind the upcoming image if
5494 framing_mode is 3, and we haven't already inserted one.
5495 */
5496 if (insert_layers && (mng_info->framing_mode == 3) &&
5497 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5498 (simplicity & 0x08)))
5499 {
5500 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5501 {
5502 /*
5503 Allocate next image structure.
5504 */
5505 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005506
cristy3ed852e2009-09-05 21:47:34 +00005507 if (GetNextImageInList(image) == (Image *) NULL)
5508 {
5509 image=DestroyImageList(image);
5510 MngInfoFreeStruct(mng_info,&have_mng_structure);
5511 return((Image *) NULL);
5512 }
glennrp47b9dd52010-11-24 18:12:06 +00005513
cristy3ed852e2009-09-05 21:47:34 +00005514 image=SyncNextImageInList(image);
5515 }
glennrp0fe50b42010-11-16 03:52:51 +00005516
cristy3ed852e2009-09-05 21:47:34 +00005517 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005518
cristy3ed852e2009-09-05 21:47:34 +00005519 if (term_chunk_found)
5520 {
5521 image->start_loop=MagickTrue;
5522 image->iterations=mng_iterations;
5523 term_chunk_found=MagickFalse;
5524 }
glennrp0fe50b42010-11-16 03:52:51 +00005525
cristy3ed852e2009-09-05 21:47:34 +00005526 else
5527 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005528
cristy3ed852e2009-09-05 21:47:34 +00005529 image->delay=0;
5530 image->columns=subframe_width;
5531 image->rows=subframe_height;
5532 image->page.width=subframe_width;
5533 image->page.height=subframe_height;
5534 image->page.x=mng_info->clip.left;
5535 image->page.y=mng_info->clip.top;
5536 image->background_color=mng_background_color;
5537 image->matte=MagickFalse;
5538 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005539
cristy3ed852e2009-09-05 21:47:34 +00005540 if (logging != MagickFalse)
5541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005542 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005543 (double) mng_info->clip.left,(double) mng_info->clip.right,
5544 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005545 }
5546#endif /* MNG_INSERT_LAYERS */
5547 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005548
cristy3ed852e2009-09-05 21:47:34 +00005549 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5550 {
5551 /*
5552 Allocate next image structure.
5553 */
5554 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005555
cristy3ed852e2009-09-05 21:47:34 +00005556 if (GetNextImageInList(image) == (Image *) NULL)
5557 {
5558 image=DestroyImageList(image);
5559 MngInfoFreeStruct(mng_info,&have_mng_structure);
5560 return((Image *) NULL);
5561 }
glennrp47b9dd52010-11-24 18:12:06 +00005562
cristy3ed852e2009-09-05 21:47:34 +00005563 image=SyncNextImageInList(image);
5564 }
5565 mng_info->image=image;
5566 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5567 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005568
cristy3ed852e2009-09-05 21:47:34 +00005569 if (status == MagickFalse)
5570 break;
glennrp0fe50b42010-11-16 03:52:51 +00005571
cristy3ed852e2009-09-05 21:47:34 +00005572 if (term_chunk_found)
5573 {
5574 image->start_loop=MagickTrue;
5575 term_chunk_found=MagickFalse;
5576 }
glennrp0fe50b42010-11-16 03:52:51 +00005577
cristy3ed852e2009-09-05 21:47:34 +00005578 else
5579 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005580
cristy3ed852e2009-09-05 21:47:34 +00005581 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5582 {
5583 image->delay=frame_delay;
5584 frame_delay=default_frame_delay;
5585 }
glennrp0fe50b42010-11-16 03:52:51 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 else
5588 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005589
cristy3ed852e2009-09-05 21:47:34 +00005590 image->page.width=mng_info->mng_width;
5591 image->page.height=mng_info->mng_height;
5592 image->page.x=mng_info->x_off[object_id];
5593 image->page.y=mng_info->y_off[object_id];
5594 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00005595
cristy3ed852e2009-09-05 21:47:34 +00005596 /*
5597 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5598 */
glennrp47b9dd52010-11-24 18:12:06 +00005599
cristy3ed852e2009-09-05 21:47:34 +00005600 if (logging != MagickFalse)
5601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5602 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5603 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005604
cristybb503372010-05-27 20:51:26 +00005605 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00005606
cristy3ed852e2009-09-05 21:47:34 +00005607 if (offset < 0)
5608 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5609 }
5610
5611 previous=image;
5612 mng_info->image=image;
5613 mng_info->mng_type=mng_type;
5614 mng_info->object_id=object_id;
5615
5616 if (memcmp(type,mng_IHDR,4) == 0)
5617 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619#if defined(JNG_SUPPORTED)
5620 else
5621 image=ReadOneJNGImage(mng_info,image_info,exception);
5622#endif
5623
5624 if (image == (Image *) NULL)
5625 {
5626 if (IsImageObject(previous) != MagickFalse)
5627 {
5628 (void) DestroyImageList(previous);
5629 (void) CloseBlob(previous);
5630 }
glennrp47b9dd52010-11-24 18:12:06 +00005631
cristy3ed852e2009-09-05 21:47:34 +00005632 MngInfoFreeStruct(mng_info,&have_mng_structure);
5633 return((Image *) NULL);
5634 }
glennrp0fe50b42010-11-16 03:52:51 +00005635
cristy3ed852e2009-09-05 21:47:34 +00005636 if (image->columns == 0 || image->rows == 0)
5637 {
5638 (void) CloseBlob(image);
5639 image=DestroyImageList(image);
5640 MngInfoFreeStruct(mng_info,&have_mng_structure);
5641 return((Image *) NULL);
5642 }
glennrp0fe50b42010-11-16 03:52:51 +00005643
cristy3ed852e2009-09-05 21:47:34 +00005644 mng_info->image=image;
5645
5646 if (mng_type)
5647 {
5648 MngBox
5649 crop_box;
5650
5651 if (mng_info->magn_methx || mng_info->magn_methy)
5652 {
5653 png_uint_32
5654 magnified_height,
5655 magnified_width;
5656
5657 if (logging != MagickFalse)
5658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5659 " Processing MNG MAGN chunk");
5660
5661 if (mng_info->magn_methx == 1)
5662 {
5663 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 if (image->columns > 1)
5666 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005667
cristy3ed852e2009-09-05 21:47:34 +00005668 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005669 magnified_width += (png_uint_32)
5670 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00005671 }
glennrp47b9dd52010-11-24 18:12:06 +00005672
cristy3ed852e2009-09-05 21:47:34 +00005673 else
5674 {
cristy4e5bc842010-06-09 13:56:01 +00005675 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00005676
cristy3ed852e2009-09-05 21:47:34 +00005677 if (image->columns > 1)
5678 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00005679
cristy3ed852e2009-09-05 21:47:34 +00005680 if (image->columns > 2)
5681 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00005682
cristy3ed852e2009-09-05 21:47:34 +00005683 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005684 magnified_width += (png_uint_32)
5685 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00005686 }
glennrp47b9dd52010-11-24 18:12:06 +00005687
cristy3ed852e2009-09-05 21:47:34 +00005688 if (mng_info->magn_methy == 1)
5689 {
5690 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 if (image->rows > 1)
5693 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005694
cristy3ed852e2009-09-05 21:47:34 +00005695 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005696 magnified_height += (png_uint_32)
5697 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00005698 }
glennrp47b9dd52010-11-24 18:12:06 +00005699
cristy3ed852e2009-09-05 21:47:34 +00005700 else
5701 {
cristy4e5bc842010-06-09 13:56:01 +00005702 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00005703
cristy3ed852e2009-09-05 21:47:34 +00005704 if (image->rows > 1)
5705 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00005706
cristy3ed852e2009-09-05 21:47:34 +00005707 if (image->rows > 2)
5708 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005711 magnified_height += (png_uint_32)
5712 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00005713 }
glennrp47b9dd52010-11-24 18:12:06 +00005714
cristy3ed852e2009-09-05 21:47:34 +00005715 if (magnified_height > image->rows ||
5716 magnified_width > image->columns)
5717 {
5718 Image
5719 *large_image;
5720
5721 int
5722 yy;
5723
cristybb503372010-05-27 20:51:26 +00005724 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005725 m,
5726 y;
5727
cristybb503372010-05-27 20:51:26 +00005728 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005729 x;
5730
5731 register PixelPacket
5732 *n,
5733 *q;
5734
5735 PixelPacket
5736 *next,
5737 *prev;
5738
5739 png_uint_16
5740 magn_methx,
5741 magn_methy;
5742
glennrp47b9dd52010-11-24 18:12:06 +00005743 /* Allocate next image structure. */
5744
cristy3ed852e2009-09-05 21:47:34 +00005745 if (logging != MagickFalse)
5746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5747 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00005748
cristy3ed852e2009-09-05 21:47:34 +00005749 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005750
cristy3ed852e2009-09-05 21:47:34 +00005751 if (GetNextImageInList(image) == (Image *) NULL)
5752 {
5753 image=DestroyImageList(image);
5754 MngInfoFreeStruct(mng_info,&have_mng_structure);
5755 return((Image *) NULL);
5756 }
5757
5758 large_image=SyncNextImageInList(image);
5759
5760 large_image->columns=magnified_width;
5761 large_image->rows=magnified_height;
5762
5763 magn_methx=mng_info->magn_methx;
5764 magn_methy=mng_info->magn_methy;
5765
glennrp3faa9a32011-04-23 14:00:25 +00005766#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00005767#define QM unsigned short
5768 if (magn_methx != 1 || magn_methy != 1)
5769 {
5770 /*
5771 Scale pixels to unsigned shorts to prevent
5772 overflow of intermediate values of interpolations
5773 */
cristybb503372010-05-27 20:51:26 +00005774 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005775 {
5776 q=GetAuthenticPixels(image,0,y,image->columns,1,
5777 exception);
glennrp47b9dd52010-11-24 18:12:06 +00005778
cristybb503372010-05-27 20:51:26 +00005779 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005780 {
glennrp7c7b3152011-04-26 04:01:27 +00005781 SetRedPixelComponent(q,ScaleQuantumToShort(
5782 GetRedPixelComponent(q));
5783 SetGreenPixelComponent(q,ScaleQuantumToShort(
5784 GetGreenPixelComponent(q));
5785 SetBluePixelComponent(q,ScaleQuantumToShort(
5786 GetBluePixelComponent(q));
5787 SetOpacityPixelComponent(q,ScaleQuantumToShort(
5788 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00005789 q++;
5790 }
glennrp47b9dd52010-11-24 18:12:06 +00005791
cristy3ed852e2009-09-05 21:47:34 +00005792 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5793 break;
5794 }
5795 }
5796#else
5797#define QM Quantum
5798#endif
5799
5800 if (image->matte != MagickFalse)
5801 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005802
cristy3ed852e2009-09-05 21:47:34 +00005803 else
5804 {
5805 large_image->background_color.opacity=OpaqueOpacity;
5806 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 if (magn_methx == 4)
5809 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00005810
cristy3ed852e2009-09-05 21:47:34 +00005811 if (magn_methx == 5)
5812 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00005813
cristy3ed852e2009-09-05 21:47:34 +00005814 if (magn_methy == 4)
5815 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 if (magn_methy == 5)
5818 magn_methy=3;
5819 }
5820
5821 /* magnify the rows into the right side of the large image */
5822
5823 if (logging != MagickFalse)
5824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005825 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00005826 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005827 yy=0;
5828 length=(size_t) image->columns;
5829 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5830 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00005831
cristy3ed852e2009-09-05 21:47:34 +00005832 if ((prev == (PixelPacket *) NULL) ||
5833 (next == (PixelPacket *) NULL))
5834 {
5835 image=DestroyImageList(image);
5836 MngInfoFreeStruct(mng_info,&have_mng_structure);
5837 ThrowReaderException(ResourceLimitError,
5838 "MemoryAllocationFailed");
5839 }
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy3ed852e2009-09-05 21:47:34 +00005841 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5842 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristybb503372010-05-27 20:51:26 +00005844 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005845 {
5846 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005847 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005848
cristybb503372010-05-27 20:51:26 +00005849 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5850 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005851
cristybb503372010-05-27 20:51:26 +00005852 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5853 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005854
cristybb503372010-05-27 20:51:26 +00005855 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005856 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005857
cristy3ed852e2009-09-05 21:47:34 +00005858 else
cristybb503372010-05-27 20:51:26 +00005859 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005860
cristy3ed852e2009-09-05 21:47:34 +00005861 n=prev;
5862 prev=next;
5863 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00005864
cristybb503372010-05-27 20:51:26 +00005865 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005866 {
5867 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5868 exception);
5869 (void) CopyMagickMemory(next,n,length);
5870 }
glennrp47b9dd52010-11-24 18:12:06 +00005871
cristy3ed852e2009-09-05 21:47:34 +00005872 for (i=0; i < m; i++, yy++)
5873 {
glennrp7c7b3152011-04-26 04:01:27 +00005874 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00005875 register PixelPacket
5876 *pixels;
5877
cristybb503372010-05-27 20:51:26 +00005878 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005879 pixels=prev;
5880 n=next;
5881 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5882 1,exception);
5883 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00005884
cristybb503372010-05-27 20:51:26 +00005885 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005886 {
glennrpfd05d622011-02-25 04:10:33 +00005887 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005888 /*
5889 if (image->storage_class == PseudoClass)
5890 {
5891 }
5892 */
5893
5894 if (magn_methy <= 1)
5895 {
5896 *q=(*pixels); /* replicate previous */
5897 }
glennrp47b9dd52010-11-24 18:12:06 +00005898
cristy3ed852e2009-09-05 21:47:34 +00005899 else if (magn_methy == 2 || magn_methy == 4)
5900 {
5901 if (i == 0)
5902 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005903
cristy3ed852e2009-09-05 21:47:34 +00005904 else
5905 {
5906 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005907 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5908 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005909 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005910 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5911 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005912 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005913 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5914 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005915 +(*pixels).blue);
glennrp47b9dd52010-11-24 18:12:06 +00005916
cristy3ed852e2009-09-05 21:47:34 +00005917 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005918 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005919 (2*i*((*n).opacity
5920 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005921 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005922 }
glennrp47b9dd52010-11-24 18:12:06 +00005923
cristy3ed852e2009-09-05 21:47:34 +00005924 if (magn_methy == 4)
5925 {
5926 /* Replicate nearest */
5927 if (i <= ((m+1) << 1))
5928 (*q).opacity=(*pixels).opacity+0;
5929 else
5930 (*q).opacity=(*n).opacity+0;
5931 }
5932 }
glennrp47b9dd52010-11-24 18:12:06 +00005933
cristy3ed852e2009-09-05 21:47:34 +00005934 else /* if (magn_methy == 3 || magn_methy == 5) */
5935 {
5936 /* Replicate nearest */
5937 if (i <= ((m+1) << 1))
5938 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005939
cristy3ed852e2009-09-05 21:47:34 +00005940 else
5941 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00005942
cristy3ed852e2009-09-05 21:47:34 +00005943 if (magn_methy == 5)
5944 {
cristybb503372010-05-27 20:51:26 +00005945 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5946 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005947 +(*pixels).opacity);
5948 }
5949 }
5950 n++;
5951 q++;
5952 pixels++;
5953 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00005954
cristy3ed852e2009-09-05 21:47:34 +00005955 if (SyncAuthenticPixels(large_image,exception) == 0)
5956 break;
glennrp47b9dd52010-11-24 18:12:06 +00005957
cristy3ed852e2009-09-05 21:47:34 +00005958 } /* i */
5959 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00005960
cristy3ed852e2009-09-05 21:47:34 +00005961 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5962 next=(PixelPacket *) RelinquishMagickMemory(next);
5963
5964 length=image->columns;
5965
5966 if (logging != MagickFalse)
5967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5968 " Delete original image");
5969
5970 DeleteImageFromList(&image);
5971
5972 image=large_image;
5973
5974 mng_info->image=image;
5975
5976 /* magnify the columns */
5977 if (logging != MagickFalse)
5978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005979 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00005980
cristybb503372010-05-27 20:51:26 +00005981 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005982 {
5983 register PixelPacket
5984 *pixels;
5985
5986 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5987 pixels=q+(image->columns-length);
5988 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00005989
cristybb503372010-05-27 20:51:26 +00005990 for (x=(ssize_t) (image->columns-length);
5991 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005992 {
glennrp7c7b3152011-04-26 04:01:27 +00005993 /* To do: Rewrite using Get/Set***PixelComponent() */
5994
cristybb503372010-05-27 20:51:26 +00005995 if (x == (ssize_t) (image->columns-length))
5996 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005997
cristybb503372010-05-27 20:51:26 +00005998 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5999 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006000
cristybb503372010-05-27 20:51:26 +00006001 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6002 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006003
cristybb503372010-05-27 20:51:26 +00006004 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006005 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006006
cristy3ed852e2009-09-05 21:47:34 +00006007 else
cristybb503372010-05-27 20:51:26 +00006008 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006009
cristy3ed852e2009-09-05 21:47:34 +00006010 for (i=0; i < m; i++)
6011 {
6012 if (magn_methx <= 1)
6013 {
6014 /* replicate previous */
6015 *q=(*pixels);
6016 }
glennrp47b9dd52010-11-24 18:12:06 +00006017
cristy3ed852e2009-09-05 21:47:34 +00006018 else if (magn_methx == 2 || magn_methx == 4)
6019 {
6020 if (i == 0)
6021 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006022
cristy3ed852e2009-09-05 21:47:34 +00006023 else
6024 {
6025 /* Interpolate */
6026 (*q).red=(QM) ((2*i*((*n).red
6027 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00006028 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00006029 (*q).green=(QM) ((2*i*((*n).green
6030 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00006031 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00006032 (*q).blue=(QM) ((2*i*((*n).blue
6033 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00006034 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00006035 if (image->matte != MagickFalse)
6036 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006037 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006038 +(*pixels).opacity);
6039 }
glennrp47b9dd52010-11-24 18:12:06 +00006040
cristy3ed852e2009-09-05 21:47:34 +00006041 if (magn_methx == 4)
6042 {
6043 /* Replicate nearest */
6044 if (i <= ((m+1) << 1))
6045 (*q).opacity=(*pixels).opacity+0;
6046 else
6047 (*q).opacity=(*n).opacity+0;
6048 }
6049 }
glennrp47b9dd52010-11-24 18:12:06 +00006050
cristy3ed852e2009-09-05 21:47:34 +00006051 else /* if (magn_methx == 3 || magn_methx == 5) */
6052 {
6053 /* Replicate nearest */
6054 if (i <= ((m+1) << 1))
6055 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006056
cristy3ed852e2009-09-05 21:47:34 +00006057 else
6058 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00006059
cristy3ed852e2009-09-05 21:47:34 +00006060 if (magn_methx == 5)
6061 {
6062 /* Interpolate */
6063 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006064 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006065 +(*pixels).opacity);
6066 }
6067 }
6068 q++;
6069 }
6070 n++;
6071 p++;
6072 }
glennrp47b9dd52010-11-24 18:12:06 +00006073
cristy3ed852e2009-09-05 21:47:34 +00006074 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6075 break;
6076 }
glennrp3faa9a32011-04-23 14:00:25 +00006077#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006078 if (magn_methx != 1 || magn_methy != 1)
6079 {
6080 /*
6081 Rescale pixels to Quantum
6082 */
cristybb503372010-05-27 20:51:26 +00006083 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006084 {
6085 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006086
cristybb503372010-05-27 20:51:26 +00006087 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006088 {
glennrp7c7b3152011-04-26 04:01:27 +00006089 SetRedPixelComponent(q,ScaleShortToQuantum(
6090 GetRedPixelComponent(q));
6091 SetGreenPixelComponent(q,ScaleShortToQuantum(
6092 GetGreenPixelComponent(q));
6093 SetBluePixelComponent(q,ScaleShortToQuantum(
6094 GetBluePixelComponent(q));
6095 SetOpacityPixelComponent(q,ScaleShortToQuantum(
6096 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00006097 q++;
6098 }
glennrp47b9dd52010-11-24 18:12:06 +00006099
cristy3ed852e2009-09-05 21:47:34 +00006100 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6101 break;
6102 }
6103 }
6104#endif
6105 if (logging != MagickFalse)
6106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6107 " Finished MAGN processing");
6108 }
6109 }
6110
6111 /*
6112 Crop_box is with respect to the upper left corner of the MNG.
6113 */
6114 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6115 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6116 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6117 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6118 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6119 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6120 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6121 if ((crop_box.left != (mng_info->image_box.left
6122 +mng_info->x_off[object_id])) ||
6123 (crop_box.right != (mng_info->image_box.right
6124 +mng_info->x_off[object_id])) ||
6125 (crop_box.top != (mng_info->image_box.top
6126 +mng_info->y_off[object_id])) ||
6127 (crop_box.bottom != (mng_info->image_box.bottom
6128 +mng_info->y_off[object_id])))
6129 {
6130 if (logging != MagickFalse)
6131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6132 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006133
cristy3ed852e2009-09-05 21:47:34 +00006134 if ((crop_box.left < crop_box.right) &&
6135 (crop_box.top < crop_box.bottom))
6136 {
6137 Image
6138 *im;
6139
6140 RectangleInfo
6141 crop_info;
6142
6143 /*
6144 Crop_info is with respect to the upper left corner of
6145 the image.
6146 */
6147 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6148 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006149 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6150 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006151 image->page.width=image->columns;
6152 image->page.height=image->rows;
6153 image->page.x=0;
6154 image->page.y=0;
6155 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006156
cristy3ed852e2009-09-05 21:47:34 +00006157 if (im != (Image *) NULL)
6158 {
6159 image->columns=im->columns;
6160 image->rows=im->rows;
6161 im=DestroyImage(im);
6162 image->page.width=image->columns;
6163 image->page.height=image->rows;
6164 image->page.x=crop_box.left;
6165 image->page.y=crop_box.top;
6166 }
6167 }
glennrp47b9dd52010-11-24 18:12:06 +00006168
cristy3ed852e2009-09-05 21:47:34 +00006169 else
6170 {
6171 /*
6172 No pixels in crop area. The MNG spec still requires
6173 a layer, though, so make a single transparent pixel in
6174 the top left corner.
6175 */
6176 image->columns=1;
6177 image->rows=1;
6178 image->colors=2;
6179 (void) SetImageBackgroundColor(image);
6180 image->page.width=1;
6181 image->page.height=1;
6182 image->page.x=0;
6183 image->page.y=0;
6184 }
6185 }
6186#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6187 image=mng_info->image;
6188#endif
6189 }
6190
glennrp2b013e42010-11-24 16:55:50 +00006191#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6192 /* PNG does not handle depths greater than 16 so reduce it even
6193 * if lossy
6194 */
6195 if (image->depth > 16)
6196 image->depth=16;
6197#endif
6198
glennrp3faa9a32011-04-23 14:00:25 +00006199#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006200 if (LosslessReduceDepthOK(image) != MagickFalse)
6201 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006202#endif
glennrpd6afd542010-11-19 01:53:05 +00006203
cristy3ed852e2009-09-05 21:47:34 +00006204 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006205
cristy3ed852e2009-09-05 21:47:34 +00006206 if (image_info->number_scenes != 0)
6207 {
6208 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006209 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006210 break;
6211 }
glennrpd6afd542010-11-19 01:53:05 +00006212
cristy3ed852e2009-09-05 21:47:34 +00006213 if (logging != MagickFalse)
6214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6215 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006216
cristy3ed852e2009-09-05 21:47:34 +00006217 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006218
cristy3ed852e2009-09-05 21:47:34 +00006219 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006220
cristy3ed852e2009-09-05 21:47:34 +00006221 if (logging != MagickFalse)
6222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6223 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006224
cristy3ed852e2009-09-05 21:47:34 +00006225#if defined(MNG_INSERT_LAYERS)
6226 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6227 (mng_info->mng_height))
6228 {
6229 /*
6230 Insert a background layer if nothing else was found.
6231 */
6232 if (logging != MagickFalse)
6233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6234 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006235
cristy3ed852e2009-09-05 21:47:34 +00006236 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6237 {
6238 /*
6239 Allocate next image structure.
6240 */
6241 AcquireNextImage(image_info,image);
6242 if (GetNextImageInList(image) == (Image *) NULL)
6243 {
6244 image=DestroyImageList(image);
6245 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006246
cristy3ed852e2009-09-05 21:47:34 +00006247 if (logging != MagickFalse)
6248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6249 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006250
cristy3ed852e2009-09-05 21:47:34 +00006251 return((Image *) NULL);
6252 }
6253 image=SyncNextImageInList(image);
6254 }
6255 image->columns=mng_info->mng_width;
6256 image->rows=mng_info->mng_height;
6257 image->page.width=mng_info->mng_width;
6258 image->page.height=mng_info->mng_height;
6259 image->page.x=0;
6260 image->page.y=0;
6261 image->background_color=mng_background_color;
6262 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006263
cristy3ed852e2009-09-05 21:47:34 +00006264 if (image_info->ping == MagickFalse)
6265 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006266
cristy3ed852e2009-09-05 21:47:34 +00006267 mng_info->image_found++;
6268 }
6269#endif
6270 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006271
cristy3ed852e2009-09-05 21:47:34 +00006272 if (mng_iterations == 1)
6273 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006274
cristy3ed852e2009-09-05 21:47:34 +00006275 while (GetPreviousImageInList(image) != (Image *) NULL)
6276 {
6277 image_count++;
6278 if (image_count > 10*mng_info->image_found)
6279 {
6280 if (logging != MagickFalse)
6281 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006282
cristy3ed852e2009-09-05 21:47:34 +00006283 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6284 CoderError,"Linked list is corrupted, beginning of list not found",
6285 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006286
cristy3ed852e2009-09-05 21:47:34 +00006287 return((Image *) NULL);
6288 }
glennrp0fe50b42010-11-16 03:52:51 +00006289
cristy3ed852e2009-09-05 21:47:34 +00006290 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006291
cristy3ed852e2009-09-05 21:47:34 +00006292 if (GetNextImageInList(image) == (Image *) NULL)
6293 {
6294 if (logging != MagickFalse)
6295 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006296
cristy3ed852e2009-09-05 21:47:34 +00006297 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6298 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6299 image_info->filename);
6300 }
6301 }
glennrp47b9dd52010-11-24 18:12:06 +00006302
cristy3ed852e2009-09-05 21:47:34 +00006303 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6304 GetNextImageInList(image) ==
6305 (Image *) NULL)
6306 {
6307 if (logging != MagickFalse)
6308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6309 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006310
cristy3ed852e2009-09-05 21:47:34 +00006311 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6312 CoderError,"image->next for first image is NULL but shouldn't be.",
6313 "`%s'",image_info->filename);
6314 }
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristy3ed852e2009-09-05 21:47:34 +00006316 if (mng_info->image_found == 0)
6317 {
6318 if (logging != MagickFalse)
6319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6320 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006321
cristy3ed852e2009-09-05 21:47:34 +00006322 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6323 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006324
cristy3ed852e2009-09-05 21:47:34 +00006325 if (image != (Image *) NULL)
6326 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006327
cristy3ed852e2009-09-05 21:47:34 +00006328 MngInfoFreeStruct(mng_info,&have_mng_structure);
6329 return((Image *) NULL);
6330 }
6331
6332 if (mng_info->ticks_per_second)
6333 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6334 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006335
cristy3ed852e2009-09-05 21:47:34 +00006336 else
6337 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006338
cristy3ed852e2009-09-05 21:47:34 +00006339 /* Find final nonzero image delay */
6340 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006341
cristy3ed852e2009-09-05 21:47:34 +00006342 while (GetNextImageInList(image) != (Image *) NULL)
6343 {
6344 if (image->delay)
6345 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006346
cristy3ed852e2009-09-05 21:47:34 +00006347 image=GetNextImageInList(image);
6348 }
glennrp0fe50b42010-11-16 03:52:51 +00006349
cristy3ed852e2009-09-05 21:47:34 +00006350 if (final_delay < final_image_delay)
6351 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006352
cristy3ed852e2009-09-05 21:47:34 +00006353 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006354
cristy3ed852e2009-09-05 21:47:34 +00006355 if (logging != MagickFalse)
6356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006357 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6358 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006359
cristy3ed852e2009-09-05 21:47:34 +00006360 if (logging != MagickFalse)
6361 {
6362 int
6363 scene;
6364
6365 scene=0;
6366 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006367
cristy3ed852e2009-09-05 21:47:34 +00006368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6369 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006370
cristy3ed852e2009-09-05 21:47:34 +00006371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006372 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006373
cristy3ed852e2009-09-05 21:47:34 +00006374 while (GetNextImageInList(image) != (Image *) NULL)
6375 {
6376 image=GetNextImageInList(image);
6377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006378 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006379 }
6380 }
6381
6382 image=GetFirstImageInList(image);
6383#ifdef MNG_COALESCE_LAYERS
6384 if (insert_layers)
6385 {
6386 Image
6387 *next_image,
6388 *next;
6389
cristybb503372010-05-27 20:51:26 +00006390 size_t
cristy3ed852e2009-09-05 21:47:34 +00006391 scene;
6392
6393 if (logging != MagickFalse)
6394 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006395
cristy3ed852e2009-09-05 21:47:34 +00006396 scene=image->scene;
6397 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006398
cristy3ed852e2009-09-05 21:47:34 +00006399 if (next_image == (Image *) NULL)
6400 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006401
cristy3ed852e2009-09-05 21:47:34 +00006402 image=DestroyImageList(image);
6403 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006404
cristy3ed852e2009-09-05 21:47:34 +00006405 for (next=image; next != (Image *) NULL; next=next_image)
6406 {
6407 next->page.width=mng_info->mng_width;
6408 next->page.height=mng_info->mng_height;
6409 next->page.x=0;
6410 next->page.y=0;
6411 next->scene=scene++;
6412 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006413
cristy3ed852e2009-09-05 21:47:34 +00006414 if (next_image == (Image *) NULL)
6415 break;
glennrp47b9dd52010-11-24 18:12:06 +00006416
cristy3ed852e2009-09-05 21:47:34 +00006417 if (next->delay == 0)
6418 {
6419 scene--;
6420 next_image->previous=GetPreviousImageInList(next);
6421 if (GetPreviousImageInList(next) == (Image *) NULL)
6422 image=next_image;
6423 else
6424 next->previous->next=next_image;
6425 next=DestroyImage(next);
6426 }
6427 }
6428 }
6429#endif
6430
6431 while (GetNextImageInList(image) != (Image *) NULL)
6432 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006433
cristy3ed852e2009-09-05 21:47:34 +00006434 image->dispose=BackgroundDispose;
6435
6436 if (logging != MagickFalse)
6437 {
6438 int
6439 scene;
6440
6441 scene=0;
6442 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006443
cristy3ed852e2009-09-05 21:47:34 +00006444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6445 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006446
cristy3ed852e2009-09-05 21:47:34 +00006447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006448 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6449 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006450
cristy3ed852e2009-09-05 21:47:34 +00006451 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006452 {
6453 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006454
cristyf2faecf2010-05-28 19:19:36 +00006455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006456 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6457 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006458 }
6459 }
glennrp47b9dd52010-11-24 18:12:06 +00006460
cristy3ed852e2009-09-05 21:47:34 +00006461 image=GetFirstImageInList(image);
6462 MngInfoFreeStruct(mng_info,&have_mng_structure);
6463 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006464
cristy3ed852e2009-09-05 21:47:34 +00006465 if (logging != MagickFalse)
6466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006467
cristy3ed852e2009-09-05 21:47:34 +00006468 return(GetFirstImageInList(image));
6469}
glennrp25c1e2b2010-03-25 01:39:56 +00006470#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006471static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6472{
6473 printf("Your PNG library is too old: You have libpng-%s\n",
6474 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristy3ed852e2009-09-05 21:47:34 +00006476 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6477 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006478
cristy3ed852e2009-09-05 21:47:34 +00006479 return(Image *) NULL;
6480}
glennrp47b9dd52010-11-24 18:12:06 +00006481
cristy3ed852e2009-09-05 21:47:34 +00006482static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6483{
6484 return(ReadPNGImage(image_info,exception));
6485}
glennrp25c1e2b2010-03-25 01:39:56 +00006486#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006487#endif
6488
6489/*
6490%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6491% %
6492% %
6493% %
6494% R e g i s t e r P N G I m a g e %
6495% %
6496% %
6497% %
6498%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6499%
6500% RegisterPNGImage() adds properties for the PNG image format to
6501% the list of supported formats. The properties include the image format
6502% tag, a method to read and/or write the format, whether the format
6503% supports the saving of more than one frame to the same file or blob,
6504% whether the format supports native in-memory I/O, and a brief
6505% description of the format.
6506%
6507% The format of the RegisterPNGImage method is:
6508%
cristybb503372010-05-27 20:51:26 +00006509% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006510%
6511*/
cristybb503372010-05-27 20:51:26 +00006512ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006513{
6514 char
6515 version[MaxTextExtent];
6516
6517 MagickInfo
6518 *entry;
6519
6520 static const char
6521 *PNGNote=
6522 {
6523 "See http://www.libpng.org/ for details about the PNG format."
6524 },
glennrp47b9dd52010-11-24 18:12:06 +00006525
cristy3ed852e2009-09-05 21:47:34 +00006526 *JNGNote=
6527 {
6528 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6529 "format."
6530 },
glennrp47b9dd52010-11-24 18:12:06 +00006531
cristy3ed852e2009-09-05 21:47:34 +00006532 *MNGNote=
6533 {
6534 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6535 "format."
6536 };
6537
6538 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006539
cristy3ed852e2009-09-05 21:47:34 +00006540#if defined(PNG_LIBPNG_VER_STRING)
6541 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6542 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006543
cristy3ed852e2009-09-05 21:47:34 +00006544 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6545 {
6546 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6547 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6548 MaxTextExtent);
6549 }
6550#endif
glennrp47b9dd52010-11-24 18:12:06 +00006551
cristy3ed852e2009-09-05 21:47:34 +00006552 entry=SetMagickInfo("MNG");
6553 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00006554
cristy3ed852e2009-09-05 21:47:34 +00006555#if defined(MAGICKCORE_PNG_DELEGATE)
6556 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6557 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6558#endif
glennrp47b9dd52010-11-24 18:12:06 +00006559
cristy3ed852e2009-09-05 21:47:34 +00006560 entry->magick=(IsImageFormatHandler *) IsMNG;
6561 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563 if (*version != '\0')
6564 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006565
cristy3ed852e2009-09-05 21:47:34 +00006566 entry->module=ConstantString("PNG");
6567 entry->note=ConstantString(MNGNote);
6568 (void) RegisterMagickInfo(entry);
6569
6570 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006571
cristy3ed852e2009-09-05 21:47:34 +00006572#if defined(MAGICKCORE_PNG_DELEGATE)
6573 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6574 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6575#endif
glennrp47b9dd52010-11-24 18:12:06 +00006576
cristy3ed852e2009-09-05 21:47:34 +00006577 entry->magick=(IsImageFormatHandler *) IsPNG;
6578 entry->adjoin=MagickFalse;
6579 entry->description=ConstantString("Portable Network Graphics");
6580 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006581
cristy3ed852e2009-09-05 21:47:34 +00006582 if (*version != '\0')
6583 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006584
cristy3ed852e2009-09-05 21:47:34 +00006585 entry->note=ConstantString(PNGNote);
6586 (void) RegisterMagickInfo(entry);
6587
6588 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00006589
cristy3ed852e2009-09-05 21:47:34 +00006590#if defined(MAGICKCORE_PNG_DELEGATE)
6591 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6592 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6593#endif
glennrp47b9dd52010-11-24 18:12:06 +00006594
cristy3ed852e2009-09-05 21:47:34 +00006595 entry->magick=(IsImageFormatHandler *) IsPNG;
6596 entry->adjoin=MagickFalse;
6597 entry->description=ConstantString(
6598 "8-bit indexed with optional binary transparency");
6599 entry->module=ConstantString("PNG");
6600 (void) RegisterMagickInfo(entry);
6601
6602 entry=SetMagickInfo("PNG24");
6603 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006604
cristy3ed852e2009-09-05 21:47:34 +00006605#if defined(ZLIB_VERSION)
6606 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6607 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006608
cristy3ed852e2009-09-05 21:47:34 +00006609 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6610 {
6611 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6612 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6613 }
6614#endif
glennrp47b9dd52010-11-24 18:12:06 +00006615
cristy3ed852e2009-09-05 21:47:34 +00006616 if (*version != '\0')
6617 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006618
cristy3ed852e2009-09-05 21:47:34 +00006619#if defined(MAGICKCORE_PNG_DELEGATE)
6620 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6621 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6622#endif
glennrp47b9dd52010-11-24 18:12:06 +00006623
cristy3ed852e2009-09-05 21:47:34 +00006624 entry->magick=(IsImageFormatHandler *) IsPNG;
6625 entry->adjoin=MagickFalse;
6626 entry->description=ConstantString("opaque 24-bit RGB");
6627 entry->module=ConstantString("PNG");
6628 (void) RegisterMagickInfo(entry);
6629
6630 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00006631
cristy3ed852e2009-09-05 21:47:34 +00006632#if defined(MAGICKCORE_PNG_DELEGATE)
6633 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6634 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6635#endif
glennrp47b9dd52010-11-24 18:12:06 +00006636
cristy3ed852e2009-09-05 21:47:34 +00006637 entry->magick=(IsImageFormatHandler *) IsPNG;
6638 entry->adjoin=MagickFalse;
6639 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6640 entry->module=ConstantString("PNG");
6641 (void) RegisterMagickInfo(entry);
6642
6643 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006644
cristy3ed852e2009-09-05 21:47:34 +00006645#if defined(JNG_SUPPORTED)
6646#if defined(MAGICKCORE_PNG_DELEGATE)
6647 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6648 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6649#endif
6650#endif
glennrp47b9dd52010-11-24 18:12:06 +00006651
cristy3ed852e2009-09-05 21:47:34 +00006652 entry->magick=(IsImageFormatHandler *) IsJNG;
6653 entry->adjoin=MagickFalse;
6654 entry->description=ConstantString("JPEG Network Graphics");
6655 entry->module=ConstantString("PNG");
6656 entry->note=ConstantString(JNGNote);
6657 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00006658
cristy18b17442009-10-25 18:36:48 +00006659#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006660 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00006661#endif
glennrp47b9dd52010-11-24 18:12:06 +00006662
cristy3ed852e2009-09-05 21:47:34 +00006663 return(MagickImageCoderSignature);
6664}
6665
6666/*
6667%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6668% %
6669% %
6670% %
6671% U n r e g i s t e r P N G I m a g e %
6672% %
6673% %
6674% %
6675%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6676%
6677% UnregisterPNGImage() removes format registrations made by the
6678% PNG module from the list of supported formats.
6679%
6680% The format of the UnregisterPNGImage method is:
6681%
6682% UnregisterPNGImage(void)
6683%
6684*/
6685ModuleExport void UnregisterPNGImage(void)
6686{
6687 (void) UnregisterMagickInfo("MNG");
6688 (void) UnregisterMagickInfo("PNG");
6689 (void) UnregisterMagickInfo("PNG8");
6690 (void) UnregisterMagickInfo("PNG24");
6691 (void) UnregisterMagickInfo("PNG32");
6692 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006693
cristy3ed852e2009-09-05 21:47:34 +00006694#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006695 if (ping_semaphore != (SemaphoreInfo *) NULL)
6696 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006697#endif
6698}
6699
6700#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006701#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006702/*
6703%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6704% %
6705% %
6706% %
6707% W r i t e M N G I m a g e %
6708% %
6709% %
6710% %
6711%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6712%
6713% WriteMNGImage() writes an image in the Portable Network Graphics
6714% Group's "Multiple-image Network Graphics" encoded image format.
6715%
6716% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6717%
6718% The format of the WriteMNGImage method is:
6719%
6720% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6721%
6722% A description of each parameter follows.
6723%
6724% o image_info: the image info.
6725%
6726% o image: The image.
6727%
6728%
6729% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6730% "To do" under ReadPNGImage):
6731%
cristy3ed852e2009-09-05 21:47:34 +00006732% Preserve all unknown and not-yet-handled known chunks found in input
6733% PNG file and copy them into output PNG files according to the PNG
6734% copying rules.
6735%
6736% Write the iCCP chunk at MNG level when (icc profile length > 0)
6737%
6738% Improve selection of color type (use indexed-colour or indexed-colour
6739% with tRNS when 256 or fewer unique RGBA values are present).
6740%
6741% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6742% This will be complicated if we limit ourselves to generating MNG-LC
6743% files. For now we ignore disposal method 3 and simply overlay the next
6744% image on it.
6745%
6746% Check for identical PLTE's or PLTE/tRNS combinations and use a
6747% global MNG PLTE or PLTE/tRNS combination when appropriate.
6748% [mostly done 15 June 1999 but still need to take care of tRNS]
6749%
6750% Check for identical sRGB and replace with a global sRGB (and remove
6751% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6752% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6753% local gAMA/cHRM with local sRGB if appropriate).
6754%
6755% Check for identical sBIT chunks and write global ones.
6756%
6757% Provide option to skip writing the signature tEXt chunks.
6758%
6759% Use signatures to detect identical objects and reuse the first
6760% instance of such objects instead of writing duplicate objects.
6761%
6762% Use a smaller-than-32k value of compression window size when
6763% appropriate.
6764%
6765% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6766% ancillary text chunks and save profiles.
6767%
6768% Provide an option to force LC files (to ensure exact framing rate)
6769% instead of VLC.
6770%
6771% Provide an option to force VLC files instead of LC, even when offsets
6772% are present. This will involve expanding the embedded images with a
6773% transparent region at the top and/or left.
6774*/
6775
cristy3ed852e2009-09-05 21:47:34 +00006776static void
glennrpcf002022011-01-30 02:38:15 +00006777Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00006778 png_info *ping_info, unsigned char *profile_type, unsigned char
6779 *profile_description, unsigned char *profile_data, png_uint_32 length)
6780{
cristy3ed852e2009-09-05 21:47:34 +00006781 png_textp
6782 text;
6783
cristybb503372010-05-27 20:51:26 +00006784 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006785 i;
6786
6787 unsigned char
6788 *sp;
6789
6790 png_charp
6791 dp;
6792
6793 png_uint_32
6794 allocated_length,
6795 description_length;
6796
6797 unsigned char
6798 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006799
cristy3ed852e2009-09-05 21:47:34 +00006800 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6801 return;
6802
6803 if (image_info->verbose)
6804 {
glennrp0fe50b42010-11-16 03:52:51 +00006805 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6806 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006807 }
glennrp0fe50b42010-11-16 03:52:51 +00006808
cristy3ed852e2009-09-05 21:47:34 +00006809 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6810 description_length=(png_uint_32) strlen((const char *) profile_description);
6811 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6812 + description_length);
6813 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6814 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6815 text[0].key[0]='\0';
6816 (void) ConcatenateMagickString(text[0].key,
6817 "Raw profile type ",MaxTextExtent);
6818 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6819 sp=profile_data;
6820 dp=text[0].text;
6821 *dp++='\n';
6822 (void) CopyMagickString(dp,(const char *) profile_description,
6823 allocated_length);
6824 dp+=description_length;
6825 *dp++='\n';
6826 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006827 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006828 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00006829
cristybb503372010-05-27 20:51:26 +00006830 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006831 {
6832 if (i%36 == 0)
6833 *dp++='\n';
6834 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6835 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6836 }
glennrp47b9dd52010-11-24 18:12:06 +00006837
cristy3ed852e2009-09-05 21:47:34 +00006838 *dp++='\n';
6839 *dp='\0';
6840 text[0].text_length=(png_size_t) (dp-text[0].text);
6841 text[0].compression=image_info->compression == NoCompression ||
6842 (image_info->compression == UndefinedCompression &&
6843 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00006844
cristy3ed852e2009-09-05 21:47:34 +00006845 if (text[0].text_length <= allocated_length)
6846 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00006847
cristy3ed852e2009-09-05 21:47:34 +00006848 png_free(ping,text[0].text);
6849 png_free(ping,text[0].key);
6850 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006851}
6852
glennrpcf002022011-01-30 02:38:15 +00006853static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00006854 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00006855{
6856 char
6857 *name;
6858
6859 const StringInfo
6860 *profile;
6861
6862 unsigned char
6863 *data;
6864
6865 png_uint_32 length;
6866
6867 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00006868
6869 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6870 {
cristy3ed852e2009-09-05 21:47:34 +00006871 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00006872
cristy3ed852e2009-09-05 21:47:34 +00006873 if (profile != (const StringInfo *) NULL)
6874 {
6875 StringInfo
glennrpcf002022011-01-30 02:38:15 +00006876 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00006877
glennrp47b9dd52010-11-24 18:12:06 +00006878 if (LocaleNCompare(name,string,11) == 0)
6879 {
6880 if (logging != MagickFalse)
6881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6882 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00006883
glennrpcf002022011-01-30 02:38:15 +00006884 ping_profile=CloneStringInfo(profile);
6885 data=GetStringInfoDatum(ping_profile),
6886 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006887 data[4]=data[3];
6888 data[3]=data[2];
6889 data[2]=data[1];
6890 data[1]=data[0];
6891 (void) WriteBlobMSBULong(image,length-5); /* data length */
6892 (void) WriteBlob(image,length-1,data+1);
6893 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00006894 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006895 }
cristy3ed852e2009-09-05 21:47:34 +00006896 }
glennrp47b9dd52010-11-24 18:12:06 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 name=GetNextImageProfile(image);
6899 }
glennrp47b9dd52010-11-24 18:12:06 +00006900
cristy3ed852e2009-09-05 21:47:34 +00006901 return(MagickTrue);
6902}
6903
glennrpb9cfe272010-12-21 15:08:06 +00006904
cristy3ed852e2009-09-05 21:47:34 +00006905/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00006906static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6907 const ImageInfo *IMimage_info,Image *IMimage)
6908{
6909 Image
6910 *image;
6911
6912 ImageInfo
6913 *image_info;
6914
cristy3ed852e2009-09-05 21:47:34 +00006915 char
6916 s[2];
6917
6918 const char
6919 *name,
6920 *property,
6921 *value;
6922
6923 const StringInfo
6924 *profile;
6925
cristy3ed852e2009-09-05 21:47:34 +00006926 int
cristy3ed852e2009-09-05 21:47:34 +00006927 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006928 pass;
6929
glennrpe9c26dc2010-05-30 01:56:35 +00006930 png_byte
6931 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006932
glennrp39992b42010-11-14 00:03:43 +00006933 png_color
6934 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00006935
glennrp5af765f2010-03-30 11:12:18 +00006936 png_color_16
6937 ping_background,
6938 ping_trans_color;
6939
cristy3ed852e2009-09-05 21:47:34 +00006940 png_info
6941 *ping_info;
6942
6943 png_struct
6944 *ping;
6945
glennrp5af765f2010-03-30 11:12:18 +00006946 png_uint_32
6947 ping_height,
6948 ping_width;
6949
cristybb503372010-05-27 20:51:26 +00006950 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006951 y;
6952
6953 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00006954 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00006955 logging,
glennrp58e01762011-01-07 15:28:54 +00006956 matte,
6957
glennrpda8f3a72011-02-27 23:54:12 +00006958 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00006959 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00006960 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00006961 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00006962 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00006963 ping_have_bKGD,
6964 ping_have_pHYs,
6965 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00006966
6967 ping_exclude_bKGD,
6968 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00006969 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00006970 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00006971 ping_exclude_gAMA,
6972 ping_exclude_iCCP,
6973 /* ping_exclude_iTXt, */
6974 ping_exclude_oFFs,
6975 ping_exclude_pHYs,
6976 ping_exclude_sRGB,
6977 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00006978 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00006979 ping_exclude_vpAg,
6980 ping_exclude_zCCP, /* hex-encoded iCCP */
6981 ping_exclude_zTXt,
6982
glennrp8d3d6e52011-04-19 04:39:51 +00006983 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00006984 ping_need_colortype_warning,
6985
glennrp82b3c532011-03-22 19:20:54 +00006986 status,
glennrpd3371642011-03-22 19:42:23 +00006987 tried_333,
6988 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00006989
6990 QuantumInfo
6991 *quantum_info;
6992
cristybb503372010-05-27 20:51:26 +00006993 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006994 i,
6995 x;
6996
6997 unsigned char
glennrpcf002022011-01-30 02:38:15 +00006998 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00006999
glennrp5af765f2010-03-30 11:12:18 +00007000 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007001 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007002 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007003 ping_color_type,
7004 ping_interlace_method,
7005 ping_compression_method,
7006 ping_filter_method,
7007 ping_num_trans;
7008
cristybb503372010-05-27 20:51:26 +00007009 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007010 image_depth,
7011 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007012
cristybb503372010-05-27 20:51:26 +00007013 size_t
cristy3ed852e2009-09-05 21:47:34 +00007014 quality,
7015 rowbytes,
7016 save_image_depth;
7017
glennrpdfd70802010-11-14 01:23:35 +00007018 int
glennrpfd05d622011-02-25 04:10:33 +00007019 j,
glennrpf09bded2011-01-08 01:15:59 +00007020 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007021 number_opaque,
7022 number_semitransparent,
7023 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007024 ping_pHYs_unit_type;
7025
7026 png_uint_32
7027 ping_pHYs_x_resolution,
7028 ping_pHYs_y_resolution;
7029
cristy3ed852e2009-09-05 21:47:34 +00007030 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007031 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007032
glennrpb9cfe272010-12-21 15:08:06 +00007033 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7034 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007035 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007036 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007037
cristy3ed852e2009-09-05 21:47:34 +00007038#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007039 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007040#endif
7041
glennrp5af765f2010-03-30 11:12:18 +00007042 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007043 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007044 ping_color_type=0,
7045 ping_interlace_method=0,
7046 ping_compression_method=0,
7047 ping_filter_method=0,
7048 ping_num_trans = 0;
7049
7050 ping_background.red = 0;
7051 ping_background.green = 0;
7052 ping_background.blue = 0;
7053 ping_background.gray = 0;
7054 ping_background.index = 0;
7055
7056 ping_trans_color.red=0;
7057 ping_trans_color.green=0;
7058 ping_trans_color.blue=0;
7059 ping_trans_color.gray=0;
7060
glennrpdfd70802010-11-14 01:23:35 +00007061 ping_pHYs_unit_type = 0;
7062 ping_pHYs_x_resolution = 0;
7063 ping_pHYs_y_resolution = 0;
7064
glennrpda8f3a72011-02-27 23:54:12 +00007065 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007066 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007067 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007068 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007069 ping_have_bKGD=MagickFalse;
7070 ping_have_pHYs=MagickFalse;
7071 ping_have_tRNS=MagickFalse;
7072
glennrp0e8ea192010-12-24 18:00:33 +00007073 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7074 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007075 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007076 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007077 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007078 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7079 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7080 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7081 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7082 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7083 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007084 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007085 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7086 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7087 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7088
glennrp8d3d6e52011-04-19 04:39:51 +00007089 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007090 ping_need_colortype_warning = MagickFalse;
7091
glennrp8bb3a022010-12-13 20:40:04 +00007092 number_opaque = 0;
7093 number_semitransparent = 0;
7094 number_transparent = 0;
7095
glennrpfd05d622011-02-25 04:10:33 +00007096 if (logging != MagickFalse)
7097 {
7098 if (image->storage_class == UndefinedClass)
7099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7100 " storage_class=UndefinedClass");
7101 if (image->storage_class == DirectClass)
7102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7103 " storage_class=DirectClass");
7104 if (image->storage_class == PseudoClass)
7105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7106 " storage_class=PseudoClass");
7107 }
glennrp28af3712011-04-06 18:07:30 +00007108
glennrpc6c391a2011-04-27 02:23:56 +00007109 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007110 {
glennrpc6c391a2011-04-27 02:23:56 +00007111 if (image->storage_class != PseudoClass && image->colormap != NULL)
7112 {
7113 /* Free the bogus colormap; it can cause trouble later */
7114 if (logging != MagickFalse)
7115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7116 " Freeing bogus colormap");
7117 (void *) RelinquishMagickMemory(image->colormap);
7118 image->colormap=NULL;
7119 }
glennrp28af3712011-04-06 18:07:30 +00007120 }
glennrpfd05d622011-02-25 04:10:33 +00007121
cristy3ed852e2009-09-05 21:47:34 +00007122 if (image->colorspace != RGBColorspace)
7123 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007124
glennrp3241bd02010-12-12 04:36:28 +00007125 /*
7126 Sometimes we get PseudoClass images whose RGB values don't match
7127 the colors in the colormap. This code syncs the RGB values.
7128 */
7129 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7130 (void) SyncImage(image);
7131
glennrpa6a06632011-01-19 15:15:34 +00007132#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7133 if (image->depth > 8)
7134 {
7135 if (logging != MagickFalse)
7136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7137 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7138
7139 image->depth=8;
7140 }
7141#endif
7142
glennrp67b9c1a2011-04-22 18:47:36 +00007143#if 0 /* To do: Option to use the original colormap */
7144 if (ping_preserve_colormap != MagickFalse)
7145 {
7146 }
7147#endif
7148
glennrp3faa9a32011-04-23 14:00:25 +00007149#if 0 /* To do: respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007150 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7151 {
glennrp9d0ea4d2011-04-22 18:35:57 +00007152 }
glennrp67b9c1a2011-04-22 18:47:36 +00007153#endif
glennrp9d0ea4d2011-04-22 18:35:57 +00007154
glennrp67b9c1a2011-04-22 18:47:36 +00007155 /* To do: set to next higher multiple of 8 */
7156 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007157 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007158
glennrp2b013e42010-11-24 16:55:50 +00007159#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7160 /* PNG does not handle depths greater than 16 so reduce it even
7161 * if lossy
7162 */
7163 if (image->depth > 16)
7164 image->depth=16;
7165#endif
7166
glennrp3faa9a32011-04-23 14:00:25 +00007167#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007168 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007169 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007170 image->depth = 8;
7171#endif
7172
glennrpc8c2f062011-02-25 19:00:33 +00007173 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007174 * we reduce the transparency to binary and run again, then if there
7175 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7176 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7177 * palette. The final reduction can only fail if there are still 256
7178 * colors present and one of them has both transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007179 */
glennrp82b3c532011-03-22 19:20:54 +00007180
7181 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007182 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007183
glennrpd3371642011-03-22 19:42:23 +00007184 for (j=0; j<5; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007185 {
7186 /* BUILD_PALETTE
7187 *
7188 * Sometimes we get DirectClass images that have 256 colors or fewer.
7189 * This code will build a colormap.
7190 *
7191 * Also, sometimes we get PseudoClass images with an out-of-date
7192 * colormap. This code will replace the colormap with a new one.
7193 * Sometimes we get PseudoClass images that have more than 256 colors.
7194 * This code will delete the colormap and change the image to
7195 * DirectClass.
7196 *
7197 * If image->matte is MagickFalse, we ignore the opacity channel
7198 * even though it sometimes contains left-over non-opaque values.
7199 *
7200 * Also we gather some information (number of opaque, transparent,
7201 * and semitransparent pixels, and whether the image has any non-gray
7202 * pixels or only black-and-white pixels) that we might need later.
7203 *
7204 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7205 * we need to check for bogus non-opaque values, at least.
7206 */
glennrp3c218112010-11-27 15:31:26 +00007207
glennrpd71e86a2011-02-24 01:28:37 +00007208 ExceptionInfo
7209 *exception;
glennrp3c218112010-11-27 15:31:26 +00007210
glennrpd71e86a2011-02-24 01:28:37 +00007211 int
7212 n;
glennrp3c218112010-11-27 15:31:26 +00007213
glennrpd71e86a2011-02-24 01:28:37 +00007214 PixelPacket
7215 opaque[260],
7216 semitransparent[260],
7217 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007218
glennrpd71e86a2011-02-24 01:28:37 +00007219 register IndexPacket
7220 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007221
glennrpd71e86a2011-02-24 01:28:37 +00007222 register const PixelPacket
7223 *s,
7224 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007225
glennrpfd05d622011-02-25 04:10:33 +00007226 register PixelPacket
7227 *r;
7228
glennrpd71e86a2011-02-24 01:28:37 +00007229 if (logging != MagickFalse)
7230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7231 " Enter BUILD_PALETTE:");
7232
7233 if (logging != MagickFalse)
7234 {
glennrp03812ae2010-12-24 01:31:34 +00007235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007236 " image->columns=%.20g",(double) image->columns);
7237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7238 " image->rows=%.20g",(double) image->rows);
7239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7240 " image->matte=%.20g",(double) image->matte);
7241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7242 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007243
glennrpfd05d622011-02-25 04:10:33 +00007244 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007245 {
7246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007247 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007249 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007250
glennrpd71e86a2011-02-24 01:28:37 +00007251 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007252 {
glennrpd71e86a2011-02-24 01:28:37 +00007253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7254 " %d (%d,%d,%d,%d)",
7255 (int) i,
7256 (int) image->colormap[i].red,
7257 (int) image->colormap[i].green,
7258 (int) image->colormap[i].blue,
7259 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007260 }
glennrp2cc891a2010-12-24 13:44:32 +00007261
glennrpd71e86a2011-02-24 01:28:37 +00007262 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7263 {
7264 if (i > 255)
7265 {
7266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7267 " %d (%d,%d,%d,%d)",
7268 (int) i,
7269 (int) image->colormap[i].red,
7270 (int) image->colormap[i].green,
7271 (int) image->colormap[i].blue,
7272 (int) image->colormap[i].opacity);
7273 }
7274 }
glennrp03812ae2010-12-24 01:31:34 +00007275 }
glennrp7ddcc222010-12-11 05:01:05 +00007276
glennrpd71e86a2011-02-24 01:28:37 +00007277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7278 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007279
glennrpd71e86a2011-02-24 01:28:37 +00007280 if (image->colors == 0)
7281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7282 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007283
glennrp8d3d6e52011-04-19 04:39:51 +00007284 if (ping_preserve_colormap == MagickFalse)
7285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7286 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007287 }
7288
7289 exception=(&image->exception);
7290
7291 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007292 number_opaque = 0;
7293 number_semitransparent = 0;
7294 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007295
7296 for (y=0; y < (ssize_t) image->rows; y++)
7297 {
7298 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7299
7300 if (q == (PixelPacket *) NULL)
7301 break;
7302
7303 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007304 {
glennrpd71e86a2011-02-24 01:28:37 +00007305 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7306 {
7307 if (number_opaque < 259)
7308 {
7309 if (number_opaque == 0)
7310 {
glennrpca7ad3a2011-04-26 04:44:54 +00007311 opaque[0].red=GetRedPixelComponent(q);
7312 opaque[0].green=GetGreenPixelComponent(q);
7313 opaque[0].blue=GetBluePixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007314 opaque[0].opacity=OpaqueOpacity;
7315 number_opaque=1;
7316 }
glennrp2cc891a2010-12-24 13:44:32 +00007317
glennrpd71e86a2011-02-24 01:28:37 +00007318 for (i=0; i< (ssize_t) number_opaque; i++)
7319 {
glennrp0e68fac2011-04-26 04:51:47 +00007320 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007321 break;
7322 }
glennrp7ddcc222010-12-11 05:01:05 +00007323
glennrpd71e86a2011-02-24 01:28:37 +00007324 if (i == (ssize_t) number_opaque &&
7325 number_opaque < 259)
7326 {
7327 number_opaque++;
glennrpca7ad3a2011-04-26 04:44:54 +00007328 opaque[i].red=GetRedPixelComponent(q);
7329 opaque[i].green=GetGreenPixelComponent(q);
7330 opaque[i].blue=GetBluePixelComponent(q);
7331 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00007332 }
7333 }
7334 }
7335 else if (q->opacity == TransparentOpacity)
7336 {
7337 if (number_transparent < 259)
7338 {
7339 if (number_transparent == 0)
7340 {
glennrpca7ad3a2011-04-26 04:44:54 +00007341 transparent[0].red=GetRedPixelComponent(q);
7342 transparent[0].green=GetGreenPixelComponent(q);
7343 transparent[0].blue=GetBluePixelComponent(q);
7344 transparent[0].opacity=GetOpacityPixelComponent(q);
glennrpa18d5bc2011-04-23 14:51:34 +00007345 ping_trans_color.red=
7346 (unsigned short) GetRedPixelComponent(q);
7347 ping_trans_color.green=
7348 (unsigned short) GetGreenPixelComponent(q);
7349 ping_trans_color.blue=
7350 (unsigned short) GetBluePixelComponent(q);
7351 ping_trans_color.gray=
7352 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007353 number_transparent = 1;
7354 }
7355
7356 for (i=0; i< (ssize_t) number_transparent; i++)
7357 {
glennrp0e68fac2011-04-26 04:51:47 +00007358 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00007359 break;
7360 }
7361
7362 if (i == (ssize_t) number_transparent &&
7363 number_transparent < 259)
7364 {
7365 number_transparent++;
glennrpca7ad3a2011-04-26 04:44:54 +00007366 transparent[i].red=GetRedPixelComponent(q);
7367 transparent[i].green=GetGreenPixelComponent(q);
7368 transparent[i].blue=GetBluePixelComponent(q);
7369 transparent[i].opacity=GetOpacityPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007370 }
7371 }
7372 }
7373 else
7374 {
7375 if (number_semitransparent < 259)
7376 {
7377 if (number_semitransparent == 0)
7378 {
glennrpca7ad3a2011-04-26 04:44:54 +00007379 semitransparent[0].red=GetRedPixelComponent(q);
7380 semitransparent[0].green=GetGreenPixelComponent(q);
7381 semitransparent[0].blue=GetBluePixelComponent(q);
7382 semitransparent[0].opacity=GetOpacityPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007383 number_semitransparent = 1;
7384 }
7385
7386 for (i=0; i< (ssize_t) number_semitransparent; i++)
7387 {
glennrp0e68fac2011-04-26 04:51:47 +00007388 if (IsColorEqual(q, semitransparent+i)
glennrpca7ad3a2011-04-26 04:44:54 +00007389 && GetOpacityPixelComponent(q) ==
7390 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00007391 break;
7392 }
7393
7394 if (i == (ssize_t) number_semitransparent &&
7395 number_semitransparent < 259)
7396 {
7397 number_semitransparent++;
glennrpca7ad3a2011-04-26 04:44:54 +00007398 semitransparent[i].red=GetRedPixelComponent(q);
7399 semitransparent[i].green=GetGreenPixelComponent(q);
7400 semitransparent[i].blue=GetBluePixelComponent(q);
7401 semitransparent[i].opacity=GetOpacityPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007402 }
7403 }
7404 }
7405 q++;
7406 }
7407 }
7408
7409 if (ping_exclude_bKGD == MagickFalse)
7410 {
7411 /* Add the background color to the palette, if it
7412 * isn't already there.
7413 */
glennrpc6c391a2011-04-27 02:23:56 +00007414 if (logging != MagickFalse)
7415 {
7416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7417 " Check colormap for background (%d,%d,%d)",
7418 (int) image->background_color.red,
7419 (int) image->background_color.green,
7420 (int) image->background_color.blue);
7421 }
glennrpd71e86a2011-02-24 01:28:37 +00007422 for (i=0; i<number_opaque; i++)
7423 {
glennrpca7ad3a2011-04-26 04:44:54 +00007424 if (opaque[i].red == image->background_color.red &&
7425 opaque[i].green == image->background_color.green &&
7426 opaque[i].blue == image->background_color.blue)
7427 break;
glennrpd71e86a2011-02-24 01:28:37 +00007428 }
glennrpd71e86a2011-02-24 01:28:37 +00007429 if (number_opaque < 259 && i == number_opaque)
7430 {
glennrpc6c391a2011-04-27 02:23:56 +00007431 opaque[i].red = image->background_color.red;
7432 opaque[i].green = image->background_color.green;
7433 opaque[i].blue = image->background_color.blue;
7434 ping_background.index = i;
7435 if (logging != MagickFalse)
7436 {
7437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7438 " background_color index is %d",(int) i);
7439 }
7440
glennrpd71e86a2011-02-24 01:28:37 +00007441 }
glennrpa080bc32011-03-11 18:03:44 +00007442 else if (logging != MagickFalse)
7443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7444 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007445 }
7446
7447 image_colors=number_opaque+number_transparent+number_semitransparent;
7448
glennrpa080bc32011-03-11 18:03:44 +00007449 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7450 {
7451 /* No room for the background color; remove it. */
7452 number_opaque--;
7453 image_colors--;
7454 }
7455
glennrpd71e86a2011-02-24 01:28:37 +00007456 if (logging != MagickFalse)
7457 {
7458 if (image_colors > 256)
7459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7460 " image has more than 256 colors");
7461
7462 else
7463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7464 " image has %d colors",image_colors);
7465 }
7466
glennrp8d3d6e52011-04-19 04:39:51 +00007467 if (ping_preserve_colormap != MagickFalse)
7468 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007469
glennrpfd05d622011-02-25 04:10:33 +00007470 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007471 {
7472 ping_have_color=MagickFalse;
7473 ping_have_non_bw=MagickFalse;
7474
7475 if(image_colors > 256)
7476 {
7477 for (y=0; y < (ssize_t) image->rows; y++)
7478 {
7479 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7480
7481 if (q == (PixelPacket *) NULL)
7482 break;
7483
7484 /* Worst case is black-and-white; we are looking at every
7485 * pixel twice.
7486 */
7487
7488 if (ping_have_color == MagickFalse)
7489 {
7490 s=q;
7491 for (x=0; x < (ssize_t) image->columns; x++)
7492 {
glennrpa18d5bc2011-04-23 14:51:34 +00007493 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7494 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00007495 {
7496 ping_have_color=MagickTrue;
7497 ping_have_non_bw=MagickTrue;
7498 break;
7499 }
7500 s++;
7501 }
7502 }
7503
7504 if (ping_have_non_bw == MagickFalse)
7505 {
7506 s=q;
7507 for (x=0; x < (ssize_t) image->columns; x++)
7508 {
glennrpa18d5bc2011-04-23 14:51:34 +00007509 if (GetRedPixelComponent(s) != 0 &&
7510 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00007511 {
7512 ping_have_non_bw=MagickTrue;
7513 }
7514 s++;
7515 }
7516 }
7517 }
7518 }
7519 }
7520
7521 if (image_colors < 257)
7522 {
7523 PixelPacket
7524 colormap[260];
7525
7526 /*
7527 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007528 */
7529
glennrpd71e86a2011-02-24 01:28:37 +00007530 if (logging != MagickFalse)
7531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7532 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007533
glennrpd71e86a2011-02-24 01:28:37 +00007534 /* Sort palette, transparent first */;
7535
7536 n = 0;
7537
7538 for (i=0; i<number_transparent; i++)
7539 colormap[n++] = transparent[i];
7540
7541 for (i=0; i<number_semitransparent; i++)
7542 colormap[n++] = semitransparent[i];
7543
7544 for (i=0; i<number_opaque; i++)
7545 colormap[n++] = opaque[i];
7546
glennrpc6c391a2011-04-27 02:23:56 +00007547 ping_background.index +=
7548 (number_transparent + number_semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00007549
7550 /* image_colors < 257; search the colormap instead of the pixels
7551 * to get ping_have_color and ping_have_non_bw
7552 */
7553 for (i=0; i<n; i++)
7554 {
7555 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007556 {
glennrpd71e86a2011-02-24 01:28:37 +00007557 if (colormap[i].red != colormap[i].green ||
7558 colormap[i].red != colormap[i].blue)
7559 {
7560 ping_have_color=MagickTrue;
7561 ping_have_non_bw=MagickTrue;
7562 break;
7563 }
7564 }
7565
7566 if (ping_have_non_bw == MagickFalse)
7567 {
7568 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007569 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007570 }
glennrp8bb3a022010-12-13 20:40:04 +00007571 }
7572
glennrpd71e86a2011-02-24 01:28:37 +00007573 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7574 (number_transparent == 0 && number_semitransparent == 0)) &&
7575 (((mng_info->write_png_colortype-1) ==
7576 PNG_COLOR_TYPE_PALETTE) ||
7577 (mng_info->write_png_colortype == 0)))
7578 {
glennrp6185c532011-01-14 17:58:40 +00007579 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007580 {
glennrpd71e86a2011-02-24 01:28:37 +00007581 if (n != (ssize_t) image_colors)
7582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7583 " image_colors (%d) and n (%d) don't match",
7584 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007585
glennrpd71e86a2011-02-24 01:28:37 +00007586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7587 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007588 }
glennrp03812ae2010-12-24 01:31:34 +00007589
glennrpd71e86a2011-02-24 01:28:37 +00007590 image->colors = image_colors;
7591
7592 if (AcquireImageColormap(image,image_colors) ==
7593 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007594 ThrowWriterException(ResourceLimitError,
7595 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007596
7597 for (i=0; i< (ssize_t) image_colors; i++)
7598 image->colormap[i] = colormap[i];
7599
7600 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007601 {
glennrpd71e86a2011-02-24 01:28:37 +00007602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7603 " image->colors=%d (%d)",
7604 (int) image->colors, image_colors);
7605
7606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7607 " Update the pixel indexes");
7608 }
glennrp6185c532011-01-14 17:58:40 +00007609
glennrpfd05d622011-02-25 04:10:33 +00007610 /* Sync the pixel indices with the new colormap */
7611
glennrpd71e86a2011-02-24 01:28:37 +00007612 for (y=0; y < (ssize_t) image->rows; y++)
7613 {
7614 q=GetAuthenticPixels(image,0,y,image->columns,1,
7615 exception);
glennrp6185c532011-01-14 17:58:40 +00007616
glennrpd71e86a2011-02-24 01:28:37 +00007617 if (q == (PixelPacket *) NULL)
7618 break;
glennrp6185c532011-01-14 17:58:40 +00007619
glennrpd71e86a2011-02-24 01:28:37 +00007620 indexes=GetAuthenticIndexQueue(image);
7621
7622 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007623 {
glennrpd71e86a2011-02-24 01:28:37 +00007624 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007625 {
glennrpd71e86a2011-02-24 01:28:37 +00007626 if ((image->matte == MagickFalse ||
glennrpca7ad3a2011-04-26 04:44:54 +00007627 image->colormap[i].opacity ==
7628 GetOpacityPixelComponent(q)) &&
7629 image->colormap[i].red ==
7630 GetRedPixelComponent(q) &&
7631 image->colormap[i].green ==
7632 GetGreenPixelComponent(q) &&
7633 image->colormap[i].blue ==
7634 GetBluePixelComponent(q))
glennrp6185c532011-01-14 17:58:40 +00007635 {
glennrpd71e86a2011-02-24 01:28:37 +00007636 indexes[x]=(IndexPacket) i;
7637 break;
glennrp6185c532011-01-14 17:58:40 +00007638 }
glennrp6185c532011-01-14 17:58:40 +00007639 }
glennrpd71e86a2011-02-24 01:28:37 +00007640 q++;
7641 }
glennrp6185c532011-01-14 17:58:40 +00007642
glennrpd71e86a2011-02-24 01:28:37 +00007643 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7644 break;
7645 }
7646 }
7647 }
7648
7649 if (logging != MagickFalse)
7650 {
7651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7652 " image->colors=%d", (int) image->colors);
7653
7654 if (image->colormap != NULL)
7655 {
7656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7657 " i (red,green,blue,opacity)");
7658
7659 for (i=0; i < (ssize_t) image->colors; i++)
7660 {
cristy72988482011-03-29 16:34:38 +00007661 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007662 {
7663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7664 " %d (%d,%d,%d,%d)",
7665 (int) i,
7666 (int) image->colormap[i].red,
7667 (int) image->colormap[i].green,
7668 (int) image->colormap[i].blue,
7669 (int) image->colormap[i].opacity);
7670 }
glennrp6185c532011-01-14 17:58:40 +00007671 }
7672 }
glennrp03812ae2010-12-24 01:31:34 +00007673
glennrpd71e86a2011-02-24 01:28:37 +00007674 if (number_transparent < 257)
7675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7676 " number_transparent = %d",
7677 number_transparent);
7678 else
glennrp03812ae2010-12-24 01:31:34 +00007679
glennrpd71e86a2011-02-24 01:28:37 +00007680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7681 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007682
glennrpd71e86a2011-02-24 01:28:37 +00007683 if (number_opaque < 257)
7684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7685 " number_opaque = %d",
7686 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007687
glennrpd71e86a2011-02-24 01:28:37 +00007688 else
7689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7690 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007691
glennrpd71e86a2011-02-24 01:28:37 +00007692 if (number_semitransparent < 257)
7693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7694 " number_semitransparent = %d",
7695 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007696
glennrpd71e86a2011-02-24 01:28:37 +00007697 else
7698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007700
glennrpd71e86a2011-02-24 01:28:37 +00007701 if (ping_have_non_bw == MagickFalse)
7702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7703 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007704
glennrpd71e86a2011-02-24 01:28:37 +00007705 else if (ping_have_color == MagickFalse)
7706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7707 " All pixels and the background are gray");
7708
7709 else
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007712
glennrp03812ae2010-12-24 01:31:34 +00007713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7714 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007715 }
glennrpfd05d622011-02-25 04:10:33 +00007716
glennrpc8c2f062011-02-25 19:00:33 +00007717 if (mng_info->write_png8 == MagickFalse)
7718 break;
glennrpfd05d622011-02-25 04:10:33 +00007719
glennrpc8c2f062011-02-25 19:00:33 +00007720 /* Make any reductions necessary for the PNG8 format */
7721 if (image_colors <= 256 &&
7722 image_colors != 0 && image->colormap != NULL &&
7723 number_semitransparent == 0 &&
7724 number_transparent <= 1)
7725 break;
7726
7727 /* PNG8 can't have semitransparent colors so we threshold the
7728 * opacity to 0 or OpaqueOpacity
7729 */
7730 if (number_semitransparent != 0)
7731 {
7732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7733 " Thresholding the alpha channel to binary");
7734
7735 for (y=0; y < (ssize_t) image->rows; y++)
7736 {
7737 r=GetAuthenticPixels(image,0,y,image->columns,1,
7738 exception);
7739
7740 if (r == (PixelPacket *) NULL)
7741 break;
7742
7743 for (x=0; x < (ssize_t) image->columns; x++)
7744 {
glennrpa18d5bc2011-04-23 14:51:34 +00007745 SetOpacityPixelComponent(r,
7746 (GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7747 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00007748 r++;
7749 }
7750
7751 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7752 break;
7753
7754 if (image_colors != 0 && image_colors <= 256 &&
7755 image->colormap != NULL)
7756 for (i=0; i<image_colors; i++)
7757 image->colormap[i].opacity =
glennrp82b3c532011-03-22 19:20:54 +00007758 image->colormap[i].opacity > TransparentOpacity/2 ?
7759 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007760 }
7761 continue;
7762 }
7763
7764 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007765 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7766 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7767 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007768 */
glennrpd3371642011-03-22 19:42:23 +00007769 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7770 {
7771 if (logging != MagickFalse)
7772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7773 " Quantizing the background color to 4-4-4");
7774
7775 tried_444 = MagickTrue;
7776
7777 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007778 ScaleCharToQuantum(
7779 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7780 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007781 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007782 ScaleCharToQuantum(
7783 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7784 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007785 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007786 ScaleCharToQuantum(
7787 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7788 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007789
7790 if (logging != MagickFalse)
7791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7792 " Quantizing the pixel colors to 4-4-4");
7793
7794 if (image->colormap == NULL)
7795 {
7796 for (y=0; y < (ssize_t) image->rows; y++)
7797 {
7798 r=GetAuthenticPixels(image,0,y,image->columns,1,
7799 exception);
7800
7801 if (r == (PixelPacket *) NULL)
7802 break;
7803
7804 for (x=0; x < (ssize_t) image->columns; x++)
7805 {
7806 if (r->opacity == TransparentOpacity)
7807 {
7808 r->red = image->background_color.red;
7809 r->green = image->background_color.green;
7810 r->blue = image->background_color.blue;
7811 }
7812 else
7813 {
glennrp3faa9a32011-04-23 14:00:25 +00007814 r->red=ScaleCharToQuantum(
7815 (ScaleQuantumToChar(r->red) & 0xf0) |
7816 (ScaleQuantumToChar(r->red) & 0xf0) >> 4);
7817 r->green=ScaleCharToQuantum(
7818 (ScaleQuantumToChar(r->green) & 0xf0) |
7819 (ScaleQuantumToChar(r->green) & 0xf0) >> 4);
7820 r->blue=ScaleCharToQuantum(
7821 (ScaleQuantumToChar(r->blue) & 0xf0) |
7822 (ScaleQuantumToChar(r->blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007823 }
7824 r++;
7825 }
7826
7827 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7828 break;
7829 }
7830 }
7831
7832 else /* Should not reach this; colormap already exists and
7833 must be <= 256 */
7834 {
7835 if (logging != MagickFalse)
7836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7837 " Quantizing the colormap to 4-4-4");
7838 for (i=0; i<image_colors; i++)
7839 {
glennrp3faa9a32011-04-23 14:00:25 +00007840 image->colormap[i].red=ScaleCharToQuantum(
7841 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7842 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7843 image->colormap[i].green=ScaleCharToQuantum(
7844 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7845 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7846 image->colormap[i].blue=ScaleCharToQuantum(
7847 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7848 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
glennrpd3371642011-03-22 19:42:23 +00007849 }
7850 }
7851 continue;
7852 }
7853
glennrp82b3c532011-03-22 19:20:54 +00007854 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7855 {
7856 if (logging != MagickFalse)
7857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7858 " Quantizing the background color to 3-3-3");
7859
7860 tried_333 = MagickTrue;
7861
7862 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007863 ScaleCharToQuantum(
7864 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7865 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7866 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007867 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007868 ScaleCharToQuantum(
7869 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7870 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7871 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007872 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007873 ScaleCharToQuantum(
7874 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7875 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7876 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007877
7878 if (logging != MagickFalse)
7879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007880 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007881
7882 if (image->colormap == NULL)
7883 {
7884 for (y=0; y < (ssize_t) image->rows; y++)
7885 {
7886 r=GetAuthenticPixels(image,0,y,image->columns,1,
7887 exception);
7888
7889 if (r == (PixelPacket *) NULL)
7890 break;
7891
7892 for (x=0; x < (ssize_t) image->columns; x++)
7893 {
7894 if (r->opacity == TransparentOpacity)
7895 {
7896 r->red = image->background_color.red;
7897 r->green = image->background_color.green;
7898 r->blue = image->background_color.blue;
7899 }
7900 else
7901 {
glennrp3faa9a32011-04-23 14:00:25 +00007902 r->red=ScaleCharToQuantum(
7903 (ScaleQuantumToChar(r->red) & 0xe0) |
7904 (ScaleQuantumToChar(r->red) & 0xe0) >> 3 |
7905 (ScaleQuantumToChar(r->red) & 0xc0) >> 6);
7906 r->green=ScaleCharToQuantum(
7907 (ScaleQuantumToChar(r->green) & 0xe0) |
7908 (ScaleQuantumToChar(r->green) & 0xe0) >> 3 |
7909 (ScaleQuantumToChar(r->green) & 0xc0) >> 6);
7910 r->blue=ScaleCharToQuantum(
7911 (ScaleQuantumToChar(r->blue) & 0xe0) |
7912 (ScaleQuantumToChar(r->blue) & 0xe0) >> 3 |
7913 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007914 }
7915 r++;
7916 }
7917
7918 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7919 break;
7920 }
7921 }
7922
7923 else /* Should not reach this; colormap already exists and
7924 must be <= 256 */
7925 {
7926 if (logging != MagickFalse)
7927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007928 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007929 for (i=0; i<image_colors; i++)
7930 {
glennrp3faa9a32011-04-23 14:00:25 +00007931 image->colormap[i].red=ScaleCharToQuantum(
7932 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7933 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7934 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7935 image->colormap[i].green=ScaleCharToQuantum(
7936 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7937 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7938 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7939 image->colormap[i].blue=ScaleCharToQuantum(
7940 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7941 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7942 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007943 }
glennrpd3371642011-03-22 19:42:23 +00007944 }
7945 continue;
glennrp82b3c532011-03-22 19:20:54 +00007946 }
glennrpc8c2f062011-02-25 19:00:33 +00007947
glennrpc8c2f062011-02-25 19:00:33 +00007948 if (image_colors == 0 || image_colors > 256)
7949 {
7950 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007952 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007953
glennrp3faa9a32011-04-23 14:00:25 +00007954 /* Red and green were already done so we only quantize the blue
7955 * channel
7956 */
7957
7958 image->background_color.blue=ScaleCharToQuantum(
7959 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7960 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7961 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7962 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrpfd05d622011-02-25 04:10:33 +00007963
glennrpc8c2f062011-02-25 19:00:33 +00007964 if (logging != MagickFalse)
7965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007966 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007967
glennrpc8c2f062011-02-25 19:00:33 +00007968 if (image->colormap == NULL)
7969 {
7970 for (y=0; y < (ssize_t) image->rows; y++)
7971 {
7972 r=GetAuthenticPixels(image,0,y,image->columns,1,
7973 exception);
7974
7975 if (r == (PixelPacket *) NULL)
7976 break;
7977
7978 for (x=0; x < (ssize_t) image->columns; x++)
7979 {
glennrp82b3c532011-03-22 19:20:54 +00007980 if (r->opacity == TransparentOpacity)
7981 {
7982 r->red = image->background_color.red;
7983 r->green = image->background_color.green;
7984 r->blue = image->background_color.blue;
7985 }
7986 else
7987 {
glennrp3faa9a32011-04-23 14:00:25 +00007988 r->blue=ScaleCharToQuantum(
7989 (ScaleQuantumToChar(r->blue) & 0xc0) |
7990 (ScaleQuantumToChar(r->blue) & 0xc0) >> 2 |
7991 (ScaleQuantumToChar(r->blue) & 0xc0) >> 4 |
7992 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007993 }
glennrp52a479c2011-02-26 21:14:38 +00007994 r++;
glennrpc8c2f062011-02-25 19:00:33 +00007995 }
glennrpfd05d622011-02-25 04:10:33 +00007996
glennrpc8c2f062011-02-25 19:00:33 +00007997 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7998 break;
7999 }
8000 }
glennrpfd05d622011-02-25 04:10:33 +00008001
glennrpc8c2f062011-02-25 19:00:33 +00008002 else /* Should not reach this; colormap already exists and
8003 must be <= 256 */
8004 {
8005 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008007 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008008 for (i=0; i<image_colors; i++)
8009 {
glennrp3faa9a32011-04-23 14:00:25 +00008010 image->colormap[i].blue=ScaleCharToQuantum(
8011 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
8012 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
8013 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
8014 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrpc8c2f062011-02-25 19:00:33 +00008015 }
8016 }
8017 continue;
8018 }
8019 break;
glennrpd71e86a2011-02-24 01:28:37 +00008020 }
glennrpfd05d622011-02-25 04:10:33 +00008021 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008022
glennrpfd05d622011-02-25 04:10:33 +00008023 /* If we are excluding the tRNS chunk and there is transparency,
8024 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8025 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008026 */
glennrp0e8ea192010-12-24 18:00:33 +00008027 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8028 (number_transparent != 0 || number_semitransparent != 0))
8029 {
8030 int colortype=mng_info->write_png_colortype;
8031
8032 if (ping_have_color == MagickFalse)
8033 mng_info->write_png_colortype = 5;
8034
8035 else
8036 mng_info->write_png_colortype = 7;
8037
glennrp8d579662011-02-23 02:05:02 +00008038 if (colortype != 0 &&
8039 mng_info->write_png_colortype != (ssize_t) colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008040 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008041
glennrp0e8ea192010-12-24 18:00:33 +00008042 }
8043
glennrpfd05d622011-02-25 04:10:33 +00008044 /* See if cheap transparency is possible. It is only possible
8045 * when there is a single transparent color, no semitransparent
8046 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008047 * as the transparent color. We only need this information if
8048 * we are writing a PNG with colortype 0 or 2, and we have not
8049 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008050 */
glennrp5a39f372011-02-25 04:52:16 +00008051 if (number_transparent == 1 &&
8052 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008053 {
8054 ping_have_cheap_transparency = MagickTrue;
8055
8056 if (number_semitransparent != 0)
8057 ping_have_cheap_transparency = MagickFalse;
8058
8059 else if (image_colors == 0 || image_colors > 256 ||
8060 image->colormap == NULL)
8061 {
8062 ExceptionInfo
8063 *exception;
8064
8065 register const PixelPacket
8066 *q;
8067
8068 exception=(&image->exception);
8069
8070 for (y=0; y < (ssize_t) image->rows; y++)
8071 {
8072 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8073
8074 if (q == (PixelPacket *) NULL)
8075 break;
8076
8077 for (x=0; x < (ssize_t) image->columns; x++)
8078 {
8079 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008080 (unsigned short) GetRedPixelComponent(q) ==
8081 ping_trans_color.red &&
8082 (unsigned short) GetGreenPixelComponent(q) ==
8083 ping_trans_color.green &&
8084 (unsigned short) GetBluePixelComponent(q) ==
8085 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008086 {
8087 ping_have_cheap_transparency = MagickFalse;
8088 break;
8089 }
8090
8091 q++;
8092 }
8093
8094 if (ping_have_cheap_transparency == MagickFalse)
8095 break;
8096 }
8097 }
8098 else
8099 {
glennrp67b9c1a2011-04-22 18:47:36 +00008100 /* Assuming that image->colormap[0] is the one transparent color
8101 * and that all others are opaque.
8102 */
glennrpfd05d622011-02-25 04:10:33 +00008103 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008104 for (i=1; i<image_colors; i++)
8105 if (image->colormap[i].red == image->colormap[0].red &&
8106 image->colormap[i].green == image->colormap[0].green &&
8107 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008108 {
glennrp67b9c1a2011-04-22 18:47:36 +00008109 ping_have_cheap_transparency = MagickFalse;
8110 break;
glennrpfd05d622011-02-25 04:10:33 +00008111 }
8112 }
8113
8114 if (logging != MagickFalse)
8115 {
8116 if (ping_have_cheap_transparency == MagickFalse)
8117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8118 " Cheap transparency is not possible.");
8119
8120 else
8121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8122 " Cheap transparency is possible.");
8123 }
8124 }
8125 else
8126 ping_have_cheap_transparency = MagickFalse;
8127
glennrp8640fb52010-11-23 15:48:26 +00008128 image_depth=image->depth;
8129
glennrp26c990a2010-11-23 02:23:20 +00008130 quantum_info = (QuantumInfo *) NULL;
8131 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008132 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008133 image_matte=image->matte;
8134
glennrp0fe50b42010-11-16 03:52:51 +00008135 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008136 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008137
glennrp52a479c2011-02-26 21:14:38 +00008138 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8139 (image->colors == 0 || image->colormap == NULL))
8140 {
glennrp52a479c2011-02-26 21:14:38 +00008141 image_info=DestroyImageInfo(image_info);
8142 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008143 (void) ThrowMagickException(&IMimage->exception,
8144 GetMagickModule(),CoderError,
8145 "Cannot write PNG8 or color-type 3; colormap is NULL",
8146 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008147#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8148 UnlockSemaphoreInfo(ping_semaphore);
8149#endif
8150 return(MagickFalse);
8151 }
8152
cristy3ed852e2009-09-05 21:47:34 +00008153 /*
8154 Allocate the PNG structures
8155 */
8156#ifdef PNG_USER_MEM_SUPPORTED
8157 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008158 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8159 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008160
cristy3ed852e2009-09-05 21:47:34 +00008161#else
8162 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008163 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008164
cristy3ed852e2009-09-05 21:47:34 +00008165#endif
8166 if (ping == (png_struct *) NULL)
8167 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008168
cristy3ed852e2009-09-05 21:47:34 +00008169 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008170
cristy3ed852e2009-09-05 21:47:34 +00008171 if (ping_info == (png_info *) NULL)
8172 {
8173 png_destroy_write_struct(&ping,(png_info **) NULL);
8174 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8175 }
glennrp0fe50b42010-11-16 03:52:51 +00008176
cristy3ed852e2009-09-05 21:47:34 +00008177 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008178 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008179
glennrp5af765f2010-03-30 11:12:18 +00008180 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008181 {
8182 /*
8183 PNG write failed.
8184 */
8185#ifdef PNG_DEBUG
8186 if (image_info->verbose)
8187 (void) printf("PNG write has failed.\n");
8188#endif
8189 png_destroy_write_struct(&ping,&ping_info);
8190#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008191 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008192#endif
glennrpda8f3a72011-02-27 23:54:12 +00008193 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008194 (void) CloseBlob(image);
8195 image_info=DestroyImageInfo(image_info);
8196 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008197 return(MagickFalse);
8198 }
8199 /*
8200 Prepare PNG for writing.
8201 */
8202#if defined(PNG_MNG_FEATURES_SUPPORTED)
8203 if (mng_info->write_mng)
8204 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008205
cristy3ed852e2009-09-05 21:47:34 +00008206#else
8207# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8208 if (mng_info->write_mng)
8209 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008210
cristy3ed852e2009-09-05 21:47:34 +00008211# endif
8212#endif
glennrp2b013e42010-11-24 16:55:50 +00008213
cristy3ed852e2009-09-05 21:47:34 +00008214 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008215
cristy4e5bc842010-06-09 13:56:01 +00008216 ping_width=(png_uint_32) image->columns;
8217 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008218
cristy3ed852e2009-09-05 21:47:34 +00008219 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8220 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008221
cristy3ed852e2009-09-05 21:47:34 +00008222 if (mng_info->write_png_depth != 0)
8223 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008224
cristy3ed852e2009-09-05 21:47:34 +00008225 /* Adjust requested depth to next higher valid depth if necessary */
8226 if (image_depth > 8)
8227 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008228
cristy3ed852e2009-09-05 21:47:34 +00008229 if ((image_depth > 4) && (image_depth < 8))
8230 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008231
cristy3ed852e2009-09-05 21:47:34 +00008232 if (image_depth == 3)
8233 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008234
cristy3ed852e2009-09-05 21:47:34 +00008235 if (logging != MagickFalse)
8236 {
8237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008238 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008240 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008242 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008244 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008246 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008247 }
glennrp8640fb52010-11-23 15:48:26 +00008248
cristy3ed852e2009-09-05 21:47:34 +00008249 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008250 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008251
glennrp26f37912010-12-23 16:22:42 +00008252
cristy3ed852e2009-09-05 21:47:34 +00008253#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008254 if (ping_exclude_pHYs == MagickFalse)
8255 {
cristy3ed852e2009-09-05 21:47:34 +00008256 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8257 (!mng_info->write_mng || !mng_info->equal_physs))
8258 {
glennrp0fe50b42010-11-16 03:52:51 +00008259 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8261 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008262
8263 if (image->units == PixelsPerInchResolution)
8264 {
glennrpdfd70802010-11-14 01:23:35 +00008265 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008266 ping_pHYs_x_resolution=
8267 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8268 ping_pHYs_y_resolution=
8269 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008270 }
glennrpdfd70802010-11-14 01:23:35 +00008271
cristy3ed852e2009-09-05 21:47:34 +00008272 else if (image->units == PixelsPerCentimeterResolution)
8273 {
glennrpdfd70802010-11-14 01:23:35 +00008274 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008275 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8276 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008277 }
glennrp991d11d2010-11-12 21:55:28 +00008278
cristy3ed852e2009-09-05 21:47:34 +00008279 else
8280 {
glennrpdfd70802010-11-14 01:23:35 +00008281 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8282 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8283 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008284 }
glennrp991d11d2010-11-12 21:55:28 +00008285
glennrp823b55c2011-03-14 18:46:46 +00008286 if (logging != MagickFalse)
8287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8288 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8289 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8290 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008291 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008292 }
glennrp26f37912010-12-23 16:22:42 +00008293 }
cristy3ed852e2009-09-05 21:47:34 +00008294#endif
glennrpa521b2f2010-10-29 04:11:03 +00008295
glennrp26f37912010-12-23 16:22:42 +00008296 if (ping_exclude_bKGD == MagickFalse)
8297 {
glennrpa521b2f2010-10-29 04:11:03 +00008298 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008299 {
glennrpa521b2f2010-10-29 04:11:03 +00008300 unsigned int
8301 mask;
cristy3ed852e2009-09-05 21:47:34 +00008302
glennrpa521b2f2010-10-29 04:11:03 +00008303 mask=0xffff;
8304 if (ping_bit_depth == 8)
8305 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008306
glennrpa521b2f2010-10-29 04:11:03 +00008307 if (ping_bit_depth == 4)
8308 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008309
glennrpa521b2f2010-10-29 04:11:03 +00008310 if (ping_bit_depth == 2)
8311 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008312
glennrpa521b2f2010-10-29 04:11:03 +00008313 if (ping_bit_depth == 1)
8314 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008315
glennrpa521b2f2010-10-29 04:11:03 +00008316 ping_background.red=(png_uint_16)
8317 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008318
glennrpa521b2f2010-10-29 04:11:03 +00008319 ping_background.green=(png_uint_16)
8320 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008321
glennrpa521b2f2010-10-29 04:11:03 +00008322 ping_background.blue=(png_uint_16)
8323 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008324
8325 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008326 }
cristy3ed852e2009-09-05 21:47:34 +00008327
glennrp0fe50b42010-11-16 03:52:51 +00008328 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008329 {
8330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8331 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333 " background_color index is %d",
8334 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008335
8336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8337 " ping_bit_depth=%d",ping_bit_depth);
8338 }
glennrp0fe50b42010-11-16 03:52:51 +00008339
8340 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008341 }
glennrp0fe50b42010-11-16 03:52:51 +00008342
cristy3ed852e2009-09-05 21:47:34 +00008343 /*
8344 Select the color type.
8345 */
8346 matte=image_matte;
8347 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008348
glennrp1273f7b2011-02-24 03:20:30 +00008349 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008350 {
glennrp0fe50b42010-11-16 03:52:51 +00008351
glennrpfd05d622011-02-25 04:10:33 +00008352 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008353 for reducing the sample depth from 8. */
8354
glennrp0fe50b42010-11-16 03:52:51 +00008355 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008356
glennrp8bb3a022010-12-13 20:40:04 +00008357 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008358
8359 /*
8360 Set image palette.
8361 */
8362 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8363
glennrp0fe50b42010-11-16 03:52:51 +00008364 if (logging != MagickFalse)
8365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8366 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008367 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008368
8369 for (i=0; i < (ssize_t) number_colors; i++)
8370 {
8371 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8372 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8373 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8374 if (logging != MagickFalse)
8375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008376#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008377 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008378#else
8379 " %5ld (%5d,%5d,%5d)",
8380#endif
glennrp0fe50b42010-11-16 03:52:51 +00008381 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8382
8383 }
glennrp2b013e42010-11-24 16:55:50 +00008384
glennrp8bb3a022010-12-13 20:40:04 +00008385 ping_have_PLTE=MagickTrue;
8386 image_depth=ping_bit_depth;
8387 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008388
glennrp58e01762011-01-07 15:28:54 +00008389 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008390 {
glennrp0fe50b42010-11-16 03:52:51 +00008391 /*
8392 Identify which colormap entry is transparent.
8393 */
8394 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008395 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008396
glennrp8bb3a022010-12-13 20:40:04 +00008397 for (i=0; i < (ssize_t) number_transparent; i++)
8398 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008399
glennrp0fe50b42010-11-16 03:52:51 +00008400
glennrp2cc891a2010-12-24 13:44:32 +00008401 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008402 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008403
8404 if (ping_num_trans == 0)
8405 ping_have_tRNS=MagickFalse;
8406
glennrp8bb3a022010-12-13 20:40:04 +00008407 else
8408 ping_have_tRNS=MagickTrue;
8409 }
glennrp0fe50b42010-11-16 03:52:51 +00008410
glennrp1273f7b2011-02-24 03:20:30 +00008411 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008412 {
glennrp1273f7b2011-02-24 03:20:30 +00008413 /*
8414 * Identify which colormap entry is the background color.
8415 */
8416
glennrp4f25bd02011-01-01 18:51:28 +00008417 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8418 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8419 break;
glennrp0fe50b42010-11-16 03:52:51 +00008420
glennrp4f25bd02011-01-01 18:51:28 +00008421 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00008422
8423 if (logging != MagickFalse)
8424 {
8425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8426 " background_color index is %d",
8427 (int) ping_background.index);
8428 }
glennrp4f25bd02011-01-01 18:51:28 +00008429 }
cristy3ed852e2009-09-05 21:47:34 +00008430 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008431
cristy3ed852e2009-09-05 21:47:34 +00008432 else if (mng_info->write_png24)
8433 {
8434 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008435 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008436 }
glennrp0fe50b42010-11-16 03:52:51 +00008437
cristy3ed852e2009-09-05 21:47:34 +00008438 else if (mng_info->write_png32)
8439 {
8440 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008441 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008442 }
glennrp0fe50b42010-11-16 03:52:51 +00008443
glennrp8bb3a022010-12-13 20:40:04 +00008444 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008445 {
glennrp5af765f2010-03-30 11:12:18 +00008446 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008447
glennrp8bb3a022010-12-13 20:40:04 +00008448 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008449 {
glennrp5af765f2010-03-30 11:12:18 +00008450 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008451
glennrp5af765f2010-03-30 11:12:18 +00008452 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8453 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008454 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008455
glennrp8bb3a022010-12-13 20:40:04 +00008456 else
8457 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008458
8459 if (logging != MagickFalse)
8460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008462 }
glennrp0fe50b42010-11-16 03:52:51 +00008463
glennrp7c4c9e62011-03-21 20:23:32 +00008464 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008465 {
8466 if (logging != MagickFalse)
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008468 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008469
glennrpd6bf1612010-12-17 17:28:54 +00008470 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008471 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008472
glennrpd6bf1612010-12-17 17:28:54 +00008473 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008474 {
glennrp5af765f2010-03-30 11:12:18 +00008475 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008476 image_matte=MagickFalse;
8477 }
glennrp0fe50b42010-11-16 03:52:51 +00008478
glennrpd6bf1612010-12-17 17:28:54 +00008479 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008480 {
glennrp5af765f2010-03-30 11:12:18 +00008481 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008482 image_matte=MagickTrue;
8483 }
glennrp0fe50b42010-11-16 03:52:51 +00008484
glennrp5aa37f62011-01-02 03:07:57 +00008485 if (image_info->type == PaletteType ||
8486 image_info->type == PaletteMatteType)
8487 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8488
glennrp7c4c9e62011-03-21 20:23:32 +00008489 if (mng_info->write_png_colortype == 0 &&
8490 (image_info->type == UndefinedType ||
8491 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008492 {
glennrp5aa37f62011-01-02 03:07:57 +00008493 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008494 {
glennrp5aa37f62011-01-02 03:07:57 +00008495 if (image_matte == MagickFalse)
8496 {
8497 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8498 image_matte=MagickFalse;
8499 }
glennrp0fe50b42010-11-16 03:52:51 +00008500
glennrp0b206f52011-01-07 04:55:32 +00008501 else
glennrp5aa37f62011-01-02 03:07:57 +00008502 {
8503 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8504 image_matte=MagickTrue;
8505 }
8506 }
8507 else
glennrp8bb3a022010-12-13 20:40:04 +00008508 {
glennrp5aa37f62011-01-02 03:07:57 +00008509 if (image_matte == MagickFalse)
8510 {
8511 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8512 image_matte=MagickFalse;
8513 }
glennrp8bb3a022010-12-13 20:40:04 +00008514
glennrp0b206f52011-01-07 04:55:32 +00008515 else
glennrp5aa37f62011-01-02 03:07:57 +00008516 {
8517 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8518 image_matte=MagickTrue;
8519 }
8520 }
glennrp0fe50b42010-11-16 03:52:51 +00008521 }
glennrp5aa37f62011-01-02 03:07:57 +00008522
cristy3ed852e2009-09-05 21:47:34 +00008523 }
glennrp0fe50b42010-11-16 03:52:51 +00008524
cristy3ed852e2009-09-05 21:47:34 +00008525 if (logging != MagickFalse)
8526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008527 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008528
glennrp5af765f2010-03-30 11:12:18 +00008529 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008530 {
8531 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8532 ping_color_type == PNG_COLOR_TYPE_RGB ||
8533 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8534 ping_bit_depth=8;
8535 }
cristy3ed852e2009-09-05 21:47:34 +00008536
glennrpd6bf1612010-12-17 17:28:54 +00008537 old_bit_depth=ping_bit_depth;
8538
glennrp5af765f2010-03-30 11:12:18 +00008539 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008540 {
glennrp8d579662011-02-23 02:05:02 +00008541 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8542 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008543 }
glennrp8640fb52010-11-23 15:48:26 +00008544
glennrp5af765f2010-03-30 11:12:18 +00008545 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008546 {
cristy35ef8242010-06-03 16:24:13 +00008547 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008548 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008549
8550 if (image->colors == 0)
8551 {
glennrp0fe50b42010-11-16 03:52:51 +00008552 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008553 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008554 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008555 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008556 }
8557
cristy35ef8242010-06-03 16:24:13 +00008558 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008559 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008560 }
glennrp2b013e42010-11-24 16:55:50 +00008561
glennrpd6bf1612010-12-17 17:28:54 +00008562 if (logging != MagickFalse)
8563 {
8564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8565 " Number of colors: %.20g",(double) image_colors);
8566
8567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568 " Tentative PNG bit depth: %d",ping_bit_depth);
8569 }
8570
8571 if (ping_bit_depth < (int) mng_info->write_png_depth)
8572 ping_bit_depth = mng_info->write_png_depth;
8573 }
glennrp2cc891a2010-12-24 13:44:32 +00008574
glennrp5af765f2010-03-30 11:12:18 +00008575 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008576
cristy3ed852e2009-09-05 21:47:34 +00008577 if (logging != MagickFalse)
8578 {
8579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008580 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008581
cristy3ed852e2009-09-05 21:47:34 +00008582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008583 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008584
cristy3ed852e2009-09-05 21:47:34 +00008585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008586 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008587
cristy3ed852e2009-09-05 21:47:34 +00008588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008589
glennrp8640fb52010-11-23 15:48:26 +00008590 " image->depth: %.20g",(double) image->depth);
8591
8592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008593 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008594 }
8595
glennrp58e01762011-01-07 15:28:54 +00008596 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008597 {
glennrp4f25bd02011-01-01 18:51:28 +00008598 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008599 {
glennrp7c4c9e62011-03-21 20:23:32 +00008600 if (mng_info->write_png_colortype == 0)
8601 {
8602 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008603
glennrp7c4c9e62011-03-21 20:23:32 +00008604 if (ping_have_color != MagickFalse)
8605 ping_color_type=PNG_COLOR_TYPE_RGBA;
8606 }
glennrp4f25bd02011-01-01 18:51:28 +00008607
8608 /*
8609 * Determine if there is any transparent color.
8610 */
8611 if (number_transparent + number_semitransparent == 0)
8612 {
8613 /*
8614 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8615 */
glennrpa6a06632011-01-19 15:15:34 +00008616
glennrp4f25bd02011-01-01 18:51:28 +00008617 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008618
8619 if (mng_info->write_png_colortype == 0)
8620 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008621 }
8622
8623 else
8624 {
8625 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008626 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008627
8628 mask=0xffff;
8629
8630 if (ping_bit_depth == 8)
8631 mask=0x00ff;
8632
8633 if (ping_bit_depth == 4)
8634 mask=0x000f;
8635
8636 if (ping_bit_depth == 2)
8637 mask=0x0003;
8638
8639 if (ping_bit_depth == 1)
8640 mask=0x0001;
8641
8642 ping_trans_color.red=(png_uint_16)
8643 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8644
8645 ping_trans_color.green=(png_uint_16)
8646 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8647
8648 ping_trans_color.blue=(png_uint_16)
8649 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8650
8651 ping_trans_color.gray=(png_uint_16)
8652 (ScaleQuantumToShort(PixelIntensityToQuantum(
8653 image->colormap)) & mask);
8654
8655 ping_trans_color.index=(png_byte) 0;
8656
8657 ping_have_tRNS=MagickTrue;
8658 }
8659
8660 if (ping_have_tRNS != MagickFalse)
8661 {
8662 /*
glennrpfd05d622011-02-25 04:10:33 +00008663 * Determine if there is one and only one transparent color
8664 * and if so if it is fully transparent.
8665 */
8666 if (ping_have_cheap_transparency == MagickFalse)
8667 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008668 }
8669
8670 if (ping_have_tRNS != MagickFalse)
8671 {
glennrp7c4c9e62011-03-21 20:23:32 +00008672 if (mng_info->write_png_colortype == 0)
8673 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008674
8675 if (image_depth == 8)
8676 {
8677 ping_trans_color.red&=0xff;
8678 ping_trans_color.green&=0xff;
8679 ping_trans_color.blue&=0xff;
8680 ping_trans_color.gray&=0xff;
8681 }
8682 }
8683 }
cristy3ed852e2009-09-05 21:47:34 +00008684 else
8685 {
cristy3ed852e2009-09-05 21:47:34 +00008686 if (image_depth == 8)
8687 {
glennrp5af765f2010-03-30 11:12:18 +00008688 ping_trans_color.red&=0xff;
8689 ping_trans_color.green&=0xff;
8690 ping_trans_color.blue&=0xff;
8691 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008692 }
8693 }
8694 }
glennrp8640fb52010-11-23 15:48:26 +00008695
cristy3ed852e2009-09-05 21:47:34 +00008696 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008697
glennrp2e09f552010-11-14 00:38:48 +00008698 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008699 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008700
glennrp39992b42010-11-14 00:03:43 +00008701 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008702 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008703 ping_have_color == MagickFalse &&
8704 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008705 {
cristy35ef8242010-06-03 16:24:13 +00008706 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008707
cristy3ed852e2009-09-05 21:47:34 +00008708 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008709 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008710
glennrp7c4c9e62011-03-21 20:23:32 +00008711 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008712 {
glennrp5af765f2010-03-30 11:12:18 +00008713 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008714
cristy3ed852e2009-09-05 21:47:34 +00008715 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008716 {
8717 if (logging != MagickFalse)
8718 {
8719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8720 " Scaling ping_trans_color (0)");
8721 }
8722 ping_trans_color.gray*=0x0101;
8723 }
cristy3ed852e2009-09-05 21:47:34 +00008724 }
glennrp0fe50b42010-11-16 03:52:51 +00008725
cristy3ed852e2009-09-05 21:47:34 +00008726 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8727 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008728
cristy85380932011-04-25 17:11:52 +00008729 if ((image_colors == 0) || ((ssize_t) (image_colors-1) > MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008730 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008731
cristy3ed852e2009-09-05 21:47:34 +00008732 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008733 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008734
cristy3ed852e2009-09-05 21:47:34 +00008735 else
8736 {
glennrp5af765f2010-03-30 11:12:18 +00008737 ping_bit_depth=8;
8738 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008739 {
8740 if(!mng_info->write_png_depth)
8741 {
glennrp5af765f2010-03-30 11:12:18 +00008742 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008743
cristy35ef8242010-06-03 16:24:13 +00008744 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008745 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008746 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008747 }
8748 }
glennrp2b013e42010-11-24 16:55:50 +00008749
glennrp0fe50b42010-11-16 03:52:51 +00008750 else if (ping_color_type ==
8751 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008752 mng_info->IsPalette)
8753 {
cristy3ed852e2009-09-05 21:47:34 +00008754 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008755
cristy3ed852e2009-09-05 21:47:34 +00008756 int
8757 depth_4_ok=MagickTrue,
8758 depth_2_ok=MagickTrue,
8759 depth_1_ok=MagickTrue;
8760
cristybb503372010-05-27 20:51:26 +00008761 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008762 {
8763 unsigned char
8764 intensity;
8765
8766 intensity=ScaleQuantumToChar(image->colormap[i].red);
8767
8768 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8769 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8770 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8771 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008772 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008773 depth_1_ok=MagickFalse;
8774 }
glennrp2b013e42010-11-24 16:55:50 +00008775
cristy3ed852e2009-09-05 21:47:34 +00008776 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008777 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008778
cristy3ed852e2009-09-05 21:47:34 +00008779 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008780 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008781
cristy3ed852e2009-09-05 21:47:34 +00008782 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008783 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008784 }
8785 }
glennrp2b013e42010-11-24 16:55:50 +00008786
glennrp5af765f2010-03-30 11:12:18 +00008787 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008788 }
glennrp0fe50b42010-11-16 03:52:51 +00008789
cristy3ed852e2009-09-05 21:47:34 +00008790 else
glennrp0fe50b42010-11-16 03:52:51 +00008791
cristy3ed852e2009-09-05 21:47:34 +00008792 if (mng_info->IsPalette)
8793 {
glennrp17a14852010-05-10 03:01:59 +00008794 number_colors=image_colors;
8795
cristy3ed852e2009-09-05 21:47:34 +00008796 if (image_depth <= 8)
8797 {
cristy3ed852e2009-09-05 21:47:34 +00008798 /*
8799 Set image palette.
8800 */
glennrp5af765f2010-03-30 11:12:18 +00008801 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008802
glennrp58e01762011-01-07 15:28:54 +00008803 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008804 {
glennrp9c1eb072010-06-06 22:19:15 +00008805 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008806
glennrp3b51f0e2010-11-27 18:14:08 +00008807 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8809 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008810 }
glennrp0fe50b42010-11-16 03:52:51 +00008811
cristy3ed852e2009-09-05 21:47:34 +00008812 else
8813 {
cristybb503372010-05-27 20:51:26 +00008814 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008815 {
8816 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8817 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8818 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8819 }
glennrp0fe50b42010-11-16 03:52:51 +00008820
glennrp3b51f0e2010-11-27 18:14:08 +00008821 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008823 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008824 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008825
glennrp39992b42010-11-14 00:03:43 +00008826 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008827 }
glennrp0fe50b42010-11-16 03:52:51 +00008828
cristy3ed852e2009-09-05 21:47:34 +00008829 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008830 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008831 {
cristybefe4d22010-06-07 01:18:58 +00008832 size_t
8833 one;
8834
glennrp5af765f2010-03-30 11:12:18 +00008835 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008836 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008837
cristybefe4d22010-06-07 01:18:58 +00008838 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008839 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008840 }
glennrp0fe50b42010-11-16 03:52:51 +00008841
glennrp5af765f2010-03-30 11:12:18 +00008842 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008843
glennrp58e01762011-01-07 15:28:54 +00008844 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008845 {
glennrp0fe50b42010-11-16 03:52:51 +00008846 /*
glennrpd6bf1612010-12-17 17:28:54 +00008847 * Set up trans_colors array.
8848 */
glennrp0fe50b42010-11-16 03:52:51 +00008849 assert(number_colors <= 256);
8850
glennrpd6bf1612010-12-17 17:28:54 +00008851 ping_num_trans=(unsigned short) (number_transparent +
8852 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008853
8854 if (ping_num_trans == 0)
8855 ping_have_tRNS=MagickFalse;
8856
glennrpd6bf1612010-12-17 17:28:54 +00008857 else
glennrp0fe50b42010-11-16 03:52:51 +00008858 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008859 if (logging != MagickFalse)
8860 {
8861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8862 " Scaling ping_trans_color (1)");
8863 }
glennrpd6bf1612010-12-17 17:28:54 +00008864 ping_have_tRNS=MagickTrue;
8865
8866 for (i=0; i < ping_num_trans; i++)
8867 {
8868 ping_trans_alpha[i]= (png_byte) (255-
8869 ScaleQuantumToChar(image->colormap[i].opacity));
8870 }
glennrp0fe50b42010-11-16 03:52:51 +00008871 }
8872 }
cristy3ed852e2009-09-05 21:47:34 +00008873 }
8874 }
glennrp0fe50b42010-11-16 03:52:51 +00008875
cristy3ed852e2009-09-05 21:47:34 +00008876 else
8877 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008878
cristy3ed852e2009-09-05 21:47:34 +00008879 if (image_depth < 8)
8880 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008881
cristy3ed852e2009-09-05 21:47:34 +00008882 if ((save_image_depth == 16) && (image_depth == 8))
8883 {
glennrp4f25bd02011-01-01 18:51:28 +00008884 if (logging != MagickFalse)
8885 {
8886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8887 " Scaling ping_trans_color from (%d,%d,%d)",
8888 (int) ping_trans_color.red,
8889 (int) ping_trans_color.green,
8890 (int) ping_trans_color.blue);
8891 }
8892
glennrp5af765f2010-03-30 11:12:18 +00008893 ping_trans_color.red*=0x0101;
8894 ping_trans_color.green*=0x0101;
8895 ping_trans_color.blue*=0x0101;
8896 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008897
8898 if (logging != MagickFalse)
8899 {
8900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8901 " to (%d,%d,%d)",
8902 (int) ping_trans_color.red,
8903 (int) ping_trans_color.green,
8904 (int) ping_trans_color.blue);
8905 }
cristy3ed852e2009-09-05 21:47:34 +00008906 }
8907 }
8908
cristy4383ec82011-01-05 15:42:32 +00008909 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8910 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008911
cristy3ed852e2009-09-05 21:47:34 +00008912 /*
8913 Adjust background and transparency samples in sub-8-bit grayscale files.
8914 */
glennrp5af765f2010-03-30 11:12:18 +00008915 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008916 PNG_COLOR_TYPE_GRAY)
8917 {
8918 png_uint_16
8919 maxval;
8920
cristy35ef8242010-06-03 16:24:13 +00008921 size_t
8922 one=1;
8923
cristy22ffd972010-06-03 16:51:47 +00008924 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008925
glennrp4f25bd02011-01-01 18:51:28 +00008926 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008927 {
cristy3ed852e2009-09-05 21:47:34 +00008928
glennrpa521b2f2010-10-29 04:11:03 +00008929 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008930 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8931
8932 if (logging != MagickFalse)
8933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008934 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00008935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8936 " background_color index is %d",
8937 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008938
glennrp991d11d2010-11-12 21:55:28 +00008939 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008940 }
cristy3ed852e2009-09-05 21:47:34 +00008941
glennrp5af765f2010-03-30 11:12:18 +00008942 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8943 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008944 }
glennrp17a14852010-05-10 03:01:59 +00008945
glennrp26f37912010-12-23 16:22:42 +00008946 if (ping_exclude_bKGD == MagickFalse)
8947 {
glennrp1273f7b2011-02-24 03:20:30 +00008948 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008949 {
8950 /*
8951 Identify which colormap entry is the background color.
8952 */
8953
glennrp17a14852010-05-10 03:01:59 +00008954 number_colors=image_colors;
8955
glennrpa521b2f2010-10-29 04:11:03 +00008956 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8957 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008958 break;
8959
8960 ping_background.index=(png_byte) i;
8961
glennrp3b51f0e2010-11-27 18:14:08 +00008962 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008963 {
8964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008965 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008966 }
glennrp0fe50b42010-11-16 03:52:51 +00008967
cristy13d07042010-11-21 20:56:18 +00008968 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008969 {
8970 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008971
8972 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008973 {
8974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8975 " background =(%d,%d,%d)",
8976 (int) ping_background.red,
8977 (int) ping_background.green,
8978 (int) ping_background.blue);
8979 }
8980 }
glennrpa521b2f2010-10-29 04:11:03 +00008981
glennrpd6bf1612010-12-17 17:28:54 +00008982 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008983 {
glennrp3b51f0e2010-11-27 18:14:08 +00008984 if (logging != MagickFalse)
8985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8986 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008987 ping_have_bKGD = MagickFalse;
8988 }
glennrp17a14852010-05-10 03:01:59 +00008989 }
glennrp26f37912010-12-23 16:22:42 +00008990 }
glennrp17a14852010-05-10 03:01:59 +00008991
cristy3ed852e2009-09-05 21:47:34 +00008992 if (logging != MagickFalse)
8993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00008994 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008995 /*
8996 Initialize compression level and filtering.
8997 */
8998 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008999 {
9000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9001 " Setting up deflate compression");
9002
9003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9004 " Compression buffer size: 32768");
9005 }
9006
cristy3ed852e2009-09-05 21:47:34 +00009007 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009008
cristy3ed852e2009-09-05 21:47:34 +00009009 if (logging != MagickFalse)
9010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009012
cristy3ed852e2009-09-05 21:47:34 +00009013 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009014
cristy3ed852e2009-09-05 21:47:34 +00009015 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9016 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009017
cristy3ed852e2009-09-05 21:47:34 +00009018 if (quality > 9)
9019 {
9020 int
9021 level;
9022
cristybb503372010-05-27 20:51:26 +00009023 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009024
cristy3ed852e2009-09-05 21:47:34 +00009025 if (logging != MagickFalse)
9026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9027 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00009028
cristy3ed852e2009-09-05 21:47:34 +00009029 png_set_compression_level(ping,level);
9030 }
glennrp0fe50b42010-11-16 03:52:51 +00009031
cristy3ed852e2009-09-05 21:47:34 +00009032 else
9033 {
9034 if (logging != MagickFalse)
9035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9036 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009037
cristy3ed852e2009-09-05 21:47:34 +00009038 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9039 }
glennrp0fe50b42010-11-16 03:52:51 +00009040
cristy3ed852e2009-09-05 21:47:34 +00009041 if (logging != MagickFalse)
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009044
glennrp2b013e42010-11-24 16:55:50 +00009045#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009046 /* This became available in libpng-1.0.9. Output must be a MNG. */
9047 if (mng_info->write_mng && ((quality % 10) == 7))
9048 {
9049 if (logging != MagickFalse)
9050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9051 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009052
glennrp5af765f2010-03-30 11:12:18 +00009053 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009054 }
glennrp0fe50b42010-11-16 03:52:51 +00009055
cristy3ed852e2009-09-05 21:47:34 +00009056 else
9057 if (logging != MagickFalse)
9058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9059 " Filter_type: 0");
9060#endif
glennrp2b013e42010-11-24 16:55:50 +00009061
cristy3ed852e2009-09-05 21:47:34 +00009062 {
9063 int
9064 base_filter;
9065
9066 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009067 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009068
glennrp26c990a2010-11-23 02:23:20 +00009069 else
glennrp8640fb52010-11-23 15:48:26 +00009070 if ((quality % 10) != 5)
9071 base_filter=(int) quality % 10;
9072
9073 else
9074 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9075 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9076 (quality < 50))
9077 base_filter=PNG_NO_FILTERS;
9078
9079 else
9080 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009081
cristy3ed852e2009-09-05 21:47:34 +00009082 if (logging != MagickFalse)
9083 {
9084 if (base_filter == PNG_ALL_FILTERS)
9085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9086 " Base filter method: ADAPTIVE");
9087 else
9088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9089 " Base filter method: NONE");
9090 }
glennrp2b013e42010-11-24 16:55:50 +00009091
cristy3ed852e2009-09-05 21:47:34 +00009092 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9093 }
9094
glennrp823b55c2011-03-14 18:46:46 +00009095 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9096 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009097 {
9098 ResetImageProfileIterator(image);
9099 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009100 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009101 profile=GetImageProfile(image,name);
9102
9103 if (profile != (StringInfo *) NULL)
9104 {
glennrp5af765f2010-03-30 11:12:18 +00009105#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009106 if ((LocaleCompare(name,"ICC") == 0) ||
9107 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009108 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009109
9110 if (ping_exclude_iCCP == MagickFalse)
9111 {
9112 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009113#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009114 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009115#else
9116 (png_const_bytep) GetStringInfoDatum(profile),
9117#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009118 (png_uint_32) GetStringInfoLength(profile));
9119 }
glennrp26f37912010-12-23 16:22:42 +00009120 }
glennrp0fe50b42010-11-16 03:52:51 +00009121
glennrpc8cbc5d2011-01-01 00:12:34 +00009122 else
cristy3ed852e2009-09-05 21:47:34 +00009123#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009124 if (ping_exclude_zCCP == MagickFalse)
9125 {
glennrpcf002022011-01-30 02:38:15 +00009126 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009127 (unsigned char *) name,(unsigned char *) name,
9128 GetStringInfoDatum(profile),
9129 (png_uint_32) GetStringInfoLength(profile));
9130 }
9131 }
glennrp0b206f52011-01-07 04:55:32 +00009132
glennrpc8cbc5d2011-01-01 00:12:34 +00009133 if (logging != MagickFalse)
9134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9135 " Setting up text chunk with %s profile",name);
9136
9137 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009138 }
cristy3ed852e2009-09-05 21:47:34 +00009139 }
9140
9141#if defined(PNG_WRITE_sRGB_SUPPORTED)
9142 if ((mng_info->have_write_global_srgb == 0) &&
9143 ((image->rendering_intent != UndefinedIntent) ||
9144 (image->colorspace == sRGBColorspace)))
9145 {
glennrp26f37912010-12-23 16:22:42 +00009146 if (ping_exclude_sRGB == MagickFalse)
9147 {
9148 /*
9149 Note image rendering intent.
9150 */
9151 if (logging != MagickFalse)
9152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9153 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009154
glennrp26f37912010-12-23 16:22:42 +00009155 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009156 Magick_RenderingIntent_to_PNG_RenderingIntent(
9157 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009158
glennrp26f37912010-12-23 16:22:42 +00009159 if (ping_exclude_gAMA == MagickFalse)
9160 png_set_gAMA(ping,ping_info,0.45455);
9161 }
cristy3ed852e2009-09-05 21:47:34 +00009162 }
glennrp26f37912010-12-23 16:22:42 +00009163
glennrp5af765f2010-03-30 11:12:18 +00009164 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009165#endif
9166 {
glennrp2cc891a2010-12-24 13:44:32 +00009167 if (ping_exclude_gAMA == MagickFalse &&
9168 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009169 (image->gamma < .45 || image->gamma > .46)))
9170 {
cristy3ed852e2009-09-05 21:47:34 +00009171 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9172 {
9173 /*
9174 Note image gamma.
9175 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9176 */
9177 if (logging != MagickFalse)
9178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9179 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009180
cristy3ed852e2009-09-05 21:47:34 +00009181 png_set_gAMA(ping,ping_info,image->gamma);
9182 }
glennrp26f37912010-12-23 16:22:42 +00009183 }
glennrp2b013e42010-11-24 16:55:50 +00009184
glennrp26f37912010-12-23 16:22:42 +00009185 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009186 {
glennrp26f37912010-12-23 16:22:42 +00009187 if ((mng_info->have_write_global_chrm == 0) &&
9188 (image->chromaticity.red_primary.x != 0.0))
9189 {
9190 /*
9191 Note image chromaticity.
9192 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9193 */
9194 PrimaryInfo
9195 bp,
9196 gp,
9197 rp,
9198 wp;
cristy3ed852e2009-09-05 21:47:34 +00009199
glennrp26f37912010-12-23 16:22:42 +00009200 wp=image->chromaticity.white_point;
9201 rp=image->chromaticity.red_primary;
9202 gp=image->chromaticity.green_primary;
9203 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009204
glennrp26f37912010-12-23 16:22:42 +00009205 if (logging != MagickFalse)
9206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9207 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009208
glennrp26f37912010-12-23 16:22:42 +00009209 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9210 bp.x,bp.y);
9211 }
9212 }
cristy3ed852e2009-09-05 21:47:34 +00009213 }
glennrpdfd70802010-11-14 01:23:35 +00009214
glennrp5af765f2010-03-30 11:12:18 +00009215 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009216
9217 if (mng_info->write_mng)
9218 png_set_sig_bytes(ping,8);
9219
9220 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9221
glennrpd6bf1612010-12-17 17:28:54 +00009222 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009223 {
9224 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009225 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009226 {
glennrp5af765f2010-03-30 11:12:18 +00009227 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009228
glennrp5af765f2010-03-30 11:12:18 +00009229 if (ping_bit_depth < 8)
9230 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009231 }
glennrp0fe50b42010-11-16 03:52:51 +00009232
cristy3ed852e2009-09-05 21:47:34 +00009233 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009234 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009235 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009236 }
9237
glennrp0e8ea192010-12-24 18:00:33 +00009238 if (ping_need_colortype_warning != MagickFalse ||
9239 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009240 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009241 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009242 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009243 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009244 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009245 {
9246 if (logging != MagickFalse)
9247 {
glennrp0e8ea192010-12-24 18:00:33 +00009248 if (ping_need_colortype_warning != MagickFalse)
9249 {
9250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9251 " Image has transparency but tRNS chunk was excluded");
9252 }
9253
cristy3ed852e2009-09-05 21:47:34 +00009254 if (mng_info->write_png_depth)
9255 {
9256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9257 " Defined PNG:bit-depth=%u, Computed depth=%u",
9258 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009259 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009260 }
glennrp0e8ea192010-12-24 18:00:33 +00009261
cristy3ed852e2009-09-05 21:47:34 +00009262 if (mng_info->write_png_colortype)
9263 {
9264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9265 " Defined PNG:color-type=%u, Computed color type=%u",
9266 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009267 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009268 }
9269 }
glennrp0e8ea192010-12-24 18:00:33 +00009270
glennrp3bd2e412010-08-10 13:34:52 +00009271 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009272 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9273 }
9274
glennrp58e01762011-01-07 15:28:54 +00009275 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009276 {
9277 /* Add an opaque matte channel */
9278 image->matte = MagickTrue;
9279 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009280
glennrpb4a13412010-05-05 12:47:19 +00009281 if (logging != MagickFalse)
9282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9283 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009284 }
9285
glennrp0e319732011-01-25 21:53:13 +00009286 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009287 {
glennrp991d11d2010-11-12 21:55:28 +00009288 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009289 {
glennrp991d11d2010-11-12 21:55:28 +00009290 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009291 if (logging != MagickFalse)
9292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9293 " Setting ping_have_tRNS=MagickTrue.");
9294 }
glennrpe9c26dc2010-05-30 01:56:35 +00009295 }
9296
cristy3ed852e2009-09-05 21:47:34 +00009297 if (logging != MagickFalse)
9298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9299 " Writing PNG header chunks");
9300
glennrp5af765f2010-03-30 11:12:18 +00009301 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9302 ping_bit_depth,ping_color_type,
9303 ping_interlace_method,ping_compression_method,
9304 ping_filter_method);
9305
glennrp39992b42010-11-14 00:03:43 +00009306 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9307 {
glennrpf09bded2011-01-08 01:15:59 +00009308 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009309
glennrp3b51f0e2010-11-27 18:14:08 +00009310 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009311 {
glennrp8640fb52010-11-23 15:48:26 +00009312 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009313 {
glennrpd6bf1612010-12-17 17:28:54 +00009314 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009316 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9317 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009318 (int) palette[i].red,
9319 (int) palette[i].green,
9320 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009321 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009322 (int) ping_trans_alpha[i]);
9323 else
9324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009325 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009326 (int) i,
9327 (int) palette[i].red,
9328 (int) palette[i].green,
9329 (int) palette[i].blue);
9330 }
glennrp39992b42010-11-14 00:03:43 +00009331 }
glennrp39992b42010-11-14 00:03:43 +00009332 }
9333
glennrp26f37912010-12-23 16:22:42 +00009334 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009335 {
glennrp26f37912010-12-23 16:22:42 +00009336 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009337 {
glennrp26f37912010-12-23 16:22:42 +00009338 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009339 if (logging)
9340 {
9341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9342 " Setting up bKGD chunk");
9343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9344 " background color = (%d,%d,%d)",
9345 (int) ping_background.red,
9346 (int) ping_background.green,
9347 (int) ping_background.blue);
9348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9349 " index = %d, gray=%d",
9350 (int) ping_background.index,
9351 (int) ping_background.gray);
9352 }
9353 }
glennrp26f37912010-12-23 16:22:42 +00009354 }
9355
9356 if (ping_exclude_pHYs == MagickFalse)
9357 {
9358 if (ping_have_pHYs != MagickFalse)
9359 {
9360 png_set_pHYs(ping,ping_info,
9361 ping_pHYs_x_resolution,
9362 ping_pHYs_y_resolution,
9363 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009364
9365 if (logging)
9366 {
9367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9368 " Setting up pHYs chunk");
9369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9370 " x_resolution=%lu",
9371 (unsigned long) ping_pHYs_x_resolution);
9372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9373 " y_resolution=%lu",
9374 (unsigned long) ping_pHYs_y_resolution);
9375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9376 " unit_type=%lu",
9377 (unsigned long) ping_pHYs_unit_type);
9378 }
glennrp26f37912010-12-23 16:22:42 +00009379 }
glennrpdfd70802010-11-14 01:23:35 +00009380 }
9381
9382#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009383 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009384 {
glennrp26f37912010-12-23 16:22:42 +00009385 if (image->page.x || image->page.y)
9386 {
9387 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9388 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009389
glennrp26f37912010-12-23 16:22:42 +00009390 if (logging != MagickFalse)
9391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9392 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9393 (int) image->page.x, (int) image->page.y);
9394 }
glennrpdfd70802010-11-14 01:23:35 +00009395 }
9396#endif
9397
glennrpda8f3a72011-02-27 23:54:12 +00009398 if (mng_info->need_blob != MagickFalse)
9399 {
9400 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9401 MagickFalse)
9402 png_error(ping,"WriteBlob Failed");
9403
9404 ping_have_blob=MagickTrue;
9405 }
9406
cristy3ed852e2009-09-05 21:47:34 +00009407 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009408
glennrp39992b42010-11-14 00:03:43 +00009409 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009410 {
glennrp3b51f0e2010-11-27 18:14:08 +00009411 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009412 {
9413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9414 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9415 }
9416
9417 if (ping_color_type == 3)
9418 (void) png_set_tRNS(ping, ping_info,
9419 ping_trans_alpha,
9420 ping_num_trans,
9421 NULL);
9422
9423 else
9424 {
9425 (void) png_set_tRNS(ping, ping_info,
9426 NULL,
9427 0,
9428 &ping_trans_color);
9429
glennrp3b51f0e2010-11-27 18:14:08 +00009430 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009431 {
9432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009433 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009434 (int) ping_trans_color.red,
9435 (int) ping_trans_color.green,
9436 (int) ping_trans_color.blue);
9437 }
9438 }
glennrp991d11d2010-11-12 21:55:28 +00009439 }
9440
cristy3ed852e2009-09-05 21:47:34 +00009441 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009442 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009443
cristy3ed852e2009-09-05 21:47:34 +00009444 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009445
cristy3ed852e2009-09-05 21:47:34 +00009446 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009447 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009448
glennrp26f37912010-12-23 16:22:42 +00009449 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009450 {
glennrp4f25bd02011-01-01 18:51:28 +00009451 if ((image->page.width != 0 && image->page.width != image->columns) ||
9452 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009453 {
9454 unsigned char
9455 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009456
glennrp26f37912010-12-23 16:22:42 +00009457 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9458 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009459 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009460 PNGLong(chunk+4,(png_uint_32) image->page.width);
9461 PNGLong(chunk+8,(png_uint_32) image->page.height);
9462 chunk[12]=0; /* unit = pixels */
9463 (void) WriteBlob(image,13,chunk);
9464 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9465 }
cristy3ed852e2009-09-05 21:47:34 +00009466 }
9467
9468#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009469 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009470#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009471 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009472#undef PNG_HAVE_IDAT
9473#endif
9474
9475 png_set_packing(ping);
9476 /*
9477 Allocate memory.
9478 */
9479 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009480 if (image_depth > 8)
9481 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009482 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009483 {
glennrpb4a13412010-05-05 12:47:19 +00009484 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009485 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009486 break;
glennrp0fe50b42010-11-16 03:52:51 +00009487
glennrpb4a13412010-05-05 12:47:19 +00009488 case PNG_COLOR_TYPE_GRAY_ALPHA:
9489 rowbytes*=2;
9490 break;
glennrp0fe50b42010-11-16 03:52:51 +00009491
glennrpb4a13412010-05-05 12:47:19 +00009492 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009493 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009494 break;
glennrp0fe50b42010-11-16 03:52:51 +00009495
glennrpb4a13412010-05-05 12:47:19 +00009496 default:
9497 break;
cristy3ed852e2009-09-05 21:47:34 +00009498 }
glennrp3b51f0e2010-11-27 18:14:08 +00009499
9500 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009501 {
9502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9503 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009504
glennrpb4a13412010-05-05 12:47:19 +00009505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009506 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009507 }
glennrpcf002022011-01-30 02:38:15 +00009508 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9509 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009510
glennrpcf002022011-01-30 02:38:15 +00009511 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009512 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009513
cristy3ed852e2009-09-05 21:47:34 +00009514 /*
9515 Initialize image scanlines.
9516 */
glennrp5af765f2010-03-30 11:12:18 +00009517 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009518 {
9519 /*
9520 PNG write failed.
9521 */
9522#ifdef PNG_DEBUG
9523 if (image_info->verbose)
9524 (void) printf("PNG write has failed.\n");
9525#endif
9526 png_destroy_write_struct(&ping,&ping_info);
9527 if (quantum_info != (QuantumInfo *) NULL)
9528 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009529 if (ping_pixels != (unsigned char *) NULL)
9530 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009531#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009532 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009533#endif
glennrpda8f3a72011-02-27 23:54:12 +00009534 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009535 (void) CloseBlob(image);
9536 image_info=DestroyImageInfo(image_info);
9537 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009538 return(MagickFalse);
9539 }
cristyed552522009-10-16 14:04:35 +00009540 quantum_info=AcquireQuantumInfo(image_info,image);
9541 if (quantum_info == (QuantumInfo *) NULL)
9542 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009543 quantum_info->format=UndefinedQuantumFormat;
9544 quantum_info->depth=image_depth;
9545 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009546
cristy3ed852e2009-09-05 21:47:34 +00009547 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009548 !mng_info->write_png32) &&
9549 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009550 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009551 image_matte == MagickFalse &&
9552 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009553 {
glennrp8bb3a022010-12-13 20:40:04 +00009554 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009555 register const PixelPacket
9556 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009557
cristy3ed852e2009-09-05 21:47:34 +00009558 quantum_info->depth=8;
9559 for (pass=0; pass < num_passes; pass++)
9560 {
9561 /*
9562 Convert PseudoClass image to a PNG monochrome image.
9563 */
cristybb503372010-05-27 20:51:26 +00009564 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009565 {
glennrpd71e86a2011-02-24 01:28:37 +00009566 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9568 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009569
cristy3ed852e2009-09-05 21:47:34 +00009570 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009571
cristy3ed852e2009-09-05 21:47:34 +00009572 if (p == (const PixelPacket *) NULL)
9573 break;
glennrp0fe50b42010-11-16 03:52:51 +00009574
cristy3ed852e2009-09-05 21:47:34 +00009575 if (mng_info->IsPalette)
9576 {
9577 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009578 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009579 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9580 mng_info->write_png_depth &&
9581 mng_info->write_png_depth != old_bit_depth)
9582 {
9583 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009584 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009585 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009586 >> (8-old_bit_depth));
9587 }
9588 }
glennrp0fe50b42010-11-16 03:52:51 +00009589
cristy3ed852e2009-09-05 21:47:34 +00009590 else
9591 {
9592 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009593 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009594 }
glennrp0fe50b42010-11-16 03:52:51 +00009595
cristy3ed852e2009-09-05 21:47:34 +00009596 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009597 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009598 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009599 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009600
glennrp3b51f0e2010-11-27 18:14:08 +00009601 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9603 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009604
glennrpcf002022011-01-30 02:38:15 +00009605 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009606 }
9607 if (image->previous == (Image *) NULL)
9608 {
9609 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9610 if (status == MagickFalse)
9611 break;
9612 }
9613 }
9614 }
glennrp0fe50b42010-11-16 03:52:51 +00009615
glennrp8bb3a022010-12-13 20:40:04 +00009616 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009617 {
glennrp0fe50b42010-11-16 03:52:51 +00009618 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009619 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009620 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009621 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009622 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009623 {
glennrp8bb3a022010-12-13 20:40:04 +00009624 register const PixelPacket
9625 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009626
glennrp8bb3a022010-12-13 20:40:04 +00009627 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009628 {
glennrp8bb3a022010-12-13 20:40:04 +00009629
cristybb503372010-05-27 20:51:26 +00009630 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009631 {
9632 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009633
cristy3ed852e2009-09-05 21:47:34 +00009634 if (p == (const PixelPacket *) NULL)
9635 break;
glennrp2cc891a2010-12-24 13:44:32 +00009636
glennrp5af765f2010-03-30 11:12:18 +00009637 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009638 {
glennrp8bb3a022010-12-13 20:40:04 +00009639 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009640 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009641 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009642
glennrp8bb3a022010-12-13 20:40:04 +00009643 else
9644 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009645 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009646
glennrp3b51f0e2010-11-27 18:14:08 +00009647 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009649 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009650 }
glennrp2cc891a2010-12-24 13:44:32 +00009651
glennrp8bb3a022010-12-13 20:40:04 +00009652 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9653 {
9654 if (logging != MagickFalse && y == 0)
9655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9656 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009657
glennrp8bb3a022010-12-13 20:40:04 +00009658 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009659 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009660 }
glennrp2cc891a2010-12-24 13:44:32 +00009661
glennrp3b51f0e2010-11-27 18:14:08 +00009662 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009664 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009665
glennrpcf002022011-01-30 02:38:15 +00009666 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009667 }
glennrp2cc891a2010-12-24 13:44:32 +00009668
glennrp8bb3a022010-12-13 20:40:04 +00009669 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009670 {
glennrp8bb3a022010-12-13 20:40:04 +00009671 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9672 if (status == MagickFalse)
9673 break;
cristy3ed852e2009-09-05 21:47:34 +00009674 }
cristy3ed852e2009-09-05 21:47:34 +00009675 }
9676 }
glennrp8bb3a022010-12-13 20:40:04 +00009677
9678 else
9679 {
9680 register const PixelPacket
9681 *p;
9682
9683 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009684 {
glennrp8bb3a022010-12-13 20:40:04 +00009685 if ((image_depth > 8) || (mng_info->write_png24 ||
9686 mng_info->write_png32 ||
9687 (!mng_info->write_png8 && !mng_info->IsPalette)))
9688 {
9689 for (y=0; y < (ssize_t) image->rows; y++)
9690 {
9691 p=GetVirtualPixels(image,0,y,image->columns,1,
9692 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009693
glennrp8bb3a022010-12-13 20:40:04 +00009694 if (p == (const PixelPacket *) NULL)
9695 break;
glennrp2cc891a2010-12-24 13:44:32 +00009696
glennrp8bb3a022010-12-13 20:40:04 +00009697 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9698 {
9699 if (image->storage_class == DirectClass)
9700 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009701 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009702
glennrp8bb3a022010-12-13 20:40:04 +00009703 else
9704 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009705 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009706 }
glennrp2cc891a2010-12-24 13:44:32 +00009707
glennrp8bb3a022010-12-13 20:40:04 +00009708 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9709 {
9710 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009711 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009712 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009713
glennrp8bb3a022010-12-13 20:40:04 +00009714 if (logging != MagickFalse && y == 0)
9715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9716 " Writing GRAY_ALPHA PNG pixels (3)");
9717 }
glennrp2cc891a2010-12-24 13:44:32 +00009718
glennrp8bb3a022010-12-13 20:40:04 +00009719 else if (image_matte != MagickFalse)
9720 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009721 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009722
glennrp8bb3a022010-12-13 20:40:04 +00009723 else
9724 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009725 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009726
glennrp8bb3a022010-12-13 20:40:04 +00009727 if (logging != MagickFalse && y == 0)
9728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9729 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009730
glennrpcf002022011-01-30 02:38:15 +00009731 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009732 }
9733 }
glennrp2cc891a2010-12-24 13:44:32 +00009734
glennrp8bb3a022010-12-13 20:40:04 +00009735 else
9736 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9737 mng_info->write_png32 ||
9738 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9739 {
9740 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9741 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9742 {
9743 if (logging != MagickFalse)
9744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9745 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009746
glennrp8bb3a022010-12-13 20:40:04 +00009747 quantum_info->depth=8;
9748 image_depth=8;
9749 }
glennrp2cc891a2010-12-24 13:44:32 +00009750
glennrp8bb3a022010-12-13 20:40:04 +00009751 for (y=0; y < (ssize_t) image->rows; y++)
9752 {
9753 if (logging != MagickFalse && y == 0)
9754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9755 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009756
glennrp770d1932011-03-06 22:11:17 +00009757 p=GetVirtualPixels(image,0,y,image->columns,1,
9758 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009759
glennrp8bb3a022010-12-13 20:40:04 +00009760 if (p == (const PixelPacket *) NULL)
9761 break;
glennrp2cc891a2010-12-24 13:44:32 +00009762
glennrp8bb3a022010-12-13 20:40:04 +00009763 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009764 {
glennrp4bf89732011-03-21 13:48:28 +00009765 quantum_info->depth=image->depth;
9766
glennrp44757ab2011-03-17 12:57:03 +00009767 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009768 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009769 }
glennrp2cc891a2010-12-24 13:44:32 +00009770
glennrp8bb3a022010-12-13 20:40:04 +00009771 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9772 {
9773 if (logging != MagickFalse && y == 0)
9774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9775 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009776
glennrp8bb3a022010-12-13 20:40:04 +00009777 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009778 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009779 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009780 }
glennrp2cc891a2010-12-24 13:44:32 +00009781
glennrp8bb3a022010-12-13 20:40:04 +00009782 else
glennrp8bb3a022010-12-13 20:40:04 +00009783 {
glennrp179d0752011-03-17 13:02:10 +00009784 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009785 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9786
9787 if (logging != MagickFalse && y <= 2)
9788 {
9789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009790 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009791
9792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9793 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9794 (int)ping_pixels[0],(int)ping_pixels[1]);
9795 }
glennrp8bb3a022010-12-13 20:40:04 +00009796 }
glennrpcf002022011-01-30 02:38:15 +00009797 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009798 }
9799 }
glennrp2cc891a2010-12-24 13:44:32 +00009800
glennrp8bb3a022010-12-13 20:40:04 +00009801 if (image->previous == (Image *) NULL)
9802 {
9803 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9804 if (status == MagickFalse)
9805 break;
9806 }
cristy3ed852e2009-09-05 21:47:34 +00009807 }
glennrp8bb3a022010-12-13 20:40:04 +00009808 }
9809 }
9810
cristyb32b90a2009-09-07 21:45:48 +00009811 if (quantum_info != (QuantumInfo *) NULL)
9812 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009813
9814 if (logging != MagickFalse)
9815 {
9816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009817 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009818
cristy3ed852e2009-09-05 21:47:34 +00009819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009820 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009821
cristy3ed852e2009-09-05 21:47:34 +00009822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009823 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009824
cristy3ed852e2009-09-05 21:47:34 +00009825 if (mng_info->write_png_depth)
9826 {
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9829 }
glennrp0fe50b42010-11-16 03:52:51 +00009830
cristy3ed852e2009-09-05 21:47:34 +00009831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009832 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009833
cristy3ed852e2009-09-05 21:47:34 +00009834 if (mng_info->write_png_colortype)
9835 {
9836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9838 }
glennrp0fe50b42010-11-16 03:52:51 +00009839
cristy3ed852e2009-09-05 21:47:34 +00009840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009841 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009842
cristy3ed852e2009-09-05 21:47:34 +00009843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009844 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009845 }
9846 /*
glennrpa0ed0092011-04-18 16:36:29 +00009847 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009848 */
glennrp823b55c2011-03-14 18:46:46 +00009849 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009850 {
glennrp26f37912010-12-23 16:22:42 +00009851 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009852 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009853 while (property != (const char *) NULL)
9854 {
9855 png_textp
9856 text;
glennrp2cc891a2010-12-24 13:44:32 +00009857
glennrp26f37912010-12-23 16:22:42 +00009858 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009859
9860 /* Don't write any "png:" properties; those are just for "identify" */
9861 if (LocaleNCompare(property,"png:",4) != 0 &&
9862
9863 /* Suppress density and units if we wrote a pHYs chunk */
9864 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009865 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009866 LocaleCompare(property,"units") != 0) &&
9867
9868 /* Suppress the IM-generated Date:create and Date:modify */
9869 (ping_exclude_date == MagickFalse ||
9870 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009871 {
glennrpc70af4a2011-03-07 00:08:23 +00009872 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009873 {
glennrpc70af4a2011-03-07 00:08:23 +00009874 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9875 text[0].key=(char *) property;
9876 text[0].text=(char *) value;
9877 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009878
glennrpc70af4a2011-03-07 00:08:23 +00009879 if (ping_exclude_tEXt != MagickFalse)
9880 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9881
9882 else if (ping_exclude_zTXt != MagickFalse)
9883 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9884
9885 else
glennrp26f37912010-12-23 16:22:42 +00009886 {
glennrpc70af4a2011-03-07 00:08:23 +00009887 text[0].compression=image_info->compression == NoCompression ||
9888 (image_info->compression == UndefinedCompression &&
9889 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9890 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009891 }
glennrp2cc891a2010-12-24 13:44:32 +00009892
glennrpc70af4a2011-03-07 00:08:23 +00009893 if (logging != MagickFalse)
9894 {
9895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9896 " Setting up text chunk");
9897
9898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9899 " keyword: %s",text[0].key);
9900 }
9901
9902 png_set_text(ping,ping_info,text,1);
9903 png_free(ping,text);
9904 }
glennrp26f37912010-12-23 16:22:42 +00009905 }
9906 property=GetNextImageProperty(image);
9907 }
cristy3ed852e2009-09-05 21:47:34 +00009908 }
9909
9910 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009911 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009912
9913 if (logging != MagickFalse)
9914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9915 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009916
cristy3ed852e2009-09-05 21:47:34 +00009917 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009918
cristy3ed852e2009-09-05 21:47:34 +00009919 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9920 {
9921 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009922 (ping_width != mng_info->page.width) ||
9923 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009924 {
9925 unsigned char
9926 chunk[32];
9927
9928 /*
9929 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9930 */
9931 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9932 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009933 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009934 chunk[4]=4;
9935 chunk[5]=0; /* frame name separator (no name) */
9936 chunk[6]=1; /* flag for changing delay, for next frame only */
9937 chunk[7]=0; /* flag for changing frame timeout */
9938 chunk[8]=1; /* flag for changing frame clipping for next frame */
9939 chunk[9]=0; /* flag for changing frame sync_id */
9940 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9941 chunk[14]=0; /* clipping boundaries delta type */
9942 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9943 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009944 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009945 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9946 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009947 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009948 (void) WriteBlob(image,31,chunk);
9949 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9950 mng_info->old_framing_mode=4;
9951 mng_info->framing_mode=1;
9952 }
glennrp0fe50b42010-11-16 03:52:51 +00009953
cristy3ed852e2009-09-05 21:47:34 +00009954 else
9955 mng_info->framing_mode=3;
9956 }
9957 if (mng_info->write_mng && !mng_info->need_fram &&
9958 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009959 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009960 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009961 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009962
cristy3ed852e2009-09-05 21:47:34 +00009963 /*
9964 Free PNG resources.
9965 */
glennrp5af765f2010-03-30 11:12:18 +00009966
cristy3ed852e2009-09-05 21:47:34 +00009967 png_destroy_write_struct(&ping,&ping_info);
9968
glennrpcf002022011-01-30 02:38:15 +00009969 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009970
9971#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009972 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009973#endif
9974
glennrpda8f3a72011-02-27 23:54:12 +00009975 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009976 (void) CloseBlob(image);
9977
9978 image_info=DestroyImageInfo(image_info);
9979 image=DestroyImage(image);
9980
9981 /* Store bit depth actually written */
9982 s[0]=(char) ping_bit_depth;
9983 s[1]='\0';
9984
9985 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9986
cristy3ed852e2009-09-05 21:47:34 +00009987 if (logging != MagickFalse)
9988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9989 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009990
cristy3ed852e2009-09-05 21:47:34 +00009991 return(MagickTrue);
9992/* End write one PNG image */
9993}
9994
9995/*
9996%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9997% %
9998% %
9999% %
10000% W r i t e P N G I m a g e %
10001% %
10002% %
10003% %
10004%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10005%
10006% WritePNGImage() writes a Portable Network Graphics (PNG) or
10007% Multiple-image Network Graphics (MNG) image file.
10008%
10009% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10010%
10011% The format of the WritePNGImage method is:
10012%
10013% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10014%
10015% A description of each parameter follows:
10016%
10017% o image_info: the image info.
10018%
10019% o image: The image.
10020%
10021% Returns MagickTrue on success, MagickFalse on failure.
10022%
10023% Communicating with the PNG encoder:
10024%
10025% While the datastream written is always in PNG format and normally would
10026% be given the "png" file extension, this method also writes the following
10027% pseudo-formats which are subsets of PNG:
10028%
glennrp5a39f372011-02-25 04:52:16 +000010029% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10030% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010031% is present, the tRNS chunk must only have values 0 and 255
10032% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010033% transparent). If other values are present they will be
10034% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010035% colors are present, they will be quantized to the 4-4-4-1,
10036% 3-3-3-1, or 3-3-2-1 palette.
10037%
10038% If you want better quantization or dithering of the colors
10039% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010040% PNG encoder. The pixels contain 8-bit indices even if
10041% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010042% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010043% PNG grayscale type might be slightly more efficient. Please
10044% note that writing to the PNG8 format may result in loss
10045% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010046%
10047% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10048% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010049% one of the colors as transparent. The only loss incurred
10050% is reduction of sample depth to 8. If the image has more
10051% than one transparent color, has semitransparent pixels, or
10052% has an opaque pixel with the same RGB components as the
10053% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010054%
10055% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10056% transparency is permitted, i.e., the alpha sample for
10057% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010058% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010059% The only loss in data is the reduction of the sample depth
10060% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010061%
10062% o -define: For more precise control of the PNG output, you can use the
10063% Image options "png:bit-depth" and "png:color-type". These
10064% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010065% from the application programming interfaces. The options
10066% are case-independent and are converted to lowercase before
10067% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010068%
10069% png:color-type can be 0, 2, 3, 4, or 6.
10070%
10071% When png:color-type is 0 (Grayscale), png:bit-depth can
10072% be 1, 2, 4, 8, or 16.
10073%
10074% When png:color-type is 2 (RGB), png:bit-depth can
10075% be 8 or 16.
10076%
10077% When png:color-type is 3 (Indexed), png:bit-depth can
10078% be 1, 2, 4, or 8. This refers to the number of bits
10079% used to store the index. The color samples always have
10080% bit-depth 8 in indexed PNG files.
10081%
10082% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10083% png:bit-depth can be 8 or 16.
10084%
glennrp5a39f372011-02-25 04:52:16 +000010085% If the image cannot be written without loss with the requested bit-depth
10086% and color-type, a PNG file will not be written, and the encoder will
10087% return MagickFalse.
10088%
cristy3ed852e2009-09-05 21:47:34 +000010089% Since image encoders should not be responsible for the "heavy lifting",
10090% the user should make sure that ImageMagick has already reduced the
10091% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010092% transparency prior to attempting to write the image with depth, color,
10093% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010094%
glennrp97cefe22011-04-22 16:17:00 +000010095% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010096%
cristy3ed852e2009-09-05 21:47:34 +000010097% Note that another definition, "png:bit-depth-written" exists, but it
10098% is not intended for external use. It is only used internally by the
10099% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10100%
10101% It is possible to request that the PNG encoder write previously-formatted
10102% ancillary chunks in the output PNG file, using the "-profile" commandline
10103% option as shown below or by setting the profile via a programming
10104% interface:
10105%
10106% -profile PNG-chunk-x:<file>
10107%
10108% where x is a location flag and <file> is a file containing the chunk
10109% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010110% This encoder will compute the chunk length and CRC, so those must not
10111% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010112%
10113% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10114% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10115% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010116% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010117%
glennrpbb8a7332010-11-13 15:17:35 +000010118% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010119%
glennrp3241bd02010-12-12 04:36:28 +000010120% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010121%
glennrpd6afd542010-11-19 01:53:05 +000010122% o 32-bit depth is reduced to 16.
10123% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10124% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010125% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010126% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010127% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010128% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10129% this can be done without loss and a larger bit depth N was not
10130% requested via the "-define PNG:bit-depth=N" option.
10131% o If matte channel is present but only one transparent color is
10132% present, RGB+tRNS is written instead of RGBA
10133% o Opaque matte channel is removed (or added, if color-type 4 or 6
10134% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010135%
cristy3ed852e2009-09-05 21:47:34 +000010136%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10137*/
10138static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10139 Image *image)
10140{
10141 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010142 excluding,
10143 logging,
10144 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010145 status;
10146
10147 MngInfo
10148 *mng_info;
10149
10150 const char
10151 *value;
10152
10153 int
glennrp21f0e622011-01-07 16:20:57 +000010154 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010155 source;
10156
cristy3ed852e2009-09-05 21:47:34 +000010157 /*
10158 Open image file.
10159 */
10160 assert(image_info != (const ImageInfo *) NULL);
10161 assert(image_info->signature == MagickSignature);
10162 assert(image != (Image *) NULL);
10163 assert(image->signature == MagickSignature);
10164 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010165 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010166 /*
10167 Allocate a MngInfo structure.
10168 */
10169 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010170 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010171
cristy3ed852e2009-09-05 21:47:34 +000010172 if (mng_info == (MngInfo *) NULL)
10173 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010174
cristy3ed852e2009-09-05 21:47:34 +000010175 /*
10176 Initialize members of the MngInfo structure.
10177 */
10178 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10179 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010180 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010181 have_mng_structure=MagickTrue;
10182
10183 /* See if user has requested a specific PNG subformat */
10184
10185 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10186 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10187 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10188
10189 if (mng_info->write_png8)
10190 {
glennrp9c1eb072010-06-06 22:19:15 +000010191 mng_info->write_png_colortype = /* 3 */ 4;
10192 mng_info->write_png_depth = 8;
10193 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010194 }
10195
10196 if (mng_info->write_png24)
10197 {
glennrp9c1eb072010-06-06 22:19:15 +000010198 mng_info->write_png_colortype = /* 2 */ 3;
10199 mng_info->write_png_depth = 8;
10200 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010201
glennrp9c1eb072010-06-06 22:19:15 +000010202 if (image->matte == MagickTrue)
10203 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010204
glennrp9c1eb072010-06-06 22:19:15 +000010205 else
10206 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010207
glennrp9c1eb072010-06-06 22:19:15 +000010208 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010209 }
10210
10211 if (mng_info->write_png32)
10212 {
glennrp9c1eb072010-06-06 22:19:15 +000010213 mng_info->write_png_colortype = /* 6 */ 7;
10214 mng_info->write_png_depth = 8;
10215 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010216
glennrp9c1eb072010-06-06 22:19:15 +000010217 if (image->matte == MagickTrue)
10218 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010219
glennrp9c1eb072010-06-06 22:19:15 +000010220 else
10221 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010222
glennrp9c1eb072010-06-06 22:19:15 +000010223 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010224 }
10225
10226 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010227
cristy3ed852e2009-09-05 21:47:34 +000010228 if (value != (char *) NULL)
10229 {
10230 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010231 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010232
cristy3ed852e2009-09-05 21:47:34 +000010233 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010234 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010235
cristy3ed852e2009-09-05 21:47:34 +000010236 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010237 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010238
cristy3ed852e2009-09-05 21:47:34 +000010239 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010240 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010241
cristy3ed852e2009-09-05 21:47:34 +000010242 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010243 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010244
glennrpbb8a7332010-11-13 15:17:35 +000010245 else
10246 (void) ThrowMagickException(&image->exception,
10247 GetMagickModule(),CoderWarning,
10248 "ignoring invalid defined png:bit-depth",
10249 "=%s",value);
10250
cristy3ed852e2009-09-05 21:47:34 +000010251 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010253 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010254 }
glennrp0fe50b42010-11-16 03:52:51 +000010255
cristy3ed852e2009-09-05 21:47:34 +000010256 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010257
cristy3ed852e2009-09-05 21:47:34 +000010258 if (value != (char *) NULL)
10259 {
10260 /* We must store colortype+1 because 0 is a valid colortype */
10261 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010262 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010263
cristy3ed852e2009-09-05 21:47:34 +000010264 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010265 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010266
cristy3ed852e2009-09-05 21:47:34 +000010267 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010268 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010269
cristy3ed852e2009-09-05 21:47:34 +000010270 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010271 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010272
cristy3ed852e2009-09-05 21:47:34 +000010273 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010274 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010275
glennrpbb8a7332010-11-13 15:17:35 +000010276 else
10277 (void) ThrowMagickException(&image->exception,
10278 GetMagickModule(),CoderWarning,
10279 "ignoring invalid defined png:color-type",
10280 "=%s",value);
10281
cristy3ed852e2009-09-05 21:47:34 +000010282 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010284 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010285 }
10286
glennrp0e8ea192010-12-24 18:00:33 +000010287 /* Check for chunks to be excluded:
10288 *
glennrp0dff56c2011-01-29 19:10:02 +000010289 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010290 * listed in the "unused_chunks" array, above.
10291 *
10292 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10293 * define (in the image properties or in the image artifacts)
10294 * or via a mng_info member. For convenience, in addition
10295 * to or instead of a comma-separated list of chunks, the
10296 * "exclude-chunk" string can be simply "all" or "none".
10297 *
10298 * The exclude-chunk define takes priority over the mng_info.
10299 *
10300 * A "PNG:include-chunk" define takes priority over both the
10301 * mng_info and the "PNG:exclude-chunk" define. Like the
10302 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010303 * well as a comma-separated list. Chunks that are unknown to
10304 * ImageMagick are always excluded, regardless of their "copy-safe"
10305 * status according to the PNG specification, and even if they
10306 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010307 *
10308 * Finally, all chunks listed in the "unused_chunks" array are
10309 * automatically excluded, regardless of the other instructions
10310 * or lack thereof.
10311 *
10312 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10313 * will not be written and the gAMA chunk will only be written if it
10314 * is not between .45 and .46, or approximately (1.0/2.2).
10315 *
10316 * If you exclude tRNS and the image has transparency, the colortype
10317 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10318 *
10319 * The -strip option causes StripImage() to set the png:include-chunk
10320 * artifact to "none,gama".
10321 */
10322
glennrp26f37912010-12-23 16:22:42 +000010323 mng_info->ping_exclude_bKGD=MagickFalse;
10324 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010325 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010326 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10327 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010328 mng_info->ping_exclude_iCCP=MagickFalse;
10329 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10330 mng_info->ping_exclude_oFFs=MagickFalse;
10331 mng_info->ping_exclude_pHYs=MagickFalse;
10332 mng_info->ping_exclude_sRGB=MagickFalse;
10333 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010334 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010335 mng_info->ping_exclude_vpAg=MagickFalse;
10336 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10337 mng_info->ping_exclude_zTXt=MagickFalse;
10338
glennrp8d3d6e52011-04-19 04:39:51 +000010339 mng_info->ping_preserve_colormap=MagickFalse;
10340
10341 value=GetImageArtifact(image,"png:preserve-colormap");
10342 if (value == NULL)
10343 value=GetImageOption(image_info,"png:preserve-colormap");
10344 if (value != NULL)
10345 mng_info->ping_preserve_colormap=MagickTrue;
10346
glennrp03812ae2010-12-24 01:31:34 +000010347 excluding=MagickFalse;
10348
glennrp5c7cf4e2010-12-24 00:30:00 +000010349 for (source=0; source<1; source++)
10350 {
10351 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010352 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010353 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010354
10355 if (value == NULL)
10356 value=GetImageArtifact(image,"png:exclude-chunks");
10357 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010358 else
glennrpacba0042010-12-24 14:27:26 +000010359 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010360 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010361
glennrpacba0042010-12-24 14:27:26 +000010362 if (value == NULL)
10363 value=GetImageOption(image_info,"png:exclude-chunks");
10364 }
10365
glennrp03812ae2010-12-24 01:31:34 +000010366 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010367 {
glennrp03812ae2010-12-24 01:31:34 +000010368
10369 size_t
10370 last;
10371
10372 excluding=MagickTrue;
10373
10374 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010375 {
10376 if (source == 0)
10377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10378 " png:exclude-chunk=%s found in image artifacts.\n", value);
10379 else
10380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10381 " png:exclude-chunk=%s found in image properties.\n", value);
10382 }
glennrp03812ae2010-12-24 01:31:34 +000010383
10384 last=strlen(value);
10385
10386 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010387 {
glennrp03812ae2010-12-24 01:31:34 +000010388
10389 if (LocaleNCompare(value+i,"all",3) == 0)
10390 {
10391 mng_info->ping_exclude_bKGD=MagickTrue;
10392 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010393 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010394 mng_info->ping_exclude_EXIF=MagickTrue;
10395 mng_info->ping_exclude_gAMA=MagickTrue;
10396 mng_info->ping_exclude_iCCP=MagickTrue;
10397 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10398 mng_info->ping_exclude_oFFs=MagickTrue;
10399 mng_info->ping_exclude_pHYs=MagickTrue;
10400 mng_info->ping_exclude_sRGB=MagickTrue;
10401 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010402 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010403 mng_info->ping_exclude_vpAg=MagickTrue;
10404 mng_info->ping_exclude_zCCP=MagickTrue;
10405 mng_info->ping_exclude_zTXt=MagickTrue;
10406 i--;
10407 }
glennrp2cc891a2010-12-24 13:44:32 +000010408
glennrp03812ae2010-12-24 01:31:34 +000010409 if (LocaleNCompare(value+i,"none",4) == 0)
10410 {
10411 mng_info->ping_exclude_bKGD=MagickFalse;
10412 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010413 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010414 mng_info->ping_exclude_EXIF=MagickFalse;
10415 mng_info->ping_exclude_gAMA=MagickFalse;
10416 mng_info->ping_exclude_iCCP=MagickFalse;
10417 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10418 mng_info->ping_exclude_oFFs=MagickFalse;
10419 mng_info->ping_exclude_pHYs=MagickFalse;
10420 mng_info->ping_exclude_sRGB=MagickFalse;
10421 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010422 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010423 mng_info->ping_exclude_vpAg=MagickFalse;
10424 mng_info->ping_exclude_zCCP=MagickFalse;
10425 mng_info->ping_exclude_zTXt=MagickFalse;
10426 }
glennrp2cc891a2010-12-24 13:44:32 +000010427
glennrp03812ae2010-12-24 01:31:34 +000010428 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10429 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010430
glennrp03812ae2010-12-24 01:31:34 +000010431 if (LocaleNCompare(value+i,"chrm",4) == 0)
10432 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010433
glennrpa0ed0092011-04-18 16:36:29 +000010434 if (LocaleNCompare(value+i,"date",4) == 0)
10435 mng_info->ping_exclude_date=MagickTrue;
10436
glennrp03812ae2010-12-24 01:31:34 +000010437 if (LocaleNCompare(value+i,"exif",4) == 0)
10438 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010439
glennrp03812ae2010-12-24 01:31:34 +000010440 if (LocaleNCompare(value+i,"gama",4) == 0)
10441 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010442
glennrp03812ae2010-12-24 01:31:34 +000010443 if (LocaleNCompare(value+i,"iccp",4) == 0)
10444 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010445
glennrp03812ae2010-12-24 01:31:34 +000010446 /*
10447 if (LocaleNCompare(value+i,"itxt",4) == 0)
10448 mng_info->ping_exclude_iTXt=MagickTrue;
10449 */
glennrp2cc891a2010-12-24 13:44:32 +000010450
glennrp03812ae2010-12-24 01:31:34 +000010451 if (LocaleNCompare(value+i,"gama",4) == 0)
10452 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010453
glennrp03812ae2010-12-24 01:31:34 +000010454 if (LocaleNCompare(value+i,"offs",4) == 0)
10455 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010456
glennrp03812ae2010-12-24 01:31:34 +000010457 if (LocaleNCompare(value+i,"phys",4) == 0)
10458 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010459
glennrpa1e3b7b2010-12-24 16:37:33 +000010460 if (LocaleNCompare(value+i,"srgb",4) == 0)
10461 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010462
glennrp03812ae2010-12-24 01:31:34 +000010463 if (LocaleNCompare(value+i,"text",4) == 0)
10464 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010465
glennrpa1e3b7b2010-12-24 16:37:33 +000010466 if (LocaleNCompare(value+i,"trns",4) == 0)
10467 mng_info->ping_exclude_tRNS=MagickTrue;
10468
glennrp03812ae2010-12-24 01:31:34 +000010469 if (LocaleNCompare(value+i,"vpag",4) == 0)
10470 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010471
glennrp03812ae2010-12-24 01:31:34 +000010472 if (LocaleNCompare(value+i,"zccp",4) == 0)
10473 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010474
glennrp03812ae2010-12-24 01:31:34 +000010475 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10476 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010477
glennrp03812ae2010-12-24 01:31:34 +000010478 }
glennrpce91ed52010-12-23 22:37:49 +000010479 }
glennrp26f37912010-12-23 16:22:42 +000010480 }
10481
glennrp5c7cf4e2010-12-24 00:30:00 +000010482 for (source=0; source<1; source++)
10483 {
10484 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010485 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010486 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010487
10488 if (value == NULL)
10489 value=GetImageArtifact(image,"png:include-chunks");
10490 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010491 else
glennrpacba0042010-12-24 14:27:26 +000010492 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010493 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010494
glennrpacba0042010-12-24 14:27:26 +000010495 if (value == NULL)
10496 value=GetImageOption(image_info,"png:include-chunks");
10497 }
10498
glennrp03812ae2010-12-24 01:31:34 +000010499 if (value != NULL)
10500 {
10501 size_t
10502 last;
glennrp26f37912010-12-23 16:22:42 +000010503
glennrp03812ae2010-12-24 01:31:34 +000010504 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010505
glennrp03812ae2010-12-24 01:31:34 +000010506 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010507 {
10508 if (source == 0)
10509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10510 " png:include-chunk=%s found in image artifacts.\n", value);
10511 else
10512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10513 " png:include-chunk=%s found in image properties.\n", value);
10514 }
glennrp03812ae2010-12-24 01:31:34 +000010515
10516 last=strlen(value);
10517
10518 for (i=0; i<(int) last; i+=5)
10519 {
10520 if (LocaleNCompare(value+i,"all",3) == 0)
10521 {
10522 mng_info->ping_exclude_bKGD=MagickFalse;
10523 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010524 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010525 mng_info->ping_exclude_EXIF=MagickFalse;
10526 mng_info->ping_exclude_gAMA=MagickFalse;
10527 mng_info->ping_exclude_iCCP=MagickFalse;
10528 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10529 mng_info->ping_exclude_oFFs=MagickFalse;
10530 mng_info->ping_exclude_pHYs=MagickFalse;
10531 mng_info->ping_exclude_sRGB=MagickFalse;
10532 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010533 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010534 mng_info->ping_exclude_vpAg=MagickFalse;
10535 mng_info->ping_exclude_zCCP=MagickFalse;
10536 mng_info->ping_exclude_zTXt=MagickFalse;
10537 i--;
10538 }
glennrp2cc891a2010-12-24 13:44:32 +000010539
glennrp03812ae2010-12-24 01:31:34 +000010540 if (LocaleNCompare(value+i,"none",4) == 0)
10541 {
10542 mng_info->ping_exclude_bKGD=MagickTrue;
10543 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010544 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010545 mng_info->ping_exclude_EXIF=MagickTrue;
10546 mng_info->ping_exclude_gAMA=MagickTrue;
10547 mng_info->ping_exclude_iCCP=MagickTrue;
10548 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10549 mng_info->ping_exclude_oFFs=MagickTrue;
10550 mng_info->ping_exclude_pHYs=MagickTrue;
10551 mng_info->ping_exclude_sRGB=MagickTrue;
10552 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010553 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010554 mng_info->ping_exclude_vpAg=MagickTrue;
10555 mng_info->ping_exclude_zCCP=MagickTrue;
10556 mng_info->ping_exclude_zTXt=MagickTrue;
10557 }
glennrp2cc891a2010-12-24 13:44:32 +000010558
glennrp03812ae2010-12-24 01:31:34 +000010559 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10560 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010561
glennrp03812ae2010-12-24 01:31:34 +000010562 if (LocaleNCompare(value+i,"chrm",4) == 0)
10563 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010564
glennrpa0ed0092011-04-18 16:36:29 +000010565 if (LocaleNCompare(value+i,"date",4) == 0)
10566 mng_info->ping_exclude_date=MagickFalse;
10567
glennrp03812ae2010-12-24 01:31:34 +000010568 if (LocaleNCompare(value+i,"exif",4) == 0)
10569 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010570
glennrp03812ae2010-12-24 01:31:34 +000010571 if (LocaleNCompare(value+i,"gama",4) == 0)
10572 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010573
glennrp03812ae2010-12-24 01:31:34 +000010574 if (LocaleNCompare(value+i,"iccp",4) == 0)
10575 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010576
glennrp03812ae2010-12-24 01:31:34 +000010577 /*
10578 if (LocaleNCompare(value+i,"itxt",4) == 0)
10579 mng_info->ping_exclude_iTXt=MagickFalse;
10580 */
glennrp2cc891a2010-12-24 13:44:32 +000010581
glennrp03812ae2010-12-24 01:31:34 +000010582 if (LocaleNCompare(value+i,"gama",4) == 0)
10583 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010584
glennrp03812ae2010-12-24 01:31:34 +000010585 if (LocaleNCompare(value+i,"offs",4) == 0)
10586 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010587
glennrp03812ae2010-12-24 01:31:34 +000010588 if (LocaleNCompare(value+i,"phys",4) == 0)
10589 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010590
glennrpa1e3b7b2010-12-24 16:37:33 +000010591 if (LocaleNCompare(value+i,"srgb",4) == 0)
10592 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010593
glennrp03812ae2010-12-24 01:31:34 +000010594 if (LocaleNCompare(value+i,"text",4) == 0)
10595 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010596
glennrpa1e3b7b2010-12-24 16:37:33 +000010597 if (LocaleNCompare(value+i,"trns",4) == 0)
10598 mng_info->ping_exclude_tRNS=MagickFalse;
10599
glennrp03812ae2010-12-24 01:31:34 +000010600 if (LocaleNCompare(value+i,"vpag",4) == 0)
10601 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010602
glennrp03812ae2010-12-24 01:31:34 +000010603 if (LocaleNCompare(value+i,"zccp",4) == 0)
10604 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010605
glennrp03812ae2010-12-24 01:31:34 +000010606 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10607 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010608
glennrp03812ae2010-12-24 01:31:34 +000010609 }
glennrpce91ed52010-12-23 22:37:49 +000010610 }
glennrp26f37912010-12-23 16:22:42 +000010611 }
10612
glennrp03812ae2010-12-24 01:31:34 +000010613 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010614 {
10615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10616 " Chunks to be excluded from the output PNG:");
10617 if (mng_info->ping_exclude_bKGD != MagickFalse)
10618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10619 " bKGD");
10620 if (mng_info->ping_exclude_cHRM != MagickFalse)
10621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010623 if (mng_info->ping_exclude_date != MagickFalse)
10624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10625 " date");
glennrp26f37912010-12-23 16:22:42 +000010626 if (mng_info->ping_exclude_EXIF != MagickFalse)
10627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10628 " EXIF");
10629 if (mng_info->ping_exclude_gAMA != MagickFalse)
10630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631 " gAMA");
10632 if (mng_info->ping_exclude_iCCP != MagickFalse)
10633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10634 " iCCP");
10635/*
10636 if (mng_info->ping_exclude_iTXt != MagickFalse)
10637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10638 " iTXt");
10639*/
10640 if (mng_info->ping_exclude_oFFs != MagickFalse)
10641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10642 " oFFs");
10643 if (mng_info->ping_exclude_pHYs != MagickFalse)
10644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10645 " pHYs");
10646 if (mng_info->ping_exclude_sRGB != MagickFalse)
10647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10648 " sRGB");
10649 if (mng_info->ping_exclude_tEXt != MagickFalse)
10650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10651 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010652 if (mng_info->ping_exclude_tRNS != MagickFalse)
10653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010655 if (mng_info->ping_exclude_vpAg != MagickFalse)
10656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10657 " vpAg");
10658 if (mng_info->ping_exclude_zCCP != MagickFalse)
10659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10660 " zCCP");
10661 if (mng_info->ping_exclude_zTXt != MagickFalse)
10662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10663 " zTXt");
10664 }
10665
glennrpb9cfe272010-12-21 15:08:06 +000010666 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010667
glennrpb9cfe272010-12-21 15:08:06 +000010668 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010669
10670 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010671
cristy3ed852e2009-09-05 21:47:34 +000010672 if (logging != MagickFalse)
10673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010674
cristy3ed852e2009-09-05 21:47:34 +000010675 return(status);
10676}
10677
10678#if defined(JNG_SUPPORTED)
10679
10680/* Write one JNG image */
10681static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10682 const ImageInfo *image_info,Image *image)
10683{
10684 Image
10685 *jpeg_image;
10686
10687 ImageInfo
10688 *jpeg_image_info;
10689
10690 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010691 logging,
cristy3ed852e2009-09-05 21:47:34 +000010692 status;
10693
10694 size_t
10695 length;
10696
10697 unsigned char
10698 *blob,
10699 chunk[80],
10700 *p;
10701
10702 unsigned int
10703 jng_alpha_compression_method,
10704 jng_alpha_sample_depth,
10705 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010706 transparent;
10707
cristybb503372010-05-27 20:51:26 +000010708 size_t
cristy3ed852e2009-09-05 21:47:34 +000010709 jng_quality;
10710
10711 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010712 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010713
10714 blob=(unsigned char *) NULL;
10715 jpeg_image=(Image *) NULL;
10716 jpeg_image_info=(ImageInfo *) NULL;
10717
10718 status=MagickTrue;
10719 transparent=image_info->type==GrayscaleMatteType ||
10720 image_info->type==TrueColorMatteType;
10721 jng_color_type=10;
10722 jng_alpha_sample_depth=0;
10723 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10724 jng_alpha_compression_method=0;
10725
10726 if (image->matte != MagickFalse)
10727 {
10728 /* if any pixels are transparent */
10729 transparent=MagickTrue;
10730 if (image_info->compression==JPEGCompression)
10731 jng_alpha_compression_method=8;
10732 }
10733
10734 if (transparent)
10735 {
10736 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010737
cristy3ed852e2009-09-05 21:47:34 +000010738 /* Create JPEG blob, image, and image_info */
10739 if (logging != MagickFalse)
10740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10741 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010742
cristy3ed852e2009-09-05 21:47:34 +000010743 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010744
cristy3ed852e2009-09-05 21:47:34 +000010745 if (jpeg_image_info == (ImageInfo *) NULL)
10746 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010747
cristy3ed852e2009-09-05 21:47:34 +000010748 if (logging != MagickFalse)
10749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10750 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010751
cristy3ed852e2009-09-05 21:47:34 +000010752 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010753
cristy3ed852e2009-09-05 21:47:34 +000010754 if (jpeg_image == (Image *) NULL)
10755 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010756
cristy3ed852e2009-09-05 21:47:34 +000010757 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10758 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10759 status=NegateImage(jpeg_image,MagickFalse);
10760 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010761
cristy3ed852e2009-09-05 21:47:34 +000010762 if (jng_quality >= 1000)
10763 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010764
cristy3ed852e2009-09-05 21:47:34 +000010765 else
10766 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010767
cristy3ed852e2009-09-05 21:47:34 +000010768 jpeg_image_info->type=GrayscaleType;
10769 (void) SetImageType(jpeg_image,GrayscaleType);
10770 (void) AcquireUniqueFilename(jpeg_image->filename);
10771 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10772 "%s",jpeg_image->filename);
10773 }
10774
10775 /* To do: check bit depth of PNG alpha channel */
10776
10777 /* Check if image is grayscale. */
10778 if (image_info->type != TrueColorMatteType && image_info->type !=
10779 TrueColorType && ImageIsGray(image))
10780 jng_color_type-=2;
10781
10782 if (transparent)
10783 {
10784 if (jng_alpha_compression_method==0)
10785 {
10786 const char
10787 *value;
10788
10789 /* Encode opacity as a grayscale PNG blob */
10790 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10791 &image->exception);
10792 if (logging != MagickFalse)
10793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10794 " Creating PNG blob.");
10795 length=0;
10796
10797 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10798 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10799 jpeg_image_info->interlace=NoInterlace;
10800
10801 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10802 &image->exception);
10803
10804 /* Retrieve sample depth used */
10805 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10806 if (value != (char *) NULL)
10807 jng_alpha_sample_depth= (unsigned int) value[0];
10808 }
10809 else
10810 {
10811 /* Encode opacity as a grayscale JPEG blob */
10812
10813 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10814 &image->exception);
10815
10816 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10817 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10818 jpeg_image_info->interlace=NoInterlace;
10819 if (logging != MagickFalse)
10820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10821 " Creating blob.");
10822 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010823 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010824 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010825
cristy3ed852e2009-09-05 21:47:34 +000010826 if (logging != MagickFalse)
10827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010828 " Successfully read jpeg_image into a blob, length=%.20g.",
10829 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010830
10831 }
10832 /* Destroy JPEG image and image_info */
10833 jpeg_image=DestroyImage(jpeg_image);
10834 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10835 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10836 }
10837
10838 /* Write JHDR chunk */
10839 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10840 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010841 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010842 PNGLong(chunk+4,(png_uint_32) image->columns);
10843 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010844 chunk[12]=jng_color_type;
10845 chunk[13]=8; /* sample depth */
10846 chunk[14]=8; /*jng_image_compression_method */
10847 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10848 chunk[16]=jng_alpha_sample_depth;
10849 chunk[17]=jng_alpha_compression_method;
10850 chunk[18]=0; /*jng_alpha_filter_method */
10851 chunk[19]=0; /*jng_alpha_interlace_method */
10852 (void) WriteBlob(image,20,chunk);
10853 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10854 if (logging != MagickFalse)
10855 {
10856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010857 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010858
cristy3ed852e2009-09-05 21:47:34 +000010859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010860 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010861
cristy3ed852e2009-09-05 21:47:34 +000010862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10863 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010864
cristy3ed852e2009-09-05 21:47:34 +000010865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10866 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010867
cristy3ed852e2009-09-05 21:47:34 +000010868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10869 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010870
cristy3ed852e2009-09-05 21:47:34 +000010871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10872 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010873
cristy3ed852e2009-09-05 21:47:34 +000010874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10875 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010876
cristy3ed852e2009-09-05 21:47:34 +000010877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10878 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010879
cristy3ed852e2009-09-05 21:47:34 +000010880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10881 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010882
cristy3ed852e2009-09-05 21:47:34 +000010883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10884 " JNG alpha interlace:%5d",0);
10885 }
10886
glennrp0fe50b42010-11-16 03:52:51 +000010887 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010888 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010889
10890 /*
10891 Write leading ancillary chunks
10892 */
10893
10894 if (transparent)
10895 {
10896 /*
10897 Write JNG bKGD chunk
10898 */
10899
10900 unsigned char
10901 blue,
10902 green,
10903 red;
10904
cristybb503372010-05-27 20:51:26 +000010905 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010906 num_bytes;
10907
10908 if (jng_color_type == 8 || jng_color_type == 12)
10909 num_bytes=6L;
10910 else
10911 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010912 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010913 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010914 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010915 red=ScaleQuantumToChar(image->background_color.red);
10916 green=ScaleQuantumToChar(image->background_color.green);
10917 blue=ScaleQuantumToChar(image->background_color.blue);
10918 *(chunk+4)=0;
10919 *(chunk+5)=red;
10920 *(chunk+6)=0;
10921 *(chunk+7)=green;
10922 *(chunk+8)=0;
10923 *(chunk+9)=blue;
10924 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10925 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10926 }
10927
10928 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10929 {
10930 /*
10931 Write JNG sRGB chunk
10932 */
10933 (void) WriteBlobMSBULong(image,1L);
10934 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010935 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010936
cristy3ed852e2009-09-05 21:47:34 +000010937 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010938 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010939 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010940 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010941
cristy3ed852e2009-09-05 21:47:34 +000010942 else
glennrpe610a072010-08-05 17:08:46 +000010943 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010944 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010945 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010946
cristy3ed852e2009-09-05 21:47:34 +000010947 (void) WriteBlob(image,5,chunk);
10948 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10949 }
10950 else
10951 {
10952 if (image->gamma != 0.0)
10953 {
10954 /*
10955 Write JNG gAMA chunk
10956 */
10957 (void) WriteBlobMSBULong(image,4L);
10958 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010959 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010960 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010961 (void) WriteBlob(image,8,chunk);
10962 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10963 }
glennrp0fe50b42010-11-16 03:52:51 +000010964
cristy3ed852e2009-09-05 21:47:34 +000010965 if ((mng_info->equal_chrms == MagickFalse) &&
10966 (image->chromaticity.red_primary.x != 0.0))
10967 {
10968 PrimaryInfo
10969 primary;
10970
10971 /*
10972 Write JNG cHRM chunk
10973 */
10974 (void) WriteBlobMSBULong(image,32L);
10975 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010976 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010977 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010978 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10979 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010980 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010981 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10982 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010983 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010984 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10985 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010986 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010987 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10988 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010989 (void) WriteBlob(image,36,chunk);
10990 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10991 }
10992 }
glennrp0fe50b42010-11-16 03:52:51 +000010993
cristy3ed852e2009-09-05 21:47:34 +000010994 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10995 {
10996 /*
10997 Write JNG pHYs chunk
10998 */
10999 (void) WriteBlobMSBULong(image,9L);
11000 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011001 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011002 if (image->units == PixelsPerInchResolution)
11003 {
cristy35ef8242010-06-03 16:24:13 +000011004 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011005 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011006
cristy35ef8242010-06-03 16:24:13 +000011007 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011008 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011009
cristy3ed852e2009-09-05 21:47:34 +000011010 chunk[12]=1;
11011 }
glennrp0fe50b42010-11-16 03:52:51 +000011012
cristy3ed852e2009-09-05 21:47:34 +000011013 else
11014 {
11015 if (image->units == PixelsPerCentimeterResolution)
11016 {
cristy35ef8242010-06-03 16:24:13 +000011017 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011018 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011019
cristy35ef8242010-06-03 16:24:13 +000011020 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011021 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011022
cristy3ed852e2009-09-05 21:47:34 +000011023 chunk[12]=1;
11024 }
glennrp0fe50b42010-11-16 03:52:51 +000011025
cristy3ed852e2009-09-05 21:47:34 +000011026 else
11027 {
cristy35ef8242010-06-03 16:24:13 +000011028 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11029 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011030 chunk[12]=0;
11031 }
11032 }
11033 (void) WriteBlob(image,13,chunk);
11034 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11035 }
11036
11037 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11038 {
11039 /*
11040 Write JNG oFFs chunk
11041 */
11042 (void) WriteBlobMSBULong(image,9L);
11043 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011044 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011045 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11046 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011047 chunk[12]=0;
11048 (void) WriteBlob(image,13,chunk);
11049 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11050 }
11051 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11052 {
11053 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11054 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011055 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011056 PNGLong(chunk+4,(png_uint_32) image->page.width);
11057 PNGLong(chunk+8,(png_uint_32) image->page.height);
11058 chunk[12]=0; /* unit = pixels */
11059 (void) WriteBlob(image,13,chunk);
11060 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11061 }
11062
11063
11064 if (transparent)
11065 {
11066 if (jng_alpha_compression_method==0)
11067 {
cristybb503372010-05-27 20:51:26 +000011068 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011069 i;
11070
cristybb503372010-05-27 20:51:26 +000011071 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011072 len;
11073
11074 /* Write IDAT chunk header */
11075 if (logging != MagickFalse)
11076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011077 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011078 length);
cristy3ed852e2009-09-05 21:47:34 +000011079
11080 /* Copy IDAT chunks */
11081 len=0;
11082 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011083 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011084 {
11085 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11086 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011087
cristy3ed852e2009-09-05 21:47:34 +000011088 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11089 {
11090 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011091 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011092 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011093 (void) WriteBlob(image,(size_t) len+4,p);
11094 (void) WriteBlobMSBULong(image,
11095 crc32(0,p,(uInt) len+4));
11096 }
glennrp0fe50b42010-11-16 03:52:51 +000011097
cristy3ed852e2009-09-05 21:47:34 +000011098 else
11099 {
11100 if (logging != MagickFalse)
11101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011102 " Skipping %c%c%c%c chunk, length=%.20g.",
11103 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011104 }
11105 p+=(8+len);
11106 }
11107 }
11108 else
11109 {
11110 /* Write JDAA chunk header */
11111 if (logging != MagickFalse)
11112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011113 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011114 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011115 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011116 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011117 /* Write JDAT chunk(s) data */
11118 (void) WriteBlob(image,4,chunk);
11119 (void) WriteBlob(image,length,blob);
11120 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11121 (uInt) length));
11122 }
11123 blob=(unsigned char *) RelinquishMagickMemory(blob);
11124 }
11125
11126 /* Encode image as a JPEG blob */
11127 if (logging != MagickFalse)
11128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11129 " Creating jpeg_image_info.");
11130 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11131 if (jpeg_image_info == (ImageInfo *) NULL)
11132 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11133
11134 if (logging != MagickFalse)
11135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11136 " Creating jpeg_image.");
11137
11138 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11139 if (jpeg_image == (Image *) NULL)
11140 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11141 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11142
11143 (void) AcquireUniqueFilename(jpeg_image->filename);
11144 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11145 jpeg_image->filename);
11146
11147 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11148 &image->exception);
11149
11150 if (logging != MagickFalse)
11151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011152 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11153 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011154
11155 if (jng_color_type == 8 || jng_color_type == 12)
11156 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011157
cristy3ed852e2009-09-05 21:47:34 +000011158 jpeg_image_info->quality=jng_quality % 1000;
11159 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11160 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011161
cristy3ed852e2009-09-05 21:47:34 +000011162 if (logging != MagickFalse)
11163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11164 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011165
cristy3ed852e2009-09-05 21:47:34 +000011166 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011167
cristy3ed852e2009-09-05 21:47:34 +000011168 if (logging != MagickFalse)
11169 {
11170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011171 " Successfully read jpeg_image into a blob, length=%.20g.",
11172 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011173
11174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011175 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011176 }
glennrp0fe50b42010-11-16 03:52:51 +000011177
cristy3ed852e2009-09-05 21:47:34 +000011178 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011179 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011180 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011181 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011182 (void) WriteBlob(image,4,chunk);
11183 (void) WriteBlob(image,length,blob);
11184 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11185
11186 jpeg_image=DestroyImage(jpeg_image);
11187 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11188 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11189 blob=(unsigned char *) RelinquishMagickMemory(blob);
11190
11191 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011192 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011193
11194 /* Write IEND chunk */
11195 (void) WriteBlobMSBULong(image,0L);
11196 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011197 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011198 (void) WriteBlob(image,4,chunk);
11199 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11200
11201 if (logging != MagickFalse)
11202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11203 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011204
cristy3ed852e2009-09-05 21:47:34 +000011205 return(status);
11206}
11207
11208
11209/*
11210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11211% %
11212% %
11213% %
11214% W r i t e J N G I m a g e %
11215% %
11216% %
11217% %
11218%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11219%
11220% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11221%
11222% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11223%
11224% The format of the WriteJNGImage method is:
11225%
11226% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11227%
11228% A description of each parameter follows:
11229%
11230% o image_info: the image info.
11231%
11232% o image: The image.
11233%
11234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11235*/
11236static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11237{
11238 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011239 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011240 logging,
cristy3ed852e2009-09-05 21:47:34 +000011241 status;
11242
11243 MngInfo
11244 *mng_info;
11245
cristy3ed852e2009-09-05 21:47:34 +000011246 /*
11247 Open image file.
11248 */
11249 assert(image_info != (const ImageInfo *) NULL);
11250 assert(image_info->signature == MagickSignature);
11251 assert(image != (Image *) NULL);
11252 assert(image->signature == MagickSignature);
11253 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011254 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011255 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11256 if (status == MagickFalse)
11257 return(status);
11258
11259 /*
11260 Allocate a MngInfo structure.
11261 */
11262 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011263 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011264 if (mng_info == (MngInfo *) NULL)
11265 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11266 /*
11267 Initialize members of the MngInfo structure.
11268 */
11269 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11270 mng_info->image=image;
11271 have_mng_structure=MagickTrue;
11272
11273 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11274
11275 status=WriteOneJNGImage(mng_info,image_info,image);
11276 (void) CloseBlob(image);
11277
11278 (void) CatchImageException(image);
11279 MngInfoFreeStruct(mng_info,&have_mng_structure);
11280 if (logging != MagickFalse)
11281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11282 return(status);
11283}
11284#endif
11285
11286
11287
11288static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11289{
11290 const char
11291 *option;
11292
11293 Image
11294 *next_image;
11295
11296 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011297 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011298 status;
11299
glennrp03812ae2010-12-24 01:31:34 +000011300 volatile MagickBooleanType
11301 logging;
11302
cristy3ed852e2009-09-05 21:47:34 +000011303 MngInfo
11304 *mng_info;
11305
11306 int
cristy3ed852e2009-09-05 21:47:34 +000011307 image_count,
11308 need_iterations,
11309 need_matte;
11310
11311 volatile int
11312#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11313 defined(PNG_MNG_FEATURES_SUPPORTED)
11314 need_local_plte,
11315#endif
11316 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011317 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011318 use_global_plte;
11319
cristybb503372010-05-27 20:51:26 +000011320 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011321 i;
11322
11323 unsigned char
11324 chunk[800];
11325
11326 volatile unsigned int
11327 write_jng,
11328 write_mng;
11329
cristybb503372010-05-27 20:51:26 +000011330 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011331 scene;
11332
cristybb503372010-05-27 20:51:26 +000011333 size_t
cristy3ed852e2009-09-05 21:47:34 +000011334 final_delay=0,
11335 initial_delay;
11336
glennrpd5045b42010-03-24 12:40:35 +000011337#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011338 if (image_info->verbose)
11339 printf("Your PNG library (libpng-%s) is rather old.\n",
11340 PNG_LIBPNG_VER_STRING);
11341#endif
11342
11343 /*
11344 Open image file.
11345 */
11346 assert(image_info != (const ImageInfo *) NULL);
11347 assert(image_info->signature == MagickSignature);
11348 assert(image != (Image *) NULL);
11349 assert(image->signature == MagickSignature);
11350 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011351 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011352 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11353 if (status == MagickFalse)
11354 return(status);
11355
11356 /*
11357 Allocate a MngInfo structure.
11358 */
11359 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011360 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011361 if (mng_info == (MngInfo *) NULL)
11362 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11363 /*
11364 Initialize members of the MngInfo structure.
11365 */
11366 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11367 mng_info->image=image;
11368 have_mng_structure=MagickTrue;
11369 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11370
11371 /*
11372 * See if user has requested a specific PNG subformat to be used
11373 * for all of the PNGs in the MNG being written, e.g.,
11374 *
11375 * convert *.png png8:animation.mng
11376 *
11377 * To do: check -define png:bit_depth and png:color_type as well,
11378 * or perhaps use mng:bit_depth and mng:color_type instead for
11379 * global settings.
11380 */
11381
11382 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11383 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11384 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11385
11386 write_jng=MagickFalse;
11387 if (image_info->compression == JPEGCompression)
11388 write_jng=MagickTrue;
11389
11390 mng_info->adjoin=image_info->adjoin &&
11391 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11392
cristy3ed852e2009-09-05 21:47:34 +000011393 if (logging != MagickFalse)
11394 {
11395 /* Log some info about the input */
11396 Image
11397 *p;
11398
11399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11400 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011401
cristy3ed852e2009-09-05 21:47:34 +000011402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011403 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011404
cristy3ed852e2009-09-05 21:47:34 +000011405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11406 " Type: %d",image_info->type);
11407
11408 scene=0;
11409 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11410 {
11411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011412 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011413
cristy3ed852e2009-09-05 21:47:34 +000011414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011415 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011416
cristy3ed852e2009-09-05 21:47:34 +000011417 if (p->matte)
11418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11419 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011420
cristy3ed852e2009-09-05 21:47:34 +000011421 else
11422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11423 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011424
cristy3ed852e2009-09-05 21:47:34 +000011425 if (p->storage_class == PseudoClass)
11426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11427 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011428
cristy3ed852e2009-09-05 21:47:34 +000011429 else
11430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11431 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011432
cristy3ed852e2009-09-05 21:47:34 +000011433 if (p->colors)
11434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011435 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011436
cristy3ed852e2009-09-05 21:47:34 +000011437 else
11438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11439 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011440
cristy3ed852e2009-09-05 21:47:34 +000011441 if (mng_info->adjoin == MagickFalse)
11442 break;
11443 }
11444 }
11445
cristy3ed852e2009-09-05 21:47:34 +000011446 use_global_plte=MagickFalse;
11447 all_images_are_gray=MagickFalse;
11448#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11449 need_local_plte=MagickTrue;
11450#endif
11451 need_defi=MagickFalse;
11452 need_matte=MagickFalse;
11453 mng_info->framing_mode=1;
11454 mng_info->old_framing_mode=1;
11455
11456 if (write_mng)
11457 if (image_info->page != (char *) NULL)
11458 {
11459 /*
11460 Determine image bounding box.
11461 */
11462 SetGeometry(image,&mng_info->page);
11463 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11464 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11465 }
11466 if (write_mng)
11467 {
11468 unsigned int
11469 need_geom;
11470
11471 unsigned short
11472 red,
11473 green,
11474 blue;
11475
11476 mng_info->page=image->page;
11477 need_geom=MagickTrue;
11478 if (mng_info->page.width || mng_info->page.height)
11479 need_geom=MagickFalse;
11480 /*
11481 Check all the scenes.
11482 */
11483 initial_delay=image->delay;
11484 need_iterations=MagickFalse;
11485 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11486 mng_info->equal_physs=MagickTrue,
11487 mng_info->equal_gammas=MagickTrue;
11488 mng_info->equal_srgbs=MagickTrue;
11489 mng_info->equal_backgrounds=MagickTrue;
11490 image_count=0;
11491#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11492 defined(PNG_MNG_FEATURES_SUPPORTED)
11493 all_images_are_gray=MagickTrue;
11494 mng_info->equal_palettes=MagickFalse;
11495 need_local_plte=MagickFalse;
11496#endif
11497 for (next_image=image; next_image != (Image *) NULL; )
11498 {
11499 if (need_geom)
11500 {
11501 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11502 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011503
cristy3ed852e2009-09-05 21:47:34 +000011504 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11505 mng_info->page.height=next_image->rows+next_image->page.y;
11506 }
glennrp0fe50b42010-11-16 03:52:51 +000011507
cristy3ed852e2009-09-05 21:47:34 +000011508 if (next_image->page.x || next_image->page.y)
11509 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011510
cristy3ed852e2009-09-05 21:47:34 +000011511 if (next_image->matte)
11512 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011513
cristy3ed852e2009-09-05 21:47:34 +000011514 if ((int) next_image->dispose >= BackgroundDispose)
11515 if (next_image->matte || next_image->page.x || next_image->page.y ||
11516 ((next_image->columns < mng_info->page.width) &&
11517 (next_image->rows < mng_info->page.height)))
11518 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011519
cristy3ed852e2009-09-05 21:47:34 +000011520 if (next_image->iterations)
11521 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011522
cristy3ed852e2009-09-05 21:47:34 +000011523 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011524
cristy3ed852e2009-09-05 21:47:34 +000011525 if (final_delay != initial_delay || final_delay > 1UL*
11526 next_image->ticks_per_second)
11527 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011528
cristy3ed852e2009-09-05 21:47:34 +000011529#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11530 defined(PNG_MNG_FEATURES_SUPPORTED)
11531 /*
11532 check for global palette possibility.
11533 */
11534 if (image->matte != MagickFalse)
11535 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011536
cristy3ed852e2009-09-05 21:47:34 +000011537 if (need_local_plte == 0)
11538 {
11539 if (ImageIsGray(image) == MagickFalse)
11540 all_images_are_gray=MagickFalse;
11541 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11542 if (use_global_plte == 0)
11543 use_global_plte=mng_info->equal_palettes;
11544 need_local_plte=!mng_info->equal_palettes;
11545 }
11546#endif
11547 if (GetNextImageInList(next_image) != (Image *) NULL)
11548 {
11549 if (next_image->background_color.red !=
11550 next_image->next->background_color.red ||
11551 next_image->background_color.green !=
11552 next_image->next->background_color.green ||
11553 next_image->background_color.blue !=
11554 next_image->next->background_color.blue)
11555 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011556
cristy3ed852e2009-09-05 21:47:34 +000011557 if (next_image->gamma != next_image->next->gamma)
11558 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011559
cristy3ed852e2009-09-05 21:47:34 +000011560 if (next_image->rendering_intent !=
11561 next_image->next->rendering_intent)
11562 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011563
cristy3ed852e2009-09-05 21:47:34 +000011564 if ((next_image->units != next_image->next->units) ||
11565 (next_image->x_resolution != next_image->next->x_resolution) ||
11566 (next_image->y_resolution != next_image->next->y_resolution))
11567 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011568
cristy3ed852e2009-09-05 21:47:34 +000011569 if (mng_info->equal_chrms)
11570 {
11571 if (next_image->chromaticity.red_primary.x !=
11572 next_image->next->chromaticity.red_primary.x ||
11573 next_image->chromaticity.red_primary.y !=
11574 next_image->next->chromaticity.red_primary.y ||
11575 next_image->chromaticity.green_primary.x !=
11576 next_image->next->chromaticity.green_primary.x ||
11577 next_image->chromaticity.green_primary.y !=
11578 next_image->next->chromaticity.green_primary.y ||
11579 next_image->chromaticity.blue_primary.x !=
11580 next_image->next->chromaticity.blue_primary.x ||
11581 next_image->chromaticity.blue_primary.y !=
11582 next_image->next->chromaticity.blue_primary.y ||
11583 next_image->chromaticity.white_point.x !=
11584 next_image->next->chromaticity.white_point.x ||
11585 next_image->chromaticity.white_point.y !=
11586 next_image->next->chromaticity.white_point.y)
11587 mng_info->equal_chrms=MagickFalse;
11588 }
11589 }
11590 image_count++;
11591 next_image=GetNextImageInList(next_image);
11592 }
11593 if (image_count < 2)
11594 {
11595 mng_info->equal_backgrounds=MagickFalse;
11596 mng_info->equal_chrms=MagickFalse;
11597 mng_info->equal_gammas=MagickFalse;
11598 mng_info->equal_srgbs=MagickFalse;
11599 mng_info->equal_physs=MagickFalse;
11600 use_global_plte=MagickFalse;
11601#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11602 need_local_plte=MagickTrue;
11603#endif
11604 need_iterations=MagickFalse;
11605 }
glennrp0fe50b42010-11-16 03:52:51 +000011606
cristy3ed852e2009-09-05 21:47:34 +000011607 if (mng_info->need_fram == MagickFalse)
11608 {
11609 /*
11610 Only certain framing rates 100/n are exactly representable without
11611 the FRAM chunk but we'll allow some slop in VLC files
11612 */
11613 if (final_delay == 0)
11614 {
11615 if (need_iterations != MagickFalse)
11616 {
11617 /*
11618 It's probably a GIF with loop; don't run it *too* fast.
11619 */
glennrp02617122010-07-28 13:07:35 +000011620 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011621 {
11622 final_delay=10;
11623 (void) ThrowMagickException(&image->exception,
11624 GetMagickModule(),CoderWarning,
11625 "input has zero delay between all frames; assuming",
11626 " 10 cs `%s'","");
11627 }
cristy3ed852e2009-09-05 21:47:34 +000011628 }
11629 else
11630 mng_info->ticks_per_second=0;
11631 }
11632 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011633 mng_info->ticks_per_second=(png_uint_32)
11634 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011635 if (final_delay > 50)
11636 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011637
cristy3ed852e2009-09-05 21:47:34 +000011638 if (final_delay > 75)
11639 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011640
cristy3ed852e2009-09-05 21:47:34 +000011641 if (final_delay > 125)
11642 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011643
cristy3ed852e2009-09-05 21:47:34 +000011644 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11645 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11646 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11647 1UL*image->ticks_per_second))
11648 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11649 }
glennrp0fe50b42010-11-16 03:52:51 +000011650
cristy3ed852e2009-09-05 21:47:34 +000011651 if (mng_info->need_fram != MagickFalse)
11652 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11653 /*
11654 If pseudocolor, we should also check to see if all the
11655 palettes are identical and write a global PLTE if they are.
11656 ../glennrp Feb 99.
11657 */
11658 /*
11659 Write the MNG version 1.0 signature and MHDR chunk.
11660 */
11661 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11662 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11663 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011664 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011665 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11666 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011667 PNGLong(chunk+12,mng_info->ticks_per_second);
11668 PNGLong(chunk+16,0L); /* layer count=unknown */
11669 PNGLong(chunk+20,0L); /* frame count=unknown */
11670 PNGLong(chunk+24,0L); /* play time=unknown */
11671 if (write_jng)
11672 {
11673 if (need_matte)
11674 {
11675 if (need_defi || mng_info->need_fram || use_global_plte)
11676 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011677
cristy3ed852e2009-09-05 21:47:34 +000011678 else
11679 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11680 }
glennrp0fe50b42010-11-16 03:52:51 +000011681
cristy3ed852e2009-09-05 21:47:34 +000011682 else
11683 {
11684 if (need_defi || mng_info->need_fram || use_global_plte)
11685 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011686
cristy3ed852e2009-09-05 21:47:34 +000011687 else
11688 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11689 }
11690 }
glennrp0fe50b42010-11-16 03:52:51 +000011691
cristy3ed852e2009-09-05 21:47:34 +000011692 else
11693 {
11694 if (need_matte)
11695 {
11696 if (need_defi || mng_info->need_fram || use_global_plte)
11697 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011698
cristy3ed852e2009-09-05 21:47:34 +000011699 else
11700 PNGLong(chunk+28,9L); /* simplicity=VLC */
11701 }
glennrp0fe50b42010-11-16 03:52:51 +000011702
cristy3ed852e2009-09-05 21:47:34 +000011703 else
11704 {
11705 if (need_defi || mng_info->need_fram || use_global_plte)
11706 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011707
cristy3ed852e2009-09-05 21:47:34 +000011708 else
11709 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11710 }
11711 }
11712 (void) WriteBlob(image,32,chunk);
11713 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11714 option=GetImageOption(image_info,"mng:need-cacheoff");
11715 if (option != (const char *) NULL)
11716 {
11717 size_t
11718 length;
11719
11720 /*
11721 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11722 */
11723 PNGType(chunk,mng_nEED);
11724 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011725 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011726 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011727 length+=4;
11728 (void) WriteBlob(image,length,chunk);
11729 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11730 }
11731 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11732 (GetNextImageInList(image) != (Image *) NULL) &&
11733 (image->iterations != 1))
11734 {
11735 /*
11736 Write MNG TERM chunk
11737 */
11738 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11739 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011740 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011741 chunk[4]=3; /* repeat animation */
11742 chunk[5]=0; /* show last frame when done */
11743 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11744 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011745
cristy3ed852e2009-09-05 21:47:34 +000011746 if (image->iterations == 0)
11747 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011748
cristy3ed852e2009-09-05 21:47:34 +000011749 else
11750 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011751
cristy3ed852e2009-09-05 21:47:34 +000011752 if (logging != MagickFalse)
11753 {
11754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011755 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11756 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011757
cristy3ed852e2009-09-05 21:47:34 +000011758 if (image->iterations == 0)
11759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011760 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011761
cristy3ed852e2009-09-05 21:47:34 +000011762 else
11763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011764 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011765 }
11766 (void) WriteBlob(image,14,chunk);
11767 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11768 }
11769 /*
11770 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11771 */
11772 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11773 mng_info->equal_srgbs)
11774 {
11775 /*
11776 Write MNG sRGB chunk
11777 */
11778 (void) WriteBlobMSBULong(image,1L);
11779 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011780 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011781
cristy3ed852e2009-09-05 21:47:34 +000011782 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011783 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011784 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011785 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011786
cristy3ed852e2009-09-05 21:47:34 +000011787 else
glennrpe610a072010-08-05 17:08:46 +000011788 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011789 Magick_RenderingIntent_to_PNG_RenderingIntent(
11790 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011791
cristy3ed852e2009-09-05 21:47:34 +000011792 (void) WriteBlob(image,5,chunk);
11793 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11794 mng_info->have_write_global_srgb=MagickTrue;
11795 }
glennrp0fe50b42010-11-16 03:52:51 +000011796
cristy3ed852e2009-09-05 21:47:34 +000011797 else
11798 {
11799 if (image->gamma && mng_info->equal_gammas)
11800 {
11801 /*
11802 Write MNG gAMA chunk
11803 */
11804 (void) WriteBlobMSBULong(image,4L);
11805 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011806 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011807 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011808 (void) WriteBlob(image,8,chunk);
11809 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11810 mng_info->have_write_global_gama=MagickTrue;
11811 }
11812 if (mng_info->equal_chrms)
11813 {
11814 PrimaryInfo
11815 primary;
11816
11817 /*
11818 Write MNG cHRM chunk
11819 */
11820 (void) WriteBlobMSBULong(image,32L);
11821 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011822 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011823 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011824 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11825 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011826 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011827 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11828 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011829 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011830 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11831 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011832 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011833 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11834 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011835 (void) WriteBlob(image,36,chunk);
11836 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11837 mng_info->have_write_global_chrm=MagickTrue;
11838 }
11839 }
11840 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11841 {
11842 /*
11843 Write MNG pHYs chunk
11844 */
11845 (void) WriteBlobMSBULong(image,9L);
11846 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011847 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011848
cristy3ed852e2009-09-05 21:47:34 +000011849 if (image->units == PixelsPerInchResolution)
11850 {
cristy35ef8242010-06-03 16:24:13 +000011851 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011852 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011853
cristy35ef8242010-06-03 16:24:13 +000011854 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011855 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011856
cristy3ed852e2009-09-05 21:47:34 +000011857 chunk[12]=1;
11858 }
glennrp0fe50b42010-11-16 03:52:51 +000011859
cristy3ed852e2009-09-05 21:47:34 +000011860 else
11861 {
11862 if (image->units == PixelsPerCentimeterResolution)
11863 {
cristy35ef8242010-06-03 16:24:13 +000011864 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011865 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011866
cristy35ef8242010-06-03 16:24:13 +000011867 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011868 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011869
cristy3ed852e2009-09-05 21:47:34 +000011870 chunk[12]=1;
11871 }
glennrp0fe50b42010-11-16 03:52:51 +000011872
cristy3ed852e2009-09-05 21:47:34 +000011873 else
11874 {
cristy35ef8242010-06-03 16:24:13 +000011875 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11876 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011877 chunk[12]=0;
11878 }
11879 }
11880 (void) WriteBlob(image,13,chunk);
11881 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11882 }
11883 /*
11884 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11885 or does not cover the entire frame.
11886 */
11887 if (write_mng && (image->matte || image->page.x > 0 ||
11888 image->page.y > 0 || (image->page.width &&
11889 (image->page.width+image->page.x < mng_info->page.width))
11890 || (image->page.height && (image->page.height+image->page.y
11891 < mng_info->page.height))))
11892 {
11893 (void) WriteBlobMSBULong(image,6L);
11894 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011895 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011896 red=ScaleQuantumToShort(image->background_color.red);
11897 green=ScaleQuantumToShort(image->background_color.green);
11898 blue=ScaleQuantumToShort(image->background_color.blue);
11899 PNGShort(chunk+4,red);
11900 PNGShort(chunk+6,green);
11901 PNGShort(chunk+8,blue);
11902 (void) WriteBlob(image,10,chunk);
11903 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11904 if (mng_info->equal_backgrounds)
11905 {
11906 (void) WriteBlobMSBULong(image,6L);
11907 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011908 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011909 (void) WriteBlob(image,10,chunk);
11910 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11911 }
11912 }
11913
11914#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11915 if ((need_local_plte == MagickFalse) &&
11916 (image->storage_class == PseudoClass) &&
11917 (all_images_are_gray == MagickFalse))
11918 {
cristybb503372010-05-27 20:51:26 +000011919 size_t
cristy3ed852e2009-09-05 21:47:34 +000011920 data_length;
11921
11922 /*
11923 Write MNG PLTE chunk
11924 */
11925 data_length=3*image->colors;
11926 (void) WriteBlobMSBULong(image,data_length);
11927 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011928 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011929
cristybb503372010-05-27 20:51:26 +000011930 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011931 {
11932 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11933 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11934 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11935 }
glennrp0fe50b42010-11-16 03:52:51 +000011936
cristy3ed852e2009-09-05 21:47:34 +000011937 (void) WriteBlob(image,data_length+4,chunk);
11938 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11939 mng_info->have_write_global_plte=MagickTrue;
11940 }
11941#endif
11942 }
11943 scene=0;
11944 mng_info->delay=0;
11945#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11946 defined(PNG_MNG_FEATURES_SUPPORTED)
11947 mng_info->equal_palettes=MagickFalse;
11948#endif
11949 do
11950 {
11951 if (mng_info->adjoin)
11952 {
11953#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11954 defined(PNG_MNG_FEATURES_SUPPORTED)
11955 /*
11956 If we aren't using a global palette for the entire MNG, check to
11957 see if we can use one for two or more consecutive images.
11958 */
11959 if (need_local_plte && use_global_plte && !all_images_are_gray)
11960 {
11961 if (mng_info->IsPalette)
11962 {
11963 /*
11964 When equal_palettes is true, this image has the same palette
11965 as the previous PseudoClass image
11966 */
11967 mng_info->have_write_global_plte=mng_info->equal_palettes;
11968 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11969 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11970 {
11971 /*
11972 Write MNG PLTE chunk
11973 */
cristybb503372010-05-27 20:51:26 +000011974 size_t
cristy3ed852e2009-09-05 21:47:34 +000011975 data_length;
11976
11977 data_length=3*image->colors;
11978 (void) WriteBlobMSBULong(image,data_length);
11979 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011980 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011981
cristybb503372010-05-27 20:51:26 +000011982 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011983 {
11984 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11985 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11986 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11987 }
glennrp0fe50b42010-11-16 03:52:51 +000011988
cristy3ed852e2009-09-05 21:47:34 +000011989 (void) WriteBlob(image,data_length+4,chunk);
11990 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11991 (uInt) (data_length+4)));
11992 mng_info->have_write_global_plte=MagickTrue;
11993 }
11994 }
11995 else
11996 mng_info->have_write_global_plte=MagickFalse;
11997 }
11998#endif
11999 if (need_defi)
12000 {
cristybb503372010-05-27 20:51:26 +000012001 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012002 previous_x,
12003 previous_y;
12004
12005 if (scene)
12006 {
12007 previous_x=mng_info->page.x;
12008 previous_y=mng_info->page.y;
12009 }
12010 else
12011 {
12012 previous_x=0;
12013 previous_y=0;
12014 }
12015 mng_info->page=image->page;
12016 if ((mng_info->page.x != previous_x) ||
12017 (mng_info->page.y != previous_y))
12018 {
12019 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12020 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012021 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012022 chunk[4]=0; /* object 0 MSB */
12023 chunk[5]=0; /* object 0 LSB */
12024 chunk[6]=0; /* visible */
12025 chunk[7]=0; /* abstract */
12026 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12027 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12028 (void) WriteBlob(image,16,chunk);
12029 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12030 }
12031 }
12032 }
12033
12034 mng_info->write_mng=write_mng;
12035
12036 if ((int) image->dispose >= 3)
12037 mng_info->framing_mode=3;
12038
12039 if (mng_info->need_fram && mng_info->adjoin &&
12040 ((image->delay != mng_info->delay) ||
12041 (mng_info->framing_mode != mng_info->old_framing_mode)))
12042 {
12043 if (image->delay == mng_info->delay)
12044 {
12045 /*
12046 Write a MNG FRAM chunk with the new framing mode.
12047 */
12048 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12049 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012050 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012051 chunk[4]=(unsigned char) mng_info->framing_mode;
12052 (void) WriteBlob(image,5,chunk);
12053 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12054 }
12055 else
12056 {
12057 /*
12058 Write a MNG FRAM chunk with the delay.
12059 */
12060 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12061 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012062 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012063 chunk[4]=(unsigned char) mng_info->framing_mode;
12064 chunk[5]=0; /* frame name separator (no name) */
12065 chunk[6]=2; /* flag for changing default delay */
12066 chunk[7]=0; /* flag for changing frame timeout */
12067 chunk[8]=0; /* flag for changing frame clipping */
12068 chunk[9]=0; /* flag for changing frame sync_id */
12069 PNGLong(chunk+10,(png_uint_32)
12070 ((mng_info->ticks_per_second*
12071 image->delay)/MagickMax(image->ticks_per_second,1)));
12072 (void) WriteBlob(image,14,chunk);
12073 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012074 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012075 }
12076 mng_info->old_framing_mode=mng_info->framing_mode;
12077 }
12078
12079#if defined(JNG_SUPPORTED)
12080 if (image_info->compression == JPEGCompression)
12081 {
12082 ImageInfo
12083 *write_info;
12084
12085 if (logging != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12087 " Writing JNG object.");
12088 /* To do: specify the desired alpha compression method. */
12089 write_info=CloneImageInfo(image_info);
12090 write_info->compression=UndefinedCompression;
12091 status=WriteOneJNGImage(mng_info,write_info,image);
12092 write_info=DestroyImageInfo(write_info);
12093 }
12094 else
12095#endif
12096 {
12097 if (logging != MagickFalse)
12098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12099 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012100
glennrpb9cfe272010-12-21 15:08:06 +000012101 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012102 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012103
12104 /* We don't want any ancillary chunks written */
12105 mng_info->ping_exclude_bKGD=MagickTrue;
12106 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012107 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012108 mng_info->ping_exclude_EXIF=MagickTrue;
12109 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012110 mng_info->ping_exclude_iCCP=MagickTrue;
12111 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12112 mng_info->ping_exclude_oFFs=MagickTrue;
12113 mng_info->ping_exclude_pHYs=MagickTrue;
12114 mng_info->ping_exclude_sRGB=MagickTrue;
12115 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012116 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012117 mng_info->ping_exclude_vpAg=MagickTrue;
12118 mng_info->ping_exclude_zCCP=MagickTrue;
12119 mng_info->ping_exclude_zTXt=MagickTrue;
12120
cristy3ed852e2009-09-05 21:47:34 +000012121 status=WriteOnePNGImage(mng_info,image_info,image);
12122 }
12123
12124 if (status == MagickFalse)
12125 {
12126 MngInfoFreeStruct(mng_info,&have_mng_structure);
12127 (void) CloseBlob(image);
12128 return(MagickFalse);
12129 }
12130 (void) CatchImageException(image);
12131 if (GetNextImageInList(image) == (Image *) NULL)
12132 break;
12133 image=SyncNextImageInList(image);
12134 status=SetImageProgress(image,SaveImagesTag,scene++,
12135 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012136
cristy3ed852e2009-09-05 21:47:34 +000012137 if (status == MagickFalse)
12138 break;
glennrp0fe50b42010-11-16 03:52:51 +000012139
cristy3ed852e2009-09-05 21:47:34 +000012140 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012141
cristy3ed852e2009-09-05 21:47:34 +000012142 if (write_mng)
12143 {
12144 while (GetPreviousImageInList(image) != (Image *) NULL)
12145 image=GetPreviousImageInList(image);
12146 /*
12147 Write the MEND chunk.
12148 */
12149 (void) WriteBlobMSBULong(image,0x00000000L);
12150 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012151 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012152 (void) WriteBlob(image,4,chunk);
12153 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12154 }
12155 /*
12156 Relinquish resources.
12157 */
12158 (void) CloseBlob(image);
12159 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012160
cristy3ed852e2009-09-05 21:47:34 +000012161 if (logging != MagickFalse)
12162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012163
cristy3ed852e2009-09-05 21:47:34 +000012164 return(MagickTrue);
12165}
glennrpd5045b42010-03-24 12:40:35 +000012166#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012167
cristy3ed852e2009-09-05 21:47:34 +000012168static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12169{
12170 image=image;
12171 printf("Your PNG library is too old: You have libpng-%s\n",
12172 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012173
cristy3ed852e2009-09-05 21:47:34 +000012174 ThrowBinaryException(CoderError,"PNG library is too old",
12175 image_info->filename);
12176}
glennrp39992b42010-11-14 00:03:43 +000012177
cristy3ed852e2009-09-05 21:47:34 +000012178static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12179{
12180 return(WritePNGImage(image_info,image));
12181}
glennrpd5045b42010-03-24 12:40:35 +000012182#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012183#endif