blob: 29ce03d4062b0349f3363b3414d8794efa80692c [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
cristy3ed852e2009-09-05 21:47:34 +00002035 png_set_bKGD(ping,ping_info,&background);
2036 }
2037#endif
2038 }
2039 else
2040 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2041 CoderError,"No global PLTE in file","`%s'",
2042 image_info->filename);
2043 }
2044 }
2045
glennrpbfd9e612011-04-22 14:02:20 +00002046#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002047 if (mng_info->have_global_bkgd &&
2048 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002049 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002050
glennrpfaa852b2010-03-30 12:17:00 +00002051 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002052 {
glennrpbfd9e612011-04-22 14:02:20 +00002053 unsigned int
2054 bkgd_scale;
2055
cristy3ed852e2009-09-05 21:47:34 +00002056 /*
2057 Set image background color.
2058 */
2059 if (logging != MagickFalse)
2060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2061 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002062
glennrpbfd9e612011-04-22 14:02:20 +00002063 /* Scale background components to 16-bit, then scale
2064 * to quantum depth
2065 */
2066 if (logging != MagickFalse)
2067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2068 " raw ping_background=(%d,%d,%d).",ping_background->red,
2069 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002070
glennrpbfd9e612011-04-22 14:02:20 +00002071 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002072
glennrpbfd9e612011-04-22 14:02:20 +00002073 if (ping_bit_depth == 1)
2074 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002075
glennrpbfd9e612011-04-22 14:02:20 +00002076 else if (ping_bit_depth == 2)
2077 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002078
glennrpbfd9e612011-04-22 14:02:20 +00002079 else if (ping_bit_depth == 4)
2080 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002081
glennrpbfd9e612011-04-22 14:02:20 +00002082 if (ping_bit_depth <= 8)
2083 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002084
glennrpbfd9e612011-04-22 14:02:20 +00002085 ping_background->red *= bkgd_scale;
2086 ping_background->green *= bkgd_scale;
2087 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002088
glennrpbfd9e612011-04-22 14:02:20 +00002089 if (logging != MagickFalse)
2090 {
glennrp2cbb4482010-06-02 04:37:24 +00002091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2092 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002093
glennrp2cbb4482010-06-02 04:37:24 +00002094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2095 " ping_background=(%d,%d,%d).",ping_background->red,
2096 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002097 }
glennrp2cbb4482010-06-02 04:37:24 +00002098
glennrpbfd9e612011-04-22 14:02:20 +00002099 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002100 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002101
glennrpbfd9e612011-04-22 14:02:20 +00002102 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002103 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002104
glennrpbfd9e612011-04-22 14:02:20 +00002105 image->background_color.blue=
2106 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002107
glennrpbfd9e612011-04-22 14:02:20 +00002108 image->background_color.opacity=OpaqueOpacity;
glennrp2cbb4482010-06-02 04:37:24 +00002109
glennrpbfd9e612011-04-22 14:02:20 +00002110 if (logging != MagickFalse)
2111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2112 " image->background_color=(%.20g,%.20g,%.20g).",
2113 (double) image->background_color.red,
2114 (double) image->background_color.green,
2115 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002116 }
glennrpbfd9e612011-04-22 14:02:20 +00002117#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002118
glennrpfaa852b2010-03-30 12:17:00 +00002119 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002120 {
2121 /*
glennrpa6a06632011-01-19 15:15:34 +00002122 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002123 */
2124 int
2125 max_sample;
2126
cristy35ef8242010-06-03 16:24:13 +00002127 size_t
2128 one=1;
2129
cristy3ed852e2009-09-05 21:47:34 +00002130 if (logging != MagickFalse)
2131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2132 " Reading PNG tRNS chunk.");
2133
cristyf9cca6a2010-06-04 23:49:28 +00002134 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002135
glennrpfaa852b2010-03-30 12:17:00 +00002136 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2137 (int)ping_trans_color->gray > max_sample) ||
2138 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2139 ((int)ping_trans_color->red > max_sample ||
2140 (int)ping_trans_color->green > max_sample ||
2141 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002142 {
2143 if (logging != MagickFalse)
2144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2145 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002146 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002147 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002148 image->matte=MagickFalse;
2149 }
2150 else
2151 {
glennrpa6a06632011-01-19 15:15:34 +00002152 int
2153 scale_to_short;
2154
2155 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2156
2157 /* Scale transparent_color to short */
2158 transparent_color.red= scale_to_short*ping_trans_color->red;
2159 transparent_color.green= scale_to_short*ping_trans_color->green;
2160 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2161 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002162
glennrpfaa852b2010-03-30 12:17:00 +00002163 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002164 {
glennrp0f111982010-07-07 20:18:33 +00002165 if (logging != MagickFalse)
2166 {
2167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2168 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002169
glennrp0f111982010-07-07 20:18:33 +00002170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2171 " scaled graylevel is %d.",transparent_color.opacity);
2172 }
cristy3ed852e2009-09-05 21:47:34 +00002173 transparent_color.red=transparent_color.opacity;
2174 transparent_color.green=transparent_color.opacity;
2175 transparent_color.blue=transparent_color.opacity;
2176 }
2177 }
2178 }
2179#if defined(PNG_READ_sBIT_SUPPORTED)
2180 if (mng_info->have_global_sbit)
2181 {
glennrpfaa852b2010-03-30 12:17:00 +00002182 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002183 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2184 }
2185#endif
2186 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002187
cristy3ed852e2009-09-05 21:47:34 +00002188 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002189
2190 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2191
cristy3ed852e2009-09-05 21:47:34 +00002192 /*
2193 Initialize image structure.
2194 */
2195 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002196 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002197 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002198 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002199 if (mng_info->mng_type == 0)
2200 {
glennrpfaa852b2010-03-30 12:17:00 +00002201 mng_info->mng_width=ping_width;
2202 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002203 mng_info->frame=mng_info->image_box;
2204 mng_info->clip=mng_info->image_box;
2205 }
glennrp0fe50b42010-11-16 03:52:51 +00002206
cristy3ed852e2009-09-05 21:47:34 +00002207 else
2208 {
2209 image->page.y=mng_info->y_off[mng_info->object_id];
2210 }
glennrp0fe50b42010-11-16 03:52:51 +00002211
cristy3ed852e2009-09-05 21:47:34 +00002212 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002213 image->columns=ping_width;
2214 image->rows=ping_height;
2215 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002216 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002217 {
cristybefe4d22010-06-07 01:18:58 +00002218 size_t
2219 one;
2220
cristy3ed852e2009-09-05 21:47:34 +00002221 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002222 one=1;
2223 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002224#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2225 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002226 image->colors=256;
2227#else
2228 if (image->colors > 65536L)
2229 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002230#endif
glennrpfaa852b2010-03-30 12:17:00 +00002231 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002232 {
2233 int
2234 number_colors;
2235
2236 png_colorp
2237 palette;
2238
2239 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002240 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002241
cristy3ed852e2009-09-05 21:47:34 +00002242 if (logging != MagickFalse)
2243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2244 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2245 }
2246 }
2247
2248 if (image->storage_class == PseudoClass)
2249 {
2250 /*
2251 Initialize image colormap.
2252 */
2253 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2254 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002255
glennrpfaa852b2010-03-30 12:17:00 +00002256 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002257 {
2258 int
2259 number_colors;
2260
2261 png_colorp
2262 palette;
2263
2264 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002265
glennrp6af6cf12011-04-22 13:05:16 +00002266 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002267 {
2268 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2269 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2270 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2271 }
glennrp6af6cf12011-04-22 13:05:16 +00002272
glennrp67b9c1a2011-04-22 18:47:36 +00002273 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002274 {
2275 image->colormap[i].red=0;
2276 image->colormap[i].green=0;
2277 image->colormap[i].blue=0;
2278 }
cristy3ed852e2009-09-05 21:47:34 +00002279 }
glennrp0fe50b42010-11-16 03:52:51 +00002280
cristy3ed852e2009-09-05 21:47:34 +00002281 else
2282 {
cristybb503372010-05-27 20:51:26 +00002283 size_t
cristy3ed852e2009-09-05 21:47:34 +00002284 scale;
2285
glennrpfaa852b2010-03-30 12:17:00 +00002286 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002287
cristy3ed852e2009-09-05 21:47:34 +00002288 if (scale < 1)
2289 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristybb503372010-05-27 20:51:26 +00002291 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002292 {
2293 image->colormap[i].red=(Quantum) (i*scale);
2294 image->colormap[i].green=(Quantum) (i*scale);
2295 image->colormap[i].blue=(Quantum) (i*scale);
2296 }
2297 }
2298 }
glennrp147bc912011-03-30 18:47:21 +00002299
glennrpcb395ac2011-03-30 19:50:23 +00002300 /* Set some properties for reporting by "identify" */
2301 {
glennrp147bc912011-03-30 18:47:21 +00002302 char
2303 msg[MaxTextExtent];
2304
2305 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2306 ping_interlace_method in value */
2307
glennrp7cdb11c2011-03-31 18:17:25 +00002308 (void) FormatMagickString(msg,MaxTextExtent,
2309 "%d, %d",(int) ping_width, (int) ping_height);
2310 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002311
2312 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2313 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2314
2315 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2316 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2317
2318 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2319 (int) ping_interlace_method);
2320 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002321 }
glennrp147bc912011-03-30 18:47:21 +00002322
cristy3ed852e2009-09-05 21:47:34 +00002323 /*
2324 Read image scanlines.
2325 */
2326 if (image->delay != 0)
2327 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002328
glennrp0ca69b12010-07-26 01:57:52 +00002329 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002330 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2331 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002332 {
2333 if (logging != MagickFalse)
2334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002335 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002336 mng_info->scenes_found-1);
2337 png_destroy_read_struct(&ping,&ping_info,&end_info);
2338#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002339 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002340#endif
2341 if (logging != MagickFalse)
2342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2343 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002344
cristy3ed852e2009-09-05 21:47:34 +00002345 return(image);
2346 }
glennrp0fe50b42010-11-16 03:52:51 +00002347
cristy3ed852e2009-09-05 21:47:34 +00002348 if (logging != MagickFalse)
2349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2350 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002353 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2354 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002355
cristy3ed852e2009-09-05 21:47:34 +00002356 else
glennrpcf002022011-01-30 02:38:15 +00002357 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2358 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002359
glennrpcf002022011-01-30 02:38:15 +00002360 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002361 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002362
cristy3ed852e2009-09-05 21:47:34 +00002363 if (logging != MagickFalse)
2364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2365 " Converting PNG pixels to pixel packets");
2366 /*
2367 Convert PNG pixels to pixel packets.
2368 */
glennrpfaa852b2010-03-30 12:17:00 +00002369 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002370 {
2371 /*
2372 PNG image is corrupt.
2373 */
2374 png_destroy_read_struct(&ping,&ping_info,&end_info);
2375#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002376 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002377#endif
2378 if (quantum_info != (QuantumInfo *) NULL)
2379 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002380
glennrpcf002022011-01-30 02:38:15 +00002381 if (ping_pixels != (unsigned char *) NULL)
2382 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002383
cristy3ed852e2009-09-05 21:47:34 +00002384 if (logging != MagickFalse)
2385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2386 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002387
cristy3ed852e2009-09-05 21:47:34 +00002388 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002389 {
2390 InheritException(exception,&image->exception);
2391 image->columns=0;
2392 }
glennrp0fe50b42010-11-16 03:52:51 +00002393
cristy3ed852e2009-09-05 21:47:34 +00002394 return(GetFirstImageInList(image));
2395 }
glennrp0fe50b42010-11-16 03:52:51 +00002396
cristyed552522009-10-16 14:04:35 +00002397 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002398
cristyed552522009-10-16 14:04:35 +00002399 if (quantum_info == (QuantumInfo *) NULL)
2400 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002401
glennrpc8cbc5d2011-01-01 00:12:34 +00002402 {
2403
2404 MagickBooleanType
2405 found_transparent_pixel;
2406
2407 found_transparent_pixel=MagickFalse;
2408
cristy3ed852e2009-09-05 21:47:34 +00002409 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002410 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002411 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002412 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002413 /*
2414 Convert image to DirectClass pixel packets.
2415 */
glennrp67b9c1a2011-04-22 18:47:36 +00002416#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2417 int
2418 depth;
2419
2420 depth=(ssize_t) ping_bit_depth;
2421#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002422 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2423 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2424 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2425 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002426
glennrpc8cbc5d2011-01-01 00:12:34 +00002427 for (y=0; y < (ssize_t) image->rows; y++)
2428 {
2429 if (num_passes > 1)
2430 row_offset=ping_rowbytes*y;
2431
2432 else
2433 row_offset=0;
2434
glennrpcf002022011-01-30 02:38:15 +00002435 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002436 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2437
2438 if (q == (PixelPacket *) NULL)
2439 break;
2440
glennrpc8cbc5d2011-01-01 00:12:34 +00002441 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2442 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002443 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002444
2445 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2446 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002447 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002448
2449 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2450 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002451 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002452
2453 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2454 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002455 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002456
2457 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2458 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002459 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002460
glennrpc8cbc5d2011-01-01 00:12:34 +00002461 if (found_transparent_pixel == MagickFalse)
2462 {
2463 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002464 if (y== 0 && logging != MagickFalse)
2465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2466 " Looking for cheap transparent pixel");
2467
glennrpc8cbc5d2011-01-01 00:12:34 +00002468 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2469 {
glennrp5aa37f62011-01-02 03:07:57 +00002470 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2471 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
glennrp8b698592011-04-26 03:38:21 +00002472 (GetOpacityPixelComponent(q) != OpaqueOpacity))
glennrpc8cbc5d2011-01-01 00:12:34 +00002473 {
glennrpa6a06632011-01-19 15:15:34 +00002474 if (logging != MagickFalse)
2475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2476 " ...got one.");
2477
glennrpc8cbc5d2011-01-01 00:12:34 +00002478 found_transparent_pixel = MagickTrue;
2479 break;
2480 }
glennrp4f25bd02011-01-01 18:51:28 +00002481 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2482 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp8b698592011-04-26 03:38:21 +00002483 (ScaleQuantumToShort(GetRedPixelComponent(q))
2484 == transparent_color.red &&
2485 ScaleQuantumToShort(GetGreenPixelComponent(q))
2486 == transparent_color.green &&
2487 ScaleQuantumToShort(GetBluePixelComponent(q))
2488 == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002489 {
glennrpa6a06632011-01-19 15:15:34 +00002490 if (logging != MagickFalse)
2491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2492 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002493 found_transparent_pixel = MagickTrue;
2494 break;
2495 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002496 q++;
2497 }
2498 }
2499
2500 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2501 {
2502 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2503 image->rows);
2504
2505 if (status == MagickFalse)
2506 break;
2507 }
2508 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2509 break;
2510 }
2511
2512 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2513 {
2514 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002515 if (status == MagickFalse)
2516 break;
2517 }
cristy3ed852e2009-09-05 21:47:34 +00002518 }
cristy3ed852e2009-09-05 21:47:34 +00002519 }
glennrp0fe50b42010-11-16 03:52:51 +00002520
cristy3ed852e2009-09-05 21:47:34 +00002521 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002522
cristy3ed852e2009-09-05 21:47:34 +00002523 for (pass=0; pass < num_passes; pass++)
2524 {
2525 Quantum
2526 *quantum_scanline;
2527
2528 register Quantum
2529 *r;
2530
2531 /*
2532 Convert grayscale image to PseudoClass pixel packets.
2533 */
glennrpfaa852b2010-03-30 12:17:00 +00002534 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002535 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002536
cristy3ed852e2009-09-05 21:47:34 +00002537 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2538 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002539
cristy3ed852e2009-09-05 21:47:34 +00002540 if (quantum_scanline == (Quantum *) NULL)
2541 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002542
cristybb503372010-05-27 20:51:26 +00002543 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002544 {
2545 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002546 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002547
cristy3ed852e2009-09-05 21:47:34 +00002548 else
2549 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00002550
glennrpcf002022011-01-30 02:38:15 +00002551 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002552 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00002553
cristy3ed852e2009-09-05 21:47:34 +00002554 if (q == (PixelPacket *) NULL)
2555 break;
glennrp0fe50b42010-11-16 03:52:51 +00002556
cristy5c6f7892010-05-05 22:53:29 +00002557 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00002558 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00002559 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00002560
glennrpfaa852b2010-03-30 12:17:00 +00002561 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00002562 {
2563 case 1:
2564 {
cristybb503372010-05-27 20:51:26 +00002565 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002566 bit;
2567
cristybb503372010-05-27 20:51:26 +00002568 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00002569 {
2570 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002571 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002572 p++;
2573 }
glennrp0fe50b42010-11-16 03:52:51 +00002574
cristy3ed852e2009-09-05 21:47:34 +00002575 if ((image->columns % 8) != 0)
2576 {
cristybb503372010-05-27 20:51:26 +00002577 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002578 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002579 }
glennrp0fe50b42010-11-16 03:52:51 +00002580
cristy3ed852e2009-09-05 21:47:34 +00002581 break;
2582 }
glennrp47b9dd52010-11-24 18:12:06 +00002583
cristy3ed852e2009-09-05 21:47:34 +00002584 case 2:
2585 {
cristybb503372010-05-27 20:51:26 +00002586 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002587 {
glennrpa18d5bc2011-04-23 14:51:34 +00002588 *r++=(*p >> 6) & 0x03;
2589 *r++=(*p >> 4) & 0x03;
2590 *r++=(*p >> 2) & 0x03;
2591 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00002592 }
glennrp0fe50b42010-11-16 03:52:51 +00002593
cristy3ed852e2009-09-05 21:47:34 +00002594 if ((image->columns % 4) != 0)
2595 {
cristybb503372010-05-27 20:51:26 +00002596 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00002597 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00002598 }
glennrp0fe50b42010-11-16 03:52:51 +00002599
cristy3ed852e2009-09-05 21:47:34 +00002600 break;
2601 }
glennrp47b9dd52010-11-24 18:12:06 +00002602
cristy3ed852e2009-09-05 21:47:34 +00002603 case 4:
2604 {
cristybb503372010-05-27 20:51:26 +00002605 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00002606 {
glennrpa18d5bc2011-04-23 14:51:34 +00002607 *r++=(*p >> 4) & 0x0f;
2608 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00002609 }
glennrp0fe50b42010-11-16 03:52:51 +00002610
cristy3ed852e2009-09-05 21:47:34 +00002611 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00002612 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00002613
cristy3ed852e2009-09-05 21:47:34 +00002614 break;
2615 }
glennrp47b9dd52010-11-24 18:12:06 +00002616
cristy3ed852e2009-09-05 21:47:34 +00002617 case 8:
2618 {
glennrpfaa852b2010-03-30 12:17:00 +00002619 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00002620 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002621 {
glennrpa18d5bc2011-04-23 14:51:34 +00002622 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00002623 /* In image.h, OpaqueOpacity is 0
2624 * TransparentOpacity is QuantumRange
2625 * In a PNG datastream, Opaque is QuantumRange
2626 * and Transparent is 0.
2627 */
glennrp8b698592011-04-26 03:38:21 +00002628 SetOpacityPixelComponent(q,
2629 ScaleCharToQuantum((unsigned char) (255-(*p++))));
2630 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00002631 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002632 q++;
2633 }
glennrp0fe50b42010-11-16 03:52:51 +00002634
cristy3ed852e2009-09-05 21:47:34 +00002635 else
cristybb503372010-05-27 20:51:26 +00002636 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002637 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00002638
cristy3ed852e2009-09-05 21:47:34 +00002639 break;
2640 }
glennrp47b9dd52010-11-24 18:12:06 +00002641
cristy3ed852e2009-09-05 21:47:34 +00002642 case 16:
2643 {
cristybb503372010-05-27 20:51:26 +00002644 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002645 {
glennrp58f77c72011-04-23 14:09:09 +00002646#if (MAGICKCORE_QUANTUM_DEPTH == 16)
2647 size_t
2648 quantum;
2649
2650 if (image->colors > 256)
2651 *r=((*p++) << 8);
2652
2653 else
2654 *r=0;
2655
2656 quantum=(*r);
2657 quantum|=(*p++);
2658 *r=(Quantum) quantum;
2659 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00002660
2661 if (ping_color_type == 4)
2662 {
glennrp58f77c72011-04-23 14:09:09 +00002663 quantum=((*p++) << 8);
2664 quantum|=(*p++);
glennrp8b698592011-04-26 03:38:21 +00002665 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-quantum));
2666 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002667 found_transparent_pixel = MagickTrue;
2668 q++;
2669 }
2670#else
2671#if (MAGICKCORE_QUANTUM_DEPTH == 32)
2672 size_t
2673 quantum;
2674
2675 if (image->colors > 256)
2676 *r=((*p++) << 8);
2677
2678 else
2679 *r=0;
2680
2681 quantum=(*r);
2682 quantum|=(*p++);
2683 *r=quantum;
2684 r++;
2685
2686 if (ping_color_type == 4)
2687 {
glennrp8b698592011-04-26 03:38:21 +00002688 quantum=(*p << 8) | *(p+1);
2689 quantum*=65537L;
2690 SetOpacityPixelComponent(q,
2691 (Quantum) GetAlphaPixelComponent(q));
2692 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002693 found_transparent_pixel = MagickTrue;
2694 p+=2;
2695 q++;
2696 }
2697
2698#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2699 *r++=(*p++);
2700 p++; /* strip low byte */
2701
2702 if (ping_color_type == 4)
2703 {
glennrp8b698592011-04-26 03:38:21 +00002704 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-(*p++)));
2705 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp9d0ea4d2011-04-22 18:35:57 +00002706 found_transparent_pixel = MagickTrue;
2707 p++;
2708 q++;
2709 }
cristy3ed852e2009-09-05 21:47:34 +00002710#endif
glennrp58f77c72011-04-23 14:09:09 +00002711#endif
glennrpa18d5bc2011-04-23 14:51:34 +00002712 }
glennrp47b9dd52010-11-24 18:12:06 +00002713
cristy3ed852e2009-09-05 21:47:34 +00002714 break;
2715 }
glennrp47b9dd52010-11-24 18:12:06 +00002716
cristy3ed852e2009-09-05 21:47:34 +00002717 default:
2718 break;
2719 }
glennrp3faa9a32011-04-23 14:00:25 +00002720
cristy3ed852e2009-09-05 21:47:34 +00002721 /*
2722 Transfer image scanline.
2723 */
2724 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00002725
cristybb503372010-05-27 20:51:26 +00002726 for (x=0; x < (ssize_t) image->columns; x++)
cristy80ac8b92010-05-08 13:36:57 +00002727 indexes[x]=(IndexPacket) (*r++);
glennrp0fe50b42010-11-16 03:52:51 +00002728
cristy3ed852e2009-09-05 21:47:34 +00002729 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2730 break;
glennrp0fe50b42010-11-16 03:52:51 +00002731
cristy7a287bf2010-02-14 02:18:09 +00002732 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2733 {
cristycee97112010-05-28 00:44:52 +00002734 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2735 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00002736
cristy7a287bf2010-02-14 02:18:09 +00002737 if (status == MagickFalse)
2738 break;
2739 }
cristy3ed852e2009-09-05 21:47:34 +00002740 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002741
cristy7a287bf2010-02-14 02:18:09 +00002742 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002743 {
2744 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00002745
cristy3ed852e2009-09-05 21:47:34 +00002746 if (status == MagickFalse)
2747 break;
2748 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002749
cristy3ed852e2009-09-05 21:47:34 +00002750 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2751 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002752
2753 image->matte=found_transparent_pixel;
2754
2755 if (logging != MagickFalse)
2756 {
2757 if (found_transparent_pixel != MagickFalse)
2758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2759 " Found transparent pixel");
2760 else
glennrp5aa37f62011-01-02 03:07:57 +00002761 {
2762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2763 " No transparent pixel was found");
glennrpa6a06632011-01-19 15:15:34 +00002764
glennrp5aa37f62011-01-02 03:07:57 +00002765 ping_color_type&=0x03;
2766 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002767 }
2768 }
2769
cristyb32b90a2009-09-07 21:45:48 +00002770 if (quantum_info != (QuantumInfo *) NULL)
2771 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002772
cristy5c6f7892010-05-05 22:53:29 +00002773 if (image->storage_class == PseudoClass)
2774 {
cristyaeb2cbc2010-05-07 13:28:58 +00002775 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002776 matte;
2777
2778 matte=image->matte;
2779 image->matte=MagickFalse;
2780 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002781 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002782 }
glennrp47b9dd52010-11-24 18:12:06 +00002783
glennrp4eb39312011-03-30 21:34:55 +00002784 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00002785
2786 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002787 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002788 {
2789 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00002790 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002791 image->colors=2;
2792 (void) SetImageBackgroundColor(image);
2793#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002794 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002795#endif
2796 if (logging != MagickFalse)
2797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2798 " exit ReadOnePNGImage() early.");
2799 return(image);
2800 }
glennrp47b9dd52010-11-24 18:12:06 +00002801
glennrpfaa852b2010-03-30 12:17:00 +00002802 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002803 {
2804 ClassType
2805 storage_class;
2806
2807 /*
2808 Image has a transparent background.
2809 */
2810 storage_class=image->storage_class;
2811 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002812
glennrp3c218112010-11-27 15:31:26 +00002813/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00002814
glennrp0fe50b42010-11-16 03:52:51 +00002815 if (storage_class == PseudoClass)
2816 {
2817 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002818 {
glennrp0fe50b42010-11-16 03:52:51 +00002819 for (x=0; x < ping_num_trans; x++)
2820 {
2821 image->colormap[x].opacity =
2822 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2823 }
glennrpc11cf6a2010-03-20 16:46:19 +00002824 }
glennrp47b9dd52010-11-24 18:12:06 +00002825
glennrp0fe50b42010-11-16 03:52:51 +00002826 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2827 {
2828 for (x=0; x < (int) image->colors; x++)
2829 {
2830 if (ScaleQuantumToShort(image->colormap[x].red) ==
2831 transparent_color.opacity)
2832 {
2833 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2834 }
2835 }
2836 }
2837 (void) SyncImage(image);
2838 }
glennrp47b9dd52010-11-24 18:12:06 +00002839
glennrpa6a06632011-01-19 15:15:34 +00002840#if 1 /* Should have already been done above, but glennrp problem P10
2841 * needs this.
2842 */
glennrp0fe50b42010-11-16 03:52:51 +00002843 else
2844 {
2845 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002846 {
glennrp0fe50b42010-11-16 03:52:51 +00002847 image->storage_class=storage_class;
2848 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2849
2850 if (q == (PixelPacket *) NULL)
2851 break;
2852
2853 indexes=GetAuthenticIndexQueue(image);
2854
glennrpa6a06632011-01-19 15:15:34 +00002855 /* Caution: on a Q8 build, this does not distinguish between
2856 * 16-bit colors that differ only in the low byte
2857 */
glennrp0fe50b42010-11-16 03:52:51 +00002858 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2859 {
glennrp8b698592011-04-26 03:38:21 +00002860 if (ScaleQuantumToShort(GetRedPixelComponent(q))
2861 == transparent_color.red &&
2862 ScaleQuantumToShort(GetGreenPixelComponent(q))
2863 == transparent_color.green &&
2864 ScaleQuantumToShort(GetBluePixelComponent(q))
2865 == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00002866 {
glennrp8b698592011-04-26 03:38:21 +00002867 SetOpacityPixelComponent(q,TransparentOpacity);
glennrp4f25bd02011-01-01 18:51:28 +00002868 }
glennrp0fe50b42010-11-16 03:52:51 +00002869
glennrp67b9c1a2011-04-22 18:47:36 +00002870#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00002871 else
glennrp4f25bd02011-01-01 18:51:28 +00002872 {
2873 q->opacity=(Quantum) OpaqueOpacity;
2874 }
glennrpa6a06632011-01-19 15:15:34 +00002875#endif
glennrp0fe50b42010-11-16 03:52:51 +00002876
2877 q++;
2878 }
2879
2880 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2881 break;
glennrpc11cf6a2010-03-20 16:46:19 +00002882 }
glennrp0fe50b42010-11-16 03:52:51 +00002883 }
glennrpa6a06632011-01-19 15:15:34 +00002884#endif
glennrpc11cf6a2010-03-20 16:46:19 +00002885
cristy3ed852e2009-09-05 21:47:34 +00002886 image->storage_class=DirectClass;
2887 }
glennrp3c218112010-11-27 15:31:26 +00002888
cristyb40fc462010-08-08 00:49:49 +00002889 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2890 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2891 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00002892
cristyeb3b22a2011-03-31 20:16:11 +00002893 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00002894 {
2895 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00002896 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2897 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00002898 else
glennrpa0ed0092011-04-18 16:36:29 +00002899 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2900 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002901
glennrp4eb39312011-03-30 21:34:55 +00002902 if (status != MagickFalse)
2903 for (i=0; i < (ssize_t) num_text; i++)
2904 {
2905 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00002906
glennrp4eb39312011-03-30 21:34:55 +00002907 if (logging != MagickFalse)
2908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2909 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00002910
glennrp4eb39312011-03-30 21:34:55 +00002911 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00002912 {
glennrp4eb39312011-03-30 21:34:55 +00002913 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2914 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00002915 }
glennrp0fe50b42010-11-16 03:52:51 +00002916
glennrp4eb39312011-03-30 21:34:55 +00002917 else
2918 {
2919 char
2920 *value;
2921
2922 length=text[i].text_length;
2923 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2924 sizeof(*value));
2925 if (value == (char *) NULL)
2926 {
2927 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2928 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2929 image->filename);
2930 break;
2931 }
2932 *value='\0';
2933 (void) ConcatenateMagickString(value,text[i].text,length+2);
2934
2935 /* Don't save "density" or "units" property if we have a pHYs
2936 * chunk
2937 */
2938 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2939 (LocaleCompare(text[i].key,"density") != 0 &&
2940 LocaleCompare(text[i].key,"units") != 0))
2941 (void) SetImageProperty(image,text[i].key,value);
2942
2943 if (logging != MagickFalse)
2944 {
2945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2946 " length: %lu",(unsigned long) length);
2947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2948 " Keyword: %s",text[i].key);
2949 }
2950
2951 value=DestroyString(value);
2952 }
2953 }
2954 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00002955 }
glennrp3c218112010-11-27 15:31:26 +00002956
cristy3ed852e2009-09-05 21:47:34 +00002957#ifdef MNG_OBJECT_BUFFERS
2958 /*
2959 Store the object if necessary.
2960 */
2961 if (object_id && !mng_info->frozen[object_id])
2962 {
2963 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2964 {
2965 /*
2966 create a new object buffer.
2967 */
2968 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00002969 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00002970
cristy3ed852e2009-09-05 21:47:34 +00002971 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2972 {
2973 mng_info->ob[object_id]->image=(Image *) NULL;
2974 mng_info->ob[object_id]->reference_count=1;
2975 }
2976 }
glennrp47b9dd52010-11-24 18:12:06 +00002977
cristy3ed852e2009-09-05 21:47:34 +00002978 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2979 mng_info->ob[object_id]->frozen)
2980 {
2981 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2982 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2983 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2984 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00002985
cristy3ed852e2009-09-05 21:47:34 +00002986 if (mng_info->ob[object_id]->frozen)
2987 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2988 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2989 "`%s'",image->filename);
2990 }
glennrp0fe50b42010-11-16 03:52:51 +00002991
cristy3ed852e2009-09-05 21:47:34 +00002992 else
2993 {
cristy3ed852e2009-09-05 21:47:34 +00002994
2995 if (mng_info->ob[object_id]->image != (Image *) NULL)
2996 mng_info->ob[object_id]->image=DestroyImage
2997 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00002998
cristy3ed852e2009-09-05 21:47:34 +00002999 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3000 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003001
cristy3ed852e2009-09-05 21:47:34 +00003002 if (mng_info->ob[object_id]->image != (Image *) NULL)
3003 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003004
cristy3ed852e2009-09-05 21:47:34 +00003005 else
3006 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3007 ResourceLimitError,"Cloning image for object buffer failed",
3008 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003009
glennrpfaa852b2010-03-30 12:17:00 +00003010 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003011 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003012
glennrpfaa852b2010-03-30 12:17:00 +00003013 mng_info->ob[object_id]->width=ping_width;
3014 mng_info->ob[object_id]->height=ping_height;
3015 mng_info->ob[object_id]->color_type=ping_color_type;
3016 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3017 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3018 mng_info->ob[object_id]->compression_method=
3019 ping_compression_method;
3020 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003021
glennrpfaa852b2010-03-30 12:17:00 +00003022 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003023 {
3024 int
3025 number_colors;
3026
3027 png_colorp
3028 plte;
3029
3030 /*
3031 Copy the PLTE to the object buffer.
3032 */
3033 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3034 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003035
cristy3ed852e2009-09-05 21:47:34 +00003036 for (i=0; i < number_colors; i++)
3037 {
3038 mng_info->ob[object_id]->plte[i]=plte[i];
3039 }
3040 }
glennrp47b9dd52010-11-24 18:12:06 +00003041
cristy3ed852e2009-09-05 21:47:34 +00003042 else
3043 mng_info->ob[object_id]->plte_length=0;
3044 }
3045 }
3046#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003047
3048 /* Set image->matte to MagickTrue if the input colortype supports
3049 * alpha or if a valid tRNS chunk is present, no matter whether there
3050 * is actual transparency present.
3051 */
3052 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3053 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3054 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3055 MagickTrue : MagickFalse;
3056
glennrpcb395ac2011-03-30 19:50:23 +00003057 /* Set more properties for identify to retrieve */
3058 {
3059 char
3060 msg[MaxTextExtent];
3061
glennrp4eb39312011-03-30 21:34:55 +00003062 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003063 {
3064 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3065 (void) FormatMagickString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003066 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003067 (void) SetImageProperty(image,"PNG:text ",msg);
3068 }
3069
3070 if (num_raw_profiles != 0)
3071 {
3072 (void) FormatMagickString(msg,MaxTextExtent,
3073 "%d were found", num_raw_profiles);
3074 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3075 }
3076
glennrpcb395ac2011-03-30 19:50:23 +00003077 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003078 {
3079 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3080 "chunk was found (see Chromaticity, above)");
3081 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3082 }
glennrpcb395ac2011-03-30 19:50:23 +00003083
3084 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003085 {
3086 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3087 "chunk was found (see Background color, above)");
3088 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3089 }
3090
3091 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3092 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003093
3094 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3095 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3096
glennrpcb395ac2011-03-30 19:50:23 +00003097 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3098 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003099
3100#if defined(PNG_sRGB_SUPPORTED)
3101 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3102 {
glennrp07523c72011-03-31 18:12:10 +00003103 (void) FormatMagickString(msg,MaxTextExtent,
3104 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003105 (int) intent);
3106 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3107 }
3108#endif
3109
3110 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3111 {
glennrp07523c72011-03-31 18:12:10 +00003112 (void) FormatMagickString(msg,MaxTextExtent,
3113 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003114 file_gamma);
3115 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3116 }
3117
3118#if defined(PNG_pHYs_SUPPORTED)
3119 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3120 {
glennrp07523c72011-03-31 18:12:10 +00003121 (void) FormatMagickString(msg,MaxTextExtent,
3122 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003123 (double) x_resolution,(double) y_resolution, unit_type);
3124 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3125 }
3126#endif
3127
3128#if defined(PNG_oFFs_SUPPORTED)
3129 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3130 {
3131 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3132 (double) image->page.x,(double) image->page.y);
3133 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3134 }
3135#endif
3136
glennrp07523c72011-03-31 18:12:10 +00003137 if ((image->page.width != 0 && image->page.width != image->columns) ||
3138 (image->page.height != 0 && image->page.height != image->rows))
3139 {
3140 (void) FormatMagickString(msg,MaxTextExtent,
3141 "width=%.20g, height=%.20g",
3142 (double) image->page.width,(double) image->page.height);
3143 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3144 }
glennrpcb395ac2011-03-30 19:50:23 +00003145 }
3146
cristy3ed852e2009-09-05 21:47:34 +00003147 /*
3148 Relinquish resources.
3149 */
3150 png_destroy_read_struct(&ping,&ping_info,&end_info);
3151
glennrpcf002022011-01-30 02:38:15 +00003152 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003153#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003154 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003155#endif
3156
3157 if (logging != MagickFalse)
3158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3159 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003160
cristy3ed852e2009-09-05 21:47:34 +00003161 return(image);
3162
3163/* end of reading one PNG image */
3164}
3165
3166static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3167{
3168 Image
3169 *image,
3170 *previous;
3171
3172 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003173 have_mng_structure,
3174 logging,
cristy3ed852e2009-09-05 21:47:34 +00003175 status;
3176
3177 MngInfo
3178 *mng_info;
3179
3180 char
3181 magic_number[MaxTextExtent];
3182
cristy3ed852e2009-09-05 21:47:34 +00003183 ssize_t
3184 count;
3185
3186 /*
3187 Open image file.
3188 */
3189 assert(image_info != (const ImageInfo *) NULL);
3190 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003191
cristy3ed852e2009-09-05 21:47:34 +00003192 if (image_info->debug != MagickFalse)
3193 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3194 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003195
cristy3ed852e2009-09-05 21:47:34 +00003196 assert(exception != (ExceptionInfo *) NULL);
3197 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003198 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003199 image=AcquireImage(image_info);
3200 mng_info=(MngInfo *) NULL;
3201 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003202
cristy3ed852e2009-09-05 21:47:34 +00003203 if (status == MagickFalse)
3204 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003205
cristy3ed852e2009-09-05 21:47:34 +00003206 /*
3207 Verify PNG signature.
3208 */
3209 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003210
glennrpdde35db2011-02-21 12:06:32 +00003211 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003212 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003213
cristy3ed852e2009-09-05 21:47:34 +00003214 /*
3215 Allocate a MngInfo structure.
3216 */
3217 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003218 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003219
cristy3ed852e2009-09-05 21:47:34 +00003220 if (mng_info == (MngInfo *) NULL)
3221 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003222
cristy3ed852e2009-09-05 21:47:34 +00003223 /*
3224 Initialize members of the MngInfo structure.
3225 */
3226 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3227 mng_info->image=image;
3228 have_mng_structure=MagickTrue;
3229
3230 previous=image;
3231 image=ReadOnePNGImage(mng_info,image_info,exception);
3232 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003233
cristy3ed852e2009-09-05 21:47:34 +00003234 if (image == (Image *) NULL)
3235 {
3236 if (previous != (Image *) NULL)
3237 {
3238 if (previous->signature != MagickSignature)
3239 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003240
cristy3ed852e2009-09-05 21:47:34 +00003241 (void) CloseBlob(previous);
3242 (void) DestroyImageList(previous);
3243 }
glennrp0fe50b42010-11-16 03:52:51 +00003244
cristy3ed852e2009-09-05 21:47:34 +00003245 if (logging != MagickFalse)
3246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3247 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003248
cristy3ed852e2009-09-05 21:47:34 +00003249 return((Image *) NULL);
3250 }
glennrp47b9dd52010-11-24 18:12:06 +00003251
cristy3ed852e2009-09-05 21:47:34 +00003252 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003253
cristy3ed852e2009-09-05 21:47:34 +00003254 if ((image->columns == 0) || (image->rows == 0))
3255 {
3256 if (logging != MagickFalse)
3257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3258 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003259
cristy3ed852e2009-09-05 21:47:34 +00003260 ThrowReaderException(CorruptImageError,"CorruptImage");
3261 }
glennrp47b9dd52010-11-24 18:12:06 +00003262
glennrp3faa9a32011-04-23 14:00:25 +00003263#if 0 /* This is probably redundant now */
cristy3ed852e2009-09-05 21:47:34 +00003264 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3265 {
3266 (void) SetImageType(image,PaletteType);
glennrp0fe50b42010-11-16 03:52:51 +00003267
cristy3ed852e2009-09-05 21:47:34 +00003268 if (image->matte != MagickFalse)
3269 {
3270 /* To do: Reduce to binary transparency */
3271 }
3272 }
glennrp3faa9a32011-04-23 14:00:25 +00003273#endif
glennrp47b9dd52010-11-24 18:12:06 +00003274
cristy3ed852e2009-09-05 21:47:34 +00003275 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3276 {
3277 (void) SetImageType(image,TrueColorType);
3278 image->matte=MagickFalse;
3279 }
glennrp0fe50b42010-11-16 03:52:51 +00003280
cristy3ed852e2009-09-05 21:47:34 +00003281 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3282 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003283
cristy3ed852e2009-09-05 21:47:34 +00003284 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3286 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3287 (double) image->page.width,(double) image->page.height,
3288 (double) image->page.x,(double) image->page.y);
3289
3290 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003292
cristy3ed852e2009-09-05 21:47:34 +00003293 return(image);
3294}
3295
3296
3297
3298#if defined(JNG_SUPPORTED)
3299/*
3300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3301% %
3302% %
3303% %
3304% R e a d O n e J N G I m a g e %
3305% %
3306% %
3307% %
3308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3309%
3310% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3311% (minus the 8-byte signature) and returns it. It allocates the memory
3312% necessary for the new Image structure and returns a pointer to the new
3313% image.
3314%
3315% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3316%
3317% The format of the ReadOneJNGImage method is:
3318%
3319% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3320% ExceptionInfo *exception)
3321%
3322% A description of each parameter follows:
3323%
3324% o mng_info: Specifies a pointer to a MngInfo structure.
3325%
3326% o image_info: the image info.
3327%
3328% o exception: return any errors or warnings in this structure.
3329%
3330*/
3331static Image *ReadOneJNGImage(MngInfo *mng_info,
3332 const ImageInfo *image_info, ExceptionInfo *exception)
3333{
3334 Image
3335 *alpha_image,
3336 *color_image,
3337 *image,
3338 *jng_image;
3339
3340 ImageInfo
3341 *alpha_image_info,
3342 *color_image_info;
3343
cristy4383ec82011-01-05 15:42:32 +00003344 MagickBooleanType
3345 logging;
3346
cristybb503372010-05-27 20:51:26 +00003347 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003348 y;
3349
3350 MagickBooleanType
3351 status;
3352
3353 png_uint_32
3354 jng_height,
3355 jng_width;
3356
3357 png_byte
3358 jng_color_type,
3359 jng_image_sample_depth,
3360 jng_image_compression_method,
3361 jng_image_interlace_method,
3362 jng_alpha_sample_depth,
3363 jng_alpha_compression_method,
3364 jng_alpha_filter_method,
3365 jng_alpha_interlace_method;
3366
3367 register const PixelPacket
3368 *s;
3369
cristybb503372010-05-27 20:51:26 +00003370 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003371 i,
3372 x;
3373
3374 register PixelPacket
3375 *q;
3376
3377 register unsigned char
3378 *p;
3379
3380 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003381 read_JSEP,
3382 reading_idat,
3383 skip_to_iend;
3384
cristybb503372010-05-27 20:51:26 +00003385 size_t
cristy3ed852e2009-09-05 21:47:34 +00003386 length;
3387
3388 jng_alpha_compression_method=0;
3389 jng_alpha_sample_depth=8;
3390 jng_color_type=0;
3391 jng_height=0;
3392 jng_width=0;
3393 alpha_image=(Image *) NULL;
3394 color_image=(Image *) NULL;
3395 alpha_image_info=(ImageInfo *) NULL;
3396 color_image_info=(ImageInfo *) NULL;
3397
3398 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003399 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003400
3401 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003402
cristy3ed852e2009-09-05 21:47:34 +00003403 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3404 {
3405 /*
3406 Allocate next image structure.
3407 */
3408 if (logging != MagickFalse)
3409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3410 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003411
cristy3ed852e2009-09-05 21:47:34 +00003412 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003413
cristy3ed852e2009-09-05 21:47:34 +00003414 if (GetNextImageInList(image) == (Image *) NULL)
3415 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003416
cristy3ed852e2009-09-05 21:47:34 +00003417 image=SyncNextImageInList(image);
3418 }
3419 mng_info->image=image;
3420
3421 /*
3422 Signature bytes have already been read.
3423 */
3424
3425 read_JSEP=MagickFalse;
3426 reading_idat=MagickFalse;
3427 skip_to_iend=MagickFalse;
3428 for (;;)
3429 {
3430 char
3431 type[MaxTextExtent];
3432
3433 unsigned char
3434 *chunk;
3435
3436 unsigned int
3437 count;
3438
3439 /*
3440 Read a new JNG chunk.
3441 */
3442 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3443 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003444
cristy3ed852e2009-09-05 21:47:34 +00003445 if (status == MagickFalse)
3446 break;
glennrp0fe50b42010-11-16 03:52:51 +00003447
cristy3ed852e2009-09-05 21:47:34 +00003448 type[0]='\0';
3449 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3450 length=ReadBlobMSBLong(image);
3451 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3452
3453 if (logging != MagickFalse)
3454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003455 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3456 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003457
3458 if (length > PNG_UINT_31_MAX || count == 0)
3459 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003460
cristy3ed852e2009-09-05 21:47:34 +00003461 p=NULL;
3462 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003463
cristy3ed852e2009-09-05 21:47:34 +00003464 if (length)
3465 {
3466 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003467
cristy3ed852e2009-09-05 21:47:34 +00003468 if (chunk == (unsigned char *) NULL)
3469 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003470
cristybb503372010-05-27 20:51:26 +00003471 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003472 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003473
cristy3ed852e2009-09-05 21:47:34 +00003474 p=chunk;
3475 }
glennrp47b9dd52010-11-24 18:12:06 +00003476
cristy3ed852e2009-09-05 21:47:34 +00003477 (void) ReadBlobMSBLong(image); /* read crc word */
3478
3479 if (skip_to_iend)
3480 {
3481 if (length)
3482 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003483
cristy3ed852e2009-09-05 21:47:34 +00003484 continue;
3485 }
3486
3487 if (memcmp(type,mng_JHDR,4) == 0)
3488 {
3489 if (length == 16)
3490 {
cristybb503372010-05-27 20:51:26 +00003491 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003492 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003493 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003494 (p[6] << 8) | p[7]);
3495 jng_color_type=p[8];
3496 jng_image_sample_depth=p[9];
3497 jng_image_compression_method=p[10];
3498 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003499
cristy3ed852e2009-09-05 21:47:34 +00003500 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3501 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003502
cristy3ed852e2009-09-05 21:47:34 +00003503 jng_alpha_sample_depth=p[12];
3504 jng_alpha_compression_method=p[13];
3505 jng_alpha_filter_method=p[14];
3506 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003507
cristy3ed852e2009-09-05 21:47:34 +00003508 if (logging != MagickFalse)
3509 {
3510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003511 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003512
cristy3ed852e2009-09-05 21:47:34 +00003513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003514 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003515
cristy3ed852e2009-09-05 21:47:34 +00003516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3517 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003518
cristy3ed852e2009-09-05 21:47:34 +00003519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3520 " jng_image_sample_depth: %3d",
3521 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003522
cristy3ed852e2009-09-05 21:47:34 +00003523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3524 " jng_image_compression_method:%3d",
3525 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003526
cristy3ed852e2009-09-05 21:47:34 +00003527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3528 " jng_image_interlace_method: %3d",
3529 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003530
cristy3ed852e2009-09-05 21:47:34 +00003531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3532 " jng_alpha_sample_depth: %3d",
3533 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003534
cristy3ed852e2009-09-05 21:47:34 +00003535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3536 " jng_alpha_compression_method:%3d",
3537 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003538
cristy3ed852e2009-09-05 21:47:34 +00003539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3540 " jng_alpha_filter_method: %3d",
3541 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003542
cristy3ed852e2009-09-05 21:47:34 +00003543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3544 " jng_alpha_interlace_method: %3d",
3545 jng_alpha_interlace_method);
3546 }
3547 }
glennrp47b9dd52010-11-24 18:12:06 +00003548
cristy3ed852e2009-09-05 21:47:34 +00003549 if (length)
3550 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003551
cristy3ed852e2009-09-05 21:47:34 +00003552 continue;
3553 }
3554
3555
3556 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3557 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3558 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3559 {
3560 /*
3561 o create color_image
3562 o open color_blob, attached to color_image
3563 o if (color type has alpha)
3564 open alpha_blob, attached to alpha_image
3565 */
3566
cristy73bd4a52010-10-05 11:24:23 +00003567 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003568
cristy3ed852e2009-09-05 21:47:34 +00003569 if (color_image_info == (ImageInfo *) NULL)
3570 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003571
cristy3ed852e2009-09-05 21:47:34 +00003572 GetImageInfo(color_image_info);
3573 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003574
cristy3ed852e2009-09-05 21:47:34 +00003575 if (color_image == (Image *) NULL)
3576 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3577
3578 if (logging != MagickFalse)
3579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3580 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003581
cristy3ed852e2009-09-05 21:47:34 +00003582 (void) AcquireUniqueFilename(color_image->filename);
3583 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3584 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003585
cristy3ed852e2009-09-05 21:47:34 +00003586 if (status == MagickFalse)
3587 return((Image *) NULL);
3588
3589 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3590 {
3591 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00003592 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00003593
cristy3ed852e2009-09-05 21:47:34 +00003594 if (alpha_image_info == (ImageInfo *) NULL)
3595 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003596
cristy3ed852e2009-09-05 21:47:34 +00003597 GetImageInfo(alpha_image_info);
3598 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003599
cristy3ed852e2009-09-05 21:47:34 +00003600 if (alpha_image == (Image *) NULL)
3601 {
3602 alpha_image=DestroyImage(alpha_image);
3603 ThrowReaderException(ResourceLimitError,
3604 "MemoryAllocationFailed");
3605 }
glennrp0fe50b42010-11-16 03:52:51 +00003606
cristy3ed852e2009-09-05 21:47:34 +00003607 if (logging != MagickFalse)
3608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3609 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003610
cristy3ed852e2009-09-05 21:47:34 +00003611 (void) AcquireUniqueFilename(alpha_image->filename);
3612 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3613 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003614
cristy3ed852e2009-09-05 21:47:34 +00003615 if (status == MagickFalse)
3616 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003617
cristy3ed852e2009-09-05 21:47:34 +00003618 if (jng_alpha_compression_method == 0)
3619 {
3620 unsigned char
3621 data[18];
3622
3623 if (logging != MagickFalse)
3624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3625 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003626
cristy3ed852e2009-09-05 21:47:34 +00003627 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3628 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00003629
cristy3ed852e2009-09-05 21:47:34 +00003630 (void) WriteBlobMSBULong(alpha_image,13L);
3631 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00003632 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00003633 PNGLong(data+4,jng_width);
3634 PNGLong(data+8,jng_height);
3635 data[12]=jng_alpha_sample_depth;
3636 data[13]=0; /* color_type gray */
3637 data[14]=0; /* compression method 0 */
3638 data[15]=0; /* filter_method 0 */
3639 data[16]=0; /* interlace_method 0 */
3640 (void) WriteBlob(alpha_image,17,data);
3641 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3642 }
3643 }
3644 reading_idat=MagickTrue;
3645 }
3646
3647 if (memcmp(type,mng_JDAT,4) == 0)
3648 {
glennrp47b9dd52010-11-24 18:12:06 +00003649 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003650
3651 if (logging != MagickFalse)
3652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3653 " Copying JDAT chunk data to color_blob.");
3654
3655 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003656
cristy3ed852e2009-09-05 21:47:34 +00003657 if (length)
3658 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003659
cristy3ed852e2009-09-05 21:47:34 +00003660 continue;
3661 }
3662
3663 if (memcmp(type,mng_IDAT,4) == 0)
3664 {
3665 png_byte
3666 data[5];
3667
glennrp47b9dd52010-11-24 18:12:06 +00003668 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003669
3670 if (image_info->ping == MagickFalse)
3671 {
3672 if (logging != MagickFalse)
3673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3674 " Copying IDAT chunk data to alpha_blob.");
3675
cristybb503372010-05-27 20:51:26 +00003676 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003677 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00003678 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00003679 (void) WriteBlob(alpha_image,4,data);
3680 (void) WriteBlob(alpha_image,length,chunk);
3681 (void) WriteBlobMSBULong(alpha_image,
3682 crc32(crc32(0,data,4),chunk,(uInt) length));
3683 }
glennrp0fe50b42010-11-16 03:52:51 +00003684
cristy3ed852e2009-09-05 21:47:34 +00003685 if (length)
3686 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688 continue;
3689 }
3690
3691 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3692 {
glennrp47b9dd52010-11-24 18:12:06 +00003693 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003694
3695 if (image_info->ping == MagickFalse)
3696 {
3697 if (logging != MagickFalse)
3698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3699 " Copying JDAA chunk data to alpha_blob.");
3700
3701 (void) WriteBlob(alpha_image,length,chunk);
3702 }
glennrp0fe50b42010-11-16 03:52:51 +00003703
cristy3ed852e2009-09-05 21:47:34 +00003704 if (length)
3705 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003706
cristy3ed852e2009-09-05 21:47:34 +00003707 continue;
3708 }
3709
3710 if (memcmp(type,mng_JSEP,4) == 0)
3711 {
3712 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00003713
cristy3ed852e2009-09-05 21:47:34 +00003714 if (length)
3715 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003716
cristy3ed852e2009-09-05 21:47:34 +00003717 continue;
3718 }
3719
3720 if (memcmp(type,mng_bKGD,4) == 0)
3721 {
3722 if (length == 2)
3723 {
3724 image->background_color.red=ScaleCharToQuantum(p[1]);
3725 image->background_color.green=image->background_color.red;
3726 image->background_color.blue=image->background_color.red;
3727 }
glennrp0fe50b42010-11-16 03:52:51 +00003728
cristy3ed852e2009-09-05 21:47:34 +00003729 if (length == 6)
3730 {
3731 image->background_color.red=ScaleCharToQuantum(p[1]);
3732 image->background_color.green=ScaleCharToQuantum(p[3]);
3733 image->background_color.blue=ScaleCharToQuantum(p[5]);
3734 }
glennrp0fe50b42010-11-16 03:52:51 +00003735
cristy3ed852e2009-09-05 21:47:34 +00003736 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3737 continue;
3738 }
3739
3740 if (memcmp(type,mng_gAMA,4) == 0)
3741 {
3742 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00003743 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00003744
cristy3ed852e2009-09-05 21:47:34 +00003745 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3746 continue;
3747 }
3748
3749 if (memcmp(type,mng_cHRM,4) == 0)
3750 {
3751 if (length == 32)
3752 {
cristy8182b072010-05-30 20:10:53 +00003753 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3754 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3755 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3756 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3757 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3758 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3759 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3760 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003761 }
glennrp47b9dd52010-11-24 18:12:06 +00003762
cristy3ed852e2009-09-05 21:47:34 +00003763 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3764 continue;
3765 }
3766
3767 if (memcmp(type,mng_sRGB,4) == 0)
3768 {
3769 if (length == 1)
3770 {
glennrpe610a072010-08-05 17:08:46 +00003771 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00003772 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00003773 image->gamma=0.45455f;
3774 image->chromaticity.red_primary.x=0.6400f;
3775 image->chromaticity.red_primary.y=0.3300f;
3776 image->chromaticity.green_primary.x=0.3000f;
3777 image->chromaticity.green_primary.y=0.6000f;
3778 image->chromaticity.blue_primary.x=0.1500f;
3779 image->chromaticity.blue_primary.y=0.0600f;
3780 image->chromaticity.white_point.x=0.3127f;
3781 image->chromaticity.white_point.y=0.3290f;
3782 }
glennrp47b9dd52010-11-24 18:12:06 +00003783
cristy3ed852e2009-09-05 21:47:34 +00003784 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3785 continue;
3786 }
3787
3788 if (memcmp(type,mng_oFFs,4) == 0)
3789 {
3790 if (length > 8)
3791 {
glennrp5eae7602011-02-22 15:21:32 +00003792 image->page.x=(ssize_t) mng_get_long(p);
3793 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00003794
cristy3ed852e2009-09-05 21:47:34 +00003795 if ((int) p[8] != 0)
3796 {
3797 image->page.x/=10000;
3798 image->page.y/=10000;
3799 }
3800 }
glennrp47b9dd52010-11-24 18:12:06 +00003801
cristy3ed852e2009-09-05 21:47:34 +00003802 if (length)
3803 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003804
cristy3ed852e2009-09-05 21:47:34 +00003805 continue;
3806 }
3807
3808 if (memcmp(type,mng_pHYs,4) == 0)
3809 {
3810 if (length > 8)
3811 {
cristy8182b072010-05-30 20:10:53 +00003812 image->x_resolution=(double) mng_get_long(p);
3813 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003814 if ((int) p[8] == PNG_RESOLUTION_METER)
3815 {
3816 image->units=PixelsPerCentimeterResolution;
3817 image->x_resolution=image->x_resolution/100.0f;
3818 image->y_resolution=image->y_resolution/100.0f;
3819 }
3820 }
glennrp0fe50b42010-11-16 03:52:51 +00003821
cristy3ed852e2009-09-05 21:47:34 +00003822 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3823 continue;
3824 }
3825
3826#if 0
3827 if (memcmp(type,mng_iCCP,4) == 0)
3828 {
glennrpfd05d622011-02-25 04:10:33 +00003829 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00003830 if (length)
3831 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003832
cristy3ed852e2009-09-05 21:47:34 +00003833 continue;
3834 }
3835#endif
3836
3837 if (length)
3838 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3839
3840 if (memcmp(type,mng_IEND,4))
3841 continue;
glennrp0fe50b42010-11-16 03:52:51 +00003842
cristy3ed852e2009-09-05 21:47:34 +00003843 break;
3844 }
3845
3846
3847 /* IEND found */
3848
3849 /*
3850 Finish up reading image data:
3851
3852 o read main image from color_blob.
3853
3854 o close color_blob.
3855
3856 o if (color_type has alpha)
3857 if alpha_encoding is PNG
3858 read secondary image from alpha_blob via ReadPNG
3859 if alpha_encoding is JPEG
3860 read secondary image from alpha_blob via ReadJPEG
3861
3862 o close alpha_blob.
3863
3864 o copy intensity of secondary image into
3865 opacity samples of main image.
3866
3867 o destroy the secondary image.
3868 */
3869
3870 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00003871
cristy3ed852e2009-09-05 21:47:34 +00003872 if (logging != MagickFalse)
3873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3874 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003875
cristy3ed852e2009-09-05 21:47:34 +00003876 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3877 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003878
cristy3ed852e2009-09-05 21:47:34 +00003879 color_image_info->ping=MagickFalse; /* To do: avoid this */
3880 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 if (jng_image == (Image *) NULL)
3883 return((Image *) NULL);
3884
3885 (void) RelinquishUniqueFileResource(color_image->filename);
3886 color_image=DestroyImage(color_image);
3887 color_image_info=DestroyImageInfo(color_image_info);
3888
3889 if (jng_image == (Image *) NULL)
3890 return((Image *) NULL);
3891
3892 if (logging != MagickFalse)
3893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3894 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00003895
cristy3ed852e2009-09-05 21:47:34 +00003896 image->rows=jng_height;
3897 image->columns=jng_width;
3898 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00003899
cristybb503372010-05-27 20:51:26 +00003900 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003901 {
3902 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3903 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3904 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00003905
cristy3ed852e2009-09-05 21:47:34 +00003906 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3907 break;
3908 }
glennrp0fe50b42010-11-16 03:52:51 +00003909
cristy3ed852e2009-09-05 21:47:34 +00003910 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00003911
cristy3ed852e2009-09-05 21:47:34 +00003912 if (image_info->ping == MagickFalse)
3913 {
3914 if (jng_color_type >= 12)
3915 {
3916 if (jng_alpha_compression_method == 0)
3917 {
3918 png_byte
3919 data[5];
3920 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3921 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00003922 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00003923 (void) WriteBlob(alpha_image,4,data);
3924 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3925 }
glennrp0fe50b42010-11-16 03:52:51 +00003926
cristy3ed852e2009-09-05 21:47:34 +00003927 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00003928
cristy3ed852e2009-09-05 21:47:34 +00003929 if (logging != MagickFalse)
3930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3931 " Reading opacity from alpha_blob.");
3932
3933 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3934 "%s",alpha_image->filename);
3935
3936 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003939 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003940 {
3941 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3942 &image->exception);
3943 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003944
cristy3ed852e2009-09-05 21:47:34 +00003945 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003946 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
glennrp7c7b3152011-04-26 04:01:27 +00003947 q->opacity=(Quantum) QuantumRange-
3948 GetRedPixelComponent(s);
glennrp0fe50b42010-11-16 03:52:51 +00003949
cristy3ed852e2009-09-05 21:47:34 +00003950 else
cristybb503372010-05-27 20:51:26 +00003951 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003952 {
glennrp7c7b3152011-04-26 04:01:27 +00003953 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
3954 GetRedPixelComponent(s));
3955 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +00003956 image->matte=MagickTrue;
3957 }
glennrp0fe50b42010-11-16 03:52:51 +00003958
cristy3ed852e2009-09-05 21:47:34 +00003959 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3960 break;
3961 }
3962 (void) RelinquishUniqueFileResource(alpha_image->filename);
3963 alpha_image=DestroyImage(alpha_image);
3964 alpha_image_info=DestroyImageInfo(alpha_image_info);
3965 if (jng_image != (Image *) NULL)
3966 jng_image=DestroyImage(jng_image);
3967 }
3968 }
3969
glennrp47b9dd52010-11-24 18:12:06 +00003970 /* Read the JNG image. */
3971
cristy3ed852e2009-09-05 21:47:34 +00003972 if (mng_info->mng_type == 0)
3973 {
3974 mng_info->mng_width=jng_width;
3975 mng_info->mng_height=jng_height;
3976 }
glennrp0fe50b42010-11-16 03:52:51 +00003977
cristy3ed852e2009-09-05 21:47:34 +00003978 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003979 {
3980 image->page.width=jng_width;
3981 image->page.height=jng_height;
3982 }
3983
cristy3ed852e2009-09-05 21:47:34 +00003984 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003985 {
3986 image->page.x=mng_info->x_off[mng_info->object_id];
3987 image->page.y=mng_info->y_off[mng_info->object_id];
3988 }
3989
cristy3ed852e2009-09-05 21:47:34 +00003990 else
glennrp0fe50b42010-11-16 03:52:51 +00003991 {
3992 image->page.y=mng_info->y_off[mng_info->object_id];
3993 }
3994
cristy3ed852e2009-09-05 21:47:34 +00003995 mng_info->image_found++;
3996 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3997 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003998
cristy3ed852e2009-09-05 21:47:34 +00003999 if (logging != MagickFalse)
4000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4001 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004002
cristy3ed852e2009-09-05 21:47:34 +00004003 return(image);
4004}
4005
4006/*
4007%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4008% %
4009% %
4010% %
4011% R e a d J N G I m a g e %
4012% %
4013% %
4014% %
4015%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4016%
4017% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4018% (including the 8-byte signature) and returns it. It allocates the memory
4019% necessary for the new Image structure and returns a pointer to the new
4020% image.
4021%
4022% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4023%
4024% The format of the ReadJNGImage method is:
4025%
4026% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4027% *exception)
4028%
4029% A description of each parameter follows:
4030%
4031% o image_info: the image info.
4032%
4033% o exception: return any errors or warnings in this structure.
4034%
4035*/
4036
4037static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4038{
4039 Image
4040 *image,
4041 *previous;
4042
4043 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004044 have_mng_structure,
4045 logging,
cristy3ed852e2009-09-05 21:47:34 +00004046 status;
4047
4048 MngInfo
4049 *mng_info;
4050
4051 char
4052 magic_number[MaxTextExtent];
4053
cristy3ed852e2009-09-05 21:47:34 +00004054 size_t
4055 count;
4056
4057 /*
4058 Open image file.
4059 */
4060 assert(image_info != (const ImageInfo *) NULL);
4061 assert(image_info->signature == MagickSignature);
4062 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4063 assert(exception != (ExceptionInfo *) NULL);
4064 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004065 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004066 image=AcquireImage(image_info);
4067 mng_info=(MngInfo *) NULL;
4068 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004069
cristy3ed852e2009-09-05 21:47:34 +00004070 if (status == MagickFalse)
4071 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004072
cristy3ed852e2009-09-05 21:47:34 +00004073 if (LocaleCompare(image_info->magick,"JNG") != 0)
4074 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004075
glennrp47b9dd52010-11-24 18:12:06 +00004076 /* Verify JNG signature. */
4077
cristy3ed852e2009-09-05 21:47:34 +00004078 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004079
glennrp3b8763e2011-02-21 12:08:18 +00004080 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004081 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004082
glennrp47b9dd52010-11-24 18:12:06 +00004083 /* Allocate a MngInfo structure. */
4084
cristy3ed852e2009-09-05 21:47:34 +00004085 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004086 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004087
cristy3ed852e2009-09-05 21:47:34 +00004088 if (mng_info == (MngInfo *) NULL)
4089 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004090
glennrp47b9dd52010-11-24 18:12:06 +00004091 /* Initialize members of the MngInfo structure. */
4092
cristy3ed852e2009-09-05 21:47:34 +00004093 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4094 have_mng_structure=MagickTrue;
4095
4096 mng_info->image=image;
4097 previous=image;
4098 image=ReadOneJNGImage(mng_info,image_info,exception);
4099 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004100
cristy3ed852e2009-09-05 21:47:34 +00004101 if (image == (Image *) NULL)
4102 {
4103 if (IsImageObject(previous) != MagickFalse)
4104 {
4105 (void) CloseBlob(previous);
4106 (void) DestroyImageList(previous);
4107 }
glennrp0fe50b42010-11-16 03:52:51 +00004108
cristy3ed852e2009-09-05 21:47:34 +00004109 if (logging != MagickFalse)
4110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4111 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004112
cristy3ed852e2009-09-05 21:47:34 +00004113 return((Image *) NULL);
4114 }
4115 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004116
cristy3ed852e2009-09-05 21:47:34 +00004117 if (image->columns == 0 || image->rows == 0)
4118 {
4119 if (logging != MagickFalse)
4120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4121 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004122
cristy3ed852e2009-09-05 21:47:34 +00004123 ThrowReaderException(CorruptImageError,"CorruptImage");
4124 }
glennrp0fe50b42010-11-16 03:52:51 +00004125
cristy3ed852e2009-09-05 21:47:34 +00004126 if (logging != MagickFalse)
4127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004128
cristy3ed852e2009-09-05 21:47:34 +00004129 return(image);
4130}
4131#endif
4132
4133static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4134{
4135 char
4136 page_geometry[MaxTextExtent];
4137
4138 Image
4139 *image,
4140 *previous;
4141
cristy4383ec82011-01-05 15:42:32 +00004142 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004143 logging,
4144 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004145
cristy3ed852e2009-09-05 21:47:34 +00004146 volatile int
4147 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004148 object_id,
4149 term_chunk_found,
4150 skip_to_iend;
4151
cristybb503372010-05-27 20:51:26 +00004152 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004153 image_count=0;
4154
4155 MagickBooleanType
4156 status;
4157
4158 MagickOffsetType
4159 offset;
4160
4161 MngInfo
4162 *mng_info;
4163
4164 MngBox
4165 default_fb,
4166 fb,
4167 previous_fb;
4168
4169#if defined(MNG_INSERT_LAYERS)
4170 PixelPacket
4171 mng_background_color;
4172#endif
4173
4174 register unsigned char
4175 *p;
4176
cristybb503372010-05-27 20:51:26 +00004177 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004178 i;
4179
4180 size_t
4181 count;
4182
cristybb503372010-05-27 20:51:26 +00004183 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004184 loop_level;
4185
4186 volatile short
4187 skipping_loop;
4188
4189#if defined(MNG_INSERT_LAYERS)
4190 unsigned int
4191 mandatory_back=0;
4192#endif
4193
4194 volatile unsigned int
4195#ifdef MNG_OBJECT_BUFFERS
4196 mng_background_object=0,
4197#endif
4198 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4199
cristybb503372010-05-27 20:51:26 +00004200 size_t
cristy3ed852e2009-09-05 21:47:34 +00004201 default_frame_timeout,
4202 frame_timeout,
4203#if defined(MNG_INSERT_LAYERS)
4204 image_height,
4205 image_width,
4206#endif
4207 length;
4208
glennrp38ea0832010-06-02 18:50:28 +00004209 /* These delays are all measured in image ticks_per_second,
4210 * not in MNG ticks_per_second
4211 */
cristybb503372010-05-27 20:51:26 +00004212 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004213 default_frame_delay,
4214 final_delay,
4215 final_image_delay,
4216 frame_delay,
4217#if defined(MNG_INSERT_LAYERS)
4218 insert_layers,
4219#endif
4220 mng_iterations=1,
4221 simplicity=0,
4222 subframe_height=0,
4223 subframe_width=0;
4224
4225 previous_fb.top=0;
4226 previous_fb.bottom=0;
4227 previous_fb.left=0;
4228 previous_fb.right=0;
4229 default_fb.top=0;
4230 default_fb.bottom=0;
4231 default_fb.left=0;
4232 default_fb.right=0;
4233
glennrp47b9dd52010-11-24 18:12:06 +00004234 /* Open image file. */
4235
cristy3ed852e2009-09-05 21:47:34 +00004236 assert(image_info != (const ImageInfo *) NULL);
4237 assert(image_info->signature == MagickSignature);
4238 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4239 assert(exception != (ExceptionInfo *) NULL);
4240 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004241 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004242 image=AcquireImage(image_info);
4243 mng_info=(MngInfo *) NULL;
4244 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004245
cristy3ed852e2009-09-05 21:47:34 +00004246 if (status == MagickFalse)
4247 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004248
cristy3ed852e2009-09-05 21:47:34 +00004249 first_mng_object=MagickFalse;
4250 skipping_loop=(-1);
4251 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004252
4253 /* Allocate a MngInfo structure. */
4254
cristy73bd4a52010-10-05 11:24:23 +00004255 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004256
cristy3ed852e2009-09-05 21:47:34 +00004257 if (mng_info == (MngInfo *) NULL)
4258 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004259
glennrp47b9dd52010-11-24 18:12:06 +00004260 /* Initialize members of the MngInfo structure. */
4261
cristy3ed852e2009-09-05 21:47:34 +00004262 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4263 mng_info->image=image;
4264 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004265
4266 if (LocaleCompare(image_info->magick,"MNG") == 0)
4267 {
4268 char
4269 magic_number[MaxTextExtent];
4270
glennrp47b9dd52010-11-24 18:12:06 +00004271 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004272 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4273 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4274 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004275
4276 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004277 for (i=0; i < MNG_MAX_OBJECTS; i++)
4278 {
cristybb503372010-05-27 20:51:26 +00004279 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4280 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004281 }
4282 mng_info->exists[0]=MagickTrue;
4283 }
glennrp47b9dd52010-11-24 18:12:06 +00004284
cristy3ed852e2009-09-05 21:47:34 +00004285 first_mng_object=MagickTrue;
4286 mng_type=0;
4287#if defined(MNG_INSERT_LAYERS)
4288 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4289#endif
4290 default_frame_delay=0;
4291 default_frame_timeout=0;
4292 frame_delay=0;
4293 final_delay=1;
4294 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4295 object_id=0;
4296 skip_to_iend=MagickFalse;
4297 term_chunk_found=MagickFalse;
4298 mng_info->framing_mode=1;
4299#if defined(MNG_INSERT_LAYERS)
4300 mandatory_back=MagickFalse;
4301#endif
4302#if defined(MNG_INSERT_LAYERS)
4303 mng_background_color=image->background_color;
4304#endif
4305 default_fb=mng_info->frame;
4306 previous_fb=mng_info->frame;
4307 do
4308 {
4309 char
4310 type[MaxTextExtent];
4311
4312 if (LocaleCompare(image_info->magick,"MNG") == 0)
4313 {
4314 unsigned char
4315 *chunk;
4316
4317 /*
4318 Read a new chunk.
4319 */
4320 type[0]='\0';
4321 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4322 length=ReadBlobMSBLong(image);
4323 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4324
4325 if (logging != MagickFalse)
4326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004327 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4328 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004329
4330 if (length > PNG_UINT_31_MAX)
4331 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004332
cristy3ed852e2009-09-05 21:47:34 +00004333 if (count == 0)
4334 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004335
cristy3ed852e2009-09-05 21:47:34 +00004336 p=NULL;
4337 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004338
cristy3ed852e2009-09-05 21:47:34 +00004339 if (length)
4340 {
4341 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004342
cristy3ed852e2009-09-05 21:47:34 +00004343 if (chunk == (unsigned char *) NULL)
4344 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004345
cristybb503372010-05-27 20:51:26 +00004346 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004347 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004348
cristy3ed852e2009-09-05 21:47:34 +00004349 p=chunk;
4350 }
glennrp0fe50b42010-11-16 03:52:51 +00004351
cristy3ed852e2009-09-05 21:47:34 +00004352 (void) ReadBlobMSBLong(image); /* read crc word */
4353
4354#if !defined(JNG_SUPPORTED)
4355 if (memcmp(type,mng_JHDR,4) == 0)
4356 {
4357 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004358
cristy3ed852e2009-09-05 21:47:34 +00004359 if (mng_info->jhdr_warning == 0)
4360 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4361 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004362
cristy3ed852e2009-09-05 21:47:34 +00004363 mng_info->jhdr_warning++;
4364 }
4365#endif
4366 if (memcmp(type,mng_DHDR,4) == 0)
4367 {
4368 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004369
cristy3ed852e2009-09-05 21:47:34 +00004370 if (mng_info->dhdr_warning == 0)
4371 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4372 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004373
cristy3ed852e2009-09-05 21:47:34 +00004374 mng_info->dhdr_warning++;
4375 }
4376 if (memcmp(type,mng_MEND,4) == 0)
4377 break;
glennrp47b9dd52010-11-24 18:12:06 +00004378
cristy3ed852e2009-09-05 21:47:34 +00004379 if (skip_to_iend)
4380 {
4381 if (memcmp(type,mng_IEND,4) == 0)
4382 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004383
cristy3ed852e2009-09-05 21:47:34 +00004384 if (length)
4385 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004386
cristy3ed852e2009-09-05 21:47:34 +00004387 if (logging != MagickFalse)
4388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4389 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004390
cristy3ed852e2009-09-05 21:47:34 +00004391 continue;
4392 }
glennrp0fe50b42010-11-16 03:52:51 +00004393
cristy3ed852e2009-09-05 21:47:34 +00004394 if (memcmp(type,mng_MHDR,4) == 0)
4395 {
cristybb503372010-05-27 20:51:26 +00004396 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004397 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004398
cristybb503372010-05-27 20:51:26 +00004399 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004400 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004401
cristy3ed852e2009-09-05 21:47:34 +00004402 if (logging != MagickFalse)
4403 {
4404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004405 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004407 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004408 }
glennrp0fe50b42010-11-16 03:52:51 +00004409
cristy3ed852e2009-09-05 21:47:34 +00004410 p+=8;
cristy8182b072010-05-30 20:10:53 +00004411 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004412
cristy3ed852e2009-09-05 21:47:34 +00004413 if (mng_info->ticks_per_second == 0)
4414 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004415
cristy3ed852e2009-09-05 21:47:34 +00004416 else
4417 default_frame_delay=1UL*image->ticks_per_second/
4418 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004419
cristy3ed852e2009-09-05 21:47:34 +00004420 frame_delay=default_frame_delay;
4421 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004422
cristy3ed852e2009-09-05 21:47:34 +00004423 if (length > 16)
4424 {
4425 p+=16;
cristy8182b072010-05-30 20:10:53 +00004426 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004427 }
glennrp0fe50b42010-11-16 03:52:51 +00004428
cristy3ed852e2009-09-05 21:47:34 +00004429 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004430
cristy3ed852e2009-09-05 21:47:34 +00004431 if ((simplicity != 0) && ((simplicity | 11) == 11))
4432 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004433
cristy3ed852e2009-09-05 21:47:34 +00004434 if ((simplicity != 0) && ((simplicity | 9) == 9))
4435 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004436
cristy3ed852e2009-09-05 21:47:34 +00004437#if defined(MNG_INSERT_LAYERS)
4438 if (mng_type != 3)
4439 insert_layers=MagickTrue;
4440#endif
4441 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4442 {
glennrp47b9dd52010-11-24 18:12:06 +00004443 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004444 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004445
cristy3ed852e2009-09-05 21:47:34 +00004446 if (GetNextImageInList(image) == (Image *) NULL)
4447 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004448
cristy3ed852e2009-09-05 21:47:34 +00004449 image=SyncNextImageInList(image);
4450 mng_info->image=image;
4451 }
4452
4453 if ((mng_info->mng_width > 65535L) ||
4454 (mng_info->mng_height > 65535L))
4455 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004456
cristye8c25f92010-06-03 00:53:06 +00004457 (void) FormatMagickString(page_geometry,MaxTextExtent,
4458 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004459 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004460
cristy3ed852e2009-09-05 21:47:34 +00004461 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004462 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004463 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004464 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004465 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004466
cristy3ed852e2009-09-05 21:47:34 +00004467 for (i=0; i < MNG_MAX_OBJECTS; i++)
4468 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004469
cristy3ed852e2009-09-05 21:47:34 +00004470 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4471 continue;
4472 }
4473
4474 if (memcmp(type,mng_TERM,4) == 0)
4475 {
4476 int
4477 repeat=0;
4478
4479
4480 if (length)
4481 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004482
cristy3ed852e2009-09-05 21:47:34 +00004483 if (repeat == 3)
4484 {
cristy8182b072010-05-30 20:10:53 +00004485 final_delay=(png_uint_32) mng_get_long(&p[2]);
4486 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004487
cristy3ed852e2009-09-05 21:47:34 +00004488 if (mng_iterations == PNG_UINT_31_MAX)
4489 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004490
cristy3ed852e2009-09-05 21:47:34 +00004491 image->iterations=mng_iterations;
4492 term_chunk_found=MagickTrue;
4493 }
glennrp0fe50b42010-11-16 03:52:51 +00004494
cristy3ed852e2009-09-05 21:47:34 +00004495 if (logging != MagickFalse)
4496 {
4497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4498 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004499
cristy3ed852e2009-09-05 21:47:34 +00004500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004501 " final_delay=%.20g",(double) final_delay);
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 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004505 }
glennrp0fe50b42010-11-16 03:52:51 +00004506
cristy3ed852e2009-09-05 21:47:34 +00004507 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4508 continue;
4509 }
4510 if (memcmp(type,mng_DEFI,4) == 0)
4511 {
4512 if (mng_type == 3)
4513 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4514 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4515 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004516
cristy3ed852e2009-09-05 21:47:34 +00004517 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004518
cristy3ed852e2009-09-05 21:47:34 +00004519 if (mng_type == 2 && object_id != 0)
4520 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4521 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4522 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004523
cristy3ed852e2009-09-05 21:47:34 +00004524 if (object_id > MNG_MAX_OBJECTS)
4525 {
4526 /*
4527 Instead ofsuing a warning we should allocate a larger
4528 MngInfo structure and continue.
4529 */
4530 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4531 CoderError,"object id too large","`%s'",image->filename);
4532 object_id=MNG_MAX_OBJECTS;
4533 }
glennrp0fe50b42010-11-16 03:52:51 +00004534
cristy3ed852e2009-09-05 21:47:34 +00004535 if (mng_info->exists[object_id])
4536 if (mng_info->frozen[object_id])
4537 {
4538 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4539 (void) ThrowMagickException(&image->exception,
4540 GetMagickModule(),CoderError,
4541 "DEFI cannot redefine a frozen MNG object","`%s'",
4542 image->filename);
4543 continue;
4544 }
glennrp0fe50b42010-11-16 03:52:51 +00004545
cristy3ed852e2009-09-05 21:47:34 +00004546 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 if (length > 2)
4549 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004550
cristy3ed852e2009-09-05 21:47:34 +00004551 /*
4552 Extract object offset info.
4553 */
4554 if (length > 11)
4555 {
glennrp0fe50b42010-11-16 03:52:51 +00004556 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4557 (p[5] << 16) | (p[6] << 8) | p[7]);
4558
4559 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4560 (p[9] << 16) | (p[10] << 8) | p[11]);
4561
cristy3ed852e2009-09-05 21:47:34 +00004562 if (logging != MagickFalse)
4563 {
4564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004565 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004566 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004567
cristy3ed852e2009-09-05 21:47:34 +00004568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004569 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004570 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004571 }
4572 }
glennrp0fe50b42010-11-16 03:52:51 +00004573
cristy3ed852e2009-09-05 21:47:34 +00004574 /*
4575 Extract object clipping info.
4576 */
4577 if (length > 27)
4578 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4579 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4582 continue;
4583 }
4584 if (memcmp(type,mng_bKGD,4) == 0)
4585 {
4586 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004587
cristy3ed852e2009-09-05 21:47:34 +00004588 if (length > 5)
4589 {
4590 mng_info->mng_global_bkgd.red=
4591 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004592
cristy3ed852e2009-09-05 21:47:34 +00004593 mng_info->mng_global_bkgd.green=
4594 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004595
cristy3ed852e2009-09-05 21:47:34 +00004596 mng_info->mng_global_bkgd.blue=
4597 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004598
cristy3ed852e2009-09-05 21:47:34 +00004599 mng_info->have_global_bkgd=MagickTrue;
4600 }
glennrp0fe50b42010-11-16 03:52:51 +00004601
cristy3ed852e2009-09-05 21:47:34 +00004602 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4603 continue;
4604 }
4605 if (memcmp(type,mng_BACK,4) == 0)
4606 {
4607#if defined(MNG_INSERT_LAYERS)
4608 if (length > 6)
4609 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00004610
cristy3ed852e2009-09-05 21:47:34 +00004611 else
4612 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00004613
cristy3ed852e2009-09-05 21:47:34 +00004614 if (mandatory_back && length > 5)
4615 {
4616 mng_background_color.red=
4617 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004618
cristy3ed852e2009-09-05 21:47:34 +00004619 mng_background_color.green=
4620 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004621
cristy3ed852e2009-09-05 21:47:34 +00004622 mng_background_color.blue=
4623 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004624
cristy3ed852e2009-09-05 21:47:34 +00004625 mng_background_color.opacity=OpaqueOpacity;
4626 }
glennrp0fe50b42010-11-16 03:52:51 +00004627
cristy3ed852e2009-09-05 21:47:34 +00004628#ifdef MNG_OBJECT_BUFFERS
4629 if (length > 8)
4630 mng_background_object=(p[7] << 8) | p[8];
4631#endif
4632#endif
4633 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4634 continue;
4635 }
glennrp47b9dd52010-11-24 18:12:06 +00004636
cristy3ed852e2009-09-05 21:47:34 +00004637 if (memcmp(type,mng_PLTE,4) == 0)
4638 {
glennrp47b9dd52010-11-24 18:12:06 +00004639 /* Read global PLTE. */
4640
cristy3ed852e2009-09-05 21:47:34 +00004641 if (length && (length < 769))
4642 {
4643 if (mng_info->global_plte == (png_colorp) NULL)
4644 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4645 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00004646
cristybb503372010-05-27 20:51:26 +00004647 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004648 {
4649 mng_info->global_plte[i].red=p[3*i];
4650 mng_info->global_plte[i].green=p[3*i+1];
4651 mng_info->global_plte[i].blue=p[3*i+2];
4652 }
glennrp0fe50b42010-11-16 03:52:51 +00004653
cristy35ef8242010-06-03 16:24:13 +00004654 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00004655 }
4656#ifdef MNG_LOOSE
4657 for ( ; i < 256; i++)
4658 {
4659 mng_info->global_plte[i].red=i;
4660 mng_info->global_plte[i].green=i;
4661 mng_info->global_plte[i].blue=i;
4662 }
glennrp0fe50b42010-11-16 03:52:51 +00004663
cristy3ed852e2009-09-05 21:47:34 +00004664 if (length)
4665 mng_info->global_plte_length=256;
4666#endif
4667 else
4668 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00004669
cristy3ed852e2009-09-05 21:47:34 +00004670 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4671 continue;
4672 }
glennrp47b9dd52010-11-24 18:12:06 +00004673
cristy3ed852e2009-09-05 21:47:34 +00004674 if (memcmp(type,mng_tRNS,4) == 0)
4675 {
4676 /* read global tRNS */
4677
4678 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004679 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004680 mng_info->global_trns[i]=p[i];
4681
4682#ifdef MNG_LOOSE
4683 for ( ; i < 256; i++)
4684 mng_info->global_trns[i]=255;
4685#endif
cristy12560f32010-06-03 16:51:08 +00004686 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00004687 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4688 continue;
4689 }
4690 if (memcmp(type,mng_gAMA,4) == 0)
4691 {
4692 if (length == 4)
4693 {
cristybb503372010-05-27 20:51:26 +00004694 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004695 igamma;
4696
cristy8182b072010-05-30 20:10:53 +00004697 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004698 mng_info->global_gamma=((float) igamma)*0.00001;
4699 mng_info->have_global_gama=MagickTrue;
4700 }
glennrp0fe50b42010-11-16 03:52:51 +00004701
cristy3ed852e2009-09-05 21:47:34 +00004702 else
4703 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004704
cristy3ed852e2009-09-05 21:47:34 +00004705 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4706 continue;
4707 }
4708
4709 if (memcmp(type,mng_cHRM,4) == 0)
4710 {
glennrp47b9dd52010-11-24 18:12:06 +00004711 /* Read global cHRM */
4712
cristy3ed852e2009-09-05 21:47:34 +00004713 if (length == 32)
4714 {
cristy8182b072010-05-30 20:10:53 +00004715 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4716 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4717 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004718 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004719 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004720 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004721 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004722 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004723 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004724 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004725 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004726 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004727 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004728 mng_info->have_global_chrm=MagickTrue;
4729 }
4730 else
4731 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004732
cristy3ed852e2009-09-05 21:47:34 +00004733 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4734 continue;
4735 }
glennrp47b9dd52010-11-24 18:12:06 +00004736
cristy3ed852e2009-09-05 21:47:34 +00004737 if (memcmp(type,mng_sRGB,4) == 0)
4738 {
4739 /*
4740 Read global sRGB.
4741 */
4742 if (length)
4743 {
glennrpe610a072010-08-05 17:08:46 +00004744 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00004745 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004746 mng_info->have_global_srgb=MagickTrue;
4747 }
4748 else
4749 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004750
cristy3ed852e2009-09-05 21:47:34 +00004751 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4752 continue;
4753 }
glennrp47b9dd52010-11-24 18:12:06 +00004754
cristy3ed852e2009-09-05 21:47:34 +00004755 if (memcmp(type,mng_iCCP,4) == 0)
4756 {
glennrpfd05d622011-02-25 04:10:33 +00004757 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004758
4759 /*
4760 Read global iCCP.
4761 */
4762 if (length)
4763 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004764
cristy3ed852e2009-09-05 21:47:34 +00004765 continue;
4766 }
glennrp47b9dd52010-11-24 18:12:06 +00004767
cristy3ed852e2009-09-05 21:47:34 +00004768 if (memcmp(type,mng_FRAM,4) == 0)
4769 {
4770 if (mng_type == 3)
4771 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4772 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4773 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004774
cristy3ed852e2009-09-05 21:47:34 +00004775 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4776 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004777
cristy3ed852e2009-09-05 21:47:34 +00004778 frame_delay=default_frame_delay;
4779 frame_timeout=default_frame_timeout;
4780 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00004781
cristy3ed852e2009-09-05 21:47:34 +00004782 if (length)
4783 if (p[0])
4784 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004785
cristy3ed852e2009-09-05 21:47:34 +00004786 if (logging != MagickFalse)
4787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4788 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00004789
cristy3ed852e2009-09-05 21:47:34 +00004790 if (length > 6)
4791 {
glennrp47b9dd52010-11-24 18:12:06 +00004792 /* Note the delay and frame clipping boundaries. */
4793
cristy3ed852e2009-09-05 21:47:34 +00004794 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00004795
cristybb503372010-05-27 20:51:26 +00004796 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004797 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00004798
cristy3ed852e2009-09-05 21:47:34 +00004799 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00004800
cristybb503372010-05-27 20:51:26 +00004801 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004802 {
4803 int
4804 change_delay,
4805 change_timeout,
4806 change_clipping;
4807
4808 change_delay=(*p++);
4809 change_timeout=(*p++);
4810 change_clipping=(*p++);
4811 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00004812
cristy3ed852e2009-09-05 21:47:34 +00004813 if (change_delay)
4814 {
cristy8182b072010-05-30 20:10:53 +00004815 frame_delay=1UL*image->ticks_per_second*
4816 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004817
cristy8182b072010-05-30 20:10:53 +00004818 if (mng_info->ticks_per_second != 0)
4819 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004820
glennrpbb010dd2010-06-01 13:07:15 +00004821 else
4822 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004823
cristy3ed852e2009-09-05 21:47:34 +00004824 if (change_delay == 2)
4825 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 if (logging != MagickFalse)
4830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004831 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004832 }
glennrp47b9dd52010-11-24 18:12:06 +00004833
cristy3ed852e2009-09-05 21:47:34 +00004834 if (change_timeout)
4835 {
glennrpbb010dd2010-06-01 13:07:15 +00004836 frame_timeout=1UL*image->ticks_per_second*
4837 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004838
glennrpbb010dd2010-06-01 13:07:15 +00004839 if (mng_info->ticks_per_second != 0)
4840 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004841
glennrpbb010dd2010-06-01 13:07:15 +00004842 else
4843 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 if (change_delay == 2)
4846 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004849
cristy3ed852e2009-09-05 21:47:34 +00004850 if (logging != MagickFalse)
4851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004852 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004853 }
glennrp47b9dd52010-11-24 18:12:06 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 if (change_clipping)
4856 {
4857 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4858 p+=17;
4859 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00004860
cristy3ed852e2009-09-05 21:47:34 +00004861 if (logging != MagickFalse)
4862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004863 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004864 (double) fb.left,(double) fb.right,(double) fb.top,
4865 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00004866
cristy3ed852e2009-09-05 21:47:34 +00004867 if (change_clipping == 2)
4868 default_fb=fb;
4869 }
4870 }
4871 }
4872 mng_info->clip=fb;
4873 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00004874
cristybb503372010-05-27 20:51:26 +00004875 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004876 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristybb503372010-05-27 20:51:26 +00004878 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004879 -mng_info->clip.top);
4880 /*
4881 Insert a background layer behind the frame if framing_mode is 4.
4882 */
4883#if defined(MNG_INSERT_LAYERS)
4884 if (logging != MagickFalse)
4885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004886 " subframe_width=%.20g, subframe_height=%.20g",(double)
4887 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00004888
cristy3ed852e2009-09-05 21:47:34 +00004889 if (insert_layers && (mng_info->framing_mode == 4) &&
4890 (subframe_width) && (subframe_height))
4891 {
glennrp47b9dd52010-11-24 18:12:06 +00004892 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004893 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4894 {
4895 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 if (GetNextImageInList(image) == (Image *) NULL)
4898 {
4899 image=DestroyImageList(image);
4900 MngInfoFreeStruct(mng_info,&have_mng_structure);
4901 return((Image *) NULL);
4902 }
glennrp47b9dd52010-11-24 18:12:06 +00004903
cristy3ed852e2009-09-05 21:47:34 +00004904 image=SyncNextImageInList(image);
4905 }
glennrp0fe50b42010-11-16 03:52:51 +00004906
cristy3ed852e2009-09-05 21:47:34 +00004907 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00004908
cristy3ed852e2009-09-05 21:47:34 +00004909 if (term_chunk_found)
4910 {
4911 image->start_loop=MagickTrue;
4912 image->iterations=mng_iterations;
4913 term_chunk_found=MagickFalse;
4914 }
glennrp0fe50b42010-11-16 03:52:51 +00004915
cristy3ed852e2009-09-05 21:47:34 +00004916 else
4917 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004918
cristy3ed852e2009-09-05 21:47:34 +00004919 image->columns=subframe_width;
4920 image->rows=subframe_height;
4921 image->page.width=subframe_width;
4922 image->page.height=subframe_height;
4923 image->page.x=mng_info->clip.left;
4924 image->page.y=mng_info->clip.top;
4925 image->background_color=mng_background_color;
4926 image->matte=MagickFalse;
4927 image->delay=0;
4928 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00004929
cristy3ed852e2009-09-05 21:47:34 +00004930 if (logging != MagickFalse)
4931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004932 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004933 (double) mng_info->clip.left,(double) mng_info->clip.right,
4934 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004935 }
4936#endif
4937 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4938 continue;
4939 }
4940 if (memcmp(type,mng_CLIP,4) == 0)
4941 {
4942 unsigned int
4943 first_object,
4944 last_object;
4945
4946 /*
4947 Read CLIP.
4948 */
4949 first_object=(p[0] << 8) | p[1];
4950 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00004951
cristy3ed852e2009-09-05 21:47:34 +00004952 for (i=(int) first_object; i <= (int) last_object; i++)
4953 {
4954 if (mng_info->exists[i] && !mng_info->frozen[i])
4955 {
4956 MngBox
4957 box;
4958
4959 box=mng_info->object_clip[i];
4960 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4961 }
4962 }
glennrp47b9dd52010-11-24 18:12:06 +00004963
cristy3ed852e2009-09-05 21:47:34 +00004964 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4965 continue;
4966 }
4967 if (memcmp(type,mng_SAVE,4) == 0)
4968 {
4969 for (i=1; i < MNG_MAX_OBJECTS; i++)
4970 if (mng_info->exists[i])
4971 {
4972 mng_info->frozen[i]=MagickTrue;
4973#ifdef MNG_OBJECT_BUFFERS
4974 if (mng_info->ob[i] != (MngBuffer *) NULL)
4975 mng_info->ob[i]->frozen=MagickTrue;
4976#endif
4977 }
glennrp0fe50b42010-11-16 03:52:51 +00004978
cristy3ed852e2009-09-05 21:47:34 +00004979 if (length)
4980 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 continue;
4983 }
4984
4985 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4986 {
glennrp47b9dd52010-11-24 18:12:06 +00004987 /* Read DISC or SEEK. */
4988
cristy3ed852e2009-09-05 21:47:34 +00004989 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4990 {
4991 for (i=1; i < MNG_MAX_OBJECTS; i++)
4992 MngInfoDiscardObject(mng_info,i);
4993 }
glennrp0fe50b42010-11-16 03:52:51 +00004994
cristy3ed852e2009-09-05 21:47:34 +00004995 else
4996 {
cristybb503372010-05-27 20:51:26 +00004997 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004998 j;
4999
cristybb503372010-05-27 20:51:26 +00005000 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005001 {
5002 i=p[j] << 8 | p[j+1];
5003 MngInfoDiscardObject(mng_info,i);
5004 }
5005 }
glennrp0fe50b42010-11-16 03:52:51 +00005006
cristy3ed852e2009-09-05 21:47:34 +00005007 if (length)
5008 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005009
cristy3ed852e2009-09-05 21:47:34 +00005010 continue;
5011 }
glennrp47b9dd52010-11-24 18:12:06 +00005012
cristy3ed852e2009-09-05 21:47:34 +00005013 if (memcmp(type,mng_MOVE,4) == 0)
5014 {
cristybb503372010-05-27 20:51:26 +00005015 size_t
cristy3ed852e2009-09-05 21:47:34 +00005016 first_object,
5017 last_object;
5018
glennrp47b9dd52010-11-24 18:12:06 +00005019 /* read MOVE */
5020
cristy3ed852e2009-09-05 21:47:34 +00005021 first_object=(p[0] << 8) | p[1];
5022 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005023 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005024 {
5025 if (mng_info->exists[i] && !mng_info->frozen[i])
5026 {
5027 MngPair
5028 new_pair;
5029
5030 MngPair
5031 old_pair;
5032
5033 old_pair.a=mng_info->x_off[i];
5034 old_pair.b=mng_info->y_off[i];
5035 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5036 mng_info->x_off[i]=new_pair.a;
5037 mng_info->y_off[i]=new_pair.b;
5038 }
5039 }
glennrp47b9dd52010-11-24 18:12:06 +00005040
cristy3ed852e2009-09-05 21:47:34 +00005041 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5042 continue;
5043 }
5044
5045 if (memcmp(type,mng_LOOP,4) == 0)
5046 {
cristybb503372010-05-27 20:51:26 +00005047 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005048 loop_level=chunk[0];
5049 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005050
5051 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005052 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005053
cristy3ed852e2009-09-05 21:47:34 +00005054 if (logging != MagickFalse)
5055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005056 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5057 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005058
cristy3ed852e2009-09-05 21:47:34 +00005059 if (loop_iters == 0)
5060 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005061
cristy3ed852e2009-09-05 21:47:34 +00005062 else
5063 {
5064 mng_info->loop_jump[loop_level]=TellBlob(image);
5065 mng_info->loop_count[loop_level]=loop_iters;
5066 }
glennrp0fe50b42010-11-16 03:52:51 +00005067
cristy3ed852e2009-09-05 21:47:34 +00005068 mng_info->loop_iteration[loop_level]=0;
5069 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5070 continue;
5071 }
glennrp47b9dd52010-11-24 18:12:06 +00005072
cristy3ed852e2009-09-05 21:47:34 +00005073 if (memcmp(type,mng_ENDL,4) == 0)
5074 {
5075 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005076
cristy3ed852e2009-09-05 21:47:34 +00005077 if (skipping_loop > 0)
5078 {
5079 if (skipping_loop == loop_level)
5080 {
5081 /*
5082 Found end of zero-iteration loop.
5083 */
5084 skipping_loop=(-1);
5085 mng_info->loop_active[loop_level]=0;
5086 }
5087 }
glennrp47b9dd52010-11-24 18:12:06 +00005088
cristy3ed852e2009-09-05 21:47:34 +00005089 else
5090 {
5091 if (mng_info->loop_active[loop_level] == 1)
5092 {
5093 mng_info->loop_count[loop_level]--;
5094 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005095
cristy3ed852e2009-09-05 21:47:34 +00005096 if (logging != MagickFalse)
5097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005098 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005099 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005100 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005101
cristy3ed852e2009-09-05 21:47:34 +00005102 if (mng_info->loop_count[loop_level] != 0)
5103 {
5104 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5105 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005106
cristy3ed852e2009-09-05 21:47:34 +00005107 if (offset < 0)
5108 ThrowReaderException(CorruptImageError,
5109 "ImproperImageHeader");
5110 }
glennrp47b9dd52010-11-24 18:12:06 +00005111
cristy3ed852e2009-09-05 21:47:34 +00005112 else
5113 {
5114 short
5115 last_level;
5116
5117 /*
5118 Finished loop.
5119 */
5120 mng_info->loop_active[loop_level]=0;
5121 last_level=(-1);
5122 for (i=0; i < loop_level; i++)
5123 if (mng_info->loop_active[i] == 1)
5124 last_level=(short) i;
5125 loop_level=last_level;
5126 }
5127 }
5128 }
glennrp47b9dd52010-11-24 18:12:06 +00005129
cristy3ed852e2009-09-05 21:47:34 +00005130 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5131 continue;
5132 }
glennrp47b9dd52010-11-24 18:12:06 +00005133
cristy3ed852e2009-09-05 21:47:34 +00005134 if (memcmp(type,mng_CLON,4) == 0)
5135 {
5136 if (mng_info->clon_warning == 0)
5137 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5138 CoderError,"CLON is not implemented yet","`%s'",
5139 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005140
cristy3ed852e2009-09-05 21:47:34 +00005141 mng_info->clon_warning++;
5142 }
glennrp47b9dd52010-11-24 18:12:06 +00005143
cristy3ed852e2009-09-05 21:47:34 +00005144 if (memcmp(type,mng_MAGN,4) == 0)
5145 {
5146 png_uint_16
5147 magn_first,
5148 magn_last,
5149 magn_mb,
5150 magn_ml,
5151 magn_mr,
5152 magn_mt,
5153 magn_mx,
5154 magn_my,
5155 magn_methx,
5156 magn_methy;
5157
5158 if (length > 1)
5159 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005160
cristy3ed852e2009-09-05 21:47:34 +00005161 else
5162 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005163
cristy3ed852e2009-09-05 21:47:34 +00005164 if (length > 3)
5165 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005166
cristy3ed852e2009-09-05 21:47:34 +00005167 else
5168 magn_last=magn_first;
5169#ifndef MNG_OBJECT_BUFFERS
5170 if (magn_first || magn_last)
5171 if (mng_info->magn_warning == 0)
5172 {
5173 (void) ThrowMagickException(&image->exception,
5174 GetMagickModule(),CoderError,
5175 "MAGN is not implemented yet for nonzero objects",
5176 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005177
cristy3ed852e2009-09-05 21:47:34 +00005178 mng_info->magn_warning++;
5179 }
5180#endif
5181 if (length > 4)
5182 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005183
cristy3ed852e2009-09-05 21:47:34 +00005184 else
5185 magn_methx=0;
5186
5187 if (length > 6)
5188 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005189
cristy3ed852e2009-09-05 21:47:34 +00005190 else
5191 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005192
cristy3ed852e2009-09-05 21:47:34 +00005193 if (magn_mx == 0)
5194 magn_mx=1;
5195
5196 if (length > 8)
5197 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005198
cristy3ed852e2009-09-05 21:47:34 +00005199 else
5200 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005201
cristy3ed852e2009-09-05 21:47:34 +00005202 if (magn_my == 0)
5203 magn_my=1;
5204
5205 if (length > 10)
5206 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005207
cristy3ed852e2009-09-05 21:47:34 +00005208 else
5209 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005210
cristy3ed852e2009-09-05 21:47:34 +00005211 if (magn_ml == 0)
5212 magn_ml=1;
5213
5214 if (length > 12)
5215 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005216
cristy3ed852e2009-09-05 21:47:34 +00005217 else
5218 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005219
cristy3ed852e2009-09-05 21:47:34 +00005220 if (magn_mr == 0)
5221 magn_mr=1;
5222
5223 if (length > 14)
5224 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005225
cristy3ed852e2009-09-05 21:47:34 +00005226 else
5227 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005228
cristy3ed852e2009-09-05 21:47:34 +00005229 if (magn_mt == 0)
5230 magn_mt=1;
5231
5232 if (length > 16)
5233 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005234
cristy3ed852e2009-09-05 21:47:34 +00005235 else
5236 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005237
cristy3ed852e2009-09-05 21:47:34 +00005238 if (magn_mb == 0)
5239 magn_mb=1;
5240
5241 if (length > 17)
5242 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005243
cristy3ed852e2009-09-05 21:47:34 +00005244 else
5245 magn_methy=magn_methx;
5246
glennrp47b9dd52010-11-24 18:12:06 +00005247
cristy3ed852e2009-09-05 21:47:34 +00005248 if (magn_methx > 5 || magn_methy > 5)
5249 if (mng_info->magn_warning == 0)
5250 {
5251 (void) ThrowMagickException(&image->exception,
5252 GetMagickModule(),CoderError,
5253 "Unknown MAGN method in MNG datastream","`%s'",
5254 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005255
cristy3ed852e2009-09-05 21:47:34 +00005256 mng_info->magn_warning++;
5257 }
5258#ifdef MNG_OBJECT_BUFFERS
5259 /* Magnify existing objects in the range magn_first to magn_last */
5260#endif
5261 if (magn_first == 0 || magn_last == 0)
5262 {
5263 /* Save the magnification factors for object 0 */
5264 mng_info->magn_mb=magn_mb;
5265 mng_info->magn_ml=magn_ml;
5266 mng_info->magn_mr=magn_mr;
5267 mng_info->magn_mt=magn_mt;
5268 mng_info->magn_mx=magn_mx;
5269 mng_info->magn_my=magn_my;
5270 mng_info->magn_methx=magn_methx;
5271 mng_info->magn_methy=magn_methy;
5272 }
5273 }
glennrp47b9dd52010-11-24 18:12:06 +00005274
cristy3ed852e2009-09-05 21:47:34 +00005275 if (memcmp(type,mng_PAST,4) == 0)
5276 {
5277 if (mng_info->past_warning == 0)
5278 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5279 CoderError,"PAST is not implemented yet","`%s'",
5280 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005281
cristy3ed852e2009-09-05 21:47:34 +00005282 mng_info->past_warning++;
5283 }
glennrp47b9dd52010-11-24 18:12:06 +00005284
cristy3ed852e2009-09-05 21:47:34 +00005285 if (memcmp(type,mng_SHOW,4) == 0)
5286 {
5287 if (mng_info->show_warning == 0)
5288 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5289 CoderError,"SHOW is not implemented yet","`%s'",
5290 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005291
cristy3ed852e2009-09-05 21:47:34 +00005292 mng_info->show_warning++;
5293 }
glennrp47b9dd52010-11-24 18:12:06 +00005294
cristy3ed852e2009-09-05 21:47:34 +00005295 if (memcmp(type,mng_sBIT,4) == 0)
5296 {
5297 if (length < 4)
5298 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005299
cristy3ed852e2009-09-05 21:47:34 +00005300 else
5301 {
5302 mng_info->global_sbit.gray=p[0];
5303 mng_info->global_sbit.red=p[0];
5304 mng_info->global_sbit.green=p[1];
5305 mng_info->global_sbit.blue=p[2];
5306 mng_info->global_sbit.alpha=p[3];
5307 mng_info->have_global_sbit=MagickTrue;
5308 }
5309 }
5310 if (memcmp(type,mng_pHYs,4) == 0)
5311 {
5312 if (length > 8)
5313 {
5314 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005315 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005316 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005317 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005318 mng_info->global_phys_unit_type=p[8];
5319 mng_info->have_global_phys=MagickTrue;
5320 }
glennrp47b9dd52010-11-24 18:12:06 +00005321
cristy3ed852e2009-09-05 21:47:34 +00005322 else
5323 mng_info->have_global_phys=MagickFalse;
5324 }
5325 if (memcmp(type,mng_pHYg,4) == 0)
5326 {
5327 if (mng_info->phyg_warning == 0)
5328 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5329 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005330
cristy3ed852e2009-09-05 21:47:34 +00005331 mng_info->phyg_warning++;
5332 }
5333 if (memcmp(type,mng_BASI,4) == 0)
5334 {
5335 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005336
cristy3ed852e2009-09-05 21:47:34 +00005337 if (mng_info->basi_warning == 0)
5338 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5339 CoderError,"BASI is not implemented yet","`%s'",
5340 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 mng_info->basi_warning++;
5343#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005344 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005345 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005346 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005347 (p[6] << 8) | p[7]);
5348 basi_color_type=p[8];
5349 basi_compression_method=p[9];
5350 basi_filter_type=p[10];
5351 basi_interlace_method=p[11];
5352 if (length > 11)
5353 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005354
cristy3ed852e2009-09-05 21:47:34 +00005355 else
5356 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005357
cristy3ed852e2009-09-05 21:47:34 +00005358 if (length > 13)
5359 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005360
cristy3ed852e2009-09-05 21:47:34 +00005361 else
5362 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 if (length > 15)
5365 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005366
cristy3ed852e2009-09-05 21:47:34 +00005367 else
5368 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005369
cristy3ed852e2009-09-05 21:47:34 +00005370 if (length > 17)
5371 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005372
cristy3ed852e2009-09-05 21:47:34 +00005373 else
5374 {
5375 if (basi_sample_depth == 16)
5376 basi_alpha=65535L;
5377 else
5378 basi_alpha=255;
5379 }
glennrp47b9dd52010-11-24 18:12:06 +00005380
cristy3ed852e2009-09-05 21:47:34 +00005381 if (length > 19)
5382 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 else
5385 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387#endif
5388 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5389 continue;
5390 }
glennrp47b9dd52010-11-24 18:12:06 +00005391
cristy3ed852e2009-09-05 21:47:34 +00005392 if (memcmp(type,mng_IHDR,4)
5393#if defined(JNG_SUPPORTED)
5394 && memcmp(type,mng_JHDR,4)
5395#endif
5396 )
5397 {
5398 /* Not an IHDR or JHDR chunk */
5399 if (length)
5400 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005401
cristy3ed852e2009-09-05 21:47:34 +00005402 continue;
5403 }
5404/* Process IHDR */
5405 if (logging != MagickFalse)
5406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5407 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005408
cristy3ed852e2009-09-05 21:47:34 +00005409 mng_info->exists[object_id]=MagickTrue;
5410 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005411
cristy3ed852e2009-09-05 21:47:34 +00005412 if (mng_info->invisible[object_id])
5413 {
5414 if (logging != MagickFalse)
5415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5416 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005417
cristy3ed852e2009-09-05 21:47:34 +00005418 skip_to_iend=MagickTrue;
5419 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5420 continue;
5421 }
5422#if defined(MNG_INSERT_LAYERS)
5423 if (length < 8)
5424 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005425
cristy8182b072010-05-30 20:10:53 +00005426 image_width=(size_t) mng_get_long(p);
5427 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005428#endif
5429 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5430
5431 /*
5432 Insert a transparent background layer behind the entire animation
5433 if it is not full screen.
5434 */
5435#if defined(MNG_INSERT_LAYERS)
5436 if (insert_layers && mng_type && first_mng_object)
5437 {
5438 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5439 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005440 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005441 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005442 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005443 {
5444 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5445 {
5446 /*
5447 Allocate next image structure.
5448 */
5449 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005450
cristy3ed852e2009-09-05 21:47:34 +00005451 if (GetNextImageInList(image) == (Image *) NULL)
5452 {
5453 image=DestroyImageList(image);
5454 MngInfoFreeStruct(mng_info,&have_mng_structure);
5455 return((Image *) NULL);
5456 }
glennrp47b9dd52010-11-24 18:12:06 +00005457
cristy3ed852e2009-09-05 21:47:34 +00005458 image=SyncNextImageInList(image);
5459 }
5460 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005461
cristy3ed852e2009-09-05 21:47:34 +00005462 if (term_chunk_found)
5463 {
5464 image->start_loop=MagickTrue;
5465 image->iterations=mng_iterations;
5466 term_chunk_found=MagickFalse;
5467 }
glennrp47b9dd52010-11-24 18:12:06 +00005468
cristy3ed852e2009-09-05 21:47:34 +00005469 else
5470 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005471
5472 /* Make a background rectangle. */
5473
cristy3ed852e2009-09-05 21:47:34 +00005474 image->delay=0;
5475 image->columns=mng_info->mng_width;
5476 image->rows=mng_info->mng_height;
5477 image->page.width=mng_info->mng_width;
5478 image->page.height=mng_info->mng_height;
5479 image->page.x=0;
5480 image->page.y=0;
5481 image->background_color=mng_background_color;
5482 (void) SetImageBackgroundColor(image);
5483 if (logging != MagickFalse)
5484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005485 " Inserted transparent background layer, W=%.20g, H=%.20g",
5486 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005487 }
5488 }
5489 /*
5490 Insert a background layer behind the upcoming image if
5491 framing_mode is 3, and we haven't already inserted one.
5492 */
5493 if (insert_layers && (mng_info->framing_mode == 3) &&
5494 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5495 (simplicity & 0x08)))
5496 {
5497 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5498 {
5499 /*
5500 Allocate next image structure.
5501 */
5502 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005503
cristy3ed852e2009-09-05 21:47:34 +00005504 if (GetNextImageInList(image) == (Image *) NULL)
5505 {
5506 image=DestroyImageList(image);
5507 MngInfoFreeStruct(mng_info,&have_mng_structure);
5508 return((Image *) NULL);
5509 }
glennrp47b9dd52010-11-24 18:12:06 +00005510
cristy3ed852e2009-09-05 21:47:34 +00005511 image=SyncNextImageInList(image);
5512 }
glennrp0fe50b42010-11-16 03:52:51 +00005513
cristy3ed852e2009-09-05 21:47:34 +00005514 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005515
cristy3ed852e2009-09-05 21:47:34 +00005516 if (term_chunk_found)
5517 {
5518 image->start_loop=MagickTrue;
5519 image->iterations=mng_iterations;
5520 term_chunk_found=MagickFalse;
5521 }
glennrp0fe50b42010-11-16 03:52:51 +00005522
cristy3ed852e2009-09-05 21:47:34 +00005523 else
5524 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005525
cristy3ed852e2009-09-05 21:47:34 +00005526 image->delay=0;
5527 image->columns=subframe_width;
5528 image->rows=subframe_height;
5529 image->page.width=subframe_width;
5530 image->page.height=subframe_height;
5531 image->page.x=mng_info->clip.left;
5532 image->page.y=mng_info->clip.top;
5533 image->background_color=mng_background_color;
5534 image->matte=MagickFalse;
5535 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 if (logging != MagickFalse)
5538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005539 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005540 (double) mng_info->clip.left,(double) mng_info->clip.right,
5541 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005542 }
5543#endif /* MNG_INSERT_LAYERS */
5544 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005545
cristy3ed852e2009-09-05 21:47:34 +00005546 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5547 {
5548 /*
5549 Allocate next image structure.
5550 */
5551 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005552
cristy3ed852e2009-09-05 21:47:34 +00005553 if (GetNextImageInList(image) == (Image *) NULL)
5554 {
5555 image=DestroyImageList(image);
5556 MngInfoFreeStruct(mng_info,&have_mng_structure);
5557 return((Image *) NULL);
5558 }
glennrp47b9dd52010-11-24 18:12:06 +00005559
cristy3ed852e2009-09-05 21:47:34 +00005560 image=SyncNextImageInList(image);
5561 }
5562 mng_info->image=image;
5563 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5564 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005565
cristy3ed852e2009-09-05 21:47:34 +00005566 if (status == MagickFalse)
5567 break;
glennrp0fe50b42010-11-16 03:52:51 +00005568
cristy3ed852e2009-09-05 21:47:34 +00005569 if (term_chunk_found)
5570 {
5571 image->start_loop=MagickTrue;
5572 term_chunk_found=MagickFalse;
5573 }
glennrp0fe50b42010-11-16 03:52:51 +00005574
cristy3ed852e2009-09-05 21:47:34 +00005575 else
5576 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005577
cristy3ed852e2009-09-05 21:47:34 +00005578 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5579 {
5580 image->delay=frame_delay;
5581 frame_delay=default_frame_delay;
5582 }
glennrp0fe50b42010-11-16 03:52:51 +00005583
cristy3ed852e2009-09-05 21:47:34 +00005584 else
5585 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 image->page.width=mng_info->mng_width;
5588 image->page.height=mng_info->mng_height;
5589 image->page.x=mng_info->x_off[object_id];
5590 image->page.y=mng_info->y_off[object_id];
5591 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00005592
cristy3ed852e2009-09-05 21:47:34 +00005593 /*
5594 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5595 */
glennrp47b9dd52010-11-24 18:12:06 +00005596
cristy3ed852e2009-09-05 21:47:34 +00005597 if (logging != MagickFalse)
5598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5599 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5600 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005601
cristybb503372010-05-27 20:51:26 +00005602 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00005603
cristy3ed852e2009-09-05 21:47:34 +00005604 if (offset < 0)
5605 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5606 }
5607
5608 previous=image;
5609 mng_info->image=image;
5610 mng_info->mng_type=mng_type;
5611 mng_info->object_id=object_id;
5612
5613 if (memcmp(type,mng_IHDR,4) == 0)
5614 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005615
cristy3ed852e2009-09-05 21:47:34 +00005616#if defined(JNG_SUPPORTED)
5617 else
5618 image=ReadOneJNGImage(mng_info,image_info,exception);
5619#endif
5620
5621 if (image == (Image *) NULL)
5622 {
5623 if (IsImageObject(previous) != MagickFalse)
5624 {
5625 (void) DestroyImageList(previous);
5626 (void) CloseBlob(previous);
5627 }
glennrp47b9dd52010-11-24 18:12:06 +00005628
cristy3ed852e2009-09-05 21:47:34 +00005629 MngInfoFreeStruct(mng_info,&have_mng_structure);
5630 return((Image *) NULL);
5631 }
glennrp0fe50b42010-11-16 03:52:51 +00005632
cristy3ed852e2009-09-05 21:47:34 +00005633 if (image->columns == 0 || image->rows == 0)
5634 {
5635 (void) CloseBlob(image);
5636 image=DestroyImageList(image);
5637 MngInfoFreeStruct(mng_info,&have_mng_structure);
5638 return((Image *) NULL);
5639 }
glennrp0fe50b42010-11-16 03:52:51 +00005640
cristy3ed852e2009-09-05 21:47:34 +00005641 mng_info->image=image;
5642
5643 if (mng_type)
5644 {
5645 MngBox
5646 crop_box;
5647
5648 if (mng_info->magn_methx || mng_info->magn_methy)
5649 {
5650 png_uint_32
5651 magnified_height,
5652 magnified_width;
5653
5654 if (logging != MagickFalse)
5655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5656 " Processing MNG MAGN chunk");
5657
5658 if (mng_info->magn_methx == 1)
5659 {
5660 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005661
cristy3ed852e2009-09-05 21:47:34 +00005662 if (image->columns > 1)
5663 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005666 magnified_width += (png_uint_32)
5667 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00005668 }
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 else
5671 {
cristy4e5bc842010-06-09 13:56:01 +00005672 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00005673
cristy3ed852e2009-09-05 21:47:34 +00005674 if (image->columns > 1)
5675 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00005676
cristy3ed852e2009-09-05 21:47:34 +00005677 if (image->columns > 2)
5678 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00005679
cristy3ed852e2009-09-05 21:47:34 +00005680 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005681 magnified_width += (png_uint_32)
5682 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00005683 }
glennrp47b9dd52010-11-24 18:12:06 +00005684
cristy3ed852e2009-09-05 21:47:34 +00005685 if (mng_info->magn_methy == 1)
5686 {
5687 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005688
cristy3ed852e2009-09-05 21:47:34 +00005689 if (image->rows > 1)
5690 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005693 magnified_height += (png_uint_32)
5694 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00005695 }
glennrp47b9dd52010-11-24 18:12:06 +00005696
cristy3ed852e2009-09-05 21:47:34 +00005697 else
5698 {
cristy4e5bc842010-06-09 13:56:01 +00005699 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00005700
cristy3ed852e2009-09-05 21:47:34 +00005701 if (image->rows > 1)
5702 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00005703
cristy3ed852e2009-09-05 21:47:34 +00005704 if (image->rows > 2)
5705 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00005706
cristy3ed852e2009-09-05 21:47:34 +00005707 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005708 magnified_height += (png_uint_32)
5709 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00005710 }
glennrp47b9dd52010-11-24 18:12:06 +00005711
cristy3ed852e2009-09-05 21:47:34 +00005712 if (magnified_height > image->rows ||
5713 magnified_width > image->columns)
5714 {
5715 Image
5716 *large_image;
5717
5718 int
5719 yy;
5720
cristybb503372010-05-27 20:51:26 +00005721 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005722 m,
5723 y;
5724
cristybb503372010-05-27 20:51:26 +00005725 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005726 x;
5727
5728 register PixelPacket
5729 *n,
5730 *q;
5731
5732 PixelPacket
5733 *next,
5734 *prev;
5735
5736 png_uint_16
5737 magn_methx,
5738 magn_methy;
5739
glennrp47b9dd52010-11-24 18:12:06 +00005740 /* Allocate next image structure. */
5741
cristy3ed852e2009-09-05 21:47:34 +00005742 if (logging != MagickFalse)
5743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5744 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00005745
cristy3ed852e2009-09-05 21:47:34 +00005746 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005747
cristy3ed852e2009-09-05 21:47:34 +00005748 if (GetNextImageInList(image) == (Image *) NULL)
5749 {
5750 image=DestroyImageList(image);
5751 MngInfoFreeStruct(mng_info,&have_mng_structure);
5752 return((Image *) NULL);
5753 }
5754
5755 large_image=SyncNextImageInList(image);
5756
5757 large_image->columns=magnified_width;
5758 large_image->rows=magnified_height;
5759
5760 magn_methx=mng_info->magn_methx;
5761 magn_methy=mng_info->magn_methy;
5762
glennrp3faa9a32011-04-23 14:00:25 +00005763#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00005764#define QM unsigned short
5765 if (magn_methx != 1 || magn_methy != 1)
5766 {
5767 /*
5768 Scale pixels to unsigned shorts to prevent
5769 overflow of intermediate values of interpolations
5770 */
cristybb503372010-05-27 20:51:26 +00005771 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005772 {
5773 q=GetAuthenticPixels(image,0,y,image->columns,1,
5774 exception);
glennrp47b9dd52010-11-24 18:12:06 +00005775
cristybb503372010-05-27 20:51:26 +00005776 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005777 {
glennrp7c7b3152011-04-26 04:01:27 +00005778 SetRedPixelComponent(q,ScaleQuantumToShort(
5779 GetRedPixelComponent(q));
5780 SetGreenPixelComponent(q,ScaleQuantumToShort(
5781 GetGreenPixelComponent(q));
5782 SetBluePixelComponent(q,ScaleQuantumToShort(
5783 GetBluePixelComponent(q));
5784 SetOpacityPixelComponent(q,ScaleQuantumToShort(
5785 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00005786 q++;
5787 }
glennrp47b9dd52010-11-24 18:12:06 +00005788
cristy3ed852e2009-09-05 21:47:34 +00005789 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5790 break;
5791 }
5792 }
5793#else
5794#define QM Quantum
5795#endif
5796
5797 if (image->matte != MagickFalse)
5798 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005799
cristy3ed852e2009-09-05 21:47:34 +00005800 else
5801 {
5802 large_image->background_color.opacity=OpaqueOpacity;
5803 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 if (magn_methx == 4)
5806 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 if (magn_methx == 5)
5809 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00005810
cristy3ed852e2009-09-05 21:47:34 +00005811 if (magn_methy == 4)
5812 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00005813
cristy3ed852e2009-09-05 21:47:34 +00005814 if (magn_methy == 5)
5815 magn_methy=3;
5816 }
5817
5818 /* magnify the rows into the right side of the large image */
5819
5820 if (logging != MagickFalse)
5821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005822 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00005823 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005824 yy=0;
5825 length=(size_t) image->columns;
5826 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5827 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00005828
cristy3ed852e2009-09-05 21:47:34 +00005829 if ((prev == (PixelPacket *) NULL) ||
5830 (next == (PixelPacket *) NULL))
5831 {
5832 image=DestroyImageList(image);
5833 MngInfoFreeStruct(mng_info,&have_mng_structure);
5834 ThrowReaderException(ResourceLimitError,
5835 "MemoryAllocationFailed");
5836 }
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5839 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristybb503372010-05-27 20:51:26 +00005841 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005842 {
5843 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005844 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005845
cristybb503372010-05-27 20:51:26 +00005846 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5847 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005848
cristybb503372010-05-27 20:51:26 +00005849 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
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)
cristy3ed852e2009-09-05 21:47:34 +00005853 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005854
cristy3ed852e2009-09-05 21:47:34 +00005855 else
cristybb503372010-05-27 20:51:26 +00005856 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005857
cristy3ed852e2009-09-05 21:47:34 +00005858 n=prev;
5859 prev=next;
5860 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00005861
cristybb503372010-05-27 20:51:26 +00005862 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005863 {
5864 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5865 exception);
5866 (void) CopyMagickMemory(next,n,length);
5867 }
glennrp47b9dd52010-11-24 18:12:06 +00005868
cristy3ed852e2009-09-05 21:47:34 +00005869 for (i=0; i < m; i++, yy++)
5870 {
glennrp7c7b3152011-04-26 04:01:27 +00005871 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00005872 register PixelPacket
5873 *pixels;
5874
cristybb503372010-05-27 20:51:26 +00005875 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005876 pixels=prev;
5877 n=next;
5878 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5879 1,exception);
5880 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00005881
cristybb503372010-05-27 20:51:26 +00005882 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005883 {
glennrpfd05d622011-02-25 04:10:33 +00005884 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005885 /*
5886 if (image->storage_class == PseudoClass)
5887 {
5888 }
5889 */
5890
5891 if (magn_methy <= 1)
5892 {
5893 *q=(*pixels); /* replicate previous */
5894 }
glennrp47b9dd52010-11-24 18:12:06 +00005895
cristy3ed852e2009-09-05 21:47:34 +00005896 else if (magn_methy == 2 || magn_methy == 4)
5897 {
5898 if (i == 0)
5899 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005900
cristy3ed852e2009-09-05 21:47:34 +00005901 else
5902 {
5903 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005904 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5905 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005906 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005907 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5908 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005909 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005910 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5911 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005912 +(*pixels).blue);
glennrp47b9dd52010-11-24 18:12:06 +00005913
cristy3ed852e2009-09-05 21:47:34 +00005914 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005915 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005916 (2*i*((*n).opacity
5917 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005918 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005919 }
glennrp47b9dd52010-11-24 18:12:06 +00005920
cristy3ed852e2009-09-05 21:47:34 +00005921 if (magn_methy == 4)
5922 {
5923 /* Replicate nearest */
5924 if (i <= ((m+1) << 1))
5925 (*q).opacity=(*pixels).opacity+0;
5926 else
5927 (*q).opacity=(*n).opacity+0;
5928 }
5929 }
glennrp47b9dd52010-11-24 18:12:06 +00005930
cristy3ed852e2009-09-05 21:47:34 +00005931 else /* if (magn_methy == 3 || magn_methy == 5) */
5932 {
5933 /* Replicate nearest */
5934 if (i <= ((m+1) << 1))
5935 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005936
cristy3ed852e2009-09-05 21:47:34 +00005937 else
5938 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00005939
cristy3ed852e2009-09-05 21:47:34 +00005940 if (magn_methy == 5)
5941 {
cristybb503372010-05-27 20:51:26 +00005942 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5943 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005944 +(*pixels).opacity);
5945 }
5946 }
5947 n++;
5948 q++;
5949 pixels++;
5950 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00005951
cristy3ed852e2009-09-05 21:47:34 +00005952 if (SyncAuthenticPixels(large_image,exception) == 0)
5953 break;
glennrp47b9dd52010-11-24 18:12:06 +00005954
cristy3ed852e2009-09-05 21:47:34 +00005955 } /* i */
5956 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00005957
cristy3ed852e2009-09-05 21:47:34 +00005958 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5959 next=(PixelPacket *) RelinquishMagickMemory(next);
5960
5961 length=image->columns;
5962
5963 if (logging != MagickFalse)
5964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5965 " Delete original image");
5966
5967 DeleteImageFromList(&image);
5968
5969 image=large_image;
5970
5971 mng_info->image=image;
5972
5973 /* magnify the columns */
5974 if (logging != MagickFalse)
5975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005976 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00005977
cristybb503372010-05-27 20:51:26 +00005978 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005979 {
5980 register PixelPacket
5981 *pixels;
5982
5983 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5984 pixels=q+(image->columns-length);
5985 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00005986
cristybb503372010-05-27 20:51:26 +00005987 for (x=(ssize_t) (image->columns-length);
5988 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005989 {
glennrp7c7b3152011-04-26 04:01:27 +00005990 /* To do: Rewrite using Get/Set***PixelComponent() */
5991
cristybb503372010-05-27 20:51:26 +00005992 if (x == (ssize_t) (image->columns-length))
5993 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005994
cristybb503372010-05-27 20:51:26 +00005995 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5996 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005997
cristybb503372010-05-27 20:51:26 +00005998 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
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)
cristy3ed852e2009-09-05 21:47:34 +00006002 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006003
cristy3ed852e2009-09-05 21:47:34 +00006004 else
cristybb503372010-05-27 20:51:26 +00006005 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006006
cristy3ed852e2009-09-05 21:47:34 +00006007 for (i=0; i < m; i++)
6008 {
6009 if (magn_methx <= 1)
6010 {
6011 /* replicate previous */
6012 *q=(*pixels);
6013 }
glennrp47b9dd52010-11-24 18:12:06 +00006014
cristy3ed852e2009-09-05 21:47:34 +00006015 else if (magn_methx == 2 || magn_methx == 4)
6016 {
6017 if (i == 0)
6018 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006019
cristy3ed852e2009-09-05 21:47:34 +00006020 else
6021 {
6022 /* Interpolate */
6023 (*q).red=(QM) ((2*i*((*n).red
6024 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00006025 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00006026 (*q).green=(QM) ((2*i*((*n).green
6027 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00006028 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00006029 (*q).blue=(QM) ((2*i*((*n).blue
6030 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00006031 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00006032 if (image->matte != MagickFalse)
6033 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006034 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006035 +(*pixels).opacity);
6036 }
glennrp47b9dd52010-11-24 18:12:06 +00006037
cristy3ed852e2009-09-05 21:47:34 +00006038 if (magn_methx == 4)
6039 {
6040 /* Replicate nearest */
6041 if (i <= ((m+1) << 1))
6042 (*q).opacity=(*pixels).opacity+0;
6043 else
6044 (*q).opacity=(*n).opacity+0;
6045 }
6046 }
glennrp47b9dd52010-11-24 18:12:06 +00006047
cristy3ed852e2009-09-05 21:47:34 +00006048 else /* if (magn_methx == 3 || magn_methx == 5) */
6049 {
6050 /* Replicate nearest */
6051 if (i <= ((m+1) << 1))
6052 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006053
cristy3ed852e2009-09-05 21:47:34 +00006054 else
6055 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00006056
cristy3ed852e2009-09-05 21:47:34 +00006057 if (magn_methx == 5)
6058 {
6059 /* Interpolate */
6060 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006061 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006062 +(*pixels).opacity);
6063 }
6064 }
6065 q++;
6066 }
6067 n++;
6068 p++;
6069 }
glennrp47b9dd52010-11-24 18:12:06 +00006070
cristy3ed852e2009-09-05 21:47:34 +00006071 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6072 break;
6073 }
glennrp3faa9a32011-04-23 14:00:25 +00006074#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006075 if (magn_methx != 1 || magn_methy != 1)
6076 {
6077 /*
6078 Rescale pixels to Quantum
6079 */
cristybb503372010-05-27 20:51:26 +00006080 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006081 {
6082 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006083
cristybb503372010-05-27 20:51:26 +00006084 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006085 {
glennrp7c7b3152011-04-26 04:01:27 +00006086 SetRedPixelComponent(q,ScaleShortToQuantum(
6087 GetRedPixelComponent(q));
6088 SetGreenPixelComponent(q,ScaleShortToQuantum(
6089 GetGreenPixelComponent(q));
6090 SetBluePixelComponent(q,ScaleShortToQuantum(
6091 GetBluePixelComponent(q));
6092 SetOpacityPixelComponent(q,ScaleShortToQuantum(
6093 GetOpacityPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +00006094 q++;
6095 }
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6098 break;
6099 }
6100 }
6101#endif
6102 if (logging != MagickFalse)
6103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6104 " Finished MAGN processing");
6105 }
6106 }
6107
6108 /*
6109 Crop_box is with respect to the upper left corner of the MNG.
6110 */
6111 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6112 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6113 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6114 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6115 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6116 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6117 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6118 if ((crop_box.left != (mng_info->image_box.left
6119 +mng_info->x_off[object_id])) ||
6120 (crop_box.right != (mng_info->image_box.right
6121 +mng_info->x_off[object_id])) ||
6122 (crop_box.top != (mng_info->image_box.top
6123 +mng_info->y_off[object_id])) ||
6124 (crop_box.bottom != (mng_info->image_box.bottom
6125 +mng_info->y_off[object_id])))
6126 {
6127 if (logging != MagickFalse)
6128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6129 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006130
cristy3ed852e2009-09-05 21:47:34 +00006131 if ((crop_box.left < crop_box.right) &&
6132 (crop_box.top < crop_box.bottom))
6133 {
6134 Image
6135 *im;
6136
6137 RectangleInfo
6138 crop_info;
6139
6140 /*
6141 Crop_info is with respect to the upper left corner of
6142 the image.
6143 */
6144 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6145 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006146 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6147 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006148 image->page.width=image->columns;
6149 image->page.height=image->rows;
6150 image->page.x=0;
6151 image->page.y=0;
6152 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006153
cristy3ed852e2009-09-05 21:47:34 +00006154 if (im != (Image *) NULL)
6155 {
6156 image->columns=im->columns;
6157 image->rows=im->rows;
6158 im=DestroyImage(im);
6159 image->page.width=image->columns;
6160 image->page.height=image->rows;
6161 image->page.x=crop_box.left;
6162 image->page.y=crop_box.top;
6163 }
6164 }
glennrp47b9dd52010-11-24 18:12:06 +00006165
cristy3ed852e2009-09-05 21:47:34 +00006166 else
6167 {
6168 /*
6169 No pixels in crop area. The MNG spec still requires
6170 a layer, though, so make a single transparent pixel in
6171 the top left corner.
6172 */
6173 image->columns=1;
6174 image->rows=1;
6175 image->colors=2;
6176 (void) SetImageBackgroundColor(image);
6177 image->page.width=1;
6178 image->page.height=1;
6179 image->page.x=0;
6180 image->page.y=0;
6181 }
6182 }
6183#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6184 image=mng_info->image;
6185#endif
6186 }
6187
glennrp2b013e42010-11-24 16:55:50 +00006188#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6189 /* PNG does not handle depths greater than 16 so reduce it even
6190 * if lossy
6191 */
6192 if (image->depth > 16)
6193 image->depth=16;
6194#endif
6195
glennrp3faa9a32011-04-23 14:00:25 +00006196#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006197 if (LosslessReduceDepthOK(image) != MagickFalse)
6198 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006199#endif
glennrpd6afd542010-11-19 01:53:05 +00006200
cristy3ed852e2009-09-05 21:47:34 +00006201 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006202
cristy3ed852e2009-09-05 21:47:34 +00006203 if (image_info->number_scenes != 0)
6204 {
6205 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006206 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006207 break;
6208 }
glennrpd6afd542010-11-19 01:53:05 +00006209
cristy3ed852e2009-09-05 21:47:34 +00006210 if (logging != MagickFalse)
6211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6212 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006213
cristy3ed852e2009-09-05 21:47:34 +00006214 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006215
cristy3ed852e2009-09-05 21:47:34 +00006216 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006217
cristy3ed852e2009-09-05 21:47:34 +00006218 if (logging != MagickFalse)
6219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6220 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006221
cristy3ed852e2009-09-05 21:47:34 +00006222#if defined(MNG_INSERT_LAYERS)
6223 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6224 (mng_info->mng_height))
6225 {
6226 /*
6227 Insert a background layer if nothing else was found.
6228 */
6229 if (logging != MagickFalse)
6230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6231 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006232
cristy3ed852e2009-09-05 21:47:34 +00006233 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6234 {
6235 /*
6236 Allocate next image structure.
6237 */
6238 AcquireNextImage(image_info,image);
6239 if (GetNextImageInList(image) == (Image *) NULL)
6240 {
6241 image=DestroyImageList(image);
6242 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006243
cristy3ed852e2009-09-05 21:47:34 +00006244 if (logging != MagickFalse)
6245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6246 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006247
cristy3ed852e2009-09-05 21:47:34 +00006248 return((Image *) NULL);
6249 }
6250 image=SyncNextImageInList(image);
6251 }
6252 image->columns=mng_info->mng_width;
6253 image->rows=mng_info->mng_height;
6254 image->page.width=mng_info->mng_width;
6255 image->page.height=mng_info->mng_height;
6256 image->page.x=0;
6257 image->page.y=0;
6258 image->background_color=mng_background_color;
6259 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006260
cristy3ed852e2009-09-05 21:47:34 +00006261 if (image_info->ping == MagickFalse)
6262 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006263
cristy3ed852e2009-09-05 21:47:34 +00006264 mng_info->image_found++;
6265 }
6266#endif
6267 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006268
cristy3ed852e2009-09-05 21:47:34 +00006269 if (mng_iterations == 1)
6270 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006271
cristy3ed852e2009-09-05 21:47:34 +00006272 while (GetPreviousImageInList(image) != (Image *) NULL)
6273 {
6274 image_count++;
6275 if (image_count > 10*mng_info->image_found)
6276 {
6277 if (logging != MagickFalse)
6278 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006279
cristy3ed852e2009-09-05 21:47:34 +00006280 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6281 CoderError,"Linked list is corrupted, beginning of list not found",
6282 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristy3ed852e2009-09-05 21:47:34 +00006284 return((Image *) NULL);
6285 }
glennrp0fe50b42010-11-16 03:52:51 +00006286
cristy3ed852e2009-09-05 21:47:34 +00006287 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006288
cristy3ed852e2009-09-05 21:47:34 +00006289 if (GetNextImageInList(image) == (Image *) NULL)
6290 {
6291 if (logging != MagickFalse)
6292 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006293
cristy3ed852e2009-09-05 21:47:34 +00006294 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6295 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6296 image_info->filename);
6297 }
6298 }
glennrp47b9dd52010-11-24 18:12:06 +00006299
cristy3ed852e2009-09-05 21:47:34 +00006300 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6301 GetNextImageInList(image) ==
6302 (Image *) NULL)
6303 {
6304 if (logging != MagickFalse)
6305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6306 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006307
cristy3ed852e2009-09-05 21:47:34 +00006308 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6309 CoderError,"image->next for first image is NULL but shouldn't be.",
6310 "`%s'",image_info->filename);
6311 }
glennrp47b9dd52010-11-24 18:12:06 +00006312
cristy3ed852e2009-09-05 21:47:34 +00006313 if (mng_info->image_found == 0)
6314 {
6315 if (logging != MagickFalse)
6316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6317 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006318
cristy3ed852e2009-09-05 21:47:34 +00006319 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6320 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006321
cristy3ed852e2009-09-05 21:47:34 +00006322 if (image != (Image *) NULL)
6323 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006324
cristy3ed852e2009-09-05 21:47:34 +00006325 MngInfoFreeStruct(mng_info,&have_mng_structure);
6326 return((Image *) NULL);
6327 }
6328
6329 if (mng_info->ticks_per_second)
6330 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6331 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006332
cristy3ed852e2009-09-05 21:47:34 +00006333 else
6334 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006335
cristy3ed852e2009-09-05 21:47:34 +00006336 /* Find final nonzero image delay */
6337 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006338
cristy3ed852e2009-09-05 21:47:34 +00006339 while (GetNextImageInList(image) != (Image *) NULL)
6340 {
6341 if (image->delay)
6342 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006343
cristy3ed852e2009-09-05 21:47:34 +00006344 image=GetNextImageInList(image);
6345 }
glennrp0fe50b42010-11-16 03:52:51 +00006346
cristy3ed852e2009-09-05 21:47:34 +00006347 if (final_delay < final_image_delay)
6348 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006349
cristy3ed852e2009-09-05 21:47:34 +00006350 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006351
cristy3ed852e2009-09-05 21:47:34 +00006352 if (logging != MagickFalse)
6353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006354 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6355 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006356
cristy3ed852e2009-09-05 21:47:34 +00006357 if (logging != MagickFalse)
6358 {
6359 int
6360 scene;
6361
6362 scene=0;
6363 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006364
cristy3ed852e2009-09-05 21:47:34 +00006365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6366 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006367
cristy3ed852e2009-09-05 21:47:34 +00006368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006369 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006370
cristy3ed852e2009-09-05 21:47:34 +00006371 while (GetNextImageInList(image) != (Image *) NULL)
6372 {
6373 image=GetNextImageInList(image);
6374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006375 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006376 }
6377 }
6378
6379 image=GetFirstImageInList(image);
6380#ifdef MNG_COALESCE_LAYERS
6381 if (insert_layers)
6382 {
6383 Image
6384 *next_image,
6385 *next;
6386
cristybb503372010-05-27 20:51:26 +00006387 size_t
cristy3ed852e2009-09-05 21:47:34 +00006388 scene;
6389
6390 if (logging != MagickFalse)
6391 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006392
cristy3ed852e2009-09-05 21:47:34 +00006393 scene=image->scene;
6394 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006395
cristy3ed852e2009-09-05 21:47:34 +00006396 if (next_image == (Image *) NULL)
6397 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006398
cristy3ed852e2009-09-05 21:47:34 +00006399 image=DestroyImageList(image);
6400 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006401
cristy3ed852e2009-09-05 21:47:34 +00006402 for (next=image; next != (Image *) NULL; next=next_image)
6403 {
6404 next->page.width=mng_info->mng_width;
6405 next->page.height=mng_info->mng_height;
6406 next->page.x=0;
6407 next->page.y=0;
6408 next->scene=scene++;
6409 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006410
cristy3ed852e2009-09-05 21:47:34 +00006411 if (next_image == (Image *) NULL)
6412 break;
glennrp47b9dd52010-11-24 18:12:06 +00006413
cristy3ed852e2009-09-05 21:47:34 +00006414 if (next->delay == 0)
6415 {
6416 scene--;
6417 next_image->previous=GetPreviousImageInList(next);
6418 if (GetPreviousImageInList(next) == (Image *) NULL)
6419 image=next_image;
6420 else
6421 next->previous->next=next_image;
6422 next=DestroyImage(next);
6423 }
6424 }
6425 }
6426#endif
6427
6428 while (GetNextImageInList(image) != (Image *) NULL)
6429 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristy3ed852e2009-09-05 21:47:34 +00006431 image->dispose=BackgroundDispose;
6432
6433 if (logging != MagickFalse)
6434 {
6435 int
6436 scene;
6437
6438 scene=0;
6439 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006440
cristy3ed852e2009-09-05 21:47:34 +00006441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6442 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006443
cristy3ed852e2009-09-05 21:47:34 +00006444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006445 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6446 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006447
cristy3ed852e2009-09-05 21:47:34 +00006448 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006449 {
6450 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006451
cristyf2faecf2010-05-28 19:19:36 +00006452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006453 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6454 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006455 }
6456 }
glennrp47b9dd52010-11-24 18:12:06 +00006457
cristy3ed852e2009-09-05 21:47:34 +00006458 image=GetFirstImageInList(image);
6459 MngInfoFreeStruct(mng_info,&have_mng_structure);
6460 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006461
cristy3ed852e2009-09-05 21:47:34 +00006462 if (logging != MagickFalse)
6463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006464
cristy3ed852e2009-09-05 21:47:34 +00006465 return(GetFirstImageInList(image));
6466}
glennrp25c1e2b2010-03-25 01:39:56 +00006467#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006468static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6469{
6470 printf("Your PNG library is too old: You have libpng-%s\n",
6471 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006472
cristy3ed852e2009-09-05 21:47:34 +00006473 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6474 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristy3ed852e2009-09-05 21:47:34 +00006476 return(Image *) NULL;
6477}
glennrp47b9dd52010-11-24 18:12:06 +00006478
cristy3ed852e2009-09-05 21:47:34 +00006479static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6480{
6481 return(ReadPNGImage(image_info,exception));
6482}
glennrp25c1e2b2010-03-25 01:39:56 +00006483#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006484#endif
6485
6486/*
6487%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6488% %
6489% %
6490% %
6491% R e g i s t e r P N G I m a g e %
6492% %
6493% %
6494% %
6495%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6496%
6497% RegisterPNGImage() adds properties for the PNG image format to
6498% the list of supported formats. The properties include the image format
6499% tag, a method to read and/or write the format, whether the format
6500% supports the saving of more than one frame to the same file or blob,
6501% whether the format supports native in-memory I/O, and a brief
6502% description of the format.
6503%
6504% The format of the RegisterPNGImage method is:
6505%
cristybb503372010-05-27 20:51:26 +00006506% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006507%
6508*/
cristybb503372010-05-27 20:51:26 +00006509ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006510{
6511 char
6512 version[MaxTextExtent];
6513
6514 MagickInfo
6515 *entry;
6516
6517 static const char
6518 *PNGNote=
6519 {
6520 "See http://www.libpng.org/ for details about the PNG format."
6521 },
glennrp47b9dd52010-11-24 18:12:06 +00006522
cristy3ed852e2009-09-05 21:47:34 +00006523 *JNGNote=
6524 {
6525 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6526 "format."
6527 },
glennrp47b9dd52010-11-24 18:12:06 +00006528
cristy3ed852e2009-09-05 21:47:34 +00006529 *MNGNote=
6530 {
6531 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6532 "format."
6533 };
6534
6535 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006536
cristy3ed852e2009-09-05 21:47:34 +00006537#if defined(PNG_LIBPNG_VER_STRING)
6538 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6539 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006540
cristy3ed852e2009-09-05 21:47:34 +00006541 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6542 {
6543 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6544 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6545 MaxTextExtent);
6546 }
6547#endif
glennrp47b9dd52010-11-24 18:12:06 +00006548
cristy3ed852e2009-09-05 21:47:34 +00006549 entry=SetMagickInfo("MNG");
6550 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00006551
cristy3ed852e2009-09-05 21:47:34 +00006552#if defined(MAGICKCORE_PNG_DELEGATE)
6553 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6554 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6555#endif
glennrp47b9dd52010-11-24 18:12:06 +00006556
cristy3ed852e2009-09-05 21:47:34 +00006557 entry->magick=(IsImageFormatHandler *) IsMNG;
6558 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00006559
cristy3ed852e2009-09-05 21:47:34 +00006560 if (*version != '\0')
6561 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563 entry->module=ConstantString("PNG");
6564 entry->note=ConstantString(MNGNote);
6565 (void) RegisterMagickInfo(entry);
6566
6567 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristy3ed852e2009-09-05 21:47:34 +00006569#if defined(MAGICKCORE_PNG_DELEGATE)
6570 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6571 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6572#endif
glennrp47b9dd52010-11-24 18:12:06 +00006573
cristy3ed852e2009-09-05 21:47:34 +00006574 entry->magick=(IsImageFormatHandler *) IsPNG;
6575 entry->adjoin=MagickFalse;
6576 entry->description=ConstantString("Portable Network Graphics");
6577 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006578
cristy3ed852e2009-09-05 21:47:34 +00006579 if (*version != '\0')
6580 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006581
cristy3ed852e2009-09-05 21:47:34 +00006582 entry->note=ConstantString(PNGNote);
6583 (void) RegisterMagickInfo(entry);
6584
6585 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00006586
cristy3ed852e2009-09-05 21:47:34 +00006587#if defined(MAGICKCORE_PNG_DELEGATE)
6588 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6589 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6590#endif
glennrp47b9dd52010-11-24 18:12:06 +00006591
cristy3ed852e2009-09-05 21:47:34 +00006592 entry->magick=(IsImageFormatHandler *) IsPNG;
6593 entry->adjoin=MagickFalse;
6594 entry->description=ConstantString(
6595 "8-bit indexed with optional binary transparency");
6596 entry->module=ConstantString("PNG");
6597 (void) RegisterMagickInfo(entry);
6598
6599 entry=SetMagickInfo("PNG24");
6600 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006601
cristy3ed852e2009-09-05 21:47:34 +00006602#if defined(ZLIB_VERSION)
6603 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6604 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006605
cristy3ed852e2009-09-05 21:47:34 +00006606 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6607 {
6608 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6609 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6610 }
6611#endif
glennrp47b9dd52010-11-24 18:12:06 +00006612
cristy3ed852e2009-09-05 21:47:34 +00006613 if (*version != '\0')
6614 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006615
cristy3ed852e2009-09-05 21:47:34 +00006616#if defined(MAGICKCORE_PNG_DELEGATE)
6617 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6618 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6619#endif
glennrp47b9dd52010-11-24 18:12:06 +00006620
cristy3ed852e2009-09-05 21:47:34 +00006621 entry->magick=(IsImageFormatHandler *) IsPNG;
6622 entry->adjoin=MagickFalse;
6623 entry->description=ConstantString("opaque 24-bit RGB");
6624 entry->module=ConstantString("PNG");
6625 (void) RegisterMagickInfo(entry);
6626
6627 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00006628
cristy3ed852e2009-09-05 21:47:34 +00006629#if defined(MAGICKCORE_PNG_DELEGATE)
6630 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6631 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6632#endif
glennrp47b9dd52010-11-24 18:12:06 +00006633
cristy3ed852e2009-09-05 21:47:34 +00006634 entry->magick=(IsImageFormatHandler *) IsPNG;
6635 entry->adjoin=MagickFalse;
6636 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6637 entry->module=ConstantString("PNG");
6638 (void) RegisterMagickInfo(entry);
6639
6640 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006641
cristy3ed852e2009-09-05 21:47:34 +00006642#if defined(JNG_SUPPORTED)
6643#if defined(MAGICKCORE_PNG_DELEGATE)
6644 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6645 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6646#endif
6647#endif
glennrp47b9dd52010-11-24 18:12:06 +00006648
cristy3ed852e2009-09-05 21:47:34 +00006649 entry->magick=(IsImageFormatHandler *) IsJNG;
6650 entry->adjoin=MagickFalse;
6651 entry->description=ConstantString("JPEG Network Graphics");
6652 entry->module=ConstantString("PNG");
6653 entry->note=ConstantString(JNGNote);
6654 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00006655
cristy18b17442009-10-25 18:36:48 +00006656#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006657 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00006658#endif
glennrp47b9dd52010-11-24 18:12:06 +00006659
cristy3ed852e2009-09-05 21:47:34 +00006660 return(MagickImageCoderSignature);
6661}
6662
6663/*
6664%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6665% %
6666% %
6667% %
6668% U n r e g i s t e r P N G I m a g e %
6669% %
6670% %
6671% %
6672%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6673%
6674% UnregisterPNGImage() removes format registrations made by the
6675% PNG module from the list of supported formats.
6676%
6677% The format of the UnregisterPNGImage method is:
6678%
6679% UnregisterPNGImage(void)
6680%
6681*/
6682ModuleExport void UnregisterPNGImage(void)
6683{
6684 (void) UnregisterMagickInfo("MNG");
6685 (void) UnregisterMagickInfo("PNG");
6686 (void) UnregisterMagickInfo("PNG8");
6687 (void) UnregisterMagickInfo("PNG24");
6688 (void) UnregisterMagickInfo("PNG32");
6689 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006690
cristy3ed852e2009-09-05 21:47:34 +00006691#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006692 if (ping_semaphore != (SemaphoreInfo *) NULL)
6693 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006694#endif
6695}
6696
6697#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006698#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006699/*
6700%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6701% %
6702% %
6703% %
6704% W r i t e M N G I m a g e %
6705% %
6706% %
6707% %
6708%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6709%
6710% WriteMNGImage() writes an image in the Portable Network Graphics
6711% Group's "Multiple-image Network Graphics" encoded image format.
6712%
6713% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6714%
6715% The format of the WriteMNGImage method is:
6716%
6717% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6718%
6719% A description of each parameter follows.
6720%
6721% o image_info: the image info.
6722%
6723% o image: The image.
6724%
6725%
6726% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6727% "To do" under ReadPNGImage):
6728%
cristy3ed852e2009-09-05 21:47:34 +00006729% Preserve all unknown and not-yet-handled known chunks found in input
6730% PNG file and copy them into output PNG files according to the PNG
6731% copying rules.
6732%
6733% Write the iCCP chunk at MNG level when (icc profile length > 0)
6734%
6735% Improve selection of color type (use indexed-colour or indexed-colour
6736% with tRNS when 256 or fewer unique RGBA values are present).
6737%
6738% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6739% This will be complicated if we limit ourselves to generating MNG-LC
6740% files. For now we ignore disposal method 3 and simply overlay the next
6741% image on it.
6742%
6743% Check for identical PLTE's or PLTE/tRNS combinations and use a
6744% global MNG PLTE or PLTE/tRNS combination when appropriate.
6745% [mostly done 15 June 1999 but still need to take care of tRNS]
6746%
6747% Check for identical sRGB and replace with a global sRGB (and remove
6748% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6749% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6750% local gAMA/cHRM with local sRGB if appropriate).
6751%
6752% Check for identical sBIT chunks and write global ones.
6753%
6754% Provide option to skip writing the signature tEXt chunks.
6755%
6756% Use signatures to detect identical objects and reuse the first
6757% instance of such objects instead of writing duplicate objects.
6758%
6759% Use a smaller-than-32k value of compression window size when
6760% appropriate.
6761%
6762% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6763% ancillary text chunks and save profiles.
6764%
6765% Provide an option to force LC files (to ensure exact framing rate)
6766% instead of VLC.
6767%
6768% Provide an option to force VLC files instead of LC, even when offsets
6769% are present. This will involve expanding the embedded images with a
6770% transparent region at the top and/or left.
6771*/
6772
cristy3ed852e2009-09-05 21:47:34 +00006773static void
glennrpcf002022011-01-30 02:38:15 +00006774Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00006775 png_info *ping_info, unsigned char *profile_type, unsigned char
6776 *profile_description, unsigned char *profile_data, png_uint_32 length)
6777{
cristy3ed852e2009-09-05 21:47:34 +00006778 png_textp
6779 text;
6780
cristybb503372010-05-27 20:51:26 +00006781 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006782 i;
6783
6784 unsigned char
6785 *sp;
6786
6787 png_charp
6788 dp;
6789
6790 png_uint_32
6791 allocated_length,
6792 description_length;
6793
6794 unsigned char
6795 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006796
cristy3ed852e2009-09-05 21:47:34 +00006797 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6798 return;
6799
6800 if (image_info->verbose)
6801 {
glennrp0fe50b42010-11-16 03:52:51 +00006802 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6803 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006804 }
glennrp0fe50b42010-11-16 03:52:51 +00006805
cristy3ed852e2009-09-05 21:47:34 +00006806 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6807 description_length=(png_uint_32) strlen((const char *) profile_description);
6808 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6809 + description_length);
6810 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6811 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6812 text[0].key[0]='\0';
6813 (void) ConcatenateMagickString(text[0].key,
6814 "Raw profile type ",MaxTextExtent);
6815 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6816 sp=profile_data;
6817 dp=text[0].text;
6818 *dp++='\n';
6819 (void) CopyMagickString(dp,(const char *) profile_description,
6820 allocated_length);
6821 dp+=description_length;
6822 *dp++='\n';
6823 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006824 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006825 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00006826
cristybb503372010-05-27 20:51:26 +00006827 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006828 {
6829 if (i%36 == 0)
6830 *dp++='\n';
6831 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6832 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6833 }
glennrp47b9dd52010-11-24 18:12:06 +00006834
cristy3ed852e2009-09-05 21:47:34 +00006835 *dp++='\n';
6836 *dp='\0';
6837 text[0].text_length=(png_size_t) (dp-text[0].text);
6838 text[0].compression=image_info->compression == NoCompression ||
6839 (image_info->compression == UndefinedCompression &&
6840 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00006841
cristy3ed852e2009-09-05 21:47:34 +00006842 if (text[0].text_length <= allocated_length)
6843 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00006844
cristy3ed852e2009-09-05 21:47:34 +00006845 png_free(ping,text[0].text);
6846 png_free(ping,text[0].key);
6847 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006848}
6849
glennrpcf002022011-01-30 02:38:15 +00006850static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00006851 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00006852{
6853 char
6854 *name;
6855
6856 const StringInfo
6857 *profile;
6858
6859 unsigned char
6860 *data;
6861
6862 png_uint_32 length;
6863
6864 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00006865
6866 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6867 {
cristy3ed852e2009-09-05 21:47:34 +00006868 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00006869
cristy3ed852e2009-09-05 21:47:34 +00006870 if (profile != (const StringInfo *) NULL)
6871 {
6872 StringInfo
glennrpcf002022011-01-30 02:38:15 +00006873 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00006874
glennrp47b9dd52010-11-24 18:12:06 +00006875 if (LocaleNCompare(name,string,11) == 0)
6876 {
6877 if (logging != MagickFalse)
6878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6879 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00006880
glennrpcf002022011-01-30 02:38:15 +00006881 ping_profile=CloneStringInfo(profile);
6882 data=GetStringInfoDatum(ping_profile),
6883 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006884 data[4]=data[3];
6885 data[3]=data[2];
6886 data[2]=data[1];
6887 data[1]=data[0];
6888 (void) WriteBlobMSBULong(image,length-5); /* data length */
6889 (void) WriteBlob(image,length-1,data+1);
6890 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00006891 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006892 }
cristy3ed852e2009-09-05 21:47:34 +00006893 }
glennrp47b9dd52010-11-24 18:12:06 +00006894
cristy3ed852e2009-09-05 21:47:34 +00006895 name=GetNextImageProfile(image);
6896 }
glennrp47b9dd52010-11-24 18:12:06 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 return(MagickTrue);
6899}
6900
glennrpb9cfe272010-12-21 15:08:06 +00006901
cristy3ed852e2009-09-05 21:47:34 +00006902/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00006903static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6904 const ImageInfo *IMimage_info,Image *IMimage)
6905{
6906 Image
6907 *image;
6908
6909 ImageInfo
6910 *image_info;
6911
cristy3ed852e2009-09-05 21:47:34 +00006912 char
6913 s[2];
6914
6915 const char
6916 *name,
6917 *property,
6918 *value;
6919
6920 const StringInfo
6921 *profile;
6922
cristy3ed852e2009-09-05 21:47:34 +00006923 int
cristy3ed852e2009-09-05 21:47:34 +00006924 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006925 pass;
6926
glennrpe9c26dc2010-05-30 01:56:35 +00006927 png_byte
6928 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006929
glennrp39992b42010-11-14 00:03:43 +00006930 png_color
6931 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00006932
glennrp5af765f2010-03-30 11:12:18 +00006933 png_color_16
6934 ping_background,
6935 ping_trans_color;
6936
cristy3ed852e2009-09-05 21:47:34 +00006937 png_info
6938 *ping_info;
6939
6940 png_struct
6941 *ping;
6942
glennrp5af765f2010-03-30 11:12:18 +00006943 png_uint_32
6944 ping_height,
6945 ping_width;
6946
cristybb503372010-05-27 20:51:26 +00006947 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006948 y;
6949
6950 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00006951 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00006952 logging,
glennrp58e01762011-01-07 15:28:54 +00006953 matte,
6954
glennrpda8f3a72011-02-27 23:54:12 +00006955 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00006956 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00006957 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00006958 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00006959 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00006960 ping_have_bKGD,
6961 ping_have_pHYs,
6962 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00006963
6964 ping_exclude_bKGD,
6965 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00006966 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00006967 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00006968 ping_exclude_gAMA,
6969 ping_exclude_iCCP,
6970 /* ping_exclude_iTXt, */
6971 ping_exclude_oFFs,
6972 ping_exclude_pHYs,
6973 ping_exclude_sRGB,
6974 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00006975 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00006976 ping_exclude_vpAg,
6977 ping_exclude_zCCP, /* hex-encoded iCCP */
6978 ping_exclude_zTXt,
6979
glennrp8d3d6e52011-04-19 04:39:51 +00006980 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00006981 ping_need_colortype_warning,
6982
glennrp82b3c532011-03-22 19:20:54 +00006983 status,
glennrpd3371642011-03-22 19:42:23 +00006984 tried_333,
6985 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00006986
6987 QuantumInfo
6988 *quantum_info;
6989
cristybb503372010-05-27 20:51:26 +00006990 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006991 i,
6992 x;
6993
6994 unsigned char
glennrpcf002022011-01-30 02:38:15 +00006995 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00006996
glennrp5af765f2010-03-30 11:12:18 +00006997 volatile int
glennrpf09bded2011-01-08 01:15:59 +00006998 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00006999 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007000 ping_color_type,
7001 ping_interlace_method,
7002 ping_compression_method,
7003 ping_filter_method,
7004 ping_num_trans;
7005
cristybb503372010-05-27 20:51:26 +00007006 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007007 image_depth,
7008 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007009
cristybb503372010-05-27 20:51:26 +00007010 size_t
cristy3ed852e2009-09-05 21:47:34 +00007011 quality,
7012 rowbytes,
7013 save_image_depth;
7014
glennrpdfd70802010-11-14 01:23:35 +00007015 int
glennrpfd05d622011-02-25 04:10:33 +00007016 j,
glennrpf09bded2011-01-08 01:15:59 +00007017 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007018 number_opaque,
7019 number_semitransparent,
7020 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007021 ping_pHYs_unit_type;
7022
7023 png_uint_32
7024 ping_pHYs_x_resolution,
7025 ping_pHYs_y_resolution;
7026
cristy3ed852e2009-09-05 21:47:34 +00007027 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007028 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007029
glennrpb9cfe272010-12-21 15:08:06 +00007030 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7031 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007032 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007033 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007034
cristy3ed852e2009-09-05 21:47:34 +00007035#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007036 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007037#endif
7038
glennrp5af765f2010-03-30 11:12:18 +00007039 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007040 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007041 ping_color_type=0,
7042 ping_interlace_method=0,
7043 ping_compression_method=0,
7044 ping_filter_method=0,
7045 ping_num_trans = 0;
7046
7047 ping_background.red = 0;
7048 ping_background.green = 0;
7049 ping_background.blue = 0;
7050 ping_background.gray = 0;
7051 ping_background.index = 0;
7052
7053 ping_trans_color.red=0;
7054 ping_trans_color.green=0;
7055 ping_trans_color.blue=0;
7056 ping_trans_color.gray=0;
7057
glennrpdfd70802010-11-14 01:23:35 +00007058 ping_pHYs_unit_type = 0;
7059 ping_pHYs_x_resolution = 0;
7060 ping_pHYs_y_resolution = 0;
7061
glennrpda8f3a72011-02-27 23:54:12 +00007062 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007063 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007064 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007065 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007066 ping_have_bKGD=MagickFalse;
7067 ping_have_pHYs=MagickFalse;
7068 ping_have_tRNS=MagickFalse;
7069
glennrp0e8ea192010-12-24 18:00:33 +00007070 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7071 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007072 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007073 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007074 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007075 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7076 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7077 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7078 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7079 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7080 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007081 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007082 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7083 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7084 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7085
glennrp8d3d6e52011-04-19 04:39:51 +00007086 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007087 ping_need_colortype_warning = MagickFalse;
7088
glennrp8bb3a022010-12-13 20:40:04 +00007089 number_opaque = 0;
7090 number_semitransparent = 0;
7091 number_transparent = 0;
7092
glennrpfd05d622011-02-25 04:10:33 +00007093 if (logging != MagickFalse)
7094 {
7095 if (image->storage_class == UndefinedClass)
7096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7097 " storage_class=UndefinedClass");
7098 if (image->storage_class == DirectClass)
7099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7100 " storage_class=DirectClass");
7101 if (image->storage_class == PseudoClass)
7102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7103 " storage_class=PseudoClass");
7104 }
glennrp28af3712011-04-06 18:07:30 +00007105
7106 if (image->storage_class != PseudoClass && image->colormap != NULL)
7107 {
7108 /* Free the bogus colormap; it can cause trouble later */
7109 if (logging != MagickFalse)
7110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7111 " Freeing bogus colormap");
7112 (void *) RelinquishMagickMemory(image->colormap);
7113 image->colormap=NULL;
7114 }
glennrpfd05d622011-02-25 04:10:33 +00007115
cristy3ed852e2009-09-05 21:47:34 +00007116 if (image->colorspace != RGBColorspace)
7117 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007118
glennrp3241bd02010-12-12 04:36:28 +00007119 /*
7120 Sometimes we get PseudoClass images whose RGB values don't match
7121 the colors in the colormap. This code syncs the RGB values.
7122 */
7123 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7124 (void) SyncImage(image);
7125
glennrpa6a06632011-01-19 15:15:34 +00007126#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7127 if (image->depth > 8)
7128 {
7129 if (logging != MagickFalse)
7130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7131 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7132
7133 image->depth=8;
7134 }
7135#endif
7136
glennrp67b9c1a2011-04-22 18:47:36 +00007137#if 0 /* To do: Option to use the original colormap */
7138 if (ping_preserve_colormap != MagickFalse)
7139 {
7140 }
7141#endif
7142
glennrp3faa9a32011-04-23 14:00:25 +00007143#if 0 /* To do: respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007144 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7145 {
glennrp9d0ea4d2011-04-22 18:35:57 +00007146 }
glennrp67b9c1a2011-04-22 18:47:36 +00007147#endif
glennrp9d0ea4d2011-04-22 18:35:57 +00007148
glennrp67b9c1a2011-04-22 18:47:36 +00007149 /* To do: set to next higher multiple of 8 */
7150 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007151 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007152
glennrp2b013e42010-11-24 16:55:50 +00007153#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7154 /* PNG does not handle depths greater than 16 so reduce it even
7155 * if lossy
7156 */
7157 if (image->depth > 16)
7158 image->depth=16;
7159#endif
7160
glennrp3faa9a32011-04-23 14:00:25 +00007161#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007162 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007163 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007164 image->depth = 8;
7165#endif
7166
glennrpc8c2f062011-02-25 19:00:33 +00007167 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007168 * we reduce the transparency to binary and run again, then if there
7169 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7170 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7171 * palette. The final reduction can only fail if there are still 256
7172 * colors present and one of them has both transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007173 */
glennrp82b3c532011-03-22 19:20:54 +00007174
7175 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007176 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007177
glennrpd3371642011-03-22 19:42:23 +00007178 for (j=0; j<5; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007179 {
7180 /* BUILD_PALETTE
7181 *
7182 * Sometimes we get DirectClass images that have 256 colors or fewer.
7183 * This code will build a colormap.
7184 *
7185 * Also, sometimes we get PseudoClass images with an out-of-date
7186 * colormap. This code will replace the colormap with a new one.
7187 * Sometimes we get PseudoClass images that have more than 256 colors.
7188 * This code will delete the colormap and change the image to
7189 * DirectClass.
7190 *
7191 * If image->matte is MagickFalse, we ignore the opacity channel
7192 * even though it sometimes contains left-over non-opaque values.
7193 *
7194 * Also we gather some information (number of opaque, transparent,
7195 * and semitransparent pixels, and whether the image has any non-gray
7196 * pixels or only black-and-white pixels) that we might need later.
7197 *
7198 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7199 * we need to check for bogus non-opaque values, at least.
7200 */
glennrp3c218112010-11-27 15:31:26 +00007201
glennrpd71e86a2011-02-24 01:28:37 +00007202 ExceptionInfo
7203 *exception;
glennrp3c218112010-11-27 15:31:26 +00007204
glennrpd71e86a2011-02-24 01:28:37 +00007205 int
7206 n;
glennrp3c218112010-11-27 15:31:26 +00007207
glennrpd71e86a2011-02-24 01:28:37 +00007208 PixelPacket
7209 opaque[260],
7210 semitransparent[260],
7211 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007212
glennrpd71e86a2011-02-24 01:28:37 +00007213 register IndexPacket
7214 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007215
glennrpd71e86a2011-02-24 01:28:37 +00007216 register const PixelPacket
7217 *s,
7218 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007219
glennrpfd05d622011-02-25 04:10:33 +00007220 register PixelPacket
7221 *r;
7222
glennrpd71e86a2011-02-24 01:28:37 +00007223 if (logging != MagickFalse)
7224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7225 " Enter BUILD_PALETTE:");
7226
7227 if (logging != MagickFalse)
7228 {
glennrp03812ae2010-12-24 01:31:34 +00007229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007230 " image->columns=%.20g",(double) image->columns);
7231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7232 " image->rows=%.20g",(double) image->rows);
7233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7234 " image->matte=%.20g",(double) image->matte);
7235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7236 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007237
glennrpfd05d622011-02-25 04:10:33 +00007238 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007239 {
7240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007241 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007243 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007244
glennrpd71e86a2011-02-24 01:28:37 +00007245 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007246 {
glennrpd71e86a2011-02-24 01:28:37 +00007247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7248 " %d (%d,%d,%d,%d)",
7249 (int) i,
7250 (int) image->colormap[i].red,
7251 (int) image->colormap[i].green,
7252 (int) image->colormap[i].blue,
7253 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007254 }
glennrp2cc891a2010-12-24 13:44:32 +00007255
glennrpd71e86a2011-02-24 01:28:37 +00007256 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7257 {
7258 if (i > 255)
7259 {
7260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7261 " %d (%d,%d,%d,%d)",
7262 (int) i,
7263 (int) image->colormap[i].red,
7264 (int) image->colormap[i].green,
7265 (int) image->colormap[i].blue,
7266 (int) image->colormap[i].opacity);
7267 }
7268 }
glennrp03812ae2010-12-24 01:31:34 +00007269 }
glennrp7ddcc222010-12-11 05:01:05 +00007270
glennrpd71e86a2011-02-24 01:28:37 +00007271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7272 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007273
glennrpd71e86a2011-02-24 01:28:37 +00007274 if (image->colors == 0)
7275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7276 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007277
glennrp8d3d6e52011-04-19 04:39:51 +00007278 if (ping_preserve_colormap == MagickFalse)
7279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7280 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007281 }
7282
7283 exception=(&image->exception);
7284
7285 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007286 number_opaque = 0;
7287 number_semitransparent = 0;
7288 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007289
7290 for (y=0; y < (ssize_t) image->rows; y++)
7291 {
7292 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7293
7294 if (q == (PixelPacket *) NULL)
7295 break;
7296
7297 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007298 {
glennrpd71e86a2011-02-24 01:28:37 +00007299 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7300 {
7301 if (number_opaque < 259)
7302 {
7303 if (number_opaque == 0)
7304 {
7305 opaque[0]=*q;
7306 opaque[0].opacity=OpaqueOpacity;
7307 number_opaque=1;
7308 }
glennrp2cc891a2010-12-24 13:44:32 +00007309
glennrpd71e86a2011-02-24 01:28:37 +00007310 for (i=0; i< (ssize_t) number_opaque; i++)
7311 {
7312 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7313 break;
7314 }
glennrp7ddcc222010-12-11 05:01:05 +00007315
glennrpd71e86a2011-02-24 01:28:37 +00007316 if (i == (ssize_t) number_opaque &&
7317 number_opaque < 259)
7318 {
7319 number_opaque++;
7320 opaque[i] = *q;
7321 opaque[i].opacity = OpaqueOpacity;
7322 }
7323 }
7324 }
7325 else if (q->opacity == TransparentOpacity)
7326 {
7327 if (number_transparent < 259)
7328 {
7329 if (number_transparent == 0)
7330 {
7331 transparent[0]=*q;
glennrpa18d5bc2011-04-23 14:51:34 +00007332 ping_trans_color.red=
7333 (unsigned short) GetRedPixelComponent(q);
7334 ping_trans_color.green=
7335 (unsigned short) GetGreenPixelComponent(q);
7336 ping_trans_color.blue=
7337 (unsigned short) GetBluePixelComponent(q);
7338 ping_trans_color.gray=
7339 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007340 number_transparent = 1;
7341 }
7342
7343 for (i=0; i< (ssize_t) number_transparent; i++)
7344 {
7345 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7346 break;
7347 }
7348
7349 if (i == (ssize_t) number_transparent &&
7350 number_transparent < 259)
7351 {
7352 number_transparent++;
7353 transparent[i] = *q;
7354 }
7355 }
7356 }
7357 else
7358 {
7359 if (number_semitransparent < 259)
7360 {
7361 if (number_semitransparent == 0)
7362 {
7363 semitransparent[0]=*q;
7364 number_semitransparent = 1;
7365 }
7366
7367 for (i=0; i< (ssize_t) number_semitransparent; i++)
7368 {
7369 if (IsColorEqual(semitransparent+i,
7370 (PixelPacket *) q) &&
7371 q->opacity == semitransparent[i].opacity)
7372 break;
7373 }
7374
7375 if (i == (ssize_t) number_semitransparent &&
7376 number_semitransparent < 259)
7377 {
7378 number_semitransparent++;
7379 semitransparent[i] = *q;
7380 }
7381 }
7382 }
7383 q++;
7384 }
7385 }
7386
7387 if (ping_exclude_bKGD == MagickFalse)
7388 {
7389 /* Add the background color to the palette, if it
7390 * isn't already there.
7391 */
7392 for (i=0; i<number_opaque; i++)
7393 {
glennrpa080bc32011-03-11 18:03:44 +00007394 if (IsColorEqual(opaque+i, &image->background_color))
glennrpd71e86a2011-02-24 01:28:37 +00007395 break;
7396 }
7397
7398 if (number_opaque < 259 && i == number_opaque)
7399 {
7400 opaque[i]=image->background_color;
7401 opaque[i].opacity = OpaqueOpacity;
7402 number_opaque++;
7403 }
glennrpa080bc32011-03-11 18:03:44 +00007404 else if (logging != MagickFalse)
7405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7406 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007407 }
7408
7409 image_colors=number_opaque+number_transparent+number_semitransparent;
7410
glennrpa080bc32011-03-11 18:03:44 +00007411 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7412 {
7413 /* No room for the background color; remove it. */
7414 number_opaque--;
7415 image_colors--;
7416 }
7417
glennrpd71e86a2011-02-24 01:28:37 +00007418 if (logging != MagickFalse)
7419 {
7420 if (image_colors > 256)
7421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7422 " image has more than 256 colors");
7423
7424 else
7425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7426 " image has %d colors",image_colors);
7427 }
7428
glennrp8d3d6e52011-04-19 04:39:51 +00007429 if (ping_preserve_colormap != MagickFalse)
7430 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007431
glennrpfd05d622011-02-25 04:10:33 +00007432 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007433 {
7434 ping_have_color=MagickFalse;
7435 ping_have_non_bw=MagickFalse;
7436
7437 if(image_colors > 256)
7438 {
7439 for (y=0; y < (ssize_t) image->rows; y++)
7440 {
7441 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7442
7443 if (q == (PixelPacket *) NULL)
7444 break;
7445
7446 /* Worst case is black-and-white; we are looking at every
7447 * pixel twice.
7448 */
7449
7450 if (ping_have_color == MagickFalse)
7451 {
7452 s=q;
7453 for (x=0; x < (ssize_t) image->columns; x++)
7454 {
glennrpa18d5bc2011-04-23 14:51:34 +00007455 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7456 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00007457 {
7458 ping_have_color=MagickTrue;
7459 ping_have_non_bw=MagickTrue;
7460 break;
7461 }
7462 s++;
7463 }
7464 }
7465
7466 if (ping_have_non_bw == MagickFalse)
7467 {
7468 s=q;
7469 for (x=0; x < (ssize_t) image->columns; x++)
7470 {
glennrpa18d5bc2011-04-23 14:51:34 +00007471 if (GetRedPixelComponent(s) != 0 &&
7472 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00007473 {
7474 ping_have_non_bw=MagickTrue;
7475 }
7476 s++;
7477 }
7478 }
7479 }
7480 }
7481 }
7482
7483 if (image_colors < 257)
7484 {
7485 PixelPacket
7486 colormap[260];
7487
7488 /*
7489 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007490 */
7491
glennrpd71e86a2011-02-24 01:28:37 +00007492 if (logging != MagickFalse)
7493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7494 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007495
glennrpd71e86a2011-02-24 01:28:37 +00007496 /* Sort palette, transparent first */;
7497
7498 n = 0;
7499
7500 for (i=0; i<number_transparent; i++)
7501 colormap[n++] = transparent[i];
7502
7503 for (i=0; i<number_semitransparent; i++)
7504 colormap[n++] = semitransparent[i];
7505
7506 for (i=0; i<number_opaque; i++)
7507 colormap[n++] = opaque[i];
7508
7509
7510 /* image_colors < 257; search the colormap instead of the pixels
7511 * to get ping_have_color and ping_have_non_bw
7512 */
7513 for (i=0; i<n; i++)
7514 {
7515 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007516 {
glennrpd71e86a2011-02-24 01:28:37 +00007517 if (colormap[i].red != colormap[i].green ||
7518 colormap[i].red != colormap[i].blue)
7519 {
7520 ping_have_color=MagickTrue;
7521 ping_have_non_bw=MagickTrue;
7522 break;
7523 }
7524 }
7525
7526 if (ping_have_non_bw == MagickFalse)
7527 {
7528 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007529 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007530 }
glennrp8bb3a022010-12-13 20:40:04 +00007531 }
7532
glennrpd71e86a2011-02-24 01:28:37 +00007533 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7534 (number_transparent == 0 && number_semitransparent == 0)) &&
7535 (((mng_info->write_png_colortype-1) ==
7536 PNG_COLOR_TYPE_PALETTE) ||
7537 (mng_info->write_png_colortype == 0)))
7538 {
glennrp6185c532011-01-14 17:58:40 +00007539 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007540 {
glennrpd71e86a2011-02-24 01:28:37 +00007541 if (n != (ssize_t) image_colors)
7542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7543 " image_colors (%d) and n (%d) don't match",
7544 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007545
glennrpd71e86a2011-02-24 01:28:37 +00007546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7547 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007548 }
glennrp03812ae2010-12-24 01:31:34 +00007549
glennrpd71e86a2011-02-24 01:28:37 +00007550 image->colors = image_colors;
7551
7552 if (AcquireImageColormap(image,image_colors) ==
7553 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007554 ThrowWriterException(ResourceLimitError,
7555 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007556
7557 for (i=0; i< (ssize_t) image_colors; i++)
7558 image->colormap[i] = colormap[i];
7559
7560 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007561 {
glennrpd71e86a2011-02-24 01:28:37 +00007562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7563 " image->colors=%d (%d)",
7564 (int) image->colors, image_colors);
7565
7566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7567 " Update the pixel indexes");
7568 }
glennrp6185c532011-01-14 17:58:40 +00007569
glennrpfd05d622011-02-25 04:10:33 +00007570 /* Sync the pixel indices with the new colormap */
7571
glennrpd71e86a2011-02-24 01:28:37 +00007572 for (y=0; y < (ssize_t) image->rows; y++)
7573 {
7574 q=GetAuthenticPixels(image,0,y,image->columns,1,
7575 exception);
glennrp6185c532011-01-14 17:58:40 +00007576
glennrpd71e86a2011-02-24 01:28:37 +00007577 if (q == (PixelPacket *) NULL)
7578 break;
glennrp6185c532011-01-14 17:58:40 +00007579
glennrpd71e86a2011-02-24 01:28:37 +00007580 indexes=GetAuthenticIndexQueue(image);
7581
7582 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007583 {
glennrpd71e86a2011-02-24 01:28:37 +00007584 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007585 {
glennrpd71e86a2011-02-24 01:28:37 +00007586 if ((image->matte == MagickFalse ||
7587 image->colormap[i].opacity == q->opacity) &&
7588 (IsColorEqual(&image->colormap[i],
7589 (PixelPacket *) q)))
glennrp6185c532011-01-14 17:58:40 +00007590 {
glennrpd71e86a2011-02-24 01:28:37 +00007591 indexes[x]=(IndexPacket) i;
7592 break;
glennrp6185c532011-01-14 17:58:40 +00007593 }
glennrp6185c532011-01-14 17:58:40 +00007594 }
glennrpd71e86a2011-02-24 01:28:37 +00007595 q++;
7596 }
glennrp6185c532011-01-14 17:58:40 +00007597
glennrpd71e86a2011-02-24 01:28:37 +00007598 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7599 break;
7600 }
7601 }
7602 }
7603
7604 if (logging != MagickFalse)
7605 {
7606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7607 " image->colors=%d", (int) image->colors);
7608
7609 if (image->colormap != NULL)
7610 {
7611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7612 " i (red,green,blue,opacity)");
7613
7614 for (i=0; i < (ssize_t) image->colors; i++)
7615 {
cristy72988482011-03-29 16:34:38 +00007616 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007617 {
7618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7619 " %d (%d,%d,%d,%d)",
7620 (int) i,
7621 (int) image->colormap[i].red,
7622 (int) image->colormap[i].green,
7623 (int) image->colormap[i].blue,
7624 (int) image->colormap[i].opacity);
7625 }
glennrp6185c532011-01-14 17:58:40 +00007626 }
7627 }
glennrp03812ae2010-12-24 01:31:34 +00007628
glennrpd71e86a2011-02-24 01:28:37 +00007629 if (number_transparent < 257)
7630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7631 " number_transparent = %d",
7632 number_transparent);
7633 else
glennrp03812ae2010-12-24 01:31:34 +00007634
glennrpd71e86a2011-02-24 01:28:37 +00007635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7636 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007637
glennrpd71e86a2011-02-24 01:28:37 +00007638 if (number_opaque < 257)
7639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7640 " number_opaque = %d",
7641 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007642
glennrpd71e86a2011-02-24 01:28:37 +00007643 else
7644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7645 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007646
glennrpd71e86a2011-02-24 01:28:37 +00007647 if (number_semitransparent < 257)
7648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7649 " number_semitransparent = %d",
7650 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007651
glennrpd71e86a2011-02-24 01:28:37 +00007652 else
7653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7654 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007655
glennrpd71e86a2011-02-24 01:28:37 +00007656 if (ping_have_non_bw == MagickFalse)
7657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7658 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007659
glennrpd71e86a2011-02-24 01:28:37 +00007660 else if (ping_have_color == MagickFalse)
7661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7662 " All pixels and the background are gray");
7663
7664 else
7665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7666 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007667
glennrp03812ae2010-12-24 01:31:34 +00007668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7669 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007670 }
glennrpfd05d622011-02-25 04:10:33 +00007671
glennrpc8c2f062011-02-25 19:00:33 +00007672 if (mng_info->write_png8 == MagickFalse)
7673 break;
glennrpfd05d622011-02-25 04:10:33 +00007674
glennrpc8c2f062011-02-25 19:00:33 +00007675 /* Make any reductions necessary for the PNG8 format */
7676 if (image_colors <= 256 &&
7677 image_colors != 0 && image->colormap != NULL &&
7678 number_semitransparent == 0 &&
7679 number_transparent <= 1)
7680 break;
7681
7682 /* PNG8 can't have semitransparent colors so we threshold the
7683 * opacity to 0 or OpaqueOpacity
7684 */
7685 if (number_semitransparent != 0)
7686 {
7687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7688 " Thresholding the alpha channel to binary");
7689
7690 for (y=0; y < (ssize_t) image->rows; y++)
7691 {
7692 r=GetAuthenticPixels(image,0,y,image->columns,1,
7693 exception);
7694
7695 if (r == (PixelPacket *) NULL)
7696 break;
7697
7698 for (x=0; x < (ssize_t) image->columns; x++)
7699 {
glennrpa18d5bc2011-04-23 14:51:34 +00007700 SetOpacityPixelComponent(r,
7701 (GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7702 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00007703 r++;
7704 }
7705
7706 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7707 break;
7708
7709 if (image_colors != 0 && image_colors <= 256 &&
7710 image->colormap != NULL)
7711 for (i=0; i<image_colors; i++)
7712 image->colormap[i].opacity =
glennrp82b3c532011-03-22 19:20:54 +00007713 image->colormap[i].opacity > TransparentOpacity/2 ?
7714 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007715 }
7716 continue;
7717 }
7718
7719 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007720 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7721 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7722 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007723 */
glennrpd3371642011-03-22 19:42:23 +00007724 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7725 {
7726 if (logging != MagickFalse)
7727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7728 " Quantizing the background color to 4-4-4");
7729
7730 tried_444 = MagickTrue;
7731
7732 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007733 ScaleCharToQuantum(
7734 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7735 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007736 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007737 ScaleCharToQuantum(
7738 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7739 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007740 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007741 ScaleCharToQuantum(
7742 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7743 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007744
7745 if (logging != MagickFalse)
7746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7747 " Quantizing the pixel colors to 4-4-4");
7748
7749 if (image->colormap == NULL)
7750 {
7751 for (y=0; y < (ssize_t) image->rows; y++)
7752 {
7753 r=GetAuthenticPixels(image,0,y,image->columns,1,
7754 exception);
7755
7756 if (r == (PixelPacket *) NULL)
7757 break;
7758
7759 for (x=0; x < (ssize_t) image->columns; x++)
7760 {
7761 if (r->opacity == TransparentOpacity)
7762 {
7763 r->red = image->background_color.red;
7764 r->green = image->background_color.green;
7765 r->blue = image->background_color.blue;
7766 }
7767 else
7768 {
glennrp3faa9a32011-04-23 14:00:25 +00007769 r->red=ScaleCharToQuantum(
7770 (ScaleQuantumToChar(r->red) & 0xf0) |
7771 (ScaleQuantumToChar(r->red) & 0xf0) >> 4);
7772 r->green=ScaleCharToQuantum(
7773 (ScaleQuantumToChar(r->green) & 0xf0) |
7774 (ScaleQuantumToChar(r->green) & 0xf0) >> 4);
7775 r->blue=ScaleCharToQuantum(
7776 (ScaleQuantumToChar(r->blue) & 0xf0) |
7777 (ScaleQuantumToChar(r->blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007778 }
7779 r++;
7780 }
7781
7782 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7783 break;
7784 }
7785 }
7786
7787 else /* Should not reach this; colormap already exists and
7788 must be <= 256 */
7789 {
7790 if (logging != MagickFalse)
7791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7792 " Quantizing the colormap to 4-4-4");
7793 for (i=0; i<image_colors; i++)
7794 {
glennrp3faa9a32011-04-23 14:00:25 +00007795 image->colormap[i].red=ScaleCharToQuantum(
7796 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7797 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7798 image->colormap[i].green=ScaleCharToQuantum(
7799 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7800 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7801 image->colormap[i].blue=ScaleCharToQuantum(
7802 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7803 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
glennrpd3371642011-03-22 19:42:23 +00007804 }
7805 }
7806 continue;
7807 }
7808
glennrp82b3c532011-03-22 19:20:54 +00007809 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7810 {
7811 if (logging != MagickFalse)
7812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7813 " Quantizing the background color to 3-3-3");
7814
7815 tried_333 = MagickTrue;
7816
7817 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007818 ScaleCharToQuantum(
7819 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7820 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7821 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007822 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007823 ScaleCharToQuantum(
7824 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7825 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7826 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007827 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007828 ScaleCharToQuantum(
7829 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7830 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7831 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007832
7833 if (logging != MagickFalse)
7834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007835 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007836
7837 if (image->colormap == NULL)
7838 {
7839 for (y=0; y < (ssize_t) image->rows; y++)
7840 {
7841 r=GetAuthenticPixels(image,0,y,image->columns,1,
7842 exception);
7843
7844 if (r == (PixelPacket *) NULL)
7845 break;
7846
7847 for (x=0; x < (ssize_t) image->columns; x++)
7848 {
7849 if (r->opacity == TransparentOpacity)
7850 {
7851 r->red = image->background_color.red;
7852 r->green = image->background_color.green;
7853 r->blue = image->background_color.blue;
7854 }
7855 else
7856 {
glennrp3faa9a32011-04-23 14:00:25 +00007857 r->red=ScaleCharToQuantum(
7858 (ScaleQuantumToChar(r->red) & 0xe0) |
7859 (ScaleQuantumToChar(r->red) & 0xe0) >> 3 |
7860 (ScaleQuantumToChar(r->red) & 0xc0) >> 6);
7861 r->green=ScaleCharToQuantum(
7862 (ScaleQuantumToChar(r->green) & 0xe0) |
7863 (ScaleQuantumToChar(r->green) & 0xe0) >> 3 |
7864 (ScaleQuantumToChar(r->green) & 0xc0) >> 6);
7865 r->blue=ScaleCharToQuantum(
7866 (ScaleQuantumToChar(r->blue) & 0xe0) |
7867 (ScaleQuantumToChar(r->blue) & 0xe0) >> 3 |
7868 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007869 }
7870 r++;
7871 }
7872
7873 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7874 break;
7875 }
7876 }
7877
7878 else /* Should not reach this; colormap already exists and
7879 must be <= 256 */
7880 {
7881 if (logging != MagickFalse)
7882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007883 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007884 for (i=0; i<image_colors; i++)
7885 {
glennrp3faa9a32011-04-23 14:00:25 +00007886 image->colormap[i].red=ScaleCharToQuantum(
7887 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7888 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7889 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7890 image->colormap[i].green=ScaleCharToQuantum(
7891 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7892 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7893 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7894 image->colormap[i].blue=ScaleCharToQuantum(
7895 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7896 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7897 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007898 }
glennrpd3371642011-03-22 19:42:23 +00007899 }
7900 continue;
glennrp82b3c532011-03-22 19:20:54 +00007901 }
glennrpc8c2f062011-02-25 19:00:33 +00007902
glennrpc8c2f062011-02-25 19:00:33 +00007903 if (image_colors == 0 || image_colors > 256)
7904 {
7905 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007907 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007908
glennrp3faa9a32011-04-23 14:00:25 +00007909 /* Red and green were already done so we only quantize the blue
7910 * channel
7911 */
7912
7913 image->background_color.blue=ScaleCharToQuantum(
7914 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7915 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7916 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7917 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrpfd05d622011-02-25 04:10:33 +00007918
glennrpc8c2f062011-02-25 19:00:33 +00007919 if (logging != MagickFalse)
7920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007921 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007922
glennrpc8c2f062011-02-25 19:00:33 +00007923 if (image->colormap == NULL)
7924 {
7925 for (y=0; y < (ssize_t) image->rows; y++)
7926 {
7927 r=GetAuthenticPixels(image,0,y,image->columns,1,
7928 exception);
7929
7930 if (r == (PixelPacket *) NULL)
7931 break;
7932
7933 for (x=0; x < (ssize_t) image->columns; x++)
7934 {
glennrp82b3c532011-03-22 19:20:54 +00007935 if (r->opacity == TransparentOpacity)
7936 {
7937 r->red = image->background_color.red;
7938 r->green = image->background_color.green;
7939 r->blue = image->background_color.blue;
7940 }
7941 else
7942 {
glennrp3faa9a32011-04-23 14:00:25 +00007943 r->blue=ScaleCharToQuantum(
7944 (ScaleQuantumToChar(r->blue) & 0xc0) |
7945 (ScaleQuantumToChar(r->blue) & 0xc0) >> 2 |
7946 (ScaleQuantumToChar(r->blue) & 0xc0) >> 4 |
7947 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007948 }
glennrp52a479c2011-02-26 21:14:38 +00007949 r++;
glennrpc8c2f062011-02-25 19:00:33 +00007950 }
glennrpfd05d622011-02-25 04:10:33 +00007951
glennrpc8c2f062011-02-25 19:00:33 +00007952 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7953 break;
7954 }
7955 }
glennrpfd05d622011-02-25 04:10:33 +00007956
glennrpc8c2f062011-02-25 19:00:33 +00007957 else /* Should not reach this; colormap already exists and
7958 must be <= 256 */
7959 {
7960 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007962 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00007963 for (i=0; i<image_colors; i++)
7964 {
glennrp3faa9a32011-04-23 14:00:25 +00007965 image->colormap[i].blue=ScaleCharToQuantum(
7966 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
7967 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
7968 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
7969 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrpc8c2f062011-02-25 19:00:33 +00007970 }
7971 }
7972 continue;
7973 }
7974 break;
glennrpd71e86a2011-02-24 01:28:37 +00007975 }
glennrpfd05d622011-02-25 04:10:33 +00007976 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00007977
glennrpfd05d622011-02-25 04:10:33 +00007978 /* If we are excluding the tRNS chunk and there is transparency,
7979 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7980 * PNG.
glennrp8d579662011-02-23 02:05:02 +00007981 */
glennrp0e8ea192010-12-24 18:00:33 +00007982 if (mng_info->ping_exclude_tRNS != MagickFalse &&
7983 (number_transparent != 0 || number_semitransparent != 0))
7984 {
7985 int colortype=mng_info->write_png_colortype;
7986
7987 if (ping_have_color == MagickFalse)
7988 mng_info->write_png_colortype = 5;
7989
7990 else
7991 mng_info->write_png_colortype = 7;
7992
glennrp8d579662011-02-23 02:05:02 +00007993 if (colortype != 0 &&
7994 mng_info->write_png_colortype != (ssize_t) colortype)
glennrp0e8ea192010-12-24 18:00:33 +00007995 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00007996
glennrp0e8ea192010-12-24 18:00:33 +00007997 }
7998
glennrpfd05d622011-02-25 04:10:33 +00007999 /* See if cheap transparency is possible. It is only possible
8000 * when there is a single transparent color, no semitransparent
8001 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008002 * as the transparent color. We only need this information if
8003 * we are writing a PNG with colortype 0 or 2, and we have not
8004 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008005 */
glennrp5a39f372011-02-25 04:52:16 +00008006 if (number_transparent == 1 &&
8007 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008008 {
8009 ping_have_cheap_transparency = MagickTrue;
8010
8011 if (number_semitransparent != 0)
8012 ping_have_cheap_transparency = MagickFalse;
8013
8014 else if (image_colors == 0 || image_colors > 256 ||
8015 image->colormap == NULL)
8016 {
8017 ExceptionInfo
8018 *exception;
8019
8020 register const PixelPacket
8021 *q;
8022
8023 exception=(&image->exception);
8024
8025 for (y=0; y < (ssize_t) image->rows; y++)
8026 {
8027 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8028
8029 if (q == (PixelPacket *) NULL)
8030 break;
8031
8032 for (x=0; x < (ssize_t) image->columns; x++)
8033 {
8034 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008035 (unsigned short) GetRedPixelComponent(q) ==
8036 ping_trans_color.red &&
8037 (unsigned short) GetGreenPixelComponent(q) ==
8038 ping_trans_color.green &&
8039 (unsigned short) GetBluePixelComponent(q) ==
8040 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008041 {
8042 ping_have_cheap_transparency = MagickFalse;
8043 break;
8044 }
8045
8046 q++;
8047 }
8048
8049 if (ping_have_cheap_transparency == MagickFalse)
8050 break;
8051 }
8052 }
8053 else
8054 {
glennrp67b9c1a2011-04-22 18:47:36 +00008055 /* Assuming that image->colormap[0] is the one transparent color
8056 * and that all others are opaque.
8057 */
glennrpfd05d622011-02-25 04:10:33 +00008058 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008059 for (i=1; i<image_colors; i++)
8060 if (image->colormap[i].red == image->colormap[0].red &&
8061 image->colormap[i].green == image->colormap[0].green &&
8062 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008063 {
glennrp67b9c1a2011-04-22 18:47:36 +00008064 ping_have_cheap_transparency = MagickFalse;
8065 break;
glennrpfd05d622011-02-25 04:10:33 +00008066 }
8067 }
8068
8069 if (logging != MagickFalse)
8070 {
8071 if (ping_have_cheap_transparency == MagickFalse)
8072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8073 " Cheap transparency is not possible.");
8074
8075 else
8076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8077 " Cheap transparency is possible.");
8078 }
8079 }
8080 else
8081 ping_have_cheap_transparency = MagickFalse;
8082
glennrp8640fb52010-11-23 15:48:26 +00008083 image_depth=image->depth;
8084
glennrp26c990a2010-11-23 02:23:20 +00008085 quantum_info = (QuantumInfo *) NULL;
8086 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008087 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008088 image_matte=image->matte;
8089
glennrp0fe50b42010-11-16 03:52:51 +00008090 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008091 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008092
glennrp52a479c2011-02-26 21:14:38 +00008093 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8094 (image->colors == 0 || image->colormap == NULL))
8095 {
glennrp52a479c2011-02-26 21:14:38 +00008096 image_info=DestroyImageInfo(image_info);
8097 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008098 (void) ThrowMagickException(&IMimage->exception,
8099 GetMagickModule(),CoderError,
8100 "Cannot write PNG8 or color-type 3; colormap is NULL",
8101 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008102#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8103 UnlockSemaphoreInfo(ping_semaphore);
8104#endif
8105 return(MagickFalse);
8106 }
8107
cristy3ed852e2009-09-05 21:47:34 +00008108 /*
8109 Allocate the PNG structures
8110 */
8111#ifdef PNG_USER_MEM_SUPPORTED
8112 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008113 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8114 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008115
cristy3ed852e2009-09-05 21:47:34 +00008116#else
8117 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008118 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008119
cristy3ed852e2009-09-05 21:47:34 +00008120#endif
8121 if (ping == (png_struct *) NULL)
8122 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008123
cristy3ed852e2009-09-05 21:47:34 +00008124 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008125
cristy3ed852e2009-09-05 21:47:34 +00008126 if (ping_info == (png_info *) NULL)
8127 {
8128 png_destroy_write_struct(&ping,(png_info **) NULL);
8129 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8130 }
glennrp0fe50b42010-11-16 03:52:51 +00008131
cristy3ed852e2009-09-05 21:47:34 +00008132 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008133 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008134
glennrp5af765f2010-03-30 11:12:18 +00008135 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008136 {
8137 /*
8138 PNG write failed.
8139 */
8140#ifdef PNG_DEBUG
8141 if (image_info->verbose)
8142 (void) printf("PNG write has failed.\n");
8143#endif
8144 png_destroy_write_struct(&ping,&ping_info);
8145#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008146 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008147#endif
glennrpda8f3a72011-02-27 23:54:12 +00008148 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008149 (void) CloseBlob(image);
8150 image_info=DestroyImageInfo(image_info);
8151 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008152 return(MagickFalse);
8153 }
8154 /*
8155 Prepare PNG for writing.
8156 */
8157#if defined(PNG_MNG_FEATURES_SUPPORTED)
8158 if (mng_info->write_mng)
8159 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008160
cristy3ed852e2009-09-05 21:47:34 +00008161#else
8162# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8163 if (mng_info->write_mng)
8164 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008165
cristy3ed852e2009-09-05 21:47:34 +00008166# endif
8167#endif
glennrp2b013e42010-11-24 16:55:50 +00008168
cristy3ed852e2009-09-05 21:47:34 +00008169 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008170
cristy4e5bc842010-06-09 13:56:01 +00008171 ping_width=(png_uint_32) image->columns;
8172 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008173
cristy3ed852e2009-09-05 21:47:34 +00008174 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8175 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008176
cristy3ed852e2009-09-05 21:47:34 +00008177 if (mng_info->write_png_depth != 0)
8178 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008179
cristy3ed852e2009-09-05 21:47:34 +00008180 /* Adjust requested depth to next higher valid depth if necessary */
8181 if (image_depth > 8)
8182 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008183
cristy3ed852e2009-09-05 21:47:34 +00008184 if ((image_depth > 4) && (image_depth < 8))
8185 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008186
cristy3ed852e2009-09-05 21:47:34 +00008187 if (image_depth == 3)
8188 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008189
cristy3ed852e2009-09-05 21:47:34 +00008190 if (logging != MagickFalse)
8191 {
8192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008193 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008195 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008197 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008199 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008201 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008202 }
glennrp8640fb52010-11-23 15:48:26 +00008203
cristy3ed852e2009-09-05 21:47:34 +00008204 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008205 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008206
glennrp26f37912010-12-23 16:22:42 +00008207
cristy3ed852e2009-09-05 21:47:34 +00008208#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008209 if (ping_exclude_pHYs == MagickFalse)
8210 {
cristy3ed852e2009-09-05 21:47:34 +00008211 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8212 (!mng_info->write_mng || !mng_info->equal_physs))
8213 {
glennrp0fe50b42010-11-16 03:52:51 +00008214 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8216 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008217
8218 if (image->units == PixelsPerInchResolution)
8219 {
glennrpdfd70802010-11-14 01:23:35 +00008220 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008221 ping_pHYs_x_resolution=
8222 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8223 ping_pHYs_y_resolution=
8224 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008225 }
glennrpdfd70802010-11-14 01:23:35 +00008226
cristy3ed852e2009-09-05 21:47:34 +00008227 else if (image->units == PixelsPerCentimeterResolution)
8228 {
glennrpdfd70802010-11-14 01:23:35 +00008229 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008230 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8231 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008232 }
glennrp991d11d2010-11-12 21:55:28 +00008233
cristy3ed852e2009-09-05 21:47:34 +00008234 else
8235 {
glennrpdfd70802010-11-14 01:23:35 +00008236 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8237 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8238 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008239 }
glennrp991d11d2010-11-12 21:55:28 +00008240
glennrp823b55c2011-03-14 18:46:46 +00008241 if (logging != MagickFalse)
8242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8243 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8244 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8245 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008246 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008247 }
glennrp26f37912010-12-23 16:22:42 +00008248 }
cristy3ed852e2009-09-05 21:47:34 +00008249#endif
glennrpa521b2f2010-10-29 04:11:03 +00008250
glennrp26f37912010-12-23 16:22:42 +00008251 if (ping_exclude_bKGD == MagickFalse)
8252 {
glennrpa521b2f2010-10-29 04:11:03 +00008253 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008254 {
glennrpa521b2f2010-10-29 04:11:03 +00008255 unsigned int
8256 mask;
cristy3ed852e2009-09-05 21:47:34 +00008257
glennrpa521b2f2010-10-29 04:11:03 +00008258 mask=0xffff;
8259 if (ping_bit_depth == 8)
8260 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008261
glennrpa521b2f2010-10-29 04:11:03 +00008262 if (ping_bit_depth == 4)
8263 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008264
glennrpa521b2f2010-10-29 04:11:03 +00008265 if (ping_bit_depth == 2)
8266 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008267
glennrpa521b2f2010-10-29 04:11:03 +00008268 if (ping_bit_depth == 1)
8269 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008270
glennrpa521b2f2010-10-29 04:11:03 +00008271 ping_background.red=(png_uint_16)
8272 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008273
glennrpa521b2f2010-10-29 04:11:03 +00008274 ping_background.green=(png_uint_16)
8275 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008276
glennrpa521b2f2010-10-29 04:11:03 +00008277 ping_background.blue=(png_uint_16)
8278 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008279 }
cristy3ed852e2009-09-05 21:47:34 +00008280
glennrp0fe50b42010-11-16 03:52:51 +00008281 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008282 {
8283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8284 " Setting up bKGD chunk (1)");
8285
8286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8287 " ping_bit_depth=%d",ping_bit_depth);
8288 }
glennrp0fe50b42010-11-16 03:52:51 +00008289
8290 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008291 }
glennrp0fe50b42010-11-16 03:52:51 +00008292
cristy3ed852e2009-09-05 21:47:34 +00008293 /*
8294 Select the color type.
8295 */
8296 matte=image_matte;
8297 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008298
glennrp1273f7b2011-02-24 03:20:30 +00008299 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008300 {
glennrp0fe50b42010-11-16 03:52:51 +00008301
glennrpfd05d622011-02-25 04:10:33 +00008302 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008303 for reducing the sample depth from 8. */
8304
glennrp0fe50b42010-11-16 03:52:51 +00008305 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008306
glennrp8bb3a022010-12-13 20:40:04 +00008307 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008308
8309 /*
8310 Set image palette.
8311 */
8312 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8313
glennrp0fe50b42010-11-16 03:52:51 +00008314 if (logging != MagickFalse)
8315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8316 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008317 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008318
8319 for (i=0; i < (ssize_t) number_colors; i++)
8320 {
8321 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8322 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8323 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8324 if (logging != MagickFalse)
8325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008326#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008327 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008328#else
8329 " %5ld (%5d,%5d,%5d)",
8330#endif
glennrp0fe50b42010-11-16 03:52:51 +00008331 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8332
8333 }
glennrp2b013e42010-11-24 16:55:50 +00008334
glennrp8bb3a022010-12-13 20:40:04 +00008335 ping_have_PLTE=MagickTrue;
8336 image_depth=ping_bit_depth;
8337 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008338
glennrp58e01762011-01-07 15:28:54 +00008339 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008340 {
glennrp0fe50b42010-11-16 03:52:51 +00008341 /*
8342 Identify which colormap entry is transparent.
8343 */
8344 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008345 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008346
glennrp8bb3a022010-12-13 20:40:04 +00008347 for (i=0; i < (ssize_t) number_transparent; i++)
8348 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008349
glennrp0fe50b42010-11-16 03:52:51 +00008350
glennrp2cc891a2010-12-24 13:44:32 +00008351 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008352 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008353
8354 if (ping_num_trans == 0)
8355 ping_have_tRNS=MagickFalse;
8356
glennrp8bb3a022010-12-13 20:40:04 +00008357 else
8358 ping_have_tRNS=MagickTrue;
8359 }
glennrp0fe50b42010-11-16 03:52:51 +00008360
glennrp1273f7b2011-02-24 03:20:30 +00008361 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008362 {
glennrp1273f7b2011-02-24 03:20:30 +00008363 /*
8364 * Identify which colormap entry is the background color.
8365 */
8366
glennrp4f25bd02011-01-01 18:51:28 +00008367 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8368 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8369 break;
glennrp0fe50b42010-11-16 03:52:51 +00008370
glennrp4f25bd02011-01-01 18:51:28 +00008371 ping_background.index=(png_byte) i;
8372 }
cristy3ed852e2009-09-05 21:47:34 +00008373 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008374
cristy3ed852e2009-09-05 21:47:34 +00008375 else if (mng_info->write_png24)
8376 {
8377 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008378 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008379 }
glennrp0fe50b42010-11-16 03:52:51 +00008380
cristy3ed852e2009-09-05 21:47:34 +00008381 else if (mng_info->write_png32)
8382 {
8383 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008384 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008385 }
glennrp0fe50b42010-11-16 03:52:51 +00008386
glennrp8bb3a022010-12-13 20:40:04 +00008387 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008388 {
glennrp5af765f2010-03-30 11:12:18 +00008389 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008390
glennrp8bb3a022010-12-13 20:40:04 +00008391 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008392 {
glennrp5af765f2010-03-30 11:12:18 +00008393 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008394
glennrp5af765f2010-03-30 11:12:18 +00008395 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8396 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008397 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008398
glennrp8bb3a022010-12-13 20:40:04 +00008399 else
8400 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008401
8402 if (logging != MagickFalse)
8403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8404 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008405 }
glennrp0fe50b42010-11-16 03:52:51 +00008406
glennrp7c4c9e62011-03-21 20:23:32 +00008407 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008408 {
8409 if (logging != MagickFalse)
8410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008411 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008412
glennrpd6bf1612010-12-17 17:28:54 +00008413 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008414 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008415
glennrpd6bf1612010-12-17 17:28:54 +00008416 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008417 {
glennrp5af765f2010-03-30 11:12:18 +00008418 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008419 image_matte=MagickFalse;
8420 }
glennrp0fe50b42010-11-16 03:52:51 +00008421
glennrpd6bf1612010-12-17 17:28:54 +00008422 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008423 {
glennrp5af765f2010-03-30 11:12:18 +00008424 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008425 image_matte=MagickTrue;
8426 }
glennrp0fe50b42010-11-16 03:52:51 +00008427
glennrp5aa37f62011-01-02 03:07:57 +00008428 if (image_info->type == PaletteType ||
8429 image_info->type == PaletteMatteType)
8430 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8431
glennrp7c4c9e62011-03-21 20:23:32 +00008432 if (mng_info->write_png_colortype == 0 &&
8433 (image_info->type == UndefinedType ||
8434 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008435 {
glennrp5aa37f62011-01-02 03:07:57 +00008436 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008437 {
glennrp5aa37f62011-01-02 03:07:57 +00008438 if (image_matte == MagickFalse)
8439 {
8440 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8441 image_matte=MagickFalse;
8442 }
glennrp0fe50b42010-11-16 03:52:51 +00008443
glennrp0b206f52011-01-07 04:55:32 +00008444 else
glennrp5aa37f62011-01-02 03:07:57 +00008445 {
8446 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8447 image_matte=MagickTrue;
8448 }
8449 }
8450 else
glennrp8bb3a022010-12-13 20:40:04 +00008451 {
glennrp5aa37f62011-01-02 03:07:57 +00008452 if (image_matte == MagickFalse)
8453 {
8454 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8455 image_matte=MagickFalse;
8456 }
glennrp8bb3a022010-12-13 20:40:04 +00008457
glennrp0b206f52011-01-07 04:55:32 +00008458 else
glennrp5aa37f62011-01-02 03:07:57 +00008459 {
8460 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8461 image_matte=MagickTrue;
8462 }
8463 }
glennrp0fe50b42010-11-16 03:52:51 +00008464 }
glennrp5aa37f62011-01-02 03:07:57 +00008465
cristy3ed852e2009-09-05 21:47:34 +00008466 }
glennrp0fe50b42010-11-16 03:52:51 +00008467
cristy3ed852e2009-09-05 21:47:34 +00008468 if (logging != MagickFalse)
8469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008470 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008471
glennrp5af765f2010-03-30 11:12:18 +00008472 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008473 {
8474 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8475 ping_color_type == PNG_COLOR_TYPE_RGB ||
8476 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8477 ping_bit_depth=8;
8478 }
cristy3ed852e2009-09-05 21:47:34 +00008479
glennrpd6bf1612010-12-17 17:28:54 +00008480 old_bit_depth=ping_bit_depth;
8481
glennrp5af765f2010-03-30 11:12:18 +00008482 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008483 {
glennrp8d579662011-02-23 02:05:02 +00008484 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8485 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008486 }
glennrp8640fb52010-11-23 15:48:26 +00008487
glennrp5af765f2010-03-30 11:12:18 +00008488 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008489 {
cristy35ef8242010-06-03 16:24:13 +00008490 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008491 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008492
8493 if (image->colors == 0)
8494 {
glennrp0fe50b42010-11-16 03:52:51 +00008495 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008496 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008497 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008498 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008499 }
8500
cristy35ef8242010-06-03 16:24:13 +00008501 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008502 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008503 }
glennrp2b013e42010-11-24 16:55:50 +00008504
glennrpd6bf1612010-12-17 17:28:54 +00008505 if (logging != MagickFalse)
8506 {
8507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8508 " Number of colors: %.20g",(double) image_colors);
8509
8510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8511 " Tentative PNG bit depth: %d",ping_bit_depth);
8512 }
8513
8514 if (ping_bit_depth < (int) mng_info->write_png_depth)
8515 ping_bit_depth = mng_info->write_png_depth;
8516 }
glennrp2cc891a2010-12-24 13:44:32 +00008517
glennrp5af765f2010-03-30 11:12:18 +00008518 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008519
cristy3ed852e2009-09-05 21:47:34 +00008520 if (logging != MagickFalse)
8521 {
8522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008523 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008524
cristy3ed852e2009-09-05 21:47:34 +00008525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008526 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008527
cristy3ed852e2009-09-05 21:47:34 +00008528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008529 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008530
cristy3ed852e2009-09-05 21:47:34 +00008531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008532
glennrp8640fb52010-11-23 15:48:26 +00008533 " image->depth: %.20g",(double) image->depth);
8534
8535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008536 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008537 }
8538
glennrp58e01762011-01-07 15:28:54 +00008539 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008540 {
glennrp4f25bd02011-01-01 18:51:28 +00008541 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008542 {
glennrp7c4c9e62011-03-21 20:23:32 +00008543 if (mng_info->write_png_colortype == 0)
8544 {
8545 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008546
glennrp7c4c9e62011-03-21 20:23:32 +00008547 if (ping_have_color != MagickFalse)
8548 ping_color_type=PNG_COLOR_TYPE_RGBA;
8549 }
glennrp4f25bd02011-01-01 18:51:28 +00008550
8551 /*
8552 * Determine if there is any transparent color.
8553 */
8554 if (number_transparent + number_semitransparent == 0)
8555 {
8556 /*
8557 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8558 */
glennrpa6a06632011-01-19 15:15:34 +00008559
glennrp4f25bd02011-01-01 18:51:28 +00008560 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008561
8562 if (mng_info->write_png_colortype == 0)
8563 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008564 }
8565
8566 else
8567 {
8568 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008569 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008570
8571 mask=0xffff;
8572
8573 if (ping_bit_depth == 8)
8574 mask=0x00ff;
8575
8576 if (ping_bit_depth == 4)
8577 mask=0x000f;
8578
8579 if (ping_bit_depth == 2)
8580 mask=0x0003;
8581
8582 if (ping_bit_depth == 1)
8583 mask=0x0001;
8584
8585 ping_trans_color.red=(png_uint_16)
8586 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8587
8588 ping_trans_color.green=(png_uint_16)
8589 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8590
8591 ping_trans_color.blue=(png_uint_16)
8592 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8593
8594 ping_trans_color.gray=(png_uint_16)
8595 (ScaleQuantumToShort(PixelIntensityToQuantum(
8596 image->colormap)) & mask);
8597
8598 ping_trans_color.index=(png_byte) 0;
8599
8600 ping_have_tRNS=MagickTrue;
8601 }
8602
8603 if (ping_have_tRNS != MagickFalse)
8604 {
8605 /*
glennrpfd05d622011-02-25 04:10:33 +00008606 * Determine if there is one and only one transparent color
8607 * and if so if it is fully transparent.
8608 */
8609 if (ping_have_cheap_transparency == MagickFalse)
8610 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008611 }
8612
8613 if (ping_have_tRNS != MagickFalse)
8614 {
glennrp7c4c9e62011-03-21 20:23:32 +00008615 if (mng_info->write_png_colortype == 0)
8616 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008617
8618 if (image_depth == 8)
8619 {
8620 ping_trans_color.red&=0xff;
8621 ping_trans_color.green&=0xff;
8622 ping_trans_color.blue&=0xff;
8623 ping_trans_color.gray&=0xff;
8624 }
8625 }
8626 }
cristy3ed852e2009-09-05 21:47:34 +00008627 else
8628 {
cristy3ed852e2009-09-05 21:47:34 +00008629 if (image_depth == 8)
8630 {
glennrp5af765f2010-03-30 11:12:18 +00008631 ping_trans_color.red&=0xff;
8632 ping_trans_color.green&=0xff;
8633 ping_trans_color.blue&=0xff;
8634 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008635 }
8636 }
8637 }
glennrp8640fb52010-11-23 15:48:26 +00008638
cristy3ed852e2009-09-05 21:47:34 +00008639 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008640
glennrp2e09f552010-11-14 00:38:48 +00008641 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008642 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008643
glennrp39992b42010-11-14 00:03:43 +00008644 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008645 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008646 ping_have_color == MagickFalse &&
8647 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008648 {
cristy35ef8242010-06-03 16:24:13 +00008649 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008650
cristy3ed852e2009-09-05 21:47:34 +00008651 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008652 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008653
glennrp7c4c9e62011-03-21 20:23:32 +00008654 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008655 {
glennrp5af765f2010-03-30 11:12:18 +00008656 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008657
cristy3ed852e2009-09-05 21:47:34 +00008658 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008659 {
8660 if (logging != MagickFalse)
8661 {
8662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8663 " Scaling ping_trans_color (0)");
8664 }
8665 ping_trans_color.gray*=0x0101;
8666 }
cristy3ed852e2009-09-05 21:47:34 +00008667 }
glennrp0fe50b42010-11-16 03:52:51 +00008668
cristy3ed852e2009-09-05 21:47:34 +00008669 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8670 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008671
cristy85380932011-04-25 17:11:52 +00008672 if ((image_colors == 0) || ((ssize_t) (image_colors-1) > MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008673 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008674
cristy3ed852e2009-09-05 21:47:34 +00008675 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008676 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008677
cristy3ed852e2009-09-05 21:47:34 +00008678 else
8679 {
glennrp5af765f2010-03-30 11:12:18 +00008680 ping_bit_depth=8;
8681 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008682 {
8683 if(!mng_info->write_png_depth)
8684 {
glennrp5af765f2010-03-30 11:12:18 +00008685 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008686
cristy35ef8242010-06-03 16:24:13 +00008687 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008688 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008689 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008690 }
8691 }
glennrp2b013e42010-11-24 16:55:50 +00008692
glennrp0fe50b42010-11-16 03:52:51 +00008693 else if (ping_color_type ==
8694 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008695 mng_info->IsPalette)
8696 {
cristy3ed852e2009-09-05 21:47:34 +00008697 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008698
cristy3ed852e2009-09-05 21:47:34 +00008699 int
8700 depth_4_ok=MagickTrue,
8701 depth_2_ok=MagickTrue,
8702 depth_1_ok=MagickTrue;
8703
cristybb503372010-05-27 20:51:26 +00008704 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008705 {
8706 unsigned char
8707 intensity;
8708
8709 intensity=ScaleQuantumToChar(image->colormap[i].red);
8710
8711 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8712 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8713 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8714 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008715 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008716 depth_1_ok=MagickFalse;
8717 }
glennrp2b013e42010-11-24 16:55:50 +00008718
cristy3ed852e2009-09-05 21:47:34 +00008719 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008720 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008721
cristy3ed852e2009-09-05 21:47:34 +00008722 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008723 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008724
cristy3ed852e2009-09-05 21:47:34 +00008725 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008726 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008727 }
8728 }
glennrp2b013e42010-11-24 16:55:50 +00008729
glennrp5af765f2010-03-30 11:12:18 +00008730 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008731 }
glennrp0fe50b42010-11-16 03:52:51 +00008732
cristy3ed852e2009-09-05 21:47:34 +00008733 else
glennrp0fe50b42010-11-16 03:52:51 +00008734
cristy3ed852e2009-09-05 21:47:34 +00008735 if (mng_info->IsPalette)
8736 {
glennrp17a14852010-05-10 03:01:59 +00008737 number_colors=image_colors;
8738
cristy3ed852e2009-09-05 21:47:34 +00008739 if (image_depth <= 8)
8740 {
cristy3ed852e2009-09-05 21:47:34 +00008741 /*
8742 Set image palette.
8743 */
glennrp5af765f2010-03-30 11:12:18 +00008744 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008745
glennrp58e01762011-01-07 15:28:54 +00008746 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008747 {
glennrp9c1eb072010-06-06 22:19:15 +00008748 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008749
glennrp3b51f0e2010-11-27 18:14:08 +00008750 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8752 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008753 }
glennrp0fe50b42010-11-16 03:52:51 +00008754
cristy3ed852e2009-09-05 21:47:34 +00008755 else
8756 {
cristybb503372010-05-27 20:51:26 +00008757 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008758 {
8759 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8760 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8761 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8762 }
glennrp0fe50b42010-11-16 03:52:51 +00008763
glennrp3b51f0e2010-11-27 18:14:08 +00008764 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008766 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008767 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008768
glennrp39992b42010-11-14 00:03:43 +00008769 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008770 }
glennrp0fe50b42010-11-16 03:52:51 +00008771
cristy3ed852e2009-09-05 21:47:34 +00008772 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008773 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008774 {
cristybefe4d22010-06-07 01:18:58 +00008775 size_t
8776 one;
8777
glennrp5af765f2010-03-30 11:12:18 +00008778 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008779 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008780
cristybefe4d22010-06-07 01:18:58 +00008781 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008782 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008783 }
glennrp0fe50b42010-11-16 03:52:51 +00008784
glennrp5af765f2010-03-30 11:12:18 +00008785 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008786
glennrp58e01762011-01-07 15:28:54 +00008787 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008788 {
glennrp0fe50b42010-11-16 03:52:51 +00008789 /*
glennrpd6bf1612010-12-17 17:28:54 +00008790 * Set up trans_colors array.
8791 */
glennrp0fe50b42010-11-16 03:52:51 +00008792 assert(number_colors <= 256);
8793
glennrpd6bf1612010-12-17 17:28:54 +00008794 ping_num_trans=(unsigned short) (number_transparent +
8795 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008796
8797 if (ping_num_trans == 0)
8798 ping_have_tRNS=MagickFalse;
8799
glennrpd6bf1612010-12-17 17:28:54 +00008800 else
glennrp0fe50b42010-11-16 03:52:51 +00008801 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008802 if (logging != MagickFalse)
8803 {
8804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8805 " Scaling ping_trans_color (1)");
8806 }
glennrpd6bf1612010-12-17 17:28:54 +00008807 ping_have_tRNS=MagickTrue;
8808
8809 for (i=0; i < ping_num_trans; i++)
8810 {
8811 ping_trans_alpha[i]= (png_byte) (255-
8812 ScaleQuantumToChar(image->colormap[i].opacity));
8813 }
glennrp0fe50b42010-11-16 03:52:51 +00008814 }
8815 }
cristy3ed852e2009-09-05 21:47:34 +00008816 }
8817 }
glennrp0fe50b42010-11-16 03:52:51 +00008818
cristy3ed852e2009-09-05 21:47:34 +00008819 else
8820 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008821
cristy3ed852e2009-09-05 21:47:34 +00008822 if (image_depth < 8)
8823 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008824
cristy3ed852e2009-09-05 21:47:34 +00008825 if ((save_image_depth == 16) && (image_depth == 8))
8826 {
glennrp4f25bd02011-01-01 18:51:28 +00008827 if (logging != MagickFalse)
8828 {
8829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8830 " Scaling ping_trans_color from (%d,%d,%d)",
8831 (int) ping_trans_color.red,
8832 (int) ping_trans_color.green,
8833 (int) ping_trans_color.blue);
8834 }
8835
glennrp5af765f2010-03-30 11:12:18 +00008836 ping_trans_color.red*=0x0101;
8837 ping_trans_color.green*=0x0101;
8838 ping_trans_color.blue*=0x0101;
8839 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008840
8841 if (logging != MagickFalse)
8842 {
8843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8844 " to (%d,%d,%d)",
8845 (int) ping_trans_color.red,
8846 (int) ping_trans_color.green,
8847 (int) ping_trans_color.blue);
8848 }
cristy3ed852e2009-09-05 21:47:34 +00008849 }
8850 }
8851
cristy4383ec82011-01-05 15:42:32 +00008852 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8853 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008854
cristy3ed852e2009-09-05 21:47:34 +00008855 /*
8856 Adjust background and transparency samples in sub-8-bit grayscale files.
8857 */
glennrp5af765f2010-03-30 11:12:18 +00008858 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008859 PNG_COLOR_TYPE_GRAY)
8860 {
8861 png_uint_16
8862 maxval;
8863
cristy35ef8242010-06-03 16:24:13 +00008864 size_t
8865 one=1;
8866
cristy22ffd972010-06-03 16:51:47 +00008867 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008868
glennrp4f25bd02011-01-01 18:51:28 +00008869 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008870 {
cristy3ed852e2009-09-05 21:47:34 +00008871
glennrpa521b2f2010-10-29 04:11:03 +00008872 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008873 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8874
8875 if (logging != MagickFalse)
8876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008877 " Setting up bKGD chunk (2)");
glennrp3b51f0e2010-11-27 18:14:08 +00008878
glennrp991d11d2010-11-12 21:55:28 +00008879 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008880 }
cristy3ed852e2009-09-05 21:47:34 +00008881
glennrp5af765f2010-03-30 11:12:18 +00008882 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8883 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008884 }
glennrp17a14852010-05-10 03:01:59 +00008885
glennrp26f37912010-12-23 16:22:42 +00008886 if (ping_exclude_bKGD == MagickFalse)
8887 {
glennrp1273f7b2011-02-24 03:20:30 +00008888 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008889 {
8890 /*
8891 Identify which colormap entry is the background color.
8892 */
8893
glennrp17a14852010-05-10 03:01:59 +00008894 number_colors=image_colors;
8895
glennrpa521b2f2010-10-29 04:11:03 +00008896 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8897 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008898 break;
8899
8900 ping_background.index=(png_byte) i;
8901
glennrp3b51f0e2010-11-27 18:14:08 +00008902 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008903 {
8904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008905 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008906 }
glennrp0fe50b42010-11-16 03:52:51 +00008907
cristy13d07042010-11-21 20:56:18 +00008908 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008909 {
8910 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008911
8912 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008913 {
8914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8915 " background =(%d,%d,%d)",
8916 (int) ping_background.red,
8917 (int) ping_background.green,
8918 (int) ping_background.blue);
8919 }
8920 }
glennrpa521b2f2010-10-29 04:11:03 +00008921
glennrpd6bf1612010-12-17 17:28:54 +00008922 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008923 {
glennrp3b51f0e2010-11-27 18:14:08 +00008924 if (logging != MagickFalse)
8925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8926 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008927 ping_have_bKGD = MagickFalse;
8928 }
glennrp17a14852010-05-10 03:01:59 +00008929 }
glennrp26f37912010-12-23 16:22:42 +00008930 }
glennrp17a14852010-05-10 03:01:59 +00008931
cristy3ed852e2009-09-05 21:47:34 +00008932 if (logging != MagickFalse)
8933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00008934 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008935 /*
8936 Initialize compression level and filtering.
8937 */
8938 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008939 {
8940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8941 " Setting up deflate compression");
8942
8943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8944 " Compression buffer size: 32768");
8945 }
8946
cristy3ed852e2009-09-05 21:47:34 +00008947 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00008948
cristy3ed852e2009-09-05 21:47:34 +00008949 if (logging != MagickFalse)
8950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8951 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00008952
cristy3ed852e2009-09-05 21:47:34 +00008953 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00008954
cristy3ed852e2009-09-05 21:47:34 +00008955 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8956 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00008957
cristy3ed852e2009-09-05 21:47:34 +00008958 if (quality > 9)
8959 {
8960 int
8961 level;
8962
cristybb503372010-05-27 20:51:26 +00008963 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00008964
cristy3ed852e2009-09-05 21:47:34 +00008965 if (logging != MagickFalse)
8966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8967 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00008968
cristy3ed852e2009-09-05 21:47:34 +00008969 png_set_compression_level(ping,level);
8970 }
glennrp0fe50b42010-11-16 03:52:51 +00008971
cristy3ed852e2009-09-05 21:47:34 +00008972 else
8973 {
8974 if (logging != MagickFalse)
8975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8976 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00008977
cristy3ed852e2009-09-05 21:47:34 +00008978 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8979 }
glennrp0fe50b42010-11-16 03:52:51 +00008980
cristy3ed852e2009-09-05 21:47:34 +00008981 if (logging != MagickFalse)
8982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8983 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00008984
glennrp2b013e42010-11-24 16:55:50 +00008985#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00008986 /* This became available in libpng-1.0.9. Output must be a MNG. */
8987 if (mng_info->write_mng && ((quality % 10) == 7))
8988 {
8989 if (logging != MagickFalse)
8990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8991 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00008992
glennrp5af765f2010-03-30 11:12:18 +00008993 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00008994 }
glennrp0fe50b42010-11-16 03:52:51 +00008995
cristy3ed852e2009-09-05 21:47:34 +00008996 else
8997 if (logging != MagickFalse)
8998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8999 " Filter_type: 0");
9000#endif
glennrp2b013e42010-11-24 16:55:50 +00009001
cristy3ed852e2009-09-05 21:47:34 +00009002 {
9003 int
9004 base_filter;
9005
9006 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009007 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009008
glennrp26c990a2010-11-23 02:23:20 +00009009 else
glennrp8640fb52010-11-23 15:48:26 +00009010 if ((quality % 10) != 5)
9011 base_filter=(int) quality % 10;
9012
9013 else
9014 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9015 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9016 (quality < 50))
9017 base_filter=PNG_NO_FILTERS;
9018
9019 else
9020 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009021
cristy3ed852e2009-09-05 21:47:34 +00009022 if (logging != MagickFalse)
9023 {
9024 if (base_filter == PNG_ALL_FILTERS)
9025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9026 " Base filter method: ADAPTIVE");
9027 else
9028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029 " Base filter method: NONE");
9030 }
glennrp2b013e42010-11-24 16:55:50 +00009031
cristy3ed852e2009-09-05 21:47:34 +00009032 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9033 }
9034
glennrp823b55c2011-03-14 18:46:46 +00009035 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9036 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009037 {
9038 ResetImageProfileIterator(image);
9039 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009040 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009041 profile=GetImageProfile(image,name);
9042
9043 if (profile != (StringInfo *) NULL)
9044 {
glennrp5af765f2010-03-30 11:12:18 +00009045#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009046 if ((LocaleCompare(name,"ICC") == 0) ||
9047 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009048 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009049
9050 if (ping_exclude_iCCP == MagickFalse)
9051 {
9052 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009053#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009054 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009055#else
9056 (png_const_bytep) GetStringInfoDatum(profile),
9057#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009058 (png_uint_32) GetStringInfoLength(profile));
9059 }
glennrp26f37912010-12-23 16:22:42 +00009060 }
glennrp0fe50b42010-11-16 03:52:51 +00009061
glennrpc8cbc5d2011-01-01 00:12:34 +00009062 else
cristy3ed852e2009-09-05 21:47:34 +00009063#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009064 if (ping_exclude_zCCP == MagickFalse)
9065 {
glennrpcf002022011-01-30 02:38:15 +00009066 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009067 (unsigned char *) name,(unsigned char *) name,
9068 GetStringInfoDatum(profile),
9069 (png_uint_32) GetStringInfoLength(profile));
9070 }
9071 }
glennrp0b206f52011-01-07 04:55:32 +00009072
glennrpc8cbc5d2011-01-01 00:12:34 +00009073 if (logging != MagickFalse)
9074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9075 " Setting up text chunk with %s profile",name);
9076
9077 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009078 }
cristy3ed852e2009-09-05 21:47:34 +00009079 }
9080
9081#if defined(PNG_WRITE_sRGB_SUPPORTED)
9082 if ((mng_info->have_write_global_srgb == 0) &&
9083 ((image->rendering_intent != UndefinedIntent) ||
9084 (image->colorspace == sRGBColorspace)))
9085 {
glennrp26f37912010-12-23 16:22:42 +00009086 if (ping_exclude_sRGB == MagickFalse)
9087 {
9088 /*
9089 Note image rendering intent.
9090 */
9091 if (logging != MagickFalse)
9092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9093 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009094
glennrp26f37912010-12-23 16:22:42 +00009095 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009096 Magick_RenderingIntent_to_PNG_RenderingIntent(
9097 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009098
glennrp26f37912010-12-23 16:22:42 +00009099 if (ping_exclude_gAMA == MagickFalse)
9100 png_set_gAMA(ping,ping_info,0.45455);
9101 }
cristy3ed852e2009-09-05 21:47:34 +00009102 }
glennrp26f37912010-12-23 16:22:42 +00009103
glennrp5af765f2010-03-30 11:12:18 +00009104 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009105#endif
9106 {
glennrp2cc891a2010-12-24 13:44:32 +00009107 if (ping_exclude_gAMA == MagickFalse &&
9108 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009109 (image->gamma < .45 || image->gamma > .46)))
9110 {
cristy3ed852e2009-09-05 21:47:34 +00009111 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9112 {
9113 /*
9114 Note image gamma.
9115 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9116 */
9117 if (logging != MagickFalse)
9118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9119 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009120
cristy3ed852e2009-09-05 21:47:34 +00009121 png_set_gAMA(ping,ping_info,image->gamma);
9122 }
glennrp26f37912010-12-23 16:22:42 +00009123 }
glennrp2b013e42010-11-24 16:55:50 +00009124
glennrp26f37912010-12-23 16:22:42 +00009125 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009126 {
glennrp26f37912010-12-23 16:22:42 +00009127 if ((mng_info->have_write_global_chrm == 0) &&
9128 (image->chromaticity.red_primary.x != 0.0))
9129 {
9130 /*
9131 Note image chromaticity.
9132 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9133 */
9134 PrimaryInfo
9135 bp,
9136 gp,
9137 rp,
9138 wp;
cristy3ed852e2009-09-05 21:47:34 +00009139
glennrp26f37912010-12-23 16:22:42 +00009140 wp=image->chromaticity.white_point;
9141 rp=image->chromaticity.red_primary;
9142 gp=image->chromaticity.green_primary;
9143 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009144
glennrp26f37912010-12-23 16:22:42 +00009145 if (logging != MagickFalse)
9146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9147 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009148
glennrp26f37912010-12-23 16:22:42 +00009149 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9150 bp.x,bp.y);
9151 }
9152 }
cristy3ed852e2009-09-05 21:47:34 +00009153 }
glennrpdfd70802010-11-14 01:23:35 +00009154
glennrp5af765f2010-03-30 11:12:18 +00009155 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009156
9157 if (mng_info->write_mng)
9158 png_set_sig_bytes(ping,8);
9159
9160 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9161
glennrpd6bf1612010-12-17 17:28:54 +00009162 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009163 {
9164 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009165 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009166 {
glennrp5af765f2010-03-30 11:12:18 +00009167 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009168
glennrp5af765f2010-03-30 11:12:18 +00009169 if (ping_bit_depth < 8)
9170 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009171 }
glennrp0fe50b42010-11-16 03:52:51 +00009172
cristy3ed852e2009-09-05 21:47:34 +00009173 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009174 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009175 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009176 }
9177
glennrp0e8ea192010-12-24 18:00:33 +00009178 if (ping_need_colortype_warning != MagickFalse ||
9179 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009180 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009181 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009182 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009183 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009184 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009185 {
9186 if (logging != MagickFalse)
9187 {
glennrp0e8ea192010-12-24 18:00:33 +00009188 if (ping_need_colortype_warning != MagickFalse)
9189 {
9190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9191 " Image has transparency but tRNS chunk was excluded");
9192 }
9193
cristy3ed852e2009-09-05 21:47:34 +00009194 if (mng_info->write_png_depth)
9195 {
9196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9197 " Defined PNG:bit-depth=%u, Computed depth=%u",
9198 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009199 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009200 }
glennrp0e8ea192010-12-24 18:00:33 +00009201
cristy3ed852e2009-09-05 21:47:34 +00009202 if (mng_info->write_png_colortype)
9203 {
9204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9205 " Defined PNG:color-type=%u, Computed color type=%u",
9206 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009207 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009208 }
9209 }
glennrp0e8ea192010-12-24 18:00:33 +00009210
glennrp3bd2e412010-08-10 13:34:52 +00009211 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009212 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9213 }
9214
glennrp58e01762011-01-07 15:28:54 +00009215 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009216 {
9217 /* Add an opaque matte channel */
9218 image->matte = MagickTrue;
9219 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009220
glennrpb4a13412010-05-05 12:47:19 +00009221 if (logging != MagickFalse)
9222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9223 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009224 }
9225
glennrp0e319732011-01-25 21:53:13 +00009226 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009227 {
glennrp991d11d2010-11-12 21:55:28 +00009228 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009229 {
glennrp991d11d2010-11-12 21:55:28 +00009230 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009231 if (logging != MagickFalse)
9232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9233 " Setting ping_have_tRNS=MagickTrue.");
9234 }
glennrpe9c26dc2010-05-30 01:56:35 +00009235 }
9236
cristy3ed852e2009-09-05 21:47:34 +00009237 if (logging != MagickFalse)
9238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9239 " Writing PNG header chunks");
9240
glennrp5af765f2010-03-30 11:12:18 +00009241 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9242 ping_bit_depth,ping_color_type,
9243 ping_interlace_method,ping_compression_method,
9244 ping_filter_method);
9245
glennrp39992b42010-11-14 00:03:43 +00009246 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9247 {
glennrpf09bded2011-01-08 01:15:59 +00009248 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009249
glennrp3b51f0e2010-11-27 18:14:08 +00009250 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009251 {
glennrp8640fb52010-11-23 15:48:26 +00009252 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009253 {
glennrpd6bf1612010-12-17 17:28:54 +00009254 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009256 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9257 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009258 (int) palette[i].red,
9259 (int) palette[i].green,
9260 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009261 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009262 (int) ping_trans_alpha[i]);
9263 else
9264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009265 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009266 (int) i,
9267 (int) palette[i].red,
9268 (int) palette[i].green,
9269 (int) palette[i].blue);
9270 }
glennrp39992b42010-11-14 00:03:43 +00009271 }
glennrp39992b42010-11-14 00:03:43 +00009272 }
9273
glennrp26f37912010-12-23 16:22:42 +00009274 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009275 {
glennrp26f37912010-12-23 16:22:42 +00009276 if (ping_have_bKGD != MagickFalse)
9277 png_set_bKGD(ping,ping_info,&ping_background);
9278 }
9279
9280 if (ping_exclude_pHYs == MagickFalse)
9281 {
9282 if (ping_have_pHYs != MagickFalse)
9283 {
9284 png_set_pHYs(ping,ping_info,
9285 ping_pHYs_x_resolution,
9286 ping_pHYs_y_resolution,
9287 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009288
9289 if (logging)
9290 {
9291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9292 " Setting up pHYs chunk");
9293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9294 " x_resolution=%lu",
9295 (unsigned long) ping_pHYs_x_resolution);
9296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9297 " y_resolution=%lu",
9298 (unsigned long) ping_pHYs_y_resolution);
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300 " unit_type=%lu",
9301 (unsigned long) ping_pHYs_unit_type);
9302 }
glennrp26f37912010-12-23 16:22:42 +00009303 }
glennrpdfd70802010-11-14 01:23:35 +00009304 }
9305
9306#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009307 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009308 {
glennrp26f37912010-12-23 16:22:42 +00009309 if (image->page.x || image->page.y)
9310 {
9311 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9312 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009313
glennrp26f37912010-12-23 16:22:42 +00009314 if (logging != MagickFalse)
9315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9316 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9317 (int) image->page.x, (int) image->page.y);
9318 }
glennrpdfd70802010-11-14 01:23:35 +00009319 }
9320#endif
9321
glennrpda8f3a72011-02-27 23:54:12 +00009322 if (mng_info->need_blob != MagickFalse)
9323 {
9324 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9325 MagickFalse)
9326 png_error(ping,"WriteBlob Failed");
9327
9328 ping_have_blob=MagickTrue;
9329 }
9330
cristy3ed852e2009-09-05 21:47:34 +00009331 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009332
glennrp39992b42010-11-14 00:03:43 +00009333 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009334 {
glennrp3b51f0e2010-11-27 18:14:08 +00009335 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009336 {
9337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9338 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9339 }
9340
9341 if (ping_color_type == 3)
9342 (void) png_set_tRNS(ping, ping_info,
9343 ping_trans_alpha,
9344 ping_num_trans,
9345 NULL);
9346
9347 else
9348 {
9349 (void) png_set_tRNS(ping, ping_info,
9350 NULL,
9351 0,
9352 &ping_trans_color);
9353
glennrp3b51f0e2010-11-27 18:14:08 +00009354 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009355 {
9356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009357 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009358 (int) ping_trans_color.red,
9359 (int) ping_trans_color.green,
9360 (int) ping_trans_color.blue);
9361 }
9362 }
glennrp991d11d2010-11-12 21:55:28 +00009363 }
9364
cristy3ed852e2009-09-05 21:47:34 +00009365 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009366 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009367
cristy3ed852e2009-09-05 21:47:34 +00009368 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009369
cristy3ed852e2009-09-05 21:47:34 +00009370 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009371 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009372
glennrp26f37912010-12-23 16:22:42 +00009373 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009374 {
glennrp4f25bd02011-01-01 18:51:28 +00009375 if ((image->page.width != 0 && image->page.width != image->columns) ||
9376 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009377 {
9378 unsigned char
9379 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009380
glennrp26f37912010-12-23 16:22:42 +00009381 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9382 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009383 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009384 PNGLong(chunk+4,(png_uint_32) image->page.width);
9385 PNGLong(chunk+8,(png_uint_32) image->page.height);
9386 chunk[12]=0; /* unit = pixels */
9387 (void) WriteBlob(image,13,chunk);
9388 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9389 }
cristy3ed852e2009-09-05 21:47:34 +00009390 }
9391
9392#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009393 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009394#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009395 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009396#undef PNG_HAVE_IDAT
9397#endif
9398
9399 png_set_packing(ping);
9400 /*
9401 Allocate memory.
9402 */
9403 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009404 if (image_depth > 8)
9405 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009406 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009407 {
glennrpb4a13412010-05-05 12:47:19 +00009408 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009409 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009410 break;
glennrp0fe50b42010-11-16 03:52:51 +00009411
glennrpb4a13412010-05-05 12:47:19 +00009412 case PNG_COLOR_TYPE_GRAY_ALPHA:
9413 rowbytes*=2;
9414 break;
glennrp0fe50b42010-11-16 03:52:51 +00009415
glennrpb4a13412010-05-05 12:47:19 +00009416 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009417 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009418 break;
glennrp0fe50b42010-11-16 03:52:51 +00009419
glennrpb4a13412010-05-05 12:47:19 +00009420 default:
9421 break;
cristy3ed852e2009-09-05 21:47:34 +00009422 }
glennrp3b51f0e2010-11-27 18:14:08 +00009423
9424 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009425 {
9426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9427 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009428
glennrpb4a13412010-05-05 12:47:19 +00009429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009430 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009431 }
glennrpcf002022011-01-30 02:38:15 +00009432 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9433 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009434
glennrpcf002022011-01-30 02:38:15 +00009435 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009436 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009437
cristy3ed852e2009-09-05 21:47:34 +00009438 /*
9439 Initialize image scanlines.
9440 */
glennrp5af765f2010-03-30 11:12:18 +00009441 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009442 {
9443 /*
9444 PNG write failed.
9445 */
9446#ifdef PNG_DEBUG
9447 if (image_info->verbose)
9448 (void) printf("PNG write has failed.\n");
9449#endif
9450 png_destroy_write_struct(&ping,&ping_info);
9451 if (quantum_info != (QuantumInfo *) NULL)
9452 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009453 if (ping_pixels != (unsigned char *) NULL)
9454 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009455#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009456 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009457#endif
glennrpda8f3a72011-02-27 23:54:12 +00009458 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009459 (void) CloseBlob(image);
9460 image_info=DestroyImageInfo(image_info);
9461 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009462 return(MagickFalse);
9463 }
cristyed552522009-10-16 14:04:35 +00009464 quantum_info=AcquireQuantumInfo(image_info,image);
9465 if (quantum_info == (QuantumInfo *) NULL)
9466 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009467 quantum_info->format=UndefinedQuantumFormat;
9468 quantum_info->depth=image_depth;
9469 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009470
cristy3ed852e2009-09-05 21:47:34 +00009471 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009472 !mng_info->write_png32) &&
9473 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009474 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009475 image_matte == MagickFalse &&
9476 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009477 {
glennrp8bb3a022010-12-13 20:40:04 +00009478 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009479 register const PixelPacket
9480 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009481
cristy3ed852e2009-09-05 21:47:34 +00009482 quantum_info->depth=8;
9483 for (pass=0; pass < num_passes; pass++)
9484 {
9485 /*
9486 Convert PseudoClass image to a PNG monochrome image.
9487 */
cristybb503372010-05-27 20:51:26 +00009488 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009489 {
glennrpd71e86a2011-02-24 01:28:37 +00009490 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9492 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009493
cristy3ed852e2009-09-05 21:47:34 +00009494 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009495
cristy3ed852e2009-09-05 21:47:34 +00009496 if (p == (const PixelPacket *) NULL)
9497 break;
glennrp0fe50b42010-11-16 03:52:51 +00009498
cristy3ed852e2009-09-05 21:47:34 +00009499 if (mng_info->IsPalette)
9500 {
9501 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009502 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009503 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9504 mng_info->write_png_depth &&
9505 mng_info->write_png_depth != old_bit_depth)
9506 {
9507 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009508 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009509 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009510 >> (8-old_bit_depth));
9511 }
9512 }
glennrp0fe50b42010-11-16 03:52:51 +00009513
cristy3ed852e2009-09-05 21:47:34 +00009514 else
9515 {
9516 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009517 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009518 }
glennrp0fe50b42010-11-16 03:52:51 +00009519
cristy3ed852e2009-09-05 21:47:34 +00009520 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009521 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009522 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009523 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009524
glennrp3b51f0e2010-11-27 18:14:08 +00009525 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9527 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009528
glennrpcf002022011-01-30 02:38:15 +00009529 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009530 }
9531 if (image->previous == (Image *) NULL)
9532 {
9533 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9534 if (status == MagickFalse)
9535 break;
9536 }
9537 }
9538 }
glennrp0fe50b42010-11-16 03:52:51 +00009539
glennrp8bb3a022010-12-13 20:40:04 +00009540 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009541 {
glennrp0fe50b42010-11-16 03:52:51 +00009542 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009543 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009544 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009545 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009546 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009547 {
glennrp8bb3a022010-12-13 20:40:04 +00009548 register const PixelPacket
9549 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009550
glennrp8bb3a022010-12-13 20:40:04 +00009551 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009552 {
glennrp8bb3a022010-12-13 20:40:04 +00009553
cristybb503372010-05-27 20:51:26 +00009554 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009555 {
9556 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009557
cristy3ed852e2009-09-05 21:47:34 +00009558 if (p == (const PixelPacket *) NULL)
9559 break;
glennrp2cc891a2010-12-24 13:44:32 +00009560
glennrp5af765f2010-03-30 11:12:18 +00009561 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009562 {
glennrp8bb3a022010-12-13 20:40:04 +00009563 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009564 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009565 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009566
glennrp8bb3a022010-12-13 20:40:04 +00009567 else
9568 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009569 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009570
glennrp3b51f0e2010-11-27 18:14:08 +00009571 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009573 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009574 }
glennrp2cc891a2010-12-24 13:44:32 +00009575
glennrp8bb3a022010-12-13 20:40:04 +00009576 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9577 {
9578 if (logging != MagickFalse && y == 0)
9579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9580 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009581
glennrp8bb3a022010-12-13 20:40:04 +00009582 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009583 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009584 }
glennrp2cc891a2010-12-24 13:44:32 +00009585
glennrp3b51f0e2010-11-27 18:14:08 +00009586 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009588 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009589
glennrpcf002022011-01-30 02:38:15 +00009590 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009591 }
glennrp2cc891a2010-12-24 13:44:32 +00009592
glennrp8bb3a022010-12-13 20:40:04 +00009593 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009594 {
glennrp8bb3a022010-12-13 20:40:04 +00009595 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9596 if (status == MagickFalse)
9597 break;
cristy3ed852e2009-09-05 21:47:34 +00009598 }
cristy3ed852e2009-09-05 21:47:34 +00009599 }
9600 }
glennrp8bb3a022010-12-13 20:40:04 +00009601
9602 else
9603 {
9604 register const PixelPacket
9605 *p;
9606
9607 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009608 {
glennrp8bb3a022010-12-13 20:40:04 +00009609 if ((image_depth > 8) || (mng_info->write_png24 ||
9610 mng_info->write_png32 ||
9611 (!mng_info->write_png8 && !mng_info->IsPalette)))
9612 {
9613 for (y=0; y < (ssize_t) image->rows; y++)
9614 {
9615 p=GetVirtualPixels(image,0,y,image->columns,1,
9616 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009617
glennrp8bb3a022010-12-13 20:40:04 +00009618 if (p == (const PixelPacket *) NULL)
9619 break;
glennrp2cc891a2010-12-24 13:44:32 +00009620
glennrp8bb3a022010-12-13 20:40:04 +00009621 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9622 {
9623 if (image->storage_class == DirectClass)
9624 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009625 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009626
glennrp8bb3a022010-12-13 20:40:04 +00009627 else
9628 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009629 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009630 }
glennrp2cc891a2010-12-24 13:44:32 +00009631
glennrp8bb3a022010-12-13 20:40:04 +00009632 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9633 {
9634 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009635 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009636 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009637
glennrp8bb3a022010-12-13 20:40:04 +00009638 if (logging != MagickFalse && y == 0)
9639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9640 " Writing GRAY_ALPHA PNG pixels (3)");
9641 }
glennrp2cc891a2010-12-24 13:44:32 +00009642
glennrp8bb3a022010-12-13 20:40:04 +00009643 else if (image_matte != MagickFalse)
9644 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009645 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009646
glennrp8bb3a022010-12-13 20:40:04 +00009647 else
9648 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009649 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009650
glennrp8bb3a022010-12-13 20:40:04 +00009651 if (logging != MagickFalse && y == 0)
9652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9653 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009654
glennrpcf002022011-01-30 02:38:15 +00009655 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009656 }
9657 }
glennrp2cc891a2010-12-24 13:44:32 +00009658
glennrp8bb3a022010-12-13 20:40:04 +00009659 else
9660 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9661 mng_info->write_png32 ||
9662 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9663 {
9664 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9665 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9666 {
9667 if (logging != MagickFalse)
9668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9669 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009670
glennrp8bb3a022010-12-13 20:40:04 +00009671 quantum_info->depth=8;
9672 image_depth=8;
9673 }
glennrp2cc891a2010-12-24 13:44:32 +00009674
glennrp8bb3a022010-12-13 20:40:04 +00009675 for (y=0; y < (ssize_t) image->rows; y++)
9676 {
9677 if (logging != MagickFalse && y == 0)
9678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9679 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009680
glennrp770d1932011-03-06 22:11:17 +00009681 p=GetVirtualPixels(image,0,y,image->columns,1,
9682 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009683
glennrp8bb3a022010-12-13 20:40:04 +00009684 if (p == (const PixelPacket *) NULL)
9685 break;
glennrp2cc891a2010-12-24 13:44:32 +00009686
glennrp8bb3a022010-12-13 20:40:04 +00009687 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009688 {
glennrp4bf89732011-03-21 13:48:28 +00009689 quantum_info->depth=image->depth;
9690
glennrp44757ab2011-03-17 12:57:03 +00009691 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009692 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009693 }
glennrp2cc891a2010-12-24 13:44:32 +00009694
glennrp8bb3a022010-12-13 20:40:04 +00009695 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9696 {
9697 if (logging != MagickFalse && y == 0)
9698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9699 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009700
glennrp8bb3a022010-12-13 20:40:04 +00009701 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009702 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009703 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009704 }
glennrp2cc891a2010-12-24 13:44:32 +00009705
glennrp8bb3a022010-12-13 20:40:04 +00009706 else
glennrp8bb3a022010-12-13 20:40:04 +00009707 {
glennrp179d0752011-03-17 13:02:10 +00009708 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009709 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9710
9711 if (logging != MagickFalse && y <= 2)
9712 {
9713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009714 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009715
9716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9717 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9718 (int)ping_pixels[0],(int)ping_pixels[1]);
9719 }
glennrp8bb3a022010-12-13 20:40:04 +00009720 }
glennrpcf002022011-01-30 02:38:15 +00009721 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009722 }
9723 }
glennrp2cc891a2010-12-24 13:44:32 +00009724
glennrp8bb3a022010-12-13 20:40:04 +00009725 if (image->previous == (Image *) NULL)
9726 {
9727 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9728 if (status == MagickFalse)
9729 break;
9730 }
cristy3ed852e2009-09-05 21:47:34 +00009731 }
glennrp8bb3a022010-12-13 20:40:04 +00009732 }
9733 }
9734
cristyb32b90a2009-09-07 21:45:48 +00009735 if (quantum_info != (QuantumInfo *) NULL)
9736 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009737
9738 if (logging != MagickFalse)
9739 {
9740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009741 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009742
cristy3ed852e2009-09-05 21:47:34 +00009743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009744 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009745
cristy3ed852e2009-09-05 21:47:34 +00009746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009747 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009748
cristy3ed852e2009-09-05 21:47:34 +00009749 if (mng_info->write_png_depth)
9750 {
9751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9752 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9753 }
glennrp0fe50b42010-11-16 03:52:51 +00009754
cristy3ed852e2009-09-05 21:47:34 +00009755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009756 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009757
cristy3ed852e2009-09-05 21:47:34 +00009758 if (mng_info->write_png_colortype)
9759 {
9760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9761 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9762 }
glennrp0fe50b42010-11-16 03:52:51 +00009763
cristy3ed852e2009-09-05 21:47:34 +00009764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009765 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009766
cristy3ed852e2009-09-05 21:47:34 +00009767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009768 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009769 }
9770 /*
glennrpa0ed0092011-04-18 16:36:29 +00009771 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009772 */
glennrp823b55c2011-03-14 18:46:46 +00009773 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009774 {
glennrp26f37912010-12-23 16:22:42 +00009775 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009776 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009777 while (property != (const char *) NULL)
9778 {
9779 png_textp
9780 text;
glennrp2cc891a2010-12-24 13:44:32 +00009781
glennrp26f37912010-12-23 16:22:42 +00009782 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009783
9784 /* Don't write any "png:" properties; those are just for "identify" */
9785 if (LocaleNCompare(property,"png:",4) != 0 &&
9786
9787 /* Suppress density and units if we wrote a pHYs chunk */
9788 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009789 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009790 LocaleCompare(property,"units") != 0) &&
9791
9792 /* Suppress the IM-generated Date:create and Date:modify */
9793 (ping_exclude_date == MagickFalse ||
9794 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009795 {
glennrpc70af4a2011-03-07 00:08:23 +00009796 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009797 {
glennrpc70af4a2011-03-07 00:08:23 +00009798 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9799 text[0].key=(char *) property;
9800 text[0].text=(char *) value;
9801 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009802
glennrpc70af4a2011-03-07 00:08:23 +00009803 if (ping_exclude_tEXt != MagickFalse)
9804 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9805
9806 else if (ping_exclude_zTXt != MagickFalse)
9807 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9808
9809 else
glennrp26f37912010-12-23 16:22:42 +00009810 {
glennrpc70af4a2011-03-07 00:08:23 +00009811 text[0].compression=image_info->compression == NoCompression ||
9812 (image_info->compression == UndefinedCompression &&
9813 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9814 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009815 }
glennrp2cc891a2010-12-24 13:44:32 +00009816
glennrpc70af4a2011-03-07 00:08:23 +00009817 if (logging != MagickFalse)
9818 {
9819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9820 " Setting up text chunk");
9821
9822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9823 " keyword: %s",text[0].key);
9824 }
9825
9826 png_set_text(ping,ping_info,text,1);
9827 png_free(ping,text);
9828 }
glennrp26f37912010-12-23 16:22:42 +00009829 }
9830 property=GetNextImageProperty(image);
9831 }
cristy3ed852e2009-09-05 21:47:34 +00009832 }
9833
9834 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009835 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009836
9837 if (logging != MagickFalse)
9838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9839 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009840
cristy3ed852e2009-09-05 21:47:34 +00009841 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009842
cristy3ed852e2009-09-05 21:47:34 +00009843 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9844 {
9845 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009846 (ping_width != mng_info->page.width) ||
9847 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009848 {
9849 unsigned char
9850 chunk[32];
9851
9852 /*
9853 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9854 */
9855 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9856 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009857 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009858 chunk[4]=4;
9859 chunk[5]=0; /* frame name separator (no name) */
9860 chunk[6]=1; /* flag for changing delay, for next frame only */
9861 chunk[7]=0; /* flag for changing frame timeout */
9862 chunk[8]=1; /* flag for changing frame clipping for next frame */
9863 chunk[9]=0; /* flag for changing frame sync_id */
9864 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9865 chunk[14]=0; /* clipping boundaries delta type */
9866 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9867 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009868 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009869 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9870 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009871 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009872 (void) WriteBlob(image,31,chunk);
9873 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9874 mng_info->old_framing_mode=4;
9875 mng_info->framing_mode=1;
9876 }
glennrp0fe50b42010-11-16 03:52:51 +00009877
cristy3ed852e2009-09-05 21:47:34 +00009878 else
9879 mng_info->framing_mode=3;
9880 }
9881 if (mng_info->write_mng && !mng_info->need_fram &&
9882 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009883 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009884 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009885 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009886
cristy3ed852e2009-09-05 21:47:34 +00009887 /*
9888 Free PNG resources.
9889 */
glennrp5af765f2010-03-30 11:12:18 +00009890
cristy3ed852e2009-09-05 21:47:34 +00009891 png_destroy_write_struct(&ping,&ping_info);
9892
glennrpcf002022011-01-30 02:38:15 +00009893 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009894
9895#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009896 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009897#endif
9898
glennrpda8f3a72011-02-27 23:54:12 +00009899 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009900 (void) CloseBlob(image);
9901
9902 image_info=DestroyImageInfo(image_info);
9903 image=DestroyImage(image);
9904
9905 /* Store bit depth actually written */
9906 s[0]=(char) ping_bit_depth;
9907 s[1]='\0';
9908
9909 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9910
cristy3ed852e2009-09-05 21:47:34 +00009911 if (logging != MagickFalse)
9912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9913 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009914
cristy3ed852e2009-09-05 21:47:34 +00009915 return(MagickTrue);
9916/* End write one PNG image */
9917}
9918
9919/*
9920%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9921% %
9922% %
9923% %
9924% W r i t e P N G I m a g e %
9925% %
9926% %
9927% %
9928%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9929%
9930% WritePNGImage() writes a Portable Network Graphics (PNG) or
9931% Multiple-image Network Graphics (MNG) image file.
9932%
9933% MNG support written by Glenn Randers-Pehrson, glennrp@image...
9934%
9935% The format of the WritePNGImage method is:
9936%
9937% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9938%
9939% A description of each parameter follows:
9940%
9941% o image_info: the image info.
9942%
9943% o image: The image.
9944%
9945% Returns MagickTrue on success, MagickFalse on failure.
9946%
9947% Communicating with the PNG encoder:
9948%
9949% While the datastream written is always in PNG format and normally would
9950% be given the "png" file extension, this method also writes the following
9951% pseudo-formats which are subsets of PNG:
9952%
glennrp5a39f372011-02-25 04:52:16 +00009953% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
9954% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +00009955% is present, the tRNS chunk must only have values 0 and 255
9956% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +00009957% transparent). If other values are present they will be
9958% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +00009959% colors are present, they will be quantized to the 4-4-4-1,
9960% 3-3-3-1, or 3-3-2-1 palette.
9961%
9962% If you want better quantization or dithering of the colors
9963% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +00009964% PNG encoder. The pixels contain 8-bit indices even if
9965% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +00009966% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +00009967% PNG grayscale type might be slightly more efficient. Please
9968% note that writing to the PNG8 format may result in loss
9969% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +00009970%
9971% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9972% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +00009973% one of the colors as transparent. The only loss incurred
9974% is reduction of sample depth to 8. If the image has more
9975% than one transparent color, has semitransparent pixels, or
9976% has an opaque pixel with the same RGB components as the
9977% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +00009978%
9979% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
9980% transparency is permitted, i.e., the alpha sample for
9981% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +00009982% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +00009983% The only loss in data is the reduction of the sample depth
9984% to 8.
cristy3ed852e2009-09-05 21:47:34 +00009985%
9986% o -define: For more precise control of the PNG output, you can use the
9987% Image options "png:bit-depth" and "png:color-type". These
9988% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +00009989% from the application programming interfaces. The options
9990% are case-independent and are converted to lowercase before
9991% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +00009992%
9993% png:color-type can be 0, 2, 3, 4, or 6.
9994%
9995% When png:color-type is 0 (Grayscale), png:bit-depth can
9996% be 1, 2, 4, 8, or 16.
9997%
9998% When png:color-type is 2 (RGB), png:bit-depth can
9999% be 8 or 16.
10000%
10001% When png:color-type is 3 (Indexed), png:bit-depth can
10002% be 1, 2, 4, or 8. This refers to the number of bits
10003% used to store the index. The color samples always have
10004% bit-depth 8 in indexed PNG files.
10005%
10006% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10007% png:bit-depth can be 8 or 16.
10008%
glennrp5a39f372011-02-25 04:52:16 +000010009% If the image cannot be written without loss with the requested bit-depth
10010% and color-type, a PNG file will not be written, and the encoder will
10011% return MagickFalse.
10012%
cristy3ed852e2009-09-05 21:47:34 +000010013% Since image encoders should not be responsible for the "heavy lifting",
10014% the user should make sure that ImageMagick has already reduced the
10015% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010016% transparency prior to attempting to write the image with depth, color,
10017% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010018%
glennrp97cefe22011-04-22 16:17:00 +000010019% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010020%
cristy3ed852e2009-09-05 21:47:34 +000010021% Note that another definition, "png:bit-depth-written" exists, but it
10022% is not intended for external use. It is only used internally by the
10023% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10024%
10025% It is possible to request that the PNG encoder write previously-formatted
10026% ancillary chunks in the output PNG file, using the "-profile" commandline
10027% option as shown below or by setting the profile via a programming
10028% interface:
10029%
10030% -profile PNG-chunk-x:<file>
10031%
10032% where x is a location flag and <file> is a file containing the chunk
10033% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010034% This encoder will compute the chunk length and CRC, so those must not
10035% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010036%
10037% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10038% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10039% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010040% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010041%
glennrpbb8a7332010-11-13 15:17:35 +000010042% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010043%
glennrp3241bd02010-12-12 04:36:28 +000010044% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010045%
glennrpd6afd542010-11-19 01:53:05 +000010046% o 32-bit depth is reduced to 16.
10047% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10048% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010049% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010050% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010051% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010052% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10053% this can be done without loss and a larger bit depth N was not
10054% requested via the "-define PNG:bit-depth=N" option.
10055% o If matte channel is present but only one transparent color is
10056% present, RGB+tRNS is written instead of RGBA
10057% o Opaque matte channel is removed (or added, if color-type 4 or 6
10058% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010059%
cristy3ed852e2009-09-05 21:47:34 +000010060%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10061*/
10062static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10063 Image *image)
10064{
10065 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010066 excluding,
10067 logging,
10068 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010069 status;
10070
10071 MngInfo
10072 *mng_info;
10073
10074 const char
10075 *value;
10076
10077 int
glennrp21f0e622011-01-07 16:20:57 +000010078 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010079 source;
10080
cristy3ed852e2009-09-05 21:47:34 +000010081 /*
10082 Open image file.
10083 */
10084 assert(image_info != (const ImageInfo *) NULL);
10085 assert(image_info->signature == MagickSignature);
10086 assert(image != (Image *) NULL);
10087 assert(image->signature == MagickSignature);
10088 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010089 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010090 /*
10091 Allocate a MngInfo structure.
10092 */
10093 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010094 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010095
cristy3ed852e2009-09-05 21:47:34 +000010096 if (mng_info == (MngInfo *) NULL)
10097 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010098
cristy3ed852e2009-09-05 21:47:34 +000010099 /*
10100 Initialize members of the MngInfo structure.
10101 */
10102 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10103 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010104 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010105 have_mng_structure=MagickTrue;
10106
10107 /* See if user has requested a specific PNG subformat */
10108
10109 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10110 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10111 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10112
10113 if (mng_info->write_png8)
10114 {
glennrp9c1eb072010-06-06 22:19:15 +000010115 mng_info->write_png_colortype = /* 3 */ 4;
10116 mng_info->write_png_depth = 8;
10117 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010118 }
10119
10120 if (mng_info->write_png24)
10121 {
glennrp9c1eb072010-06-06 22:19:15 +000010122 mng_info->write_png_colortype = /* 2 */ 3;
10123 mng_info->write_png_depth = 8;
10124 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010125
glennrp9c1eb072010-06-06 22:19:15 +000010126 if (image->matte == MagickTrue)
10127 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010128
glennrp9c1eb072010-06-06 22:19:15 +000010129 else
10130 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010131
glennrp9c1eb072010-06-06 22:19:15 +000010132 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010133 }
10134
10135 if (mng_info->write_png32)
10136 {
glennrp9c1eb072010-06-06 22:19:15 +000010137 mng_info->write_png_colortype = /* 6 */ 7;
10138 mng_info->write_png_depth = 8;
10139 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010140
glennrp9c1eb072010-06-06 22:19:15 +000010141 if (image->matte == MagickTrue)
10142 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010143
glennrp9c1eb072010-06-06 22:19:15 +000010144 else
10145 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010146
glennrp9c1eb072010-06-06 22:19:15 +000010147 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010148 }
10149
10150 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010151
cristy3ed852e2009-09-05 21:47:34 +000010152 if (value != (char *) NULL)
10153 {
10154 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010155 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010156
cristy3ed852e2009-09-05 21:47:34 +000010157 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010158 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010159
cristy3ed852e2009-09-05 21:47:34 +000010160 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010161 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010162
cristy3ed852e2009-09-05 21:47:34 +000010163 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010164 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010165
cristy3ed852e2009-09-05 21:47:34 +000010166 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010167 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010168
glennrpbb8a7332010-11-13 15:17:35 +000010169 else
10170 (void) ThrowMagickException(&image->exception,
10171 GetMagickModule(),CoderWarning,
10172 "ignoring invalid defined png:bit-depth",
10173 "=%s",value);
10174
cristy3ed852e2009-09-05 21:47:34 +000010175 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010177 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010178 }
glennrp0fe50b42010-11-16 03:52:51 +000010179
cristy3ed852e2009-09-05 21:47:34 +000010180 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010181
cristy3ed852e2009-09-05 21:47:34 +000010182 if (value != (char *) NULL)
10183 {
10184 /* We must store colortype+1 because 0 is a valid colortype */
10185 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010186 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010187
cristy3ed852e2009-09-05 21:47:34 +000010188 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010189 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010190
cristy3ed852e2009-09-05 21:47:34 +000010191 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010192 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010193
cristy3ed852e2009-09-05 21:47:34 +000010194 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010195 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010196
cristy3ed852e2009-09-05 21:47:34 +000010197 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010198 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010199
glennrpbb8a7332010-11-13 15:17:35 +000010200 else
10201 (void) ThrowMagickException(&image->exception,
10202 GetMagickModule(),CoderWarning,
10203 "ignoring invalid defined png:color-type",
10204 "=%s",value);
10205
cristy3ed852e2009-09-05 21:47:34 +000010206 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010208 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010209 }
10210
glennrp0e8ea192010-12-24 18:00:33 +000010211 /* Check for chunks to be excluded:
10212 *
glennrp0dff56c2011-01-29 19:10:02 +000010213 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010214 * listed in the "unused_chunks" array, above.
10215 *
10216 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10217 * define (in the image properties or in the image artifacts)
10218 * or via a mng_info member. For convenience, in addition
10219 * to or instead of a comma-separated list of chunks, the
10220 * "exclude-chunk" string can be simply "all" or "none".
10221 *
10222 * The exclude-chunk define takes priority over the mng_info.
10223 *
10224 * A "PNG:include-chunk" define takes priority over both the
10225 * mng_info and the "PNG:exclude-chunk" define. Like the
10226 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010227 * well as a comma-separated list. Chunks that are unknown to
10228 * ImageMagick are always excluded, regardless of their "copy-safe"
10229 * status according to the PNG specification, and even if they
10230 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010231 *
10232 * Finally, all chunks listed in the "unused_chunks" array are
10233 * automatically excluded, regardless of the other instructions
10234 * or lack thereof.
10235 *
10236 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10237 * will not be written and the gAMA chunk will only be written if it
10238 * is not between .45 and .46, or approximately (1.0/2.2).
10239 *
10240 * If you exclude tRNS and the image has transparency, the colortype
10241 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10242 *
10243 * The -strip option causes StripImage() to set the png:include-chunk
10244 * artifact to "none,gama".
10245 */
10246
glennrp26f37912010-12-23 16:22:42 +000010247 mng_info->ping_exclude_bKGD=MagickFalse;
10248 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010249 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010250 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10251 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010252 mng_info->ping_exclude_iCCP=MagickFalse;
10253 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10254 mng_info->ping_exclude_oFFs=MagickFalse;
10255 mng_info->ping_exclude_pHYs=MagickFalse;
10256 mng_info->ping_exclude_sRGB=MagickFalse;
10257 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010258 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010259 mng_info->ping_exclude_vpAg=MagickFalse;
10260 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10261 mng_info->ping_exclude_zTXt=MagickFalse;
10262
glennrp8d3d6e52011-04-19 04:39:51 +000010263 mng_info->ping_preserve_colormap=MagickFalse;
10264
10265 value=GetImageArtifact(image,"png:preserve-colormap");
10266 if (value == NULL)
10267 value=GetImageOption(image_info,"png:preserve-colormap");
10268 if (value != NULL)
10269 mng_info->ping_preserve_colormap=MagickTrue;
10270
glennrp03812ae2010-12-24 01:31:34 +000010271 excluding=MagickFalse;
10272
glennrp5c7cf4e2010-12-24 00:30:00 +000010273 for (source=0; source<1; source++)
10274 {
10275 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010276 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010277 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010278
10279 if (value == NULL)
10280 value=GetImageArtifact(image,"png:exclude-chunks");
10281 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010282 else
glennrpacba0042010-12-24 14:27:26 +000010283 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010284 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010285
glennrpacba0042010-12-24 14:27:26 +000010286 if (value == NULL)
10287 value=GetImageOption(image_info,"png:exclude-chunks");
10288 }
10289
glennrp03812ae2010-12-24 01:31:34 +000010290 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010291 {
glennrp03812ae2010-12-24 01:31:34 +000010292
10293 size_t
10294 last;
10295
10296 excluding=MagickTrue;
10297
10298 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010299 {
10300 if (source == 0)
10301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10302 " png:exclude-chunk=%s found in image artifacts.\n", value);
10303 else
10304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10305 " png:exclude-chunk=%s found in image properties.\n", value);
10306 }
glennrp03812ae2010-12-24 01:31:34 +000010307
10308 last=strlen(value);
10309
10310 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010311 {
glennrp03812ae2010-12-24 01:31:34 +000010312
10313 if (LocaleNCompare(value+i,"all",3) == 0)
10314 {
10315 mng_info->ping_exclude_bKGD=MagickTrue;
10316 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010317 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010318 mng_info->ping_exclude_EXIF=MagickTrue;
10319 mng_info->ping_exclude_gAMA=MagickTrue;
10320 mng_info->ping_exclude_iCCP=MagickTrue;
10321 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10322 mng_info->ping_exclude_oFFs=MagickTrue;
10323 mng_info->ping_exclude_pHYs=MagickTrue;
10324 mng_info->ping_exclude_sRGB=MagickTrue;
10325 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010326 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010327 mng_info->ping_exclude_vpAg=MagickTrue;
10328 mng_info->ping_exclude_zCCP=MagickTrue;
10329 mng_info->ping_exclude_zTXt=MagickTrue;
10330 i--;
10331 }
glennrp2cc891a2010-12-24 13:44:32 +000010332
glennrp03812ae2010-12-24 01:31:34 +000010333 if (LocaleNCompare(value+i,"none",4) == 0)
10334 {
10335 mng_info->ping_exclude_bKGD=MagickFalse;
10336 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010337 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010338 mng_info->ping_exclude_EXIF=MagickFalse;
10339 mng_info->ping_exclude_gAMA=MagickFalse;
10340 mng_info->ping_exclude_iCCP=MagickFalse;
10341 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10342 mng_info->ping_exclude_oFFs=MagickFalse;
10343 mng_info->ping_exclude_pHYs=MagickFalse;
10344 mng_info->ping_exclude_sRGB=MagickFalse;
10345 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010346 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010347 mng_info->ping_exclude_vpAg=MagickFalse;
10348 mng_info->ping_exclude_zCCP=MagickFalse;
10349 mng_info->ping_exclude_zTXt=MagickFalse;
10350 }
glennrp2cc891a2010-12-24 13:44:32 +000010351
glennrp03812ae2010-12-24 01:31:34 +000010352 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10353 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010354
glennrp03812ae2010-12-24 01:31:34 +000010355 if (LocaleNCompare(value+i,"chrm",4) == 0)
10356 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010357
glennrpa0ed0092011-04-18 16:36:29 +000010358 if (LocaleNCompare(value+i,"date",4) == 0)
10359 mng_info->ping_exclude_date=MagickTrue;
10360
glennrp03812ae2010-12-24 01:31:34 +000010361 if (LocaleNCompare(value+i,"exif",4) == 0)
10362 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010363
glennrp03812ae2010-12-24 01:31:34 +000010364 if (LocaleNCompare(value+i,"gama",4) == 0)
10365 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010366
glennrp03812ae2010-12-24 01:31:34 +000010367 if (LocaleNCompare(value+i,"iccp",4) == 0)
10368 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010369
glennrp03812ae2010-12-24 01:31:34 +000010370 /*
10371 if (LocaleNCompare(value+i,"itxt",4) == 0)
10372 mng_info->ping_exclude_iTXt=MagickTrue;
10373 */
glennrp2cc891a2010-12-24 13:44:32 +000010374
glennrp03812ae2010-12-24 01:31:34 +000010375 if (LocaleNCompare(value+i,"gama",4) == 0)
10376 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010377
glennrp03812ae2010-12-24 01:31:34 +000010378 if (LocaleNCompare(value+i,"offs",4) == 0)
10379 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010380
glennrp03812ae2010-12-24 01:31:34 +000010381 if (LocaleNCompare(value+i,"phys",4) == 0)
10382 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010383
glennrpa1e3b7b2010-12-24 16:37:33 +000010384 if (LocaleNCompare(value+i,"srgb",4) == 0)
10385 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010386
glennrp03812ae2010-12-24 01:31:34 +000010387 if (LocaleNCompare(value+i,"text",4) == 0)
10388 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010389
glennrpa1e3b7b2010-12-24 16:37:33 +000010390 if (LocaleNCompare(value+i,"trns",4) == 0)
10391 mng_info->ping_exclude_tRNS=MagickTrue;
10392
glennrp03812ae2010-12-24 01:31:34 +000010393 if (LocaleNCompare(value+i,"vpag",4) == 0)
10394 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010395
glennrp03812ae2010-12-24 01:31:34 +000010396 if (LocaleNCompare(value+i,"zccp",4) == 0)
10397 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010398
glennrp03812ae2010-12-24 01:31:34 +000010399 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10400 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010401
glennrp03812ae2010-12-24 01:31:34 +000010402 }
glennrpce91ed52010-12-23 22:37:49 +000010403 }
glennrp26f37912010-12-23 16:22:42 +000010404 }
10405
glennrp5c7cf4e2010-12-24 00:30:00 +000010406 for (source=0; source<1; source++)
10407 {
10408 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010409 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010410 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010411
10412 if (value == NULL)
10413 value=GetImageArtifact(image,"png:include-chunks");
10414 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010415 else
glennrpacba0042010-12-24 14:27:26 +000010416 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010417 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010418
glennrpacba0042010-12-24 14:27:26 +000010419 if (value == NULL)
10420 value=GetImageOption(image_info,"png:include-chunks");
10421 }
10422
glennrp03812ae2010-12-24 01:31:34 +000010423 if (value != NULL)
10424 {
10425 size_t
10426 last;
glennrp26f37912010-12-23 16:22:42 +000010427
glennrp03812ae2010-12-24 01:31:34 +000010428 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010429
glennrp03812ae2010-12-24 01:31:34 +000010430 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010431 {
10432 if (source == 0)
10433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10434 " png:include-chunk=%s found in image artifacts.\n", value);
10435 else
10436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10437 " png:include-chunk=%s found in image properties.\n", value);
10438 }
glennrp03812ae2010-12-24 01:31:34 +000010439
10440 last=strlen(value);
10441
10442 for (i=0; i<(int) last; i+=5)
10443 {
10444 if (LocaleNCompare(value+i,"all",3) == 0)
10445 {
10446 mng_info->ping_exclude_bKGD=MagickFalse;
10447 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010448 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010449 mng_info->ping_exclude_EXIF=MagickFalse;
10450 mng_info->ping_exclude_gAMA=MagickFalse;
10451 mng_info->ping_exclude_iCCP=MagickFalse;
10452 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10453 mng_info->ping_exclude_oFFs=MagickFalse;
10454 mng_info->ping_exclude_pHYs=MagickFalse;
10455 mng_info->ping_exclude_sRGB=MagickFalse;
10456 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010457 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010458 mng_info->ping_exclude_vpAg=MagickFalse;
10459 mng_info->ping_exclude_zCCP=MagickFalse;
10460 mng_info->ping_exclude_zTXt=MagickFalse;
10461 i--;
10462 }
glennrp2cc891a2010-12-24 13:44:32 +000010463
glennrp03812ae2010-12-24 01:31:34 +000010464 if (LocaleNCompare(value+i,"none",4) == 0)
10465 {
10466 mng_info->ping_exclude_bKGD=MagickTrue;
10467 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010468 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010469 mng_info->ping_exclude_EXIF=MagickTrue;
10470 mng_info->ping_exclude_gAMA=MagickTrue;
10471 mng_info->ping_exclude_iCCP=MagickTrue;
10472 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10473 mng_info->ping_exclude_oFFs=MagickTrue;
10474 mng_info->ping_exclude_pHYs=MagickTrue;
10475 mng_info->ping_exclude_sRGB=MagickTrue;
10476 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010477 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010478 mng_info->ping_exclude_vpAg=MagickTrue;
10479 mng_info->ping_exclude_zCCP=MagickTrue;
10480 mng_info->ping_exclude_zTXt=MagickTrue;
10481 }
glennrp2cc891a2010-12-24 13:44:32 +000010482
glennrp03812ae2010-12-24 01:31:34 +000010483 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10484 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010485
glennrp03812ae2010-12-24 01:31:34 +000010486 if (LocaleNCompare(value+i,"chrm",4) == 0)
10487 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010488
glennrpa0ed0092011-04-18 16:36:29 +000010489 if (LocaleNCompare(value+i,"date",4) == 0)
10490 mng_info->ping_exclude_date=MagickFalse;
10491
glennrp03812ae2010-12-24 01:31:34 +000010492 if (LocaleNCompare(value+i,"exif",4) == 0)
10493 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010494
glennrp03812ae2010-12-24 01:31:34 +000010495 if (LocaleNCompare(value+i,"gama",4) == 0)
10496 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010497
glennrp03812ae2010-12-24 01:31:34 +000010498 if (LocaleNCompare(value+i,"iccp",4) == 0)
10499 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010500
glennrp03812ae2010-12-24 01:31:34 +000010501 /*
10502 if (LocaleNCompare(value+i,"itxt",4) == 0)
10503 mng_info->ping_exclude_iTXt=MagickFalse;
10504 */
glennrp2cc891a2010-12-24 13:44:32 +000010505
glennrp03812ae2010-12-24 01:31:34 +000010506 if (LocaleNCompare(value+i,"gama",4) == 0)
10507 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010508
glennrp03812ae2010-12-24 01:31:34 +000010509 if (LocaleNCompare(value+i,"offs",4) == 0)
10510 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010511
glennrp03812ae2010-12-24 01:31:34 +000010512 if (LocaleNCompare(value+i,"phys",4) == 0)
10513 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010514
glennrpa1e3b7b2010-12-24 16:37:33 +000010515 if (LocaleNCompare(value+i,"srgb",4) == 0)
10516 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010517
glennrp03812ae2010-12-24 01:31:34 +000010518 if (LocaleNCompare(value+i,"text",4) == 0)
10519 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010520
glennrpa1e3b7b2010-12-24 16:37:33 +000010521 if (LocaleNCompare(value+i,"trns",4) == 0)
10522 mng_info->ping_exclude_tRNS=MagickFalse;
10523
glennrp03812ae2010-12-24 01:31:34 +000010524 if (LocaleNCompare(value+i,"vpag",4) == 0)
10525 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010526
glennrp03812ae2010-12-24 01:31:34 +000010527 if (LocaleNCompare(value+i,"zccp",4) == 0)
10528 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010529
glennrp03812ae2010-12-24 01:31:34 +000010530 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10531 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010532
glennrp03812ae2010-12-24 01:31:34 +000010533 }
glennrpce91ed52010-12-23 22:37:49 +000010534 }
glennrp26f37912010-12-23 16:22:42 +000010535 }
10536
glennrp03812ae2010-12-24 01:31:34 +000010537 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010538 {
10539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10540 " Chunks to be excluded from the output PNG:");
10541 if (mng_info->ping_exclude_bKGD != MagickFalse)
10542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10543 " bKGD");
10544 if (mng_info->ping_exclude_cHRM != MagickFalse)
10545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10546 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010547 if (mng_info->ping_exclude_date != MagickFalse)
10548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10549 " date");
glennrp26f37912010-12-23 16:22:42 +000010550 if (mng_info->ping_exclude_EXIF != MagickFalse)
10551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10552 " EXIF");
10553 if (mng_info->ping_exclude_gAMA != MagickFalse)
10554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10555 " gAMA");
10556 if (mng_info->ping_exclude_iCCP != MagickFalse)
10557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10558 " iCCP");
10559/*
10560 if (mng_info->ping_exclude_iTXt != MagickFalse)
10561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10562 " iTXt");
10563*/
10564 if (mng_info->ping_exclude_oFFs != MagickFalse)
10565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10566 " oFFs");
10567 if (mng_info->ping_exclude_pHYs != MagickFalse)
10568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10569 " pHYs");
10570 if (mng_info->ping_exclude_sRGB != MagickFalse)
10571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10572 " sRGB");
10573 if (mng_info->ping_exclude_tEXt != MagickFalse)
10574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10575 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010576 if (mng_info->ping_exclude_tRNS != MagickFalse)
10577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10578 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010579 if (mng_info->ping_exclude_vpAg != MagickFalse)
10580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10581 " vpAg");
10582 if (mng_info->ping_exclude_zCCP != MagickFalse)
10583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10584 " zCCP");
10585 if (mng_info->ping_exclude_zTXt != MagickFalse)
10586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10587 " zTXt");
10588 }
10589
glennrpb9cfe272010-12-21 15:08:06 +000010590 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010591
glennrpb9cfe272010-12-21 15:08:06 +000010592 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010593
10594 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010595
cristy3ed852e2009-09-05 21:47:34 +000010596 if (logging != MagickFalse)
10597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010598
cristy3ed852e2009-09-05 21:47:34 +000010599 return(status);
10600}
10601
10602#if defined(JNG_SUPPORTED)
10603
10604/* Write one JNG image */
10605static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10606 const ImageInfo *image_info,Image *image)
10607{
10608 Image
10609 *jpeg_image;
10610
10611 ImageInfo
10612 *jpeg_image_info;
10613
10614 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010615 logging,
cristy3ed852e2009-09-05 21:47:34 +000010616 status;
10617
10618 size_t
10619 length;
10620
10621 unsigned char
10622 *blob,
10623 chunk[80],
10624 *p;
10625
10626 unsigned int
10627 jng_alpha_compression_method,
10628 jng_alpha_sample_depth,
10629 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010630 transparent;
10631
cristybb503372010-05-27 20:51:26 +000010632 size_t
cristy3ed852e2009-09-05 21:47:34 +000010633 jng_quality;
10634
10635 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010636 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010637
10638 blob=(unsigned char *) NULL;
10639 jpeg_image=(Image *) NULL;
10640 jpeg_image_info=(ImageInfo *) NULL;
10641
10642 status=MagickTrue;
10643 transparent=image_info->type==GrayscaleMatteType ||
10644 image_info->type==TrueColorMatteType;
10645 jng_color_type=10;
10646 jng_alpha_sample_depth=0;
10647 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10648 jng_alpha_compression_method=0;
10649
10650 if (image->matte != MagickFalse)
10651 {
10652 /* if any pixels are transparent */
10653 transparent=MagickTrue;
10654 if (image_info->compression==JPEGCompression)
10655 jng_alpha_compression_method=8;
10656 }
10657
10658 if (transparent)
10659 {
10660 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010661
cristy3ed852e2009-09-05 21:47:34 +000010662 /* Create JPEG blob, image, and image_info */
10663 if (logging != MagickFalse)
10664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10665 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010666
cristy3ed852e2009-09-05 21:47:34 +000010667 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010668
cristy3ed852e2009-09-05 21:47:34 +000010669 if (jpeg_image_info == (ImageInfo *) NULL)
10670 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010671
cristy3ed852e2009-09-05 21:47:34 +000010672 if (logging != MagickFalse)
10673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10674 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010675
cristy3ed852e2009-09-05 21:47:34 +000010676 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010677
cristy3ed852e2009-09-05 21:47:34 +000010678 if (jpeg_image == (Image *) NULL)
10679 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010680
cristy3ed852e2009-09-05 21:47:34 +000010681 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10682 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10683 status=NegateImage(jpeg_image,MagickFalse);
10684 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010685
cristy3ed852e2009-09-05 21:47:34 +000010686 if (jng_quality >= 1000)
10687 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010688
cristy3ed852e2009-09-05 21:47:34 +000010689 else
10690 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010691
cristy3ed852e2009-09-05 21:47:34 +000010692 jpeg_image_info->type=GrayscaleType;
10693 (void) SetImageType(jpeg_image,GrayscaleType);
10694 (void) AcquireUniqueFilename(jpeg_image->filename);
10695 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10696 "%s",jpeg_image->filename);
10697 }
10698
10699 /* To do: check bit depth of PNG alpha channel */
10700
10701 /* Check if image is grayscale. */
10702 if (image_info->type != TrueColorMatteType && image_info->type !=
10703 TrueColorType && ImageIsGray(image))
10704 jng_color_type-=2;
10705
10706 if (transparent)
10707 {
10708 if (jng_alpha_compression_method==0)
10709 {
10710 const char
10711 *value;
10712
10713 /* Encode opacity as a grayscale PNG blob */
10714 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10715 &image->exception);
10716 if (logging != MagickFalse)
10717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10718 " Creating PNG blob.");
10719 length=0;
10720
10721 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10722 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10723 jpeg_image_info->interlace=NoInterlace;
10724
10725 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10726 &image->exception);
10727
10728 /* Retrieve sample depth used */
10729 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10730 if (value != (char *) NULL)
10731 jng_alpha_sample_depth= (unsigned int) value[0];
10732 }
10733 else
10734 {
10735 /* Encode opacity as a grayscale JPEG blob */
10736
10737 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10738 &image->exception);
10739
10740 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10741 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10742 jpeg_image_info->interlace=NoInterlace;
10743 if (logging != MagickFalse)
10744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10745 " Creating blob.");
10746 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010747 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010748 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010749
cristy3ed852e2009-09-05 21:47:34 +000010750 if (logging != MagickFalse)
10751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010752 " Successfully read jpeg_image into a blob, length=%.20g.",
10753 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010754
10755 }
10756 /* Destroy JPEG image and image_info */
10757 jpeg_image=DestroyImage(jpeg_image);
10758 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10759 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10760 }
10761
10762 /* Write JHDR chunk */
10763 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10764 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010765 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010766 PNGLong(chunk+4,(png_uint_32) image->columns);
10767 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010768 chunk[12]=jng_color_type;
10769 chunk[13]=8; /* sample depth */
10770 chunk[14]=8; /*jng_image_compression_method */
10771 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10772 chunk[16]=jng_alpha_sample_depth;
10773 chunk[17]=jng_alpha_compression_method;
10774 chunk[18]=0; /*jng_alpha_filter_method */
10775 chunk[19]=0; /*jng_alpha_interlace_method */
10776 (void) WriteBlob(image,20,chunk);
10777 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10778 if (logging != MagickFalse)
10779 {
10780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010781 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010782
cristy3ed852e2009-09-05 21:47:34 +000010783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010784 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010785
cristy3ed852e2009-09-05 21:47:34 +000010786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10787 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010788
cristy3ed852e2009-09-05 21:47:34 +000010789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10790 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010791
cristy3ed852e2009-09-05 21:47:34 +000010792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10793 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010794
cristy3ed852e2009-09-05 21:47:34 +000010795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10796 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010797
cristy3ed852e2009-09-05 21:47:34 +000010798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10799 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010800
cristy3ed852e2009-09-05 21:47:34 +000010801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10802 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010803
cristy3ed852e2009-09-05 21:47:34 +000010804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10805 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010806
cristy3ed852e2009-09-05 21:47:34 +000010807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10808 " JNG alpha interlace:%5d",0);
10809 }
10810
glennrp0fe50b42010-11-16 03:52:51 +000010811 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010812 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010813
10814 /*
10815 Write leading ancillary chunks
10816 */
10817
10818 if (transparent)
10819 {
10820 /*
10821 Write JNG bKGD chunk
10822 */
10823
10824 unsigned char
10825 blue,
10826 green,
10827 red;
10828
cristybb503372010-05-27 20:51:26 +000010829 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010830 num_bytes;
10831
10832 if (jng_color_type == 8 || jng_color_type == 12)
10833 num_bytes=6L;
10834 else
10835 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010836 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010837 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010838 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010839 red=ScaleQuantumToChar(image->background_color.red);
10840 green=ScaleQuantumToChar(image->background_color.green);
10841 blue=ScaleQuantumToChar(image->background_color.blue);
10842 *(chunk+4)=0;
10843 *(chunk+5)=red;
10844 *(chunk+6)=0;
10845 *(chunk+7)=green;
10846 *(chunk+8)=0;
10847 *(chunk+9)=blue;
10848 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10849 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10850 }
10851
10852 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10853 {
10854 /*
10855 Write JNG sRGB chunk
10856 */
10857 (void) WriteBlobMSBULong(image,1L);
10858 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010859 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010860
cristy3ed852e2009-09-05 21:47:34 +000010861 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010862 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010863 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010864 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010865
cristy3ed852e2009-09-05 21:47:34 +000010866 else
glennrpe610a072010-08-05 17:08:46 +000010867 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010868 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010869 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010870
cristy3ed852e2009-09-05 21:47:34 +000010871 (void) WriteBlob(image,5,chunk);
10872 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10873 }
10874 else
10875 {
10876 if (image->gamma != 0.0)
10877 {
10878 /*
10879 Write JNG gAMA chunk
10880 */
10881 (void) WriteBlobMSBULong(image,4L);
10882 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010883 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010884 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010885 (void) WriteBlob(image,8,chunk);
10886 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10887 }
glennrp0fe50b42010-11-16 03:52:51 +000010888
cristy3ed852e2009-09-05 21:47:34 +000010889 if ((mng_info->equal_chrms == MagickFalse) &&
10890 (image->chromaticity.red_primary.x != 0.0))
10891 {
10892 PrimaryInfo
10893 primary;
10894
10895 /*
10896 Write JNG cHRM chunk
10897 */
10898 (void) WriteBlobMSBULong(image,32L);
10899 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010900 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010901 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010902 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10903 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010904 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010905 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10906 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010907 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010908 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10909 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010910 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010911 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10912 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010913 (void) WriteBlob(image,36,chunk);
10914 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10915 }
10916 }
glennrp0fe50b42010-11-16 03:52:51 +000010917
cristy3ed852e2009-09-05 21:47:34 +000010918 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10919 {
10920 /*
10921 Write JNG pHYs chunk
10922 */
10923 (void) WriteBlobMSBULong(image,9L);
10924 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000010925 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000010926 if (image->units == PixelsPerInchResolution)
10927 {
cristy35ef8242010-06-03 16:24:13 +000010928 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010929 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010930
cristy35ef8242010-06-03 16:24:13 +000010931 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010932 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010933
cristy3ed852e2009-09-05 21:47:34 +000010934 chunk[12]=1;
10935 }
glennrp0fe50b42010-11-16 03:52:51 +000010936
cristy3ed852e2009-09-05 21:47:34 +000010937 else
10938 {
10939 if (image->units == PixelsPerCentimeterResolution)
10940 {
cristy35ef8242010-06-03 16:24:13 +000010941 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010942 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010943
cristy35ef8242010-06-03 16:24:13 +000010944 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010945 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010946
cristy3ed852e2009-09-05 21:47:34 +000010947 chunk[12]=1;
10948 }
glennrp0fe50b42010-11-16 03:52:51 +000010949
cristy3ed852e2009-09-05 21:47:34 +000010950 else
10951 {
cristy35ef8242010-06-03 16:24:13 +000010952 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10953 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010954 chunk[12]=0;
10955 }
10956 }
10957 (void) WriteBlob(image,13,chunk);
10958 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10959 }
10960
10961 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10962 {
10963 /*
10964 Write JNG oFFs chunk
10965 */
10966 (void) WriteBlobMSBULong(image,9L);
10967 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000010968 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000010969 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10970 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000010971 chunk[12]=0;
10972 (void) WriteBlob(image,13,chunk);
10973 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10974 }
10975 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10976 {
10977 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10978 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010979 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000010980 PNGLong(chunk+4,(png_uint_32) image->page.width);
10981 PNGLong(chunk+8,(png_uint_32) image->page.height);
10982 chunk[12]=0; /* unit = pixels */
10983 (void) WriteBlob(image,13,chunk);
10984 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10985 }
10986
10987
10988 if (transparent)
10989 {
10990 if (jng_alpha_compression_method==0)
10991 {
cristybb503372010-05-27 20:51:26 +000010992 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010993 i;
10994
cristybb503372010-05-27 20:51:26 +000010995 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010996 len;
10997
10998 /* Write IDAT chunk header */
10999 if (logging != MagickFalse)
11000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011001 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011002 length);
cristy3ed852e2009-09-05 21:47:34 +000011003
11004 /* Copy IDAT chunks */
11005 len=0;
11006 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011007 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011008 {
11009 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11010 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011011
cristy3ed852e2009-09-05 21:47:34 +000011012 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11013 {
11014 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011015 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011016 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011017 (void) WriteBlob(image,(size_t) len+4,p);
11018 (void) WriteBlobMSBULong(image,
11019 crc32(0,p,(uInt) len+4));
11020 }
glennrp0fe50b42010-11-16 03:52:51 +000011021
cristy3ed852e2009-09-05 21:47:34 +000011022 else
11023 {
11024 if (logging != MagickFalse)
11025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011026 " Skipping %c%c%c%c chunk, length=%.20g.",
11027 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011028 }
11029 p+=(8+len);
11030 }
11031 }
11032 else
11033 {
11034 /* Write JDAA chunk header */
11035 if (logging != MagickFalse)
11036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011037 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011038 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011039 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011040 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011041 /* Write JDAT chunk(s) data */
11042 (void) WriteBlob(image,4,chunk);
11043 (void) WriteBlob(image,length,blob);
11044 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11045 (uInt) length));
11046 }
11047 blob=(unsigned char *) RelinquishMagickMemory(blob);
11048 }
11049
11050 /* Encode image as a JPEG blob */
11051 if (logging != MagickFalse)
11052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11053 " Creating jpeg_image_info.");
11054 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11055 if (jpeg_image_info == (ImageInfo *) NULL)
11056 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11057
11058 if (logging != MagickFalse)
11059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11060 " Creating jpeg_image.");
11061
11062 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11063 if (jpeg_image == (Image *) NULL)
11064 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11065 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11066
11067 (void) AcquireUniqueFilename(jpeg_image->filename);
11068 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11069 jpeg_image->filename);
11070
11071 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11072 &image->exception);
11073
11074 if (logging != MagickFalse)
11075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011076 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11077 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011078
11079 if (jng_color_type == 8 || jng_color_type == 12)
11080 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011081
cristy3ed852e2009-09-05 21:47:34 +000011082 jpeg_image_info->quality=jng_quality % 1000;
11083 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11084 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011085
cristy3ed852e2009-09-05 21:47:34 +000011086 if (logging != MagickFalse)
11087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11088 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011089
cristy3ed852e2009-09-05 21:47:34 +000011090 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011091
cristy3ed852e2009-09-05 21:47:34 +000011092 if (logging != MagickFalse)
11093 {
11094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011095 " Successfully read jpeg_image into a blob, length=%.20g.",
11096 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011097
11098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011099 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011100 }
glennrp0fe50b42010-11-16 03:52:51 +000011101
cristy3ed852e2009-09-05 21:47:34 +000011102 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011103 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011104 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011105 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011106 (void) WriteBlob(image,4,chunk);
11107 (void) WriteBlob(image,length,blob);
11108 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11109
11110 jpeg_image=DestroyImage(jpeg_image);
11111 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11112 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11113 blob=(unsigned char *) RelinquishMagickMemory(blob);
11114
11115 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011116 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011117
11118 /* Write IEND chunk */
11119 (void) WriteBlobMSBULong(image,0L);
11120 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011121 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011122 (void) WriteBlob(image,4,chunk);
11123 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11124
11125 if (logging != MagickFalse)
11126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11127 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011128
cristy3ed852e2009-09-05 21:47:34 +000011129 return(status);
11130}
11131
11132
11133/*
11134%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11135% %
11136% %
11137% %
11138% W r i t e J N G I m a g e %
11139% %
11140% %
11141% %
11142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11143%
11144% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11145%
11146% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11147%
11148% The format of the WriteJNGImage method is:
11149%
11150% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11151%
11152% A description of each parameter follows:
11153%
11154% o image_info: the image info.
11155%
11156% o image: The image.
11157%
11158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11159*/
11160static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11161{
11162 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011163 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011164 logging,
cristy3ed852e2009-09-05 21:47:34 +000011165 status;
11166
11167 MngInfo
11168 *mng_info;
11169
cristy3ed852e2009-09-05 21:47:34 +000011170 /*
11171 Open image file.
11172 */
11173 assert(image_info != (const ImageInfo *) NULL);
11174 assert(image_info->signature == MagickSignature);
11175 assert(image != (Image *) NULL);
11176 assert(image->signature == MagickSignature);
11177 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011178 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011179 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11180 if (status == MagickFalse)
11181 return(status);
11182
11183 /*
11184 Allocate a MngInfo structure.
11185 */
11186 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011187 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011188 if (mng_info == (MngInfo *) NULL)
11189 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11190 /*
11191 Initialize members of the MngInfo structure.
11192 */
11193 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11194 mng_info->image=image;
11195 have_mng_structure=MagickTrue;
11196
11197 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11198
11199 status=WriteOneJNGImage(mng_info,image_info,image);
11200 (void) CloseBlob(image);
11201
11202 (void) CatchImageException(image);
11203 MngInfoFreeStruct(mng_info,&have_mng_structure);
11204 if (logging != MagickFalse)
11205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11206 return(status);
11207}
11208#endif
11209
11210
11211
11212static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11213{
11214 const char
11215 *option;
11216
11217 Image
11218 *next_image;
11219
11220 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011221 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011222 status;
11223
glennrp03812ae2010-12-24 01:31:34 +000011224 volatile MagickBooleanType
11225 logging;
11226
cristy3ed852e2009-09-05 21:47:34 +000011227 MngInfo
11228 *mng_info;
11229
11230 int
cristy3ed852e2009-09-05 21:47:34 +000011231 image_count,
11232 need_iterations,
11233 need_matte;
11234
11235 volatile int
11236#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11237 defined(PNG_MNG_FEATURES_SUPPORTED)
11238 need_local_plte,
11239#endif
11240 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011241 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011242 use_global_plte;
11243
cristybb503372010-05-27 20:51:26 +000011244 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011245 i;
11246
11247 unsigned char
11248 chunk[800];
11249
11250 volatile unsigned int
11251 write_jng,
11252 write_mng;
11253
cristybb503372010-05-27 20:51:26 +000011254 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011255 scene;
11256
cristybb503372010-05-27 20:51:26 +000011257 size_t
cristy3ed852e2009-09-05 21:47:34 +000011258 final_delay=0,
11259 initial_delay;
11260
glennrpd5045b42010-03-24 12:40:35 +000011261#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011262 if (image_info->verbose)
11263 printf("Your PNG library (libpng-%s) is rather old.\n",
11264 PNG_LIBPNG_VER_STRING);
11265#endif
11266
11267 /*
11268 Open image file.
11269 */
11270 assert(image_info != (const ImageInfo *) NULL);
11271 assert(image_info->signature == MagickSignature);
11272 assert(image != (Image *) NULL);
11273 assert(image->signature == MagickSignature);
11274 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011275 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011276 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11277 if (status == MagickFalse)
11278 return(status);
11279
11280 /*
11281 Allocate a MngInfo structure.
11282 */
11283 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011284 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011285 if (mng_info == (MngInfo *) NULL)
11286 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11287 /*
11288 Initialize members of the MngInfo structure.
11289 */
11290 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11291 mng_info->image=image;
11292 have_mng_structure=MagickTrue;
11293 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11294
11295 /*
11296 * See if user has requested a specific PNG subformat to be used
11297 * for all of the PNGs in the MNG being written, e.g.,
11298 *
11299 * convert *.png png8:animation.mng
11300 *
11301 * To do: check -define png:bit_depth and png:color_type as well,
11302 * or perhaps use mng:bit_depth and mng:color_type instead for
11303 * global settings.
11304 */
11305
11306 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11307 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11308 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11309
11310 write_jng=MagickFalse;
11311 if (image_info->compression == JPEGCompression)
11312 write_jng=MagickTrue;
11313
11314 mng_info->adjoin=image_info->adjoin &&
11315 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11316
cristy3ed852e2009-09-05 21:47:34 +000011317 if (logging != MagickFalse)
11318 {
11319 /* Log some info about the input */
11320 Image
11321 *p;
11322
11323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11324 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011325
cristy3ed852e2009-09-05 21:47:34 +000011326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011327 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011328
cristy3ed852e2009-09-05 21:47:34 +000011329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11330 " Type: %d",image_info->type);
11331
11332 scene=0;
11333 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11334 {
11335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011336 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011337
cristy3ed852e2009-09-05 21:47:34 +000011338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011339 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011340
cristy3ed852e2009-09-05 21:47:34 +000011341 if (p->matte)
11342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11343 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011344
cristy3ed852e2009-09-05 21:47:34 +000011345 else
11346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11347 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011348
cristy3ed852e2009-09-05 21:47:34 +000011349 if (p->storage_class == PseudoClass)
11350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11351 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011352
cristy3ed852e2009-09-05 21:47:34 +000011353 else
11354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11355 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011356
cristy3ed852e2009-09-05 21:47:34 +000011357 if (p->colors)
11358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011359 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011360
cristy3ed852e2009-09-05 21:47:34 +000011361 else
11362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11363 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011364
cristy3ed852e2009-09-05 21:47:34 +000011365 if (mng_info->adjoin == MagickFalse)
11366 break;
11367 }
11368 }
11369
cristy3ed852e2009-09-05 21:47:34 +000011370 use_global_plte=MagickFalse;
11371 all_images_are_gray=MagickFalse;
11372#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11373 need_local_plte=MagickTrue;
11374#endif
11375 need_defi=MagickFalse;
11376 need_matte=MagickFalse;
11377 mng_info->framing_mode=1;
11378 mng_info->old_framing_mode=1;
11379
11380 if (write_mng)
11381 if (image_info->page != (char *) NULL)
11382 {
11383 /*
11384 Determine image bounding box.
11385 */
11386 SetGeometry(image,&mng_info->page);
11387 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11388 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11389 }
11390 if (write_mng)
11391 {
11392 unsigned int
11393 need_geom;
11394
11395 unsigned short
11396 red,
11397 green,
11398 blue;
11399
11400 mng_info->page=image->page;
11401 need_geom=MagickTrue;
11402 if (mng_info->page.width || mng_info->page.height)
11403 need_geom=MagickFalse;
11404 /*
11405 Check all the scenes.
11406 */
11407 initial_delay=image->delay;
11408 need_iterations=MagickFalse;
11409 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11410 mng_info->equal_physs=MagickTrue,
11411 mng_info->equal_gammas=MagickTrue;
11412 mng_info->equal_srgbs=MagickTrue;
11413 mng_info->equal_backgrounds=MagickTrue;
11414 image_count=0;
11415#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11416 defined(PNG_MNG_FEATURES_SUPPORTED)
11417 all_images_are_gray=MagickTrue;
11418 mng_info->equal_palettes=MagickFalse;
11419 need_local_plte=MagickFalse;
11420#endif
11421 for (next_image=image; next_image != (Image *) NULL; )
11422 {
11423 if (need_geom)
11424 {
11425 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11426 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011427
cristy3ed852e2009-09-05 21:47:34 +000011428 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11429 mng_info->page.height=next_image->rows+next_image->page.y;
11430 }
glennrp0fe50b42010-11-16 03:52:51 +000011431
cristy3ed852e2009-09-05 21:47:34 +000011432 if (next_image->page.x || next_image->page.y)
11433 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011434
cristy3ed852e2009-09-05 21:47:34 +000011435 if (next_image->matte)
11436 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011437
cristy3ed852e2009-09-05 21:47:34 +000011438 if ((int) next_image->dispose >= BackgroundDispose)
11439 if (next_image->matte || next_image->page.x || next_image->page.y ||
11440 ((next_image->columns < mng_info->page.width) &&
11441 (next_image->rows < mng_info->page.height)))
11442 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011443
cristy3ed852e2009-09-05 21:47:34 +000011444 if (next_image->iterations)
11445 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011446
cristy3ed852e2009-09-05 21:47:34 +000011447 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011448
cristy3ed852e2009-09-05 21:47:34 +000011449 if (final_delay != initial_delay || final_delay > 1UL*
11450 next_image->ticks_per_second)
11451 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011452
cristy3ed852e2009-09-05 21:47:34 +000011453#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11454 defined(PNG_MNG_FEATURES_SUPPORTED)
11455 /*
11456 check for global palette possibility.
11457 */
11458 if (image->matte != MagickFalse)
11459 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011460
cristy3ed852e2009-09-05 21:47:34 +000011461 if (need_local_plte == 0)
11462 {
11463 if (ImageIsGray(image) == MagickFalse)
11464 all_images_are_gray=MagickFalse;
11465 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11466 if (use_global_plte == 0)
11467 use_global_plte=mng_info->equal_palettes;
11468 need_local_plte=!mng_info->equal_palettes;
11469 }
11470#endif
11471 if (GetNextImageInList(next_image) != (Image *) NULL)
11472 {
11473 if (next_image->background_color.red !=
11474 next_image->next->background_color.red ||
11475 next_image->background_color.green !=
11476 next_image->next->background_color.green ||
11477 next_image->background_color.blue !=
11478 next_image->next->background_color.blue)
11479 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011480
cristy3ed852e2009-09-05 21:47:34 +000011481 if (next_image->gamma != next_image->next->gamma)
11482 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011483
cristy3ed852e2009-09-05 21:47:34 +000011484 if (next_image->rendering_intent !=
11485 next_image->next->rendering_intent)
11486 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011487
cristy3ed852e2009-09-05 21:47:34 +000011488 if ((next_image->units != next_image->next->units) ||
11489 (next_image->x_resolution != next_image->next->x_resolution) ||
11490 (next_image->y_resolution != next_image->next->y_resolution))
11491 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011492
cristy3ed852e2009-09-05 21:47:34 +000011493 if (mng_info->equal_chrms)
11494 {
11495 if (next_image->chromaticity.red_primary.x !=
11496 next_image->next->chromaticity.red_primary.x ||
11497 next_image->chromaticity.red_primary.y !=
11498 next_image->next->chromaticity.red_primary.y ||
11499 next_image->chromaticity.green_primary.x !=
11500 next_image->next->chromaticity.green_primary.x ||
11501 next_image->chromaticity.green_primary.y !=
11502 next_image->next->chromaticity.green_primary.y ||
11503 next_image->chromaticity.blue_primary.x !=
11504 next_image->next->chromaticity.blue_primary.x ||
11505 next_image->chromaticity.blue_primary.y !=
11506 next_image->next->chromaticity.blue_primary.y ||
11507 next_image->chromaticity.white_point.x !=
11508 next_image->next->chromaticity.white_point.x ||
11509 next_image->chromaticity.white_point.y !=
11510 next_image->next->chromaticity.white_point.y)
11511 mng_info->equal_chrms=MagickFalse;
11512 }
11513 }
11514 image_count++;
11515 next_image=GetNextImageInList(next_image);
11516 }
11517 if (image_count < 2)
11518 {
11519 mng_info->equal_backgrounds=MagickFalse;
11520 mng_info->equal_chrms=MagickFalse;
11521 mng_info->equal_gammas=MagickFalse;
11522 mng_info->equal_srgbs=MagickFalse;
11523 mng_info->equal_physs=MagickFalse;
11524 use_global_plte=MagickFalse;
11525#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11526 need_local_plte=MagickTrue;
11527#endif
11528 need_iterations=MagickFalse;
11529 }
glennrp0fe50b42010-11-16 03:52:51 +000011530
cristy3ed852e2009-09-05 21:47:34 +000011531 if (mng_info->need_fram == MagickFalse)
11532 {
11533 /*
11534 Only certain framing rates 100/n are exactly representable without
11535 the FRAM chunk but we'll allow some slop in VLC files
11536 */
11537 if (final_delay == 0)
11538 {
11539 if (need_iterations != MagickFalse)
11540 {
11541 /*
11542 It's probably a GIF with loop; don't run it *too* fast.
11543 */
glennrp02617122010-07-28 13:07:35 +000011544 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011545 {
11546 final_delay=10;
11547 (void) ThrowMagickException(&image->exception,
11548 GetMagickModule(),CoderWarning,
11549 "input has zero delay between all frames; assuming",
11550 " 10 cs `%s'","");
11551 }
cristy3ed852e2009-09-05 21:47:34 +000011552 }
11553 else
11554 mng_info->ticks_per_second=0;
11555 }
11556 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011557 mng_info->ticks_per_second=(png_uint_32)
11558 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011559 if (final_delay > 50)
11560 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011561
cristy3ed852e2009-09-05 21:47:34 +000011562 if (final_delay > 75)
11563 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011564
cristy3ed852e2009-09-05 21:47:34 +000011565 if (final_delay > 125)
11566 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011567
cristy3ed852e2009-09-05 21:47:34 +000011568 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11569 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11570 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11571 1UL*image->ticks_per_second))
11572 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11573 }
glennrp0fe50b42010-11-16 03:52:51 +000011574
cristy3ed852e2009-09-05 21:47:34 +000011575 if (mng_info->need_fram != MagickFalse)
11576 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11577 /*
11578 If pseudocolor, we should also check to see if all the
11579 palettes are identical and write a global PLTE if they are.
11580 ../glennrp Feb 99.
11581 */
11582 /*
11583 Write the MNG version 1.0 signature and MHDR chunk.
11584 */
11585 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11586 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11587 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011588 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011589 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11590 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011591 PNGLong(chunk+12,mng_info->ticks_per_second);
11592 PNGLong(chunk+16,0L); /* layer count=unknown */
11593 PNGLong(chunk+20,0L); /* frame count=unknown */
11594 PNGLong(chunk+24,0L); /* play time=unknown */
11595 if (write_jng)
11596 {
11597 if (need_matte)
11598 {
11599 if (need_defi || mng_info->need_fram || use_global_plte)
11600 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011601
cristy3ed852e2009-09-05 21:47:34 +000011602 else
11603 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11604 }
glennrp0fe50b42010-11-16 03:52:51 +000011605
cristy3ed852e2009-09-05 21:47:34 +000011606 else
11607 {
11608 if (need_defi || mng_info->need_fram || use_global_plte)
11609 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011610
cristy3ed852e2009-09-05 21:47:34 +000011611 else
11612 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11613 }
11614 }
glennrp0fe50b42010-11-16 03:52:51 +000011615
cristy3ed852e2009-09-05 21:47:34 +000011616 else
11617 {
11618 if (need_matte)
11619 {
11620 if (need_defi || mng_info->need_fram || use_global_plte)
11621 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011622
cristy3ed852e2009-09-05 21:47:34 +000011623 else
11624 PNGLong(chunk+28,9L); /* simplicity=VLC */
11625 }
glennrp0fe50b42010-11-16 03:52:51 +000011626
cristy3ed852e2009-09-05 21:47:34 +000011627 else
11628 {
11629 if (need_defi || mng_info->need_fram || use_global_plte)
11630 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011631
cristy3ed852e2009-09-05 21:47:34 +000011632 else
11633 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11634 }
11635 }
11636 (void) WriteBlob(image,32,chunk);
11637 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11638 option=GetImageOption(image_info,"mng:need-cacheoff");
11639 if (option != (const char *) NULL)
11640 {
11641 size_t
11642 length;
11643
11644 /*
11645 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11646 */
11647 PNGType(chunk,mng_nEED);
11648 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011649 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011650 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011651 length+=4;
11652 (void) WriteBlob(image,length,chunk);
11653 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11654 }
11655 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11656 (GetNextImageInList(image) != (Image *) NULL) &&
11657 (image->iterations != 1))
11658 {
11659 /*
11660 Write MNG TERM chunk
11661 */
11662 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11663 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011664 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011665 chunk[4]=3; /* repeat animation */
11666 chunk[5]=0; /* show last frame when done */
11667 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11668 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011669
cristy3ed852e2009-09-05 21:47:34 +000011670 if (image->iterations == 0)
11671 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011672
cristy3ed852e2009-09-05 21:47:34 +000011673 else
11674 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011675
cristy3ed852e2009-09-05 21:47:34 +000011676 if (logging != MagickFalse)
11677 {
11678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011679 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11680 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011681
cristy3ed852e2009-09-05 21:47:34 +000011682 if (image->iterations == 0)
11683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011684 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011685
cristy3ed852e2009-09-05 21:47:34 +000011686 else
11687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011688 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011689 }
11690 (void) WriteBlob(image,14,chunk);
11691 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11692 }
11693 /*
11694 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11695 */
11696 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11697 mng_info->equal_srgbs)
11698 {
11699 /*
11700 Write MNG sRGB chunk
11701 */
11702 (void) WriteBlobMSBULong(image,1L);
11703 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011704 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011705
cristy3ed852e2009-09-05 21:47:34 +000011706 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011707 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011708 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011709 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011710
cristy3ed852e2009-09-05 21:47:34 +000011711 else
glennrpe610a072010-08-05 17:08:46 +000011712 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011713 Magick_RenderingIntent_to_PNG_RenderingIntent(
11714 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011715
cristy3ed852e2009-09-05 21:47:34 +000011716 (void) WriteBlob(image,5,chunk);
11717 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11718 mng_info->have_write_global_srgb=MagickTrue;
11719 }
glennrp0fe50b42010-11-16 03:52:51 +000011720
cristy3ed852e2009-09-05 21:47:34 +000011721 else
11722 {
11723 if (image->gamma && mng_info->equal_gammas)
11724 {
11725 /*
11726 Write MNG gAMA chunk
11727 */
11728 (void) WriteBlobMSBULong(image,4L);
11729 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011730 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011731 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011732 (void) WriteBlob(image,8,chunk);
11733 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11734 mng_info->have_write_global_gama=MagickTrue;
11735 }
11736 if (mng_info->equal_chrms)
11737 {
11738 PrimaryInfo
11739 primary;
11740
11741 /*
11742 Write MNG cHRM chunk
11743 */
11744 (void) WriteBlobMSBULong(image,32L);
11745 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011746 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011747 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011748 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11749 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011750 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011751 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11752 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011753 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011754 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11755 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011756 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011757 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11758 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011759 (void) WriteBlob(image,36,chunk);
11760 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11761 mng_info->have_write_global_chrm=MagickTrue;
11762 }
11763 }
11764 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11765 {
11766 /*
11767 Write MNG pHYs chunk
11768 */
11769 (void) WriteBlobMSBULong(image,9L);
11770 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011771 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011772
cristy3ed852e2009-09-05 21:47:34 +000011773 if (image->units == PixelsPerInchResolution)
11774 {
cristy35ef8242010-06-03 16:24:13 +000011775 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011776 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011777
cristy35ef8242010-06-03 16:24:13 +000011778 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011779 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011780
cristy3ed852e2009-09-05 21:47:34 +000011781 chunk[12]=1;
11782 }
glennrp0fe50b42010-11-16 03:52:51 +000011783
cristy3ed852e2009-09-05 21:47:34 +000011784 else
11785 {
11786 if (image->units == PixelsPerCentimeterResolution)
11787 {
cristy35ef8242010-06-03 16:24:13 +000011788 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011789 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011790
cristy35ef8242010-06-03 16:24:13 +000011791 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011792 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011793
cristy3ed852e2009-09-05 21:47:34 +000011794 chunk[12]=1;
11795 }
glennrp0fe50b42010-11-16 03:52:51 +000011796
cristy3ed852e2009-09-05 21:47:34 +000011797 else
11798 {
cristy35ef8242010-06-03 16:24:13 +000011799 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11800 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011801 chunk[12]=0;
11802 }
11803 }
11804 (void) WriteBlob(image,13,chunk);
11805 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11806 }
11807 /*
11808 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11809 or does not cover the entire frame.
11810 */
11811 if (write_mng && (image->matte || image->page.x > 0 ||
11812 image->page.y > 0 || (image->page.width &&
11813 (image->page.width+image->page.x < mng_info->page.width))
11814 || (image->page.height && (image->page.height+image->page.y
11815 < mng_info->page.height))))
11816 {
11817 (void) WriteBlobMSBULong(image,6L);
11818 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011819 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011820 red=ScaleQuantumToShort(image->background_color.red);
11821 green=ScaleQuantumToShort(image->background_color.green);
11822 blue=ScaleQuantumToShort(image->background_color.blue);
11823 PNGShort(chunk+4,red);
11824 PNGShort(chunk+6,green);
11825 PNGShort(chunk+8,blue);
11826 (void) WriteBlob(image,10,chunk);
11827 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11828 if (mng_info->equal_backgrounds)
11829 {
11830 (void) WriteBlobMSBULong(image,6L);
11831 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011832 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011833 (void) WriteBlob(image,10,chunk);
11834 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11835 }
11836 }
11837
11838#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11839 if ((need_local_plte == MagickFalse) &&
11840 (image->storage_class == PseudoClass) &&
11841 (all_images_are_gray == MagickFalse))
11842 {
cristybb503372010-05-27 20:51:26 +000011843 size_t
cristy3ed852e2009-09-05 21:47:34 +000011844 data_length;
11845
11846 /*
11847 Write MNG PLTE chunk
11848 */
11849 data_length=3*image->colors;
11850 (void) WriteBlobMSBULong(image,data_length);
11851 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011852 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011853
cristybb503372010-05-27 20:51:26 +000011854 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011855 {
11856 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11857 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11858 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11859 }
glennrp0fe50b42010-11-16 03:52:51 +000011860
cristy3ed852e2009-09-05 21:47:34 +000011861 (void) WriteBlob(image,data_length+4,chunk);
11862 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11863 mng_info->have_write_global_plte=MagickTrue;
11864 }
11865#endif
11866 }
11867 scene=0;
11868 mng_info->delay=0;
11869#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11870 defined(PNG_MNG_FEATURES_SUPPORTED)
11871 mng_info->equal_palettes=MagickFalse;
11872#endif
11873 do
11874 {
11875 if (mng_info->adjoin)
11876 {
11877#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11878 defined(PNG_MNG_FEATURES_SUPPORTED)
11879 /*
11880 If we aren't using a global palette for the entire MNG, check to
11881 see if we can use one for two or more consecutive images.
11882 */
11883 if (need_local_plte && use_global_plte && !all_images_are_gray)
11884 {
11885 if (mng_info->IsPalette)
11886 {
11887 /*
11888 When equal_palettes is true, this image has the same palette
11889 as the previous PseudoClass image
11890 */
11891 mng_info->have_write_global_plte=mng_info->equal_palettes;
11892 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11893 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11894 {
11895 /*
11896 Write MNG PLTE chunk
11897 */
cristybb503372010-05-27 20:51:26 +000011898 size_t
cristy3ed852e2009-09-05 21:47:34 +000011899 data_length;
11900
11901 data_length=3*image->colors;
11902 (void) WriteBlobMSBULong(image,data_length);
11903 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011904 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011905
cristybb503372010-05-27 20:51:26 +000011906 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011907 {
11908 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11909 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11910 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11911 }
glennrp0fe50b42010-11-16 03:52:51 +000011912
cristy3ed852e2009-09-05 21:47:34 +000011913 (void) WriteBlob(image,data_length+4,chunk);
11914 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11915 (uInt) (data_length+4)));
11916 mng_info->have_write_global_plte=MagickTrue;
11917 }
11918 }
11919 else
11920 mng_info->have_write_global_plte=MagickFalse;
11921 }
11922#endif
11923 if (need_defi)
11924 {
cristybb503372010-05-27 20:51:26 +000011925 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011926 previous_x,
11927 previous_y;
11928
11929 if (scene)
11930 {
11931 previous_x=mng_info->page.x;
11932 previous_y=mng_info->page.y;
11933 }
11934 else
11935 {
11936 previous_x=0;
11937 previous_y=0;
11938 }
11939 mng_info->page=image->page;
11940 if ((mng_info->page.x != previous_x) ||
11941 (mng_info->page.y != previous_y))
11942 {
11943 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11944 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000011945 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000011946 chunk[4]=0; /* object 0 MSB */
11947 chunk[5]=0; /* object 0 LSB */
11948 chunk[6]=0; /* visible */
11949 chunk[7]=0; /* abstract */
11950 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11951 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11952 (void) WriteBlob(image,16,chunk);
11953 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11954 }
11955 }
11956 }
11957
11958 mng_info->write_mng=write_mng;
11959
11960 if ((int) image->dispose >= 3)
11961 mng_info->framing_mode=3;
11962
11963 if (mng_info->need_fram && mng_info->adjoin &&
11964 ((image->delay != mng_info->delay) ||
11965 (mng_info->framing_mode != mng_info->old_framing_mode)))
11966 {
11967 if (image->delay == mng_info->delay)
11968 {
11969 /*
11970 Write a MNG FRAM chunk with the new framing mode.
11971 */
11972 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11973 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011974 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000011975 chunk[4]=(unsigned char) mng_info->framing_mode;
11976 (void) WriteBlob(image,5,chunk);
11977 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11978 }
11979 else
11980 {
11981 /*
11982 Write a MNG FRAM chunk with the delay.
11983 */
11984 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11985 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011986 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011987 chunk[4]=(unsigned char) mng_info->framing_mode;
11988 chunk[5]=0; /* frame name separator (no name) */
11989 chunk[6]=2; /* flag for changing default delay */
11990 chunk[7]=0; /* flag for changing frame timeout */
11991 chunk[8]=0; /* flag for changing frame clipping */
11992 chunk[9]=0; /* flag for changing frame sync_id */
11993 PNGLong(chunk+10,(png_uint_32)
11994 ((mng_info->ticks_per_second*
11995 image->delay)/MagickMax(image->ticks_per_second,1)));
11996 (void) WriteBlob(image,14,chunk);
11997 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000011998 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000011999 }
12000 mng_info->old_framing_mode=mng_info->framing_mode;
12001 }
12002
12003#if defined(JNG_SUPPORTED)
12004 if (image_info->compression == JPEGCompression)
12005 {
12006 ImageInfo
12007 *write_info;
12008
12009 if (logging != MagickFalse)
12010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12011 " Writing JNG object.");
12012 /* To do: specify the desired alpha compression method. */
12013 write_info=CloneImageInfo(image_info);
12014 write_info->compression=UndefinedCompression;
12015 status=WriteOneJNGImage(mng_info,write_info,image);
12016 write_info=DestroyImageInfo(write_info);
12017 }
12018 else
12019#endif
12020 {
12021 if (logging != MagickFalse)
12022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12023 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012024
glennrpb9cfe272010-12-21 15:08:06 +000012025 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012026 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012027
12028 /* We don't want any ancillary chunks written */
12029 mng_info->ping_exclude_bKGD=MagickTrue;
12030 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012031 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012032 mng_info->ping_exclude_EXIF=MagickTrue;
12033 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012034 mng_info->ping_exclude_iCCP=MagickTrue;
12035 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12036 mng_info->ping_exclude_oFFs=MagickTrue;
12037 mng_info->ping_exclude_pHYs=MagickTrue;
12038 mng_info->ping_exclude_sRGB=MagickTrue;
12039 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012040 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012041 mng_info->ping_exclude_vpAg=MagickTrue;
12042 mng_info->ping_exclude_zCCP=MagickTrue;
12043 mng_info->ping_exclude_zTXt=MagickTrue;
12044
cristy3ed852e2009-09-05 21:47:34 +000012045 status=WriteOnePNGImage(mng_info,image_info,image);
12046 }
12047
12048 if (status == MagickFalse)
12049 {
12050 MngInfoFreeStruct(mng_info,&have_mng_structure);
12051 (void) CloseBlob(image);
12052 return(MagickFalse);
12053 }
12054 (void) CatchImageException(image);
12055 if (GetNextImageInList(image) == (Image *) NULL)
12056 break;
12057 image=SyncNextImageInList(image);
12058 status=SetImageProgress(image,SaveImagesTag,scene++,
12059 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012060
cristy3ed852e2009-09-05 21:47:34 +000012061 if (status == MagickFalse)
12062 break;
glennrp0fe50b42010-11-16 03:52:51 +000012063
cristy3ed852e2009-09-05 21:47:34 +000012064 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012065
cristy3ed852e2009-09-05 21:47:34 +000012066 if (write_mng)
12067 {
12068 while (GetPreviousImageInList(image) != (Image *) NULL)
12069 image=GetPreviousImageInList(image);
12070 /*
12071 Write the MEND chunk.
12072 */
12073 (void) WriteBlobMSBULong(image,0x00000000L);
12074 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012075 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012076 (void) WriteBlob(image,4,chunk);
12077 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12078 }
12079 /*
12080 Relinquish resources.
12081 */
12082 (void) CloseBlob(image);
12083 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012084
cristy3ed852e2009-09-05 21:47:34 +000012085 if (logging != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012087
cristy3ed852e2009-09-05 21:47:34 +000012088 return(MagickTrue);
12089}
glennrpd5045b42010-03-24 12:40:35 +000012090#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012091
cristy3ed852e2009-09-05 21:47:34 +000012092static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12093{
12094 image=image;
12095 printf("Your PNG library is too old: You have libpng-%s\n",
12096 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012097
cristy3ed852e2009-09-05 21:47:34 +000012098 ThrowBinaryException(CoderError,"PNG library is too old",
12099 image_info->filename);
12100}
glennrp39992b42010-11-14 00:03:43 +000012101
cristy3ed852e2009-09-05 21:47:34 +000012102static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12103{
12104 return(WritePNGImage(image_info,image));
12105}
glennrpd5045b42010-03-24 12:40:35 +000012106#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012107#endif