blob: 504bbbbd3c5ee9c7acf6f12f998170301588183e [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
glennrp136ee3a2011-04-27 15:47:45 +00008729 if ((image_colors == 0) ||
8730 ((ssize_t) (image_colors-1) > MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008731 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008732
cristy3ed852e2009-09-05 21:47:34 +00008733 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008734 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008735
cristy3ed852e2009-09-05 21:47:34 +00008736 else
8737 {
glennrp5af765f2010-03-30 11:12:18 +00008738 ping_bit_depth=8;
8739 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008740 {
8741 if(!mng_info->write_png_depth)
8742 {
glennrp5af765f2010-03-30 11:12:18 +00008743 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008744
cristy35ef8242010-06-03 16:24:13 +00008745 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008746 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008747 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008748 }
8749 }
glennrp2b013e42010-11-24 16:55:50 +00008750
glennrp0fe50b42010-11-16 03:52:51 +00008751 else if (ping_color_type ==
8752 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008753 mng_info->IsPalette)
8754 {
cristy3ed852e2009-09-05 21:47:34 +00008755 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008756
cristy3ed852e2009-09-05 21:47:34 +00008757 int
8758 depth_4_ok=MagickTrue,
8759 depth_2_ok=MagickTrue,
8760 depth_1_ok=MagickTrue;
8761
cristybb503372010-05-27 20:51:26 +00008762 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008763 {
8764 unsigned char
8765 intensity;
8766
8767 intensity=ScaleQuantumToChar(image->colormap[i].red);
8768
8769 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8770 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8771 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8772 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008773 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008774 depth_1_ok=MagickFalse;
8775 }
glennrp2b013e42010-11-24 16:55:50 +00008776
cristy3ed852e2009-09-05 21:47:34 +00008777 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008778 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008779
cristy3ed852e2009-09-05 21:47:34 +00008780 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008781 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008782
cristy3ed852e2009-09-05 21:47:34 +00008783 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008784 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008785 }
8786 }
glennrp2b013e42010-11-24 16:55:50 +00008787
glennrp5af765f2010-03-30 11:12:18 +00008788 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008789 }
glennrp0fe50b42010-11-16 03:52:51 +00008790
cristy3ed852e2009-09-05 21:47:34 +00008791 else
glennrp0fe50b42010-11-16 03:52:51 +00008792
cristy3ed852e2009-09-05 21:47:34 +00008793 if (mng_info->IsPalette)
8794 {
glennrp17a14852010-05-10 03:01:59 +00008795 number_colors=image_colors;
8796
cristy3ed852e2009-09-05 21:47:34 +00008797 if (image_depth <= 8)
8798 {
cristy3ed852e2009-09-05 21:47:34 +00008799 /*
8800 Set image palette.
8801 */
glennrp5af765f2010-03-30 11:12:18 +00008802 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008803
glennrp58e01762011-01-07 15:28:54 +00008804 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008805 {
glennrp9c1eb072010-06-06 22:19:15 +00008806 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008807
glennrp3b51f0e2010-11-27 18:14:08 +00008808 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8810 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008811 }
glennrp0fe50b42010-11-16 03:52:51 +00008812
cristy3ed852e2009-09-05 21:47:34 +00008813 else
8814 {
cristybb503372010-05-27 20:51:26 +00008815 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008816 {
8817 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8818 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8819 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8820 }
glennrp0fe50b42010-11-16 03:52:51 +00008821
glennrp3b51f0e2010-11-27 18:14:08 +00008822 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008824 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008825 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008826
glennrp39992b42010-11-14 00:03:43 +00008827 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008828 }
glennrp0fe50b42010-11-16 03:52:51 +00008829
cristy3ed852e2009-09-05 21:47:34 +00008830 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008831 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008832 {
cristybefe4d22010-06-07 01:18:58 +00008833 size_t
8834 one;
8835
glennrp5af765f2010-03-30 11:12:18 +00008836 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008837 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008838
cristybefe4d22010-06-07 01:18:58 +00008839 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008840 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008841 }
glennrp0fe50b42010-11-16 03:52:51 +00008842
glennrp5af765f2010-03-30 11:12:18 +00008843 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008844
glennrp58e01762011-01-07 15:28:54 +00008845 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008846 {
glennrp0fe50b42010-11-16 03:52:51 +00008847 /*
glennrpd6bf1612010-12-17 17:28:54 +00008848 * Set up trans_colors array.
8849 */
glennrp0fe50b42010-11-16 03:52:51 +00008850 assert(number_colors <= 256);
8851
glennrpd6bf1612010-12-17 17:28:54 +00008852 ping_num_trans=(unsigned short) (number_transparent +
8853 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008854
8855 if (ping_num_trans == 0)
8856 ping_have_tRNS=MagickFalse;
8857
glennrpd6bf1612010-12-17 17:28:54 +00008858 else
glennrp0fe50b42010-11-16 03:52:51 +00008859 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008860 if (logging != MagickFalse)
8861 {
8862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8863 " Scaling ping_trans_color (1)");
8864 }
glennrpd6bf1612010-12-17 17:28:54 +00008865 ping_have_tRNS=MagickTrue;
8866
8867 for (i=0; i < ping_num_trans; i++)
8868 {
8869 ping_trans_alpha[i]= (png_byte) (255-
8870 ScaleQuantumToChar(image->colormap[i].opacity));
8871 }
glennrp0fe50b42010-11-16 03:52:51 +00008872 }
8873 }
cristy3ed852e2009-09-05 21:47:34 +00008874 }
8875 }
glennrp0fe50b42010-11-16 03:52:51 +00008876
cristy3ed852e2009-09-05 21:47:34 +00008877 else
8878 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008879
cristy3ed852e2009-09-05 21:47:34 +00008880 if (image_depth < 8)
8881 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008882
cristy3ed852e2009-09-05 21:47:34 +00008883 if ((save_image_depth == 16) && (image_depth == 8))
8884 {
glennrp4f25bd02011-01-01 18:51:28 +00008885 if (logging != MagickFalse)
8886 {
8887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8888 " Scaling ping_trans_color from (%d,%d,%d)",
8889 (int) ping_trans_color.red,
8890 (int) ping_trans_color.green,
8891 (int) ping_trans_color.blue);
8892 }
8893
glennrp5af765f2010-03-30 11:12:18 +00008894 ping_trans_color.red*=0x0101;
8895 ping_trans_color.green*=0x0101;
8896 ping_trans_color.blue*=0x0101;
8897 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008898
8899 if (logging != MagickFalse)
8900 {
8901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902 " to (%d,%d,%d)",
8903 (int) ping_trans_color.red,
8904 (int) ping_trans_color.green,
8905 (int) ping_trans_color.blue);
8906 }
cristy3ed852e2009-09-05 21:47:34 +00008907 }
8908 }
8909
cristy4383ec82011-01-05 15:42:32 +00008910 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8911 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008912
cristy3ed852e2009-09-05 21:47:34 +00008913 /*
8914 Adjust background and transparency samples in sub-8-bit grayscale files.
8915 */
glennrp5af765f2010-03-30 11:12:18 +00008916 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008917 PNG_COLOR_TYPE_GRAY)
8918 {
8919 png_uint_16
8920 maxval;
8921
cristy35ef8242010-06-03 16:24:13 +00008922 size_t
8923 one=1;
8924
cristy22ffd972010-06-03 16:51:47 +00008925 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008926
glennrp4f25bd02011-01-01 18:51:28 +00008927 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008928 {
cristy3ed852e2009-09-05 21:47:34 +00008929
glennrpa521b2f2010-10-29 04:11:03 +00008930 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008931 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8932
8933 if (logging != MagickFalse)
8934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008935 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00008936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8937 " background_color index is %d",
8938 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008939
glennrp991d11d2010-11-12 21:55:28 +00008940 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008941 }
cristy3ed852e2009-09-05 21:47:34 +00008942
glennrp5af765f2010-03-30 11:12:18 +00008943 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8944 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008945 }
glennrp17a14852010-05-10 03:01:59 +00008946
glennrp26f37912010-12-23 16:22:42 +00008947 if (ping_exclude_bKGD == MagickFalse)
8948 {
glennrp1273f7b2011-02-24 03:20:30 +00008949 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008950 {
8951 /*
8952 Identify which colormap entry is the background color.
8953 */
8954
glennrp17a14852010-05-10 03:01:59 +00008955 number_colors=image_colors;
8956
glennrpa521b2f2010-10-29 04:11:03 +00008957 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8958 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008959 break;
8960
8961 ping_background.index=(png_byte) i;
8962
glennrp3b51f0e2010-11-27 18:14:08 +00008963 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008964 {
8965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008966 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008967 }
glennrp0fe50b42010-11-16 03:52:51 +00008968
cristy13d07042010-11-21 20:56:18 +00008969 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008970 {
8971 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008972
8973 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008974 {
8975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8976 " background =(%d,%d,%d)",
8977 (int) ping_background.red,
8978 (int) ping_background.green,
8979 (int) ping_background.blue);
8980 }
8981 }
glennrpa521b2f2010-10-29 04:11:03 +00008982
glennrpd6bf1612010-12-17 17:28:54 +00008983 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008984 {
glennrp3b51f0e2010-11-27 18:14:08 +00008985 if (logging != MagickFalse)
8986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8987 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008988 ping_have_bKGD = MagickFalse;
8989 }
glennrp17a14852010-05-10 03:01:59 +00008990 }
glennrp26f37912010-12-23 16:22:42 +00008991 }
glennrp17a14852010-05-10 03:01:59 +00008992
cristy3ed852e2009-09-05 21:47:34 +00008993 if (logging != MagickFalse)
8994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00008995 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008996 /*
8997 Initialize compression level and filtering.
8998 */
8999 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009000 {
9001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002 " Setting up deflate compression");
9003
9004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9005 " Compression buffer size: 32768");
9006 }
9007
cristy3ed852e2009-09-05 21:47:34 +00009008 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009009
cristy3ed852e2009-09-05 21:47:34 +00009010 if (logging != MagickFalse)
9011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9012 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009013
cristy3ed852e2009-09-05 21:47:34 +00009014 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009015
cristy3ed852e2009-09-05 21:47:34 +00009016 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9017 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009018
cristy3ed852e2009-09-05 21:47:34 +00009019 if (quality > 9)
9020 {
9021 int
9022 level;
9023
cristybb503372010-05-27 20:51:26 +00009024 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009025
cristy3ed852e2009-09-05 21:47:34 +00009026 if (logging != MagickFalse)
9027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9028 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00009029
cristy3ed852e2009-09-05 21:47:34 +00009030 png_set_compression_level(ping,level);
9031 }
glennrp0fe50b42010-11-16 03:52:51 +00009032
cristy3ed852e2009-09-05 21:47:34 +00009033 else
9034 {
9035 if (logging != MagickFalse)
9036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9037 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009038
cristy3ed852e2009-09-05 21:47:34 +00009039 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9040 }
glennrp0fe50b42010-11-16 03:52:51 +00009041
cristy3ed852e2009-09-05 21:47:34 +00009042 if (logging != MagickFalse)
9043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009045
glennrp2b013e42010-11-24 16:55:50 +00009046#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009047 /* This became available in libpng-1.0.9. Output must be a MNG. */
9048 if (mng_info->write_mng && ((quality % 10) == 7))
9049 {
9050 if (logging != MagickFalse)
9051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009053
glennrp5af765f2010-03-30 11:12:18 +00009054 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009055 }
glennrp0fe50b42010-11-16 03:52:51 +00009056
cristy3ed852e2009-09-05 21:47:34 +00009057 else
9058 if (logging != MagickFalse)
9059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060 " Filter_type: 0");
9061#endif
glennrp2b013e42010-11-24 16:55:50 +00009062
cristy3ed852e2009-09-05 21:47:34 +00009063 {
9064 int
9065 base_filter;
9066
9067 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009068 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009069
glennrp26c990a2010-11-23 02:23:20 +00009070 else
glennrp8640fb52010-11-23 15:48:26 +00009071 if ((quality % 10) != 5)
9072 base_filter=(int) quality % 10;
9073
9074 else
9075 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9076 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9077 (quality < 50))
9078 base_filter=PNG_NO_FILTERS;
9079
9080 else
9081 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009082
cristy3ed852e2009-09-05 21:47:34 +00009083 if (logging != MagickFalse)
9084 {
9085 if (base_filter == PNG_ALL_FILTERS)
9086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9087 " Base filter method: ADAPTIVE");
9088 else
9089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9090 " Base filter method: NONE");
9091 }
glennrp2b013e42010-11-24 16:55:50 +00009092
cristy3ed852e2009-09-05 21:47:34 +00009093 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9094 }
9095
glennrp823b55c2011-03-14 18:46:46 +00009096 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9097 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009098 {
9099 ResetImageProfileIterator(image);
9100 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009101 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009102 profile=GetImageProfile(image,name);
9103
9104 if (profile != (StringInfo *) NULL)
9105 {
glennrp5af765f2010-03-30 11:12:18 +00009106#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009107 if ((LocaleCompare(name,"ICC") == 0) ||
9108 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009109 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009110
9111 if (ping_exclude_iCCP == MagickFalse)
9112 {
9113 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009114#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009115 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009116#else
9117 (png_const_bytep) GetStringInfoDatum(profile),
9118#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009119 (png_uint_32) GetStringInfoLength(profile));
9120 }
glennrp26f37912010-12-23 16:22:42 +00009121 }
glennrp0fe50b42010-11-16 03:52:51 +00009122
glennrpc8cbc5d2011-01-01 00:12:34 +00009123 else
cristy3ed852e2009-09-05 21:47:34 +00009124#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009125 if (ping_exclude_zCCP == MagickFalse)
9126 {
glennrpcf002022011-01-30 02:38:15 +00009127 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009128 (unsigned char *) name,(unsigned char *) name,
9129 GetStringInfoDatum(profile),
9130 (png_uint_32) GetStringInfoLength(profile));
9131 }
9132 }
glennrp0b206f52011-01-07 04:55:32 +00009133
glennrpc8cbc5d2011-01-01 00:12:34 +00009134 if (logging != MagickFalse)
9135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9136 " Setting up text chunk with %s profile",name);
9137
9138 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009139 }
cristy3ed852e2009-09-05 21:47:34 +00009140 }
9141
9142#if defined(PNG_WRITE_sRGB_SUPPORTED)
9143 if ((mng_info->have_write_global_srgb == 0) &&
9144 ((image->rendering_intent != UndefinedIntent) ||
9145 (image->colorspace == sRGBColorspace)))
9146 {
glennrp26f37912010-12-23 16:22:42 +00009147 if (ping_exclude_sRGB == MagickFalse)
9148 {
9149 /*
9150 Note image rendering intent.
9151 */
9152 if (logging != MagickFalse)
9153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9154 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009155
glennrp26f37912010-12-23 16:22:42 +00009156 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009157 Magick_RenderingIntent_to_PNG_RenderingIntent(
9158 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009159
glennrp26f37912010-12-23 16:22:42 +00009160 if (ping_exclude_gAMA == MagickFalse)
9161 png_set_gAMA(ping,ping_info,0.45455);
9162 }
cristy3ed852e2009-09-05 21:47:34 +00009163 }
glennrp26f37912010-12-23 16:22:42 +00009164
glennrp5af765f2010-03-30 11:12:18 +00009165 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009166#endif
9167 {
glennrp2cc891a2010-12-24 13:44:32 +00009168 if (ping_exclude_gAMA == MagickFalse &&
9169 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009170 (image->gamma < .45 || image->gamma > .46)))
9171 {
cristy3ed852e2009-09-05 21:47:34 +00009172 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9173 {
9174 /*
9175 Note image gamma.
9176 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9177 */
9178 if (logging != MagickFalse)
9179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9180 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009181
cristy3ed852e2009-09-05 21:47:34 +00009182 png_set_gAMA(ping,ping_info,image->gamma);
9183 }
glennrp26f37912010-12-23 16:22:42 +00009184 }
glennrp2b013e42010-11-24 16:55:50 +00009185
glennrp26f37912010-12-23 16:22:42 +00009186 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009187 {
glennrp26f37912010-12-23 16:22:42 +00009188 if ((mng_info->have_write_global_chrm == 0) &&
9189 (image->chromaticity.red_primary.x != 0.0))
9190 {
9191 /*
9192 Note image chromaticity.
9193 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9194 */
9195 PrimaryInfo
9196 bp,
9197 gp,
9198 rp,
9199 wp;
cristy3ed852e2009-09-05 21:47:34 +00009200
glennrp26f37912010-12-23 16:22:42 +00009201 wp=image->chromaticity.white_point;
9202 rp=image->chromaticity.red_primary;
9203 gp=image->chromaticity.green_primary;
9204 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009205
glennrp26f37912010-12-23 16:22:42 +00009206 if (logging != MagickFalse)
9207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9208 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009209
glennrp26f37912010-12-23 16:22:42 +00009210 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9211 bp.x,bp.y);
9212 }
9213 }
cristy3ed852e2009-09-05 21:47:34 +00009214 }
glennrpdfd70802010-11-14 01:23:35 +00009215
glennrp5af765f2010-03-30 11:12:18 +00009216 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009217
9218 if (mng_info->write_mng)
9219 png_set_sig_bytes(ping,8);
9220
9221 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9222
glennrpd6bf1612010-12-17 17:28:54 +00009223 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009224 {
9225 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009226 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009227 {
glennrp5af765f2010-03-30 11:12:18 +00009228 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009229
glennrp5af765f2010-03-30 11:12:18 +00009230 if (ping_bit_depth < 8)
9231 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009232 }
glennrp0fe50b42010-11-16 03:52:51 +00009233
cristy3ed852e2009-09-05 21:47:34 +00009234 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009235 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009236 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009237 }
9238
glennrp0e8ea192010-12-24 18:00:33 +00009239 if (ping_need_colortype_warning != MagickFalse ||
9240 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009241 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009242 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009243 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009244 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009245 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009246 {
9247 if (logging != MagickFalse)
9248 {
glennrp0e8ea192010-12-24 18:00:33 +00009249 if (ping_need_colortype_warning != MagickFalse)
9250 {
9251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9252 " Image has transparency but tRNS chunk was excluded");
9253 }
9254
cristy3ed852e2009-09-05 21:47:34 +00009255 if (mng_info->write_png_depth)
9256 {
9257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9258 " Defined PNG:bit-depth=%u, Computed depth=%u",
9259 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009260 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009261 }
glennrp0e8ea192010-12-24 18:00:33 +00009262
cristy3ed852e2009-09-05 21:47:34 +00009263 if (mng_info->write_png_colortype)
9264 {
9265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9266 " Defined PNG:color-type=%u, Computed color type=%u",
9267 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009268 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009269 }
9270 }
glennrp0e8ea192010-12-24 18:00:33 +00009271
glennrp3bd2e412010-08-10 13:34:52 +00009272 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009273 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9274 }
9275
glennrp58e01762011-01-07 15:28:54 +00009276 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009277 {
9278 /* Add an opaque matte channel */
9279 image->matte = MagickTrue;
9280 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009281
glennrpb4a13412010-05-05 12:47:19 +00009282 if (logging != MagickFalse)
9283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9284 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009285 }
9286
glennrp0e319732011-01-25 21:53:13 +00009287 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009288 {
glennrp991d11d2010-11-12 21:55:28 +00009289 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009290 {
glennrp991d11d2010-11-12 21:55:28 +00009291 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009292 if (logging != MagickFalse)
9293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9294 " Setting ping_have_tRNS=MagickTrue.");
9295 }
glennrpe9c26dc2010-05-30 01:56:35 +00009296 }
9297
cristy3ed852e2009-09-05 21:47:34 +00009298 if (logging != MagickFalse)
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300 " Writing PNG header chunks");
9301
glennrp5af765f2010-03-30 11:12:18 +00009302 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9303 ping_bit_depth,ping_color_type,
9304 ping_interlace_method,ping_compression_method,
9305 ping_filter_method);
9306
glennrp39992b42010-11-14 00:03:43 +00009307 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9308 {
glennrpf09bded2011-01-08 01:15:59 +00009309 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009310
glennrp3b51f0e2010-11-27 18:14:08 +00009311 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009312 {
glennrp8640fb52010-11-23 15:48:26 +00009313 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009314 {
glennrpd6bf1612010-12-17 17:28:54 +00009315 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009317 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9318 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009319 (int) palette[i].red,
9320 (int) palette[i].green,
9321 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009322 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009323 (int) ping_trans_alpha[i]);
9324 else
9325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009326 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009327 (int) i,
9328 (int) palette[i].red,
9329 (int) palette[i].green,
9330 (int) palette[i].blue);
9331 }
glennrp39992b42010-11-14 00:03:43 +00009332 }
glennrp39992b42010-11-14 00:03:43 +00009333 }
9334
glennrp26f37912010-12-23 16:22:42 +00009335 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009336 {
glennrp26f37912010-12-23 16:22:42 +00009337 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009338 {
glennrp26f37912010-12-23 16:22:42 +00009339 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009340 if (logging)
9341 {
9342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9343 " Setting up bKGD chunk");
9344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9345 " background color = (%d,%d,%d)",
9346 (int) ping_background.red,
9347 (int) ping_background.green,
9348 (int) ping_background.blue);
9349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9350 " index = %d, gray=%d",
9351 (int) ping_background.index,
9352 (int) ping_background.gray);
9353 }
9354 }
glennrp26f37912010-12-23 16:22:42 +00009355 }
9356
9357 if (ping_exclude_pHYs == MagickFalse)
9358 {
9359 if (ping_have_pHYs != MagickFalse)
9360 {
9361 png_set_pHYs(ping,ping_info,
9362 ping_pHYs_x_resolution,
9363 ping_pHYs_y_resolution,
9364 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009365
9366 if (logging)
9367 {
9368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9369 " Setting up pHYs chunk");
9370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9371 " x_resolution=%lu",
9372 (unsigned long) ping_pHYs_x_resolution);
9373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9374 " y_resolution=%lu",
9375 (unsigned long) ping_pHYs_y_resolution);
9376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9377 " unit_type=%lu",
9378 (unsigned long) ping_pHYs_unit_type);
9379 }
glennrp26f37912010-12-23 16:22:42 +00009380 }
glennrpdfd70802010-11-14 01:23:35 +00009381 }
9382
9383#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009384 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009385 {
glennrp26f37912010-12-23 16:22:42 +00009386 if (image->page.x || image->page.y)
9387 {
9388 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9389 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009390
glennrp26f37912010-12-23 16:22:42 +00009391 if (logging != MagickFalse)
9392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9393 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9394 (int) image->page.x, (int) image->page.y);
9395 }
glennrpdfd70802010-11-14 01:23:35 +00009396 }
9397#endif
9398
glennrpda8f3a72011-02-27 23:54:12 +00009399 if (mng_info->need_blob != MagickFalse)
9400 {
9401 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9402 MagickFalse)
9403 png_error(ping,"WriteBlob Failed");
9404
9405 ping_have_blob=MagickTrue;
9406 }
9407
cristy3ed852e2009-09-05 21:47:34 +00009408 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009409
glennrp39992b42010-11-14 00:03:43 +00009410 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009411 {
glennrp3b51f0e2010-11-27 18:14:08 +00009412 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009413 {
9414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9415 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9416 }
9417
9418 if (ping_color_type == 3)
9419 (void) png_set_tRNS(ping, ping_info,
9420 ping_trans_alpha,
9421 ping_num_trans,
9422 NULL);
9423
9424 else
9425 {
9426 (void) png_set_tRNS(ping, ping_info,
9427 NULL,
9428 0,
9429 &ping_trans_color);
9430
glennrp3b51f0e2010-11-27 18:14:08 +00009431 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009432 {
9433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009434 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009435 (int) ping_trans_color.red,
9436 (int) ping_trans_color.green,
9437 (int) ping_trans_color.blue);
9438 }
9439 }
glennrp991d11d2010-11-12 21:55:28 +00009440 }
9441
cristy3ed852e2009-09-05 21:47:34 +00009442 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009443 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009444
cristy3ed852e2009-09-05 21:47:34 +00009445 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009446
cristy3ed852e2009-09-05 21:47:34 +00009447 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009448 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009449
glennrp26f37912010-12-23 16:22:42 +00009450 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009451 {
glennrp4f25bd02011-01-01 18:51:28 +00009452 if ((image->page.width != 0 && image->page.width != image->columns) ||
9453 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009454 {
9455 unsigned char
9456 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009457
glennrp26f37912010-12-23 16:22:42 +00009458 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9459 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009460 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009461 PNGLong(chunk+4,(png_uint_32) image->page.width);
9462 PNGLong(chunk+8,(png_uint_32) image->page.height);
9463 chunk[12]=0; /* unit = pixels */
9464 (void) WriteBlob(image,13,chunk);
9465 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9466 }
cristy3ed852e2009-09-05 21:47:34 +00009467 }
9468
9469#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009470 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009471#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009472 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009473#undef PNG_HAVE_IDAT
9474#endif
9475
9476 png_set_packing(ping);
9477 /*
9478 Allocate memory.
9479 */
9480 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009481 if (image_depth > 8)
9482 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009483 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009484 {
glennrpb4a13412010-05-05 12:47:19 +00009485 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009486 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009487 break;
glennrp0fe50b42010-11-16 03:52:51 +00009488
glennrpb4a13412010-05-05 12:47:19 +00009489 case PNG_COLOR_TYPE_GRAY_ALPHA:
9490 rowbytes*=2;
9491 break;
glennrp0fe50b42010-11-16 03:52:51 +00009492
glennrpb4a13412010-05-05 12:47:19 +00009493 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009494 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009495 break;
glennrp0fe50b42010-11-16 03:52:51 +00009496
glennrpb4a13412010-05-05 12:47:19 +00009497 default:
9498 break;
cristy3ed852e2009-09-05 21:47:34 +00009499 }
glennrp3b51f0e2010-11-27 18:14:08 +00009500
9501 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009502 {
9503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9504 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009505
glennrpb4a13412010-05-05 12:47:19 +00009506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009507 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009508 }
glennrpcf002022011-01-30 02:38:15 +00009509 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9510 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009511
glennrpcf002022011-01-30 02:38:15 +00009512 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009513 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009514
cristy3ed852e2009-09-05 21:47:34 +00009515 /*
9516 Initialize image scanlines.
9517 */
glennrp5af765f2010-03-30 11:12:18 +00009518 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009519 {
9520 /*
9521 PNG write failed.
9522 */
9523#ifdef PNG_DEBUG
9524 if (image_info->verbose)
9525 (void) printf("PNG write has failed.\n");
9526#endif
9527 png_destroy_write_struct(&ping,&ping_info);
9528 if (quantum_info != (QuantumInfo *) NULL)
9529 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009530 if (ping_pixels != (unsigned char *) NULL)
9531 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009532#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009533 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009534#endif
glennrpda8f3a72011-02-27 23:54:12 +00009535 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009536 (void) CloseBlob(image);
9537 image_info=DestroyImageInfo(image_info);
9538 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009539 return(MagickFalse);
9540 }
cristyed552522009-10-16 14:04:35 +00009541 quantum_info=AcquireQuantumInfo(image_info,image);
9542 if (quantum_info == (QuantumInfo *) NULL)
9543 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009544 quantum_info->format=UndefinedQuantumFormat;
9545 quantum_info->depth=image_depth;
9546 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009547
cristy3ed852e2009-09-05 21:47:34 +00009548 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009549 !mng_info->write_png32) &&
9550 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009551 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009552 image_matte == MagickFalse &&
9553 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009554 {
glennrp8bb3a022010-12-13 20:40:04 +00009555 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009556 register const PixelPacket
9557 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009558
cristy3ed852e2009-09-05 21:47:34 +00009559 quantum_info->depth=8;
9560 for (pass=0; pass < num_passes; pass++)
9561 {
9562 /*
9563 Convert PseudoClass image to a PNG monochrome image.
9564 */
cristybb503372010-05-27 20:51:26 +00009565 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009566 {
glennrpd71e86a2011-02-24 01:28:37 +00009567 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9569 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009570
cristy3ed852e2009-09-05 21:47:34 +00009571 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009572
cristy3ed852e2009-09-05 21:47:34 +00009573 if (p == (const PixelPacket *) NULL)
9574 break;
glennrp0fe50b42010-11-16 03:52:51 +00009575
cristy3ed852e2009-09-05 21:47:34 +00009576 if (mng_info->IsPalette)
9577 {
9578 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009579 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009580 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9581 mng_info->write_png_depth &&
9582 mng_info->write_png_depth != old_bit_depth)
9583 {
9584 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009585 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009586 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009587 >> (8-old_bit_depth));
9588 }
9589 }
glennrp0fe50b42010-11-16 03:52:51 +00009590
cristy3ed852e2009-09-05 21:47:34 +00009591 else
9592 {
9593 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009594 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009595 }
glennrp0fe50b42010-11-16 03:52:51 +00009596
cristy3ed852e2009-09-05 21:47:34 +00009597 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009598 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009599 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009600 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009601
glennrp3b51f0e2010-11-27 18:14:08 +00009602 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9604 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009605
glennrpcf002022011-01-30 02:38:15 +00009606 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009607 }
9608 if (image->previous == (Image *) NULL)
9609 {
9610 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9611 if (status == MagickFalse)
9612 break;
9613 }
9614 }
9615 }
glennrp0fe50b42010-11-16 03:52:51 +00009616
glennrp8bb3a022010-12-13 20:40:04 +00009617 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009618 {
glennrp0fe50b42010-11-16 03:52:51 +00009619 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009620 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009621 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009622 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009623 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009624 {
glennrp8bb3a022010-12-13 20:40:04 +00009625 register const PixelPacket
9626 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009627
glennrp8bb3a022010-12-13 20:40:04 +00009628 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009629 {
glennrp8bb3a022010-12-13 20:40:04 +00009630
cristybb503372010-05-27 20:51:26 +00009631 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009632 {
9633 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009634
cristy3ed852e2009-09-05 21:47:34 +00009635 if (p == (const PixelPacket *) NULL)
9636 break;
glennrp2cc891a2010-12-24 13:44:32 +00009637
glennrp5af765f2010-03-30 11:12:18 +00009638 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009639 {
glennrp8bb3a022010-12-13 20:40:04 +00009640 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009641 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009642 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009643
glennrp8bb3a022010-12-13 20:40:04 +00009644 else
9645 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009646 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009647
glennrp3b51f0e2010-11-27 18:14:08 +00009648 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009650 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009651 }
glennrp2cc891a2010-12-24 13:44:32 +00009652
glennrp8bb3a022010-12-13 20:40:04 +00009653 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9654 {
9655 if (logging != MagickFalse && y == 0)
9656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9657 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009658
glennrp8bb3a022010-12-13 20:40:04 +00009659 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009660 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009661 }
glennrp2cc891a2010-12-24 13:44:32 +00009662
glennrp3b51f0e2010-11-27 18:14:08 +00009663 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009665 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009666
glennrpcf002022011-01-30 02:38:15 +00009667 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009668 }
glennrp2cc891a2010-12-24 13:44:32 +00009669
glennrp8bb3a022010-12-13 20:40:04 +00009670 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009671 {
glennrp8bb3a022010-12-13 20:40:04 +00009672 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9673 if (status == MagickFalse)
9674 break;
cristy3ed852e2009-09-05 21:47:34 +00009675 }
cristy3ed852e2009-09-05 21:47:34 +00009676 }
9677 }
glennrp8bb3a022010-12-13 20:40:04 +00009678
9679 else
9680 {
9681 register const PixelPacket
9682 *p;
9683
9684 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009685 {
glennrp8bb3a022010-12-13 20:40:04 +00009686 if ((image_depth > 8) || (mng_info->write_png24 ||
9687 mng_info->write_png32 ||
9688 (!mng_info->write_png8 && !mng_info->IsPalette)))
9689 {
9690 for (y=0; y < (ssize_t) image->rows; y++)
9691 {
9692 p=GetVirtualPixels(image,0,y,image->columns,1,
9693 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009694
glennrp8bb3a022010-12-13 20:40:04 +00009695 if (p == (const PixelPacket *) NULL)
9696 break;
glennrp2cc891a2010-12-24 13:44:32 +00009697
glennrp8bb3a022010-12-13 20:40:04 +00009698 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9699 {
9700 if (image->storage_class == DirectClass)
9701 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009702 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009703
glennrp8bb3a022010-12-13 20:40:04 +00009704 else
9705 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009706 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009707 }
glennrp2cc891a2010-12-24 13:44:32 +00009708
glennrp8bb3a022010-12-13 20:40:04 +00009709 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9710 {
9711 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009712 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009713 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009714
glennrp8bb3a022010-12-13 20:40:04 +00009715 if (logging != MagickFalse && y == 0)
9716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9717 " Writing GRAY_ALPHA PNG pixels (3)");
9718 }
glennrp2cc891a2010-12-24 13:44:32 +00009719
glennrp8bb3a022010-12-13 20:40:04 +00009720 else if (image_matte != MagickFalse)
9721 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009722 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009723
glennrp8bb3a022010-12-13 20:40:04 +00009724 else
9725 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009726 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009727
glennrp8bb3a022010-12-13 20:40:04 +00009728 if (logging != MagickFalse && y == 0)
9729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9730 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009731
glennrpcf002022011-01-30 02:38:15 +00009732 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009733 }
9734 }
glennrp2cc891a2010-12-24 13:44:32 +00009735
glennrp8bb3a022010-12-13 20:40:04 +00009736 else
9737 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9738 mng_info->write_png32 ||
9739 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9740 {
9741 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9742 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9743 {
9744 if (logging != MagickFalse)
9745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9746 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009747
glennrp8bb3a022010-12-13 20:40:04 +00009748 quantum_info->depth=8;
9749 image_depth=8;
9750 }
glennrp2cc891a2010-12-24 13:44:32 +00009751
glennrp8bb3a022010-12-13 20:40:04 +00009752 for (y=0; y < (ssize_t) image->rows; y++)
9753 {
9754 if (logging != MagickFalse && y == 0)
9755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9756 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009757
glennrp770d1932011-03-06 22:11:17 +00009758 p=GetVirtualPixels(image,0,y,image->columns,1,
9759 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009760
glennrp8bb3a022010-12-13 20:40:04 +00009761 if (p == (const PixelPacket *) NULL)
9762 break;
glennrp2cc891a2010-12-24 13:44:32 +00009763
glennrp8bb3a022010-12-13 20:40:04 +00009764 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009765 {
glennrp4bf89732011-03-21 13:48:28 +00009766 quantum_info->depth=image->depth;
9767
glennrp44757ab2011-03-17 12:57:03 +00009768 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009769 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009770 }
glennrp2cc891a2010-12-24 13:44:32 +00009771
glennrp8bb3a022010-12-13 20:40:04 +00009772 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9773 {
9774 if (logging != MagickFalse && y == 0)
9775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9776 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009777
glennrp8bb3a022010-12-13 20:40:04 +00009778 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009779 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009780 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009781 }
glennrp2cc891a2010-12-24 13:44:32 +00009782
glennrp8bb3a022010-12-13 20:40:04 +00009783 else
glennrp8bb3a022010-12-13 20:40:04 +00009784 {
glennrp179d0752011-03-17 13:02:10 +00009785 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009786 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9787
9788 if (logging != MagickFalse && y <= 2)
9789 {
9790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009791 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009792
9793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9794 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9795 (int)ping_pixels[0],(int)ping_pixels[1]);
9796 }
glennrp8bb3a022010-12-13 20:40:04 +00009797 }
glennrpcf002022011-01-30 02:38:15 +00009798 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009799 }
9800 }
glennrp2cc891a2010-12-24 13:44:32 +00009801
glennrp8bb3a022010-12-13 20:40:04 +00009802 if (image->previous == (Image *) NULL)
9803 {
9804 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9805 if (status == MagickFalse)
9806 break;
9807 }
cristy3ed852e2009-09-05 21:47:34 +00009808 }
glennrp8bb3a022010-12-13 20:40:04 +00009809 }
9810 }
9811
cristyb32b90a2009-09-07 21:45:48 +00009812 if (quantum_info != (QuantumInfo *) NULL)
9813 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009814
9815 if (logging != MagickFalse)
9816 {
9817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009818 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009819
cristy3ed852e2009-09-05 21:47:34 +00009820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009821 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009822
cristy3ed852e2009-09-05 21:47:34 +00009823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009824 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009825
cristy3ed852e2009-09-05 21:47:34 +00009826 if (mng_info->write_png_depth)
9827 {
9828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9829 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9830 }
glennrp0fe50b42010-11-16 03:52:51 +00009831
cristy3ed852e2009-09-05 21:47:34 +00009832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009833 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009834
cristy3ed852e2009-09-05 21:47:34 +00009835 if (mng_info->write_png_colortype)
9836 {
9837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9838 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9839 }
glennrp0fe50b42010-11-16 03:52:51 +00009840
cristy3ed852e2009-09-05 21:47:34 +00009841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009842 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009843
cristy3ed852e2009-09-05 21:47:34 +00009844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009845 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009846 }
9847 /*
glennrpa0ed0092011-04-18 16:36:29 +00009848 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009849 */
glennrp823b55c2011-03-14 18:46:46 +00009850 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009851 {
glennrp26f37912010-12-23 16:22:42 +00009852 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009853 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009854 while (property != (const char *) NULL)
9855 {
9856 png_textp
9857 text;
glennrp2cc891a2010-12-24 13:44:32 +00009858
glennrp26f37912010-12-23 16:22:42 +00009859 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009860
9861 /* Don't write any "png:" properties; those are just for "identify" */
9862 if (LocaleNCompare(property,"png:",4) != 0 &&
9863
9864 /* Suppress density and units if we wrote a pHYs chunk */
9865 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009866 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009867 LocaleCompare(property,"units") != 0) &&
9868
9869 /* Suppress the IM-generated Date:create and Date:modify */
9870 (ping_exclude_date == MagickFalse ||
9871 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009872 {
glennrpc70af4a2011-03-07 00:08:23 +00009873 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009874 {
glennrpc70af4a2011-03-07 00:08:23 +00009875 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9876 text[0].key=(char *) property;
9877 text[0].text=(char *) value;
9878 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009879
glennrpc70af4a2011-03-07 00:08:23 +00009880 if (ping_exclude_tEXt != MagickFalse)
9881 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9882
9883 else if (ping_exclude_zTXt != MagickFalse)
9884 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9885
9886 else
glennrp26f37912010-12-23 16:22:42 +00009887 {
glennrpc70af4a2011-03-07 00:08:23 +00009888 text[0].compression=image_info->compression == NoCompression ||
9889 (image_info->compression == UndefinedCompression &&
9890 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9891 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009892 }
glennrp2cc891a2010-12-24 13:44:32 +00009893
glennrpc70af4a2011-03-07 00:08:23 +00009894 if (logging != MagickFalse)
9895 {
9896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9897 " Setting up text chunk");
9898
9899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9900 " keyword: %s",text[0].key);
9901 }
9902
9903 png_set_text(ping,ping_info,text,1);
9904 png_free(ping,text);
9905 }
glennrp26f37912010-12-23 16:22:42 +00009906 }
9907 property=GetNextImageProperty(image);
9908 }
cristy3ed852e2009-09-05 21:47:34 +00009909 }
9910
9911 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009912 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009913
9914 if (logging != MagickFalse)
9915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9916 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009917
cristy3ed852e2009-09-05 21:47:34 +00009918 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009919
cristy3ed852e2009-09-05 21:47:34 +00009920 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9921 {
9922 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009923 (ping_width != mng_info->page.width) ||
9924 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009925 {
9926 unsigned char
9927 chunk[32];
9928
9929 /*
9930 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9931 */
9932 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9933 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009934 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009935 chunk[4]=4;
9936 chunk[5]=0; /* frame name separator (no name) */
9937 chunk[6]=1; /* flag for changing delay, for next frame only */
9938 chunk[7]=0; /* flag for changing frame timeout */
9939 chunk[8]=1; /* flag for changing frame clipping for next frame */
9940 chunk[9]=0; /* flag for changing frame sync_id */
9941 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9942 chunk[14]=0; /* clipping boundaries delta type */
9943 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9944 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009945 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009946 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9947 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009948 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009949 (void) WriteBlob(image,31,chunk);
9950 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9951 mng_info->old_framing_mode=4;
9952 mng_info->framing_mode=1;
9953 }
glennrp0fe50b42010-11-16 03:52:51 +00009954
cristy3ed852e2009-09-05 21:47:34 +00009955 else
9956 mng_info->framing_mode=3;
9957 }
9958 if (mng_info->write_mng && !mng_info->need_fram &&
9959 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009960 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009961 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009962 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009963
cristy3ed852e2009-09-05 21:47:34 +00009964 /*
9965 Free PNG resources.
9966 */
glennrp5af765f2010-03-30 11:12:18 +00009967
cristy3ed852e2009-09-05 21:47:34 +00009968 png_destroy_write_struct(&ping,&ping_info);
9969
glennrpcf002022011-01-30 02:38:15 +00009970 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009971
9972#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009973 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009974#endif
9975
glennrpda8f3a72011-02-27 23:54:12 +00009976 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009977 (void) CloseBlob(image);
9978
9979 image_info=DestroyImageInfo(image_info);
9980 image=DestroyImage(image);
9981
9982 /* Store bit depth actually written */
9983 s[0]=(char) ping_bit_depth;
9984 s[1]='\0';
9985
9986 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9987
cristy3ed852e2009-09-05 21:47:34 +00009988 if (logging != MagickFalse)
9989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9990 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009991
cristy3ed852e2009-09-05 21:47:34 +00009992 return(MagickTrue);
9993/* End write one PNG image */
9994}
9995
9996/*
9997%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9998% %
9999% %
10000% %
10001% W r i t e P N G I m a g e %
10002% %
10003% %
10004% %
10005%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10006%
10007% WritePNGImage() writes a Portable Network Graphics (PNG) or
10008% Multiple-image Network Graphics (MNG) image file.
10009%
10010% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10011%
10012% The format of the WritePNGImage method is:
10013%
10014% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10015%
10016% A description of each parameter follows:
10017%
10018% o image_info: the image info.
10019%
10020% o image: The image.
10021%
10022% Returns MagickTrue on success, MagickFalse on failure.
10023%
10024% Communicating with the PNG encoder:
10025%
10026% While the datastream written is always in PNG format and normally would
10027% be given the "png" file extension, this method also writes the following
10028% pseudo-formats which are subsets of PNG:
10029%
glennrp5a39f372011-02-25 04:52:16 +000010030% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10031% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010032% is present, the tRNS chunk must only have values 0 and 255
10033% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010034% transparent). If other values are present they will be
10035% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010036% colors are present, they will be quantized to the 4-4-4-1,
10037% 3-3-3-1, or 3-3-2-1 palette.
10038%
10039% If you want better quantization or dithering of the colors
10040% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010041% PNG encoder. The pixels contain 8-bit indices even if
10042% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010043% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010044% PNG grayscale type might be slightly more efficient. Please
10045% note that writing to the PNG8 format may result in loss
10046% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010047%
10048% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10049% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010050% one of the colors as transparent. The only loss incurred
10051% is reduction of sample depth to 8. If the image has more
10052% than one transparent color, has semitransparent pixels, or
10053% has an opaque pixel with the same RGB components as the
10054% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010055%
10056% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10057% transparency is permitted, i.e., the alpha sample for
10058% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010059% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010060% The only loss in data is the reduction of the sample depth
10061% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010062%
10063% o -define: For more precise control of the PNG output, you can use the
10064% Image options "png:bit-depth" and "png:color-type". These
10065% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010066% from the application programming interfaces. The options
10067% are case-independent and are converted to lowercase before
10068% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010069%
10070% png:color-type can be 0, 2, 3, 4, or 6.
10071%
10072% When png:color-type is 0 (Grayscale), png:bit-depth can
10073% be 1, 2, 4, 8, or 16.
10074%
10075% When png:color-type is 2 (RGB), png:bit-depth can
10076% be 8 or 16.
10077%
10078% When png:color-type is 3 (Indexed), png:bit-depth can
10079% be 1, 2, 4, or 8. This refers to the number of bits
10080% used to store the index. The color samples always have
10081% bit-depth 8 in indexed PNG files.
10082%
10083% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10084% png:bit-depth can be 8 or 16.
10085%
glennrp5a39f372011-02-25 04:52:16 +000010086% If the image cannot be written without loss with the requested bit-depth
10087% and color-type, a PNG file will not be written, and the encoder will
10088% return MagickFalse.
10089%
cristy3ed852e2009-09-05 21:47:34 +000010090% Since image encoders should not be responsible for the "heavy lifting",
10091% the user should make sure that ImageMagick has already reduced the
10092% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010093% transparency prior to attempting to write the image with depth, color,
10094% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010095%
glennrp97cefe22011-04-22 16:17:00 +000010096% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010097%
cristy3ed852e2009-09-05 21:47:34 +000010098% Note that another definition, "png:bit-depth-written" exists, but it
10099% is not intended for external use. It is only used internally by the
10100% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10101%
10102% It is possible to request that the PNG encoder write previously-formatted
10103% ancillary chunks in the output PNG file, using the "-profile" commandline
10104% option as shown below or by setting the profile via a programming
10105% interface:
10106%
10107% -profile PNG-chunk-x:<file>
10108%
10109% where x is a location flag and <file> is a file containing the chunk
10110% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010111% This encoder will compute the chunk length and CRC, so those must not
10112% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010113%
10114% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10115% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10116% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010117% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010118%
glennrpbb8a7332010-11-13 15:17:35 +000010119% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010120%
glennrp3241bd02010-12-12 04:36:28 +000010121% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010122%
glennrpd6afd542010-11-19 01:53:05 +000010123% o 32-bit depth is reduced to 16.
10124% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10125% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010126% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010127% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010128% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010129% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10130% this can be done without loss and a larger bit depth N was not
10131% requested via the "-define PNG:bit-depth=N" option.
10132% o If matte channel is present but only one transparent color is
10133% present, RGB+tRNS is written instead of RGBA
10134% o Opaque matte channel is removed (or added, if color-type 4 or 6
10135% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010136%
cristy3ed852e2009-09-05 21:47:34 +000010137%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10138*/
10139static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10140 Image *image)
10141{
10142 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010143 excluding,
10144 logging,
10145 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010146 status;
10147
10148 MngInfo
10149 *mng_info;
10150
10151 const char
10152 *value;
10153
10154 int
glennrp21f0e622011-01-07 16:20:57 +000010155 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010156 source;
10157
cristy3ed852e2009-09-05 21:47:34 +000010158 /*
10159 Open image file.
10160 */
10161 assert(image_info != (const ImageInfo *) NULL);
10162 assert(image_info->signature == MagickSignature);
10163 assert(image != (Image *) NULL);
10164 assert(image->signature == MagickSignature);
10165 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010166 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010167 /*
10168 Allocate a MngInfo structure.
10169 */
10170 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010171 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010172
cristy3ed852e2009-09-05 21:47:34 +000010173 if (mng_info == (MngInfo *) NULL)
10174 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010175
cristy3ed852e2009-09-05 21:47:34 +000010176 /*
10177 Initialize members of the MngInfo structure.
10178 */
10179 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10180 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010181 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010182 have_mng_structure=MagickTrue;
10183
10184 /* See if user has requested a specific PNG subformat */
10185
10186 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10187 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10188 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10189
10190 if (mng_info->write_png8)
10191 {
glennrp9c1eb072010-06-06 22:19:15 +000010192 mng_info->write_png_colortype = /* 3 */ 4;
10193 mng_info->write_png_depth = 8;
10194 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010195 }
10196
10197 if (mng_info->write_png24)
10198 {
glennrp9c1eb072010-06-06 22:19:15 +000010199 mng_info->write_png_colortype = /* 2 */ 3;
10200 mng_info->write_png_depth = 8;
10201 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010202
glennrp9c1eb072010-06-06 22:19:15 +000010203 if (image->matte == MagickTrue)
10204 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010205
glennrp9c1eb072010-06-06 22:19:15 +000010206 else
10207 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010208
glennrp9c1eb072010-06-06 22:19:15 +000010209 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010210 }
10211
10212 if (mng_info->write_png32)
10213 {
glennrp9c1eb072010-06-06 22:19:15 +000010214 mng_info->write_png_colortype = /* 6 */ 7;
10215 mng_info->write_png_depth = 8;
10216 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010217
glennrp9c1eb072010-06-06 22:19:15 +000010218 if (image->matte == MagickTrue)
10219 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010220
glennrp9c1eb072010-06-06 22:19:15 +000010221 else
10222 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010223
glennrp9c1eb072010-06-06 22:19:15 +000010224 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010225 }
10226
10227 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010228
cristy3ed852e2009-09-05 21:47:34 +000010229 if (value != (char *) NULL)
10230 {
10231 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010232 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010233
cristy3ed852e2009-09-05 21:47:34 +000010234 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010235 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010236
cristy3ed852e2009-09-05 21:47:34 +000010237 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010238 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010239
cristy3ed852e2009-09-05 21:47:34 +000010240 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010241 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010242
cristy3ed852e2009-09-05 21:47:34 +000010243 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010244 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010245
glennrpbb8a7332010-11-13 15:17:35 +000010246 else
10247 (void) ThrowMagickException(&image->exception,
10248 GetMagickModule(),CoderWarning,
10249 "ignoring invalid defined png:bit-depth",
10250 "=%s",value);
10251
cristy3ed852e2009-09-05 21:47:34 +000010252 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010254 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010255 }
glennrp0fe50b42010-11-16 03:52:51 +000010256
cristy3ed852e2009-09-05 21:47:34 +000010257 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010258
cristy3ed852e2009-09-05 21:47:34 +000010259 if (value != (char *) NULL)
10260 {
10261 /* We must store colortype+1 because 0 is a valid colortype */
10262 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010263 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010264
cristy3ed852e2009-09-05 21:47:34 +000010265 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010266 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010267
cristy3ed852e2009-09-05 21:47:34 +000010268 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010269 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010270
cristy3ed852e2009-09-05 21:47:34 +000010271 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010272 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010273
cristy3ed852e2009-09-05 21:47:34 +000010274 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010275 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010276
glennrpbb8a7332010-11-13 15:17:35 +000010277 else
10278 (void) ThrowMagickException(&image->exception,
10279 GetMagickModule(),CoderWarning,
10280 "ignoring invalid defined png:color-type",
10281 "=%s",value);
10282
cristy3ed852e2009-09-05 21:47:34 +000010283 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010285 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010286 }
10287
glennrp0e8ea192010-12-24 18:00:33 +000010288 /* Check for chunks to be excluded:
10289 *
glennrp0dff56c2011-01-29 19:10:02 +000010290 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010291 * listed in the "unused_chunks" array, above.
10292 *
10293 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10294 * define (in the image properties or in the image artifacts)
10295 * or via a mng_info member. For convenience, in addition
10296 * to or instead of a comma-separated list of chunks, the
10297 * "exclude-chunk" string can be simply "all" or "none".
10298 *
10299 * The exclude-chunk define takes priority over the mng_info.
10300 *
10301 * A "PNG:include-chunk" define takes priority over both the
10302 * mng_info and the "PNG:exclude-chunk" define. Like the
10303 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010304 * well as a comma-separated list. Chunks that are unknown to
10305 * ImageMagick are always excluded, regardless of their "copy-safe"
10306 * status according to the PNG specification, and even if they
10307 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010308 *
10309 * Finally, all chunks listed in the "unused_chunks" array are
10310 * automatically excluded, regardless of the other instructions
10311 * or lack thereof.
10312 *
10313 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10314 * will not be written and the gAMA chunk will only be written if it
10315 * is not between .45 and .46, or approximately (1.0/2.2).
10316 *
10317 * If you exclude tRNS and the image has transparency, the colortype
10318 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10319 *
10320 * The -strip option causes StripImage() to set the png:include-chunk
10321 * artifact to "none,gama".
10322 */
10323
glennrp26f37912010-12-23 16:22:42 +000010324 mng_info->ping_exclude_bKGD=MagickFalse;
10325 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010326 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010327 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10328 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010329 mng_info->ping_exclude_iCCP=MagickFalse;
10330 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10331 mng_info->ping_exclude_oFFs=MagickFalse;
10332 mng_info->ping_exclude_pHYs=MagickFalse;
10333 mng_info->ping_exclude_sRGB=MagickFalse;
10334 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010335 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010336 mng_info->ping_exclude_vpAg=MagickFalse;
10337 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10338 mng_info->ping_exclude_zTXt=MagickFalse;
10339
glennrp8d3d6e52011-04-19 04:39:51 +000010340 mng_info->ping_preserve_colormap=MagickFalse;
10341
10342 value=GetImageArtifact(image,"png:preserve-colormap");
10343 if (value == NULL)
10344 value=GetImageOption(image_info,"png:preserve-colormap");
10345 if (value != NULL)
10346 mng_info->ping_preserve_colormap=MagickTrue;
10347
glennrp03812ae2010-12-24 01:31:34 +000010348 excluding=MagickFalse;
10349
glennrp5c7cf4e2010-12-24 00:30:00 +000010350 for (source=0; source<1; source++)
10351 {
10352 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010353 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010354 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010355
10356 if (value == NULL)
10357 value=GetImageArtifact(image,"png:exclude-chunks");
10358 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010359 else
glennrpacba0042010-12-24 14:27:26 +000010360 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010361 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010362
glennrpacba0042010-12-24 14:27:26 +000010363 if (value == NULL)
10364 value=GetImageOption(image_info,"png:exclude-chunks");
10365 }
10366
glennrp03812ae2010-12-24 01:31:34 +000010367 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010368 {
glennrp03812ae2010-12-24 01:31:34 +000010369
10370 size_t
10371 last;
10372
10373 excluding=MagickTrue;
10374
10375 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010376 {
10377 if (source == 0)
10378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10379 " png:exclude-chunk=%s found in image artifacts.\n", value);
10380 else
10381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10382 " png:exclude-chunk=%s found in image properties.\n", value);
10383 }
glennrp03812ae2010-12-24 01:31:34 +000010384
10385 last=strlen(value);
10386
10387 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010388 {
glennrp03812ae2010-12-24 01:31:34 +000010389
10390 if (LocaleNCompare(value+i,"all",3) == 0)
10391 {
10392 mng_info->ping_exclude_bKGD=MagickTrue;
10393 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010394 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010395 mng_info->ping_exclude_EXIF=MagickTrue;
10396 mng_info->ping_exclude_gAMA=MagickTrue;
10397 mng_info->ping_exclude_iCCP=MagickTrue;
10398 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10399 mng_info->ping_exclude_oFFs=MagickTrue;
10400 mng_info->ping_exclude_pHYs=MagickTrue;
10401 mng_info->ping_exclude_sRGB=MagickTrue;
10402 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010403 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010404 mng_info->ping_exclude_vpAg=MagickTrue;
10405 mng_info->ping_exclude_zCCP=MagickTrue;
10406 mng_info->ping_exclude_zTXt=MagickTrue;
10407 i--;
10408 }
glennrp2cc891a2010-12-24 13:44:32 +000010409
glennrp03812ae2010-12-24 01:31:34 +000010410 if (LocaleNCompare(value+i,"none",4) == 0)
10411 {
10412 mng_info->ping_exclude_bKGD=MagickFalse;
10413 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010414 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010415 mng_info->ping_exclude_EXIF=MagickFalse;
10416 mng_info->ping_exclude_gAMA=MagickFalse;
10417 mng_info->ping_exclude_iCCP=MagickFalse;
10418 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10419 mng_info->ping_exclude_oFFs=MagickFalse;
10420 mng_info->ping_exclude_pHYs=MagickFalse;
10421 mng_info->ping_exclude_sRGB=MagickFalse;
10422 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010423 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010424 mng_info->ping_exclude_vpAg=MagickFalse;
10425 mng_info->ping_exclude_zCCP=MagickFalse;
10426 mng_info->ping_exclude_zTXt=MagickFalse;
10427 }
glennrp2cc891a2010-12-24 13:44:32 +000010428
glennrp03812ae2010-12-24 01:31:34 +000010429 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10430 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010431
glennrp03812ae2010-12-24 01:31:34 +000010432 if (LocaleNCompare(value+i,"chrm",4) == 0)
10433 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010434
glennrpa0ed0092011-04-18 16:36:29 +000010435 if (LocaleNCompare(value+i,"date",4) == 0)
10436 mng_info->ping_exclude_date=MagickTrue;
10437
glennrp03812ae2010-12-24 01:31:34 +000010438 if (LocaleNCompare(value+i,"exif",4) == 0)
10439 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010440
glennrp03812ae2010-12-24 01:31:34 +000010441 if (LocaleNCompare(value+i,"gama",4) == 0)
10442 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010443
glennrp03812ae2010-12-24 01:31:34 +000010444 if (LocaleNCompare(value+i,"iccp",4) == 0)
10445 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010446
glennrp03812ae2010-12-24 01:31:34 +000010447 /*
10448 if (LocaleNCompare(value+i,"itxt",4) == 0)
10449 mng_info->ping_exclude_iTXt=MagickTrue;
10450 */
glennrp2cc891a2010-12-24 13:44:32 +000010451
glennrp03812ae2010-12-24 01:31:34 +000010452 if (LocaleNCompare(value+i,"gama",4) == 0)
10453 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010454
glennrp03812ae2010-12-24 01:31:34 +000010455 if (LocaleNCompare(value+i,"offs",4) == 0)
10456 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010457
glennrp03812ae2010-12-24 01:31:34 +000010458 if (LocaleNCompare(value+i,"phys",4) == 0)
10459 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010460
glennrpa1e3b7b2010-12-24 16:37:33 +000010461 if (LocaleNCompare(value+i,"srgb",4) == 0)
10462 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010463
glennrp03812ae2010-12-24 01:31:34 +000010464 if (LocaleNCompare(value+i,"text",4) == 0)
10465 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010466
glennrpa1e3b7b2010-12-24 16:37:33 +000010467 if (LocaleNCompare(value+i,"trns",4) == 0)
10468 mng_info->ping_exclude_tRNS=MagickTrue;
10469
glennrp03812ae2010-12-24 01:31:34 +000010470 if (LocaleNCompare(value+i,"vpag",4) == 0)
10471 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010472
glennrp03812ae2010-12-24 01:31:34 +000010473 if (LocaleNCompare(value+i,"zccp",4) == 0)
10474 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010475
glennrp03812ae2010-12-24 01:31:34 +000010476 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10477 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010478
glennrp03812ae2010-12-24 01:31:34 +000010479 }
glennrpce91ed52010-12-23 22:37:49 +000010480 }
glennrp26f37912010-12-23 16:22:42 +000010481 }
10482
glennrp5c7cf4e2010-12-24 00:30:00 +000010483 for (source=0; source<1; source++)
10484 {
10485 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010486 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010487 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010488
10489 if (value == NULL)
10490 value=GetImageArtifact(image,"png:include-chunks");
10491 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010492 else
glennrpacba0042010-12-24 14:27:26 +000010493 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010494 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010495
glennrpacba0042010-12-24 14:27:26 +000010496 if (value == NULL)
10497 value=GetImageOption(image_info,"png:include-chunks");
10498 }
10499
glennrp03812ae2010-12-24 01:31:34 +000010500 if (value != NULL)
10501 {
10502 size_t
10503 last;
glennrp26f37912010-12-23 16:22:42 +000010504
glennrp03812ae2010-12-24 01:31:34 +000010505 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010506
glennrp03812ae2010-12-24 01:31:34 +000010507 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010508 {
10509 if (source == 0)
10510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10511 " png:include-chunk=%s found in image artifacts.\n", value);
10512 else
10513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10514 " png:include-chunk=%s found in image properties.\n", value);
10515 }
glennrp03812ae2010-12-24 01:31:34 +000010516
10517 last=strlen(value);
10518
10519 for (i=0; i<(int) last; i+=5)
10520 {
10521 if (LocaleNCompare(value+i,"all",3) == 0)
10522 {
10523 mng_info->ping_exclude_bKGD=MagickFalse;
10524 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010525 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010526 mng_info->ping_exclude_EXIF=MagickFalse;
10527 mng_info->ping_exclude_gAMA=MagickFalse;
10528 mng_info->ping_exclude_iCCP=MagickFalse;
10529 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10530 mng_info->ping_exclude_oFFs=MagickFalse;
10531 mng_info->ping_exclude_pHYs=MagickFalse;
10532 mng_info->ping_exclude_sRGB=MagickFalse;
10533 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010534 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010535 mng_info->ping_exclude_vpAg=MagickFalse;
10536 mng_info->ping_exclude_zCCP=MagickFalse;
10537 mng_info->ping_exclude_zTXt=MagickFalse;
10538 i--;
10539 }
glennrp2cc891a2010-12-24 13:44:32 +000010540
glennrp03812ae2010-12-24 01:31:34 +000010541 if (LocaleNCompare(value+i,"none",4) == 0)
10542 {
10543 mng_info->ping_exclude_bKGD=MagickTrue;
10544 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010545 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010546 mng_info->ping_exclude_EXIF=MagickTrue;
10547 mng_info->ping_exclude_gAMA=MagickTrue;
10548 mng_info->ping_exclude_iCCP=MagickTrue;
10549 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10550 mng_info->ping_exclude_oFFs=MagickTrue;
10551 mng_info->ping_exclude_pHYs=MagickTrue;
10552 mng_info->ping_exclude_sRGB=MagickTrue;
10553 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010554 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010555 mng_info->ping_exclude_vpAg=MagickTrue;
10556 mng_info->ping_exclude_zCCP=MagickTrue;
10557 mng_info->ping_exclude_zTXt=MagickTrue;
10558 }
glennrp2cc891a2010-12-24 13:44:32 +000010559
glennrp03812ae2010-12-24 01:31:34 +000010560 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10561 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010562
glennrp03812ae2010-12-24 01:31:34 +000010563 if (LocaleNCompare(value+i,"chrm",4) == 0)
10564 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010565
glennrpa0ed0092011-04-18 16:36:29 +000010566 if (LocaleNCompare(value+i,"date",4) == 0)
10567 mng_info->ping_exclude_date=MagickFalse;
10568
glennrp03812ae2010-12-24 01:31:34 +000010569 if (LocaleNCompare(value+i,"exif",4) == 0)
10570 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010571
glennrp03812ae2010-12-24 01:31:34 +000010572 if (LocaleNCompare(value+i,"gama",4) == 0)
10573 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010574
glennrp03812ae2010-12-24 01:31:34 +000010575 if (LocaleNCompare(value+i,"iccp",4) == 0)
10576 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010577
glennrp03812ae2010-12-24 01:31:34 +000010578 /*
10579 if (LocaleNCompare(value+i,"itxt",4) == 0)
10580 mng_info->ping_exclude_iTXt=MagickFalse;
10581 */
glennrp2cc891a2010-12-24 13:44:32 +000010582
glennrp03812ae2010-12-24 01:31:34 +000010583 if (LocaleNCompare(value+i,"gama",4) == 0)
10584 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010585
glennrp03812ae2010-12-24 01:31:34 +000010586 if (LocaleNCompare(value+i,"offs",4) == 0)
10587 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010588
glennrp03812ae2010-12-24 01:31:34 +000010589 if (LocaleNCompare(value+i,"phys",4) == 0)
10590 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010591
glennrpa1e3b7b2010-12-24 16:37:33 +000010592 if (LocaleNCompare(value+i,"srgb",4) == 0)
10593 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010594
glennrp03812ae2010-12-24 01:31:34 +000010595 if (LocaleNCompare(value+i,"text",4) == 0)
10596 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010597
glennrpa1e3b7b2010-12-24 16:37:33 +000010598 if (LocaleNCompare(value+i,"trns",4) == 0)
10599 mng_info->ping_exclude_tRNS=MagickFalse;
10600
glennrp03812ae2010-12-24 01:31:34 +000010601 if (LocaleNCompare(value+i,"vpag",4) == 0)
10602 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010603
glennrp03812ae2010-12-24 01:31:34 +000010604 if (LocaleNCompare(value+i,"zccp",4) == 0)
10605 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010606
glennrp03812ae2010-12-24 01:31:34 +000010607 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10608 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010609
glennrp03812ae2010-12-24 01:31:34 +000010610 }
glennrpce91ed52010-12-23 22:37:49 +000010611 }
glennrp26f37912010-12-23 16:22:42 +000010612 }
10613
glennrp03812ae2010-12-24 01:31:34 +000010614 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010615 {
10616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10617 " Chunks to be excluded from the output PNG:");
10618 if (mng_info->ping_exclude_bKGD != MagickFalse)
10619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10620 " bKGD");
10621 if (mng_info->ping_exclude_cHRM != MagickFalse)
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10623 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010624 if (mng_info->ping_exclude_date != MagickFalse)
10625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10626 " date");
glennrp26f37912010-12-23 16:22:42 +000010627 if (mng_info->ping_exclude_EXIF != MagickFalse)
10628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10629 " EXIF");
10630 if (mng_info->ping_exclude_gAMA != MagickFalse)
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 " gAMA");
10633 if (mng_info->ping_exclude_iCCP != MagickFalse)
10634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10635 " iCCP");
10636/*
10637 if (mng_info->ping_exclude_iTXt != MagickFalse)
10638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639 " iTXt");
10640*/
10641 if (mng_info->ping_exclude_oFFs != MagickFalse)
10642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10643 " oFFs");
10644 if (mng_info->ping_exclude_pHYs != MagickFalse)
10645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10646 " pHYs");
10647 if (mng_info->ping_exclude_sRGB != MagickFalse)
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 " sRGB");
10650 if (mng_info->ping_exclude_tEXt != MagickFalse)
10651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010653 if (mng_info->ping_exclude_tRNS != MagickFalse)
10654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10655 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010656 if (mng_info->ping_exclude_vpAg != MagickFalse)
10657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10658 " vpAg");
10659 if (mng_info->ping_exclude_zCCP != MagickFalse)
10660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10661 " zCCP");
10662 if (mng_info->ping_exclude_zTXt != MagickFalse)
10663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10664 " zTXt");
10665 }
10666
glennrpb9cfe272010-12-21 15:08:06 +000010667 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010668
glennrpb9cfe272010-12-21 15:08:06 +000010669 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010670
10671 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010672
cristy3ed852e2009-09-05 21:47:34 +000010673 if (logging != MagickFalse)
10674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010675
cristy3ed852e2009-09-05 21:47:34 +000010676 return(status);
10677}
10678
10679#if defined(JNG_SUPPORTED)
10680
10681/* Write one JNG image */
10682static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10683 const ImageInfo *image_info,Image *image)
10684{
10685 Image
10686 *jpeg_image;
10687
10688 ImageInfo
10689 *jpeg_image_info;
10690
10691 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010692 logging,
cristy3ed852e2009-09-05 21:47:34 +000010693 status;
10694
10695 size_t
10696 length;
10697
10698 unsigned char
10699 *blob,
10700 chunk[80],
10701 *p;
10702
10703 unsigned int
10704 jng_alpha_compression_method,
10705 jng_alpha_sample_depth,
10706 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010707 transparent;
10708
cristybb503372010-05-27 20:51:26 +000010709 size_t
cristy3ed852e2009-09-05 21:47:34 +000010710 jng_quality;
10711
10712 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010713 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010714
10715 blob=(unsigned char *) NULL;
10716 jpeg_image=(Image *) NULL;
10717 jpeg_image_info=(ImageInfo *) NULL;
10718
10719 status=MagickTrue;
10720 transparent=image_info->type==GrayscaleMatteType ||
10721 image_info->type==TrueColorMatteType;
10722 jng_color_type=10;
10723 jng_alpha_sample_depth=0;
10724 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10725 jng_alpha_compression_method=0;
10726
10727 if (image->matte != MagickFalse)
10728 {
10729 /* if any pixels are transparent */
10730 transparent=MagickTrue;
10731 if (image_info->compression==JPEGCompression)
10732 jng_alpha_compression_method=8;
10733 }
10734
10735 if (transparent)
10736 {
10737 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010738
cristy3ed852e2009-09-05 21:47:34 +000010739 /* Create JPEG blob, image, and image_info */
10740 if (logging != MagickFalse)
10741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10742 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010743
cristy3ed852e2009-09-05 21:47:34 +000010744 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010745
cristy3ed852e2009-09-05 21:47:34 +000010746 if (jpeg_image_info == (ImageInfo *) NULL)
10747 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010748
cristy3ed852e2009-09-05 21:47:34 +000010749 if (logging != MagickFalse)
10750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10751 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010752
cristy3ed852e2009-09-05 21:47:34 +000010753 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010754
cristy3ed852e2009-09-05 21:47:34 +000010755 if (jpeg_image == (Image *) NULL)
10756 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010757
cristy3ed852e2009-09-05 21:47:34 +000010758 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10759 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10760 status=NegateImage(jpeg_image,MagickFalse);
10761 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010762
cristy3ed852e2009-09-05 21:47:34 +000010763 if (jng_quality >= 1000)
10764 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010765
cristy3ed852e2009-09-05 21:47:34 +000010766 else
10767 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010768
cristy3ed852e2009-09-05 21:47:34 +000010769 jpeg_image_info->type=GrayscaleType;
10770 (void) SetImageType(jpeg_image,GrayscaleType);
10771 (void) AcquireUniqueFilename(jpeg_image->filename);
10772 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10773 "%s",jpeg_image->filename);
10774 }
10775
10776 /* To do: check bit depth of PNG alpha channel */
10777
10778 /* Check if image is grayscale. */
10779 if (image_info->type != TrueColorMatteType && image_info->type !=
10780 TrueColorType && ImageIsGray(image))
10781 jng_color_type-=2;
10782
10783 if (transparent)
10784 {
10785 if (jng_alpha_compression_method==0)
10786 {
10787 const char
10788 *value;
10789
10790 /* Encode opacity as a grayscale PNG blob */
10791 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10792 &image->exception);
10793 if (logging != MagickFalse)
10794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10795 " Creating PNG blob.");
10796 length=0;
10797
10798 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10799 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10800 jpeg_image_info->interlace=NoInterlace;
10801
10802 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10803 &image->exception);
10804
10805 /* Retrieve sample depth used */
10806 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10807 if (value != (char *) NULL)
10808 jng_alpha_sample_depth= (unsigned int) value[0];
10809 }
10810 else
10811 {
10812 /* Encode opacity as a grayscale JPEG blob */
10813
10814 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10815 &image->exception);
10816
10817 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10818 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10819 jpeg_image_info->interlace=NoInterlace;
10820 if (logging != MagickFalse)
10821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10822 " Creating blob.");
10823 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010824 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010825 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010826
cristy3ed852e2009-09-05 21:47:34 +000010827 if (logging != MagickFalse)
10828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010829 " Successfully read jpeg_image into a blob, length=%.20g.",
10830 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010831
10832 }
10833 /* Destroy JPEG image and image_info */
10834 jpeg_image=DestroyImage(jpeg_image);
10835 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10836 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10837 }
10838
10839 /* Write JHDR chunk */
10840 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10841 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010842 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010843 PNGLong(chunk+4,(png_uint_32) image->columns);
10844 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010845 chunk[12]=jng_color_type;
10846 chunk[13]=8; /* sample depth */
10847 chunk[14]=8; /*jng_image_compression_method */
10848 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10849 chunk[16]=jng_alpha_sample_depth;
10850 chunk[17]=jng_alpha_compression_method;
10851 chunk[18]=0; /*jng_alpha_filter_method */
10852 chunk[19]=0; /*jng_alpha_interlace_method */
10853 (void) WriteBlob(image,20,chunk);
10854 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10855 if (logging != MagickFalse)
10856 {
10857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010858 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010859
cristy3ed852e2009-09-05 21:47:34 +000010860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010861 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010862
cristy3ed852e2009-09-05 21:47:34 +000010863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10864 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010865
cristy3ed852e2009-09-05 21:47:34 +000010866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10867 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010868
cristy3ed852e2009-09-05 21:47:34 +000010869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10870 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010871
cristy3ed852e2009-09-05 21:47:34 +000010872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10873 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010874
cristy3ed852e2009-09-05 21:47:34 +000010875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10876 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010877
cristy3ed852e2009-09-05 21:47:34 +000010878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10879 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010880
cristy3ed852e2009-09-05 21:47:34 +000010881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10882 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010883
cristy3ed852e2009-09-05 21:47:34 +000010884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10885 " JNG alpha interlace:%5d",0);
10886 }
10887
glennrp0fe50b42010-11-16 03:52:51 +000010888 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010889 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010890
10891 /*
10892 Write leading ancillary chunks
10893 */
10894
10895 if (transparent)
10896 {
10897 /*
10898 Write JNG bKGD chunk
10899 */
10900
10901 unsigned char
10902 blue,
10903 green,
10904 red;
10905
cristybb503372010-05-27 20:51:26 +000010906 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010907 num_bytes;
10908
10909 if (jng_color_type == 8 || jng_color_type == 12)
10910 num_bytes=6L;
10911 else
10912 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010913 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010914 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010915 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010916 red=ScaleQuantumToChar(image->background_color.red);
10917 green=ScaleQuantumToChar(image->background_color.green);
10918 blue=ScaleQuantumToChar(image->background_color.blue);
10919 *(chunk+4)=0;
10920 *(chunk+5)=red;
10921 *(chunk+6)=0;
10922 *(chunk+7)=green;
10923 *(chunk+8)=0;
10924 *(chunk+9)=blue;
10925 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10926 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10927 }
10928
10929 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10930 {
10931 /*
10932 Write JNG sRGB chunk
10933 */
10934 (void) WriteBlobMSBULong(image,1L);
10935 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010936 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010937
cristy3ed852e2009-09-05 21:47:34 +000010938 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010939 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010940 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010941 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010942
cristy3ed852e2009-09-05 21:47:34 +000010943 else
glennrpe610a072010-08-05 17:08:46 +000010944 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010945 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010946 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010947
cristy3ed852e2009-09-05 21:47:34 +000010948 (void) WriteBlob(image,5,chunk);
10949 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10950 }
10951 else
10952 {
10953 if (image->gamma != 0.0)
10954 {
10955 /*
10956 Write JNG gAMA chunk
10957 */
10958 (void) WriteBlobMSBULong(image,4L);
10959 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010960 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010961 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010962 (void) WriteBlob(image,8,chunk);
10963 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10964 }
glennrp0fe50b42010-11-16 03:52:51 +000010965
cristy3ed852e2009-09-05 21:47:34 +000010966 if ((mng_info->equal_chrms == MagickFalse) &&
10967 (image->chromaticity.red_primary.x != 0.0))
10968 {
10969 PrimaryInfo
10970 primary;
10971
10972 /*
10973 Write JNG cHRM chunk
10974 */
10975 (void) WriteBlobMSBULong(image,32L);
10976 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010977 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010978 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010979 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10980 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010981 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010982 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10983 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010984 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010985 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10986 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010987 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010988 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10989 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010990 (void) WriteBlob(image,36,chunk);
10991 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10992 }
10993 }
glennrp0fe50b42010-11-16 03:52:51 +000010994
cristy3ed852e2009-09-05 21:47:34 +000010995 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10996 {
10997 /*
10998 Write JNG pHYs chunk
10999 */
11000 (void) WriteBlobMSBULong(image,9L);
11001 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011002 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011003 if (image->units == PixelsPerInchResolution)
11004 {
cristy35ef8242010-06-03 16:24:13 +000011005 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011006 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011007
cristy35ef8242010-06-03 16:24:13 +000011008 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011009 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011010
cristy3ed852e2009-09-05 21:47:34 +000011011 chunk[12]=1;
11012 }
glennrp0fe50b42010-11-16 03:52:51 +000011013
cristy3ed852e2009-09-05 21:47:34 +000011014 else
11015 {
11016 if (image->units == PixelsPerCentimeterResolution)
11017 {
cristy35ef8242010-06-03 16:24:13 +000011018 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011019 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011020
cristy35ef8242010-06-03 16:24:13 +000011021 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011022 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011023
cristy3ed852e2009-09-05 21:47:34 +000011024 chunk[12]=1;
11025 }
glennrp0fe50b42010-11-16 03:52:51 +000011026
cristy3ed852e2009-09-05 21:47:34 +000011027 else
11028 {
cristy35ef8242010-06-03 16:24:13 +000011029 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11030 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011031 chunk[12]=0;
11032 }
11033 }
11034 (void) WriteBlob(image,13,chunk);
11035 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11036 }
11037
11038 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11039 {
11040 /*
11041 Write JNG oFFs chunk
11042 */
11043 (void) WriteBlobMSBULong(image,9L);
11044 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011045 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011046 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11047 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011048 chunk[12]=0;
11049 (void) WriteBlob(image,13,chunk);
11050 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11051 }
11052 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11053 {
11054 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11055 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011056 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011057 PNGLong(chunk+4,(png_uint_32) image->page.width);
11058 PNGLong(chunk+8,(png_uint_32) image->page.height);
11059 chunk[12]=0; /* unit = pixels */
11060 (void) WriteBlob(image,13,chunk);
11061 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11062 }
11063
11064
11065 if (transparent)
11066 {
11067 if (jng_alpha_compression_method==0)
11068 {
cristybb503372010-05-27 20:51:26 +000011069 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011070 i;
11071
cristybb503372010-05-27 20:51:26 +000011072 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011073 len;
11074
11075 /* Write IDAT chunk header */
11076 if (logging != MagickFalse)
11077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011078 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011079 length);
cristy3ed852e2009-09-05 21:47:34 +000011080
11081 /* Copy IDAT chunks */
11082 len=0;
11083 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011084 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011085 {
11086 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11087 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011088
cristy3ed852e2009-09-05 21:47:34 +000011089 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11090 {
11091 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011092 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011093 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011094 (void) WriteBlob(image,(size_t) len+4,p);
11095 (void) WriteBlobMSBULong(image,
11096 crc32(0,p,(uInt) len+4));
11097 }
glennrp0fe50b42010-11-16 03:52:51 +000011098
cristy3ed852e2009-09-05 21:47:34 +000011099 else
11100 {
11101 if (logging != MagickFalse)
11102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011103 " Skipping %c%c%c%c chunk, length=%.20g.",
11104 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011105 }
11106 p+=(8+len);
11107 }
11108 }
11109 else
11110 {
11111 /* Write JDAA chunk header */
11112 if (logging != MagickFalse)
11113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011114 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011115 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011116 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011117 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011118 /* Write JDAT chunk(s) data */
11119 (void) WriteBlob(image,4,chunk);
11120 (void) WriteBlob(image,length,blob);
11121 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11122 (uInt) length));
11123 }
11124 blob=(unsigned char *) RelinquishMagickMemory(blob);
11125 }
11126
11127 /* Encode image as a JPEG blob */
11128 if (logging != MagickFalse)
11129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11130 " Creating jpeg_image_info.");
11131 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11132 if (jpeg_image_info == (ImageInfo *) NULL)
11133 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11134
11135 if (logging != MagickFalse)
11136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11137 " Creating jpeg_image.");
11138
11139 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11140 if (jpeg_image == (Image *) NULL)
11141 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11142 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11143
11144 (void) AcquireUniqueFilename(jpeg_image->filename);
11145 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11146 jpeg_image->filename);
11147
11148 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11149 &image->exception);
11150
11151 if (logging != MagickFalse)
11152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011153 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11154 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011155
11156 if (jng_color_type == 8 || jng_color_type == 12)
11157 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011158
cristy3ed852e2009-09-05 21:47:34 +000011159 jpeg_image_info->quality=jng_quality % 1000;
11160 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11161 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011162
cristy3ed852e2009-09-05 21:47:34 +000011163 if (logging != MagickFalse)
11164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11165 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011166
cristy3ed852e2009-09-05 21:47:34 +000011167 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011168
cristy3ed852e2009-09-05 21:47:34 +000011169 if (logging != MagickFalse)
11170 {
11171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011172 " Successfully read jpeg_image into a blob, length=%.20g.",
11173 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011174
11175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011176 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011177 }
glennrp0fe50b42010-11-16 03:52:51 +000011178
cristy3ed852e2009-09-05 21:47:34 +000011179 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011180 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011181 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011182 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011183 (void) WriteBlob(image,4,chunk);
11184 (void) WriteBlob(image,length,blob);
11185 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11186
11187 jpeg_image=DestroyImage(jpeg_image);
11188 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11189 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11190 blob=(unsigned char *) RelinquishMagickMemory(blob);
11191
11192 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011193 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011194
11195 /* Write IEND chunk */
11196 (void) WriteBlobMSBULong(image,0L);
11197 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011198 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011199 (void) WriteBlob(image,4,chunk);
11200 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11201
11202 if (logging != MagickFalse)
11203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11204 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011205
cristy3ed852e2009-09-05 21:47:34 +000011206 return(status);
11207}
11208
11209
11210/*
11211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11212% %
11213% %
11214% %
11215% W r i t e J N G I m a g e %
11216% %
11217% %
11218% %
11219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11220%
11221% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11222%
11223% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11224%
11225% The format of the WriteJNGImage method is:
11226%
11227% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11228%
11229% A description of each parameter follows:
11230%
11231% o image_info: the image info.
11232%
11233% o image: The image.
11234%
11235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11236*/
11237static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11238{
11239 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011240 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011241 logging,
cristy3ed852e2009-09-05 21:47:34 +000011242 status;
11243
11244 MngInfo
11245 *mng_info;
11246
cristy3ed852e2009-09-05 21:47:34 +000011247 /*
11248 Open image file.
11249 */
11250 assert(image_info != (const ImageInfo *) NULL);
11251 assert(image_info->signature == MagickSignature);
11252 assert(image != (Image *) NULL);
11253 assert(image->signature == MagickSignature);
11254 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011255 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011256 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11257 if (status == MagickFalse)
11258 return(status);
11259
11260 /*
11261 Allocate a MngInfo structure.
11262 */
11263 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011264 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011265 if (mng_info == (MngInfo *) NULL)
11266 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11267 /*
11268 Initialize members of the MngInfo structure.
11269 */
11270 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11271 mng_info->image=image;
11272 have_mng_structure=MagickTrue;
11273
11274 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11275
11276 status=WriteOneJNGImage(mng_info,image_info,image);
11277 (void) CloseBlob(image);
11278
11279 (void) CatchImageException(image);
11280 MngInfoFreeStruct(mng_info,&have_mng_structure);
11281 if (logging != MagickFalse)
11282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11283 return(status);
11284}
11285#endif
11286
11287
11288
11289static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11290{
11291 const char
11292 *option;
11293
11294 Image
11295 *next_image;
11296
11297 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011298 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011299 status;
11300
glennrp03812ae2010-12-24 01:31:34 +000011301 volatile MagickBooleanType
11302 logging;
11303
cristy3ed852e2009-09-05 21:47:34 +000011304 MngInfo
11305 *mng_info;
11306
11307 int
cristy3ed852e2009-09-05 21:47:34 +000011308 image_count,
11309 need_iterations,
11310 need_matte;
11311
11312 volatile int
11313#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11314 defined(PNG_MNG_FEATURES_SUPPORTED)
11315 need_local_plte,
11316#endif
11317 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011318 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011319 use_global_plte;
11320
cristybb503372010-05-27 20:51:26 +000011321 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011322 i;
11323
11324 unsigned char
11325 chunk[800];
11326
11327 volatile unsigned int
11328 write_jng,
11329 write_mng;
11330
cristybb503372010-05-27 20:51:26 +000011331 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011332 scene;
11333
cristybb503372010-05-27 20:51:26 +000011334 size_t
cristy3ed852e2009-09-05 21:47:34 +000011335 final_delay=0,
11336 initial_delay;
11337
glennrpd5045b42010-03-24 12:40:35 +000011338#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011339 if (image_info->verbose)
11340 printf("Your PNG library (libpng-%s) is rather old.\n",
11341 PNG_LIBPNG_VER_STRING);
11342#endif
11343
11344 /*
11345 Open image file.
11346 */
11347 assert(image_info != (const ImageInfo *) NULL);
11348 assert(image_info->signature == MagickSignature);
11349 assert(image != (Image *) NULL);
11350 assert(image->signature == MagickSignature);
11351 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011352 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011353 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11354 if (status == MagickFalse)
11355 return(status);
11356
11357 /*
11358 Allocate a MngInfo structure.
11359 */
11360 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011361 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011362 if (mng_info == (MngInfo *) NULL)
11363 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11364 /*
11365 Initialize members of the MngInfo structure.
11366 */
11367 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11368 mng_info->image=image;
11369 have_mng_structure=MagickTrue;
11370 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11371
11372 /*
11373 * See if user has requested a specific PNG subformat to be used
11374 * for all of the PNGs in the MNG being written, e.g.,
11375 *
11376 * convert *.png png8:animation.mng
11377 *
11378 * To do: check -define png:bit_depth and png:color_type as well,
11379 * or perhaps use mng:bit_depth and mng:color_type instead for
11380 * global settings.
11381 */
11382
11383 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11384 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11385 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11386
11387 write_jng=MagickFalse;
11388 if (image_info->compression == JPEGCompression)
11389 write_jng=MagickTrue;
11390
11391 mng_info->adjoin=image_info->adjoin &&
11392 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11393
cristy3ed852e2009-09-05 21:47:34 +000011394 if (logging != MagickFalse)
11395 {
11396 /* Log some info about the input */
11397 Image
11398 *p;
11399
11400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11401 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011402
cristy3ed852e2009-09-05 21:47:34 +000011403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011404 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011405
cristy3ed852e2009-09-05 21:47:34 +000011406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11407 " Type: %d",image_info->type);
11408
11409 scene=0;
11410 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11411 {
11412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011413 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011414
cristy3ed852e2009-09-05 21:47:34 +000011415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011416 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011417
cristy3ed852e2009-09-05 21:47:34 +000011418 if (p->matte)
11419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11420 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011421
cristy3ed852e2009-09-05 21:47:34 +000011422 else
11423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11424 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011425
cristy3ed852e2009-09-05 21:47:34 +000011426 if (p->storage_class == PseudoClass)
11427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11428 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011429
cristy3ed852e2009-09-05 21:47:34 +000011430 else
11431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11432 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011433
cristy3ed852e2009-09-05 21:47:34 +000011434 if (p->colors)
11435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011436 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011437
cristy3ed852e2009-09-05 21:47:34 +000011438 else
11439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11440 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011441
cristy3ed852e2009-09-05 21:47:34 +000011442 if (mng_info->adjoin == MagickFalse)
11443 break;
11444 }
11445 }
11446
cristy3ed852e2009-09-05 21:47:34 +000011447 use_global_plte=MagickFalse;
11448 all_images_are_gray=MagickFalse;
11449#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11450 need_local_plte=MagickTrue;
11451#endif
11452 need_defi=MagickFalse;
11453 need_matte=MagickFalse;
11454 mng_info->framing_mode=1;
11455 mng_info->old_framing_mode=1;
11456
11457 if (write_mng)
11458 if (image_info->page != (char *) NULL)
11459 {
11460 /*
11461 Determine image bounding box.
11462 */
11463 SetGeometry(image,&mng_info->page);
11464 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11465 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11466 }
11467 if (write_mng)
11468 {
11469 unsigned int
11470 need_geom;
11471
11472 unsigned short
11473 red,
11474 green,
11475 blue;
11476
11477 mng_info->page=image->page;
11478 need_geom=MagickTrue;
11479 if (mng_info->page.width || mng_info->page.height)
11480 need_geom=MagickFalse;
11481 /*
11482 Check all the scenes.
11483 */
11484 initial_delay=image->delay;
11485 need_iterations=MagickFalse;
11486 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11487 mng_info->equal_physs=MagickTrue,
11488 mng_info->equal_gammas=MagickTrue;
11489 mng_info->equal_srgbs=MagickTrue;
11490 mng_info->equal_backgrounds=MagickTrue;
11491 image_count=0;
11492#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11493 defined(PNG_MNG_FEATURES_SUPPORTED)
11494 all_images_are_gray=MagickTrue;
11495 mng_info->equal_palettes=MagickFalse;
11496 need_local_plte=MagickFalse;
11497#endif
11498 for (next_image=image; next_image != (Image *) NULL; )
11499 {
11500 if (need_geom)
11501 {
11502 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11503 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011504
cristy3ed852e2009-09-05 21:47:34 +000011505 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11506 mng_info->page.height=next_image->rows+next_image->page.y;
11507 }
glennrp0fe50b42010-11-16 03:52:51 +000011508
cristy3ed852e2009-09-05 21:47:34 +000011509 if (next_image->page.x || next_image->page.y)
11510 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011511
cristy3ed852e2009-09-05 21:47:34 +000011512 if (next_image->matte)
11513 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011514
cristy3ed852e2009-09-05 21:47:34 +000011515 if ((int) next_image->dispose >= BackgroundDispose)
11516 if (next_image->matte || next_image->page.x || next_image->page.y ||
11517 ((next_image->columns < mng_info->page.width) &&
11518 (next_image->rows < mng_info->page.height)))
11519 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011520
cristy3ed852e2009-09-05 21:47:34 +000011521 if (next_image->iterations)
11522 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011523
cristy3ed852e2009-09-05 21:47:34 +000011524 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011525
cristy3ed852e2009-09-05 21:47:34 +000011526 if (final_delay != initial_delay || final_delay > 1UL*
11527 next_image->ticks_per_second)
11528 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011529
cristy3ed852e2009-09-05 21:47:34 +000011530#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11531 defined(PNG_MNG_FEATURES_SUPPORTED)
11532 /*
11533 check for global palette possibility.
11534 */
11535 if (image->matte != MagickFalse)
11536 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011537
cristy3ed852e2009-09-05 21:47:34 +000011538 if (need_local_plte == 0)
11539 {
11540 if (ImageIsGray(image) == MagickFalse)
11541 all_images_are_gray=MagickFalse;
11542 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11543 if (use_global_plte == 0)
11544 use_global_plte=mng_info->equal_palettes;
11545 need_local_plte=!mng_info->equal_palettes;
11546 }
11547#endif
11548 if (GetNextImageInList(next_image) != (Image *) NULL)
11549 {
11550 if (next_image->background_color.red !=
11551 next_image->next->background_color.red ||
11552 next_image->background_color.green !=
11553 next_image->next->background_color.green ||
11554 next_image->background_color.blue !=
11555 next_image->next->background_color.blue)
11556 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011557
cristy3ed852e2009-09-05 21:47:34 +000011558 if (next_image->gamma != next_image->next->gamma)
11559 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011560
cristy3ed852e2009-09-05 21:47:34 +000011561 if (next_image->rendering_intent !=
11562 next_image->next->rendering_intent)
11563 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011564
cristy3ed852e2009-09-05 21:47:34 +000011565 if ((next_image->units != next_image->next->units) ||
11566 (next_image->x_resolution != next_image->next->x_resolution) ||
11567 (next_image->y_resolution != next_image->next->y_resolution))
11568 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011569
cristy3ed852e2009-09-05 21:47:34 +000011570 if (mng_info->equal_chrms)
11571 {
11572 if (next_image->chromaticity.red_primary.x !=
11573 next_image->next->chromaticity.red_primary.x ||
11574 next_image->chromaticity.red_primary.y !=
11575 next_image->next->chromaticity.red_primary.y ||
11576 next_image->chromaticity.green_primary.x !=
11577 next_image->next->chromaticity.green_primary.x ||
11578 next_image->chromaticity.green_primary.y !=
11579 next_image->next->chromaticity.green_primary.y ||
11580 next_image->chromaticity.blue_primary.x !=
11581 next_image->next->chromaticity.blue_primary.x ||
11582 next_image->chromaticity.blue_primary.y !=
11583 next_image->next->chromaticity.blue_primary.y ||
11584 next_image->chromaticity.white_point.x !=
11585 next_image->next->chromaticity.white_point.x ||
11586 next_image->chromaticity.white_point.y !=
11587 next_image->next->chromaticity.white_point.y)
11588 mng_info->equal_chrms=MagickFalse;
11589 }
11590 }
11591 image_count++;
11592 next_image=GetNextImageInList(next_image);
11593 }
11594 if (image_count < 2)
11595 {
11596 mng_info->equal_backgrounds=MagickFalse;
11597 mng_info->equal_chrms=MagickFalse;
11598 mng_info->equal_gammas=MagickFalse;
11599 mng_info->equal_srgbs=MagickFalse;
11600 mng_info->equal_physs=MagickFalse;
11601 use_global_plte=MagickFalse;
11602#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11603 need_local_plte=MagickTrue;
11604#endif
11605 need_iterations=MagickFalse;
11606 }
glennrp0fe50b42010-11-16 03:52:51 +000011607
cristy3ed852e2009-09-05 21:47:34 +000011608 if (mng_info->need_fram == MagickFalse)
11609 {
11610 /*
11611 Only certain framing rates 100/n are exactly representable without
11612 the FRAM chunk but we'll allow some slop in VLC files
11613 */
11614 if (final_delay == 0)
11615 {
11616 if (need_iterations != MagickFalse)
11617 {
11618 /*
11619 It's probably a GIF with loop; don't run it *too* fast.
11620 */
glennrp02617122010-07-28 13:07:35 +000011621 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011622 {
11623 final_delay=10;
11624 (void) ThrowMagickException(&image->exception,
11625 GetMagickModule(),CoderWarning,
11626 "input has zero delay between all frames; assuming",
11627 " 10 cs `%s'","");
11628 }
cristy3ed852e2009-09-05 21:47:34 +000011629 }
11630 else
11631 mng_info->ticks_per_second=0;
11632 }
11633 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011634 mng_info->ticks_per_second=(png_uint_32)
11635 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011636 if (final_delay > 50)
11637 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011638
cristy3ed852e2009-09-05 21:47:34 +000011639 if (final_delay > 75)
11640 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011641
cristy3ed852e2009-09-05 21:47:34 +000011642 if (final_delay > 125)
11643 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011644
cristy3ed852e2009-09-05 21:47:34 +000011645 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11646 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11647 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11648 1UL*image->ticks_per_second))
11649 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11650 }
glennrp0fe50b42010-11-16 03:52:51 +000011651
cristy3ed852e2009-09-05 21:47:34 +000011652 if (mng_info->need_fram != MagickFalse)
11653 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11654 /*
11655 If pseudocolor, we should also check to see if all the
11656 palettes are identical and write a global PLTE if they are.
11657 ../glennrp Feb 99.
11658 */
11659 /*
11660 Write the MNG version 1.0 signature and MHDR chunk.
11661 */
11662 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11663 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11664 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011665 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011666 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11667 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011668 PNGLong(chunk+12,mng_info->ticks_per_second);
11669 PNGLong(chunk+16,0L); /* layer count=unknown */
11670 PNGLong(chunk+20,0L); /* frame count=unknown */
11671 PNGLong(chunk+24,0L); /* play time=unknown */
11672 if (write_jng)
11673 {
11674 if (need_matte)
11675 {
11676 if (need_defi || mng_info->need_fram || use_global_plte)
11677 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011678
cristy3ed852e2009-09-05 21:47:34 +000011679 else
11680 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11681 }
glennrp0fe50b42010-11-16 03:52:51 +000011682
cristy3ed852e2009-09-05 21:47:34 +000011683 else
11684 {
11685 if (need_defi || mng_info->need_fram || use_global_plte)
11686 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011687
cristy3ed852e2009-09-05 21:47:34 +000011688 else
11689 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11690 }
11691 }
glennrp0fe50b42010-11-16 03:52:51 +000011692
cristy3ed852e2009-09-05 21:47:34 +000011693 else
11694 {
11695 if (need_matte)
11696 {
11697 if (need_defi || mng_info->need_fram || use_global_plte)
11698 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011699
cristy3ed852e2009-09-05 21:47:34 +000011700 else
11701 PNGLong(chunk+28,9L); /* simplicity=VLC */
11702 }
glennrp0fe50b42010-11-16 03:52:51 +000011703
cristy3ed852e2009-09-05 21:47:34 +000011704 else
11705 {
11706 if (need_defi || mng_info->need_fram || use_global_plte)
11707 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011708
cristy3ed852e2009-09-05 21:47:34 +000011709 else
11710 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11711 }
11712 }
11713 (void) WriteBlob(image,32,chunk);
11714 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11715 option=GetImageOption(image_info,"mng:need-cacheoff");
11716 if (option != (const char *) NULL)
11717 {
11718 size_t
11719 length;
11720
11721 /*
11722 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11723 */
11724 PNGType(chunk,mng_nEED);
11725 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011726 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011727 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011728 length+=4;
11729 (void) WriteBlob(image,length,chunk);
11730 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11731 }
11732 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11733 (GetNextImageInList(image) != (Image *) NULL) &&
11734 (image->iterations != 1))
11735 {
11736 /*
11737 Write MNG TERM chunk
11738 */
11739 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11740 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011741 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011742 chunk[4]=3; /* repeat animation */
11743 chunk[5]=0; /* show last frame when done */
11744 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11745 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011746
cristy3ed852e2009-09-05 21:47:34 +000011747 if (image->iterations == 0)
11748 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011749
cristy3ed852e2009-09-05 21:47:34 +000011750 else
11751 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011752
cristy3ed852e2009-09-05 21:47:34 +000011753 if (logging != MagickFalse)
11754 {
11755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011756 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11757 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011758
cristy3ed852e2009-09-05 21:47:34 +000011759 if (image->iterations == 0)
11760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011761 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011762
cristy3ed852e2009-09-05 21:47:34 +000011763 else
11764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011765 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011766 }
11767 (void) WriteBlob(image,14,chunk);
11768 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11769 }
11770 /*
11771 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11772 */
11773 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11774 mng_info->equal_srgbs)
11775 {
11776 /*
11777 Write MNG sRGB chunk
11778 */
11779 (void) WriteBlobMSBULong(image,1L);
11780 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011781 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011782
cristy3ed852e2009-09-05 21:47:34 +000011783 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011784 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011785 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011786 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011787
cristy3ed852e2009-09-05 21:47:34 +000011788 else
glennrpe610a072010-08-05 17:08:46 +000011789 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011790 Magick_RenderingIntent_to_PNG_RenderingIntent(
11791 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011792
cristy3ed852e2009-09-05 21:47:34 +000011793 (void) WriteBlob(image,5,chunk);
11794 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11795 mng_info->have_write_global_srgb=MagickTrue;
11796 }
glennrp0fe50b42010-11-16 03:52:51 +000011797
cristy3ed852e2009-09-05 21:47:34 +000011798 else
11799 {
11800 if (image->gamma && mng_info->equal_gammas)
11801 {
11802 /*
11803 Write MNG gAMA chunk
11804 */
11805 (void) WriteBlobMSBULong(image,4L);
11806 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011807 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011808 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011809 (void) WriteBlob(image,8,chunk);
11810 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11811 mng_info->have_write_global_gama=MagickTrue;
11812 }
11813 if (mng_info->equal_chrms)
11814 {
11815 PrimaryInfo
11816 primary;
11817
11818 /*
11819 Write MNG cHRM chunk
11820 */
11821 (void) WriteBlobMSBULong(image,32L);
11822 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011823 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011824 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011825 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11826 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011827 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011828 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11829 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011830 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011831 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11832 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011833 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011834 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11835 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011836 (void) WriteBlob(image,36,chunk);
11837 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11838 mng_info->have_write_global_chrm=MagickTrue;
11839 }
11840 }
11841 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11842 {
11843 /*
11844 Write MNG pHYs chunk
11845 */
11846 (void) WriteBlobMSBULong(image,9L);
11847 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011848 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011849
cristy3ed852e2009-09-05 21:47:34 +000011850 if (image->units == PixelsPerInchResolution)
11851 {
cristy35ef8242010-06-03 16:24:13 +000011852 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011853 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011854
cristy35ef8242010-06-03 16:24:13 +000011855 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011856 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011857
cristy3ed852e2009-09-05 21:47:34 +000011858 chunk[12]=1;
11859 }
glennrp0fe50b42010-11-16 03:52:51 +000011860
cristy3ed852e2009-09-05 21:47:34 +000011861 else
11862 {
11863 if (image->units == PixelsPerCentimeterResolution)
11864 {
cristy35ef8242010-06-03 16:24:13 +000011865 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011866 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011867
cristy35ef8242010-06-03 16:24:13 +000011868 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011869 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011870
cristy3ed852e2009-09-05 21:47:34 +000011871 chunk[12]=1;
11872 }
glennrp0fe50b42010-11-16 03:52:51 +000011873
cristy3ed852e2009-09-05 21:47:34 +000011874 else
11875 {
cristy35ef8242010-06-03 16:24:13 +000011876 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11877 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011878 chunk[12]=0;
11879 }
11880 }
11881 (void) WriteBlob(image,13,chunk);
11882 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11883 }
11884 /*
11885 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11886 or does not cover the entire frame.
11887 */
11888 if (write_mng && (image->matte || image->page.x > 0 ||
11889 image->page.y > 0 || (image->page.width &&
11890 (image->page.width+image->page.x < mng_info->page.width))
11891 || (image->page.height && (image->page.height+image->page.y
11892 < mng_info->page.height))))
11893 {
11894 (void) WriteBlobMSBULong(image,6L);
11895 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011896 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011897 red=ScaleQuantumToShort(image->background_color.red);
11898 green=ScaleQuantumToShort(image->background_color.green);
11899 blue=ScaleQuantumToShort(image->background_color.blue);
11900 PNGShort(chunk+4,red);
11901 PNGShort(chunk+6,green);
11902 PNGShort(chunk+8,blue);
11903 (void) WriteBlob(image,10,chunk);
11904 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11905 if (mng_info->equal_backgrounds)
11906 {
11907 (void) WriteBlobMSBULong(image,6L);
11908 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011909 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011910 (void) WriteBlob(image,10,chunk);
11911 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11912 }
11913 }
11914
11915#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11916 if ((need_local_plte == MagickFalse) &&
11917 (image->storage_class == PseudoClass) &&
11918 (all_images_are_gray == MagickFalse))
11919 {
cristybb503372010-05-27 20:51:26 +000011920 size_t
cristy3ed852e2009-09-05 21:47:34 +000011921 data_length;
11922
11923 /*
11924 Write MNG PLTE chunk
11925 */
11926 data_length=3*image->colors;
11927 (void) WriteBlobMSBULong(image,data_length);
11928 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011929 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011930
cristybb503372010-05-27 20:51:26 +000011931 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011932 {
11933 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11934 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11935 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11936 }
glennrp0fe50b42010-11-16 03:52:51 +000011937
cristy3ed852e2009-09-05 21:47:34 +000011938 (void) WriteBlob(image,data_length+4,chunk);
11939 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11940 mng_info->have_write_global_plte=MagickTrue;
11941 }
11942#endif
11943 }
11944 scene=0;
11945 mng_info->delay=0;
11946#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11947 defined(PNG_MNG_FEATURES_SUPPORTED)
11948 mng_info->equal_palettes=MagickFalse;
11949#endif
11950 do
11951 {
11952 if (mng_info->adjoin)
11953 {
11954#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11955 defined(PNG_MNG_FEATURES_SUPPORTED)
11956 /*
11957 If we aren't using a global palette for the entire MNG, check to
11958 see if we can use one for two or more consecutive images.
11959 */
11960 if (need_local_plte && use_global_plte && !all_images_are_gray)
11961 {
11962 if (mng_info->IsPalette)
11963 {
11964 /*
11965 When equal_palettes is true, this image has the same palette
11966 as the previous PseudoClass image
11967 */
11968 mng_info->have_write_global_plte=mng_info->equal_palettes;
11969 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11970 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11971 {
11972 /*
11973 Write MNG PLTE chunk
11974 */
cristybb503372010-05-27 20:51:26 +000011975 size_t
cristy3ed852e2009-09-05 21:47:34 +000011976 data_length;
11977
11978 data_length=3*image->colors;
11979 (void) WriteBlobMSBULong(image,data_length);
11980 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011981 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011982
cristybb503372010-05-27 20:51:26 +000011983 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011984 {
11985 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11986 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11987 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11988 }
glennrp0fe50b42010-11-16 03:52:51 +000011989
cristy3ed852e2009-09-05 21:47:34 +000011990 (void) WriteBlob(image,data_length+4,chunk);
11991 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11992 (uInt) (data_length+4)));
11993 mng_info->have_write_global_plte=MagickTrue;
11994 }
11995 }
11996 else
11997 mng_info->have_write_global_plte=MagickFalse;
11998 }
11999#endif
12000 if (need_defi)
12001 {
cristybb503372010-05-27 20:51:26 +000012002 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012003 previous_x,
12004 previous_y;
12005
12006 if (scene)
12007 {
12008 previous_x=mng_info->page.x;
12009 previous_y=mng_info->page.y;
12010 }
12011 else
12012 {
12013 previous_x=0;
12014 previous_y=0;
12015 }
12016 mng_info->page=image->page;
12017 if ((mng_info->page.x != previous_x) ||
12018 (mng_info->page.y != previous_y))
12019 {
12020 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12021 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012022 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012023 chunk[4]=0; /* object 0 MSB */
12024 chunk[5]=0; /* object 0 LSB */
12025 chunk[6]=0; /* visible */
12026 chunk[7]=0; /* abstract */
12027 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12028 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12029 (void) WriteBlob(image,16,chunk);
12030 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12031 }
12032 }
12033 }
12034
12035 mng_info->write_mng=write_mng;
12036
12037 if ((int) image->dispose >= 3)
12038 mng_info->framing_mode=3;
12039
12040 if (mng_info->need_fram && mng_info->adjoin &&
12041 ((image->delay != mng_info->delay) ||
12042 (mng_info->framing_mode != mng_info->old_framing_mode)))
12043 {
12044 if (image->delay == mng_info->delay)
12045 {
12046 /*
12047 Write a MNG FRAM chunk with the new framing mode.
12048 */
12049 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12050 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012051 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012052 chunk[4]=(unsigned char) mng_info->framing_mode;
12053 (void) WriteBlob(image,5,chunk);
12054 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12055 }
12056 else
12057 {
12058 /*
12059 Write a MNG FRAM chunk with the delay.
12060 */
12061 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12062 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012063 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012064 chunk[4]=(unsigned char) mng_info->framing_mode;
12065 chunk[5]=0; /* frame name separator (no name) */
12066 chunk[6]=2; /* flag for changing default delay */
12067 chunk[7]=0; /* flag for changing frame timeout */
12068 chunk[8]=0; /* flag for changing frame clipping */
12069 chunk[9]=0; /* flag for changing frame sync_id */
12070 PNGLong(chunk+10,(png_uint_32)
12071 ((mng_info->ticks_per_second*
12072 image->delay)/MagickMax(image->ticks_per_second,1)));
12073 (void) WriteBlob(image,14,chunk);
12074 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012075 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012076 }
12077 mng_info->old_framing_mode=mng_info->framing_mode;
12078 }
12079
12080#if defined(JNG_SUPPORTED)
12081 if (image_info->compression == JPEGCompression)
12082 {
12083 ImageInfo
12084 *write_info;
12085
12086 if (logging != MagickFalse)
12087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12088 " Writing JNG object.");
12089 /* To do: specify the desired alpha compression method. */
12090 write_info=CloneImageInfo(image_info);
12091 write_info->compression=UndefinedCompression;
12092 status=WriteOneJNGImage(mng_info,write_info,image);
12093 write_info=DestroyImageInfo(write_info);
12094 }
12095 else
12096#endif
12097 {
12098 if (logging != MagickFalse)
12099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012101
glennrpb9cfe272010-12-21 15:08:06 +000012102 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012103 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012104
12105 /* We don't want any ancillary chunks written */
12106 mng_info->ping_exclude_bKGD=MagickTrue;
12107 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012108 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012109 mng_info->ping_exclude_EXIF=MagickTrue;
12110 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012111 mng_info->ping_exclude_iCCP=MagickTrue;
12112 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12113 mng_info->ping_exclude_oFFs=MagickTrue;
12114 mng_info->ping_exclude_pHYs=MagickTrue;
12115 mng_info->ping_exclude_sRGB=MagickTrue;
12116 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012117 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012118 mng_info->ping_exclude_vpAg=MagickTrue;
12119 mng_info->ping_exclude_zCCP=MagickTrue;
12120 mng_info->ping_exclude_zTXt=MagickTrue;
12121
cristy3ed852e2009-09-05 21:47:34 +000012122 status=WriteOnePNGImage(mng_info,image_info,image);
12123 }
12124
12125 if (status == MagickFalse)
12126 {
12127 MngInfoFreeStruct(mng_info,&have_mng_structure);
12128 (void) CloseBlob(image);
12129 return(MagickFalse);
12130 }
12131 (void) CatchImageException(image);
12132 if (GetNextImageInList(image) == (Image *) NULL)
12133 break;
12134 image=SyncNextImageInList(image);
12135 status=SetImageProgress(image,SaveImagesTag,scene++,
12136 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012137
cristy3ed852e2009-09-05 21:47:34 +000012138 if (status == MagickFalse)
12139 break;
glennrp0fe50b42010-11-16 03:52:51 +000012140
cristy3ed852e2009-09-05 21:47:34 +000012141 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012142
cristy3ed852e2009-09-05 21:47:34 +000012143 if (write_mng)
12144 {
12145 while (GetPreviousImageInList(image) != (Image *) NULL)
12146 image=GetPreviousImageInList(image);
12147 /*
12148 Write the MEND chunk.
12149 */
12150 (void) WriteBlobMSBULong(image,0x00000000L);
12151 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012152 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012153 (void) WriteBlob(image,4,chunk);
12154 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12155 }
12156 /*
12157 Relinquish resources.
12158 */
12159 (void) CloseBlob(image);
12160 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012161
cristy3ed852e2009-09-05 21:47:34 +000012162 if (logging != MagickFalse)
12163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012164
cristy3ed852e2009-09-05 21:47:34 +000012165 return(MagickTrue);
12166}
glennrpd5045b42010-03-24 12:40:35 +000012167#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012168
cristy3ed852e2009-09-05 21:47:34 +000012169static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12170{
12171 image=image;
12172 printf("Your PNG library is too old: You have libpng-%s\n",
12173 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012174
cristy3ed852e2009-09-05 21:47:34 +000012175 ThrowBinaryException(CoderError,"PNG library is too old",
12176 image_info->filename);
12177}
glennrp39992b42010-11-14 00:03:43 +000012178
cristy3ed852e2009-09-05 21:47:34 +000012179static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12180{
12181 return(WritePNGImage(image_info,image));
12182}
glennrpd5045b42010-03-24 12:40:35 +000012183#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012184#endif