blob: fd36582b8b17250a95aa8d433c84ec2e42b96f7b [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) &&
2472 (q->opacity != 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) &&
glennrpa6a06632011-01-19 15:15:34 +00002483 (ScaleQuantumToShort(q->red) == transparent_color.red &&
2484 ScaleQuantumToShort(q->green) == transparent_color.green &&
2485 ScaleQuantumToShort(q->blue) == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002486 {
glennrpa6a06632011-01-19 15:15:34 +00002487 if (logging != MagickFalse)
2488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2489 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002490 found_transparent_pixel = MagickTrue;
2491 break;
2492 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002493 q++;
2494 }
2495 }
2496
2497 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2498 {
2499 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2500 image->rows);
2501
2502 if (status == MagickFalse)
2503 break;
2504 }
2505 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2506 break;
2507 }
2508
2509 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2510 {
2511 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002512 if (status == MagickFalse)
2513 break;
2514 }
cristy3ed852e2009-09-05 21:47:34 +00002515 }
cristy3ed852e2009-09-05 21:47:34 +00002516 }
glennrp0fe50b42010-11-16 03:52:51 +00002517
cristy3ed852e2009-09-05 21:47:34 +00002518 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002519
cristy3ed852e2009-09-05 21:47:34 +00002520 for (pass=0; pass < num_passes; pass++)
2521 {
2522 Quantum
2523 *quantum_scanline;
2524
2525 register Quantum
2526 *r;
2527
2528 /*
2529 Convert grayscale image to PseudoClass pixel packets.
2530 */
glennrpfaa852b2010-03-30 12:17:00 +00002531 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002532 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002533
cristy3ed852e2009-09-05 21:47:34 +00002534 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2535 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002536
cristy3ed852e2009-09-05 21:47:34 +00002537 if (quantum_scanline == (Quantum *) NULL)
2538 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002539
cristybb503372010-05-27 20:51:26 +00002540 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002541 {
2542 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002543 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002544
cristy3ed852e2009-09-05 21:47:34 +00002545 else
2546 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00002547
glennrpcf002022011-01-30 02:38:15 +00002548 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002549 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00002550
cristy3ed852e2009-09-05 21:47:34 +00002551 if (q == (PixelPacket *) NULL)
2552 break;
glennrp0fe50b42010-11-16 03:52:51 +00002553
cristy5c6f7892010-05-05 22:53:29 +00002554 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00002555 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00002556 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00002557
glennrpfaa852b2010-03-30 12:17:00 +00002558 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00002559 {
2560 case 1:
2561 {
cristybb503372010-05-27 20:51:26 +00002562 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002563 bit;
2564
cristybb503372010-05-27 20:51:26 +00002565 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00002566 {
2567 for (bit=7; bit >= 0; bit--)
glennrp3faa9a32011-04-23 14:00:25 +00002568 *r++=ScaleCharToQuantum(
2569 (unsigned char) ((*p) & (0x01 << bit) ? 0xff : 0x00));
cristy3ed852e2009-09-05 21:47:34 +00002570 p++;
2571 }
glennrp0fe50b42010-11-16 03:52:51 +00002572
cristy3ed852e2009-09-05 21:47:34 +00002573 if ((image->columns % 8) != 0)
2574 {
cristybb503372010-05-27 20:51:26 +00002575 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrp3faa9a32011-04-23 14:00:25 +00002576 *r++=ScaleCharToQuantum(
2577 (unsigned char) ((*p) & (0x01 << bit) ? 0xff : 0x00));
cristy3ed852e2009-09-05 21:47:34 +00002578 }
glennrp0fe50b42010-11-16 03:52:51 +00002579
cristy3ed852e2009-09-05 21:47:34 +00002580 break;
2581 }
glennrp47b9dd52010-11-24 18:12:06 +00002582
cristy3ed852e2009-09-05 21:47:34 +00002583 case 2:
2584 {
cristybb503372010-05-27 20:51:26 +00002585 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002586 {
glennrp3faa9a32011-04-23 14:00:25 +00002587 *r++=ScaleCharToQuantum((unsigned char) (0x55*(*p >> 6) & 0x03));
2588 *r++=ScaleCharToQuantum((unsigned char) (0x55*(*p >> 4) & 0x03));
2589 *r++=ScaleCharToQuantum((unsigned char) (0x55*(*p >> 2) & 0x03));
2590 *r++=ScaleCharToQuantum((unsigned char) (0x55*(*p++) & 0x03));
cristy3ed852e2009-09-05 21:47:34 +00002591 }
glennrp0fe50b42010-11-16 03:52:51 +00002592
cristy3ed852e2009-09-05 21:47:34 +00002593 if ((image->columns % 4) != 0)
2594 {
cristybb503372010-05-27 20:51:26 +00002595 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrp3faa9a32011-04-23 14:00:25 +00002596 *r++=ScaleCharToQuantum((unsigned char)
2597 (0x55*((*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 {
glennrp3faa9a32011-04-23 14:00:25 +00002607 *r++=ScaleCharToQuantum((unsigned char) (0x11*(*p >> 4) & 0x0f));
2608 *r++=ScaleCharToQuantum((unsigned char) (0x11*(*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)
glennrp3faa9a32011-04-23 14:00:25 +00002612 *r++=ScaleCharToQuantum((unsigned char) (0x11*(*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 {
glennrp3faa9a32011-04-23 14:00:25 +00002622 *r++=ScaleCharToQuantum((unsigned char) *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 */
2628 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
glennrpc8cbc5d2011-01-01 00:12:34 +00002629 if (q->opacity != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00002630 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002631 q++;
2632 }
glennrp0fe50b42010-11-16 03:52:51 +00002633
cristy3ed852e2009-09-05 21:47:34 +00002634 else
cristybb503372010-05-27 20:51:26 +00002635 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrp3faa9a32011-04-23 14:00:25 +00002636 *r++=ScaleCharToQuantum((unsigned char) *p++);
glennrp0fe50b42010-11-16 03:52:51 +00002637
cristy3ed852e2009-09-05 21:47:34 +00002638 break;
2639 }
glennrp47b9dd52010-11-24 18:12:06 +00002640
cristy3ed852e2009-09-05 21:47:34 +00002641 case 16:
2642 {
cristybb503372010-05-27 20:51:26 +00002643 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002644 {
glennrp3faa9a32011-04-23 14:00:25 +00002645#if MAGICKCORE_QUANTUM_DEPTH == 8
2646 *r++=*p++;
2647 p++; /* skip low byte */
glennrp9d0ea4d2011-04-22 18:35:57 +00002648
2649 if (ping_color_type == 4)
2650 {
glennrp3faa9a32011-04-23 14:00:25 +00002651 q->opacity=(Quantum) (QuantumRange - *p++);
glennrp9d0ea4d2011-04-22 18:35:57 +00002652 if (q->opacity != OpaqueOpacity)
2653 found_transparent_pixel = MagickTrue;
2654 p++;
2655 q++;
2656 }
glennrp3faa9a32011-04-23 14:00:25 +00002657
2658#else /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
2659 *r++ = ScaleShortToQuantum(
2660 (unsigned short) (((*p) << 8) | (*(p+1))));
2661 p+=2;
2662
2663 if (ping_color_type == 4)
2664 {
2665 q->opacity = QuantumRange - ScaleShortToQuantum(
2666 (unsigned short) (((*p) << 8) | (*(p+1))));
2667 p+=2;
2668
2669 if (q->opacity != OpaqueOpacity)
2670 found_transparent_pixel = MagickTrue;
2671 q++;
2672 }
cristy3ed852e2009-09-05 21:47:34 +00002673#endif
2674 }
glennrp47b9dd52010-11-24 18:12:06 +00002675
cristy3ed852e2009-09-05 21:47:34 +00002676 break;
2677 }
glennrp47b9dd52010-11-24 18:12:06 +00002678
cristy3ed852e2009-09-05 21:47:34 +00002679 default:
2680 break;
2681 }
glennrp3faa9a32011-04-23 14:00:25 +00002682
cristy3ed852e2009-09-05 21:47:34 +00002683 /*
2684 Transfer image scanline.
2685 */
2686 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00002687
cristybb503372010-05-27 20:51:26 +00002688 for (x=0; x < (ssize_t) image->columns; x++)
cristy80ac8b92010-05-08 13:36:57 +00002689 indexes[x]=(IndexPacket) (*r++);
glennrp0fe50b42010-11-16 03:52:51 +00002690
cristy3ed852e2009-09-05 21:47:34 +00002691 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2692 break;
glennrp0fe50b42010-11-16 03:52:51 +00002693
cristy7a287bf2010-02-14 02:18:09 +00002694 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2695 {
cristycee97112010-05-28 00:44:52 +00002696 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2697 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00002698
cristy7a287bf2010-02-14 02:18:09 +00002699 if (status == MagickFalse)
2700 break;
2701 }
cristy3ed852e2009-09-05 21:47:34 +00002702 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002703
cristy7a287bf2010-02-14 02:18:09 +00002704 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002705 {
2706 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00002707
cristy3ed852e2009-09-05 21:47:34 +00002708 if (status == MagickFalse)
2709 break;
2710 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002711
cristy3ed852e2009-09-05 21:47:34 +00002712 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2713 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002714
2715 image->matte=found_transparent_pixel;
2716
2717 if (logging != MagickFalse)
2718 {
2719 if (found_transparent_pixel != MagickFalse)
2720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2721 " Found transparent pixel");
2722 else
glennrp5aa37f62011-01-02 03:07:57 +00002723 {
2724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2725 " No transparent pixel was found");
glennrpa6a06632011-01-19 15:15:34 +00002726
glennrp5aa37f62011-01-02 03:07:57 +00002727 ping_color_type&=0x03;
2728 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002729 }
2730 }
2731
cristyb32b90a2009-09-07 21:45:48 +00002732 if (quantum_info != (QuantumInfo *) NULL)
2733 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002734
cristy5c6f7892010-05-05 22:53:29 +00002735 if (image->storage_class == PseudoClass)
2736 {
cristyaeb2cbc2010-05-07 13:28:58 +00002737 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002738 matte;
2739
2740 matte=image->matte;
2741 image->matte=MagickFalse;
2742 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002743 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002744 }
glennrp47b9dd52010-11-24 18:12:06 +00002745
glennrp4eb39312011-03-30 21:34:55 +00002746 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00002747
2748 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002749 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002750 {
2751 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00002752 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002753 image->colors=2;
2754 (void) SetImageBackgroundColor(image);
2755#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002756 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002757#endif
2758 if (logging != MagickFalse)
2759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2760 " exit ReadOnePNGImage() early.");
2761 return(image);
2762 }
glennrp47b9dd52010-11-24 18:12:06 +00002763
glennrpfaa852b2010-03-30 12:17:00 +00002764 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002765 {
2766 ClassType
2767 storage_class;
2768
2769 /*
2770 Image has a transparent background.
2771 */
2772 storage_class=image->storage_class;
2773 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002774
glennrp3c218112010-11-27 15:31:26 +00002775/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00002776
glennrp0fe50b42010-11-16 03:52:51 +00002777 if (storage_class == PseudoClass)
2778 {
2779 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002780 {
glennrp0fe50b42010-11-16 03:52:51 +00002781 for (x=0; x < ping_num_trans; x++)
2782 {
2783 image->colormap[x].opacity =
2784 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2785 }
glennrpc11cf6a2010-03-20 16:46:19 +00002786 }
glennrp47b9dd52010-11-24 18:12:06 +00002787
glennrp0fe50b42010-11-16 03:52:51 +00002788 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2789 {
2790 for (x=0; x < (int) image->colors; x++)
2791 {
2792 if (ScaleQuantumToShort(image->colormap[x].red) ==
2793 transparent_color.opacity)
2794 {
2795 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2796 }
2797 }
2798 }
2799 (void) SyncImage(image);
2800 }
glennrp47b9dd52010-11-24 18:12:06 +00002801
glennrpa6a06632011-01-19 15:15:34 +00002802#if 1 /* Should have already been done above, but glennrp problem P10
2803 * needs this.
2804 */
glennrp0fe50b42010-11-16 03:52:51 +00002805 else
2806 {
2807 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002808 {
glennrp0fe50b42010-11-16 03:52:51 +00002809 image->storage_class=storage_class;
2810 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2811
2812 if (q == (PixelPacket *) NULL)
2813 break;
2814
2815 indexes=GetAuthenticIndexQueue(image);
2816
glennrpa6a06632011-01-19 15:15:34 +00002817 /* Caution: on a Q8 build, this does not distinguish between
2818 * 16-bit colors that differ only in the low byte
2819 */
glennrp0fe50b42010-11-16 03:52:51 +00002820 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2821 {
glennrpa6a06632011-01-19 15:15:34 +00002822 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2823 ScaleQuantumToShort(q->green) == transparent_color.green &&
2824 ScaleQuantumToShort(q->blue) == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00002825 {
glennrp0fe50b42010-11-16 03:52:51 +00002826 q->opacity=(Quantum) TransparentOpacity;
glennrp4f25bd02011-01-01 18:51:28 +00002827 }
glennrp0fe50b42010-11-16 03:52:51 +00002828
glennrp67b9c1a2011-04-22 18:47:36 +00002829#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00002830 else
glennrp4f25bd02011-01-01 18:51:28 +00002831 {
2832 q->opacity=(Quantum) OpaqueOpacity;
2833 }
glennrpa6a06632011-01-19 15:15:34 +00002834#endif
glennrp0fe50b42010-11-16 03:52:51 +00002835
2836 q++;
2837 }
2838
2839 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2840 break;
glennrpc11cf6a2010-03-20 16:46:19 +00002841 }
glennrp0fe50b42010-11-16 03:52:51 +00002842 }
glennrpa6a06632011-01-19 15:15:34 +00002843#endif
glennrpc11cf6a2010-03-20 16:46:19 +00002844
cristy3ed852e2009-09-05 21:47:34 +00002845 image->storage_class=DirectClass;
2846 }
glennrp3c218112010-11-27 15:31:26 +00002847
cristyb40fc462010-08-08 00:49:49 +00002848 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2849 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2850 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00002851
cristyeb3b22a2011-03-31 20:16:11 +00002852 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00002853 {
2854 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00002855 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2856 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00002857 else
glennrpa0ed0092011-04-18 16:36:29 +00002858 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2859 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002860
glennrp4eb39312011-03-30 21:34:55 +00002861 if (status != MagickFalse)
2862 for (i=0; i < (ssize_t) num_text; i++)
2863 {
2864 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00002865
glennrp4eb39312011-03-30 21:34:55 +00002866 if (logging != MagickFalse)
2867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2868 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00002869
glennrp4eb39312011-03-30 21:34:55 +00002870 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00002871 {
glennrp4eb39312011-03-30 21:34:55 +00002872 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2873 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00002874 }
glennrp0fe50b42010-11-16 03:52:51 +00002875
glennrp4eb39312011-03-30 21:34:55 +00002876 else
2877 {
2878 char
2879 *value;
2880
2881 length=text[i].text_length;
2882 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2883 sizeof(*value));
2884 if (value == (char *) NULL)
2885 {
2886 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2887 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2888 image->filename);
2889 break;
2890 }
2891 *value='\0';
2892 (void) ConcatenateMagickString(value,text[i].text,length+2);
2893
2894 /* Don't save "density" or "units" property if we have a pHYs
2895 * chunk
2896 */
2897 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2898 (LocaleCompare(text[i].key,"density") != 0 &&
2899 LocaleCompare(text[i].key,"units") != 0))
2900 (void) SetImageProperty(image,text[i].key,value);
2901
2902 if (logging != MagickFalse)
2903 {
2904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2905 " length: %lu",(unsigned long) length);
2906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2907 " Keyword: %s",text[i].key);
2908 }
2909
2910 value=DestroyString(value);
2911 }
2912 }
2913 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00002914 }
glennrp3c218112010-11-27 15:31:26 +00002915
cristy3ed852e2009-09-05 21:47:34 +00002916#ifdef MNG_OBJECT_BUFFERS
2917 /*
2918 Store the object if necessary.
2919 */
2920 if (object_id && !mng_info->frozen[object_id])
2921 {
2922 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2923 {
2924 /*
2925 create a new object buffer.
2926 */
2927 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00002928 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00002929
cristy3ed852e2009-09-05 21:47:34 +00002930 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2931 {
2932 mng_info->ob[object_id]->image=(Image *) NULL;
2933 mng_info->ob[object_id]->reference_count=1;
2934 }
2935 }
glennrp47b9dd52010-11-24 18:12:06 +00002936
cristy3ed852e2009-09-05 21:47:34 +00002937 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2938 mng_info->ob[object_id]->frozen)
2939 {
2940 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2941 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2942 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2943 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00002944
cristy3ed852e2009-09-05 21:47:34 +00002945 if (mng_info->ob[object_id]->frozen)
2946 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2947 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2948 "`%s'",image->filename);
2949 }
glennrp0fe50b42010-11-16 03:52:51 +00002950
cristy3ed852e2009-09-05 21:47:34 +00002951 else
2952 {
cristy3ed852e2009-09-05 21:47:34 +00002953
2954 if (mng_info->ob[object_id]->image != (Image *) NULL)
2955 mng_info->ob[object_id]->image=DestroyImage
2956 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00002957
cristy3ed852e2009-09-05 21:47:34 +00002958 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2959 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00002960
cristy3ed852e2009-09-05 21:47:34 +00002961 if (mng_info->ob[object_id]->image != (Image *) NULL)
2962 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002963
cristy3ed852e2009-09-05 21:47:34 +00002964 else
2965 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2966 ResourceLimitError,"Cloning image for object buffer failed",
2967 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00002968
glennrpfaa852b2010-03-30 12:17:00 +00002969 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00002970 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00002971
glennrpfaa852b2010-03-30 12:17:00 +00002972 mng_info->ob[object_id]->width=ping_width;
2973 mng_info->ob[object_id]->height=ping_height;
2974 mng_info->ob[object_id]->color_type=ping_color_type;
2975 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
2976 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
2977 mng_info->ob[object_id]->compression_method=
2978 ping_compression_method;
2979 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00002980
glennrpfaa852b2010-03-30 12:17:00 +00002981 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002982 {
2983 int
2984 number_colors;
2985
2986 png_colorp
2987 plte;
2988
2989 /*
2990 Copy the PLTE to the object buffer.
2991 */
2992 png_get_PLTE(ping,ping_info,&plte,&number_colors);
2993 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00002994
cristy3ed852e2009-09-05 21:47:34 +00002995 for (i=0; i < number_colors; i++)
2996 {
2997 mng_info->ob[object_id]->plte[i]=plte[i];
2998 }
2999 }
glennrp47b9dd52010-11-24 18:12:06 +00003000
cristy3ed852e2009-09-05 21:47:34 +00003001 else
3002 mng_info->ob[object_id]->plte_length=0;
3003 }
3004 }
3005#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003006
3007 /* Set image->matte to MagickTrue if the input colortype supports
3008 * alpha or if a valid tRNS chunk is present, no matter whether there
3009 * is actual transparency present.
3010 */
3011 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3012 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3013 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3014 MagickTrue : MagickFalse;
3015
glennrpcb395ac2011-03-30 19:50:23 +00003016 /* Set more properties for identify to retrieve */
3017 {
3018 char
3019 msg[MaxTextExtent];
3020
glennrp4eb39312011-03-30 21:34:55 +00003021 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003022 {
3023 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3024 (void) FormatMagickString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003025 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003026 (void) SetImageProperty(image,"PNG:text ",msg);
3027 }
3028
3029 if (num_raw_profiles != 0)
3030 {
3031 (void) FormatMagickString(msg,MaxTextExtent,
3032 "%d were found", num_raw_profiles);
3033 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3034 }
3035
glennrpcb395ac2011-03-30 19:50:23 +00003036 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003037 {
3038 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3039 "chunk was found (see Chromaticity, above)");
3040 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3041 }
glennrpcb395ac2011-03-30 19:50:23 +00003042
3043 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003044 {
3045 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3046 "chunk was found (see Background color, above)");
3047 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3048 }
3049
3050 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3051 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003052
3053 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3054 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3055
glennrpcb395ac2011-03-30 19:50:23 +00003056 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3057 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003058
3059#if defined(PNG_sRGB_SUPPORTED)
3060 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3061 {
glennrp07523c72011-03-31 18:12:10 +00003062 (void) FormatMagickString(msg,MaxTextExtent,
3063 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003064 (int) intent);
3065 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3066 }
3067#endif
3068
3069 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3070 {
glennrp07523c72011-03-31 18:12:10 +00003071 (void) FormatMagickString(msg,MaxTextExtent,
3072 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003073 file_gamma);
3074 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3075 }
3076
3077#if defined(PNG_pHYs_SUPPORTED)
3078 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3079 {
glennrp07523c72011-03-31 18:12:10 +00003080 (void) FormatMagickString(msg,MaxTextExtent,
3081 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003082 (double) x_resolution,(double) y_resolution, unit_type);
3083 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3084 }
3085#endif
3086
3087#if defined(PNG_oFFs_SUPPORTED)
3088 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3089 {
3090 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3091 (double) image->page.x,(double) image->page.y);
3092 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3093 }
3094#endif
3095
glennrp07523c72011-03-31 18:12:10 +00003096 if ((image->page.width != 0 && image->page.width != image->columns) ||
3097 (image->page.height != 0 && image->page.height != image->rows))
3098 {
3099 (void) FormatMagickString(msg,MaxTextExtent,
3100 "width=%.20g, height=%.20g",
3101 (double) image->page.width,(double) image->page.height);
3102 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3103 }
glennrpcb395ac2011-03-30 19:50:23 +00003104 }
3105
cristy3ed852e2009-09-05 21:47:34 +00003106 /*
3107 Relinquish resources.
3108 */
3109 png_destroy_read_struct(&ping,&ping_info,&end_info);
3110
glennrpcf002022011-01-30 02:38:15 +00003111 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003112#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003113 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003114#endif
3115
3116 if (logging != MagickFalse)
3117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3118 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003119
cristy3ed852e2009-09-05 21:47:34 +00003120 return(image);
3121
3122/* end of reading one PNG image */
3123}
3124
3125static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3126{
3127 Image
3128 *image,
3129 *previous;
3130
3131 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003132 have_mng_structure,
3133 logging,
cristy3ed852e2009-09-05 21:47:34 +00003134 status;
3135
3136 MngInfo
3137 *mng_info;
3138
3139 char
3140 magic_number[MaxTextExtent];
3141
cristy3ed852e2009-09-05 21:47:34 +00003142 ssize_t
3143 count;
3144
3145 /*
3146 Open image file.
3147 */
3148 assert(image_info != (const ImageInfo *) NULL);
3149 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003150
cristy3ed852e2009-09-05 21:47:34 +00003151 if (image_info->debug != MagickFalse)
3152 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3153 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003154
cristy3ed852e2009-09-05 21:47:34 +00003155 assert(exception != (ExceptionInfo *) NULL);
3156 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003157 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003158 image=AcquireImage(image_info);
3159 mng_info=(MngInfo *) NULL;
3160 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003161
cristy3ed852e2009-09-05 21:47:34 +00003162 if (status == MagickFalse)
3163 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003164
cristy3ed852e2009-09-05 21:47:34 +00003165 /*
3166 Verify PNG signature.
3167 */
3168 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003169
glennrpdde35db2011-02-21 12:06:32 +00003170 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003171 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003172
cristy3ed852e2009-09-05 21:47:34 +00003173 /*
3174 Allocate a MngInfo structure.
3175 */
3176 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003177 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003178
cristy3ed852e2009-09-05 21:47:34 +00003179 if (mng_info == (MngInfo *) NULL)
3180 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003181
cristy3ed852e2009-09-05 21:47:34 +00003182 /*
3183 Initialize members of the MngInfo structure.
3184 */
3185 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3186 mng_info->image=image;
3187 have_mng_structure=MagickTrue;
3188
3189 previous=image;
3190 image=ReadOnePNGImage(mng_info,image_info,exception);
3191 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003192
cristy3ed852e2009-09-05 21:47:34 +00003193 if (image == (Image *) NULL)
3194 {
3195 if (previous != (Image *) NULL)
3196 {
3197 if (previous->signature != MagickSignature)
3198 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003199
cristy3ed852e2009-09-05 21:47:34 +00003200 (void) CloseBlob(previous);
3201 (void) DestroyImageList(previous);
3202 }
glennrp0fe50b42010-11-16 03:52:51 +00003203
cristy3ed852e2009-09-05 21:47:34 +00003204 if (logging != MagickFalse)
3205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3206 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003207
cristy3ed852e2009-09-05 21:47:34 +00003208 return((Image *) NULL);
3209 }
glennrp47b9dd52010-11-24 18:12:06 +00003210
cristy3ed852e2009-09-05 21:47:34 +00003211 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003212
cristy3ed852e2009-09-05 21:47:34 +00003213 if ((image->columns == 0) || (image->rows == 0))
3214 {
3215 if (logging != MagickFalse)
3216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3217 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003218
cristy3ed852e2009-09-05 21:47:34 +00003219 ThrowReaderException(CorruptImageError,"CorruptImage");
3220 }
glennrp47b9dd52010-11-24 18:12:06 +00003221
glennrp3faa9a32011-04-23 14:00:25 +00003222#if 0 /* This is probably redundant now */
cristy3ed852e2009-09-05 21:47:34 +00003223 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3224 {
3225 (void) SetImageType(image,PaletteType);
glennrp0fe50b42010-11-16 03:52:51 +00003226
cristy3ed852e2009-09-05 21:47:34 +00003227 if (image->matte != MagickFalse)
3228 {
3229 /* To do: Reduce to binary transparency */
3230 }
3231 }
glennrp3faa9a32011-04-23 14:00:25 +00003232#endif
glennrp47b9dd52010-11-24 18:12:06 +00003233
cristy3ed852e2009-09-05 21:47:34 +00003234 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3235 {
3236 (void) SetImageType(image,TrueColorType);
3237 image->matte=MagickFalse;
3238 }
glennrp0fe50b42010-11-16 03:52:51 +00003239
cristy3ed852e2009-09-05 21:47:34 +00003240 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3241 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003242
cristy3ed852e2009-09-05 21:47:34 +00003243 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3245 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3246 (double) image->page.width,(double) image->page.height,
3247 (double) image->page.x,(double) image->page.y);
3248
3249 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003251
cristy3ed852e2009-09-05 21:47:34 +00003252 return(image);
3253}
3254
3255
3256
3257#if defined(JNG_SUPPORTED)
3258/*
3259%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3260% %
3261% %
3262% %
3263% R e a d O n e J N G I m a g e %
3264% %
3265% %
3266% %
3267%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3268%
3269% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3270% (minus the 8-byte signature) and returns it. It allocates the memory
3271% necessary for the new Image structure and returns a pointer to the new
3272% image.
3273%
3274% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3275%
3276% The format of the ReadOneJNGImage method is:
3277%
3278% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3279% ExceptionInfo *exception)
3280%
3281% A description of each parameter follows:
3282%
3283% o mng_info: Specifies a pointer to a MngInfo structure.
3284%
3285% o image_info: the image info.
3286%
3287% o exception: return any errors or warnings in this structure.
3288%
3289*/
3290static Image *ReadOneJNGImage(MngInfo *mng_info,
3291 const ImageInfo *image_info, ExceptionInfo *exception)
3292{
3293 Image
3294 *alpha_image,
3295 *color_image,
3296 *image,
3297 *jng_image;
3298
3299 ImageInfo
3300 *alpha_image_info,
3301 *color_image_info;
3302
cristy4383ec82011-01-05 15:42:32 +00003303 MagickBooleanType
3304 logging;
3305
cristybb503372010-05-27 20:51:26 +00003306 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003307 y;
3308
3309 MagickBooleanType
3310 status;
3311
3312 png_uint_32
3313 jng_height,
3314 jng_width;
3315
3316 png_byte
3317 jng_color_type,
3318 jng_image_sample_depth,
3319 jng_image_compression_method,
3320 jng_image_interlace_method,
3321 jng_alpha_sample_depth,
3322 jng_alpha_compression_method,
3323 jng_alpha_filter_method,
3324 jng_alpha_interlace_method;
3325
3326 register const PixelPacket
3327 *s;
3328
cristybb503372010-05-27 20:51:26 +00003329 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003330 i,
3331 x;
3332
3333 register PixelPacket
3334 *q;
3335
3336 register unsigned char
3337 *p;
3338
3339 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003340 read_JSEP,
3341 reading_idat,
3342 skip_to_iend;
3343
cristybb503372010-05-27 20:51:26 +00003344 size_t
cristy3ed852e2009-09-05 21:47:34 +00003345 length;
3346
3347 jng_alpha_compression_method=0;
3348 jng_alpha_sample_depth=8;
3349 jng_color_type=0;
3350 jng_height=0;
3351 jng_width=0;
3352 alpha_image=(Image *) NULL;
3353 color_image=(Image *) NULL;
3354 alpha_image_info=(ImageInfo *) NULL;
3355 color_image_info=(ImageInfo *) NULL;
3356
3357 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003358 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003359
3360 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003361
cristy3ed852e2009-09-05 21:47:34 +00003362 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3363 {
3364 /*
3365 Allocate next image structure.
3366 */
3367 if (logging != MagickFalse)
3368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3369 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003370
cristy3ed852e2009-09-05 21:47:34 +00003371 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003372
cristy3ed852e2009-09-05 21:47:34 +00003373 if (GetNextImageInList(image) == (Image *) NULL)
3374 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003375
cristy3ed852e2009-09-05 21:47:34 +00003376 image=SyncNextImageInList(image);
3377 }
3378 mng_info->image=image;
3379
3380 /*
3381 Signature bytes have already been read.
3382 */
3383
3384 read_JSEP=MagickFalse;
3385 reading_idat=MagickFalse;
3386 skip_to_iend=MagickFalse;
3387 for (;;)
3388 {
3389 char
3390 type[MaxTextExtent];
3391
3392 unsigned char
3393 *chunk;
3394
3395 unsigned int
3396 count;
3397
3398 /*
3399 Read a new JNG chunk.
3400 */
3401 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3402 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003403
cristy3ed852e2009-09-05 21:47:34 +00003404 if (status == MagickFalse)
3405 break;
glennrp0fe50b42010-11-16 03:52:51 +00003406
cristy3ed852e2009-09-05 21:47:34 +00003407 type[0]='\0';
3408 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3409 length=ReadBlobMSBLong(image);
3410 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3411
3412 if (logging != MagickFalse)
3413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003414 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3415 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003416
3417 if (length > PNG_UINT_31_MAX || count == 0)
3418 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003419
cristy3ed852e2009-09-05 21:47:34 +00003420 p=NULL;
3421 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003422
cristy3ed852e2009-09-05 21:47:34 +00003423 if (length)
3424 {
3425 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003426
cristy3ed852e2009-09-05 21:47:34 +00003427 if (chunk == (unsigned char *) NULL)
3428 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003429
cristybb503372010-05-27 20:51:26 +00003430 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003431 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003432
cristy3ed852e2009-09-05 21:47:34 +00003433 p=chunk;
3434 }
glennrp47b9dd52010-11-24 18:12:06 +00003435
cristy3ed852e2009-09-05 21:47:34 +00003436 (void) ReadBlobMSBLong(image); /* read crc word */
3437
3438 if (skip_to_iend)
3439 {
3440 if (length)
3441 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003442
cristy3ed852e2009-09-05 21:47:34 +00003443 continue;
3444 }
3445
3446 if (memcmp(type,mng_JHDR,4) == 0)
3447 {
3448 if (length == 16)
3449 {
cristybb503372010-05-27 20:51:26 +00003450 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003451 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003452 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003453 (p[6] << 8) | p[7]);
3454 jng_color_type=p[8];
3455 jng_image_sample_depth=p[9];
3456 jng_image_compression_method=p[10];
3457 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003458
cristy3ed852e2009-09-05 21:47:34 +00003459 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3460 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003461
cristy3ed852e2009-09-05 21:47:34 +00003462 jng_alpha_sample_depth=p[12];
3463 jng_alpha_compression_method=p[13];
3464 jng_alpha_filter_method=p[14];
3465 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003466
cristy3ed852e2009-09-05 21:47:34 +00003467 if (logging != MagickFalse)
3468 {
3469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003470 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003471
cristy3ed852e2009-09-05 21:47:34 +00003472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003473 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003474
cristy3ed852e2009-09-05 21:47:34 +00003475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3476 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003477
cristy3ed852e2009-09-05 21:47:34 +00003478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3479 " jng_image_sample_depth: %3d",
3480 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003481
cristy3ed852e2009-09-05 21:47:34 +00003482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3483 " jng_image_compression_method:%3d",
3484 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003485
cristy3ed852e2009-09-05 21:47:34 +00003486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3487 " jng_image_interlace_method: %3d",
3488 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003489
cristy3ed852e2009-09-05 21:47:34 +00003490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3491 " jng_alpha_sample_depth: %3d",
3492 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003493
cristy3ed852e2009-09-05 21:47:34 +00003494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3495 " jng_alpha_compression_method:%3d",
3496 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003497
cristy3ed852e2009-09-05 21:47:34 +00003498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3499 " jng_alpha_filter_method: %3d",
3500 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003501
cristy3ed852e2009-09-05 21:47:34 +00003502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3503 " jng_alpha_interlace_method: %3d",
3504 jng_alpha_interlace_method);
3505 }
3506 }
glennrp47b9dd52010-11-24 18:12:06 +00003507
cristy3ed852e2009-09-05 21:47:34 +00003508 if (length)
3509 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003510
cristy3ed852e2009-09-05 21:47:34 +00003511 continue;
3512 }
3513
3514
3515 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3516 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3517 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3518 {
3519 /*
3520 o create color_image
3521 o open color_blob, attached to color_image
3522 o if (color type has alpha)
3523 open alpha_blob, attached to alpha_image
3524 */
3525
cristy73bd4a52010-10-05 11:24:23 +00003526 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003527
cristy3ed852e2009-09-05 21:47:34 +00003528 if (color_image_info == (ImageInfo *) NULL)
3529 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003530
cristy3ed852e2009-09-05 21:47:34 +00003531 GetImageInfo(color_image_info);
3532 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003533
cristy3ed852e2009-09-05 21:47:34 +00003534 if (color_image == (Image *) NULL)
3535 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3536
3537 if (logging != MagickFalse)
3538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3539 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003540
cristy3ed852e2009-09-05 21:47:34 +00003541 (void) AcquireUniqueFilename(color_image->filename);
3542 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3543 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003544
cristy3ed852e2009-09-05 21:47:34 +00003545 if (status == MagickFalse)
3546 return((Image *) NULL);
3547
3548 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3549 {
3550 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00003551 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00003552
cristy3ed852e2009-09-05 21:47:34 +00003553 if (alpha_image_info == (ImageInfo *) NULL)
3554 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003555
cristy3ed852e2009-09-05 21:47:34 +00003556 GetImageInfo(alpha_image_info);
3557 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003558
cristy3ed852e2009-09-05 21:47:34 +00003559 if (alpha_image == (Image *) NULL)
3560 {
3561 alpha_image=DestroyImage(alpha_image);
3562 ThrowReaderException(ResourceLimitError,
3563 "MemoryAllocationFailed");
3564 }
glennrp0fe50b42010-11-16 03:52:51 +00003565
cristy3ed852e2009-09-05 21:47:34 +00003566 if (logging != MagickFalse)
3567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3568 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003569
cristy3ed852e2009-09-05 21:47:34 +00003570 (void) AcquireUniqueFilename(alpha_image->filename);
3571 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3572 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003573
cristy3ed852e2009-09-05 21:47:34 +00003574 if (status == MagickFalse)
3575 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003576
cristy3ed852e2009-09-05 21:47:34 +00003577 if (jng_alpha_compression_method == 0)
3578 {
3579 unsigned char
3580 data[18];
3581
3582 if (logging != MagickFalse)
3583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3584 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003585
cristy3ed852e2009-09-05 21:47:34 +00003586 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3587 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00003588
cristy3ed852e2009-09-05 21:47:34 +00003589 (void) WriteBlobMSBULong(alpha_image,13L);
3590 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00003591 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00003592 PNGLong(data+4,jng_width);
3593 PNGLong(data+8,jng_height);
3594 data[12]=jng_alpha_sample_depth;
3595 data[13]=0; /* color_type gray */
3596 data[14]=0; /* compression method 0 */
3597 data[15]=0; /* filter_method 0 */
3598 data[16]=0; /* interlace_method 0 */
3599 (void) WriteBlob(alpha_image,17,data);
3600 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3601 }
3602 }
3603 reading_idat=MagickTrue;
3604 }
3605
3606 if (memcmp(type,mng_JDAT,4) == 0)
3607 {
glennrp47b9dd52010-11-24 18:12:06 +00003608 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003609
3610 if (logging != MagickFalse)
3611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3612 " Copying JDAT chunk data to color_blob.");
3613
3614 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003615
cristy3ed852e2009-09-05 21:47:34 +00003616 if (length)
3617 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003618
cristy3ed852e2009-09-05 21:47:34 +00003619 continue;
3620 }
3621
3622 if (memcmp(type,mng_IDAT,4) == 0)
3623 {
3624 png_byte
3625 data[5];
3626
glennrp47b9dd52010-11-24 18:12:06 +00003627 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003628
3629 if (image_info->ping == MagickFalse)
3630 {
3631 if (logging != MagickFalse)
3632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3633 " Copying IDAT chunk data to alpha_blob.");
3634
cristybb503372010-05-27 20:51:26 +00003635 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003636 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00003637 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00003638 (void) WriteBlob(alpha_image,4,data);
3639 (void) WriteBlob(alpha_image,length,chunk);
3640 (void) WriteBlobMSBULong(alpha_image,
3641 crc32(crc32(0,data,4),chunk,(uInt) length));
3642 }
glennrp0fe50b42010-11-16 03:52:51 +00003643
cristy3ed852e2009-09-05 21:47:34 +00003644 if (length)
3645 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003646
cristy3ed852e2009-09-05 21:47:34 +00003647 continue;
3648 }
3649
3650 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3651 {
glennrp47b9dd52010-11-24 18:12:06 +00003652 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003653
3654 if (image_info->ping == MagickFalse)
3655 {
3656 if (logging != MagickFalse)
3657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3658 " Copying JDAA chunk data to alpha_blob.");
3659
3660 (void) WriteBlob(alpha_image,length,chunk);
3661 }
glennrp0fe50b42010-11-16 03:52:51 +00003662
cristy3ed852e2009-09-05 21:47:34 +00003663 if (length)
3664 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003665
cristy3ed852e2009-09-05 21:47:34 +00003666 continue;
3667 }
3668
3669 if (memcmp(type,mng_JSEP,4) == 0)
3670 {
3671 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00003672
cristy3ed852e2009-09-05 21:47:34 +00003673 if (length)
3674 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003675
cristy3ed852e2009-09-05 21:47:34 +00003676 continue;
3677 }
3678
3679 if (memcmp(type,mng_bKGD,4) == 0)
3680 {
3681 if (length == 2)
3682 {
3683 image->background_color.red=ScaleCharToQuantum(p[1]);
3684 image->background_color.green=image->background_color.red;
3685 image->background_color.blue=image->background_color.red;
3686 }
glennrp0fe50b42010-11-16 03:52:51 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688 if (length == 6)
3689 {
3690 image->background_color.red=ScaleCharToQuantum(p[1]);
3691 image->background_color.green=ScaleCharToQuantum(p[3]);
3692 image->background_color.blue=ScaleCharToQuantum(p[5]);
3693 }
glennrp0fe50b42010-11-16 03:52:51 +00003694
cristy3ed852e2009-09-05 21:47:34 +00003695 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3696 continue;
3697 }
3698
3699 if (memcmp(type,mng_gAMA,4) == 0)
3700 {
3701 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00003702 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00003703
cristy3ed852e2009-09-05 21:47:34 +00003704 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3705 continue;
3706 }
3707
3708 if (memcmp(type,mng_cHRM,4) == 0)
3709 {
3710 if (length == 32)
3711 {
cristy8182b072010-05-30 20:10:53 +00003712 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3713 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3714 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3715 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3716 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3717 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3718 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3719 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003720 }
glennrp47b9dd52010-11-24 18:12:06 +00003721
cristy3ed852e2009-09-05 21:47:34 +00003722 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3723 continue;
3724 }
3725
3726 if (memcmp(type,mng_sRGB,4) == 0)
3727 {
3728 if (length == 1)
3729 {
glennrpe610a072010-08-05 17:08:46 +00003730 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00003731 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00003732 image->gamma=0.45455f;
3733 image->chromaticity.red_primary.x=0.6400f;
3734 image->chromaticity.red_primary.y=0.3300f;
3735 image->chromaticity.green_primary.x=0.3000f;
3736 image->chromaticity.green_primary.y=0.6000f;
3737 image->chromaticity.blue_primary.x=0.1500f;
3738 image->chromaticity.blue_primary.y=0.0600f;
3739 image->chromaticity.white_point.x=0.3127f;
3740 image->chromaticity.white_point.y=0.3290f;
3741 }
glennrp47b9dd52010-11-24 18:12:06 +00003742
cristy3ed852e2009-09-05 21:47:34 +00003743 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3744 continue;
3745 }
3746
3747 if (memcmp(type,mng_oFFs,4) == 0)
3748 {
3749 if (length > 8)
3750 {
glennrp5eae7602011-02-22 15:21:32 +00003751 image->page.x=(ssize_t) mng_get_long(p);
3752 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00003753
cristy3ed852e2009-09-05 21:47:34 +00003754 if ((int) p[8] != 0)
3755 {
3756 image->page.x/=10000;
3757 image->page.y/=10000;
3758 }
3759 }
glennrp47b9dd52010-11-24 18:12:06 +00003760
cristy3ed852e2009-09-05 21:47:34 +00003761 if (length)
3762 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003763
cristy3ed852e2009-09-05 21:47:34 +00003764 continue;
3765 }
3766
3767 if (memcmp(type,mng_pHYs,4) == 0)
3768 {
3769 if (length > 8)
3770 {
cristy8182b072010-05-30 20:10:53 +00003771 image->x_resolution=(double) mng_get_long(p);
3772 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003773 if ((int) p[8] == PNG_RESOLUTION_METER)
3774 {
3775 image->units=PixelsPerCentimeterResolution;
3776 image->x_resolution=image->x_resolution/100.0f;
3777 image->y_resolution=image->y_resolution/100.0f;
3778 }
3779 }
glennrp0fe50b42010-11-16 03:52:51 +00003780
cristy3ed852e2009-09-05 21:47:34 +00003781 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3782 continue;
3783 }
3784
3785#if 0
3786 if (memcmp(type,mng_iCCP,4) == 0)
3787 {
glennrpfd05d622011-02-25 04:10:33 +00003788 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00003789 if (length)
3790 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003791
cristy3ed852e2009-09-05 21:47:34 +00003792 continue;
3793 }
3794#endif
3795
3796 if (length)
3797 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3798
3799 if (memcmp(type,mng_IEND,4))
3800 continue;
glennrp0fe50b42010-11-16 03:52:51 +00003801
cristy3ed852e2009-09-05 21:47:34 +00003802 break;
3803 }
3804
3805
3806 /* IEND found */
3807
3808 /*
3809 Finish up reading image data:
3810
3811 o read main image from color_blob.
3812
3813 o close color_blob.
3814
3815 o if (color_type has alpha)
3816 if alpha_encoding is PNG
3817 read secondary image from alpha_blob via ReadPNG
3818 if alpha_encoding is JPEG
3819 read secondary image from alpha_blob via ReadJPEG
3820
3821 o close alpha_blob.
3822
3823 o copy intensity of secondary image into
3824 opacity samples of main image.
3825
3826 o destroy the secondary image.
3827 */
3828
3829 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00003830
cristy3ed852e2009-09-05 21:47:34 +00003831 if (logging != MagickFalse)
3832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3833 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003834
cristy3ed852e2009-09-05 21:47:34 +00003835 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3836 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003837
cristy3ed852e2009-09-05 21:47:34 +00003838 color_image_info->ping=MagickFalse; /* To do: avoid this */
3839 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003840
cristy3ed852e2009-09-05 21:47:34 +00003841 if (jng_image == (Image *) NULL)
3842 return((Image *) NULL);
3843
3844 (void) RelinquishUniqueFileResource(color_image->filename);
3845 color_image=DestroyImage(color_image);
3846 color_image_info=DestroyImageInfo(color_image_info);
3847
3848 if (jng_image == (Image *) NULL)
3849 return((Image *) NULL);
3850
3851 if (logging != MagickFalse)
3852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3853 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00003854
cristy3ed852e2009-09-05 21:47:34 +00003855 image->rows=jng_height;
3856 image->columns=jng_width;
3857 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00003858
cristybb503372010-05-27 20:51:26 +00003859 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003860 {
3861 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3862 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3863 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00003864
cristy3ed852e2009-09-05 21:47:34 +00003865 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3866 break;
3867 }
glennrp0fe50b42010-11-16 03:52:51 +00003868
cristy3ed852e2009-09-05 21:47:34 +00003869 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00003870
cristy3ed852e2009-09-05 21:47:34 +00003871 if (image_info->ping == MagickFalse)
3872 {
3873 if (jng_color_type >= 12)
3874 {
3875 if (jng_alpha_compression_method == 0)
3876 {
3877 png_byte
3878 data[5];
3879 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3880 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00003881 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00003882 (void) WriteBlob(alpha_image,4,data);
3883 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3884 }
glennrp0fe50b42010-11-16 03:52:51 +00003885
cristy3ed852e2009-09-05 21:47:34 +00003886 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00003887
cristy3ed852e2009-09-05 21:47:34 +00003888 if (logging != MagickFalse)
3889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3890 " Reading opacity from alpha_blob.");
3891
3892 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3893 "%s",alpha_image->filename);
3894
3895 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003896
cristy3ed852e2009-09-05 21:47:34 +00003897 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003898 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003899 {
3900 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3901 &image->exception);
3902 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003903
cristy3ed852e2009-09-05 21:47:34 +00003904 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003905 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003906 q->opacity=(Quantum) QuantumRange-s->red;
glennrp0fe50b42010-11-16 03:52:51 +00003907
cristy3ed852e2009-09-05 21:47:34 +00003908 else
cristybb503372010-05-27 20:51:26 +00003909 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003910 {
3911 q->opacity=(Quantum) QuantumRange-s->red;
3912 if (q->opacity != OpaqueOpacity)
3913 image->matte=MagickTrue;
3914 }
glennrp0fe50b42010-11-16 03:52:51 +00003915
cristy3ed852e2009-09-05 21:47:34 +00003916 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3917 break;
3918 }
3919 (void) RelinquishUniqueFileResource(alpha_image->filename);
3920 alpha_image=DestroyImage(alpha_image);
3921 alpha_image_info=DestroyImageInfo(alpha_image_info);
3922 if (jng_image != (Image *) NULL)
3923 jng_image=DestroyImage(jng_image);
3924 }
3925 }
3926
glennrp47b9dd52010-11-24 18:12:06 +00003927 /* Read the JNG image. */
3928
cristy3ed852e2009-09-05 21:47:34 +00003929 if (mng_info->mng_type == 0)
3930 {
3931 mng_info->mng_width=jng_width;
3932 mng_info->mng_height=jng_height;
3933 }
glennrp0fe50b42010-11-16 03:52:51 +00003934
cristy3ed852e2009-09-05 21:47:34 +00003935 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003936 {
3937 image->page.width=jng_width;
3938 image->page.height=jng_height;
3939 }
3940
cristy3ed852e2009-09-05 21:47:34 +00003941 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003942 {
3943 image->page.x=mng_info->x_off[mng_info->object_id];
3944 image->page.y=mng_info->y_off[mng_info->object_id];
3945 }
3946
cristy3ed852e2009-09-05 21:47:34 +00003947 else
glennrp0fe50b42010-11-16 03:52:51 +00003948 {
3949 image->page.y=mng_info->y_off[mng_info->object_id];
3950 }
3951
cristy3ed852e2009-09-05 21:47:34 +00003952 mng_info->image_found++;
3953 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3954 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003955
cristy3ed852e2009-09-05 21:47:34 +00003956 if (logging != MagickFalse)
3957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3958 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003959
cristy3ed852e2009-09-05 21:47:34 +00003960 return(image);
3961}
3962
3963/*
3964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3965% %
3966% %
3967% %
3968% R e a d J N G I m a g e %
3969% %
3970% %
3971% %
3972%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3973%
3974% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3975% (including the 8-byte signature) and returns it. It allocates the memory
3976% necessary for the new Image structure and returns a pointer to the new
3977% image.
3978%
3979% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3980%
3981% The format of the ReadJNGImage method is:
3982%
3983% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3984% *exception)
3985%
3986% A description of each parameter follows:
3987%
3988% o image_info: the image info.
3989%
3990% o exception: return any errors or warnings in this structure.
3991%
3992*/
3993
3994static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3995{
3996 Image
3997 *image,
3998 *previous;
3999
4000 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004001 have_mng_structure,
4002 logging,
cristy3ed852e2009-09-05 21:47:34 +00004003 status;
4004
4005 MngInfo
4006 *mng_info;
4007
4008 char
4009 magic_number[MaxTextExtent];
4010
cristy3ed852e2009-09-05 21:47:34 +00004011 size_t
4012 count;
4013
4014 /*
4015 Open image file.
4016 */
4017 assert(image_info != (const ImageInfo *) NULL);
4018 assert(image_info->signature == MagickSignature);
4019 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4020 assert(exception != (ExceptionInfo *) NULL);
4021 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004022 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004023 image=AcquireImage(image_info);
4024 mng_info=(MngInfo *) NULL;
4025 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004026
cristy3ed852e2009-09-05 21:47:34 +00004027 if (status == MagickFalse)
4028 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004029
cristy3ed852e2009-09-05 21:47:34 +00004030 if (LocaleCompare(image_info->magick,"JNG") != 0)
4031 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004032
glennrp47b9dd52010-11-24 18:12:06 +00004033 /* Verify JNG signature. */
4034
cristy3ed852e2009-09-05 21:47:34 +00004035 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004036
glennrp3b8763e2011-02-21 12:08:18 +00004037 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004038 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004039
glennrp47b9dd52010-11-24 18:12:06 +00004040 /* Allocate a MngInfo structure. */
4041
cristy3ed852e2009-09-05 21:47:34 +00004042 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004043 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004044
cristy3ed852e2009-09-05 21:47:34 +00004045 if (mng_info == (MngInfo *) NULL)
4046 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004047
glennrp47b9dd52010-11-24 18:12:06 +00004048 /* Initialize members of the MngInfo structure. */
4049
cristy3ed852e2009-09-05 21:47:34 +00004050 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4051 have_mng_structure=MagickTrue;
4052
4053 mng_info->image=image;
4054 previous=image;
4055 image=ReadOneJNGImage(mng_info,image_info,exception);
4056 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004057
cristy3ed852e2009-09-05 21:47:34 +00004058 if (image == (Image *) NULL)
4059 {
4060 if (IsImageObject(previous) != MagickFalse)
4061 {
4062 (void) CloseBlob(previous);
4063 (void) DestroyImageList(previous);
4064 }
glennrp0fe50b42010-11-16 03:52:51 +00004065
cristy3ed852e2009-09-05 21:47:34 +00004066 if (logging != MagickFalse)
4067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4068 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004069
cristy3ed852e2009-09-05 21:47:34 +00004070 return((Image *) NULL);
4071 }
4072 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004073
cristy3ed852e2009-09-05 21:47:34 +00004074 if (image->columns == 0 || image->rows == 0)
4075 {
4076 if (logging != MagickFalse)
4077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4078 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004079
cristy3ed852e2009-09-05 21:47:34 +00004080 ThrowReaderException(CorruptImageError,"CorruptImage");
4081 }
glennrp0fe50b42010-11-16 03:52:51 +00004082
cristy3ed852e2009-09-05 21:47:34 +00004083 if (logging != MagickFalse)
4084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004085
cristy3ed852e2009-09-05 21:47:34 +00004086 return(image);
4087}
4088#endif
4089
4090static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4091{
4092 char
4093 page_geometry[MaxTextExtent];
4094
4095 Image
4096 *image,
4097 *previous;
4098
cristy4383ec82011-01-05 15:42:32 +00004099 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004100 logging,
4101 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004102
cristy3ed852e2009-09-05 21:47:34 +00004103 volatile int
4104 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004105 object_id,
4106 term_chunk_found,
4107 skip_to_iend;
4108
cristybb503372010-05-27 20:51:26 +00004109 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004110 image_count=0;
4111
4112 MagickBooleanType
4113 status;
4114
4115 MagickOffsetType
4116 offset;
4117
4118 MngInfo
4119 *mng_info;
4120
4121 MngBox
4122 default_fb,
4123 fb,
4124 previous_fb;
4125
4126#if defined(MNG_INSERT_LAYERS)
4127 PixelPacket
4128 mng_background_color;
4129#endif
4130
4131 register unsigned char
4132 *p;
4133
cristybb503372010-05-27 20:51:26 +00004134 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004135 i;
4136
4137 size_t
4138 count;
4139
cristybb503372010-05-27 20:51:26 +00004140 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004141 loop_level;
4142
4143 volatile short
4144 skipping_loop;
4145
4146#if defined(MNG_INSERT_LAYERS)
4147 unsigned int
4148 mandatory_back=0;
4149#endif
4150
4151 volatile unsigned int
4152#ifdef MNG_OBJECT_BUFFERS
4153 mng_background_object=0,
4154#endif
4155 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4156
cristybb503372010-05-27 20:51:26 +00004157 size_t
cristy3ed852e2009-09-05 21:47:34 +00004158 default_frame_timeout,
4159 frame_timeout,
4160#if defined(MNG_INSERT_LAYERS)
4161 image_height,
4162 image_width,
4163#endif
4164 length;
4165
glennrp38ea0832010-06-02 18:50:28 +00004166 /* These delays are all measured in image ticks_per_second,
4167 * not in MNG ticks_per_second
4168 */
cristybb503372010-05-27 20:51:26 +00004169 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004170 default_frame_delay,
4171 final_delay,
4172 final_image_delay,
4173 frame_delay,
4174#if defined(MNG_INSERT_LAYERS)
4175 insert_layers,
4176#endif
4177 mng_iterations=1,
4178 simplicity=0,
4179 subframe_height=0,
4180 subframe_width=0;
4181
4182 previous_fb.top=0;
4183 previous_fb.bottom=0;
4184 previous_fb.left=0;
4185 previous_fb.right=0;
4186 default_fb.top=0;
4187 default_fb.bottom=0;
4188 default_fb.left=0;
4189 default_fb.right=0;
4190
glennrp47b9dd52010-11-24 18:12:06 +00004191 /* Open image file. */
4192
cristy3ed852e2009-09-05 21:47:34 +00004193 assert(image_info != (const ImageInfo *) NULL);
4194 assert(image_info->signature == MagickSignature);
4195 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4196 assert(exception != (ExceptionInfo *) NULL);
4197 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004198 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004199 image=AcquireImage(image_info);
4200 mng_info=(MngInfo *) NULL;
4201 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004202
cristy3ed852e2009-09-05 21:47:34 +00004203 if (status == MagickFalse)
4204 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004205
cristy3ed852e2009-09-05 21:47:34 +00004206 first_mng_object=MagickFalse;
4207 skipping_loop=(-1);
4208 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004209
4210 /* Allocate a MngInfo structure. */
4211
cristy73bd4a52010-10-05 11:24:23 +00004212 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004213
cristy3ed852e2009-09-05 21:47:34 +00004214 if (mng_info == (MngInfo *) NULL)
4215 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004216
glennrp47b9dd52010-11-24 18:12:06 +00004217 /* Initialize members of the MngInfo structure. */
4218
cristy3ed852e2009-09-05 21:47:34 +00004219 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4220 mng_info->image=image;
4221 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004222
4223 if (LocaleCompare(image_info->magick,"MNG") == 0)
4224 {
4225 char
4226 magic_number[MaxTextExtent];
4227
glennrp47b9dd52010-11-24 18:12:06 +00004228 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004229 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4230 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4231 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004232
4233 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004234 for (i=0; i < MNG_MAX_OBJECTS; i++)
4235 {
cristybb503372010-05-27 20:51:26 +00004236 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4237 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004238 }
4239 mng_info->exists[0]=MagickTrue;
4240 }
glennrp47b9dd52010-11-24 18:12:06 +00004241
cristy3ed852e2009-09-05 21:47:34 +00004242 first_mng_object=MagickTrue;
4243 mng_type=0;
4244#if defined(MNG_INSERT_LAYERS)
4245 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4246#endif
4247 default_frame_delay=0;
4248 default_frame_timeout=0;
4249 frame_delay=0;
4250 final_delay=1;
4251 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4252 object_id=0;
4253 skip_to_iend=MagickFalse;
4254 term_chunk_found=MagickFalse;
4255 mng_info->framing_mode=1;
4256#if defined(MNG_INSERT_LAYERS)
4257 mandatory_back=MagickFalse;
4258#endif
4259#if defined(MNG_INSERT_LAYERS)
4260 mng_background_color=image->background_color;
4261#endif
4262 default_fb=mng_info->frame;
4263 previous_fb=mng_info->frame;
4264 do
4265 {
4266 char
4267 type[MaxTextExtent];
4268
4269 if (LocaleCompare(image_info->magick,"MNG") == 0)
4270 {
4271 unsigned char
4272 *chunk;
4273
4274 /*
4275 Read a new chunk.
4276 */
4277 type[0]='\0';
4278 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4279 length=ReadBlobMSBLong(image);
4280 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4281
4282 if (logging != MagickFalse)
4283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004284 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4285 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004286
4287 if (length > PNG_UINT_31_MAX)
4288 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004289
cristy3ed852e2009-09-05 21:47:34 +00004290 if (count == 0)
4291 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004292
cristy3ed852e2009-09-05 21:47:34 +00004293 p=NULL;
4294 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004295
cristy3ed852e2009-09-05 21:47:34 +00004296 if (length)
4297 {
4298 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004299
cristy3ed852e2009-09-05 21:47:34 +00004300 if (chunk == (unsigned char *) NULL)
4301 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004302
cristybb503372010-05-27 20:51:26 +00004303 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004304 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004305
cristy3ed852e2009-09-05 21:47:34 +00004306 p=chunk;
4307 }
glennrp0fe50b42010-11-16 03:52:51 +00004308
cristy3ed852e2009-09-05 21:47:34 +00004309 (void) ReadBlobMSBLong(image); /* read crc word */
4310
4311#if !defined(JNG_SUPPORTED)
4312 if (memcmp(type,mng_JHDR,4) == 0)
4313 {
4314 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004315
cristy3ed852e2009-09-05 21:47:34 +00004316 if (mng_info->jhdr_warning == 0)
4317 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4318 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004319
cristy3ed852e2009-09-05 21:47:34 +00004320 mng_info->jhdr_warning++;
4321 }
4322#endif
4323 if (memcmp(type,mng_DHDR,4) == 0)
4324 {
4325 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004326
cristy3ed852e2009-09-05 21:47:34 +00004327 if (mng_info->dhdr_warning == 0)
4328 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4329 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004330
cristy3ed852e2009-09-05 21:47:34 +00004331 mng_info->dhdr_warning++;
4332 }
4333 if (memcmp(type,mng_MEND,4) == 0)
4334 break;
glennrp47b9dd52010-11-24 18:12:06 +00004335
cristy3ed852e2009-09-05 21:47:34 +00004336 if (skip_to_iend)
4337 {
4338 if (memcmp(type,mng_IEND,4) == 0)
4339 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004340
cristy3ed852e2009-09-05 21:47:34 +00004341 if (length)
4342 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004343
cristy3ed852e2009-09-05 21:47:34 +00004344 if (logging != MagickFalse)
4345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4346 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004347
cristy3ed852e2009-09-05 21:47:34 +00004348 continue;
4349 }
glennrp0fe50b42010-11-16 03:52:51 +00004350
cristy3ed852e2009-09-05 21:47:34 +00004351 if (memcmp(type,mng_MHDR,4) == 0)
4352 {
cristybb503372010-05-27 20:51:26 +00004353 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004354 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004355
cristybb503372010-05-27 20:51:26 +00004356 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004357 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004358
cristy3ed852e2009-09-05 21:47:34 +00004359 if (logging != MagickFalse)
4360 {
4361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004362 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004364 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004365 }
glennrp0fe50b42010-11-16 03:52:51 +00004366
cristy3ed852e2009-09-05 21:47:34 +00004367 p+=8;
cristy8182b072010-05-30 20:10:53 +00004368 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004369
cristy3ed852e2009-09-05 21:47:34 +00004370 if (mng_info->ticks_per_second == 0)
4371 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004372
cristy3ed852e2009-09-05 21:47:34 +00004373 else
4374 default_frame_delay=1UL*image->ticks_per_second/
4375 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004376
cristy3ed852e2009-09-05 21:47:34 +00004377 frame_delay=default_frame_delay;
4378 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004379
cristy3ed852e2009-09-05 21:47:34 +00004380 if (length > 16)
4381 {
4382 p+=16;
cristy8182b072010-05-30 20:10:53 +00004383 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004384 }
glennrp0fe50b42010-11-16 03:52:51 +00004385
cristy3ed852e2009-09-05 21:47:34 +00004386 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004387
cristy3ed852e2009-09-05 21:47:34 +00004388 if ((simplicity != 0) && ((simplicity | 11) == 11))
4389 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004390
cristy3ed852e2009-09-05 21:47:34 +00004391 if ((simplicity != 0) && ((simplicity | 9) == 9))
4392 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004393
cristy3ed852e2009-09-05 21:47:34 +00004394#if defined(MNG_INSERT_LAYERS)
4395 if (mng_type != 3)
4396 insert_layers=MagickTrue;
4397#endif
4398 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4399 {
glennrp47b9dd52010-11-24 18:12:06 +00004400 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004401 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004402
cristy3ed852e2009-09-05 21:47:34 +00004403 if (GetNextImageInList(image) == (Image *) NULL)
4404 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004405
cristy3ed852e2009-09-05 21:47:34 +00004406 image=SyncNextImageInList(image);
4407 mng_info->image=image;
4408 }
4409
4410 if ((mng_info->mng_width > 65535L) ||
4411 (mng_info->mng_height > 65535L))
4412 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004413
cristye8c25f92010-06-03 00:53:06 +00004414 (void) FormatMagickString(page_geometry,MaxTextExtent,
4415 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004416 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004417
cristy3ed852e2009-09-05 21:47:34 +00004418 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004419 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004420 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004421 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004422 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004423
cristy3ed852e2009-09-05 21:47:34 +00004424 for (i=0; i < MNG_MAX_OBJECTS; i++)
4425 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004426
cristy3ed852e2009-09-05 21:47:34 +00004427 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4428 continue;
4429 }
4430
4431 if (memcmp(type,mng_TERM,4) == 0)
4432 {
4433 int
4434 repeat=0;
4435
4436
4437 if (length)
4438 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004439
cristy3ed852e2009-09-05 21:47:34 +00004440 if (repeat == 3)
4441 {
cristy8182b072010-05-30 20:10:53 +00004442 final_delay=(png_uint_32) mng_get_long(&p[2]);
4443 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004444
cristy3ed852e2009-09-05 21:47:34 +00004445 if (mng_iterations == PNG_UINT_31_MAX)
4446 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004447
cristy3ed852e2009-09-05 21:47:34 +00004448 image->iterations=mng_iterations;
4449 term_chunk_found=MagickTrue;
4450 }
glennrp0fe50b42010-11-16 03:52:51 +00004451
cristy3ed852e2009-09-05 21:47:34 +00004452 if (logging != MagickFalse)
4453 {
4454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4455 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004456
cristy3ed852e2009-09-05 21:47:34 +00004457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004458 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004459
cristy3ed852e2009-09-05 21:47:34 +00004460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004461 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004462 }
glennrp0fe50b42010-11-16 03:52:51 +00004463
cristy3ed852e2009-09-05 21:47:34 +00004464 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4465 continue;
4466 }
4467 if (memcmp(type,mng_DEFI,4) == 0)
4468 {
4469 if (mng_type == 3)
4470 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4471 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4472 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004473
cristy3ed852e2009-09-05 21:47:34 +00004474 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004475
cristy3ed852e2009-09-05 21:47:34 +00004476 if (mng_type == 2 && object_id != 0)
4477 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4478 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4479 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004480
cristy3ed852e2009-09-05 21:47:34 +00004481 if (object_id > MNG_MAX_OBJECTS)
4482 {
4483 /*
4484 Instead ofsuing a warning we should allocate a larger
4485 MngInfo structure and continue.
4486 */
4487 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4488 CoderError,"object id too large","`%s'",image->filename);
4489 object_id=MNG_MAX_OBJECTS;
4490 }
glennrp0fe50b42010-11-16 03:52:51 +00004491
cristy3ed852e2009-09-05 21:47:34 +00004492 if (mng_info->exists[object_id])
4493 if (mng_info->frozen[object_id])
4494 {
4495 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4496 (void) ThrowMagickException(&image->exception,
4497 GetMagickModule(),CoderError,
4498 "DEFI cannot redefine a frozen MNG object","`%s'",
4499 image->filename);
4500 continue;
4501 }
glennrp0fe50b42010-11-16 03:52:51 +00004502
cristy3ed852e2009-09-05 21:47:34 +00004503 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004504
cristy3ed852e2009-09-05 21:47:34 +00004505 if (length > 2)
4506 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004507
cristy3ed852e2009-09-05 21:47:34 +00004508 /*
4509 Extract object offset info.
4510 */
4511 if (length > 11)
4512 {
glennrp0fe50b42010-11-16 03:52:51 +00004513 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4514 (p[5] << 16) | (p[6] << 8) | p[7]);
4515
4516 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4517 (p[9] << 16) | (p[10] << 8) | p[11]);
4518
cristy3ed852e2009-09-05 21:47:34 +00004519 if (logging != MagickFalse)
4520 {
4521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004522 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004523 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004524
cristy3ed852e2009-09-05 21:47:34 +00004525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004526 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004527 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004528 }
4529 }
glennrp0fe50b42010-11-16 03:52:51 +00004530
cristy3ed852e2009-09-05 21:47:34 +00004531 /*
4532 Extract object clipping info.
4533 */
4534 if (length > 27)
4535 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4536 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004537
cristy3ed852e2009-09-05 21:47:34 +00004538 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4539 continue;
4540 }
4541 if (memcmp(type,mng_bKGD,4) == 0)
4542 {
4543 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004544
cristy3ed852e2009-09-05 21:47:34 +00004545 if (length > 5)
4546 {
4547 mng_info->mng_global_bkgd.red=
4548 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004549
cristy3ed852e2009-09-05 21:47:34 +00004550 mng_info->mng_global_bkgd.green=
4551 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004552
cristy3ed852e2009-09-05 21:47:34 +00004553 mng_info->mng_global_bkgd.blue=
4554 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004555
cristy3ed852e2009-09-05 21:47:34 +00004556 mng_info->have_global_bkgd=MagickTrue;
4557 }
glennrp0fe50b42010-11-16 03:52:51 +00004558
cristy3ed852e2009-09-05 21:47:34 +00004559 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4560 continue;
4561 }
4562 if (memcmp(type,mng_BACK,4) == 0)
4563 {
4564#if defined(MNG_INSERT_LAYERS)
4565 if (length > 6)
4566 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00004567
cristy3ed852e2009-09-05 21:47:34 +00004568 else
4569 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00004570
cristy3ed852e2009-09-05 21:47:34 +00004571 if (mandatory_back && length > 5)
4572 {
4573 mng_background_color.red=
4574 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004575
cristy3ed852e2009-09-05 21:47:34 +00004576 mng_background_color.green=
4577 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004578
cristy3ed852e2009-09-05 21:47:34 +00004579 mng_background_color.blue=
4580 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004581
cristy3ed852e2009-09-05 21:47:34 +00004582 mng_background_color.opacity=OpaqueOpacity;
4583 }
glennrp0fe50b42010-11-16 03:52:51 +00004584
cristy3ed852e2009-09-05 21:47:34 +00004585#ifdef MNG_OBJECT_BUFFERS
4586 if (length > 8)
4587 mng_background_object=(p[7] << 8) | p[8];
4588#endif
4589#endif
4590 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4591 continue;
4592 }
glennrp47b9dd52010-11-24 18:12:06 +00004593
cristy3ed852e2009-09-05 21:47:34 +00004594 if (memcmp(type,mng_PLTE,4) == 0)
4595 {
glennrp47b9dd52010-11-24 18:12:06 +00004596 /* Read global PLTE. */
4597
cristy3ed852e2009-09-05 21:47:34 +00004598 if (length && (length < 769))
4599 {
4600 if (mng_info->global_plte == (png_colorp) NULL)
4601 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4602 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00004603
cristybb503372010-05-27 20:51:26 +00004604 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004605 {
4606 mng_info->global_plte[i].red=p[3*i];
4607 mng_info->global_plte[i].green=p[3*i+1];
4608 mng_info->global_plte[i].blue=p[3*i+2];
4609 }
glennrp0fe50b42010-11-16 03:52:51 +00004610
cristy35ef8242010-06-03 16:24:13 +00004611 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00004612 }
4613#ifdef MNG_LOOSE
4614 for ( ; i < 256; i++)
4615 {
4616 mng_info->global_plte[i].red=i;
4617 mng_info->global_plte[i].green=i;
4618 mng_info->global_plte[i].blue=i;
4619 }
glennrp0fe50b42010-11-16 03:52:51 +00004620
cristy3ed852e2009-09-05 21:47:34 +00004621 if (length)
4622 mng_info->global_plte_length=256;
4623#endif
4624 else
4625 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00004626
cristy3ed852e2009-09-05 21:47:34 +00004627 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4628 continue;
4629 }
glennrp47b9dd52010-11-24 18:12:06 +00004630
cristy3ed852e2009-09-05 21:47:34 +00004631 if (memcmp(type,mng_tRNS,4) == 0)
4632 {
4633 /* read global tRNS */
4634
4635 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004636 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004637 mng_info->global_trns[i]=p[i];
4638
4639#ifdef MNG_LOOSE
4640 for ( ; i < 256; i++)
4641 mng_info->global_trns[i]=255;
4642#endif
cristy12560f32010-06-03 16:51:08 +00004643 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00004644 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4645 continue;
4646 }
4647 if (memcmp(type,mng_gAMA,4) == 0)
4648 {
4649 if (length == 4)
4650 {
cristybb503372010-05-27 20:51:26 +00004651 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004652 igamma;
4653
cristy8182b072010-05-30 20:10:53 +00004654 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004655 mng_info->global_gamma=((float) igamma)*0.00001;
4656 mng_info->have_global_gama=MagickTrue;
4657 }
glennrp0fe50b42010-11-16 03:52:51 +00004658
cristy3ed852e2009-09-05 21:47:34 +00004659 else
4660 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004661
cristy3ed852e2009-09-05 21:47:34 +00004662 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4663 continue;
4664 }
4665
4666 if (memcmp(type,mng_cHRM,4) == 0)
4667 {
glennrp47b9dd52010-11-24 18:12:06 +00004668 /* Read global cHRM */
4669
cristy3ed852e2009-09-05 21:47:34 +00004670 if (length == 32)
4671 {
cristy8182b072010-05-30 20:10:53 +00004672 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4673 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4674 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004675 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004676 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004677 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004678 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004679 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004680 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004681 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004682 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004683 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004684 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004685 mng_info->have_global_chrm=MagickTrue;
4686 }
4687 else
4688 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004689
cristy3ed852e2009-09-05 21:47:34 +00004690 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4691 continue;
4692 }
glennrp47b9dd52010-11-24 18:12:06 +00004693
cristy3ed852e2009-09-05 21:47:34 +00004694 if (memcmp(type,mng_sRGB,4) == 0)
4695 {
4696 /*
4697 Read global sRGB.
4698 */
4699 if (length)
4700 {
glennrpe610a072010-08-05 17:08:46 +00004701 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00004702 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004703 mng_info->have_global_srgb=MagickTrue;
4704 }
4705 else
4706 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004707
cristy3ed852e2009-09-05 21:47:34 +00004708 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4709 continue;
4710 }
glennrp47b9dd52010-11-24 18:12:06 +00004711
cristy3ed852e2009-09-05 21:47:34 +00004712 if (memcmp(type,mng_iCCP,4) == 0)
4713 {
glennrpfd05d622011-02-25 04:10:33 +00004714 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004715
4716 /*
4717 Read global iCCP.
4718 */
4719 if (length)
4720 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004721
cristy3ed852e2009-09-05 21:47:34 +00004722 continue;
4723 }
glennrp47b9dd52010-11-24 18:12:06 +00004724
cristy3ed852e2009-09-05 21:47:34 +00004725 if (memcmp(type,mng_FRAM,4) == 0)
4726 {
4727 if (mng_type == 3)
4728 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4729 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4730 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004731
cristy3ed852e2009-09-05 21:47:34 +00004732 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4733 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004734
cristy3ed852e2009-09-05 21:47:34 +00004735 frame_delay=default_frame_delay;
4736 frame_timeout=default_frame_timeout;
4737 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00004738
cristy3ed852e2009-09-05 21:47:34 +00004739 if (length)
4740 if (p[0])
4741 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004742
cristy3ed852e2009-09-05 21:47:34 +00004743 if (logging != MagickFalse)
4744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4745 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00004746
cristy3ed852e2009-09-05 21:47:34 +00004747 if (length > 6)
4748 {
glennrp47b9dd52010-11-24 18:12:06 +00004749 /* Note the delay and frame clipping boundaries. */
4750
cristy3ed852e2009-09-05 21:47:34 +00004751 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00004752
cristybb503372010-05-27 20:51:26 +00004753 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004754 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00004755
cristy3ed852e2009-09-05 21:47:34 +00004756 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00004757
cristybb503372010-05-27 20:51:26 +00004758 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004759 {
4760 int
4761 change_delay,
4762 change_timeout,
4763 change_clipping;
4764
4765 change_delay=(*p++);
4766 change_timeout=(*p++);
4767 change_clipping=(*p++);
4768 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00004769
cristy3ed852e2009-09-05 21:47:34 +00004770 if (change_delay)
4771 {
cristy8182b072010-05-30 20:10:53 +00004772 frame_delay=1UL*image->ticks_per_second*
4773 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004774
cristy8182b072010-05-30 20:10:53 +00004775 if (mng_info->ticks_per_second != 0)
4776 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004777
glennrpbb010dd2010-06-01 13:07:15 +00004778 else
4779 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004780
cristy3ed852e2009-09-05 21:47:34 +00004781 if (change_delay == 2)
4782 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004783
cristy3ed852e2009-09-05 21:47:34 +00004784 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004785
cristy3ed852e2009-09-05 21:47:34 +00004786 if (logging != MagickFalse)
4787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004788 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004789 }
glennrp47b9dd52010-11-24 18:12:06 +00004790
cristy3ed852e2009-09-05 21:47:34 +00004791 if (change_timeout)
4792 {
glennrpbb010dd2010-06-01 13:07:15 +00004793 frame_timeout=1UL*image->ticks_per_second*
4794 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004795
glennrpbb010dd2010-06-01 13:07:15 +00004796 if (mng_info->ticks_per_second != 0)
4797 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004798
glennrpbb010dd2010-06-01 13:07:15 +00004799 else
4800 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004801
cristy3ed852e2009-09-05 21:47:34 +00004802 if (change_delay == 2)
4803 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00004804
cristy3ed852e2009-09-05 21:47:34 +00004805 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004806
cristy3ed852e2009-09-05 21:47:34 +00004807 if (logging != MagickFalse)
4808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004809 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004810 }
glennrp47b9dd52010-11-24 18:12:06 +00004811
cristy3ed852e2009-09-05 21:47:34 +00004812 if (change_clipping)
4813 {
4814 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4815 p+=17;
4816 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00004817
cristy3ed852e2009-09-05 21:47:34 +00004818 if (logging != MagickFalse)
4819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004820 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004821 (double) fb.left,(double) fb.right,(double) fb.top,
4822 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00004823
cristy3ed852e2009-09-05 21:47:34 +00004824 if (change_clipping == 2)
4825 default_fb=fb;
4826 }
4827 }
4828 }
4829 mng_info->clip=fb;
4830 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00004831
cristybb503372010-05-27 20:51:26 +00004832 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004833 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00004834
cristybb503372010-05-27 20:51:26 +00004835 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004836 -mng_info->clip.top);
4837 /*
4838 Insert a background layer behind the frame if framing_mode is 4.
4839 */
4840#if defined(MNG_INSERT_LAYERS)
4841 if (logging != MagickFalse)
4842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004843 " subframe_width=%.20g, subframe_height=%.20g",(double)
4844 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00004845
cristy3ed852e2009-09-05 21:47:34 +00004846 if (insert_layers && (mng_info->framing_mode == 4) &&
4847 (subframe_width) && (subframe_height))
4848 {
glennrp47b9dd52010-11-24 18:12:06 +00004849 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004850 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4851 {
4852 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854 if (GetNextImageInList(image) == (Image *) NULL)
4855 {
4856 image=DestroyImageList(image);
4857 MngInfoFreeStruct(mng_info,&have_mng_structure);
4858 return((Image *) NULL);
4859 }
glennrp47b9dd52010-11-24 18:12:06 +00004860
cristy3ed852e2009-09-05 21:47:34 +00004861 image=SyncNextImageInList(image);
4862 }
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 if (term_chunk_found)
4867 {
4868 image->start_loop=MagickTrue;
4869 image->iterations=mng_iterations;
4870 term_chunk_found=MagickFalse;
4871 }
glennrp0fe50b42010-11-16 03:52:51 +00004872
cristy3ed852e2009-09-05 21:47:34 +00004873 else
4874 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004875
cristy3ed852e2009-09-05 21:47:34 +00004876 image->columns=subframe_width;
4877 image->rows=subframe_height;
4878 image->page.width=subframe_width;
4879 image->page.height=subframe_height;
4880 image->page.x=mng_info->clip.left;
4881 image->page.y=mng_info->clip.top;
4882 image->background_color=mng_background_color;
4883 image->matte=MagickFalse;
4884 image->delay=0;
4885 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00004886
cristy3ed852e2009-09-05 21:47:34 +00004887 if (logging != MagickFalse)
4888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004889 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004890 (double) mng_info->clip.left,(double) mng_info->clip.right,
4891 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004892 }
4893#endif
4894 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4895 continue;
4896 }
4897 if (memcmp(type,mng_CLIP,4) == 0)
4898 {
4899 unsigned int
4900 first_object,
4901 last_object;
4902
4903 /*
4904 Read CLIP.
4905 */
4906 first_object=(p[0] << 8) | p[1];
4907 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00004908
cristy3ed852e2009-09-05 21:47:34 +00004909 for (i=(int) first_object; i <= (int) last_object; i++)
4910 {
4911 if (mng_info->exists[i] && !mng_info->frozen[i])
4912 {
4913 MngBox
4914 box;
4915
4916 box=mng_info->object_clip[i];
4917 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4918 }
4919 }
glennrp47b9dd52010-11-24 18:12:06 +00004920
cristy3ed852e2009-09-05 21:47:34 +00004921 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4922 continue;
4923 }
4924 if (memcmp(type,mng_SAVE,4) == 0)
4925 {
4926 for (i=1; i < MNG_MAX_OBJECTS; i++)
4927 if (mng_info->exists[i])
4928 {
4929 mng_info->frozen[i]=MagickTrue;
4930#ifdef MNG_OBJECT_BUFFERS
4931 if (mng_info->ob[i] != (MngBuffer *) NULL)
4932 mng_info->ob[i]->frozen=MagickTrue;
4933#endif
4934 }
glennrp0fe50b42010-11-16 03:52:51 +00004935
cristy3ed852e2009-09-05 21:47:34 +00004936 if (length)
4937 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004938
cristy3ed852e2009-09-05 21:47:34 +00004939 continue;
4940 }
4941
4942 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4943 {
glennrp47b9dd52010-11-24 18:12:06 +00004944 /* Read DISC or SEEK. */
4945
cristy3ed852e2009-09-05 21:47:34 +00004946 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4947 {
4948 for (i=1; i < MNG_MAX_OBJECTS; i++)
4949 MngInfoDiscardObject(mng_info,i);
4950 }
glennrp0fe50b42010-11-16 03:52:51 +00004951
cristy3ed852e2009-09-05 21:47:34 +00004952 else
4953 {
cristybb503372010-05-27 20:51:26 +00004954 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004955 j;
4956
cristybb503372010-05-27 20:51:26 +00004957 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00004958 {
4959 i=p[j] << 8 | p[j+1];
4960 MngInfoDiscardObject(mng_info,i);
4961 }
4962 }
glennrp0fe50b42010-11-16 03:52:51 +00004963
cristy3ed852e2009-09-05 21:47:34 +00004964 if (length)
4965 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004966
cristy3ed852e2009-09-05 21:47:34 +00004967 continue;
4968 }
glennrp47b9dd52010-11-24 18:12:06 +00004969
cristy3ed852e2009-09-05 21:47:34 +00004970 if (memcmp(type,mng_MOVE,4) == 0)
4971 {
cristybb503372010-05-27 20:51:26 +00004972 size_t
cristy3ed852e2009-09-05 21:47:34 +00004973 first_object,
4974 last_object;
4975
glennrp47b9dd52010-11-24 18:12:06 +00004976 /* read MOVE */
4977
cristy3ed852e2009-09-05 21:47:34 +00004978 first_object=(p[0] << 8) | p[1];
4979 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00004980 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00004981 {
4982 if (mng_info->exists[i] && !mng_info->frozen[i])
4983 {
4984 MngPair
4985 new_pair;
4986
4987 MngPair
4988 old_pair;
4989
4990 old_pair.a=mng_info->x_off[i];
4991 old_pair.b=mng_info->y_off[i];
4992 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4993 mng_info->x_off[i]=new_pair.a;
4994 mng_info->y_off[i]=new_pair.b;
4995 }
4996 }
glennrp47b9dd52010-11-24 18:12:06 +00004997
cristy3ed852e2009-09-05 21:47:34 +00004998 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4999 continue;
5000 }
5001
5002 if (memcmp(type,mng_LOOP,4) == 0)
5003 {
cristybb503372010-05-27 20:51:26 +00005004 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005005 loop_level=chunk[0];
5006 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005007
5008 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005009 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005010
cristy3ed852e2009-09-05 21:47:34 +00005011 if (logging != MagickFalse)
5012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005013 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5014 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016 if (loop_iters == 0)
5017 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005018
cristy3ed852e2009-09-05 21:47:34 +00005019 else
5020 {
5021 mng_info->loop_jump[loop_level]=TellBlob(image);
5022 mng_info->loop_count[loop_level]=loop_iters;
5023 }
glennrp0fe50b42010-11-16 03:52:51 +00005024
cristy3ed852e2009-09-05 21:47:34 +00005025 mng_info->loop_iteration[loop_level]=0;
5026 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5027 continue;
5028 }
glennrp47b9dd52010-11-24 18:12:06 +00005029
cristy3ed852e2009-09-05 21:47:34 +00005030 if (memcmp(type,mng_ENDL,4) == 0)
5031 {
5032 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005033
cristy3ed852e2009-09-05 21:47:34 +00005034 if (skipping_loop > 0)
5035 {
5036 if (skipping_loop == loop_level)
5037 {
5038 /*
5039 Found end of zero-iteration loop.
5040 */
5041 skipping_loop=(-1);
5042 mng_info->loop_active[loop_level]=0;
5043 }
5044 }
glennrp47b9dd52010-11-24 18:12:06 +00005045
cristy3ed852e2009-09-05 21:47:34 +00005046 else
5047 {
5048 if (mng_info->loop_active[loop_level] == 1)
5049 {
5050 mng_info->loop_count[loop_level]--;
5051 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005052
cristy3ed852e2009-09-05 21:47:34 +00005053 if (logging != MagickFalse)
5054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005055 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005056 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005057 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005058
cristy3ed852e2009-09-05 21:47:34 +00005059 if (mng_info->loop_count[loop_level] != 0)
5060 {
5061 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5062 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005063
cristy3ed852e2009-09-05 21:47:34 +00005064 if (offset < 0)
5065 ThrowReaderException(CorruptImageError,
5066 "ImproperImageHeader");
5067 }
glennrp47b9dd52010-11-24 18:12:06 +00005068
cristy3ed852e2009-09-05 21:47:34 +00005069 else
5070 {
5071 short
5072 last_level;
5073
5074 /*
5075 Finished loop.
5076 */
5077 mng_info->loop_active[loop_level]=0;
5078 last_level=(-1);
5079 for (i=0; i < loop_level; i++)
5080 if (mng_info->loop_active[i] == 1)
5081 last_level=(short) i;
5082 loop_level=last_level;
5083 }
5084 }
5085 }
glennrp47b9dd52010-11-24 18:12:06 +00005086
cristy3ed852e2009-09-05 21:47:34 +00005087 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5088 continue;
5089 }
glennrp47b9dd52010-11-24 18:12:06 +00005090
cristy3ed852e2009-09-05 21:47:34 +00005091 if (memcmp(type,mng_CLON,4) == 0)
5092 {
5093 if (mng_info->clon_warning == 0)
5094 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5095 CoderError,"CLON is not implemented yet","`%s'",
5096 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005097
cristy3ed852e2009-09-05 21:47:34 +00005098 mng_info->clon_warning++;
5099 }
glennrp47b9dd52010-11-24 18:12:06 +00005100
cristy3ed852e2009-09-05 21:47:34 +00005101 if (memcmp(type,mng_MAGN,4) == 0)
5102 {
5103 png_uint_16
5104 magn_first,
5105 magn_last,
5106 magn_mb,
5107 magn_ml,
5108 magn_mr,
5109 magn_mt,
5110 magn_mx,
5111 magn_my,
5112 magn_methx,
5113 magn_methy;
5114
5115 if (length > 1)
5116 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005117
cristy3ed852e2009-09-05 21:47:34 +00005118 else
5119 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005120
cristy3ed852e2009-09-05 21:47:34 +00005121 if (length > 3)
5122 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005123
cristy3ed852e2009-09-05 21:47:34 +00005124 else
5125 magn_last=magn_first;
5126#ifndef MNG_OBJECT_BUFFERS
5127 if (magn_first || magn_last)
5128 if (mng_info->magn_warning == 0)
5129 {
5130 (void) ThrowMagickException(&image->exception,
5131 GetMagickModule(),CoderError,
5132 "MAGN is not implemented yet for nonzero objects",
5133 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005134
cristy3ed852e2009-09-05 21:47:34 +00005135 mng_info->magn_warning++;
5136 }
5137#endif
5138 if (length > 4)
5139 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005140
cristy3ed852e2009-09-05 21:47:34 +00005141 else
5142 magn_methx=0;
5143
5144 if (length > 6)
5145 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005146
cristy3ed852e2009-09-05 21:47:34 +00005147 else
5148 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005149
cristy3ed852e2009-09-05 21:47:34 +00005150 if (magn_mx == 0)
5151 magn_mx=1;
5152
5153 if (length > 8)
5154 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005155
cristy3ed852e2009-09-05 21:47:34 +00005156 else
5157 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005158
cristy3ed852e2009-09-05 21:47:34 +00005159 if (magn_my == 0)
5160 magn_my=1;
5161
5162 if (length > 10)
5163 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005164
cristy3ed852e2009-09-05 21:47:34 +00005165 else
5166 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005167
cristy3ed852e2009-09-05 21:47:34 +00005168 if (magn_ml == 0)
5169 magn_ml=1;
5170
5171 if (length > 12)
5172 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005173
cristy3ed852e2009-09-05 21:47:34 +00005174 else
5175 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005176
cristy3ed852e2009-09-05 21:47:34 +00005177 if (magn_mr == 0)
5178 magn_mr=1;
5179
5180 if (length > 14)
5181 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005182
cristy3ed852e2009-09-05 21:47:34 +00005183 else
5184 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005185
cristy3ed852e2009-09-05 21:47:34 +00005186 if (magn_mt == 0)
5187 magn_mt=1;
5188
5189 if (length > 16)
5190 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005191
cristy3ed852e2009-09-05 21:47:34 +00005192 else
5193 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005194
cristy3ed852e2009-09-05 21:47:34 +00005195 if (magn_mb == 0)
5196 magn_mb=1;
5197
5198 if (length > 17)
5199 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201 else
5202 magn_methy=magn_methx;
5203
glennrp47b9dd52010-11-24 18:12:06 +00005204
cristy3ed852e2009-09-05 21:47:34 +00005205 if (magn_methx > 5 || magn_methy > 5)
5206 if (mng_info->magn_warning == 0)
5207 {
5208 (void) ThrowMagickException(&image->exception,
5209 GetMagickModule(),CoderError,
5210 "Unknown MAGN method in MNG datastream","`%s'",
5211 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005212
cristy3ed852e2009-09-05 21:47:34 +00005213 mng_info->magn_warning++;
5214 }
5215#ifdef MNG_OBJECT_BUFFERS
5216 /* Magnify existing objects in the range magn_first to magn_last */
5217#endif
5218 if (magn_first == 0 || magn_last == 0)
5219 {
5220 /* Save the magnification factors for object 0 */
5221 mng_info->magn_mb=magn_mb;
5222 mng_info->magn_ml=magn_ml;
5223 mng_info->magn_mr=magn_mr;
5224 mng_info->magn_mt=magn_mt;
5225 mng_info->magn_mx=magn_mx;
5226 mng_info->magn_my=magn_my;
5227 mng_info->magn_methx=magn_methx;
5228 mng_info->magn_methy=magn_methy;
5229 }
5230 }
glennrp47b9dd52010-11-24 18:12:06 +00005231
cristy3ed852e2009-09-05 21:47:34 +00005232 if (memcmp(type,mng_PAST,4) == 0)
5233 {
5234 if (mng_info->past_warning == 0)
5235 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5236 CoderError,"PAST is not implemented yet","`%s'",
5237 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005238
cristy3ed852e2009-09-05 21:47:34 +00005239 mng_info->past_warning++;
5240 }
glennrp47b9dd52010-11-24 18:12:06 +00005241
cristy3ed852e2009-09-05 21:47:34 +00005242 if (memcmp(type,mng_SHOW,4) == 0)
5243 {
5244 if (mng_info->show_warning == 0)
5245 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5246 CoderError,"SHOW is not implemented yet","`%s'",
5247 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005248
cristy3ed852e2009-09-05 21:47:34 +00005249 mng_info->show_warning++;
5250 }
glennrp47b9dd52010-11-24 18:12:06 +00005251
cristy3ed852e2009-09-05 21:47:34 +00005252 if (memcmp(type,mng_sBIT,4) == 0)
5253 {
5254 if (length < 4)
5255 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005256
cristy3ed852e2009-09-05 21:47:34 +00005257 else
5258 {
5259 mng_info->global_sbit.gray=p[0];
5260 mng_info->global_sbit.red=p[0];
5261 mng_info->global_sbit.green=p[1];
5262 mng_info->global_sbit.blue=p[2];
5263 mng_info->global_sbit.alpha=p[3];
5264 mng_info->have_global_sbit=MagickTrue;
5265 }
5266 }
5267 if (memcmp(type,mng_pHYs,4) == 0)
5268 {
5269 if (length > 8)
5270 {
5271 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005272 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005273 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005274 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005275 mng_info->global_phys_unit_type=p[8];
5276 mng_info->have_global_phys=MagickTrue;
5277 }
glennrp47b9dd52010-11-24 18:12:06 +00005278
cristy3ed852e2009-09-05 21:47:34 +00005279 else
5280 mng_info->have_global_phys=MagickFalse;
5281 }
5282 if (memcmp(type,mng_pHYg,4) == 0)
5283 {
5284 if (mng_info->phyg_warning == 0)
5285 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5286 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005287
cristy3ed852e2009-09-05 21:47:34 +00005288 mng_info->phyg_warning++;
5289 }
5290 if (memcmp(type,mng_BASI,4) == 0)
5291 {
5292 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005293
cristy3ed852e2009-09-05 21:47:34 +00005294 if (mng_info->basi_warning == 0)
5295 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5296 CoderError,"BASI is not implemented yet","`%s'",
5297 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005298
cristy3ed852e2009-09-05 21:47:34 +00005299 mng_info->basi_warning++;
5300#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005301 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005302 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005303 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005304 (p[6] << 8) | p[7]);
5305 basi_color_type=p[8];
5306 basi_compression_method=p[9];
5307 basi_filter_type=p[10];
5308 basi_interlace_method=p[11];
5309 if (length > 11)
5310 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005311
cristy3ed852e2009-09-05 21:47:34 +00005312 else
5313 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005314
cristy3ed852e2009-09-05 21:47:34 +00005315 if (length > 13)
5316 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005317
cristy3ed852e2009-09-05 21:47:34 +00005318 else
5319 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005320
cristy3ed852e2009-09-05 21:47:34 +00005321 if (length > 15)
5322 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005323
cristy3ed852e2009-09-05 21:47:34 +00005324 else
5325 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005326
cristy3ed852e2009-09-05 21:47:34 +00005327 if (length > 17)
5328 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 else
5331 {
5332 if (basi_sample_depth == 16)
5333 basi_alpha=65535L;
5334 else
5335 basi_alpha=255;
5336 }
glennrp47b9dd52010-11-24 18:12:06 +00005337
cristy3ed852e2009-09-05 21:47:34 +00005338 if (length > 19)
5339 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005340
cristy3ed852e2009-09-05 21:47:34 +00005341 else
5342 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005343
cristy3ed852e2009-09-05 21:47:34 +00005344#endif
5345 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5346 continue;
5347 }
glennrp47b9dd52010-11-24 18:12:06 +00005348
cristy3ed852e2009-09-05 21:47:34 +00005349 if (memcmp(type,mng_IHDR,4)
5350#if defined(JNG_SUPPORTED)
5351 && memcmp(type,mng_JHDR,4)
5352#endif
5353 )
5354 {
5355 /* Not an IHDR or JHDR chunk */
5356 if (length)
5357 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005358
cristy3ed852e2009-09-05 21:47:34 +00005359 continue;
5360 }
5361/* Process IHDR */
5362 if (logging != MagickFalse)
5363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5364 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005365
cristy3ed852e2009-09-05 21:47:34 +00005366 mng_info->exists[object_id]=MagickTrue;
5367 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005368
cristy3ed852e2009-09-05 21:47:34 +00005369 if (mng_info->invisible[object_id])
5370 {
5371 if (logging != MagickFalse)
5372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5373 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005374
cristy3ed852e2009-09-05 21:47:34 +00005375 skip_to_iend=MagickTrue;
5376 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5377 continue;
5378 }
5379#if defined(MNG_INSERT_LAYERS)
5380 if (length < 8)
5381 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005382
cristy8182b072010-05-30 20:10:53 +00005383 image_width=(size_t) mng_get_long(p);
5384 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005385#endif
5386 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5387
5388 /*
5389 Insert a transparent background layer behind the entire animation
5390 if it is not full screen.
5391 */
5392#if defined(MNG_INSERT_LAYERS)
5393 if (insert_layers && mng_type && first_mng_object)
5394 {
5395 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5396 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005397 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005398 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005399 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005400 {
5401 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5402 {
5403 /*
5404 Allocate next image structure.
5405 */
5406 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005407
cristy3ed852e2009-09-05 21:47:34 +00005408 if (GetNextImageInList(image) == (Image *) NULL)
5409 {
5410 image=DestroyImageList(image);
5411 MngInfoFreeStruct(mng_info,&have_mng_structure);
5412 return((Image *) NULL);
5413 }
glennrp47b9dd52010-11-24 18:12:06 +00005414
cristy3ed852e2009-09-05 21:47:34 +00005415 image=SyncNextImageInList(image);
5416 }
5417 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005418
cristy3ed852e2009-09-05 21:47:34 +00005419 if (term_chunk_found)
5420 {
5421 image->start_loop=MagickTrue;
5422 image->iterations=mng_iterations;
5423 term_chunk_found=MagickFalse;
5424 }
glennrp47b9dd52010-11-24 18:12:06 +00005425
cristy3ed852e2009-09-05 21:47:34 +00005426 else
5427 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005428
5429 /* Make a background rectangle. */
5430
cristy3ed852e2009-09-05 21:47:34 +00005431 image->delay=0;
5432 image->columns=mng_info->mng_width;
5433 image->rows=mng_info->mng_height;
5434 image->page.width=mng_info->mng_width;
5435 image->page.height=mng_info->mng_height;
5436 image->page.x=0;
5437 image->page.y=0;
5438 image->background_color=mng_background_color;
5439 (void) SetImageBackgroundColor(image);
5440 if (logging != MagickFalse)
5441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005442 " Inserted transparent background layer, W=%.20g, H=%.20g",
5443 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005444 }
5445 }
5446 /*
5447 Insert a background layer behind the upcoming image if
5448 framing_mode is 3, and we haven't already inserted one.
5449 */
5450 if (insert_layers && (mng_info->framing_mode == 3) &&
5451 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5452 (simplicity & 0x08)))
5453 {
5454 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5455 {
5456 /*
5457 Allocate next image structure.
5458 */
5459 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005460
cristy3ed852e2009-09-05 21:47:34 +00005461 if (GetNextImageInList(image) == (Image *) NULL)
5462 {
5463 image=DestroyImageList(image);
5464 MngInfoFreeStruct(mng_info,&have_mng_structure);
5465 return((Image *) NULL);
5466 }
glennrp47b9dd52010-11-24 18:12:06 +00005467
cristy3ed852e2009-09-05 21:47:34 +00005468 image=SyncNextImageInList(image);
5469 }
glennrp0fe50b42010-11-16 03:52:51 +00005470
cristy3ed852e2009-09-05 21:47:34 +00005471 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005472
cristy3ed852e2009-09-05 21:47:34 +00005473 if (term_chunk_found)
5474 {
5475 image->start_loop=MagickTrue;
5476 image->iterations=mng_iterations;
5477 term_chunk_found=MagickFalse;
5478 }
glennrp0fe50b42010-11-16 03:52:51 +00005479
cristy3ed852e2009-09-05 21:47:34 +00005480 else
5481 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005482
cristy3ed852e2009-09-05 21:47:34 +00005483 image->delay=0;
5484 image->columns=subframe_width;
5485 image->rows=subframe_height;
5486 image->page.width=subframe_width;
5487 image->page.height=subframe_height;
5488 image->page.x=mng_info->clip.left;
5489 image->page.y=mng_info->clip.top;
5490 image->background_color=mng_background_color;
5491 image->matte=MagickFalse;
5492 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005493
cristy3ed852e2009-09-05 21:47:34 +00005494 if (logging != MagickFalse)
5495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005496 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005497 (double) mng_info->clip.left,(double) mng_info->clip.right,
5498 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005499 }
5500#endif /* MNG_INSERT_LAYERS */
5501 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005502
cristy3ed852e2009-09-05 21:47:34 +00005503 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5504 {
5505 /*
5506 Allocate next image structure.
5507 */
5508 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005509
cristy3ed852e2009-09-05 21:47:34 +00005510 if (GetNextImageInList(image) == (Image *) NULL)
5511 {
5512 image=DestroyImageList(image);
5513 MngInfoFreeStruct(mng_info,&have_mng_structure);
5514 return((Image *) NULL);
5515 }
glennrp47b9dd52010-11-24 18:12:06 +00005516
cristy3ed852e2009-09-05 21:47:34 +00005517 image=SyncNextImageInList(image);
5518 }
5519 mng_info->image=image;
5520 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5521 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005522
cristy3ed852e2009-09-05 21:47:34 +00005523 if (status == MagickFalse)
5524 break;
glennrp0fe50b42010-11-16 03:52:51 +00005525
cristy3ed852e2009-09-05 21:47:34 +00005526 if (term_chunk_found)
5527 {
5528 image->start_loop=MagickTrue;
5529 term_chunk_found=MagickFalse;
5530 }
glennrp0fe50b42010-11-16 03:52:51 +00005531
cristy3ed852e2009-09-05 21:47:34 +00005532 else
5533 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005534
cristy3ed852e2009-09-05 21:47:34 +00005535 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5536 {
5537 image->delay=frame_delay;
5538 frame_delay=default_frame_delay;
5539 }
glennrp0fe50b42010-11-16 03:52:51 +00005540
cristy3ed852e2009-09-05 21:47:34 +00005541 else
5542 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005543
cristy3ed852e2009-09-05 21:47:34 +00005544 image->page.width=mng_info->mng_width;
5545 image->page.height=mng_info->mng_height;
5546 image->page.x=mng_info->x_off[object_id];
5547 image->page.y=mng_info->y_off[object_id];
5548 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00005549
cristy3ed852e2009-09-05 21:47:34 +00005550 /*
5551 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5552 */
glennrp47b9dd52010-11-24 18:12:06 +00005553
cristy3ed852e2009-09-05 21:47:34 +00005554 if (logging != MagickFalse)
5555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5556 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5557 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005558
cristybb503372010-05-27 20:51:26 +00005559 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00005560
cristy3ed852e2009-09-05 21:47:34 +00005561 if (offset < 0)
5562 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5563 }
5564
5565 previous=image;
5566 mng_info->image=image;
5567 mng_info->mng_type=mng_type;
5568 mng_info->object_id=object_id;
5569
5570 if (memcmp(type,mng_IHDR,4) == 0)
5571 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005572
cristy3ed852e2009-09-05 21:47:34 +00005573#if defined(JNG_SUPPORTED)
5574 else
5575 image=ReadOneJNGImage(mng_info,image_info,exception);
5576#endif
5577
5578 if (image == (Image *) NULL)
5579 {
5580 if (IsImageObject(previous) != MagickFalse)
5581 {
5582 (void) DestroyImageList(previous);
5583 (void) CloseBlob(previous);
5584 }
glennrp47b9dd52010-11-24 18:12:06 +00005585
cristy3ed852e2009-09-05 21:47:34 +00005586 MngInfoFreeStruct(mng_info,&have_mng_structure);
5587 return((Image *) NULL);
5588 }
glennrp0fe50b42010-11-16 03:52:51 +00005589
cristy3ed852e2009-09-05 21:47:34 +00005590 if (image->columns == 0 || image->rows == 0)
5591 {
5592 (void) CloseBlob(image);
5593 image=DestroyImageList(image);
5594 MngInfoFreeStruct(mng_info,&have_mng_structure);
5595 return((Image *) NULL);
5596 }
glennrp0fe50b42010-11-16 03:52:51 +00005597
cristy3ed852e2009-09-05 21:47:34 +00005598 mng_info->image=image;
5599
5600 if (mng_type)
5601 {
5602 MngBox
5603 crop_box;
5604
5605 if (mng_info->magn_methx || mng_info->magn_methy)
5606 {
5607 png_uint_32
5608 magnified_height,
5609 magnified_width;
5610
5611 if (logging != MagickFalse)
5612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5613 " Processing MNG MAGN chunk");
5614
5615 if (mng_info->magn_methx == 1)
5616 {
5617 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 if (image->columns > 1)
5620 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005621
cristy3ed852e2009-09-05 21:47:34 +00005622 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005623 magnified_width += (png_uint_32)
5624 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00005625 }
glennrp47b9dd52010-11-24 18:12:06 +00005626
cristy3ed852e2009-09-05 21:47:34 +00005627 else
5628 {
cristy4e5bc842010-06-09 13:56:01 +00005629 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00005630
cristy3ed852e2009-09-05 21:47:34 +00005631 if (image->columns > 1)
5632 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00005633
cristy3ed852e2009-09-05 21:47:34 +00005634 if (image->columns > 2)
5635 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00005636
cristy3ed852e2009-09-05 21:47:34 +00005637 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005638 magnified_width += (png_uint_32)
5639 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00005640 }
glennrp47b9dd52010-11-24 18:12:06 +00005641
cristy3ed852e2009-09-05 21:47:34 +00005642 if (mng_info->magn_methy == 1)
5643 {
5644 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005645
cristy3ed852e2009-09-05 21:47:34 +00005646 if (image->rows > 1)
5647 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005648
cristy3ed852e2009-09-05 21:47:34 +00005649 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005650 magnified_height += (png_uint_32)
5651 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00005652 }
glennrp47b9dd52010-11-24 18:12:06 +00005653
cristy3ed852e2009-09-05 21:47:34 +00005654 else
5655 {
cristy4e5bc842010-06-09 13:56:01 +00005656 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00005657
cristy3ed852e2009-09-05 21:47:34 +00005658 if (image->rows > 1)
5659 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00005660
cristy3ed852e2009-09-05 21:47:34 +00005661 if (image->rows > 2)
5662 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005665 magnified_height += (png_uint_32)
5666 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00005667 }
glennrp47b9dd52010-11-24 18:12:06 +00005668
cristy3ed852e2009-09-05 21:47:34 +00005669 if (magnified_height > image->rows ||
5670 magnified_width > image->columns)
5671 {
5672 Image
5673 *large_image;
5674
5675 int
5676 yy;
5677
cristybb503372010-05-27 20:51:26 +00005678 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005679 m,
5680 y;
5681
cristybb503372010-05-27 20:51:26 +00005682 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005683 x;
5684
5685 register PixelPacket
5686 *n,
5687 *q;
5688
5689 PixelPacket
5690 *next,
5691 *prev;
5692
5693 png_uint_16
5694 magn_methx,
5695 magn_methy;
5696
glennrp47b9dd52010-11-24 18:12:06 +00005697 /* Allocate next image structure. */
5698
cristy3ed852e2009-09-05 21:47:34 +00005699 if (logging != MagickFalse)
5700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5701 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00005702
cristy3ed852e2009-09-05 21:47:34 +00005703 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005704
cristy3ed852e2009-09-05 21:47:34 +00005705 if (GetNextImageInList(image) == (Image *) NULL)
5706 {
5707 image=DestroyImageList(image);
5708 MngInfoFreeStruct(mng_info,&have_mng_structure);
5709 return((Image *) NULL);
5710 }
5711
5712 large_image=SyncNextImageInList(image);
5713
5714 large_image->columns=magnified_width;
5715 large_image->rows=magnified_height;
5716
5717 magn_methx=mng_info->magn_methx;
5718 magn_methy=mng_info->magn_methy;
5719
glennrp3faa9a32011-04-23 14:00:25 +00005720#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00005721#define QM unsigned short
5722 if (magn_methx != 1 || magn_methy != 1)
5723 {
5724 /*
5725 Scale pixels to unsigned shorts to prevent
5726 overflow of intermediate values of interpolations
5727 */
cristybb503372010-05-27 20:51:26 +00005728 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005729 {
5730 q=GetAuthenticPixels(image,0,y,image->columns,1,
5731 exception);
glennrp47b9dd52010-11-24 18:12:06 +00005732
cristybb503372010-05-27 20:51:26 +00005733 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005734 {
5735 q->red=ScaleQuantumToShort(q->red);
5736 q->green=ScaleQuantumToShort(q->green);
5737 q->blue=ScaleQuantumToShort(q->blue);
5738 q->opacity=ScaleQuantumToShort(q->opacity);
5739 q++;
5740 }
glennrp47b9dd52010-11-24 18:12:06 +00005741
cristy3ed852e2009-09-05 21:47:34 +00005742 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5743 break;
5744 }
5745 }
5746#else
5747#define QM Quantum
5748#endif
5749
5750 if (image->matte != MagickFalse)
5751 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005752
cristy3ed852e2009-09-05 21:47:34 +00005753 else
5754 {
5755 large_image->background_color.opacity=OpaqueOpacity;
5756 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005757
cristy3ed852e2009-09-05 21:47:34 +00005758 if (magn_methx == 4)
5759 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00005760
cristy3ed852e2009-09-05 21:47:34 +00005761 if (magn_methx == 5)
5762 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00005763
cristy3ed852e2009-09-05 21:47:34 +00005764 if (magn_methy == 4)
5765 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00005766
cristy3ed852e2009-09-05 21:47:34 +00005767 if (magn_methy == 5)
5768 magn_methy=3;
5769 }
5770
5771 /* magnify the rows into the right side of the large image */
5772
5773 if (logging != MagickFalse)
5774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005775 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00005776 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005777 yy=0;
5778 length=(size_t) image->columns;
5779 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5780 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00005781
cristy3ed852e2009-09-05 21:47:34 +00005782 if ((prev == (PixelPacket *) NULL) ||
5783 (next == (PixelPacket *) NULL))
5784 {
5785 image=DestroyImageList(image);
5786 MngInfoFreeStruct(mng_info,&have_mng_structure);
5787 ThrowReaderException(ResourceLimitError,
5788 "MemoryAllocationFailed");
5789 }
glennrp47b9dd52010-11-24 18:12:06 +00005790
cristy3ed852e2009-09-05 21:47:34 +00005791 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5792 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00005793
cristybb503372010-05-27 20:51:26 +00005794 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005795 {
5796 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005797 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristybb503372010-05-27 20:51:26 +00005799 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5800 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristybb503372010-05-27 20:51:26 +00005802 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5803 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristybb503372010-05-27 20:51:26 +00005805 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005806 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 else
cristybb503372010-05-27 20:51:26 +00005809 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005810
cristy3ed852e2009-09-05 21:47:34 +00005811 n=prev;
5812 prev=next;
5813 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00005814
cristybb503372010-05-27 20:51:26 +00005815 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005816 {
5817 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5818 exception);
5819 (void) CopyMagickMemory(next,n,length);
5820 }
glennrp47b9dd52010-11-24 18:12:06 +00005821
cristy3ed852e2009-09-05 21:47:34 +00005822 for (i=0; i < m; i++, yy++)
5823 {
5824 register PixelPacket
5825 *pixels;
5826
cristybb503372010-05-27 20:51:26 +00005827 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005828 pixels=prev;
5829 n=next;
5830 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5831 1,exception);
5832 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00005833
cristybb503372010-05-27 20:51:26 +00005834 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005835 {
glennrpfd05d622011-02-25 04:10:33 +00005836 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005837 /*
5838 if (image->storage_class == PseudoClass)
5839 {
5840 }
5841 */
5842
5843 if (magn_methy <= 1)
5844 {
5845 *q=(*pixels); /* replicate previous */
5846 }
glennrp47b9dd52010-11-24 18:12:06 +00005847
cristy3ed852e2009-09-05 21:47:34 +00005848 else if (magn_methy == 2 || magn_methy == 4)
5849 {
5850 if (i == 0)
5851 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005852
cristy3ed852e2009-09-05 21:47:34 +00005853 else
5854 {
5855 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005856 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5857 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005858 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005859 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5860 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005861 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005862 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5863 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005864 +(*pixels).blue);
glennrp47b9dd52010-11-24 18:12:06 +00005865
cristy3ed852e2009-09-05 21:47:34 +00005866 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005867 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005868 (2*i*((*n).opacity
5869 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005870 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005871 }
glennrp47b9dd52010-11-24 18:12:06 +00005872
cristy3ed852e2009-09-05 21:47:34 +00005873 if (magn_methy == 4)
5874 {
5875 /* Replicate nearest */
5876 if (i <= ((m+1) << 1))
5877 (*q).opacity=(*pixels).opacity+0;
5878 else
5879 (*q).opacity=(*n).opacity+0;
5880 }
5881 }
glennrp47b9dd52010-11-24 18:12:06 +00005882
cristy3ed852e2009-09-05 21:47:34 +00005883 else /* if (magn_methy == 3 || magn_methy == 5) */
5884 {
5885 /* Replicate nearest */
5886 if (i <= ((m+1) << 1))
5887 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005888
cristy3ed852e2009-09-05 21:47:34 +00005889 else
5890 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00005891
cristy3ed852e2009-09-05 21:47:34 +00005892 if (magn_methy == 5)
5893 {
cristybb503372010-05-27 20:51:26 +00005894 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5895 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005896 +(*pixels).opacity);
5897 }
5898 }
5899 n++;
5900 q++;
5901 pixels++;
5902 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00005903
cristy3ed852e2009-09-05 21:47:34 +00005904 if (SyncAuthenticPixels(large_image,exception) == 0)
5905 break;
glennrp47b9dd52010-11-24 18:12:06 +00005906
cristy3ed852e2009-09-05 21:47:34 +00005907 } /* i */
5908 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00005909
cristy3ed852e2009-09-05 21:47:34 +00005910 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5911 next=(PixelPacket *) RelinquishMagickMemory(next);
5912
5913 length=image->columns;
5914
5915 if (logging != MagickFalse)
5916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5917 " Delete original image");
5918
5919 DeleteImageFromList(&image);
5920
5921 image=large_image;
5922
5923 mng_info->image=image;
5924
5925 /* magnify the columns */
5926 if (logging != MagickFalse)
5927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005928 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00005929
cristybb503372010-05-27 20:51:26 +00005930 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005931 {
5932 register PixelPacket
5933 *pixels;
5934
5935 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5936 pixels=q+(image->columns-length);
5937 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00005938
cristybb503372010-05-27 20:51:26 +00005939 for (x=(ssize_t) (image->columns-length);
5940 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005941 {
cristybb503372010-05-27 20:51:26 +00005942 if (x == (ssize_t) (image->columns-length))
5943 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005944
cristybb503372010-05-27 20:51:26 +00005945 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5946 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005947
cristybb503372010-05-27 20:51:26 +00005948 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5949 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005950
cristybb503372010-05-27 20:51:26 +00005951 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00005952 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005953
cristy3ed852e2009-09-05 21:47:34 +00005954 else
cristybb503372010-05-27 20:51:26 +00005955 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005956
cristy3ed852e2009-09-05 21:47:34 +00005957 for (i=0; i < m; i++)
5958 {
5959 if (magn_methx <= 1)
5960 {
5961 /* replicate previous */
5962 *q=(*pixels);
5963 }
glennrp47b9dd52010-11-24 18:12:06 +00005964
cristy3ed852e2009-09-05 21:47:34 +00005965 else if (magn_methx == 2 || magn_methx == 4)
5966 {
5967 if (i == 0)
5968 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005969
cristy3ed852e2009-09-05 21:47:34 +00005970 else
5971 {
5972 /* Interpolate */
5973 (*q).red=(QM) ((2*i*((*n).red
5974 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00005975 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00005976 (*q).green=(QM) ((2*i*((*n).green
5977 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00005978 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00005979 (*q).blue=(QM) ((2*i*((*n).blue
5980 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00005981 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00005982 if (image->matte != MagickFalse)
5983 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00005984 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005985 +(*pixels).opacity);
5986 }
glennrp47b9dd52010-11-24 18:12:06 +00005987
cristy3ed852e2009-09-05 21:47:34 +00005988 if (magn_methx == 4)
5989 {
5990 /* Replicate nearest */
5991 if (i <= ((m+1) << 1))
5992 (*q).opacity=(*pixels).opacity+0;
5993 else
5994 (*q).opacity=(*n).opacity+0;
5995 }
5996 }
glennrp47b9dd52010-11-24 18:12:06 +00005997
cristy3ed852e2009-09-05 21:47:34 +00005998 else /* if (magn_methx == 3 || magn_methx == 5) */
5999 {
6000 /* Replicate nearest */
6001 if (i <= ((m+1) << 1))
6002 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006003
cristy3ed852e2009-09-05 21:47:34 +00006004 else
6005 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00006006
cristy3ed852e2009-09-05 21:47:34 +00006007 if (magn_methx == 5)
6008 {
6009 /* Interpolate */
6010 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006011 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006012 +(*pixels).opacity);
6013 }
6014 }
6015 q++;
6016 }
6017 n++;
6018 p++;
6019 }
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristy3ed852e2009-09-05 21:47:34 +00006021 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6022 break;
6023 }
glennrp3faa9a32011-04-23 14:00:25 +00006024#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006025 if (magn_methx != 1 || magn_methy != 1)
6026 {
6027 /*
6028 Rescale pixels to Quantum
6029 */
cristybb503372010-05-27 20:51:26 +00006030 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006031 {
6032 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006033
cristybb503372010-05-27 20:51:26 +00006034 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006035 {
6036 q->red=ScaleShortToQuantum(q->red);
6037 q->green=ScaleShortToQuantum(q->green);
6038 q->blue=ScaleShortToQuantum(q->blue);
6039 q->opacity=ScaleShortToQuantum(q->opacity);
6040 q++;
6041 }
glennrp47b9dd52010-11-24 18:12:06 +00006042
cristy3ed852e2009-09-05 21:47:34 +00006043 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6044 break;
6045 }
6046 }
6047#endif
6048 if (logging != MagickFalse)
6049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6050 " Finished MAGN processing");
6051 }
6052 }
6053
6054 /*
6055 Crop_box is with respect to the upper left corner of the MNG.
6056 */
6057 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6058 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6059 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6060 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6061 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6062 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6063 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6064 if ((crop_box.left != (mng_info->image_box.left
6065 +mng_info->x_off[object_id])) ||
6066 (crop_box.right != (mng_info->image_box.right
6067 +mng_info->x_off[object_id])) ||
6068 (crop_box.top != (mng_info->image_box.top
6069 +mng_info->y_off[object_id])) ||
6070 (crop_box.bottom != (mng_info->image_box.bottom
6071 +mng_info->y_off[object_id])))
6072 {
6073 if (logging != MagickFalse)
6074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6075 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006076
cristy3ed852e2009-09-05 21:47:34 +00006077 if ((crop_box.left < crop_box.right) &&
6078 (crop_box.top < crop_box.bottom))
6079 {
6080 Image
6081 *im;
6082
6083 RectangleInfo
6084 crop_info;
6085
6086 /*
6087 Crop_info is with respect to the upper left corner of
6088 the image.
6089 */
6090 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6091 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006092 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6093 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006094 image->page.width=image->columns;
6095 image->page.height=image->rows;
6096 image->page.x=0;
6097 image->page.y=0;
6098 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006099
cristy3ed852e2009-09-05 21:47:34 +00006100 if (im != (Image *) NULL)
6101 {
6102 image->columns=im->columns;
6103 image->rows=im->rows;
6104 im=DestroyImage(im);
6105 image->page.width=image->columns;
6106 image->page.height=image->rows;
6107 image->page.x=crop_box.left;
6108 image->page.y=crop_box.top;
6109 }
6110 }
glennrp47b9dd52010-11-24 18:12:06 +00006111
cristy3ed852e2009-09-05 21:47:34 +00006112 else
6113 {
6114 /*
6115 No pixels in crop area. The MNG spec still requires
6116 a layer, though, so make a single transparent pixel in
6117 the top left corner.
6118 */
6119 image->columns=1;
6120 image->rows=1;
6121 image->colors=2;
6122 (void) SetImageBackgroundColor(image);
6123 image->page.width=1;
6124 image->page.height=1;
6125 image->page.x=0;
6126 image->page.y=0;
6127 }
6128 }
6129#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6130 image=mng_info->image;
6131#endif
6132 }
6133
glennrp2b013e42010-11-24 16:55:50 +00006134#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6135 /* PNG does not handle depths greater than 16 so reduce it even
6136 * if lossy
6137 */
6138 if (image->depth > 16)
6139 image->depth=16;
6140#endif
6141
glennrp3faa9a32011-04-23 14:00:25 +00006142#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006143 if (LosslessReduceDepthOK(image) != MagickFalse)
6144 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006145#endif
glennrpd6afd542010-11-19 01:53:05 +00006146
cristy3ed852e2009-09-05 21:47:34 +00006147 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006148
cristy3ed852e2009-09-05 21:47:34 +00006149 if (image_info->number_scenes != 0)
6150 {
6151 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006152 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006153 break;
6154 }
glennrpd6afd542010-11-19 01:53:05 +00006155
cristy3ed852e2009-09-05 21:47:34 +00006156 if (logging != MagickFalse)
6157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6158 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006159
cristy3ed852e2009-09-05 21:47:34 +00006160 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006161
cristy3ed852e2009-09-05 21:47:34 +00006162 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006163
cristy3ed852e2009-09-05 21:47:34 +00006164 if (logging != MagickFalse)
6165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6166 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006167
cristy3ed852e2009-09-05 21:47:34 +00006168#if defined(MNG_INSERT_LAYERS)
6169 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6170 (mng_info->mng_height))
6171 {
6172 /*
6173 Insert a background layer if nothing else was found.
6174 */
6175 if (logging != MagickFalse)
6176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6177 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006178
cristy3ed852e2009-09-05 21:47:34 +00006179 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6180 {
6181 /*
6182 Allocate next image structure.
6183 */
6184 AcquireNextImage(image_info,image);
6185 if (GetNextImageInList(image) == (Image *) NULL)
6186 {
6187 image=DestroyImageList(image);
6188 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006189
cristy3ed852e2009-09-05 21:47:34 +00006190 if (logging != MagickFalse)
6191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6192 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006193
cristy3ed852e2009-09-05 21:47:34 +00006194 return((Image *) NULL);
6195 }
6196 image=SyncNextImageInList(image);
6197 }
6198 image->columns=mng_info->mng_width;
6199 image->rows=mng_info->mng_height;
6200 image->page.width=mng_info->mng_width;
6201 image->page.height=mng_info->mng_height;
6202 image->page.x=0;
6203 image->page.y=0;
6204 image->background_color=mng_background_color;
6205 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006206
cristy3ed852e2009-09-05 21:47:34 +00006207 if (image_info->ping == MagickFalse)
6208 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006209
cristy3ed852e2009-09-05 21:47:34 +00006210 mng_info->image_found++;
6211 }
6212#endif
6213 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006214
cristy3ed852e2009-09-05 21:47:34 +00006215 if (mng_iterations == 1)
6216 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006217
cristy3ed852e2009-09-05 21:47:34 +00006218 while (GetPreviousImageInList(image) != (Image *) NULL)
6219 {
6220 image_count++;
6221 if (image_count > 10*mng_info->image_found)
6222 {
6223 if (logging != MagickFalse)
6224 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006225
cristy3ed852e2009-09-05 21:47:34 +00006226 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6227 CoderError,"Linked list is corrupted, beginning of list not found",
6228 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006229
cristy3ed852e2009-09-05 21:47:34 +00006230 return((Image *) NULL);
6231 }
glennrp0fe50b42010-11-16 03:52:51 +00006232
cristy3ed852e2009-09-05 21:47:34 +00006233 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 if (GetNextImageInList(image) == (Image *) NULL)
6236 {
6237 if (logging != MagickFalse)
6238 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006239
cristy3ed852e2009-09-05 21:47:34 +00006240 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6241 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6242 image_info->filename);
6243 }
6244 }
glennrp47b9dd52010-11-24 18:12:06 +00006245
cristy3ed852e2009-09-05 21:47:34 +00006246 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6247 GetNextImageInList(image) ==
6248 (Image *) NULL)
6249 {
6250 if (logging != MagickFalse)
6251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6252 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006253
cristy3ed852e2009-09-05 21:47:34 +00006254 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6255 CoderError,"image->next for first image is NULL but shouldn't be.",
6256 "`%s'",image_info->filename);
6257 }
glennrp47b9dd52010-11-24 18:12:06 +00006258
cristy3ed852e2009-09-05 21:47:34 +00006259 if (mng_info->image_found == 0)
6260 {
6261 if (logging != MagickFalse)
6262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6263 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006264
cristy3ed852e2009-09-05 21:47:34 +00006265 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6266 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006267
cristy3ed852e2009-09-05 21:47:34 +00006268 if (image != (Image *) NULL)
6269 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006270
cristy3ed852e2009-09-05 21:47:34 +00006271 MngInfoFreeStruct(mng_info,&have_mng_structure);
6272 return((Image *) NULL);
6273 }
6274
6275 if (mng_info->ticks_per_second)
6276 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6277 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006278
cristy3ed852e2009-09-05 21:47:34 +00006279 else
6280 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006281
cristy3ed852e2009-09-05 21:47:34 +00006282 /* Find final nonzero image delay */
6283 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006284
cristy3ed852e2009-09-05 21:47:34 +00006285 while (GetNextImageInList(image) != (Image *) NULL)
6286 {
6287 if (image->delay)
6288 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006289
cristy3ed852e2009-09-05 21:47:34 +00006290 image=GetNextImageInList(image);
6291 }
glennrp0fe50b42010-11-16 03:52:51 +00006292
cristy3ed852e2009-09-05 21:47:34 +00006293 if (final_delay < final_image_delay)
6294 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006295
cristy3ed852e2009-09-05 21:47:34 +00006296 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006297
cristy3ed852e2009-09-05 21:47:34 +00006298 if (logging != MagickFalse)
6299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006300 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6301 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006302
cristy3ed852e2009-09-05 21:47:34 +00006303 if (logging != MagickFalse)
6304 {
6305 int
6306 scene;
6307
6308 scene=0;
6309 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006310
cristy3ed852e2009-09-05 21:47:34 +00006311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6312 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006313
cristy3ed852e2009-09-05 21:47:34 +00006314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006315 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006316
cristy3ed852e2009-09-05 21:47:34 +00006317 while (GetNextImageInList(image) != (Image *) NULL)
6318 {
6319 image=GetNextImageInList(image);
6320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006321 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006322 }
6323 }
6324
6325 image=GetFirstImageInList(image);
6326#ifdef MNG_COALESCE_LAYERS
6327 if (insert_layers)
6328 {
6329 Image
6330 *next_image,
6331 *next;
6332
cristybb503372010-05-27 20:51:26 +00006333 size_t
cristy3ed852e2009-09-05 21:47:34 +00006334 scene;
6335
6336 if (logging != MagickFalse)
6337 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006338
cristy3ed852e2009-09-05 21:47:34 +00006339 scene=image->scene;
6340 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006341
cristy3ed852e2009-09-05 21:47:34 +00006342 if (next_image == (Image *) NULL)
6343 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006344
cristy3ed852e2009-09-05 21:47:34 +00006345 image=DestroyImageList(image);
6346 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006347
cristy3ed852e2009-09-05 21:47:34 +00006348 for (next=image; next != (Image *) NULL; next=next_image)
6349 {
6350 next->page.width=mng_info->mng_width;
6351 next->page.height=mng_info->mng_height;
6352 next->page.x=0;
6353 next->page.y=0;
6354 next->scene=scene++;
6355 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006356
cristy3ed852e2009-09-05 21:47:34 +00006357 if (next_image == (Image *) NULL)
6358 break;
glennrp47b9dd52010-11-24 18:12:06 +00006359
cristy3ed852e2009-09-05 21:47:34 +00006360 if (next->delay == 0)
6361 {
6362 scene--;
6363 next_image->previous=GetPreviousImageInList(next);
6364 if (GetPreviousImageInList(next) == (Image *) NULL)
6365 image=next_image;
6366 else
6367 next->previous->next=next_image;
6368 next=DestroyImage(next);
6369 }
6370 }
6371 }
6372#endif
6373
6374 while (GetNextImageInList(image) != (Image *) NULL)
6375 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006376
cristy3ed852e2009-09-05 21:47:34 +00006377 image->dispose=BackgroundDispose;
6378
6379 if (logging != MagickFalse)
6380 {
6381 int
6382 scene;
6383
6384 scene=0;
6385 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006386
cristy3ed852e2009-09-05 21:47:34 +00006387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6388 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006389
cristy3ed852e2009-09-05 21:47:34 +00006390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006391 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6392 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006393
cristy3ed852e2009-09-05 21:47:34 +00006394 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006395 {
6396 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006397
cristyf2faecf2010-05-28 19:19:36 +00006398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006399 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6400 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006401 }
6402 }
glennrp47b9dd52010-11-24 18:12:06 +00006403
cristy3ed852e2009-09-05 21:47:34 +00006404 image=GetFirstImageInList(image);
6405 MngInfoFreeStruct(mng_info,&have_mng_structure);
6406 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006407
cristy3ed852e2009-09-05 21:47:34 +00006408 if (logging != MagickFalse)
6409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006410
cristy3ed852e2009-09-05 21:47:34 +00006411 return(GetFirstImageInList(image));
6412}
glennrp25c1e2b2010-03-25 01:39:56 +00006413#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006414static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6415{
6416 printf("Your PNG library is too old: You have libpng-%s\n",
6417 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006418
cristy3ed852e2009-09-05 21:47:34 +00006419 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6420 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006421
cristy3ed852e2009-09-05 21:47:34 +00006422 return(Image *) NULL;
6423}
glennrp47b9dd52010-11-24 18:12:06 +00006424
cristy3ed852e2009-09-05 21:47:34 +00006425static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6426{
6427 return(ReadPNGImage(image_info,exception));
6428}
glennrp25c1e2b2010-03-25 01:39:56 +00006429#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006430#endif
6431
6432/*
6433%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6434% %
6435% %
6436% %
6437% R e g i s t e r P N G I m a g e %
6438% %
6439% %
6440% %
6441%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6442%
6443% RegisterPNGImage() adds properties for the PNG image format to
6444% the list of supported formats. The properties include the image format
6445% tag, a method to read and/or write the format, whether the format
6446% supports the saving of more than one frame to the same file or blob,
6447% whether the format supports native in-memory I/O, and a brief
6448% description of the format.
6449%
6450% The format of the RegisterPNGImage method is:
6451%
cristybb503372010-05-27 20:51:26 +00006452% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006453%
6454*/
cristybb503372010-05-27 20:51:26 +00006455ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006456{
6457 char
6458 version[MaxTextExtent];
6459
6460 MagickInfo
6461 *entry;
6462
6463 static const char
6464 *PNGNote=
6465 {
6466 "See http://www.libpng.org/ for details about the PNG format."
6467 },
glennrp47b9dd52010-11-24 18:12:06 +00006468
cristy3ed852e2009-09-05 21:47:34 +00006469 *JNGNote=
6470 {
6471 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6472 "format."
6473 },
glennrp47b9dd52010-11-24 18:12:06 +00006474
cristy3ed852e2009-09-05 21:47:34 +00006475 *MNGNote=
6476 {
6477 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6478 "format."
6479 };
6480
6481 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006482
cristy3ed852e2009-09-05 21:47:34 +00006483#if defined(PNG_LIBPNG_VER_STRING)
6484 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6485 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006486
cristy3ed852e2009-09-05 21:47:34 +00006487 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6488 {
6489 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6490 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6491 MaxTextExtent);
6492 }
6493#endif
glennrp47b9dd52010-11-24 18:12:06 +00006494
cristy3ed852e2009-09-05 21:47:34 +00006495 entry=SetMagickInfo("MNG");
6496 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00006497
cristy3ed852e2009-09-05 21:47:34 +00006498#if defined(MAGICKCORE_PNG_DELEGATE)
6499 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6500 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6501#endif
glennrp47b9dd52010-11-24 18:12:06 +00006502
cristy3ed852e2009-09-05 21:47:34 +00006503 entry->magick=(IsImageFormatHandler *) IsMNG;
6504 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00006505
cristy3ed852e2009-09-05 21:47:34 +00006506 if (*version != '\0')
6507 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006508
cristy3ed852e2009-09-05 21:47:34 +00006509 entry->module=ConstantString("PNG");
6510 entry->note=ConstantString(MNGNote);
6511 (void) RegisterMagickInfo(entry);
6512
6513 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006514
cristy3ed852e2009-09-05 21:47:34 +00006515#if defined(MAGICKCORE_PNG_DELEGATE)
6516 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6517 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6518#endif
glennrp47b9dd52010-11-24 18:12:06 +00006519
cristy3ed852e2009-09-05 21:47:34 +00006520 entry->magick=(IsImageFormatHandler *) IsPNG;
6521 entry->adjoin=MagickFalse;
6522 entry->description=ConstantString("Portable Network Graphics");
6523 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006524
cristy3ed852e2009-09-05 21:47:34 +00006525 if (*version != '\0')
6526 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006527
cristy3ed852e2009-09-05 21:47:34 +00006528 entry->note=ConstantString(PNGNote);
6529 (void) RegisterMagickInfo(entry);
6530
6531 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00006532
cristy3ed852e2009-09-05 21:47:34 +00006533#if defined(MAGICKCORE_PNG_DELEGATE)
6534 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6535 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6536#endif
glennrp47b9dd52010-11-24 18:12:06 +00006537
cristy3ed852e2009-09-05 21:47:34 +00006538 entry->magick=(IsImageFormatHandler *) IsPNG;
6539 entry->adjoin=MagickFalse;
6540 entry->description=ConstantString(
6541 "8-bit indexed with optional binary transparency");
6542 entry->module=ConstantString("PNG");
6543 (void) RegisterMagickInfo(entry);
6544
6545 entry=SetMagickInfo("PNG24");
6546 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006547
cristy3ed852e2009-09-05 21:47:34 +00006548#if defined(ZLIB_VERSION)
6549 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6550 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006551
cristy3ed852e2009-09-05 21:47:34 +00006552 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6553 {
6554 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6555 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6556 }
6557#endif
glennrp47b9dd52010-11-24 18:12:06 +00006558
cristy3ed852e2009-09-05 21:47:34 +00006559 if (*version != '\0')
6560 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006561
cristy3ed852e2009-09-05 21:47:34 +00006562#if defined(MAGICKCORE_PNG_DELEGATE)
6563 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6564 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6565#endif
glennrp47b9dd52010-11-24 18:12:06 +00006566
cristy3ed852e2009-09-05 21:47:34 +00006567 entry->magick=(IsImageFormatHandler *) IsPNG;
6568 entry->adjoin=MagickFalse;
6569 entry->description=ConstantString("opaque 24-bit RGB");
6570 entry->module=ConstantString("PNG");
6571 (void) RegisterMagickInfo(entry);
6572
6573 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00006574
cristy3ed852e2009-09-05 21:47:34 +00006575#if defined(MAGICKCORE_PNG_DELEGATE)
6576 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6577 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6578#endif
glennrp47b9dd52010-11-24 18:12:06 +00006579
cristy3ed852e2009-09-05 21:47:34 +00006580 entry->magick=(IsImageFormatHandler *) IsPNG;
6581 entry->adjoin=MagickFalse;
6582 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6583 entry->module=ConstantString("PNG");
6584 (void) RegisterMagickInfo(entry);
6585
6586 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006587
cristy3ed852e2009-09-05 21:47:34 +00006588#if defined(JNG_SUPPORTED)
6589#if defined(MAGICKCORE_PNG_DELEGATE)
6590 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6591 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6592#endif
6593#endif
glennrp47b9dd52010-11-24 18:12:06 +00006594
cristy3ed852e2009-09-05 21:47:34 +00006595 entry->magick=(IsImageFormatHandler *) IsJNG;
6596 entry->adjoin=MagickFalse;
6597 entry->description=ConstantString("JPEG Network Graphics");
6598 entry->module=ConstantString("PNG");
6599 entry->note=ConstantString(JNGNote);
6600 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00006601
cristy18b17442009-10-25 18:36:48 +00006602#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006603 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00006604#endif
glennrp47b9dd52010-11-24 18:12:06 +00006605
cristy3ed852e2009-09-05 21:47:34 +00006606 return(MagickImageCoderSignature);
6607}
6608
6609/*
6610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6611% %
6612% %
6613% %
6614% U n r e g i s t e r P N G I m a g e %
6615% %
6616% %
6617% %
6618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6619%
6620% UnregisterPNGImage() removes format registrations made by the
6621% PNG module from the list of supported formats.
6622%
6623% The format of the UnregisterPNGImage method is:
6624%
6625% UnregisterPNGImage(void)
6626%
6627*/
6628ModuleExport void UnregisterPNGImage(void)
6629{
6630 (void) UnregisterMagickInfo("MNG");
6631 (void) UnregisterMagickInfo("PNG");
6632 (void) UnregisterMagickInfo("PNG8");
6633 (void) UnregisterMagickInfo("PNG24");
6634 (void) UnregisterMagickInfo("PNG32");
6635 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006636
cristy3ed852e2009-09-05 21:47:34 +00006637#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006638 if (ping_semaphore != (SemaphoreInfo *) NULL)
6639 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006640#endif
6641}
6642
6643#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006644#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006645/*
6646%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6647% %
6648% %
6649% %
6650% W r i t e M N G I m a g e %
6651% %
6652% %
6653% %
6654%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6655%
6656% WriteMNGImage() writes an image in the Portable Network Graphics
6657% Group's "Multiple-image Network Graphics" encoded image format.
6658%
6659% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6660%
6661% The format of the WriteMNGImage method is:
6662%
6663% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6664%
6665% A description of each parameter follows.
6666%
6667% o image_info: the image info.
6668%
6669% o image: The image.
6670%
6671%
6672% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6673% "To do" under ReadPNGImage):
6674%
cristy3ed852e2009-09-05 21:47:34 +00006675% Preserve all unknown and not-yet-handled known chunks found in input
6676% PNG file and copy them into output PNG files according to the PNG
6677% copying rules.
6678%
6679% Write the iCCP chunk at MNG level when (icc profile length > 0)
6680%
6681% Improve selection of color type (use indexed-colour or indexed-colour
6682% with tRNS when 256 or fewer unique RGBA values are present).
6683%
6684% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6685% This will be complicated if we limit ourselves to generating MNG-LC
6686% files. For now we ignore disposal method 3 and simply overlay the next
6687% image on it.
6688%
6689% Check for identical PLTE's or PLTE/tRNS combinations and use a
6690% global MNG PLTE or PLTE/tRNS combination when appropriate.
6691% [mostly done 15 June 1999 but still need to take care of tRNS]
6692%
6693% Check for identical sRGB and replace with a global sRGB (and remove
6694% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6695% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6696% local gAMA/cHRM with local sRGB if appropriate).
6697%
6698% Check for identical sBIT chunks and write global ones.
6699%
6700% Provide option to skip writing the signature tEXt chunks.
6701%
6702% Use signatures to detect identical objects and reuse the first
6703% instance of such objects instead of writing duplicate objects.
6704%
6705% Use a smaller-than-32k value of compression window size when
6706% appropriate.
6707%
6708% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6709% ancillary text chunks and save profiles.
6710%
6711% Provide an option to force LC files (to ensure exact framing rate)
6712% instead of VLC.
6713%
6714% Provide an option to force VLC files instead of LC, even when offsets
6715% are present. This will involve expanding the embedded images with a
6716% transparent region at the top and/or left.
6717*/
6718
cristy3ed852e2009-09-05 21:47:34 +00006719static void
glennrpcf002022011-01-30 02:38:15 +00006720Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00006721 png_info *ping_info, unsigned char *profile_type, unsigned char
6722 *profile_description, unsigned char *profile_data, png_uint_32 length)
6723{
cristy3ed852e2009-09-05 21:47:34 +00006724 png_textp
6725 text;
6726
cristybb503372010-05-27 20:51:26 +00006727 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006728 i;
6729
6730 unsigned char
6731 *sp;
6732
6733 png_charp
6734 dp;
6735
6736 png_uint_32
6737 allocated_length,
6738 description_length;
6739
6740 unsigned char
6741 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006742
cristy3ed852e2009-09-05 21:47:34 +00006743 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6744 return;
6745
6746 if (image_info->verbose)
6747 {
glennrp0fe50b42010-11-16 03:52:51 +00006748 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6749 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006750 }
glennrp0fe50b42010-11-16 03:52:51 +00006751
cristy3ed852e2009-09-05 21:47:34 +00006752 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6753 description_length=(png_uint_32) strlen((const char *) profile_description);
6754 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6755 + description_length);
6756 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6757 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6758 text[0].key[0]='\0';
6759 (void) ConcatenateMagickString(text[0].key,
6760 "Raw profile type ",MaxTextExtent);
6761 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6762 sp=profile_data;
6763 dp=text[0].text;
6764 *dp++='\n';
6765 (void) CopyMagickString(dp,(const char *) profile_description,
6766 allocated_length);
6767 dp+=description_length;
6768 *dp++='\n';
6769 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006770 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006771 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00006772
cristybb503372010-05-27 20:51:26 +00006773 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006774 {
6775 if (i%36 == 0)
6776 *dp++='\n';
6777 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6778 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6779 }
glennrp47b9dd52010-11-24 18:12:06 +00006780
cristy3ed852e2009-09-05 21:47:34 +00006781 *dp++='\n';
6782 *dp='\0';
6783 text[0].text_length=(png_size_t) (dp-text[0].text);
6784 text[0].compression=image_info->compression == NoCompression ||
6785 (image_info->compression == UndefinedCompression &&
6786 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00006787
cristy3ed852e2009-09-05 21:47:34 +00006788 if (text[0].text_length <= allocated_length)
6789 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00006790
cristy3ed852e2009-09-05 21:47:34 +00006791 png_free(ping,text[0].text);
6792 png_free(ping,text[0].key);
6793 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006794}
6795
glennrpcf002022011-01-30 02:38:15 +00006796static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00006797 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00006798{
6799 char
6800 *name;
6801
6802 const StringInfo
6803 *profile;
6804
6805 unsigned char
6806 *data;
6807
6808 png_uint_32 length;
6809
6810 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00006811
6812 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6813 {
cristy3ed852e2009-09-05 21:47:34 +00006814 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00006815
cristy3ed852e2009-09-05 21:47:34 +00006816 if (profile != (const StringInfo *) NULL)
6817 {
6818 StringInfo
glennrpcf002022011-01-30 02:38:15 +00006819 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00006820
glennrp47b9dd52010-11-24 18:12:06 +00006821 if (LocaleNCompare(name,string,11) == 0)
6822 {
6823 if (logging != MagickFalse)
6824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6825 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00006826
glennrpcf002022011-01-30 02:38:15 +00006827 ping_profile=CloneStringInfo(profile);
6828 data=GetStringInfoDatum(ping_profile),
6829 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006830 data[4]=data[3];
6831 data[3]=data[2];
6832 data[2]=data[1];
6833 data[1]=data[0];
6834 (void) WriteBlobMSBULong(image,length-5); /* data length */
6835 (void) WriteBlob(image,length-1,data+1);
6836 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00006837 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006838 }
cristy3ed852e2009-09-05 21:47:34 +00006839 }
glennrp47b9dd52010-11-24 18:12:06 +00006840
cristy3ed852e2009-09-05 21:47:34 +00006841 name=GetNextImageProfile(image);
6842 }
glennrp47b9dd52010-11-24 18:12:06 +00006843
cristy3ed852e2009-09-05 21:47:34 +00006844 return(MagickTrue);
6845}
6846
glennrpb9cfe272010-12-21 15:08:06 +00006847
cristy3ed852e2009-09-05 21:47:34 +00006848/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00006849static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6850 const ImageInfo *IMimage_info,Image *IMimage)
6851{
6852 Image
6853 *image;
6854
6855 ImageInfo
6856 *image_info;
6857
cristy3ed852e2009-09-05 21:47:34 +00006858 char
6859 s[2];
6860
6861 const char
6862 *name,
6863 *property,
6864 *value;
6865
6866 const StringInfo
6867 *profile;
6868
cristy3ed852e2009-09-05 21:47:34 +00006869 int
cristy3ed852e2009-09-05 21:47:34 +00006870 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006871 pass;
6872
glennrpe9c26dc2010-05-30 01:56:35 +00006873 png_byte
6874 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006875
glennrp39992b42010-11-14 00:03:43 +00006876 png_color
6877 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00006878
glennrp5af765f2010-03-30 11:12:18 +00006879 png_color_16
6880 ping_background,
6881 ping_trans_color;
6882
cristy3ed852e2009-09-05 21:47:34 +00006883 png_info
6884 *ping_info;
6885
6886 png_struct
6887 *ping;
6888
glennrp5af765f2010-03-30 11:12:18 +00006889 png_uint_32
6890 ping_height,
6891 ping_width;
6892
cristybb503372010-05-27 20:51:26 +00006893 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006894 y;
6895
6896 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00006897 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00006898 logging,
glennrp58e01762011-01-07 15:28:54 +00006899 matte,
6900
glennrpda8f3a72011-02-27 23:54:12 +00006901 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00006902 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00006903 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00006904 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00006905 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00006906 ping_have_bKGD,
6907 ping_have_pHYs,
6908 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00006909
6910 ping_exclude_bKGD,
6911 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00006912 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00006913 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00006914 ping_exclude_gAMA,
6915 ping_exclude_iCCP,
6916 /* ping_exclude_iTXt, */
6917 ping_exclude_oFFs,
6918 ping_exclude_pHYs,
6919 ping_exclude_sRGB,
6920 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00006921 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00006922 ping_exclude_vpAg,
6923 ping_exclude_zCCP, /* hex-encoded iCCP */
6924 ping_exclude_zTXt,
6925
glennrp8d3d6e52011-04-19 04:39:51 +00006926 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00006927 ping_need_colortype_warning,
6928
glennrp82b3c532011-03-22 19:20:54 +00006929 status,
glennrpd3371642011-03-22 19:42:23 +00006930 tried_333,
6931 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00006932
6933 QuantumInfo
6934 *quantum_info;
6935
cristybb503372010-05-27 20:51:26 +00006936 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006937 i,
6938 x;
6939
6940 unsigned char
glennrpcf002022011-01-30 02:38:15 +00006941 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00006942
glennrp5af765f2010-03-30 11:12:18 +00006943 volatile int
glennrpf09bded2011-01-08 01:15:59 +00006944 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00006945 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00006946 ping_color_type,
6947 ping_interlace_method,
6948 ping_compression_method,
6949 ping_filter_method,
6950 ping_num_trans;
6951
cristybb503372010-05-27 20:51:26 +00006952 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00006953 image_depth,
6954 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006955
cristybb503372010-05-27 20:51:26 +00006956 size_t
cristy3ed852e2009-09-05 21:47:34 +00006957 quality,
6958 rowbytes,
6959 save_image_depth;
6960
glennrpdfd70802010-11-14 01:23:35 +00006961 int
glennrpfd05d622011-02-25 04:10:33 +00006962 j,
glennrpf09bded2011-01-08 01:15:59 +00006963 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00006964 number_opaque,
6965 number_semitransparent,
6966 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00006967 ping_pHYs_unit_type;
6968
6969 png_uint_32
6970 ping_pHYs_x_resolution,
6971 ping_pHYs_y_resolution;
6972
cristy3ed852e2009-09-05 21:47:34 +00006973 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00006974 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00006975
glennrpb9cfe272010-12-21 15:08:06 +00006976 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
6977 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00006978 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00006979 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00006980
cristy3ed852e2009-09-05 21:47:34 +00006981#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006982 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006983#endif
6984
glennrp5af765f2010-03-30 11:12:18 +00006985 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00006986 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00006987 ping_color_type=0,
6988 ping_interlace_method=0,
6989 ping_compression_method=0,
6990 ping_filter_method=0,
6991 ping_num_trans = 0;
6992
6993 ping_background.red = 0;
6994 ping_background.green = 0;
6995 ping_background.blue = 0;
6996 ping_background.gray = 0;
6997 ping_background.index = 0;
6998
6999 ping_trans_color.red=0;
7000 ping_trans_color.green=0;
7001 ping_trans_color.blue=0;
7002 ping_trans_color.gray=0;
7003
glennrpdfd70802010-11-14 01:23:35 +00007004 ping_pHYs_unit_type = 0;
7005 ping_pHYs_x_resolution = 0;
7006 ping_pHYs_y_resolution = 0;
7007
glennrpda8f3a72011-02-27 23:54:12 +00007008 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007009 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007010 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007011 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007012 ping_have_bKGD=MagickFalse;
7013 ping_have_pHYs=MagickFalse;
7014 ping_have_tRNS=MagickFalse;
7015
glennrp0e8ea192010-12-24 18:00:33 +00007016 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7017 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007018 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007019 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007020 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007021 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7022 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7023 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7024 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7025 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7026 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007027 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007028 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7029 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7030 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7031
glennrp8d3d6e52011-04-19 04:39:51 +00007032 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007033 ping_need_colortype_warning = MagickFalse;
7034
glennrp8bb3a022010-12-13 20:40:04 +00007035 number_opaque = 0;
7036 number_semitransparent = 0;
7037 number_transparent = 0;
7038
glennrpfd05d622011-02-25 04:10:33 +00007039 if (logging != MagickFalse)
7040 {
7041 if (image->storage_class == UndefinedClass)
7042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7043 " storage_class=UndefinedClass");
7044 if (image->storage_class == DirectClass)
7045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7046 " storage_class=DirectClass");
7047 if (image->storage_class == PseudoClass)
7048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7049 " storage_class=PseudoClass");
7050 }
glennrp28af3712011-04-06 18:07:30 +00007051
7052 if (image->storage_class != PseudoClass && image->colormap != NULL)
7053 {
7054 /* Free the bogus colormap; it can cause trouble later */
7055 if (logging != MagickFalse)
7056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7057 " Freeing bogus colormap");
7058 (void *) RelinquishMagickMemory(image->colormap);
7059 image->colormap=NULL;
7060 }
glennrpfd05d622011-02-25 04:10:33 +00007061
cristy3ed852e2009-09-05 21:47:34 +00007062 if (image->colorspace != RGBColorspace)
7063 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007064
glennrp3241bd02010-12-12 04:36:28 +00007065 /*
7066 Sometimes we get PseudoClass images whose RGB values don't match
7067 the colors in the colormap. This code syncs the RGB values.
7068 */
7069 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7070 (void) SyncImage(image);
7071
glennrpa6a06632011-01-19 15:15:34 +00007072#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7073 if (image->depth > 8)
7074 {
7075 if (logging != MagickFalse)
7076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7077 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7078
7079 image->depth=8;
7080 }
7081#endif
7082
glennrp67b9c1a2011-04-22 18:47:36 +00007083#if 0 /* To do: Option to use the original colormap */
7084 if (ping_preserve_colormap != MagickFalse)
7085 {
7086 }
7087#endif
7088
glennrp3faa9a32011-04-23 14:00:25 +00007089#if 0 /* To do: respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007090 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7091 {
glennrp9d0ea4d2011-04-22 18:35:57 +00007092 }
glennrp67b9c1a2011-04-22 18:47:36 +00007093#endif
glennrp9d0ea4d2011-04-22 18:35:57 +00007094
glennrp67b9c1a2011-04-22 18:47:36 +00007095 /* To do: set to next higher multiple of 8 */
7096 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007097 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007098
glennrp2b013e42010-11-24 16:55:50 +00007099#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7100 /* PNG does not handle depths greater than 16 so reduce it even
7101 * if lossy
7102 */
7103 if (image->depth > 16)
7104 image->depth=16;
7105#endif
7106
glennrp3faa9a32011-04-23 14:00:25 +00007107#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007108 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007109 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007110 image->depth = 8;
7111#endif
7112
glennrpc8c2f062011-02-25 19:00:33 +00007113 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007114 * we reduce the transparency to binary and run again, then if there
7115 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7116 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7117 * palette. The final reduction can only fail if there are still 256
7118 * colors present and one of them has both transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007119 */
glennrp82b3c532011-03-22 19:20:54 +00007120
7121 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007122 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007123
glennrpd3371642011-03-22 19:42:23 +00007124 for (j=0; j<5; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007125 {
7126 /* BUILD_PALETTE
7127 *
7128 * Sometimes we get DirectClass images that have 256 colors or fewer.
7129 * This code will build a colormap.
7130 *
7131 * Also, sometimes we get PseudoClass images with an out-of-date
7132 * colormap. This code will replace the colormap with a new one.
7133 * Sometimes we get PseudoClass images that have more than 256 colors.
7134 * This code will delete the colormap and change the image to
7135 * DirectClass.
7136 *
7137 * If image->matte is MagickFalse, we ignore the opacity channel
7138 * even though it sometimes contains left-over non-opaque values.
7139 *
7140 * Also we gather some information (number of opaque, transparent,
7141 * and semitransparent pixels, and whether the image has any non-gray
7142 * pixels or only black-and-white pixels) that we might need later.
7143 *
7144 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7145 * we need to check for bogus non-opaque values, at least.
7146 */
glennrp3c218112010-11-27 15:31:26 +00007147
glennrpd71e86a2011-02-24 01:28:37 +00007148 ExceptionInfo
7149 *exception;
glennrp3c218112010-11-27 15:31:26 +00007150
glennrpd71e86a2011-02-24 01:28:37 +00007151 int
7152 n;
glennrp3c218112010-11-27 15:31:26 +00007153
glennrpd71e86a2011-02-24 01:28:37 +00007154 PixelPacket
7155 opaque[260],
7156 semitransparent[260],
7157 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007158
glennrpd71e86a2011-02-24 01:28:37 +00007159 register IndexPacket
7160 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007161
glennrpd71e86a2011-02-24 01:28:37 +00007162 register const PixelPacket
7163 *s,
7164 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007165
glennrpfd05d622011-02-25 04:10:33 +00007166 register PixelPacket
7167 *r;
7168
glennrpd71e86a2011-02-24 01:28:37 +00007169 if (logging != MagickFalse)
7170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7171 " Enter BUILD_PALETTE:");
7172
7173 if (logging != MagickFalse)
7174 {
glennrp03812ae2010-12-24 01:31:34 +00007175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007176 " image->columns=%.20g",(double) image->columns);
7177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7178 " image->rows=%.20g",(double) image->rows);
7179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7180 " image->matte=%.20g",(double) image->matte);
7181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7182 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007183
glennrpfd05d622011-02-25 04:10:33 +00007184 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007185 {
7186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007187 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007189 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007190
glennrpd71e86a2011-02-24 01:28:37 +00007191 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007192 {
glennrpd71e86a2011-02-24 01:28:37 +00007193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7194 " %d (%d,%d,%d,%d)",
7195 (int) i,
7196 (int) image->colormap[i].red,
7197 (int) image->colormap[i].green,
7198 (int) image->colormap[i].blue,
7199 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007200 }
glennrp2cc891a2010-12-24 13:44:32 +00007201
glennrpd71e86a2011-02-24 01:28:37 +00007202 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7203 {
7204 if (i > 255)
7205 {
7206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7207 " %d (%d,%d,%d,%d)",
7208 (int) i,
7209 (int) image->colormap[i].red,
7210 (int) image->colormap[i].green,
7211 (int) image->colormap[i].blue,
7212 (int) image->colormap[i].opacity);
7213 }
7214 }
glennrp03812ae2010-12-24 01:31:34 +00007215 }
glennrp7ddcc222010-12-11 05:01:05 +00007216
glennrpd71e86a2011-02-24 01:28:37 +00007217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7218 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007219
glennrpd71e86a2011-02-24 01:28:37 +00007220 if (image->colors == 0)
7221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7222 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007223
glennrp8d3d6e52011-04-19 04:39:51 +00007224 if (ping_preserve_colormap == MagickFalse)
7225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7226 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007227 }
7228
7229 exception=(&image->exception);
7230
7231 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007232 number_opaque = 0;
7233 number_semitransparent = 0;
7234 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007235
7236 for (y=0; y < (ssize_t) image->rows; y++)
7237 {
7238 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7239
7240 if (q == (PixelPacket *) NULL)
7241 break;
7242
7243 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007244 {
glennrpd71e86a2011-02-24 01:28:37 +00007245 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7246 {
7247 if (number_opaque < 259)
7248 {
7249 if (number_opaque == 0)
7250 {
7251 opaque[0]=*q;
7252 opaque[0].opacity=OpaqueOpacity;
7253 number_opaque=1;
7254 }
glennrp2cc891a2010-12-24 13:44:32 +00007255
glennrpd71e86a2011-02-24 01:28:37 +00007256 for (i=0; i< (ssize_t) number_opaque; i++)
7257 {
7258 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7259 break;
7260 }
glennrp7ddcc222010-12-11 05:01:05 +00007261
glennrpd71e86a2011-02-24 01:28:37 +00007262 if (i == (ssize_t) number_opaque &&
7263 number_opaque < 259)
7264 {
7265 number_opaque++;
7266 opaque[i] = *q;
7267 opaque[i].opacity = OpaqueOpacity;
7268 }
7269 }
7270 }
7271 else if (q->opacity == TransparentOpacity)
7272 {
7273 if (number_transparent < 259)
7274 {
7275 if (number_transparent == 0)
7276 {
7277 transparent[0]=*q;
7278 ping_trans_color.red=(unsigned short)(q->red);
7279 ping_trans_color.green=(unsigned short) (q->green);
7280 ping_trans_color.blue=(unsigned short) (q->blue);
7281 ping_trans_color.gray=(unsigned short) (q->blue);
7282 number_transparent = 1;
7283 }
7284
7285 for (i=0; i< (ssize_t) number_transparent; i++)
7286 {
7287 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7288 break;
7289 }
7290
7291 if (i == (ssize_t) number_transparent &&
7292 number_transparent < 259)
7293 {
7294 number_transparent++;
7295 transparent[i] = *q;
7296 }
7297 }
7298 }
7299 else
7300 {
7301 if (number_semitransparent < 259)
7302 {
7303 if (number_semitransparent == 0)
7304 {
7305 semitransparent[0]=*q;
7306 number_semitransparent = 1;
7307 }
7308
7309 for (i=0; i< (ssize_t) number_semitransparent; i++)
7310 {
7311 if (IsColorEqual(semitransparent+i,
7312 (PixelPacket *) q) &&
7313 q->opacity == semitransparent[i].opacity)
7314 break;
7315 }
7316
7317 if (i == (ssize_t) number_semitransparent &&
7318 number_semitransparent < 259)
7319 {
7320 number_semitransparent++;
7321 semitransparent[i] = *q;
7322 }
7323 }
7324 }
7325 q++;
7326 }
7327 }
7328
7329 if (ping_exclude_bKGD == MagickFalse)
7330 {
7331 /* Add the background color to the palette, if it
7332 * isn't already there.
7333 */
7334 for (i=0; i<number_opaque; i++)
7335 {
glennrpa080bc32011-03-11 18:03:44 +00007336 if (IsColorEqual(opaque+i, &image->background_color))
glennrpd71e86a2011-02-24 01:28:37 +00007337 break;
7338 }
7339
7340 if (number_opaque < 259 && i == number_opaque)
7341 {
7342 opaque[i]=image->background_color;
7343 opaque[i].opacity = OpaqueOpacity;
7344 number_opaque++;
7345 }
glennrpa080bc32011-03-11 18:03:44 +00007346 else if (logging != MagickFalse)
7347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7348 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007349 }
7350
7351 image_colors=number_opaque+number_transparent+number_semitransparent;
7352
glennrpa080bc32011-03-11 18:03:44 +00007353 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7354 {
7355 /* No room for the background color; remove it. */
7356 number_opaque--;
7357 image_colors--;
7358 }
7359
glennrpd71e86a2011-02-24 01:28:37 +00007360 if (logging != MagickFalse)
7361 {
7362 if (image_colors > 256)
7363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7364 " image has more than 256 colors");
7365
7366 else
7367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7368 " image has %d colors",image_colors);
7369 }
7370
glennrp8d3d6e52011-04-19 04:39:51 +00007371 if (ping_preserve_colormap != MagickFalse)
7372 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007373
glennrpfd05d622011-02-25 04:10:33 +00007374 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007375 {
7376 ping_have_color=MagickFalse;
7377 ping_have_non_bw=MagickFalse;
7378
7379 if(image_colors > 256)
7380 {
7381 for (y=0; y < (ssize_t) image->rows; y++)
7382 {
7383 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7384
7385 if (q == (PixelPacket *) NULL)
7386 break;
7387
7388 /* Worst case is black-and-white; we are looking at every
7389 * pixel twice.
7390 */
7391
7392 if (ping_have_color == MagickFalse)
7393 {
7394 s=q;
7395 for (x=0; x < (ssize_t) image->columns; x++)
7396 {
7397 if (s->red != s->green || s->red != s->blue)
7398 {
7399 ping_have_color=MagickTrue;
7400 ping_have_non_bw=MagickTrue;
7401 break;
7402 }
7403 s++;
7404 }
7405 }
7406
7407 if (ping_have_non_bw == MagickFalse)
7408 {
7409 s=q;
7410 for (x=0; x < (ssize_t) image->columns; x++)
7411 {
7412 if (s->red != 0 && s->red != QuantumRange)
7413 {
7414 ping_have_non_bw=MagickTrue;
7415 }
7416 s++;
7417 }
7418 }
7419 }
7420 }
7421 }
7422
7423 if (image_colors < 257)
7424 {
7425 PixelPacket
7426 colormap[260];
7427
7428 /*
7429 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007430 */
7431
glennrpd71e86a2011-02-24 01:28:37 +00007432 if (logging != MagickFalse)
7433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7434 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007435
glennrpd71e86a2011-02-24 01:28:37 +00007436 /* Sort palette, transparent first */;
7437
7438 n = 0;
7439
7440 for (i=0; i<number_transparent; i++)
7441 colormap[n++] = transparent[i];
7442
7443 for (i=0; i<number_semitransparent; i++)
7444 colormap[n++] = semitransparent[i];
7445
7446 for (i=0; i<number_opaque; i++)
7447 colormap[n++] = opaque[i];
7448
7449
7450 /* image_colors < 257; search the colormap instead of the pixels
7451 * to get ping_have_color and ping_have_non_bw
7452 */
7453 for (i=0; i<n; i++)
7454 {
7455 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007456 {
glennrpd71e86a2011-02-24 01:28:37 +00007457 if (colormap[i].red != colormap[i].green ||
7458 colormap[i].red != colormap[i].blue)
7459 {
7460 ping_have_color=MagickTrue;
7461 ping_have_non_bw=MagickTrue;
7462 break;
7463 }
7464 }
7465
7466 if (ping_have_non_bw == MagickFalse)
7467 {
7468 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007469 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007470 }
glennrp8bb3a022010-12-13 20:40:04 +00007471 }
7472
glennrpd71e86a2011-02-24 01:28:37 +00007473 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7474 (number_transparent == 0 && number_semitransparent == 0)) &&
7475 (((mng_info->write_png_colortype-1) ==
7476 PNG_COLOR_TYPE_PALETTE) ||
7477 (mng_info->write_png_colortype == 0)))
7478 {
glennrp6185c532011-01-14 17:58:40 +00007479 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007480 {
glennrpd71e86a2011-02-24 01:28:37 +00007481 if (n != (ssize_t) image_colors)
7482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7483 " image_colors (%d) and n (%d) don't match",
7484 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007485
glennrpd71e86a2011-02-24 01:28:37 +00007486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7487 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007488 }
glennrp03812ae2010-12-24 01:31:34 +00007489
glennrpd71e86a2011-02-24 01:28:37 +00007490 image->colors = image_colors;
7491
7492 if (AcquireImageColormap(image,image_colors) ==
7493 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007494 ThrowWriterException(ResourceLimitError,
7495 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007496
7497 for (i=0; i< (ssize_t) image_colors; i++)
7498 image->colormap[i] = colormap[i];
7499
7500 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007501 {
glennrpd71e86a2011-02-24 01:28:37 +00007502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7503 " image->colors=%d (%d)",
7504 (int) image->colors, image_colors);
7505
7506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7507 " Update the pixel indexes");
7508 }
glennrp6185c532011-01-14 17:58:40 +00007509
glennrpfd05d622011-02-25 04:10:33 +00007510 /* Sync the pixel indices with the new colormap */
7511
glennrpd71e86a2011-02-24 01:28:37 +00007512 for (y=0; y < (ssize_t) image->rows; y++)
7513 {
7514 q=GetAuthenticPixels(image,0,y,image->columns,1,
7515 exception);
glennrp6185c532011-01-14 17:58:40 +00007516
glennrpd71e86a2011-02-24 01:28:37 +00007517 if (q == (PixelPacket *) NULL)
7518 break;
glennrp6185c532011-01-14 17:58:40 +00007519
glennrpd71e86a2011-02-24 01:28:37 +00007520 indexes=GetAuthenticIndexQueue(image);
7521
7522 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007523 {
glennrpd71e86a2011-02-24 01:28:37 +00007524 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007525 {
glennrpd71e86a2011-02-24 01:28:37 +00007526 if ((image->matte == MagickFalse ||
7527 image->colormap[i].opacity == q->opacity) &&
7528 (IsColorEqual(&image->colormap[i],
7529 (PixelPacket *) q)))
glennrp6185c532011-01-14 17:58:40 +00007530 {
glennrpd71e86a2011-02-24 01:28:37 +00007531 indexes[x]=(IndexPacket) i;
7532 break;
glennrp6185c532011-01-14 17:58:40 +00007533 }
glennrp6185c532011-01-14 17:58:40 +00007534 }
glennrpd71e86a2011-02-24 01:28:37 +00007535 q++;
7536 }
glennrp6185c532011-01-14 17:58:40 +00007537
glennrpd71e86a2011-02-24 01:28:37 +00007538 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7539 break;
7540 }
7541 }
7542 }
7543
7544 if (logging != MagickFalse)
7545 {
7546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7547 " image->colors=%d", (int) image->colors);
7548
7549 if (image->colormap != NULL)
7550 {
7551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7552 " i (red,green,blue,opacity)");
7553
7554 for (i=0; i < (ssize_t) image->colors; i++)
7555 {
cristy72988482011-03-29 16:34:38 +00007556 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007557 {
7558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7559 " %d (%d,%d,%d,%d)",
7560 (int) i,
7561 (int) image->colormap[i].red,
7562 (int) image->colormap[i].green,
7563 (int) image->colormap[i].blue,
7564 (int) image->colormap[i].opacity);
7565 }
glennrp6185c532011-01-14 17:58:40 +00007566 }
7567 }
glennrp03812ae2010-12-24 01:31:34 +00007568
glennrpd71e86a2011-02-24 01:28:37 +00007569 if (number_transparent < 257)
7570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7571 " number_transparent = %d",
7572 number_transparent);
7573 else
glennrp03812ae2010-12-24 01:31:34 +00007574
glennrpd71e86a2011-02-24 01:28:37 +00007575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7576 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007577
glennrpd71e86a2011-02-24 01:28:37 +00007578 if (number_opaque < 257)
7579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7580 " number_opaque = %d",
7581 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007582
glennrpd71e86a2011-02-24 01:28:37 +00007583 else
7584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7585 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007586
glennrpd71e86a2011-02-24 01:28:37 +00007587 if (number_semitransparent < 257)
7588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7589 " number_semitransparent = %d",
7590 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007591
glennrpd71e86a2011-02-24 01:28:37 +00007592 else
7593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7594 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007595
glennrpd71e86a2011-02-24 01:28:37 +00007596 if (ping_have_non_bw == MagickFalse)
7597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7598 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007599
glennrpd71e86a2011-02-24 01:28:37 +00007600 else if (ping_have_color == MagickFalse)
7601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7602 " All pixels and the background are gray");
7603
7604 else
7605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7606 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007607
glennrp03812ae2010-12-24 01:31:34 +00007608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7609 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007610 }
glennrpfd05d622011-02-25 04:10:33 +00007611
glennrpc8c2f062011-02-25 19:00:33 +00007612 if (mng_info->write_png8 == MagickFalse)
7613 break;
glennrpfd05d622011-02-25 04:10:33 +00007614
glennrpc8c2f062011-02-25 19:00:33 +00007615 /* Make any reductions necessary for the PNG8 format */
7616 if (image_colors <= 256 &&
7617 image_colors != 0 && image->colormap != NULL &&
7618 number_semitransparent == 0 &&
7619 number_transparent <= 1)
7620 break;
7621
7622 /* PNG8 can't have semitransparent colors so we threshold the
7623 * opacity to 0 or OpaqueOpacity
7624 */
7625 if (number_semitransparent != 0)
7626 {
7627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7628 " Thresholding the alpha channel to binary");
7629
7630 for (y=0; y < (ssize_t) image->rows; y++)
7631 {
7632 r=GetAuthenticPixels(image,0,y,image->columns,1,
7633 exception);
7634
7635 if (r == (PixelPacket *) NULL)
7636 break;
7637
7638 for (x=0; x < (ssize_t) image->columns; x++)
7639 {
glennrp82b3c532011-03-22 19:20:54 +00007640 r->opacity = r->opacity > TransparentOpacity/2 ?
7641 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007642 r++;
7643 }
7644
7645 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7646 break;
7647
7648 if (image_colors != 0 && image_colors <= 256 &&
7649 image->colormap != NULL)
7650 for (i=0; i<image_colors; i++)
7651 image->colormap[i].opacity =
glennrp82b3c532011-03-22 19:20:54 +00007652 image->colormap[i].opacity > TransparentOpacity/2 ?
7653 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007654 }
7655 continue;
7656 }
7657
7658 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007659 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7660 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7661 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007662 */
glennrpd3371642011-03-22 19:42:23 +00007663 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7664 {
7665 if (logging != MagickFalse)
7666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7667 " Quantizing the background color to 4-4-4");
7668
7669 tried_444 = MagickTrue;
7670
7671 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007672 ScaleCharToQuantum(
7673 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7674 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007675 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007676 ScaleCharToQuantum(
7677 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7678 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007679 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007680 ScaleCharToQuantum(
7681 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7682 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007683
7684 if (logging != MagickFalse)
7685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7686 " Quantizing the pixel colors to 4-4-4");
7687
7688 if (image->colormap == NULL)
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 {
7700 if (r->opacity == TransparentOpacity)
7701 {
7702 r->red = image->background_color.red;
7703 r->green = image->background_color.green;
7704 r->blue = image->background_color.blue;
7705 }
7706 else
7707 {
glennrp3faa9a32011-04-23 14:00:25 +00007708 r->red=ScaleCharToQuantum(
7709 (ScaleQuantumToChar(r->red) & 0xf0) |
7710 (ScaleQuantumToChar(r->red) & 0xf0) >> 4);
7711 r->green=ScaleCharToQuantum(
7712 (ScaleQuantumToChar(r->green) & 0xf0) |
7713 (ScaleQuantumToChar(r->green) & 0xf0) >> 4);
7714 r->blue=ScaleCharToQuantum(
7715 (ScaleQuantumToChar(r->blue) & 0xf0) |
7716 (ScaleQuantumToChar(r->blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007717 }
7718 r++;
7719 }
7720
7721 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7722 break;
7723 }
7724 }
7725
7726 else /* Should not reach this; colormap already exists and
7727 must be <= 256 */
7728 {
7729 if (logging != MagickFalse)
7730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7731 " Quantizing the colormap to 4-4-4");
7732 for (i=0; i<image_colors; i++)
7733 {
glennrp3faa9a32011-04-23 14:00:25 +00007734 image->colormap[i].red=ScaleCharToQuantum(
7735 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7736 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7737 image->colormap[i].green=ScaleCharToQuantum(
7738 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7739 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7740 image->colormap[i].blue=ScaleCharToQuantum(
7741 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7742 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
glennrpd3371642011-03-22 19:42:23 +00007743 }
7744 }
7745 continue;
7746 }
7747
glennrp82b3c532011-03-22 19:20:54 +00007748 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7749 {
7750 if (logging != MagickFalse)
7751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7752 " Quantizing the background color to 3-3-3");
7753
7754 tried_333 = MagickTrue;
7755
7756 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007757 ScaleCharToQuantum(
7758 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7759 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7760 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007761 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007762 ScaleCharToQuantum(
7763 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7764 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7765 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007766 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007767 ScaleCharToQuantum(
7768 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7769 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7770 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007771
7772 if (logging != MagickFalse)
7773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007774 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007775
7776 if (image->colormap == NULL)
7777 {
7778 for (y=0; y < (ssize_t) image->rows; y++)
7779 {
7780 r=GetAuthenticPixels(image,0,y,image->columns,1,
7781 exception);
7782
7783 if (r == (PixelPacket *) NULL)
7784 break;
7785
7786 for (x=0; x < (ssize_t) image->columns; x++)
7787 {
7788 if (r->opacity == TransparentOpacity)
7789 {
7790 r->red = image->background_color.red;
7791 r->green = image->background_color.green;
7792 r->blue = image->background_color.blue;
7793 }
7794 else
7795 {
glennrp3faa9a32011-04-23 14:00:25 +00007796 r->red=ScaleCharToQuantum(
7797 (ScaleQuantumToChar(r->red) & 0xe0) |
7798 (ScaleQuantumToChar(r->red) & 0xe0) >> 3 |
7799 (ScaleQuantumToChar(r->red) & 0xc0) >> 6);
7800 r->green=ScaleCharToQuantum(
7801 (ScaleQuantumToChar(r->green) & 0xe0) |
7802 (ScaleQuantumToChar(r->green) & 0xe0) >> 3 |
7803 (ScaleQuantumToChar(r->green) & 0xc0) >> 6);
7804 r->blue=ScaleCharToQuantum(
7805 (ScaleQuantumToChar(r->blue) & 0xe0) |
7806 (ScaleQuantumToChar(r->blue) & 0xe0) >> 3 |
7807 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007808 }
7809 r++;
7810 }
7811
7812 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7813 break;
7814 }
7815 }
7816
7817 else /* Should not reach this; colormap already exists and
7818 must be <= 256 */
7819 {
7820 if (logging != MagickFalse)
7821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007822 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007823 for (i=0; i<image_colors; i++)
7824 {
glennrp3faa9a32011-04-23 14:00:25 +00007825 image->colormap[i].red=ScaleCharToQuantum(
7826 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7827 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7828 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7829 image->colormap[i].green=ScaleCharToQuantum(
7830 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7831 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7832 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7833 image->colormap[i].blue=ScaleCharToQuantum(
7834 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7835 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7836 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007837 }
glennrpd3371642011-03-22 19:42:23 +00007838 }
7839 continue;
glennrp82b3c532011-03-22 19:20:54 +00007840 }
glennrpc8c2f062011-02-25 19:00:33 +00007841
glennrpc8c2f062011-02-25 19:00:33 +00007842 if (image_colors == 0 || image_colors > 256)
7843 {
7844 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007846 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007847
glennrp3faa9a32011-04-23 14:00:25 +00007848 /* Red and green were already done so we only quantize the blue
7849 * channel
7850 */
7851
7852 image->background_color.blue=ScaleCharToQuantum(
7853 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7854 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7855 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7856 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrpfd05d622011-02-25 04:10:33 +00007857
glennrpc8c2f062011-02-25 19:00:33 +00007858 if (logging != MagickFalse)
7859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007860 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007861
glennrpc8c2f062011-02-25 19:00:33 +00007862 if (image->colormap == NULL)
7863 {
7864 for (y=0; y < (ssize_t) image->rows; y++)
7865 {
7866 r=GetAuthenticPixels(image,0,y,image->columns,1,
7867 exception);
7868
7869 if (r == (PixelPacket *) NULL)
7870 break;
7871
7872 for (x=0; x < (ssize_t) image->columns; x++)
7873 {
glennrp82b3c532011-03-22 19:20:54 +00007874 if (r->opacity == TransparentOpacity)
7875 {
7876 r->red = image->background_color.red;
7877 r->green = image->background_color.green;
7878 r->blue = image->background_color.blue;
7879 }
7880 else
7881 {
glennrp3faa9a32011-04-23 14:00:25 +00007882 r->blue=ScaleCharToQuantum(
7883 (ScaleQuantumToChar(r->blue) & 0xc0) |
7884 (ScaleQuantumToChar(r->blue) & 0xc0) >> 2 |
7885 (ScaleQuantumToChar(r->blue) & 0xc0) >> 4 |
7886 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007887 }
glennrp52a479c2011-02-26 21:14:38 +00007888 r++;
glennrpc8c2f062011-02-25 19:00:33 +00007889 }
glennrpfd05d622011-02-25 04:10:33 +00007890
glennrpc8c2f062011-02-25 19:00:33 +00007891 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7892 break;
7893 }
7894 }
glennrpfd05d622011-02-25 04:10:33 +00007895
glennrpc8c2f062011-02-25 19:00:33 +00007896 else /* Should not reach this; colormap already exists and
7897 must be <= 256 */
7898 {
7899 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007901 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00007902 for (i=0; i<image_colors; i++)
7903 {
glennrp3faa9a32011-04-23 14:00:25 +00007904 image->colormap[i].blue=ScaleCharToQuantum(
7905 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
7906 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
7907 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
7908 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrpc8c2f062011-02-25 19:00:33 +00007909 }
7910 }
7911 continue;
7912 }
7913 break;
glennrpd71e86a2011-02-24 01:28:37 +00007914 }
glennrpfd05d622011-02-25 04:10:33 +00007915 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00007916
glennrpfd05d622011-02-25 04:10:33 +00007917 /* If we are excluding the tRNS chunk and there is transparency,
7918 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7919 * PNG.
glennrp8d579662011-02-23 02:05:02 +00007920 */
glennrp0e8ea192010-12-24 18:00:33 +00007921 if (mng_info->ping_exclude_tRNS != MagickFalse &&
7922 (number_transparent != 0 || number_semitransparent != 0))
7923 {
7924 int colortype=mng_info->write_png_colortype;
7925
7926 if (ping_have_color == MagickFalse)
7927 mng_info->write_png_colortype = 5;
7928
7929 else
7930 mng_info->write_png_colortype = 7;
7931
glennrp8d579662011-02-23 02:05:02 +00007932 if (colortype != 0 &&
7933 mng_info->write_png_colortype != (ssize_t) colortype)
glennrp0e8ea192010-12-24 18:00:33 +00007934 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00007935
glennrp0e8ea192010-12-24 18:00:33 +00007936 }
7937
glennrpfd05d622011-02-25 04:10:33 +00007938 /* See if cheap transparency is possible. It is only possible
7939 * when there is a single transparent color, no semitransparent
7940 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00007941 * as the transparent color. We only need this information if
7942 * we are writing a PNG with colortype 0 or 2, and we have not
7943 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00007944 */
glennrp5a39f372011-02-25 04:52:16 +00007945 if (number_transparent == 1 &&
7946 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00007947 {
7948 ping_have_cheap_transparency = MagickTrue;
7949
7950 if (number_semitransparent != 0)
7951 ping_have_cheap_transparency = MagickFalse;
7952
7953 else if (image_colors == 0 || image_colors > 256 ||
7954 image->colormap == NULL)
7955 {
7956 ExceptionInfo
7957 *exception;
7958
7959 register const PixelPacket
7960 *q;
7961
7962 exception=(&image->exception);
7963
7964 for (y=0; y < (ssize_t) image->rows; y++)
7965 {
7966 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
7967
7968 if (q == (PixelPacket *) NULL)
7969 break;
7970
7971 for (x=0; x < (ssize_t) image->columns; x++)
7972 {
7973 if (q->opacity != TransparentOpacity &&
7974 (unsigned short) q->red == ping_trans_color.red &&
7975 (unsigned short) q->green == ping_trans_color.green &&
7976 (unsigned short) q->blue == ping_trans_color.blue)
7977 {
7978 ping_have_cheap_transparency = MagickFalse;
7979 break;
7980 }
7981
7982 q++;
7983 }
7984
7985 if (ping_have_cheap_transparency == MagickFalse)
7986 break;
7987 }
7988 }
7989 else
7990 {
glennrp67b9c1a2011-04-22 18:47:36 +00007991 /* Assuming that image->colormap[0] is the one transparent color
7992 * and that all others are opaque.
7993 */
glennrpfd05d622011-02-25 04:10:33 +00007994 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00007995 for (i=1; i<image_colors; i++)
7996 if (image->colormap[i].red == image->colormap[0].red &&
7997 image->colormap[i].green == image->colormap[0].green &&
7998 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00007999 {
glennrp67b9c1a2011-04-22 18:47:36 +00008000 ping_have_cheap_transparency = MagickFalse;
8001 break;
glennrpfd05d622011-02-25 04:10:33 +00008002 }
8003 }
8004
8005 if (logging != MagickFalse)
8006 {
8007 if (ping_have_cheap_transparency == MagickFalse)
8008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8009 " Cheap transparency is not possible.");
8010
8011 else
8012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8013 " Cheap transparency is possible.");
8014 }
8015 }
8016 else
8017 ping_have_cheap_transparency = MagickFalse;
8018
glennrp8640fb52010-11-23 15:48:26 +00008019 image_depth=image->depth;
8020
glennrp26c990a2010-11-23 02:23:20 +00008021 quantum_info = (QuantumInfo *) NULL;
8022 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008023 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008024 image_matte=image->matte;
8025
glennrp0fe50b42010-11-16 03:52:51 +00008026 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008027 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008028
glennrp52a479c2011-02-26 21:14:38 +00008029 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8030 (image->colors == 0 || image->colormap == NULL))
8031 {
glennrp52a479c2011-02-26 21:14:38 +00008032 image_info=DestroyImageInfo(image_info);
8033 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008034 (void) ThrowMagickException(&IMimage->exception,
8035 GetMagickModule(),CoderError,
8036 "Cannot write PNG8 or color-type 3; colormap is NULL",
8037 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008038#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8039 UnlockSemaphoreInfo(ping_semaphore);
8040#endif
8041 return(MagickFalse);
8042 }
8043
cristy3ed852e2009-09-05 21:47:34 +00008044 /*
8045 Allocate the PNG structures
8046 */
8047#ifdef PNG_USER_MEM_SUPPORTED
8048 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008049 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8050 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008051
cristy3ed852e2009-09-05 21:47:34 +00008052#else
8053 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008054 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008055
cristy3ed852e2009-09-05 21:47:34 +00008056#endif
8057 if (ping == (png_struct *) NULL)
8058 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008059
cristy3ed852e2009-09-05 21:47:34 +00008060 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008061
cristy3ed852e2009-09-05 21:47:34 +00008062 if (ping_info == (png_info *) NULL)
8063 {
8064 png_destroy_write_struct(&ping,(png_info **) NULL);
8065 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8066 }
glennrp0fe50b42010-11-16 03:52:51 +00008067
cristy3ed852e2009-09-05 21:47:34 +00008068 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008069 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008070
glennrp5af765f2010-03-30 11:12:18 +00008071 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008072 {
8073 /*
8074 PNG write failed.
8075 */
8076#ifdef PNG_DEBUG
8077 if (image_info->verbose)
8078 (void) printf("PNG write has failed.\n");
8079#endif
8080 png_destroy_write_struct(&ping,&ping_info);
8081#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008082 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008083#endif
glennrpda8f3a72011-02-27 23:54:12 +00008084 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008085 (void) CloseBlob(image);
8086 image_info=DestroyImageInfo(image_info);
8087 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008088 return(MagickFalse);
8089 }
8090 /*
8091 Prepare PNG for writing.
8092 */
8093#if defined(PNG_MNG_FEATURES_SUPPORTED)
8094 if (mng_info->write_mng)
8095 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008096
cristy3ed852e2009-09-05 21:47:34 +00008097#else
8098# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8099 if (mng_info->write_mng)
8100 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008101
cristy3ed852e2009-09-05 21:47:34 +00008102# endif
8103#endif
glennrp2b013e42010-11-24 16:55:50 +00008104
cristy3ed852e2009-09-05 21:47:34 +00008105 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008106
cristy4e5bc842010-06-09 13:56:01 +00008107 ping_width=(png_uint_32) image->columns;
8108 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008109
cristy3ed852e2009-09-05 21:47:34 +00008110 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8111 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008112
cristy3ed852e2009-09-05 21:47:34 +00008113 if (mng_info->write_png_depth != 0)
8114 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008115
cristy3ed852e2009-09-05 21:47:34 +00008116 /* Adjust requested depth to next higher valid depth if necessary */
8117 if (image_depth > 8)
8118 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008119
cristy3ed852e2009-09-05 21:47:34 +00008120 if ((image_depth > 4) && (image_depth < 8))
8121 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008122
cristy3ed852e2009-09-05 21:47:34 +00008123 if (image_depth == 3)
8124 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008125
cristy3ed852e2009-09-05 21:47:34 +00008126 if (logging != MagickFalse)
8127 {
8128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008129 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008131 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008133 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008135 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008137 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008138 }
glennrp8640fb52010-11-23 15:48:26 +00008139
cristy3ed852e2009-09-05 21:47:34 +00008140 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008141 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008142
glennrp26f37912010-12-23 16:22:42 +00008143
cristy3ed852e2009-09-05 21:47:34 +00008144#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008145 if (ping_exclude_pHYs == MagickFalse)
8146 {
cristy3ed852e2009-09-05 21:47:34 +00008147 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8148 (!mng_info->write_mng || !mng_info->equal_physs))
8149 {
glennrp0fe50b42010-11-16 03:52:51 +00008150 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8152 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008153
8154 if (image->units == PixelsPerInchResolution)
8155 {
glennrpdfd70802010-11-14 01:23:35 +00008156 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008157 ping_pHYs_x_resolution=
8158 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8159 ping_pHYs_y_resolution=
8160 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008161 }
glennrpdfd70802010-11-14 01:23:35 +00008162
cristy3ed852e2009-09-05 21:47:34 +00008163 else if (image->units == PixelsPerCentimeterResolution)
8164 {
glennrpdfd70802010-11-14 01:23:35 +00008165 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008166 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8167 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008168 }
glennrp991d11d2010-11-12 21:55:28 +00008169
cristy3ed852e2009-09-05 21:47:34 +00008170 else
8171 {
glennrpdfd70802010-11-14 01:23:35 +00008172 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8173 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8174 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008175 }
glennrp991d11d2010-11-12 21:55:28 +00008176
glennrp823b55c2011-03-14 18:46:46 +00008177 if (logging != MagickFalse)
8178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8179 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8180 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8181 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008182 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008183 }
glennrp26f37912010-12-23 16:22:42 +00008184 }
cristy3ed852e2009-09-05 21:47:34 +00008185#endif
glennrpa521b2f2010-10-29 04:11:03 +00008186
glennrp26f37912010-12-23 16:22:42 +00008187 if (ping_exclude_bKGD == MagickFalse)
8188 {
glennrpa521b2f2010-10-29 04:11:03 +00008189 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008190 {
glennrpa521b2f2010-10-29 04:11:03 +00008191 unsigned int
8192 mask;
cristy3ed852e2009-09-05 21:47:34 +00008193
glennrpa521b2f2010-10-29 04:11:03 +00008194 mask=0xffff;
8195 if (ping_bit_depth == 8)
8196 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008197
glennrpa521b2f2010-10-29 04:11:03 +00008198 if (ping_bit_depth == 4)
8199 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008200
glennrpa521b2f2010-10-29 04:11:03 +00008201 if (ping_bit_depth == 2)
8202 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008203
glennrpa521b2f2010-10-29 04:11:03 +00008204 if (ping_bit_depth == 1)
8205 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008206
glennrpa521b2f2010-10-29 04:11:03 +00008207 ping_background.red=(png_uint_16)
8208 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008209
glennrpa521b2f2010-10-29 04:11:03 +00008210 ping_background.green=(png_uint_16)
8211 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008212
glennrpa521b2f2010-10-29 04:11:03 +00008213 ping_background.blue=(png_uint_16)
8214 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008215 }
cristy3ed852e2009-09-05 21:47:34 +00008216
glennrp0fe50b42010-11-16 03:52:51 +00008217 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008218 {
8219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8220 " Setting up bKGD chunk (1)");
8221
8222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8223 " ping_bit_depth=%d",ping_bit_depth);
8224 }
glennrp0fe50b42010-11-16 03:52:51 +00008225
8226 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008227 }
glennrp0fe50b42010-11-16 03:52:51 +00008228
cristy3ed852e2009-09-05 21:47:34 +00008229 /*
8230 Select the color type.
8231 */
8232 matte=image_matte;
8233 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008234
glennrp1273f7b2011-02-24 03:20:30 +00008235 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008236 {
glennrp0fe50b42010-11-16 03:52:51 +00008237
glennrpfd05d622011-02-25 04:10:33 +00008238 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008239 for reducing the sample depth from 8. */
8240
glennrp0fe50b42010-11-16 03:52:51 +00008241 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008242
glennrp8bb3a022010-12-13 20:40:04 +00008243 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008244
8245 /*
8246 Set image palette.
8247 */
8248 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8249
glennrp0fe50b42010-11-16 03:52:51 +00008250 if (logging != MagickFalse)
8251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8252 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008253 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008254
8255 for (i=0; i < (ssize_t) number_colors; i++)
8256 {
8257 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8258 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8259 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8260 if (logging != MagickFalse)
8261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008262#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008263 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008264#else
8265 " %5ld (%5d,%5d,%5d)",
8266#endif
glennrp0fe50b42010-11-16 03:52:51 +00008267 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8268
8269 }
glennrp2b013e42010-11-24 16:55:50 +00008270
glennrp8bb3a022010-12-13 20:40:04 +00008271 ping_have_PLTE=MagickTrue;
8272 image_depth=ping_bit_depth;
8273 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008274
glennrp58e01762011-01-07 15:28:54 +00008275 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008276 {
glennrp0fe50b42010-11-16 03:52:51 +00008277 /*
8278 Identify which colormap entry is transparent.
8279 */
8280 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008281 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008282
glennrp8bb3a022010-12-13 20:40:04 +00008283 for (i=0; i < (ssize_t) number_transparent; i++)
8284 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008285
glennrp0fe50b42010-11-16 03:52:51 +00008286
glennrp2cc891a2010-12-24 13:44:32 +00008287 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008288 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008289
8290 if (ping_num_trans == 0)
8291 ping_have_tRNS=MagickFalse;
8292
glennrp8bb3a022010-12-13 20:40:04 +00008293 else
8294 ping_have_tRNS=MagickTrue;
8295 }
glennrp0fe50b42010-11-16 03:52:51 +00008296
glennrp1273f7b2011-02-24 03:20:30 +00008297 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008298 {
glennrp1273f7b2011-02-24 03:20:30 +00008299 /*
8300 * Identify which colormap entry is the background color.
8301 */
8302
glennrp4f25bd02011-01-01 18:51:28 +00008303 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8304 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8305 break;
glennrp0fe50b42010-11-16 03:52:51 +00008306
glennrp4f25bd02011-01-01 18:51:28 +00008307 ping_background.index=(png_byte) i;
8308 }
cristy3ed852e2009-09-05 21:47:34 +00008309 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008310
cristy3ed852e2009-09-05 21:47:34 +00008311 else if (mng_info->write_png24)
8312 {
8313 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008314 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008315 }
glennrp0fe50b42010-11-16 03:52:51 +00008316
cristy3ed852e2009-09-05 21:47:34 +00008317 else if (mng_info->write_png32)
8318 {
8319 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008320 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008321 }
glennrp0fe50b42010-11-16 03:52:51 +00008322
glennrp8bb3a022010-12-13 20:40:04 +00008323 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008324 {
glennrp5af765f2010-03-30 11:12:18 +00008325 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008326
glennrp8bb3a022010-12-13 20:40:04 +00008327 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008328 {
glennrp5af765f2010-03-30 11:12:18 +00008329 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008330
glennrp5af765f2010-03-30 11:12:18 +00008331 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8332 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008333 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008334
glennrp8bb3a022010-12-13 20:40:04 +00008335 else
8336 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008337
8338 if (logging != MagickFalse)
8339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8340 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008341 }
glennrp0fe50b42010-11-16 03:52:51 +00008342
glennrp7c4c9e62011-03-21 20:23:32 +00008343 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008344 {
8345 if (logging != MagickFalse)
8346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008347 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008348
glennrpd6bf1612010-12-17 17:28:54 +00008349 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008350 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008351
glennrpd6bf1612010-12-17 17:28:54 +00008352 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008353 {
glennrp5af765f2010-03-30 11:12:18 +00008354 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008355 image_matte=MagickFalse;
8356 }
glennrp0fe50b42010-11-16 03:52:51 +00008357
glennrpd6bf1612010-12-17 17:28:54 +00008358 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008359 {
glennrp5af765f2010-03-30 11:12:18 +00008360 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008361 image_matte=MagickTrue;
8362 }
glennrp0fe50b42010-11-16 03:52:51 +00008363
glennrp5aa37f62011-01-02 03:07:57 +00008364 if (image_info->type == PaletteType ||
8365 image_info->type == PaletteMatteType)
8366 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8367
glennrp7c4c9e62011-03-21 20:23:32 +00008368 if (mng_info->write_png_colortype == 0 &&
8369 (image_info->type == UndefinedType ||
8370 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008371 {
glennrp5aa37f62011-01-02 03:07:57 +00008372 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008373 {
glennrp5aa37f62011-01-02 03:07:57 +00008374 if (image_matte == MagickFalse)
8375 {
8376 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8377 image_matte=MagickFalse;
8378 }
glennrp0fe50b42010-11-16 03:52:51 +00008379
glennrp0b206f52011-01-07 04:55:32 +00008380 else
glennrp5aa37f62011-01-02 03:07:57 +00008381 {
8382 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8383 image_matte=MagickTrue;
8384 }
8385 }
8386 else
glennrp8bb3a022010-12-13 20:40:04 +00008387 {
glennrp5aa37f62011-01-02 03:07:57 +00008388 if (image_matte == MagickFalse)
8389 {
8390 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8391 image_matte=MagickFalse;
8392 }
glennrp8bb3a022010-12-13 20:40:04 +00008393
glennrp0b206f52011-01-07 04:55:32 +00008394 else
glennrp5aa37f62011-01-02 03:07:57 +00008395 {
8396 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8397 image_matte=MagickTrue;
8398 }
8399 }
glennrp0fe50b42010-11-16 03:52:51 +00008400 }
glennrp5aa37f62011-01-02 03:07:57 +00008401
cristy3ed852e2009-09-05 21:47:34 +00008402 }
glennrp0fe50b42010-11-16 03:52:51 +00008403
cristy3ed852e2009-09-05 21:47:34 +00008404 if (logging != MagickFalse)
8405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008406 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008407
glennrp5af765f2010-03-30 11:12:18 +00008408 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008409 {
8410 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8411 ping_color_type == PNG_COLOR_TYPE_RGB ||
8412 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8413 ping_bit_depth=8;
8414 }
cristy3ed852e2009-09-05 21:47:34 +00008415
glennrpd6bf1612010-12-17 17:28:54 +00008416 old_bit_depth=ping_bit_depth;
8417
glennrp5af765f2010-03-30 11:12:18 +00008418 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008419 {
glennrp8d579662011-02-23 02:05:02 +00008420 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8421 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008422 }
glennrp8640fb52010-11-23 15:48:26 +00008423
glennrp5af765f2010-03-30 11:12:18 +00008424 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008425 {
cristy35ef8242010-06-03 16:24:13 +00008426 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008427 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008428
8429 if (image->colors == 0)
8430 {
glennrp0fe50b42010-11-16 03:52:51 +00008431 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008432 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008433 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008434 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008435 }
8436
cristy35ef8242010-06-03 16:24:13 +00008437 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008438 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008439 }
glennrp2b013e42010-11-24 16:55:50 +00008440
glennrpd6bf1612010-12-17 17:28:54 +00008441 if (logging != MagickFalse)
8442 {
8443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8444 " Number of colors: %.20g",(double) image_colors);
8445
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " Tentative PNG bit depth: %d",ping_bit_depth);
8448 }
8449
8450 if (ping_bit_depth < (int) mng_info->write_png_depth)
8451 ping_bit_depth = mng_info->write_png_depth;
8452 }
glennrp2cc891a2010-12-24 13:44:32 +00008453
glennrp5af765f2010-03-30 11:12:18 +00008454 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008455
cristy3ed852e2009-09-05 21:47:34 +00008456 if (logging != MagickFalse)
8457 {
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008459 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008460
cristy3ed852e2009-09-05 21:47:34 +00008461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008462 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008463
cristy3ed852e2009-09-05 21:47:34 +00008464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008465 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008466
cristy3ed852e2009-09-05 21:47:34 +00008467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008468
glennrp8640fb52010-11-23 15:48:26 +00008469 " image->depth: %.20g",(double) image->depth);
8470
8471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008472 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008473 }
8474
glennrp58e01762011-01-07 15:28:54 +00008475 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008476 {
glennrp4f25bd02011-01-01 18:51:28 +00008477 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008478 {
glennrp7c4c9e62011-03-21 20:23:32 +00008479 if (mng_info->write_png_colortype == 0)
8480 {
8481 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008482
glennrp7c4c9e62011-03-21 20:23:32 +00008483 if (ping_have_color != MagickFalse)
8484 ping_color_type=PNG_COLOR_TYPE_RGBA;
8485 }
glennrp4f25bd02011-01-01 18:51:28 +00008486
8487 /*
8488 * Determine if there is any transparent color.
8489 */
8490 if (number_transparent + number_semitransparent == 0)
8491 {
8492 /*
8493 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8494 */
glennrpa6a06632011-01-19 15:15:34 +00008495
glennrp4f25bd02011-01-01 18:51:28 +00008496 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008497
8498 if (mng_info->write_png_colortype == 0)
8499 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008500 }
8501
8502 else
8503 {
8504 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008505 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008506
8507 mask=0xffff;
8508
8509 if (ping_bit_depth == 8)
8510 mask=0x00ff;
8511
8512 if (ping_bit_depth == 4)
8513 mask=0x000f;
8514
8515 if (ping_bit_depth == 2)
8516 mask=0x0003;
8517
8518 if (ping_bit_depth == 1)
8519 mask=0x0001;
8520
8521 ping_trans_color.red=(png_uint_16)
8522 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8523
8524 ping_trans_color.green=(png_uint_16)
8525 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8526
8527 ping_trans_color.blue=(png_uint_16)
8528 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8529
8530 ping_trans_color.gray=(png_uint_16)
8531 (ScaleQuantumToShort(PixelIntensityToQuantum(
8532 image->colormap)) & mask);
8533
8534 ping_trans_color.index=(png_byte) 0;
8535
8536 ping_have_tRNS=MagickTrue;
8537 }
8538
8539 if (ping_have_tRNS != MagickFalse)
8540 {
8541 /*
glennrpfd05d622011-02-25 04:10:33 +00008542 * Determine if there is one and only one transparent color
8543 * and if so if it is fully transparent.
8544 */
8545 if (ping_have_cheap_transparency == MagickFalse)
8546 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008547 }
8548
8549 if (ping_have_tRNS != MagickFalse)
8550 {
glennrp7c4c9e62011-03-21 20:23:32 +00008551 if (mng_info->write_png_colortype == 0)
8552 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008553
8554 if (image_depth == 8)
8555 {
8556 ping_trans_color.red&=0xff;
8557 ping_trans_color.green&=0xff;
8558 ping_trans_color.blue&=0xff;
8559 ping_trans_color.gray&=0xff;
8560 }
8561 }
8562 }
cristy3ed852e2009-09-05 21:47:34 +00008563 else
8564 {
cristy3ed852e2009-09-05 21:47:34 +00008565 if (image_depth == 8)
8566 {
glennrp5af765f2010-03-30 11:12:18 +00008567 ping_trans_color.red&=0xff;
8568 ping_trans_color.green&=0xff;
8569 ping_trans_color.blue&=0xff;
8570 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008571 }
8572 }
8573 }
glennrp8640fb52010-11-23 15:48:26 +00008574
cristy3ed852e2009-09-05 21:47:34 +00008575 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008576
glennrp2e09f552010-11-14 00:38:48 +00008577 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008578 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008579
glennrp39992b42010-11-14 00:03:43 +00008580 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008581 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008582 ping_have_color == MagickFalse &&
8583 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008584 {
cristy35ef8242010-06-03 16:24:13 +00008585 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008586
cristy3ed852e2009-09-05 21:47:34 +00008587 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008588 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008589
glennrp7c4c9e62011-03-21 20:23:32 +00008590 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008591 {
glennrp5af765f2010-03-30 11:12:18 +00008592 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008593
cristy3ed852e2009-09-05 21:47:34 +00008594 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008595 {
8596 if (logging != MagickFalse)
8597 {
8598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8599 " Scaling ping_trans_color (0)");
8600 }
8601 ping_trans_color.gray*=0x0101;
8602 }
cristy3ed852e2009-09-05 21:47:34 +00008603 }
glennrp0fe50b42010-11-16 03:52:51 +00008604
cristy3ed852e2009-09-05 21:47:34 +00008605 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8606 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008607
glennrpa0ed0092011-04-18 16:36:29 +00008608 if ((image_colors == 0) || ((ssize_t) image_colors-1 > MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008609 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008610
cristy3ed852e2009-09-05 21:47:34 +00008611 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008612 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008613
cristy3ed852e2009-09-05 21:47:34 +00008614 else
8615 {
glennrp5af765f2010-03-30 11:12:18 +00008616 ping_bit_depth=8;
8617 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008618 {
8619 if(!mng_info->write_png_depth)
8620 {
glennrp5af765f2010-03-30 11:12:18 +00008621 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008622
cristy35ef8242010-06-03 16:24:13 +00008623 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008624 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008625 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008626 }
8627 }
glennrp2b013e42010-11-24 16:55:50 +00008628
glennrp0fe50b42010-11-16 03:52:51 +00008629 else if (ping_color_type ==
8630 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008631 mng_info->IsPalette)
8632 {
cristy3ed852e2009-09-05 21:47:34 +00008633 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008634
cristy3ed852e2009-09-05 21:47:34 +00008635 int
8636 depth_4_ok=MagickTrue,
8637 depth_2_ok=MagickTrue,
8638 depth_1_ok=MagickTrue;
8639
cristybb503372010-05-27 20:51:26 +00008640 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008641 {
8642 unsigned char
8643 intensity;
8644
8645 intensity=ScaleQuantumToChar(image->colormap[i].red);
8646
8647 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8648 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8649 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8650 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008651 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008652 depth_1_ok=MagickFalse;
8653 }
glennrp2b013e42010-11-24 16:55:50 +00008654
cristy3ed852e2009-09-05 21:47:34 +00008655 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008656 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008657
cristy3ed852e2009-09-05 21:47:34 +00008658 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008659 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008660
cristy3ed852e2009-09-05 21:47:34 +00008661 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008662 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008663 }
8664 }
glennrp2b013e42010-11-24 16:55:50 +00008665
glennrp5af765f2010-03-30 11:12:18 +00008666 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008667 }
glennrp0fe50b42010-11-16 03:52:51 +00008668
cristy3ed852e2009-09-05 21:47:34 +00008669 else
glennrp0fe50b42010-11-16 03:52:51 +00008670
cristy3ed852e2009-09-05 21:47:34 +00008671 if (mng_info->IsPalette)
8672 {
glennrp17a14852010-05-10 03:01:59 +00008673 number_colors=image_colors;
8674
cristy3ed852e2009-09-05 21:47:34 +00008675 if (image_depth <= 8)
8676 {
cristy3ed852e2009-09-05 21:47:34 +00008677 /*
8678 Set image palette.
8679 */
glennrp5af765f2010-03-30 11:12:18 +00008680 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008681
glennrp58e01762011-01-07 15:28:54 +00008682 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008683 {
glennrp9c1eb072010-06-06 22:19:15 +00008684 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008685
glennrp3b51f0e2010-11-27 18:14:08 +00008686 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8688 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008689 }
glennrp0fe50b42010-11-16 03:52:51 +00008690
cristy3ed852e2009-09-05 21:47:34 +00008691 else
8692 {
cristybb503372010-05-27 20:51:26 +00008693 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008694 {
8695 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8696 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8697 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8698 }
glennrp0fe50b42010-11-16 03:52:51 +00008699
glennrp3b51f0e2010-11-27 18:14:08 +00008700 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008702 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008703 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008704
glennrp39992b42010-11-14 00:03:43 +00008705 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008706 }
glennrp0fe50b42010-11-16 03:52:51 +00008707
cristy3ed852e2009-09-05 21:47:34 +00008708 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008709 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008710 {
cristybefe4d22010-06-07 01:18:58 +00008711 size_t
8712 one;
8713
glennrp5af765f2010-03-30 11:12:18 +00008714 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008715 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008716
cristybefe4d22010-06-07 01:18:58 +00008717 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008718 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008719 }
glennrp0fe50b42010-11-16 03:52:51 +00008720
glennrp5af765f2010-03-30 11:12:18 +00008721 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008722
glennrp58e01762011-01-07 15:28:54 +00008723 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008724 {
glennrp0fe50b42010-11-16 03:52:51 +00008725 /*
glennrpd6bf1612010-12-17 17:28:54 +00008726 * Set up trans_colors array.
8727 */
glennrp0fe50b42010-11-16 03:52:51 +00008728 assert(number_colors <= 256);
8729
glennrpd6bf1612010-12-17 17:28:54 +00008730 ping_num_trans=(unsigned short) (number_transparent +
8731 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008732
8733 if (ping_num_trans == 0)
8734 ping_have_tRNS=MagickFalse;
8735
glennrpd6bf1612010-12-17 17:28:54 +00008736 else
glennrp0fe50b42010-11-16 03:52:51 +00008737 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008738 if (logging != MagickFalse)
8739 {
8740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8741 " Scaling ping_trans_color (1)");
8742 }
glennrpd6bf1612010-12-17 17:28:54 +00008743 ping_have_tRNS=MagickTrue;
8744
8745 for (i=0; i < ping_num_trans; i++)
8746 {
8747 ping_trans_alpha[i]= (png_byte) (255-
8748 ScaleQuantumToChar(image->colormap[i].opacity));
8749 }
glennrp0fe50b42010-11-16 03:52:51 +00008750 }
8751 }
cristy3ed852e2009-09-05 21:47:34 +00008752 }
8753 }
glennrp0fe50b42010-11-16 03:52:51 +00008754
cristy3ed852e2009-09-05 21:47:34 +00008755 else
8756 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008757
cristy3ed852e2009-09-05 21:47:34 +00008758 if (image_depth < 8)
8759 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008760
cristy3ed852e2009-09-05 21:47:34 +00008761 if ((save_image_depth == 16) && (image_depth == 8))
8762 {
glennrp4f25bd02011-01-01 18:51:28 +00008763 if (logging != MagickFalse)
8764 {
8765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8766 " Scaling ping_trans_color from (%d,%d,%d)",
8767 (int) ping_trans_color.red,
8768 (int) ping_trans_color.green,
8769 (int) ping_trans_color.blue);
8770 }
8771
glennrp5af765f2010-03-30 11:12:18 +00008772 ping_trans_color.red*=0x0101;
8773 ping_trans_color.green*=0x0101;
8774 ping_trans_color.blue*=0x0101;
8775 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008776
8777 if (logging != MagickFalse)
8778 {
8779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8780 " to (%d,%d,%d)",
8781 (int) ping_trans_color.red,
8782 (int) ping_trans_color.green,
8783 (int) ping_trans_color.blue);
8784 }
cristy3ed852e2009-09-05 21:47:34 +00008785 }
8786 }
8787
cristy4383ec82011-01-05 15:42:32 +00008788 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8789 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008790
cristy3ed852e2009-09-05 21:47:34 +00008791 /*
8792 Adjust background and transparency samples in sub-8-bit grayscale files.
8793 */
glennrp5af765f2010-03-30 11:12:18 +00008794 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008795 PNG_COLOR_TYPE_GRAY)
8796 {
8797 png_uint_16
8798 maxval;
8799
cristy35ef8242010-06-03 16:24:13 +00008800 size_t
8801 one=1;
8802
cristy22ffd972010-06-03 16:51:47 +00008803 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008804
glennrp4f25bd02011-01-01 18:51:28 +00008805 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008806 {
cristy3ed852e2009-09-05 21:47:34 +00008807
glennrpa521b2f2010-10-29 04:11:03 +00008808 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008809 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8810
8811 if (logging != MagickFalse)
8812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008813 " Setting up bKGD chunk (2)");
glennrp3b51f0e2010-11-27 18:14:08 +00008814
glennrp991d11d2010-11-12 21:55:28 +00008815 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008816 }
cristy3ed852e2009-09-05 21:47:34 +00008817
glennrp5af765f2010-03-30 11:12:18 +00008818 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8819 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008820 }
glennrp17a14852010-05-10 03:01:59 +00008821
glennrp26f37912010-12-23 16:22:42 +00008822 if (ping_exclude_bKGD == MagickFalse)
8823 {
glennrp1273f7b2011-02-24 03:20:30 +00008824 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008825 {
8826 /*
8827 Identify which colormap entry is the background color.
8828 */
8829
glennrp17a14852010-05-10 03:01:59 +00008830 number_colors=image_colors;
8831
glennrpa521b2f2010-10-29 04:11:03 +00008832 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8833 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008834 break;
8835
8836 ping_background.index=(png_byte) i;
8837
glennrp3b51f0e2010-11-27 18:14:08 +00008838 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008839 {
8840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008841 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008842 }
glennrp0fe50b42010-11-16 03:52:51 +00008843
cristy13d07042010-11-21 20:56:18 +00008844 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008845 {
8846 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008847
8848 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008849 {
8850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8851 " background =(%d,%d,%d)",
8852 (int) ping_background.red,
8853 (int) ping_background.green,
8854 (int) ping_background.blue);
8855 }
8856 }
glennrpa521b2f2010-10-29 04:11:03 +00008857
glennrpd6bf1612010-12-17 17:28:54 +00008858 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008859 {
glennrp3b51f0e2010-11-27 18:14:08 +00008860 if (logging != MagickFalse)
8861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8862 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008863 ping_have_bKGD = MagickFalse;
8864 }
glennrp17a14852010-05-10 03:01:59 +00008865 }
glennrp26f37912010-12-23 16:22:42 +00008866 }
glennrp17a14852010-05-10 03:01:59 +00008867
cristy3ed852e2009-09-05 21:47:34 +00008868 if (logging != MagickFalse)
8869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00008870 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008871 /*
8872 Initialize compression level and filtering.
8873 */
8874 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008875 {
8876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8877 " Setting up deflate compression");
8878
8879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8880 " Compression buffer size: 32768");
8881 }
8882
cristy3ed852e2009-09-05 21:47:34 +00008883 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00008884
cristy3ed852e2009-09-05 21:47:34 +00008885 if (logging != MagickFalse)
8886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8887 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00008888
cristy3ed852e2009-09-05 21:47:34 +00008889 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00008890
cristy3ed852e2009-09-05 21:47:34 +00008891 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8892 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00008893
cristy3ed852e2009-09-05 21:47:34 +00008894 if (quality > 9)
8895 {
8896 int
8897 level;
8898
cristybb503372010-05-27 20:51:26 +00008899 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00008900
cristy3ed852e2009-09-05 21:47:34 +00008901 if (logging != MagickFalse)
8902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8903 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00008904
cristy3ed852e2009-09-05 21:47:34 +00008905 png_set_compression_level(ping,level);
8906 }
glennrp0fe50b42010-11-16 03:52:51 +00008907
cristy3ed852e2009-09-05 21:47:34 +00008908 else
8909 {
8910 if (logging != MagickFalse)
8911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8912 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00008913
cristy3ed852e2009-09-05 21:47:34 +00008914 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8915 }
glennrp0fe50b42010-11-16 03:52:51 +00008916
cristy3ed852e2009-09-05 21:47:34 +00008917 if (logging != MagickFalse)
8918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8919 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00008920
glennrp2b013e42010-11-24 16:55:50 +00008921#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00008922 /* This became available in libpng-1.0.9. Output must be a MNG. */
8923 if (mng_info->write_mng && ((quality % 10) == 7))
8924 {
8925 if (logging != MagickFalse)
8926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8927 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00008928
glennrp5af765f2010-03-30 11:12:18 +00008929 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00008930 }
glennrp0fe50b42010-11-16 03:52:51 +00008931
cristy3ed852e2009-09-05 21:47:34 +00008932 else
8933 if (logging != MagickFalse)
8934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8935 " Filter_type: 0");
8936#endif
glennrp2b013e42010-11-24 16:55:50 +00008937
cristy3ed852e2009-09-05 21:47:34 +00008938 {
8939 int
8940 base_filter;
8941
8942 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00008943 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00008944
glennrp26c990a2010-11-23 02:23:20 +00008945 else
glennrp8640fb52010-11-23 15:48:26 +00008946 if ((quality % 10) != 5)
8947 base_filter=(int) quality % 10;
8948
8949 else
8950 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8951 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8952 (quality < 50))
8953 base_filter=PNG_NO_FILTERS;
8954
8955 else
8956 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00008957
cristy3ed852e2009-09-05 21:47:34 +00008958 if (logging != MagickFalse)
8959 {
8960 if (base_filter == PNG_ALL_FILTERS)
8961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8962 " Base filter method: ADAPTIVE");
8963 else
8964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8965 " Base filter method: NONE");
8966 }
glennrp2b013e42010-11-24 16:55:50 +00008967
cristy3ed852e2009-09-05 21:47:34 +00008968 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8969 }
8970
glennrp823b55c2011-03-14 18:46:46 +00008971 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
8972 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00008973 {
8974 ResetImageProfileIterator(image);
8975 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00008976 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008977 profile=GetImageProfile(image,name);
8978
8979 if (profile != (StringInfo *) NULL)
8980 {
glennrp5af765f2010-03-30 11:12:18 +00008981#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00008982 if ((LocaleCompare(name,"ICC") == 0) ||
8983 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00008984 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008985
8986 if (ping_exclude_iCCP == MagickFalse)
8987 {
8988 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00008989#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00008990 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00008991#else
8992 (png_const_bytep) GetStringInfoDatum(profile),
8993#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00008994 (png_uint_32) GetStringInfoLength(profile));
8995 }
glennrp26f37912010-12-23 16:22:42 +00008996 }
glennrp0fe50b42010-11-16 03:52:51 +00008997
glennrpc8cbc5d2011-01-01 00:12:34 +00008998 else
cristy3ed852e2009-09-05 21:47:34 +00008999#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009000 if (ping_exclude_zCCP == MagickFalse)
9001 {
glennrpcf002022011-01-30 02:38:15 +00009002 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009003 (unsigned char *) name,(unsigned char *) name,
9004 GetStringInfoDatum(profile),
9005 (png_uint_32) GetStringInfoLength(profile));
9006 }
9007 }
glennrp0b206f52011-01-07 04:55:32 +00009008
glennrpc8cbc5d2011-01-01 00:12:34 +00009009 if (logging != MagickFalse)
9010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011 " Setting up text chunk with %s profile",name);
9012
9013 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009014 }
cristy3ed852e2009-09-05 21:47:34 +00009015 }
9016
9017#if defined(PNG_WRITE_sRGB_SUPPORTED)
9018 if ((mng_info->have_write_global_srgb == 0) &&
9019 ((image->rendering_intent != UndefinedIntent) ||
9020 (image->colorspace == sRGBColorspace)))
9021 {
glennrp26f37912010-12-23 16:22:42 +00009022 if (ping_exclude_sRGB == MagickFalse)
9023 {
9024 /*
9025 Note image rendering intent.
9026 */
9027 if (logging != MagickFalse)
9028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009030
glennrp26f37912010-12-23 16:22:42 +00009031 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009032 Magick_RenderingIntent_to_PNG_RenderingIntent(
9033 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009034
glennrp26f37912010-12-23 16:22:42 +00009035 if (ping_exclude_gAMA == MagickFalse)
9036 png_set_gAMA(ping,ping_info,0.45455);
9037 }
cristy3ed852e2009-09-05 21:47:34 +00009038 }
glennrp26f37912010-12-23 16:22:42 +00009039
glennrp5af765f2010-03-30 11:12:18 +00009040 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009041#endif
9042 {
glennrp2cc891a2010-12-24 13:44:32 +00009043 if (ping_exclude_gAMA == MagickFalse &&
9044 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009045 (image->gamma < .45 || image->gamma > .46)))
9046 {
cristy3ed852e2009-09-05 21:47:34 +00009047 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9048 {
9049 /*
9050 Note image gamma.
9051 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9052 */
9053 if (logging != MagickFalse)
9054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9055 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009056
cristy3ed852e2009-09-05 21:47:34 +00009057 png_set_gAMA(ping,ping_info,image->gamma);
9058 }
glennrp26f37912010-12-23 16:22:42 +00009059 }
glennrp2b013e42010-11-24 16:55:50 +00009060
glennrp26f37912010-12-23 16:22:42 +00009061 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009062 {
glennrp26f37912010-12-23 16:22:42 +00009063 if ((mng_info->have_write_global_chrm == 0) &&
9064 (image->chromaticity.red_primary.x != 0.0))
9065 {
9066 /*
9067 Note image chromaticity.
9068 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9069 */
9070 PrimaryInfo
9071 bp,
9072 gp,
9073 rp,
9074 wp;
cristy3ed852e2009-09-05 21:47:34 +00009075
glennrp26f37912010-12-23 16:22:42 +00009076 wp=image->chromaticity.white_point;
9077 rp=image->chromaticity.red_primary;
9078 gp=image->chromaticity.green_primary;
9079 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009080
glennrp26f37912010-12-23 16:22:42 +00009081 if (logging != MagickFalse)
9082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9083 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009084
glennrp26f37912010-12-23 16:22:42 +00009085 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9086 bp.x,bp.y);
9087 }
9088 }
cristy3ed852e2009-09-05 21:47:34 +00009089 }
glennrpdfd70802010-11-14 01:23:35 +00009090
glennrp5af765f2010-03-30 11:12:18 +00009091 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009092
9093 if (mng_info->write_mng)
9094 png_set_sig_bytes(ping,8);
9095
9096 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9097
glennrpd6bf1612010-12-17 17:28:54 +00009098 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009099 {
9100 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009101 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009102 {
glennrp5af765f2010-03-30 11:12:18 +00009103 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009104
glennrp5af765f2010-03-30 11:12:18 +00009105 if (ping_bit_depth < 8)
9106 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009107 }
glennrp0fe50b42010-11-16 03:52:51 +00009108
cristy3ed852e2009-09-05 21:47:34 +00009109 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009110 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009111 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009112 }
9113
glennrp0e8ea192010-12-24 18:00:33 +00009114 if (ping_need_colortype_warning != MagickFalse ||
9115 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009116 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009117 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009118 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009119 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009120 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009121 {
9122 if (logging != MagickFalse)
9123 {
glennrp0e8ea192010-12-24 18:00:33 +00009124 if (ping_need_colortype_warning != MagickFalse)
9125 {
9126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9127 " Image has transparency but tRNS chunk was excluded");
9128 }
9129
cristy3ed852e2009-09-05 21:47:34 +00009130 if (mng_info->write_png_depth)
9131 {
9132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9133 " Defined PNG:bit-depth=%u, Computed depth=%u",
9134 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009135 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009136 }
glennrp0e8ea192010-12-24 18:00:33 +00009137
cristy3ed852e2009-09-05 21:47:34 +00009138 if (mng_info->write_png_colortype)
9139 {
9140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9141 " Defined PNG:color-type=%u, Computed color type=%u",
9142 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009143 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009144 }
9145 }
glennrp0e8ea192010-12-24 18:00:33 +00009146
glennrp3bd2e412010-08-10 13:34:52 +00009147 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009148 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9149 }
9150
glennrp58e01762011-01-07 15:28:54 +00009151 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009152 {
9153 /* Add an opaque matte channel */
9154 image->matte = MagickTrue;
9155 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009156
glennrpb4a13412010-05-05 12:47:19 +00009157 if (logging != MagickFalse)
9158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9159 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009160 }
9161
glennrp0e319732011-01-25 21:53:13 +00009162 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009163 {
glennrp991d11d2010-11-12 21:55:28 +00009164 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009165 {
glennrp991d11d2010-11-12 21:55:28 +00009166 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009167 if (logging != MagickFalse)
9168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9169 " Setting ping_have_tRNS=MagickTrue.");
9170 }
glennrpe9c26dc2010-05-30 01:56:35 +00009171 }
9172
cristy3ed852e2009-09-05 21:47:34 +00009173 if (logging != MagickFalse)
9174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9175 " Writing PNG header chunks");
9176
glennrp5af765f2010-03-30 11:12:18 +00009177 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9178 ping_bit_depth,ping_color_type,
9179 ping_interlace_method,ping_compression_method,
9180 ping_filter_method);
9181
glennrp39992b42010-11-14 00:03:43 +00009182 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9183 {
glennrpf09bded2011-01-08 01:15:59 +00009184 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009185
glennrp3b51f0e2010-11-27 18:14:08 +00009186 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009187 {
glennrp8640fb52010-11-23 15:48:26 +00009188 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009189 {
glennrpd6bf1612010-12-17 17:28:54 +00009190 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009192 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9193 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009194 (int) palette[i].red,
9195 (int) palette[i].green,
9196 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009197 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009198 (int) ping_trans_alpha[i]);
9199 else
9200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009201 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009202 (int) i,
9203 (int) palette[i].red,
9204 (int) palette[i].green,
9205 (int) palette[i].blue);
9206 }
glennrp39992b42010-11-14 00:03:43 +00009207 }
glennrp39992b42010-11-14 00:03:43 +00009208 }
9209
glennrp26f37912010-12-23 16:22:42 +00009210 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009211 {
glennrp26f37912010-12-23 16:22:42 +00009212 if (ping_have_bKGD != MagickFalse)
9213 png_set_bKGD(ping,ping_info,&ping_background);
9214 }
9215
9216 if (ping_exclude_pHYs == MagickFalse)
9217 {
9218 if (ping_have_pHYs != MagickFalse)
9219 {
9220 png_set_pHYs(ping,ping_info,
9221 ping_pHYs_x_resolution,
9222 ping_pHYs_y_resolution,
9223 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009224
9225 if (logging)
9226 {
9227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9228 " Setting up pHYs chunk");
9229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9230 " x_resolution=%lu",
9231 (unsigned long) ping_pHYs_x_resolution);
9232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9233 " y_resolution=%lu",
9234 (unsigned long) ping_pHYs_y_resolution);
9235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9236 " unit_type=%lu",
9237 (unsigned long) ping_pHYs_unit_type);
9238 }
glennrp26f37912010-12-23 16:22:42 +00009239 }
glennrpdfd70802010-11-14 01:23:35 +00009240 }
9241
9242#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009243 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009244 {
glennrp26f37912010-12-23 16:22:42 +00009245 if (image->page.x || image->page.y)
9246 {
9247 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9248 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009249
glennrp26f37912010-12-23 16:22:42 +00009250 if (logging != MagickFalse)
9251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9252 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9253 (int) image->page.x, (int) image->page.y);
9254 }
glennrpdfd70802010-11-14 01:23:35 +00009255 }
9256#endif
9257
glennrpda8f3a72011-02-27 23:54:12 +00009258 if (mng_info->need_blob != MagickFalse)
9259 {
9260 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9261 MagickFalse)
9262 png_error(ping,"WriteBlob Failed");
9263
9264 ping_have_blob=MagickTrue;
9265 }
9266
cristy3ed852e2009-09-05 21:47:34 +00009267 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009268
glennrp39992b42010-11-14 00:03:43 +00009269 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009270 {
glennrp3b51f0e2010-11-27 18:14:08 +00009271 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009272 {
9273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9274 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9275 }
9276
9277 if (ping_color_type == 3)
9278 (void) png_set_tRNS(ping, ping_info,
9279 ping_trans_alpha,
9280 ping_num_trans,
9281 NULL);
9282
9283 else
9284 {
9285 (void) png_set_tRNS(ping, ping_info,
9286 NULL,
9287 0,
9288 &ping_trans_color);
9289
glennrp3b51f0e2010-11-27 18:14:08 +00009290 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009291 {
9292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009293 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009294 (int) ping_trans_color.red,
9295 (int) ping_trans_color.green,
9296 (int) ping_trans_color.blue);
9297 }
9298 }
glennrp991d11d2010-11-12 21:55:28 +00009299 }
9300
cristy3ed852e2009-09-05 21:47:34 +00009301 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009302 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009303
cristy3ed852e2009-09-05 21:47:34 +00009304 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009305
cristy3ed852e2009-09-05 21:47:34 +00009306 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009307 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009308
glennrp26f37912010-12-23 16:22:42 +00009309 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009310 {
glennrp4f25bd02011-01-01 18:51:28 +00009311 if ((image->page.width != 0 && image->page.width != image->columns) ||
9312 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009313 {
9314 unsigned char
9315 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009316
glennrp26f37912010-12-23 16:22:42 +00009317 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9318 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009319 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009320 PNGLong(chunk+4,(png_uint_32) image->page.width);
9321 PNGLong(chunk+8,(png_uint_32) image->page.height);
9322 chunk[12]=0; /* unit = pixels */
9323 (void) WriteBlob(image,13,chunk);
9324 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9325 }
cristy3ed852e2009-09-05 21:47:34 +00009326 }
9327
9328#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009329 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009330#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009331 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009332#undef PNG_HAVE_IDAT
9333#endif
9334
9335 png_set_packing(ping);
9336 /*
9337 Allocate memory.
9338 */
9339 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009340 if (image_depth > 8)
9341 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009342 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009343 {
glennrpb4a13412010-05-05 12:47:19 +00009344 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009345 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009346 break;
glennrp0fe50b42010-11-16 03:52:51 +00009347
glennrpb4a13412010-05-05 12:47:19 +00009348 case PNG_COLOR_TYPE_GRAY_ALPHA:
9349 rowbytes*=2;
9350 break;
glennrp0fe50b42010-11-16 03:52:51 +00009351
glennrpb4a13412010-05-05 12:47:19 +00009352 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009353 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009354 break;
glennrp0fe50b42010-11-16 03:52:51 +00009355
glennrpb4a13412010-05-05 12:47:19 +00009356 default:
9357 break;
cristy3ed852e2009-09-05 21:47:34 +00009358 }
glennrp3b51f0e2010-11-27 18:14:08 +00009359
9360 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009361 {
9362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9363 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009364
glennrpb4a13412010-05-05 12:47:19 +00009365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009366 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009367 }
glennrpcf002022011-01-30 02:38:15 +00009368 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9369 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009370
glennrpcf002022011-01-30 02:38:15 +00009371 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009372 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009373
cristy3ed852e2009-09-05 21:47:34 +00009374 /*
9375 Initialize image scanlines.
9376 */
glennrp5af765f2010-03-30 11:12:18 +00009377 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009378 {
9379 /*
9380 PNG write failed.
9381 */
9382#ifdef PNG_DEBUG
9383 if (image_info->verbose)
9384 (void) printf("PNG write has failed.\n");
9385#endif
9386 png_destroy_write_struct(&ping,&ping_info);
9387 if (quantum_info != (QuantumInfo *) NULL)
9388 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009389 if (ping_pixels != (unsigned char *) NULL)
9390 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009391#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009392 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009393#endif
glennrpda8f3a72011-02-27 23:54:12 +00009394 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009395 (void) CloseBlob(image);
9396 image_info=DestroyImageInfo(image_info);
9397 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009398 return(MagickFalse);
9399 }
cristyed552522009-10-16 14:04:35 +00009400 quantum_info=AcquireQuantumInfo(image_info,image);
9401 if (quantum_info == (QuantumInfo *) NULL)
9402 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009403 quantum_info->format=UndefinedQuantumFormat;
9404 quantum_info->depth=image_depth;
9405 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009406
cristy3ed852e2009-09-05 21:47:34 +00009407 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009408 !mng_info->write_png32) &&
9409 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009410 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009411 image_matte == MagickFalse &&
9412 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009413 {
glennrp8bb3a022010-12-13 20:40:04 +00009414 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009415 register const PixelPacket
9416 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009417
cristy3ed852e2009-09-05 21:47:34 +00009418 quantum_info->depth=8;
9419 for (pass=0; pass < num_passes; pass++)
9420 {
9421 /*
9422 Convert PseudoClass image to a PNG monochrome image.
9423 */
cristybb503372010-05-27 20:51:26 +00009424 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009425 {
glennrpd71e86a2011-02-24 01:28:37 +00009426 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9428 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009429
cristy3ed852e2009-09-05 21:47:34 +00009430 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009431
cristy3ed852e2009-09-05 21:47:34 +00009432 if (p == (const PixelPacket *) NULL)
9433 break;
glennrp0fe50b42010-11-16 03:52:51 +00009434
cristy3ed852e2009-09-05 21:47:34 +00009435 if (mng_info->IsPalette)
9436 {
9437 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009438 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009439 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9440 mng_info->write_png_depth &&
9441 mng_info->write_png_depth != old_bit_depth)
9442 {
9443 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009444 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009445 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009446 >> (8-old_bit_depth));
9447 }
9448 }
glennrp0fe50b42010-11-16 03:52:51 +00009449
cristy3ed852e2009-09-05 21:47:34 +00009450 else
9451 {
9452 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009453 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009454 }
glennrp0fe50b42010-11-16 03:52:51 +00009455
cristy3ed852e2009-09-05 21:47:34 +00009456 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009457 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009458 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009459 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009460
glennrp3b51f0e2010-11-27 18:14:08 +00009461 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9463 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009464
glennrpcf002022011-01-30 02:38:15 +00009465 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009466 }
9467 if (image->previous == (Image *) NULL)
9468 {
9469 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9470 if (status == MagickFalse)
9471 break;
9472 }
9473 }
9474 }
glennrp0fe50b42010-11-16 03:52:51 +00009475
glennrp8bb3a022010-12-13 20:40:04 +00009476 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009477 {
glennrp0fe50b42010-11-16 03:52:51 +00009478 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009479 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009480 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009481 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009482 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009483 {
glennrp8bb3a022010-12-13 20:40:04 +00009484 register const PixelPacket
9485 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009486
glennrp8bb3a022010-12-13 20:40:04 +00009487 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009488 {
glennrp8bb3a022010-12-13 20:40:04 +00009489
cristybb503372010-05-27 20:51:26 +00009490 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009491 {
9492 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009493
cristy3ed852e2009-09-05 21:47:34 +00009494 if (p == (const PixelPacket *) NULL)
9495 break;
glennrp2cc891a2010-12-24 13:44:32 +00009496
glennrp5af765f2010-03-30 11:12:18 +00009497 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009498 {
glennrp8bb3a022010-12-13 20:40:04 +00009499 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009500 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009501 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009502
glennrp8bb3a022010-12-13 20:40:04 +00009503 else
9504 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009505 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009506
glennrp3b51f0e2010-11-27 18:14:08 +00009507 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009509 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009510 }
glennrp2cc891a2010-12-24 13:44:32 +00009511
glennrp8bb3a022010-12-13 20:40:04 +00009512 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9513 {
9514 if (logging != MagickFalse && y == 0)
9515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9516 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009517
glennrp8bb3a022010-12-13 20:40:04 +00009518 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009519 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009520 }
glennrp2cc891a2010-12-24 13:44:32 +00009521
glennrp3b51f0e2010-11-27 18:14:08 +00009522 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009524 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009525
glennrpcf002022011-01-30 02:38:15 +00009526 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009527 }
glennrp2cc891a2010-12-24 13:44:32 +00009528
glennrp8bb3a022010-12-13 20:40:04 +00009529 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009530 {
glennrp8bb3a022010-12-13 20:40:04 +00009531 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9532 if (status == MagickFalse)
9533 break;
cristy3ed852e2009-09-05 21:47:34 +00009534 }
cristy3ed852e2009-09-05 21:47:34 +00009535 }
9536 }
glennrp8bb3a022010-12-13 20:40:04 +00009537
9538 else
9539 {
9540 register const PixelPacket
9541 *p;
9542
9543 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009544 {
glennrp8bb3a022010-12-13 20:40:04 +00009545 if ((image_depth > 8) || (mng_info->write_png24 ||
9546 mng_info->write_png32 ||
9547 (!mng_info->write_png8 && !mng_info->IsPalette)))
9548 {
9549 for (y=0; y < (ssize_t) image->rows; y++)
9550 {
9551 p=GetVirtualPixels(image,0,y,image->columns,1,
9552 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009553
glennrp8bb3a022010-12-13 20:40:04 +00009554 if (p == (const PixelPacket *) NULL)
9555 break;
glennrp2cc891a2010-12-24 13:44:32 +00009556
glennrp8bb3a022010-12-13 20:40:04 +00009557 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9558 {
9559 if (image->storage_class == DirectClass)
9560 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009561 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009562
glennrp8bb3a022010-12-13 20:40:04 +00009563 else
9564 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009565 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009566 }
glennrp2cc891a2010-12-24 13:44:32 +00009567
glennrp8bb3a022010-12-13 20:40:04 +00009568 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9569 {
9570 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009571 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009572 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009573
glennrp8bb3a022010-12-13 20:40:04 +00009574 if (logging != MagickFalse && y == 0)
9575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9576 " Writing GRAY_ALPHA PNG pixels (3)");
9577 }
glennrp2cc891a2010-12-24 13:44:32 +00009578
glennrp8bb3a022010-12-13 20:40:04 +00009579 else if (image_matte != MagickFalse)
9580 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009581 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009582
glennrp8bb3a022010-12-13 20:40:04 +00009583 else
9584 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009585 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009586
glennrp8bb3a022010-12-13 20:40:04 +00009587 if (logging != MagickFalse && y == 0)
9588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9589 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009590
glennrpcf002022011-01-30 02:38:15 +00009591 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009592 }
9593 }
glennrp2cc891a2010-12-24 13:44:32 +00009594
glennrp8bb3a022010-12-13 20:40:04 +00009595 else
9596 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9597 mng_info->write_png32 ||
9598 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9599 {
9600 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9601 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9602 {
9603 if (logging != MagickFalse)
9604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9605 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009606
glennrp8bb3a022010-12-13 20:40:04 +00009607 quantum_info->depth=8;
9608 image_depth=8;
9609 }
glennrp2cc891a2010-12-24 13:44:32 +00009610
glennrp8bb3a022010-12-13 20:40:04 +00009611 for (y=0; y < (ssize_t) image->rows; y++)
9612 {
9613 if (logging != MagickFalse && y == 0)
9614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9615 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009616
glennrp770d1932011-03-06 22:11:17 +00009617 p=GetVirtualPixels(image,0,y,image->columns,1,
9618 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009619
glennrp8bb3a022010-12-13 20:40:04 +00009620 if (p == (const PixelPacket *) NULL)
9621 break;
glennrp2cc891a2010-12-24 13:44:32 +00009622
glennrp8bb3a022010-12-13 20:40:04 +00009623 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009624 {
glennrp4bf89732011-03-21 13:48:28 +00009625 quantum_info->depth=image->depth;
9626
glennrp44757ab2011-03-17 12:57:03 +00009627 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009628 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009629 }
glennrp2cc891a2010-12-24 13:44:32 +00009630
glennrp8bb3a022010-12-13 20:40:04 +00009631 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9632 {
9633 if (logging != MagickFalse && y == 0)
9634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9635 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009636
glennrp8bb3a022010-12-13 20:40:04 +00009637 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009638 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009639 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009640 }
glennrp2cc891a2010-12-24 13:44:32 +00009641
glennrp8bb3a022010-12-13 20:40:04 +00009642 else
glennrp8bb3a022010-12-13 20:40:04 +00009643 {
glennrp179d0752011-03-17 13:02:10 +00009644 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009645 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9646
9647 if (logging != MagickFalse && y <= 2)
9648 {
9649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009650 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009651
9652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9653 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9654 (int)ping_pixels[0],(int)ping_pixels[1]);
9655 }
glennrp8bb3a022010-12-13 20:40:04 +00009656 }
glennrpcf002022011-01-30 02:38:15 +00009657 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009658 }
9659 }
glennrp2cc891a2010-12-24 13:44:32 +00009660
glennrp8bb3a022010-12-13 20:40:04 +00009661 if (image->previous == (Image *) NULL)
9662 {
9663 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9664 if (status == MagickFalse)
9665 break;
9666 }
cristy3ed852e2009-09-05 21:47:34 +00009667 }
glennrp8bb3a022010-12-13 20:40:04 +00009668 }
9669 }
9670
cristyb32b90a2009-09-07 21:45:48 +00009671 if (quantum_info != (QuantumInfo *) NULL)
9672 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009673
9674 if (logging != MagickFalse)
9675 {
9676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009677 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009678
cristy3ed852e2009-09-05 21:47:34 +00009679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009680 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009681
cristy3ed852e2009-09-05 21:47:34 +00009682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009683 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009684
cristy3ed852e2009-09-05 21:47:34 +00009685 if (mng_info->write_png_depth)
9686 {
9687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9688 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9689 }
glennrp0fe50b42010-11-16 03:52:51 +00009690
cristy3ed852e2009-09-05 21:47:34 +00009691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009692 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009693
cristy3ed852e2009-09-05 21:47:34 +00009694 if (mng_info->write_png_colortype)
9695 {
9696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9697 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9698 }
glennrp0fe50b42010-11-16 03:52:51 +00009699
cristy3ed852e2009-09-05 21:47:34 +00009700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009701 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009702
cristy3ed852e2009-09-05 21:47:34 +00009703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009704 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009705 }
9706 /*
glennrpa0ed0092011-04-18 16:36:29 +00009707 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009708 */
glennrp823b55c2011-03-14 18:46:46 +00009709 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009710 {
glennrp26f37912010-12-23 16:22:42 +00009711 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009712 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009713 while (property != (const char *) NULL)
9714 {
9715 png_textp
9716 text;
glennrp2cc891a2010-12-24 13:44:32 +00009717
glennrp26f37912010-12-23 16:22:42 +00009718 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009719
9720 /* Don't write any "png:" properties; those are just for "identify" */
9721 if (LocaleNCompare(property,"png:",4) != 0 &&
9722
9723 /* Suppress density and units if we wrote a pHYs chunk */
9724 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009725 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009726 LocaleCompare(property,"units") != 0) &&
9727
9728 /* Suppress the IM-generated Date:create and Date:modify */
9729 (ping_exclude_date == MagickFalse ||
9730 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009731 {
glennrpc70af4a2011-03-07 00:08:23 +00009732 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009733 {
glennrpc70af4a2011-03-07 00:08:23 +00009734 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9735 text[0].key=(char *) property;
9736 text[0].text=(char *) value;
9737 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009738
glennrpc70af4a2011-03-07 00:08:23 +00009739 if (ping_exclude_tEXt != MagickFalse)
9740 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9741
9742 else if (ping_exclude_zTXt != MagickFalse)
9743 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9744
9745 else
glennrp26f37912010-12-23 16:22:42 +00009746 {
glennrpc70af4a2011-03-07 00:08:23 +00009747 text[0].compression=image_info->compression == NoCompression ||
9748 (image_info->compression == UndefinedCompression &&
9749 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9750 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009751 }
glennrp2cc891a2010-12-24 13:44:32 +00009752
glennrpc70af4a2011-03-07 00:08:23 +00009753 if (logging != MagickFalse)
9754 {
9755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9756 " Setting up text chunk");
9757
9758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9759 " keyword: %s",text[0].key);
9760 }
9761
9762 png_set_text(ping,ping_info,text,1);
9763 png_free(ping,text);
9764 }
glennrp26f37912010-12-23 16:22:42 +00009765 }
9766 property=GetNextImageProperty(image);
9767 }
cristy3ed852e2009-09-05 21:47:34 +00009768 }
9769
9770 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009771 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009772
9773 if (logging != MagickFalse)
9774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9775 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009776
cristy3ed852e2009-09-05 21:47:34 +00009777 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009778
cristy3ed852e2009-09-05 21:47:34 +00009779 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9780 {
9781 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009782 (ping_width != mng_info->page.width) ||
9783 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009784 {
9785 unsigned char
9786 chunk[32];
9787
9788 /*
9789 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9790 */
9791 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9792 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009793 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009794 chunk[4]=4;
9795 chunk[5]=0; /* frame name separator (no name) */
9796 chunk[6]=1; /* flag for changing delay, for next frame only */
9797 chunk[7]=0; /* flag for changing frame timeout */
9798 chunk[8]=1; /* flag for changing frame clipping for next frame */
9799 chunk[9]=0; /* flag for changing frame sync_id */
9800 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9801 chunk[14]=0; /* clipping boundaries delta type */
9802 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9803 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009804 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009805 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9806 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009807 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009808 (void) WriteBlob(image,31,chunk);
9809 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9810 mng_info->old_framing_mode=4;
9811 mng_info->framing_mode=1;
9812 }
glennrp0fe50b42010-11-16 03:52:51 +00009813
cristy3ed852e2009-09-05 21:47:34 +00009814 else
9815 mng_info->framing_mode=3;
9816 }
9817 if (mng_info->write_mng && !mng_info->need_fram &&
9818 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009819 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009820 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009821 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009822
cristy3ed852e2009-09-05 21:47:34 +00009823 /*
9824 Free PNG resources.
9825 */
glennrp5af765f2010-03-30 11:12:18 +00009826
cristy3ed852e2009-09-05 21:47:34 +00009827 png_destroy_write_struct(&ping,&ping_info);
9828
glennrpcf002022011-01-30 02:38:15 +00009829 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009830
9831#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009832 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009833#endif
9834
glennrpda8f3a72011-02-27 23:54:12 +00009835 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009836 (void) CloseBlob(image);
9837
9838 image_info=DestroyImageInfo(image_info);
9839 image=DestroyImage(image);
9840
9841 /* Store bit depth actually written */
9842 s[0]=(char) ping_bit_depth;
9843 s[1]='\0';
9844
9845 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9846
cristy3ed852e2009-09-05 21:47:34 +00009847 if (logging != MagickFalse)
9848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9849 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009850
cristy3ed852e2009-09-05 21:47:34 +00009851 return(MagickTrue);
9852/* End write one PNG image */
9853}
9854
9855/*
9856%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9857% %
9858% %
9859% %
9860% W r i t e P N G I m a g e %
9861% %
9862% %
9863% %
9864%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9865%
9866% WritePNGImage() writes a Portable Network Graphics (PNG) or
9867% Multiple-image Network Graphics (MNG) image file.
9868%
9869% MNG support written by Glenn Randers-Pehrson, glennrp@image...
9870%
9871% The format of the WritePNGImage method is:
9872%
9873% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9874%
9875% A description of each parameter follows:
9876%
9877% o image_info: the image info.
9878%
9879% o image: The image.
9880%
9881% Returns MagickTrue on success, MagickFalse on failure.
9882%
9883% Communicating with the PNG encoder:
9884%
9885% While the datastream written is always in PNG format and normally would
9886% be given the "png" file extension, this method also writes the following
9887% pseudo-formats which are subsets of PNG:
9888%
glennrp5a39f372011-02-25 04:52:16 +00009889% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
9890% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +00009891% is present, the tRNS chunk must only have values 0 and 255
9892% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +00009893% transparent). If other values are present they will be
9894% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +00009895% colors are present, they will be quantized to the 4-4-4-1,
9896% 3-3-3-1, or 3-3-2-1 palette.
9897%
9898% If you want better quantization or dithering of the colors
9899% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +00009900% PNG encoder. The pixels contain 8-bit indices even if
9901% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +00009902% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +00009903% PNG grayscale type might be slightly more efficient. Please
9904% note that writing to the PNG8 format may result in loss
9905% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +00009906%
9907% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9908% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +00009909% one of the colors as transparent. The only loss incurred
9910% is reduction of sample depth to 8. If the image has more
9911% than one transparent color, has semitransparent pixels, or
9912% has an opaque pixel with the same RGB components as the
9913% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +00009914%
9915% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
9916% transparency is permitted, i.e., the alpha sample for
9917% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +00009918% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +00009919% The only loss in data is the reduction of the sample depth
9920% to 8.
cristy3ed852e2009-09-05 21:47:34 +00009921%
9922% o -define: For more precise control of the PNG output, you can use the
9923% Image options "png:bit-depth" and "png:color-type". These
9924% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +00009925% from the application programming interfaces. The options
9926% are case-independent and are converted to lowercase before
9927% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +00009928%
9929% png:color-type can be 0, 2, 3, 4, or 6.
9930%
9931% When png:color-type is 0 (Grayscale), png:bit-depth can
9932% be 1, 2, 4, 8, or 16.
9933%
9934% When png:color-type is 2 (RGB), png:bit-depth can
9935% be 8 or 16.
9936%
9937% When png:color-type is 3 (Indexed), png:bit-depth can
9938% be 1, 2, 4, or 8. This refers to the number of bits
9939% used to store the index. The color samples always have
9940% bit-depth 8 in indexed PNG files.
9941%
9942% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9943% png:bit-depth can be 8 or 16.
9944%
glennrp5a39f372011-02-25 04:52:16 +00009945% If the image cannot be written without loss with the requested bit-depth
9946% and color-type, a PNG file will not be written, and the encoder will
9947% return MagickFalse.
9948%
cristy3ed852e2009-09-05 21:47:34 +00009949% Since image encoders should not be responsible for the "heavy lifting",
9950% the user should make sure that ImageMagick has already reduced the
9951% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +00009952% transparency prior to attempting to write the image with depth, color,
9953% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +00009954%
glennrp97cefe22011-04-22 16:17:00 +00009955% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +00009956%
cristy3ed852e2009-09-05 21:47:34 +00009957% Note that another definition, "png:bit-depth-written" exists, but it
9958% is not intended for external use. It is only used internally by the
9959% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9960%
9961% It is possible to request that the PNG encoder write previously-formatted
9962% ancillary chunks in the output PNG file, using the "-profile" commandline
9963% option as shown below or by setting the profile via a programming
9964% interface:
9965%
9966% -profile PNG-chunk-x:<file>
9967%
9968% where x is a location flag and <file> is a file containing the chunk
9969% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +00009970% This encoder will compute the chunk length and CRC, so those must not
9971% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +00009972%
9973% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9974% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
9975% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +00009976% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +00009977%
glennrpbb8a7332010-11-13 15:17:35 +00009978% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +00009979%
glennrp3241bd02010-12-12 04:36:28 +00009980% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +00009981%
glennrpd6afd542010-11-19 01:53:05 +00009982% o 32-bit depth is reduced to 16.
9983% o 16-bit depth is reduced to 8 if all pixels contain samples whose
9984% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +00009985% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +00009986% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +00009987% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +00009988% o Grayscale images are reduced to 1, 2, or 4 bit depth if
9989% this can be done without loss and a larger bit depth N was not
9990% requested via the "-define PNG:bit-depth=N" option.
9991% o If matte channel is present but only one transparent color is
9992% present, RGB+tRNS is written instead of RGBA
9993% o Opaque matte channel is removed (or added, if color-type 4 or 6
9994% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +00009995%
cristy3ed852e2009-09-05 21:47:34 +00009996%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9997*/
9998static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9999 Image *image)
10000{
10001 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010002 excluding,
10003 logging,
10004 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010005 status;
10006
10007 MngInfo
10008 *mng_info;
10009
10010 const char
10011 *value;
10012
10013 int
glennrp21f0e622011-01-07 16:20:57 +000010014 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010015 source;
10016
cristy3ed852e2009-09-05 21:47:34 +000010017 /*
10018 Open image file.
10019 */
10020 assert(image_info != (const ImageInfo *) NULL);
10021 assert(image_info->signature == MagickSignature);
10022 assert(image != (Image *) NULL);
10023 assert(image->signature == MagickSignature);
10024 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010025 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010026 /*
10027 Allocate a MngInfo structure.
10028 */
10029 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010030 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010031
cristy3ed852e2009-09-05 21:47:34 +000010032 if (mng_info == (MngInfo *) NULL)
10033 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010034
cristy3ed852e2009-09-05 21:47:34 +000010035 /*
10036 Initialize members of the MngInfo structure.
10037 */
10038 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10039 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010040 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010041 have_mng_structure=MagickTrue;
10042
10043 /* See if user has requested a specific PNG subformat */
10044
10045 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10046 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10047 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10048
10049 if (mng_info->write_png8)
10050 {
glennrp9c1eb072010-06-06 22:19:15 +000010051 mng_info->write_png_colortype = /* 3 */ 4;
10052 mng_info->write_png_depth = 8;
10053 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010054 }
10055
10056 if (mng_info->write_png24)
10057 {
glennrp9c1eb072010-06-06 22:19:15 +000010058 mng_info->write_png_colortype = /* 2 */ 3;
10059 mng_info->write_png_depth = 8;
10060 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010061
glennrp9c1eb072010-06-06 22:19:15 +000010062 if (image->matte == MagickTrue)
10063 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010064
glennrp9c1eb072010-06-06 22:19:15 +000010065 else
10066 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010067
glennrp9c1eb072010-06-06 22:19:15 +000010068 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010069 }
10070
10071 if (mng_info->write_png32)
10072 {
glennrp9c1eb072010-06-06 22:19:15 +000010073 mng_info->write_png_colortype = /* 6 */ 7;
10074 mng_info->write_png_depth = 8;
10075 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010076
glennrp9c1eb072010-06-06 22:19:15 +000010077 if (image->matte == MagickTrue)
10078 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010079
glennrp9c1eb072010-06-06 22:19:15 +000010080 else
10081 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010082
glennrp9c1eb072010-06-06 22:19:15 +000010083 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010084 }
10085
10086 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010087
cristy3ed852e2009-09-05 21:47:34 +000010088 if (value != (char *) NULL)
10089 {
10090 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010091 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010092
cristy3ed852e2009-09-05 21:47:34 +000010093 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010094 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010095
cristy3ed852e2009-09-05 21:47:34 +000010096 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010097 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010098
cristy3ed852e2009-09-05 21:47:34 +000010099 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010100 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010101
cristy3ed852e2009-09-05 21:47:34 +000010102 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010103 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010104
glennrpbb8a7332010-11-13 15:17:35 +000010105 else
10106 (void) ThrowMagickException(&image->exception,
10107 GetMagickModule(),CoderWarning,
10108 "ignoring invalid defined png:bit-depth",
10109 "=%s",value);
10110
cristy3ed852e2009-09-05 21:47:34 +000010111 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010113 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010114 }
glennrp0fe50b42010-11-16 03:52:51 +000010115
cristy3ed852e2009-09-05 21:47:34 +000010116 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010117
cristy3ed852e2009-09-05 21:47:34 +000010118 if (value != (char *) NULL)
10119 {
10120 /* We must store colortype+1 because 0 is a valid colortype */
10121 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010122 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010123
cristy3ed852e2009-09-05 21:47:34 +000010124 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010125 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010126
cristy3ed852e2009-09-05 21:47:34 +000010127 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010128 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010129
cristy3ed852e2009-09-05 21:47:34 +000010130 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010131 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010132
cristy3ed852e2009-09-05 21:47:34 +000010133 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010134 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010135
glennrpbb8a7332010-11-13 15:17:35 +000010136 else
10137 (void) ThrowMagickException(&image->exception,
10138 GetMagickModule(),CoderWarning,
10139 "ignoring invalid defined png:color-type",
10140 "=%s",value);
10141
cristy3ed852e2009-09-05 21:47:34 +000010142 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010144 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010145 }
10146
glennrp0e8ea192010-12-24 18:00:33 +000010147 /* Check for chunks to be excluded:
10148 *
glennrp0dff56c2011-01-29 19:10:02 +000010149 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010150 * listed in the "unused_chunks" array, above.
10151 *
10152 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10153 * define (in the image properties or in the image artifacts)
10154 * or via a mng_info member. For convenience, in addition
10155 * to or instead of a comma-separated list of chunks, the
10156 * "exclude-chunk" string can be simply "all" or "none".
10157 *
10158 * The exclude-chunk define takes priority over the mng_info.
10159 *
10160 * A "PNG:include-chunk" define takes priority over both the
10161 * mng_info and the "PNG:exclude-chunk" define. Like the
10162 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010163 * well as a comma-separated list. Chunks that are unknown to
10164 * ImageMagick are always excluded, regardless of their "copy-safe"
10165 * status according to the PNG specification, and even if they
10166 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010167 *
10168 * Finally, all chunks listed in the "unused_chunks" array are
10169 * automatically excluded, regardless of the other instructions
10170 * or lack thereof.
10171 *
10172 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10173 * will not be written and the gAMA chunk will only be written if it
10174 * is not between .45 and .46, or approximately (1.0/2.2).
10175 *
10176 * If you exclude tRNS and the image has transparency, the colortype
10177 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10178 *
10179 * The -strip option causes StripImage() to set the png:include-chunk
10180 * artifact to "none,gama".
10181 */
10182
glennrp26f37912010-12-23 16:22:42 +000010183 mng_info->ping_exclude_bKGD=MagickFalse;
10184 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010185 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010186 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10187 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010188 mng_info->ping_exclude_iCCP=MagickFalse;
10189 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10190 mng_info->ping_exclude_oFFs=MagickFalse;
10191 mng_info->ping_exclude_pHYs=MagickFalse;
10192 mng_info->ping_exclude_sRGB=MagickFalse;
10193 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010194 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010195 mng_info->ping_exclude_vpAg=MagickFalse;
10196 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10197 mng_info->ping_exclude_zTXt=MagickFalse;
10198
glennrp8d3d6e52011-04-19 04:39:51 +000010199 mng_info->ping_preserve_colormap=MagickFalse;
10200
10201 value=GetImageArtifact(image,"png:preserve-colormap");
10202 if (value == NULL)
10203 value=GetImageOption(image_info,"png:preserve-colormap");
10204 if (value != NULL)
10205 mng_info->ping_preserve_colormap=MagickTrue;
10206
glennrp03812ae2010-12-24 01:31:34 +000010207 excluding=MagickFalse;
10208
glennrp5c7cf4e2010-12-24 00:30:00 +000010209 for (source=0; source<1; source++)
10210 {
10211 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010212 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010213 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010214
10215 if (value == NULL)
10216 value=GetImageArtifact(image,"png:exclude-chunks");
10217 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010218 else
glennrpacba0042010-12-24 14:27:26 +000010219 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010220 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010221
glennrpacba0042010-12-24 14:27:26 +000010222 if (value == NULL)
10223 value=GetImageOption(image_info,"png:exclude-chunks");
10224 }
10225
glennrp03812ae2010-12-24 01:31:34 +000010226 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010227 {
glennrp03812ae2010-12-24 01:31:34 +000010228
10229 size_t
10230 last;
10231
10232 excluding=MagickTrue;
10233
10234 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010235 {
10236 if (source == 0)
10237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10238 " png:exclude-chunk=%s found in image artifacts.\n", value);
10239 else
10240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10241 " png:exclude-chunk=%s found in image properties.\n", value);
10242 }
glennrp03812ae2010-12-24 01:31:34 +000010243
10244 last=strlen(value);
10245
10246 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010247 {
glennrp03812ae2010-12-24 01:31:34 +000010248
10249 if (LocaleNCompare(value+i,"all",3) == 0)
10250 {
10251 mng_info->ping_exclude_bKGD=MagickTrue;
10252 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010253 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010254 mng_info->ping_exclude_EXIF=MagickTrue;
10255 mng_info->ping_exclude_gAMA=MagickTrue;
10256 mng_info->ping_exclude_iCCP=MagickTrue;
10257 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10258 mng_info->ping_exclude_oFFs=MagickTrue;
10259 mng_info->ping_exclude_pHYs=MagickTrue;
10260 mng_info->ping_exclude_sRGB=MagickTrue;
10261 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010262 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010263 mng_info->ping_exclude_vpAg=MagickTrue;
10264 mng_info->ping_exclude_zCCP=MagickTrue;
10265 mng_info->ping_exclude_zTXt=MagickTrue;
10266 i--;
10267 }
glennrp2cc891a2010-12-24 13:44:32 +000010268
glennrp03812ae2010-12-24 01:31:34 +000010269 if (LocaleNCompare(value+i,"none",4) == 0)
10270 {
10271 mng_info->ping_exclude_bKGD=MagickFalse;
10272 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010273 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010274 mng_info->ping_exclude_EXIF=MagickFalse;
10275 mng_info->ping_exclude_gAMA=MagickFalse;
10276 mng_info->ping_exclude_iCCP=MagickFalse;
10277 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10278 mng_info->ping_exclude_oFFs=MagickFalse;
10279 mng_info->ping_exclude_pHYs=MagickFalse;
10280 mng_info->ping_exclude_sRGB=MagickFalse;
10281 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010282 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010283 mng_info->ping_exclude_vpAg=MagickFalse;
10284 mng_info->ping_exclude_zCCP=MagickFalse;
10285 mng_info->ping_exclude_zTXt=MagickFalse;
10286 }
glennrp2cc891a2010-12-24 13:44:32 +000010287
glennrp03812ae2010-12-24 01:31:34 +000010288 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10289 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010290
glennrp03812ae2010-12-24 01:31:34 +000010291 if (LocaleNCompare(value+i,"chrm",4) == 0)
10292 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010293
glennrpa0ed0092011-04-18 16:36:29 +000010294 if (LocaleNCompare(value+i,"date",4) == 0)
10295 mng_info->ping_exclude_date=MagickTrue;
10296
glennrp03812ae2010-12-24 01:31:34 +000010297 if (LocaleNCompare(value+i,"exif",4) == 0)
10298 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010299
glennrp03812ae2010-12-24 01:31:34 +000010300 if (LocaleNCompare(value+i,"gama",4) == 0)
10301 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010302
glennrp03812ae2010-12-24 01:31:34 +000010303 if (LocaleNCompare(value+i,"iccp",4) == 0)
10304 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010305
glennrp03812ae2010-12-24 01:31:34 +000010306 /*
10307 if (LocaleNCompare(value+i,"itxt",4) == 0)
10308 mng_info->ping_exclude_iTXt=MagickTrue;
10309 */
glennrp2cc891a2010-12-24 13:44:32 +000010310
glennrp03812ae2010-12-24 01:31:34 +000010311 if (LocaleNCompare(value+i,"gama",4) == 0)
10312 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010313
glennrp03812ae2010-12-24 01:31:34 +000010314 if (LocaleNCompare(value+i,"offs",4) == 0)
10315 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010316
glennrp03812ae2010-12-24 01:31:34 +000010317 if (LocaleNCompare(value+i,"phys",4) == 0)
10318 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010319
glennrpa1e3b7b2010-12-24 16:37:33 +000010320 if (LocaleNCompare(value+i,"srgb",4) == 0)
10321 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010322
glennrp03812ae2010-12-24 01:31:34 +000010323 if (LocaleNCompare(value+i,"text",4) == 0)
10324 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010325
glennrpa1e3b7b2010-12-24 16:37:33 +000010326 if (LocaleNCompare(value+i,"trns",4) == 0)
10327 mng_info->ping_exclude_tRNS=MagickTrue;
10328
glennrp03812ae2010-12-24 01:31:34 +000010329 if (LocaleNCompare(value+i,"vpag",4) == 0)
10330 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010331
glennrp03812ae2010-12-24 01:31:34 +000010332 if (LocaleNCompare(value+i,"zccp",4) == 0)
10333 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010334
glennrp03812ae2010-12-24 01:31:34 +000010335 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10336 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010337
glennrp03812ae2010-12-24 01:31:34 +000010338 }
glennrpce91ed52010-12-23 22:37:49 +000010339 }
glennrp26f37912010-12-23 16:22:42 +000010340 }
10341
glennrp5c7cf4e2010-12-24 00:30:00 +000010342 for (source=0; source<1; source++)
10343 {
10344 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010345 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010346 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010347
10348 if (value == NULL)
10349 value=GetImageArtifact(image,"png:include-chunks");
10350 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010351 else
glennrpacba0042010-12-24 14:27:26 +000010352 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010353 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010354
glennrpacba0042010-12-24 14:27:26 +000010355 if (value == NULL)
10356 value=GetImageOption(image_info,"png:include-chunks");
10357 }
10358
glennrp03812ae2010-12-24 01:31:34 +000010359 if (value != NULL)
10360 {
10361 size_t
10362 last;
glennrp26f37912010-12-23 16:22:42 +000010363
glennrp03812ae2010-12-24 01:31:34 +000010364 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010365
glennrp03812ae2010-12-24 01:31:34 +000010366 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010367 {
10368 if (source == 0)
10369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10370 " png:include-chunk=%s found in image artifacts.\n", value);
10371 else
10372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10373 " png:include-chunk=%s found in image properties.\n", value);
10374 }
glennrp03812ae2010-12-24 01:31:34 +000010375
10376 last=strlen(value);
10377
10378 for (i=0; i<(int) last; i+=5)
10379 {
10380 if (LocaleNCompare(value+i,"all",3) == 0)
10381 {
10382 mng_info->ping_exclude_bKGD=MagickFalse;
10383 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010384 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010385 mng_info->ping_exclude_EXIF=MagickFalse;
10386 mng_info->ping_exclude_gAMA=MagickFalse;
10387 mng_info->ping_exclude_iCCP=MagickFalse;
10388 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10389 mng_info->ping_exclude_oFFs=MagickFalse;
10390 mng_info->ping_exclude_pHYs=MagickFalse;
10391 mng_info->ping_exclude_sRGB=MagickFalse;
10392 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010393 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010394 mng_info->ping_exclude_vpAg=MagickFalse;
10395 mng_info->ping_exclude_zCCP=MagickFalse;
10396 mng_info->ping_exclude_zTXt=MagickFalse;
10397 i--;
10398 }
glennrp2cc891a2010-12-24 13:44:32 +000010399
glennrp03812ae2010-12-24 01:31:34 +000010400 if (LocaleNCompare(value+i,"none",4) == 0)
10401 {
10402 mng_info->ping_exclude_bKGD=MagickTrue;
10403 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010404 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010405 mng_info->ping_exclude_EXIF=MagickTrue;
10406 mng_info->ping_exclude_gAMA=MagickTrue;
10407 mng_info->ping_exclude_iCCP=MagickTrue;
10408 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10409 mng_info->ping_exclude_oFFs=MagickTrue;
10410 mng_info->ping_exclude_pHYs=MagickTrue;
10411 mng_info->ping_exclude_sRGB=MagickTrue;
10412 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010413 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010414 mng_info->ping_exclude_vpAg=MagickTrue;
10415 mng_info->ping_exclude_zCCP=MagickTrue;
10416 mng_info->ping_exclude_zTXt=MagickTrue;
10417 }
glennrp2cc891a2010-12-24 13:44:32 +000010418
glennrp03812ae2010-12-24 01:31:34 +000010419 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10420 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010421
glennrp03812ae2010-12-24 01:31:34 +000010422 if (LocaleNCompare(value+i,"chrm",4) == 0)
10423 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010424
glennrpa0ed0092011-04-18 16:36:29 +000010425 if (LocaleNCompare(value+i,"date",4) == 0)
10426 mng_info->ping_exclude_date=MagickFalse;
10427
glennrp03812ae2010-12-24 01:31:34 +000010428 if (LocaleNCompare(value+i,"exif",4) == 0)
10429 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010430
glennrp03812ae2010-12-24 01:31:34 +000010431 if (LocaleNCompare(value+i,"gama",4) == 0)
10432 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010433
glennrp03812ae2010-12-24 01:31:34 +000010434 if (LocaleNCompare(value+i,"iccp",4) == 0)
10435 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010436
glennrp03812ae2010-12-24 01:31:34 +000010437 /*
10438 if (LocaleNCompare(value+i,"itxt",4) == 0)
10439 mng_info->ping_exclude_iTXt=MagickFalse;
10440 */
glennrp2cc891a2010-12-24 13:44:32 +000010441
glennrp03812ae2010-12-24 01:31:34 +000010442 if (LocaleNCompare(value+i,"gama",4) == 0)
10443 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010444
glennrp03812ae2010-12-24 01:31:34 +000010445 if (LocaleNCompare(value+i,"offs",4) == 0)
10446 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010447
glennrp03812ae2010-12-24 01:31:34 +000010448 if (LocaleNCompare(value+i,"phys",4) == 0)
10449 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010450
glennrpa1e3b7b2010-12-24 16:37:33 +000010451 if (LocaleNCompare(value+i,"srgb",4) == 0)
10452 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010453
glennrp03812ae2010-12-24 01:31:34 +000010454 if (LocaleNCompare(value+i,"text",4) == 0)
10455 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010456
glennrpa1e3b7b2010-12-24 16:37:33 +000010457 if (LocaleNCompare(value+i,"trns",4) == 0)
10458 mng_info->ping_exclude_tRNS=MagickFalse;
10459
glennrp03812ae2010-12-24 01:31:34 +000010460 if (LocaleNCompare(value+i,"vpag",4) == 0)
10461 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010462
glennrp03812ae2010-12-24 01:31:34 +000010463 if (LocaleNCompare(value+i,"zccp",4) == 0)
10464 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010465
glennrp03812ae2010-12-24 01:31:34 +000010466 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10467 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010468
glennrp03812ae2010-12-24 01:31:34 +000010469 }
glennrpce91ed52010-12-23 22:37:49 +000010470 }
glennrp26f37912010-12-23 16:22:42 +000010471 }
10472
glennrp03812ae2010-12-24 01:31:34 +000010473 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010474 {
10475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10476 " Chunks to be excluded from the output PNG:");
10477 if (mng_info->ping_exclude_bKGD != MagickFalse)
10478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10479 " bKGD");
10480 if (mng_info->ping_exclude_cHRM != MagickFalse)
10481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10482 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010483 if (mng_info->ping_exclude_date != MagickFalse)
10484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10485 " date");
glennrp26f37912010-12-23 16:22:42 +000010486 if (mng_info->ping_exclude_EXIF != MagickFalse)
10487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10488 " EXIF");
10489 if (mng_info->ping_exclude_gAMA != MagickFalse)
10490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10491 " gAMA");
10492 if (mng_info->ping_exclude_iCCP != MagickFalse)
10493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10494 " iCCP");
10495/*
10496 if (mng_info->ping_exclude_iTXt != MagickFalse)
10497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10498 " iTXt");
10499*/
10500 if (mng_info->ping_exclude_oFFs != MagickFalse)
10501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10502 " oFFs");
10503 if (mng_info->ping_exclude_pHYs != MagickFalse)
10504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10505 " pHYs");
10506 if (mng_info->ping_exclude_sRGB != MagickFalse)
10507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10508 " sRGB");
10509 if (mng_info->ping_exclude_tEXt != MagickFalse)
10510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10511 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010512 if (mng_info->ping_exclude_tRNS != MagickFalse)
10513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10514 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010515 if (mng_info->ping_exclude_vpAg != MagickFalse)
10516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10517 " vpAg");
10518 if (mng_info->ping_exclude_zCCP != MagickFalse)
10519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10520 " zCCP");
10521 if (mng_info->ping_exclude_zTXt != MagickFalse)
10522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10523 " zTXt");
10524 }
10525
glennrpb9cfe272010-12-21 15:08:06 +000010526 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010527
glennrpb9cfe272010-12-21 15:08:06 +000010528 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010529
10530 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010531
cristy3ed852e2009-09-05 21:47:34 +000010532 if (logging != MagickFalse)
10533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010534
cristy3ed852e2009-09-05 21:47:34 +000010535 return(status);
10536}
10537
10538#if defined(JNG_SUPPORTED)
10539
10540/* Write one JNG image */
10541static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10542 const ImageInfo *image_info,Image *image)
10543{
10544 Image
10545 *jpeg_image;
10546
10547 ImageInfo
10548 *jpeg_image_info;
10549
10550 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010551 logging,
cristy3ed852e2009-09-05 21:47:34 +000010552 status;
10553
10554 size_t
10555 length;
10556
10557 unsigned char
10558 *blob,
10559 chunk[80],
10560 *p;
10561
10562 unsigned int
10563 jng_alpha_compression_method,
10564 jng_alpha_sample_depth,
10565 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010566 transparent;
10567
cristybb503372010-05-27 20:51:26 +000010568 size_t
cristy3ed852e2009-09-05 21:47:34 +000010569 jng_quality;
10570
10571 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010572 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010573
10574 blob=(unsigned char *) NULL;
10575 jpeg_image=(Image *) NULL;
10576 jpeg_image_info=(ImageInfo *) NULL;
10577
10578 status=MagickTrue;
10579 transparent=image_info->type==GrayscaleMatteType ||
10580 image_info->type==TrueColorMatteType;
10581 jng_color_type=10;
10582 jng_alpha_sample_depth=0;
10583 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10584 jng_alpha_compression_method=0;
10585
10586 if (image->matte != MagickFalse)
10587 {
10588 /* if any pixels are transparent */
10589 transparent=MagickTrue;
10590 if (image_info->compression==JPEGCompression)
10591 jng_alpha_compression_method=8;
10592 }
10593
10594 if (transparent)
10595 {
10596 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010597
cristy3ed852e2009-09-05 21:47:34 +000010598 /* Create JPEG blob, image, and image_info */
10599 if (logging != MagickFalse)
10600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10601 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010602
cristy3ed852e2009-09-05 21:47:34 +000010603 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010604
cristy3ed852e2009-09-05 21:47:34 +000010605 if (jpeg_image_info == (ImageInfo *) NULL)
10606 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010607
cristy3ed852e2009-09-05 21:47:34 +000010608 if (logging != MagickFalse)
10609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010611
cristy3ed852e2009-09-05 21:47:34 +000010612 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010613
cristy3ed852e2009-09-05 21:47:34 +000010614 if (jpeg_image == (Image *) NULL)
10615 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010616
cristy3ed852e2009-09-05 21:47:34 +000010617 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10618 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10619 status=NegateImage(jpeg_image,MagickFalse);
10620 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010621
cristy3ed852e2009-09-05 21:47:34 +000010622 if (jng_quality >= 1000)
10623 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010624
cristy3ed852e2009-09-05 21:47:34 +000010625 else
10626 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010627
cristy3ed852e2009-09-05 21:47:34 +000010628 jpeg_image_info->type=GrayscaleType;
10629 (void) SetImageType(jpeg_image,GrayscaleType);
10630 (void) AcquireUniqueFilename(jpeg_image->filename);
10631 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10632 "%s",jpeg_image->filename);
10633 }
10634
10635 /* To do: check bit depth of PNG alpha channel */
10636
10637 /* Check if image is grayscale. */
10638 if (image_info->type != TrueColorMatteType && image_info->type !=
10639 TrueColorType && ImageIsGray(image))
10640 jng_color_type-=2;
10641
10642 if (transparent)
10643 {
10644 if (jng_alpha_compression_method==0)
10645 {
10646 const char
10647 *value;
10648
10649 /* Encode opacity as a grayscale PNG blob */
10650 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10651 &image->exception);
10652 if (logging != MagickFalse)
10653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654 " Creating PNG blob.");
10655 length=0;
10656
10657 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10658 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10659 jpeg_image_info->interlace=NoInterlace;
10660
10661 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10662 &image->exception);
10663
10664 /* Retrieve sample depth used */
10665 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10666 if (value != (char *) NULL)
10667 jng_alpha_sample_depth= (unsigned int) value[0];
10668 }
10669 else
10670 {
10671 /* Encode opacity as a grayscale JPEG blob */
10672
10673 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10674 &image->exception);
10675
10676 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10677 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10678 jpeg_image_info->interlace=NoInterlace;
10679 if (logging != MagickFalse)
10680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681 " Creating blob.");
10682 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010683 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010684 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010685
cristy3ed852e2009-09-05 21:47:34 +000010686 if (logging != MagickFalse)
10687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010688 " Successfully read jpeg_image into a blob, length=%.20g.",
10689 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010690
10691 }
10692 /* Destroy JPEG image and image_info */
10693 jpeg_image=DestroyImage(jpeg_image);
10694 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10695 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10696 }
10697
10698 /* Write JHDR chunk */
10699 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10700 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010701 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010702 PNGLong(chunk+4,(png_uint_32) image->columns);
10703 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010704 chunk[12]=jng_color_type;
10705 chunk[13]=8; /* sample depth */
10706 chunk[14]=8; /*jng_image_compression_method */
10707 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10708 chunk[16]=jng_alpha_sample_depth;
10709 chunk[17]=jng_alpha_compression_method;
10710 chunk[18]=0; /*jng_alpha_filter_method */
10711 chunk[19]=0; /*jng_alpha_interlace_method */
10712 (void) WriteBlob(image,20,chunk);
10713 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10714 if (logging != MagickFalse)
10715 {
10716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010717 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010718
cristy3ed852e2009-09-05 21:47:34 +000010719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010720 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010721
cristy3ed852e2009-09-05 21:47:34 +000010722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10723 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010724
cristy3ed852e2009-09-05 21:47:34 +000010725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10726 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010727
cristy3ed852e2009-09-05 21:47:34 +000010728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10729 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010730
cristy3ed852e2009-09-05 21:47:34 +000010731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10732 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010733
cristy3ed852e2009-09-05 21:47:34 +000010734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10735 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010736
cristy3ed852e2009-09-05 21:47:34 +000010737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10738 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010739
cristy3ed852e2009-09-05 21:47:34 +000010740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10741 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010742
cristy3ed852e2009-09-05 21:47:34 +000010743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10744 " JNG alpha interlace:%5d",0);
10745 }
10746
glennrp0fe50b42010-11-16 03:52:51 +000010747 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010748 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010749
10750 /*
10751 Write leading ancillary chunks
10752 */
10753
10754 if (transparent)
10755 {
10756 /*
10757 Write JNG bKGD chunk
10758 */
10759
10760 unsigned char
10761 blue,
10762 green,
10763 red;
10764
cristybb503372010-05-27 20:51:26 +000010765 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010766 num_bytes;
10767
10768 if (jng_color_type == 8 || jng_color_type == 12)
10769 num_bytes=6L;
10770 else
10771 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010772 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010773 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010774 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010775 red=ScaleQuantumToChar(image->background_color.red);
10776 green=ScaleQuantumToChar(image->background_color.green);
10777 blue=ScaleQuantumToChar(image->background_color.blue);
10778 *(chunk+4)=0;
10779 *(chunk+5)=red;
10780 *(chunk+6)=0;
10781 *(chunk+7)=green;
10782 *(chunk+8)=0;
10783 *(chunk+9)=blue;
10784 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10785 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10786 }
10787
10788 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10789 {
10790 /*
10791 Write JNG sRGB chunk
10792 */
10793 (void) WriteBlobMSBULong(image,1L);
10794 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010795 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010796
cristy3ed852e2009-09-05 21:47:34 +000010797 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010798 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010799 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010800 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010801
cristy3ed852e2009-09-05 21:47:34 +000010802 else
glennrpe610a072010-08-05 17:08:46 +000010803 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010804 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010805 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010806
cristy3ed852e2009-09-05 21:47:34 +000010807 (void) WriteBlob(image,5,chunk);
10808 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10809 }
10810 else
10811 {
10812 if (image->gamma != 0.0)
10813 {
10814 /*
10815 Write JNG gAMA chunk
10816 */
10817 (void) WriteBlobMSBULong(image,4L);
10818 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010819 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010820 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010821 (void) WriteBlob(image,8,chunk);
10822 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10823 }
glennrp0fe50b42010-11-16 03:52:51 +000010824
cristy3ed852e2009-09-05 21:47:34 +000010825 if ((mng_info->equal_chrms == MagickFalse) &&
10826 (image->chromaticity.red_primary.x != 0.0))
10827 {
10828 PrimaryInfo
10829 primary;
10830
10831 /*
10832 Write JNG cHRM chunk
10833 */
10834 (void) WriteBlobMSBULong(image,32L);
10835 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010836 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010837 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010838 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10839 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010840 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010841 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10842 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010843 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010844 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10845 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010846 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010847 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10848 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010849 (void) WriteBlob(image,36,chunk);
10850 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10851 }
10852 }
glennrp0fe50b42010-11-16 03:52:51 +000010853
cristy3ed852e2009-09-05 21:47:34 +000010854 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10855 {
10856 /*
10857 Write JNG pHYs chunk
10858 */
10859 (void) WriteBlobMSBULong(image,9L);
10860 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000010861 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000010862 if (image->units == PixelsPerInchResolution)
10863 {
cristy35ef8242010-06-03 16:24:13 +000010864 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010865 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010866
cristy35ef8242010-06-03 16:24:13 +000010867 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010868 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010869
cristy3ed852e2009-09-05 21:47:34 +000010870 chunk[12]=1;
10871 }
glennrp0fe50b42010-11-16 03:52:51 +000010872
cristy3ed852e2009-09-05 21:47:34 +000010873 else
10874 {
10875 if (image->units == PixelsPerCentimeterResolution)
10876 {
cristy35ef8242010-06-03 16:24:13 +000010877 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010878 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010879
cristy35ef8242010-06-03 16:24:13 +000010880 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010881 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010882
cristy3ed852e2009-09-05 21:47:34 +000010883 chunk[12]=1;
10884 }
glennrp0fe50b42010-11-16 03:52:51 +000010885
cristy3ed852e2009-09-05 21:47:34 +000010886 else
10887 {
cristy35ef8242010-06-03 16:24:13 +000010888 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10889 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010890 chunk[12]=0;
10891 }
10892 }
10893 (void) WriteBlob(image,13,chunk);
10894 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10895 }
10896
10897 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10898 {
10899 /*
10900 Write JNG oFFs chunk
10901 */
10902 (void) WriteBlobMSBULong(image,9L);
10903 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000010904 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000010905 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10906 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000010907 chunk[12]=0;
10908 (void) WriteBlob(image,13,chunk);
10909 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10910 }
10911 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10912 {
10913 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10914 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010915 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000010916 PNGLong(chunk+4,(png_uint_32) image->page.width);
10917 PNGLong(chunk+8,(png_uint_32) image->page.height);
10918 chunk[12]=0; /* unit = pixels */
10919 (void) WriteBlob(image,13,chunk);
10920 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10921 }
10922
10923
10924 if (transparent)
10925 {
10926 if (jng_alpha_compression_method==0)
10927 {
cristybb503372010-05-27 20:51:26 +000010928 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010929 i;
10930
cristybb503372010-05-27 20:51:26 +000010931 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010932 len;
10933
10934 /* Write IDAT chunk header */
10935 if (logging != MagickFalse)
10936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010937 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000010938 length);
cristy3ed852e2009-09-05 21:47:34 +000010939
10940 /* Copy IDAT chunks */
10941 len=0;
10942 p=blob+8;
cristybb503372010-05-27 20:51:26 +000010943 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000010944 {
10945 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10946 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000010947
cristy3ed852e2009-09-05 21:47:34 +000010948 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10949 {
10950 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000010951 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000010952 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000010953 (void) WriteBlob(image,(size_t) len+4,p);
10954 (void) WriteBlobMSBULong(image,
10955 crc32(0,p,(uInt) len+4));
10956 }
glennrp0fe50b42010-11-16 03:52:51 +000010957
cristy3ed852e2009-09-05 21:47:34 +000010958 else
10959 {
10960 if (logging != MagickFalse)
10961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010962 " Skipping %c%c%c%c chunk, length=%.20g.",
10963 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000010964 }
10965 p+=(8+len);
10966 }
10967 }
10968 else
10969 {
10970 /* Write JDAA chunk header */
10971 if (logging != MagickFalse)
10972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010973 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000010974 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000010975 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000010976 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000010977 /* Write JDAT chunk(s) data */
10978 (void) WriteBlob(image,4,chunk);
10979 (void) WriteBlob(image,length,blob);
10980 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10981 (uInt) length));
10982 }
10983 blob=(unsigned char *) RelinquishMagickMemory(blob);
10984 }
10985
10986 /* Encode image as a JPEG blob */
10987 if (logging != MagickFalse)
10988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10989 " Creating jpeg_image_info.");
10990 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10991 if (jpeg_image_info == (ImageInfo *) NULL)
10992 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10993
10994 if (logging != MagickFalse)
10995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10996 " Creating jpeg_image.");
10997
10998 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10999 if (jpeg_image == (Image *) NULL)
11000 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11001 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11002
11003 (void) AcquireUniqueFilename(jpeg_image->filename);
11004 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11005 jpeg_image->filename);
11006
11007 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11008 &image->exception);
11009
11010 if (logging != MagickFalse)
11011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011012 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11013 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011014
11015 if (jng_color_type == 8 || jng_color_type == 12)
11016 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011017
cristy3ed852e2009-09-05 21:47:34 +000011018 jpeg_image_info->quality=jng_quality % 1000;
11019 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11020 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011021
cristy3ed852e2009-09-05 21:47:34 +000011022 if (logging != MagickFalse)
11023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11024 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011025
cristy3ed852e2009-09-05 21:47:34 +000011026 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011027
cristy3ed852e2009-09-05 21:47:34 +000011028 if (logging != MagickFalse)
11029 {
11030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011031 " Successfully read jpeg_image into a blob, length=%.20g.",
11032 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011033
11034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011035 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011036 }
glennrp0fe50b42010-11-16 03:52:51 +000011037
cristy3ed852e2009-09-05 21:47:34 +000011038 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011039 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011040 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011041 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011042 (void) WriteBlob(image,4,chunk);
11043 (void) WriteBlob(image,length,blob);
11044 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11045
11046 jpeg_image=DestroyImage(jpeg_image);
11047 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11048 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11049 blob=(unsigned char *) RelinquishMagickMemory(blob);
11050
11051 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011052 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011053
11054 /* Write IEND chunk */
11055 (void) WriteBlobMSBULong(image,0L);
11056 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011057 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011058 (void) WriteBlob(image,4,chunk);
11059 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11060
11061 if (logging != MagickFalse)
11062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11063 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011064
cristy3ed852e2009-09-05 21:47:34 +000011065 return(status);
11066}
11067
11068
11069/*
11070%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11071% %
11072% %
11073% %
11074% W r i t e J N G I m a g e %
11075% %
11076% %
11077% %
11078%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11079%
11080% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11081%
11082% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11083%
11084% The format of the WriteJNGImage method is:
11085%
11086% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11087%
11088% A description of each parameter follows:
11089%
11090% o image_info: the image info.
11091%
11092% o image: The image.
11093%
11094%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11095*/
11096static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11097{
11098 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011099 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011100 logging,
cristy3ed852e2009-09-05 21:47:34 +000011101 status;
11102
11103 MngInfo
11104 *mng_info;
11105
cristy3ed852e2009-09-05 21:47:34 +000011106 /*
11107 Open image file.
11108 */
11109 assert(image_info != (const ImageInfo *) NULL);
11110 assert(image_info->signature == MagickSignature);
11111 assert(image != (Image *) NULL);
11112 assert(image->signature == MagickSignature);
11113 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011114 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011115 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11116 if (status == MagickFalse)
11117 return(status);
11118
11119 /*
11120 Allocate a MngInfo structure.
11121 */
11122 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011123 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011124 if (mng_info == (MngInfo *) NULL)
11125 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11126 /*
11127 Initialize members of the MngInfo structure.
11128 */
11129 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11130 mng_info->image=image;
11131 have_mng_structure=MagickTrue;
11132
11133 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11134
11135 status=WriteOneJNGImage(mng_info,image_info,image);
11136 (void) CloseBlob(image);
11137
11138 (void) CatchImageException(image);
11139 MngInfoFreeStruct(mng_info,&have_mng_structure);
11140 if (logging != MagickFalse)
11141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11142 return(status);
11143}
11144#endif
11145
11146
11147
11148static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11149{
11150 const char
11151 *option;
11152
11153 Image
11154 *next_image;
11155
11156 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011157 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011158 status;
11159
glennrp03812ae2010-12-24 01:31:34 +000011160 volatile MagickBooleanType
11161 logging;
11162
cristy3ed852e2009-09-05 21:47:34 +000011163 MngInfo
11164 *mng_info;
11165
11166 int
cristy3ed852e2009-09-05 21:47:34 +000011167 image_count,
11168 need_iterations,
11169 need_matte;
11170
11171 volatile int
11172#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11173 defined(PNG_MNG_FEATURES_SUPPORTED)
11174 need_local_plte,
11175#endif
11176 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011177 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011178 use_global_plte;
11179
cristybb503372010-05-27 20:51:26 +000011180 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011181 i;
11182
11183 unsigned char
11184 chunk[800];
11185
11186 volatile unsigned int
11187 write_jng,
11188 write_mng;
11189
cristybb503372010-05-27 20:51:26 +000011190 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011191 scene;
11192
cristybb503372010-05-27 20:51:26 +000011193 size_t
cristy3ed852e2009-09-05 21:47:34 +000011194 final_delay=0,
11195 initial_delay;
11196
glennrpd5045b42010-03-24 12:40:35 +000011197#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011198 if (image_info->verbose)
11199 printf("Your PNG library (libpng-%s) is rather old.\n",
11200 PNG_LIBPNG_VER_STRING);
11201#endif
11202
11203 /*
11204 Open image file.
11205 */
11206 assert(image_info != (const ImageInfo *) NULL);
11207 assert(image_info->signature == MagickSignature);
11208 assert(image != (Image *) NULL);
11209 assert(image->signature == MagickSignature);
11210 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011211 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011212 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11213 if (status == MagickFalse)
11214 return(status);
11215
11216 /*
11217 Allocate a MngInfo structure.
11218 */
11219 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011220 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011221 if (mng_info == (MngInfo *) NULL)
11222 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11223 /*
11224 Initialize members of the MngInfo structure.
11225 */
11226 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11227 mng_info->image=image;
11228 have_mng_structure=MagickTrue;
11229 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11230
11231 /*
11232 * See if user has requested a specific PNG subformat to be used
11233 * for all of the PNGs in the MNG being written, e.g.,
11234 *
11235 * convert *.png png8:animation.mng
11236 *
11237 * To do: check -define png:bit_depth and png:color_type as well,
11238 * or perhaps use mng:bit_depth and mng:color_type instead for
11239 * global settings.
11240 */
11241
11242 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11243 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11244 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11245
11246 write_jng=MagickFalse;
11247 if (image_info->compression == JPEGCompression)
11248 write_jng=MagickTrue;
11249
11250 mng_info->adjoin=image_info->adjoin &&
11251 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11252
cristy3ed852e2009-09-05 21:47:34 +000011253 if (logging != MagickFalse)
11254 {
11255 /* Log some info about the input */
11256 Image
11257 *p;
11258
11259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11260 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011261
cristy3ed852e2009-09-05 21:47:34 +000011262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011263 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011264
cristy3ed852e2009-09-05 21:47:34 +000011265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11266 " Type: %d",image_info->type);
11267
11268 scene=0;
11269 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11270 {
11271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011272 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011273
cristy3ed852e2009-09-05 21:47:34 +000011274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011275 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011276
cristy3ed852e2009-09-05 21:47:34 +000011277 if (p->matte)
11278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11279 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011280
cristy3ed852e2009-09-05 21:47:34 +000011281 else
11282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11283 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011284
cristy3ed852e2009-09-05 21:47:34 +000011285 if (p->storage_class == PseudoClass)
11286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11287 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011288
cristy3ed852e2009-09-05 21:47:34 +000011289 else
11290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11291 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011292
cristy3ed852e2009-09-05 21:47:34 +000011293 if (p->colors)
11294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011295 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011296
cristy3ed852e2009-09-05 21:47:34 +000011297 else
11298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11299 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011300
cristy3ed852e2009-09-05 21:47:34 +000011301 if (mng_info->adjoin == MagickFalse)
11302 break;
11303 }
11304 }
11305
cristy3ed852e2009-09-05 21:47:34 +000011306 use_global_plte=MagickFalse;
11307 all_images_are_gray=MagickFalse;
11308#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11309 need_local_plte=MagickTrue;
11310#endif
11311 need_defi=MagickFalse;
11312 need_matte=MagickFalse;
11313 mng_info->framing_mode=1;
11314 mng_info->old_framing_mode=1;
11315
11316 if (write_mng)
11317 if (image_info->page != (char *) NULL)
11318 {
11319 /*
11320 Determine image bounding box.
11321 */
11322 SetGeometry(image,&mng_info->page);
11323 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11324 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11325 }
11326 if (write_mng)
11327 {
11328 unsigned int
11329 need_geom;
11330
11331 unsigned short
11332 red,
11333 green,
11334 blue;
11335
11336 mng_info->page=image->page;
11337 need_geom=MagickTrue;
11338 if (mng_info->page.width || mng_info->page.height)
11339 need_geom=MagickFalse;
11340 /*
11341 Check all the scenes.
11342 */
11343 initial_delay=image->delay;
11344 need_iterations=MagickFalse;
11345 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11346 mng_info->equal_physs=MagickTrue,
11347 mng_info->equal_gammas=MagickTrue;
11348 mng_info->equal_srgbs=MagickTrue;
11349 mng_info->equal_backgrounds=MagickTrue;
11350 image_count=0;
11351#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11352 defined(PNG_MNG_FEATURES_SUPPORTED)
11353 all_images_are_gray=MagickTrue;
11354 mng_info->equal_palettes=MagickFalse;
11355 need_local_plte=MagickFalse;
11356#endif
11357 for (next_image=image; next_image != (Image *) NULL; )
11358 {
11359 if (need_geom)
11360 {
11361 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11362 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011363
cristy3ed852e2009-09-05 21:47:34 +000011364 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11365 mng_info->page.height=next_image->rows+next_image->page.y;
11366 }
glennrp0fe50b42010-11-16 03:52:51 +000011367
cristy3ed852e2009-09-05 21:47:34 +000011368 if (next_image->page.x || next_image->page.y)
11369 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011370
cristy3ed852e2009-09-05 21:47:34 +000011371 if (next_image->matte)
11372 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011373
cristy3ed852e2009-09-05 21:47:34 +000011374 if ((int) next_image->dispose >= BackgroundDispose)
11375 if (next_image->matte || next_image->page.x || next_image->page.y ||
11376 ((next_image->columns < mng_info->page.width) &&
11377 (next_image->rows < mng_info->page.height)))
11378 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011379
cristy3ed852e2009-09-05 21:47:34 +000011380 if (next_image->iterations)
11381 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011382
cristy3ed852e2009-09-05 21:47:34 +000011383 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011384
cristy3ed852e2009-09-05 21:47:34 +000011385 if (final_delay != initial_delay || final_delay > 1UL*
11386 next_image->ticks_per_second)
11387 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011388
cristy3ed852e2009-09-05 21:47:34 +000011389#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11390 defined(PNG_MNG_FEATURES_SUPPORTED)
11391 /*
11392 check for global palette possibility.
11393 */
11394 if (image->matte != MagickFalse)
11395 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011396
cristy3ed852e2009-09-05 21:47:34 +000011397 if (need_local_plte == 0)
11398 {
11399 if (ImageIsGray(image) == MagickFalse)
11400 all_images_are_gray=MagickFalse;
11401 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11402 if (use_global_plte == 0)
11403 use_global_plte=mng_info->equal_palettes;
11404 need_local_plte=!mng_info->equal_palettes;
11405 }
11406#endif
11407 if (GetNextImageInList(next_image) != (Image *) NULL)
11408 {
11409 if (next_image->background_color.red !=
11410 next_image->next->background_color.red ||
11411 next_image->background_color.green !=
11412 next_image->next->background_color.green ||
11413 next_image->background_color.blue !=
11414 next_image->next->background_color.blue)
11415 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011416
cristy3ed852e2009-09-05 21:47:34 +000011417 if (next_image->gamma != next_image->next->gamma)
11418 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011419
cristy3ed852e2009-09-05 21:47:34 +000011420 if (next_image->rendering_intent !=
11421 next_image->next->rendering_intent)
11422 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011423
cristy3ed852e2009-09-05 21:47:34 +000011424 if ((next_image->units != next_image->next->units) ||
11425 (next_image->x_resolution != next_image->next->x_resolution) ||
11426 (next_image->y_resolution != next_image->next->y_resolution))
11427 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011428
cristy3ed852e2009-09-05 21:47:34 +000011429 if (mng_info->equal_chrms)
11430 {
11431 if (next_image->chromaticity.red_primary.x !=
11432 next_image->next->chromaticity.red_primary.x ||
11433 next_image->chromaticity.red_primary.y !=
11434 next_image->next->chromaticity.red_primary.y ||
11435 next_image->chromaticity.green_primary.x !=
11436 next_image->next->chromaticity.green_primary.x ||
11437 next_image->chromaticity.green_primary.y !=
11438 next_image->next->chromaticity.green_primary.y ||
11439 next_image->chromaticity.blue_primary.x !=
11440 next_image->next->chromaticity.blue_primary.x ||
11441 next_image->chromaticity.blue_primary.y !=
11442 next_image->next->chromaticity.blue_primary.y ||
11443 next_image->chromaticity.white_point.x !=
11444 next_image->next->chromaticity.white_point.x ||
11445 next_image->chromaticity.white_point.y !=
11446 next_image->next->chromaticity.white_point.y)
11447 mng_info->equal_chrms=MagickFalse;
11448 }
11449 }
11450 image_count++;
11451 next_image=GetNextImageInList(next_image);
11452 }
11453 if (image_count < 2)
11454 {
11455 mng_info->equal_backgrounds=MagickFalse;
11456 mng_info->equal_chrms=MagickFalse;
11457 mng_info->equal_gammas=MagickFalse;
11458 mng_info->equal_srgbs=MagickFalse;
11459 mng_info->equal_physs=MagickFalse;
11460 use_global_plte=MagickFalse;
11461#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11462 need_local_plte=MagickTrue;
11463#endif
11464 need_iterations=MagickFalse;
11465 }
glennrp0fe50b42010-11-16 03:52:51 +000011466
cristy3ed852e2009-09-05 21:47:34 +000011467 if (mng_info->need_fram == MagickFalse)
11468 {
11469 /*
11470 Only certain framing rates 100/n are exactly representable without
11471 the FRAM chunk but we'll allow some slop in VLC files
11472 */
11473 if (final_delay == 0)
11474 {
11475 if (need_iterations != MagickFalse)
11476 {
11477 /*
11478 It's probably a GIF with loop; don't run it *too* fast.
11479 */
glennrp02617122010-07-28 13:07:35 +000011480 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011481 {
11482 final_delay=10;
11483 (void) ThrowMagickException(&image->exception,
11484 GetMagickModule(),CoderWarning,
11485 "input has zero delay between all frames; assuming",
11486 " 10 cs `%s'","");
11487 }
cristy3ed852e2009-09-05 21:47:34 +000011488 }
11489 else
11490 mng_info->ticks_per_second=0;
11491 }
11492 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011493 mng_info->ticks_per_second=(png_uint_32)
11494 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011495 if (final_delay > 50)
11496 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011497
cristy3ed852e2009-09-05 21:47:34 +000011498 if (final_delay > 75)
11499 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011500
cristy3ed852e2009-09-05 21:47:34 +000011501 if (final_delay > 125)
11502 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011503
cristy3ed852e2009-09-05 21:47:34 +000011504 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11505 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11506 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11507 1UL*image->ticks_per_second))
11508 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11509 }
glennrp0fe50b42010-11-16 03:52:51 +000011510
cristy3ed852e2009-09-05 21:47:34 +000011511 if (mng_info->need_fram != MagickFalse)
11512 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11513 /*
11514 If pseudocolor, we should also check to see if all the
11515 palettes are identical and write a global PLTE if they are.
11516 ../glennrp Feb 99.
11517 */
11518 /*
11519 Write the MNG version 1.0 signature and MHDR chunk.
11520 */
11521 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11522 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11523 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011524 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011525 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11526 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011527 PNGLong(chunk+12,mng_info->ticks_per_second);
11528 PNGLong(chunk+16,0L); /* layer count=unknown */
11529 PNGLong(chunk+20,0L); /* frame count=unknown */
11530 PNGLong(chunk+24,0L); /* play time=unknown */
11531 if (write_jng)
11532 {
11533 if (need_matte)
11534 {
11535 if (need_defi || mng_info->need_fram || use_global_plte)
11536 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011537
cristy3ed852e2009-09-05 21:47:34 +000011538 else
11539 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11540 }
glennrp0fe50b42010-11-16 03:52:51 +000011541
cristy3ed852e2009-09-05 21:47:34 +000011542 else
11543 {
11544 if (need_defi || mng_info->need_fram || use_global_plte)
11545 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011546
cristy3ed852e2009-09-05 21:47:34 +000011547 else
11548 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11549 }
11550 }
glennrp0fe50b42010-11-16 03:52:51 +000011551
cristy3ed852e2009-09-05 21:47:34 +000011552 else
11553 {
11554 if (need_matte)
11555 {
11556 if (need_defi || mng_info->need_fram || use_global_plte)
11557 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011558
cristy3ed852e2009-09-05 21:47:34 +000011559 else
11560 PNGLong(chunk+28,9L); /* simplicity=VLC */
11561 }
glennrp0fe50b42010-11-16 03:52:51 +000011562
cristy3ed852e2009-09-05 21:47:34 +000011563 else
11564 {
11565 if (need_defi || mng_info->need_fram || use_global_plte)
11566 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011567
cristy3ed852e2009-09-05 21:47:34 +000011568 else
11569 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11570 }
11571 }
11572 (void) WriteBlob(image,32,chunk);
11573 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11574 option=GetImageOption(image_info,"mng:need-cacheoff");
11575 if (option != (const char *) NULL)
11576 {
11577 size_t
11578 length;
11579
11580 /*
11581 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11582 */
11583 PNGType(chunk,mng_nEED);
11584 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011585 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011586 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011587 length+=4;
11588 (void) WriteBlob(image,length,chunk);
11589 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11590 }
11591 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11592 (GetNextImageInList(image) != (Image *) NULL) &&
11593 (image->iterations != 1))
11594 {
11595 /*
11596 Write MNG TERM chunk
11597 */
11598 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11599 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011600 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011601 chunk[4]=3; /* repeat animation */
11602 chunk[5]=0; /* show last frame when done */
11603 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11604 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011605
cristy3ed852e2009-09-05 21:47:34 +000011606 if (image->iterations == 0)
11607 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011608
cristy3ed852e2009-09-05 21:47:34 +000011609 else
11610 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011611
cristy3ed852e2009-09-05 21:47:34 +000011612 if (logging != MagickFalse)
11613 {
11614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011615 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11616 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011617
cristy3ed852e2009-09-05 21:47:34 +000011618 if (image->iterations == 0)
11619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011620 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011621
cristy3ed852e2009-09-05 21:47:34 +000011622 else
11623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011624 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011625 }
11626 (void) WriteBlob(image,14,chunk);
11627 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11628 }
11629 /*
11630 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11631 */
11632 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11633 mng_info->equal_srgbs)
11634 {
11635 /*
11636 Write MNG sRGB chunk
11637 */
11638 (void) WriteBlobMSBULong(image,1L);
11639 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011640 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011641
cristy3ed852e2009-09-05 21:47:34 +000011642 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011643 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011644 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011645 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011646
cristy3ed852e2009-09-05 21:47:34 +000011647 else
glennrpe610a072010-08-05 17:08:46 +000011648 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011649 Magick_RenderingIntent_to_PNG_RenderingIntent(
11650 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011651
cristy3ed852e2009-09-05 21:47:34 +000011652 (void) WriteBlob(image,5,chunk);
11653 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11654 mng_info->have_write_global_srgb=MagickTrue;
11655 }
glennrp0fe50b42010-11-16 03:52:51 +000011656
cristy3ed852e2009-09-05 21:47:34 +000011657 else
11658 {
11659 if (image->gamma && mng_info->equal_gammas)
11660 {
11661 /*
11662 Write MNG gAMA chunk
11663 */
11664 (void) WriteBlobMSBULong(image,4L);
11665 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011666 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011667 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011668 (void) WriteBlob(image,8,chunk);
11669 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11670 mng_info->have_write_global_gama=MagickTrue;
11671 }
11672 if (mng_info->equal_chrms)
11673 {
11674 PrimaryInfo
11675 primary;
11676
11677 /*
11678 Write MNG cHRM chunk
11679 */
11680 (void) WriteBlobMSBULong(image,32L);
11681 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011682 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011683 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011684 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11685 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011686 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011687 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11688 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011689 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011690 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11691 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011692 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011693 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11694 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011695 (void) WriteBlob(image,36,chunk);
11696 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11697 mng_info->have_write_global_chrm=MagickTrue;
11698 }
11699 }
11700 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11701 {
11702 /*
11703 Write MNG pHYs chunk
11704 */
11705 (void) WriteBlobMSBULong(image,9L);
11706 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011707 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011708
cristy3ed852e2009-09-05 21:47:34 +000011709 if (image->units == PixelsPerInchResolution)
11710 {
cristy35ef8242010-06-03 16:24:13 +000011711 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011712 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011713
cristy35ef8242010-06-03 16:24:13 +000011714 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011715 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011716
cristy3ed852e2009-09-05 21:47:34 +000011717 chunk[12]=1;
11718 }
glennrp0fe50b42010-11-16 03:52:51 +000011719
cristy3ed852e2009-09-05 21:47:34 +000011720 else
11721 {
11722 if (image->units == PixelsPerCentimeterResolution)
11723 {
cristy35ef8242010-06-03 16:24:13 +000011724 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011725 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011726
cristy35ef8242010-06-03 16:24:13 +000011727 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011728 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011729
cristy3ed852e2009-09-05 21:47:34 +000011730 chunk[12]=1;
11731 }
glennrp0fe50b42010-11-16 03:52:51 +000011732
cristy3ed852e2009-09-05 21:47:34 +000011733 else
11734 {
cristy35ef8242010-06-03 16:24:13 +000011735 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11736 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011737 chunk[12]=0;
11738 }
11739 }
11740 (void) WriteBlob(image,13,chunk);
11741 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11742 }
11743 /*
11744 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11745 or does not cover the entire frame.
11746 */
11747 if (write_mng && (image->matte || image->page.x > 0 ||
11748 image->page.y > 0 || (image->page.width &&
11749 (image->page.width+image->page.x < mng_info->page.width))
11750 || (image->page.height && (image->page.height+image->page.y
11751 < mng_info->page.height))))
11752 {
11753 (void) WriteBlobMSBULong(image,6L);
11754 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011755 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011756 red=ScaleQuantumToShort(image->background_color.red);
11757 green=ScaleQuantumToShort(image->background_color.green);
11758 blue=ScaleQuantumToShort(image->background_color.blue);
11759 PNGShort(chunk+4,red);
11760 PNGShort(chunk+6,green);
11761 PNGShort(chunk+8,blue);
11762 (void) WriteBlob(image,10,chunk);
11763 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11764 if (mng_info->equal_backgrounds)
11765 {
11766 (void) WriteBlobMSBULong(image,6L);
11767 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011768 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011769 (void) WriteBlob(image,10,chunk);
11770 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11771 }
11772 }
11773
11774#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11775 if ((need_local_plte == MagickFalse) &&
11776 (image->storage_class == PseudoClass) &&
11777 (all_images_are_gray == MagickFalse))
11778 {
cristybb503372010-05-27 20:51:26 +000011779 size_t
cristy3ed852e2009-09-05 21:47:34 +000011780 data_length;
11781
11782 /*
11783 Write MNG PLTE chunk
11784 */
11785 data_length=3*image->colors;
11786 (void) WriteBlobMSBULong(image,data_length);
11787 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011788 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011789
cristybb503372010-05-27 20:51:26 +000011790 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011791 {
11792 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11793 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11794 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11795 }
glennrp0fe50b42010-11-16 03:52:51 +000011796
cristy3ed852e2009-09-05 21:47:34 +000011797 (void) WriteBlob(image,data_length+4,chunk);
11798 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11799 mng_info->have_write_global_plte=MagickTrue;
11800 }
11801#endif
11802 }
11803 scene=0;
11804 mng_info->delay=0;
11805#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11806 defined(PNG_MNG_FEATURES_SUPPORTED)
11807 mng_info->equal_palettes=MagickFalse;
11808#endif
11809 do
11810 {
11811 if (mng_info->adjoin)
11812 {
11813#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11814 defined(PNG_MNG_FEATURES_SUPPORTED)
11815 /*
11816 If we aren't using a global palette for the entire MNG, check to
11817 see if we can use one for two or more consecutive images.
11818 */
11819 if (need_local_plte && use_global_plte && !all_images_are_gray)
11820 {
11821 if (mng_info->IsPalette)
11822 {
11823 /*
11824 When equal_palettes is true, this image has the same palette
11825 as the previous PseudoClass image
11826 */
11827 mng_info->have_write_global_plte=mng_info->equal_palettes;
11828 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11829 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11830 {
11831 /*
11832 Write MNG PLTE chunk
11833 */
cristybb503372010-05-27 20:51:26 +000011834 size_t
cristy3ed852e2009-09-05 21:47:34 +000011835 data_length;
11836
11837 data_length=3*image->colors;
11838 (void) WriteBlobMSBULong(image,data_length);
11839 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011840 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011841
cristybb503372010-05-27 20:51:26 +000011842 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011843 {
11844 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11845 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11846 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11847 }
glennrp0fe50b42010-11-16 03:52:51 +000011848
cristy3ed852e2009-09-05 21:47:34 +000011849 (void) WriteBlob(image,data_length+4,chunk);
11850 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11851 (uInt) (data_length+4)));
11852 mng_info->have_write_global_plte=MagickTrue;
11853 }
11854 }
11855 else
11856 mng_info->have_write_global_plte=MagickFalse;
11857 }
11858#endif
11859 if (need_defi)
11860 {
cristybb503372010-05-27 20:51:26 +000011861 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011862 previous_x,
11863 previous_y;
11864
11865 if (scene)
11866 {
11867 previous_x=mng_info->page.x;
11868 previous_y=mng_info->page.y;
11869 }
11870 else
11871 {
11872 previous_x=0;
11873 previous_y=0;
11874 }
11875 mng_info->page=image->page;
11876 if ((mng_info->page.x != previous_x) ||
11877 (mng_info->page.y != previous_y))
11878 {
11879 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11880 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000011881 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000011882 chunk[4]=0; /* object 0 MSB */
11883 chunk[5]=0; /* object 0 LSB */
11884 chunk[6]=0; /* visible */
11885 chunk[7]=0; /* abstract */
11886 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11887 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11888 (void) WriteBlob(image,16,chunk);
11889 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11890 }
11891 }
11892 }
11893
11894 mng_info->write_mng=write_mng;
11895
11896 if ((int) image->dispose >= 3)
11897 mng_info->framing_mode=3;
11898
11899 if (mng_info->need_fram && mng_info->adjoin &&
11900 ((image->delay != mng_info->delay) ||
11901 (mng_info->framing_mode != mng_info->old_framing_mode)))
11902 {
11903 if (image->delay == mng_info->delay)
11904 {
11905 /*
11906 Write a MNG FRAM chunk with the new framing mode.
11907 */
11908 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11909 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011910 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000011911 chunk[4]=(unsigned char) mng_info->framing_mode;
11912 (void) WriteBlob(image,5,chunk);
11913 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11914 }
11915 else
11916 {
11917 /*
11918 Write a MNG FRAM chunk with the delay.
11919 */
11920 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11921 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011922 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011923 chunk[4]=(unsigned char) mng_info->framing_mode;
11924 chunk[5]=0; /* frame name separator (no name) */
11925 chunk[6]=2; /* flag for changing default delay */
11926 chunk[7]=0; /* flag for changing frame timeout */
11927 chunk[8]=0; /* flag for changing frame clipping */
11928 chunk[9]=0; /* flag for changing frame sync_id */
11929 PNGLong(chunk+10,(png_uint_32)
11930 ((mng_info->ticks_per_second*
11931 image->delay)/MagickMax(image->ticks_per_second,1)));
11932 (void) WriteBlob(image,14,chunk);
11933 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000011934 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000011935 }
11936 mng_info->old_framing_mode=mng_info->framing_mode;
11937 }
11938
11939#if defined(JNG_SUPPORTED)
11940 if (image_info->compression == JPEGCompression)
11941 {
11942 ImageInfo
11943 *write_info;
11944
11945 if (logging != MagickFalse)
11946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11947 " Writing JNG object.");
11948 /* To do: specify the desired alpha compression method. */
11949 write_info=CloneImageInfo(image_info);
11950 write_info->compression=UndefinedCompression;
11951 status=WriteOneJNGImage(mng_info,write_info,image);
11952 write_info=DestroyImageInfo(write_info);
11953 }
11954 else
11955#endif
11956 {
11957 if (logging != MagickFalse)
11958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11959 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000011960
glennrpb9cfe272010-12-21 15:08:06 +000011961 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000011962 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000011963
11964 /* We don't want any ancillary chunks written */
11965 mng_info->ping_exclude_bKGD=MagickTrue;
11966 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011967 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000011968 mng_info->ping_exclude_EXIF=MagickTrue;
11969 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000011970 mng_info->ping_exclude_iCCP=MagickTrue;
11971 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11972 mng_info->ping_exclude_oFFs=MagickTrue;
11973 mng_info->ping_exclude_pHYs=MagickTrue;
11974 mng_info->ping_exclude_sRGB=MagickTrue;
11975 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011976 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000011977 mng_info->ping_exclude_vpAg=MagickTrue;
11978 mng_info->ping_exclude_zCCP=MagickTrue;
11979 mng_info->ping_exclude_zTXt=MagickTrue;
11980
cristy3ed852e2009-09-05 21:47:34 +000011981 status=WriteOnePNGImage(mng_info,image_info,image);
11982 }
11983
11984 if (status == MagickFalse)
11985 {
11986 MngInfoFreeStruct(mng_info,&have_mng_structure);
11987 (void) CloseBlob(image);
11988 return(MagickFalse);
11989 }
11990 (void) CatchImageException(image);
11991 if (GetNextImageInList(image) == (Image *) NULL)
11992 break;
11993 image=SyncNextImageInList(image);
11994 status=SetImageProgress(image,SaveImagesTag,scene++,
11995 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000011996
cristy3ed852e2009-09-05 21:47:34 +000011997 if (status == MagickFalse)
11998 break;
glennrp0fe50b42010-11-16 03:52:51 +000011999
cristy3ed852e2009-09-05 21:47:34 +000012000 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012001
cristy3ed852e2009-09-05 21:47:34 +000012002 if (write_mng)
12003 {
12004 while (GetPreviousImageInList(image) != (Image *) NULL)
12005 image=GetPreviousImageInList(image);
12006 /*
12007 Write the MEND chunk.
12008 */
12009 (void) WriteBlobMSBULong(image,0x00000000L);
12010 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012011 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012012 (void) WriteBlob(image,4,chunk);
12013 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12014 }
12015 /*
12016 Relinquish resources.
12017 */
12018 (void) CloseBlob(image);
12019 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012020
cristy3ed852e2009-09-05 21:47:34 +000012021 if (logging != MagickFalse)
12022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012023
cristy3ed852e2009-09-05 21:47:34 +000012024 return(MagickTrue);
12025}
glennrpd5045b42010-03-24 12:40:35 +000012026#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012027
cristy3ed852e2009-09-05 21:47:34 +000012028static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12029{
12030 image=image;
12031 printf("Your PNG library is too old: You have libpng-%s\n",
12032 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012033
cristy3ed852e2009-09-05 21:47:34 +000012034 ThrowBinaryException(CoderError,"PNG library is too old",
12035 image_info->filename);
12036}
glennrp39992b42010-11-14 00:03:43 +000012037
cristy3ed852e2009-09-05 21:47:34 +000012038static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12039{
12040 return(WritePNGImage(image_info,image));
12041}
glennrpd5045b42010-03-24 12:40:35 +000012042#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012043#endif