blob: 8375436429632457a6d99a43dc7a01584aa605f6 [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--)
glennrpa18d5bc2011-04-23 14:51:34 +00002568 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002569 p++;
2570 }
glennrp0fe50b42010-11-16 03:52:51 +00002571
cristy3ed852e2009-09-05 21:47:34 +00002572 if ((image->columns % 8) != 0)
2573 {
cristybb503372010-05-27 20:51:26 +00002574 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002575 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002576 }
glennrp0fe50b42010-11-16 03:52:51 +00002577
cristy3ed852e2009-09-05 21:47:34 +00002578 break;
2579 }
glennrp47b9dd52010-11-24 18:12:06 +00002580
cristy3ed852e2009-09-05 21:47:34 +00002581 case 2:
2582 {
cristybb503372010-05-27 20:51:26 +00002583 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002584 {
glennrpa18d5bc2011-04-23 14:51:34 +00002585 *r++=(*p >> 6) & 0x03;
2586 *r++=(*p >> 4) & 0x03;
2587 *r++=(*p >> 2) & 0x03;
2588 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00002589 }
glennrp0fe50b42010-11-16 03:52:51 +00002590
cristy3ed852e2009-09-05 21:47:34 +00002591 if ((image->columns % 4) != 0)
2592 {
cristybb503372010-05-27 20:51:26 +00002593 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00002594 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00002595 }
glennrp0fe50b42010-11-16 03:52:51 +00002596
cristy3ed852e2009-09-05 21:47:34 +00002597 break;
2598 }
glennrp47b9dd52010-11-24 18:12:06 +00002599
cristy3ed852e2009-09-05 21:47:34 +00002600 case 4:
2601 {
cristybb503372010-05-27 20:51:26 +00002602 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00002603 {
glennrpa18d5bc2011-04-23 14:51:34 +00002604 *r++=(*p >> 4) & 0x0f;
2605 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00002606 }
glennrp0fe50b42010-11-16 03:52:51 +00002607
cristy3ed852e2009-09-05 21:47:34 +00002608 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00002609 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00002610
cristy3ed852e2009-09-05 21:47:34 +00002611 break;
2612 }
glennrp47b9dd52010-11-24 18:12:06 +00002613
cristy3ed852e2009-09-05 21:47:34 +00002614 case 8:
2615 {
glennrpfaa852b2010-03-30 12:17:00 +00002616 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00002617 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002618 {
glennrpa18d5bc2011-04-23 14:51:34 +00002619 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00002620 /* In image.h, OpaqueOpacity is 0
2621 * TransparentOpacity is QuantumRange
2622 * In a PNG datastream, Opaque is QuantumRange
2623 * and Transparent is 0.
2624 */
2625 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
glennrpc8cbc5d2011-01-01 00:12:34 +00002626 if (q->opacity != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00002627 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002628 q++;
2629 }
glennrp0fe50b42010-11-16 03:52:51 +00002630
cristy3ed852e2009-09-05 21:47:34 +00002631 else
cristybb503372010-05-27 20:51:26 +00002632 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002633 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00002634
cristy3ed852e2009-09-05 21:47:34 +00002635 break;
2636 }
glennrp47b9dd52010-11-24 18:12:06 +00002637
cristy3ed852e2009-09-05 21:47:34 +00002638 case 16:
2639 {
cristybb503372010-05-27 20:51:26 +00002640 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002641 {
glennrp58f77c72011-04-23 14:09:09 +00002642#if (MAGICKCORE_QUANTUM_DEPTH == 16)
2643 size_t
2644 quantum;
2645
2646 if (image->colors > 256)
2647 *r=((*p++) << 8);
2648
2649 else
2650 *r=0;
2651
2652 quantum=(*r);
2653 quantum|=(*p++);
2654 *r=(Quantum) quantum;
2655 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00002656
2657 if (ping_color_type == 4)
2658 {
glennrp58f77c72011-04-23 14:09:09 +00002659 quantum=((*p++) << 8);
2660 quantum|=(*p++);
2661 q->opacity=(Quantum) (QuantumRange-quantum);
2662 if (q->opacity != OpaqueOpacity)
2663 found_transparent_pixel = MagickTrue;
2664 q++;
2665 }
2666#else
2667#if (MAGICKCORE_QUANTUM_DEPTH == 32)
2668 size_t
2669 quantum;
2670
2671 if (image->colors > 256)
2672 *r=((*p++) << 8);
2673
2674 else
2675 *r=0;
2676
2677 quantum=(*r);
2678 quantum|=(*p++);
2679 *r=quantum;
2680 r++;
2681
2682 if (ping_color_type == 4)
2683 {
2684 q->opacity=(*p << 8) | *(p+1);
2685 q->opacity*=65537L;
2686 q->opacity=(Quantum) GetAlphaPixelComponent(q);
2687 if (q->opacity != OpaqueOpacity)
2688 found_transparent_pixel = MagickTrue;
2689 p+=2;
2690 q++;
2691 }
2692
2693#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2694 *r++=(*p++);
2695 p++; /* strip low byte */
2696
2697 if (ping_color_type == 4)
2698 {
2699 q->opacity=(Quantum) (QuantumRange-(*p++));
glennrp9d0ea4d2011-04-22 18:35:57 +00002700 if (q->opacity != OpaqueOpacity)
2701 found_transparent_pixel = MagickTrue;
2702 p++;
2703 q++;
2704 }
cristy3ed852e2009-09-05 21:47:34 +00002705#endif
glennrp58f77c72011-04-23 14:09:09 +00002706#endif
glennrpa18d5bc2011-04-23 14:51:34 +00002707 }
glennrp47b9dd52010-11-24 18:12:06 +00002708
cristy3ed852e2009-09-05 21:47:34 +00002709 break;
2710 }
glennrp47b9dd52010-11-24 18:12:06 +00002711
cristy3ed852e2009-09-05 21:47:34 +00002712 default:
2713 break;
2714 }
glennrp3faa9a32011-04-23 14:00:25 +00002715
cristy3ed852e2009-09-05 21:47:34 +00002716 /*
2717 Transfer image scanline.
2718 */
2719 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00002720
cristybb503372010-05-27 20:51:26 +00002721 for (x=0; x < (ssize_t) image->columns; x++)
cristy80ac8b92010-05-08 13:36:57 +00002722 indexes[x]=(IndexPacket) (*r++);
glennrp0fe50b42010-11-16 03:52:51 +00002723
cristy3ed852e2009-09-05 21:47:34 +00002724 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2725 break;
glennrp0fe50b42010-11-16 03:52:51 +00002726
cristy7a287bf2010-02-14 02:18:09 +00002727 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2728 {
cristycee97112010-05-28 00:44:52 +00002729 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2730 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00002731
cristy7a287bf2010-02-14 02:18:09 +00002732 if (status == MagickFalse)
2733 break;
2734 }
cristy3ed852e2009-09-05 21:47:34 +00002735 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002736
cristy7a287bf2010-02-14 02:18:09 +00002737 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002738 {
2739 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00002740
cristy3ed852e2009-09-05 21:47:34 +00002741 if (status == MagickFalse)
2742 break;
2743 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002744
cristy3ed852e2009-09-05 21:47:34 +00002745 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2746 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002747
2748 image->matte=found_transparent_pixel;
2749
2750 if (logging != MagickFalse)
2751 {
2752 if (found_transparent_pixel != MagickFalse)
2753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2754 " Found transparent pixel");
2755 else
glennrp5aa37f62011-01-02 03:07:57 +00002756 {
2757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2758 " No transparent pixel was found");
glennrpa6a06632011-01-19 15:15:34 +00002759
glennrp5aa37f62011-01-02 03:07:57 +00002760 ping_color_type&=0x03;
2761 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002762 }
2763 }
2764
cristyb32b90a2009-09-07 21:45:48 +00002765 if (quantum_info != (QuantumInfo *) NULL)
2766 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002767
cristy5c6f7892010-05-05 22:53:29 +00002768 if (image->storage_class == PseudoClass)
2769 {
cristyaeb2cbc2010-05-07 13:28:58 +00002770 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002771 matte;
2772
2773 matte=image->matte;
2774 image->matte=MagickFalse;
2775 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002776 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002777 }
glennrp47b9dd52010-11-24 18:12:06 +00002778
glennrp4eb39312011-03-30 21:34:55 +00002779 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00002780
2781 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002782 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002783 {
2784 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00002785 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002786 image->colors=2;
2787 (void) SetImageBackgroundColor(image);
2788#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002789 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002790#endif
2791 if (logging != MagickFalse)
2792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2793 " exit ReadOnePNGImage() early.");
2794 return(image);
2795 }
glennrp47b9dd52010-11-24 18:12:06 +00002796
glennrpfaa852b2010-03-30 12:17:00 +00002797 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002798 {
2799 ClassType
2800 storage_class;
2801
2802 /*
2803 Image has a transparent background.
2804 */
2805 storage_class=image->storage_class;
2806 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002807
glennrp3c218112010-11-27 15:31:26 +00002808/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00002809
glennrp0fe50b42010-11-16 03:52:51 +00002810 if (storage_class == PseudoClass)
2811 {
2812 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002813 {
glennrp0fe50b42010-11-16 03:52:51 +00002814 for (x=0; x < ping_num_trans; x++)
2815 {
2816 image->colormap[x].opacity =
2817 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2818 }
glennrpc11cf6a2010-03-20 16:46:19 +00002819 }
glennrp47b9dd52010-11-24 18:12:06 +00002820
glennrp0fe50b42010-11-16 03:52:51 +00002821 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2822 {
2823 for (x=0; x < (int) image->colors; x++)
2824 {
2825 if (ScaleQuantumToShort(image->colormap[x].red) ==
2826 transparent_color.opacity)
2827 {
2828 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2829 }
2830 }
2831 }
2832 (void) SyncImage(image);
2833 }
glennrp47b9dd52010-11-24 18:12:06 +00002834
glennrpa6a06632011-01-19 15:15:34 +00002835#if 1 /* Should have already been done above, but glennrp problem P10
2836 * needs this.
2837 */
glennrp0fe50b42010-11-16 03:52:51 +00002838 else
2839 {
2840 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002841 {
glennrp0fe50b42010-11-16 03:52:51 +00002842 image->storage_class=storage_class;
2843 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2844
2845 if (q == (PixelPacket *) NULL)
2846 break;
2847
2848 indexes=GetAuthenticIndexQueue(image);
2849
glennrpa6a06632011-01-19 15:15:34 +00002850 /* Caution: on a Q8 build, this does not distinguish between
2851 * 16-bit colors that differ only in the low byte
2852 */
glennrp0fe50b42010-11-16 03:52:51 +00002853 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2854 {
glennrpa6a06632011-01-19 15:15:34 +00002855 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2856 ScaleQuantumToShort(q->green) == transparent_color.green &&
2857 ScaleQuantumToShort(q->blue) == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00002858 {
glennrp0fe50b42010-11-16 03:52:51 +00002859 q->opacity=(Quantum) TransparentOpacity;
glennrp4f25bd02011-01-01 18:51:28 +00002860 }
glennrp0fe50b42010-11-16 03:52:51 +00002861
glennrp67b9c1a2011-04-22 18:47:36 +00002862#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00002863 else
glennrp4f25bd02011-01-01 18:51:28 +00002864 {
2865 q->opacity=(Quantum) OpaqueOpacity;
2866 }
glennrpa6a06632011-01-19 15:15:34 +00002867#endif
glennrp0fe50b42010-11-16 03:52:51 +00002868
2869 q++;
2870 }
2871
2872 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2873 break;
glennrpc11cf6a2010-03-20 16:46:19 +00002874 }
glennrp0fe50b42010-11-16 03:52:51 +00002875 }
glennrpa6a06632011-01-19 15:15:34 +00002876#endif
glennrpc11cf6a2010-03-20 16:46:19 +00002877
cristy3ed852e2009-09-05 21:47:34 +00002878 image->storage_class=DirectClass;
2879 }
glennrp3c218112010-11-27 15:31:26 +00002880
cristyb40fc462010-08-08 00:49:49 +00002881 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2882 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2883 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00002884
cristyeb3b22a2011-03-31 20:16:11 +00002885 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00002886 {
2887 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00002888 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2889 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00002890 else
glennrpa0ed0092011-04-18 16:36:29 +00002891 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2892 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002893
glennrp4eb39312011-03-30 21:34:55 +00002894 if (status != MagickFalse)
2895 for (i=0; i < (ssize_t) num_text; i++)
2896 {
2897 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00002898
glennrp4eb39312011-03-30 21:34:55 +00002899 if (logging != MagickFalse)
2900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2901 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00002902
glennrp4eb39312011-03-30 21:34:55 +00002903 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00002904 {
glennrp4eb39312011-03-30 21:34:55 +00002905 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2906 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00002907 }
glennrp0fe50b42010-11-16 03:52:51 +00002908
glennrp4eb39312011-03-30 21:34:55 +00002909 else
2910 {
2911 char
2912 *value;
2913
2914 length=text[i].text_length;
2915 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2916 sizeof(*value));
2917 if (value == (char *) NULL)
2918 {
2919 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2920 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2921 image->filename);
2922 break;
2923 }
2924 *value='\0';
2925 (void) ConcatenateMagickString(value,text[i].text,length+2);
2926
2927 /* Don't save "density" or "units" property if we have a pHYs
2928 * chunk
2929 */
2930 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2931 (LocaleCompare(text[i].key,"density") != 0 &&
2932 LocaleCompare(text[i].key,"units") != 0))
2933 (void) SetImageProperty(image,text[i].key,value);
2934
2935 if (logging != MagickFalse)
2936 {
2937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2938 " length: %lu",(unsigned long) length);
2939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2940 " Keyword: %s",text[i].key);
2941 }
2942
2943 value=DestroyString(value);
2944 }
2945 }
2946 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00002947 }
glennrp3c218112010-11-27 15:31:26 +00002948
cristy3ed852e2009-09-05 21:47:34 +00002949#ifdef MNG_OBJECT_BUFFERS
2950 /*
2951 Store the object if necessary.
2952 */
2953 if (object_id && !mng_info->frozen[object_id])
2954 {
2955 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2956 {
2957 /*
2958 create a new object buffer.
2959 */
2960 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00002961 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00002962
cristy3ed852e2009-09-05 21:47:34 +00002963 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2964 {
2965 mng_info->ob[object_id]->image=(Image *) NULL;
2966 mng_info->ob[object_id]->reference_count=1;
2967 }
2968 }
glennrp47b9dd52010-11-24 18:12:06 +00002969
cristy3ed852e2009-09-05 21:47:34 +00002970 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2971 mng_info->ob[object_id]->frozen)
2972 {
2973 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2974 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2975 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2976 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00002977
cristy3ed852e2009-09-05 21:47:34 +00002978 if (mng_info->ob[object_id]->frozen)
2979 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2980 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2981 "`%s'",image->filename);
2982 }
glennrp0fe50b42010-11-16 03:52:51 +00002983
cristy3ed852e2009-09-05 21:47:34 +00002984 else
2985 {
cristy3ed852e2009-09-05 21:47:34 +00002986
2987 if (mng_info->ob[object_id]->image != (Image *) NULL)
2988 mng_info->ob[object_id]->image=DestroyImage
2989 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00002990
cristy3ed852e2009-09-05 21:47:34 +00002991 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2992 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00002993
cristy3ed852e2009-09-05 21:47:34 +00002994 if (mng_info->ob[object_id]->image != (Image *) NULL)
2995 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002996
cristy3ed852e2009-09-05 21:47:34 +00002997 else
2998 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2999 ResourceLimitError,"Cloning image for object buffer failed",
3000 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003001
glennrpfaa852b2010-03-30 12:17:00 +00003002 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003003 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003004
glennrpfaa852b2010-03-30 12:17:00 +00003005 mng_info->ob[object_id]->width=ping_width;
3006 mng_info->ob[object_id]->height=ping_height;
3007 mng_info->ob[object_id]->color_type=ping_color_type;
3008 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3009 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3010 mng_info->ob[object_id]->compression_method=
3011 ping_compression_method;
3012 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003013
glennrpfaa852b2010-03-30 12:17:00 +00003014 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003015 {
3016 int
3017 number_colors;
3018
3019 png_colorp
3020 plte;
3021
3022 /*
3023 Copy the PLTE to the object buffer.
3024 */
3025 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3026 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003027
cristy3ed852e2009-09-05 21:47:34 +00003028 for (i=0; i < number_colors; i++)
3029 {
3030 mng_info->ob[object_id]->plte[i]=plte[i];
3031 }
3032 }
glennrp47b9dd52010-11-24 18:12:06 +00003033
cristy3ed852e2009-09-05 21:47:34 +00003034 else
3035 mng_info->ob[object_id]->plte_length=0;
3036 }
3037 }
3038#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003039
3040 /* Set image->matte to MagickTrue if the input colortype supports
3041 * alpha or if a valid tRNS chunk is present, no matter whether there
3042 * is actual transparency present.
3043 */
3044 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3045 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3046 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3047 MagickTrue : MagickFalse;
3048
glennrpcb395ac2011-03-30 19:50:23 +00003049 /* Set more properties for identify to retrieve */
3050 {
3051 char
3052 msg[MaxTextExtent];
3053
glennrp4eb39312011-03-30 21:34:55 +00003054 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003055 {
3056 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3057 (void) FormatMagickString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003058 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003059 (void) SetImageProperty(image,"PNG:text ",msg);
3060 }
3061
3062 if (num_raw_profiles != 0)
3063 {
3064 (void) FormatMagickString(msg,MaxTextExtent,
3065 "%d were found", num_raw_profiles);
3066 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3067 }
3068
glennrpcb395ac2011-03-30 19:50:23 +00003069 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003070 {
3071 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3072 "chunk was found (see Chromaticity, above)");
3073 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3074 }
glennrpcb395ac2011-03-30 19:50:23 +00003075
3076 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003077 {
3078 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3079 "chunk was found (see Background color, above)");
3080 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3081 }
3082
3083 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3084 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003085
3086 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3087 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3088
glennrpcb395ac2011-03-30 19:50:23 +00003089 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3090 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003091
3092#if defined(PNG_sRGB_SUPPORTED)
3093 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3094 {
glennrp07523c72011-03-31 18:12:10 +00003095 (void) FormatMagickString(msg,MaxTextExtent,
3096 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003097 (int) intent);
3098 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3099 }
3100#endif
3101
3102 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3103 {
glennrp07523c72011-03-31 18:12:10 +00003104 (void) FormatMagickString(msg,MaxTextExtent,
3105 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003106 file_gamma);
3107 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3108 }
3109
3110#if defined(PNG_pHYs_SUPPORTED)
3111 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3112 {
glennrp07523c72011-03-31 18:12:10 +00003113 (void) FormatMagickString(msg,MaxTextExtent,
3114 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003115 (double) x_resolution,(double) y_resolution, unit_type);
3116 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3117 }
3118#endif
3119
3120#if defined(PNG_oFFs_SUPPORTED)
3121 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3122 {
3123 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3124 (double) image->page.x,(double) image->page.y);
3125 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3126 }
3127#endif
3128
glennrp07523c72011-03-31 18:12:10 +00003129 if ((image->page.width != 0 && image->page.width != image->columns) ||
3130 (image->page.height != 0 && image->page.height != image->rows))
3131 {
3132 (void) FormatMagickString(msg,MaxTextExtent,
3133 "width=%.20g, height=%.20g",
3134 (double) image->page.width,(double) image->page.height);
3135 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3136 }
glennrpcb395ac2011-03-30 19:50:23 +00003137 }
3138
cristy3ed852e2009-09-05 21:47:34 +00003139 /*
3140 Relinquish resources.
3141 */
3142 png_destroy_read_struct(&ping,&ping_info,&end_info);
3143
glennrpcf002022011-01-30 02:38:15 +00003144 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003145#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003146 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003147#endif
3148
3149 if (logging != MagickFalse)
3150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3151 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003152
cristy3ed852e2009-09-05 21:47:34 +00003153 return(image);
3154
3155/* end of reading one PNG image */
3156}
3157
3158static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3159{
3160 Image
3161 *image,
3162 *previous;
3163
3164 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003165 have_mng_structure,
3166 logging,
cristy3ed852e2009-09-05 21:47:34 +00003167 status;
3168
3169 MngInfo
3170 *mng_info;
3171
3172 char
3173 magic_number[MaxTextExtent];
3174
cristy3ed852e2009-09-05 21:47:34 +00003175 ssize_t
3176 count;
3177
3178 /*
3179 Open image file.
3180 */
3181 assert(image_info != (const ImageInfo *) NULL);
3182 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003183
cristy3ed852e2009-09-05 21:47:34 +00003184 if (image_info->debug != MagickFalse)
3185 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3186 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003187
cristy3ed852e2009-09-05 21:47:34 +00003188 assert(exception != (ExceptionInfo *) NULL);
3189 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003190 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003191 image=AcquireImage(image_info);
3192 mng_info=(MngInfo *) NULL;
3193 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003194
cristy3ed852e2009-09-05 21:47:34 +00003195 if (status == MagickFalse)
3196 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003197
cristy3ed852e2009-09-05 21:47:34 +00003198 /*
3199 Verify PNG signature.
3200 */
3201 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003202
glennrpdde35db2011-02-21 12:06:32 +00003203 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003204 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003205
cristy3ed852e2009-09-05 21:47:34 +00003206 /*
3207 Allocate a MngInfo structure.
3208 */
3209 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003210 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003211
cristy3ed852e2009-09-05 21:47:34 +00003212 if (mng_info == (MngInfo *) NULL)
3213 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003214
cristy3ed852e2009-09-05 21:47:34 +00003215 /*
3216 Initialize members of the MngInfo structure.
3217 */
3218 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3219 mng_info->image=image;
3220 have_mng_structure=MagickTrue;
3221
3222 previous=image;
3223 image=ReadOnePNGImage(mng_info,image_info,exception);
3224 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003225
cristy3ed852e2009-09-05 21:47:34 +00003226 if (image == (Image *) NULL)
3227 {
3228 if (previous != (Image *) NULL)
3229 {
3230 if (previous->signature != MagickSignature)
3231 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003232
cristy3ed852e2009-09-05 21:47:34 +00003233 (void) CloseBlob(previous);
3234 (void) DestroyImageList(previous);
3235 }
glennrp0fe50b42010-11-16 03:52:51 +00003236
cristy3ed852e2009-09-05 21:47:34 +00003237 if (logging != MagickFalse)
3238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3239 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003240
cristy3ed852e2009-09-05 21:47:34 +00003241 return((Image *) NULL);
3242 }
glennrp47b9dd52010-11-24 18:12:06 +00003243
cristy3ed852e2009-09-05 21:47:34 +00003244 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003245
cristy3ed852e2009-09-05 21:47:34 +00003246 if ((image->columns == 0) || (image->rows == 0))
3247 {
3248 if (logging != MagickFalse)
3249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3250 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003251
cristy3ed852e2009-09-05 21:47:34 +00003252 ThrowReaderException(CorruptImageError,"CorruptImage");
3253 }
glennrp47b9dd52010-11-24 18:12:06 +00003254
glennrp3faa9a32011-04-23 14:00:25 +00003255#if 0 /* This is probably redundant now */
cristy3ed852e2009-09-05 21:47:34 +00003256 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3257 {
3258 (void) SetImageType(image,PaletteType);
glennrp0fe50b42010-11-16 03:52:51 +00003259
cristy3ed852e2009-09-05 21:47:34 +00003260 if (image->matte != MagickFalse)
3261 {
3262 /* To do: Reduce to binary transparency */
3263 }
3264 }
glennrp3faa9a32011-04-23 14:00:25 +00003265#endif
glennrp47b9dd52010-11-24 18:12:06 +00003266
cristy3ed852e2009-09-05 21:47:34 +00003267 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3268 {
3269 (void) SetImageType(image,TrueColorType);
3270 image->matte=MagickFalse;
3271 }
glennrp0fe50b42010-11-16 03:52:51 +00003272
cristy3ed852e2009-09-05 21:47:34 +00003273 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3274 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003275
cristy3ed852e2009-09-05 21:47:34 +00003276 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3278 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3279 (double) image->page.width,(double) image->page.height,
3280 (double) image->page.x,(double) image->page.y);
3281
3282 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003284
cristy3ed852e2009-09-05 21:47:34 +00003285 return(image);
3286}
3287
3288
3289
3290#if defined(JNG_SUPPORTED)
3291/*
3292%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3293% %
3294% %
3295% %
3296% R e a d O n e J N G I m a g e %
3297% %
3298% %
3299% %
3300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3301%
3302% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3303% (minus the 8-byte signature) and returns it. It allocates the memory
3304% necessary for the new Image structure and returns a pointer to the new
3305% image.
3306%
3307% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3308%
3309% The format of the ReadOneJNGImage method is:
3310%
3311% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3312% ExceptionInfo *exception)
3313%
3314% A description of each parameter follows:
3315%
3316% o mng_info: Specifies a pointer to a MngInfo structure.
3317%
3318% o image_info: the image info.
3319%
3320% o exception: return any errors or warnings in this structure.
3321%
3322*/
3323static Image *ReadOneJNGImage(MngInfo *mng_info,
3324 const ImageInfo *image_info, ExceptionInfo *exception)
3325{
3326 Image
3327 *alpha_image,
3328 *color_image,
3329 *image,
3330 *jng_image;
3331
3332 ImageInfo
3333 *alpha_image_info,
3334 *color_image_info;
3335
cristy4383ec82011-01-05 15:42:32 +00003336 MagickBooleanType
3337 logging;
3338
cristybb503372010-05-27 20:51:26 +00003339 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003340 y;
3341
3342 MagickBooleanType
3343 status;
3344
3345 png_uint_32
3346 jng_height,
3347 jng_width;
3348
3349 png_byte
3350 jng_color_type,
3351 jng_image_sample_depth,
3352 jng_image_compression_method,
3353 jng_image_interlace_method,
3354 jng_alpha_sample_depth,
3355 jng_alpha_compression_method,
3356 jng_alpha_filter_method,
3357 jng_alpha_interlace_method;
3358
3359 register const PixelPacket
3360 *s;
3361
cristybb503372010-05-27 20:51:26 +00003362 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003363 i,
3364 x;
3365
3366 register PixelPacket
3367 *q;
3368
3369 register unsigned char
3370 *p;
3371
3372 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003373 read_JSEP,
3374 reading_idat,
3375 skip_to_iend;
3376
cristybb503372010-05-27 20:51:26 +00003377 size_t
cristy3ed852e2009-09-05 21:47:34 +00003378 length;
3379
3380 jng_alpha_compression_method=0;
3381 jng_alpha_sample_depth=8;
3382 jng_color_type=0;
3383 jng_height=0;
3384 jng_width=0;
3385 alpha_image=(Image *) NULL;
3386 color_image=(Image *) NULL;
3387 alpha_image_info=(ImageInfo *) NULL;
3388 color_image_info=(ImageInfo *) NULL;
3389
3390 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003391 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003392
3393 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003394
cristy3ed852e2009-09-05 21:47:34 +00003395 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3396 {
3397 /*
3398 Allocate next image structure.
3399 */
3400 if (logging != MagickFalse)
3401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3402 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003403
cristy3ed852e2009-09-05 21:47:34 +00003404 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003405
cristy3ed852e2009-09-05 21:47:34 +00003406 if (GetNextImageInList(image) == (Image *) NULL)
3407 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003408
cristy3ed852e2009-09-05 21:47:34 +00003409 image=SyncNextImageInList(image);
3410 }
3411 mng_info->image=image;
3412
3413 /*
3414 Signature bytes have already been read.
3415 */
3416
3417 read_JSEP=MagickFalse;
3418 reading_idat=MagickFalse;
3419 skip_to_iend=MagickFalse;
3420 for (;;)
3421 {
3422 char
3423 type[MaxTextExtent];
3424
3425 unsigned char
3426 *chunk;
3427
3428 unsigned int
3429 count;
3430
3431 /*
3432 Read a new JNG chunk.
3433 */
3434 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3435 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003436
cristy3ed852e2009-09-05 21:47:34 +00003437 if (status == MagickFalse)
3438 break;
glennrp0fe50b42010-11-16 03:52:51 +00003439
cristy3ed852e2009-09-05 21:47:34 +00003440 type[0]='\0';
3441 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3442 length=ReadBlobMSBLong(image);
3443 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3444
3445 if (logging != MagickFalse)
3446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003447 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3448 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003449
3450 if (length > PNG_UINT_31_MAX || count == 0)
3451 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003452
cristy3ed852e2009-09-05 21:47:34 +00003453 p=NULL;
3454 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003455
cristy3ed852e2009-09-05 21:47:34 +00003456 if (length)
3457 {
3458 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003459
cristy3ed852e2009-09-05 21:47:34 +00003460 if (chunk == (unsigned char *) NULL)
3461 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003462
cristybb503372010-05-27 20:51:26 +00003463 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003464 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003465
cristy3ed852e2009-09-05 21:47:34 +00003466 p=chunk;
3467 }
glennrp47b9dd52010-11-24 18:12:06 +00003468
cristy3ed852e2009-09-05 21:47:34 +00003469 (void) ReadBlobMSBLong(image); /* read crc word */
3470
3471 if (skip_to_iend)
3472 {
3473 if (length)
3474 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003475
cristy3ed852e2009-09-05 21:47:34 +00003476 continue;
3477 }
3478
3479 if (memcmp(type,mng_JHDR,4) == 0)
3480 {
3481 if (length == 16)
3482 {
cristybb503372010-05-27 20:51:26 +00003483 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003484 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003485 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003486 (p[6] << 8) | p[7]);
3487 jng_color_type=p[8];
3488 jng_image_sample_depth=p[9];
3489 jng_image_compression_method=p[10];
3490 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003491
cristy3ed852e2009-09-05 21:47:34 +00003492 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3493 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003494
cristy3ed852e2009-09-05 21:47:34 +00003495 jng_alpha_sample_depth=p[12];
3496 jng_alpha_compression_method=p[13];
3497 jng_alpha_filter_method=p[14];
3498 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003499
cristy3ed852e2009-09-05 21:47:34 +00003500 if (logging != MagickFalse)
3501 {
3502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003503 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003504
cristy3ed852e2009-09-05 21:47:34 +00003505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003506 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003507
cristy3ed852e2009-09-05 21:47:34 +00003508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3509 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003510
cristy3ed852e2009-09-05 21:47:34 +00003511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3512 " jng_image_sample_depth: %3d",
3513 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003514
cristy3ed852e2009-09-05 21:47:34 +00003515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3516 " jng_image_compression_method:%3d",
3517 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003518
cristy3ed852e2009-09-05 21:47:34 +00003519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3520 " jng_image_interlace_method: %3d",
3521 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003522
cristy3ed852e2009-09-05 21:47:34 +00003523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3524 " jng_alpha_sample_depth: %3d",
3525 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003526
cristy3ed852e2009-09-05 21:47:34 +00003527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3528 " jng_alpha_compression_method:%3d",
3529 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003530
cristy3ed852e2009-09-05 21:47:34 +00003531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3532 " jng_alpha_filter_method: %3d",
3533 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003534
cristy3ed852e2009-09-05 21:47:34 +00003535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3536 " jng_alpha_interlace_method: %3d",
3537 jng_alpha_interlace_method);
3538 }
3539 }
glennrp47b9dd52010-11-24 18:12:06 +00003540
cristy3ed852e2009-09-05 21:47:34 +00003541 if (length)
3542 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003543
cristy3ed852e2009-09-05 21:47:34 +00003544 continue;
3545 }
3546
3547
3548 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3549 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3550 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3551 {
3552 /*
3553 o create color_image
3554 o open color_blob, attached to color_image
3555 o if (color type has alpha)
3556 open alpha_blob, attached to alpha_image
3557 */
3558
cristy73bd4a52010-10-05 11:24:23 +00003559 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003560
cristy3ed852e2009-09-05 21:47:34 +00003561 if (color_image_info == (ImageInfo *) NULL)
3562 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003563
cristy3ed852e2009-09-05 21:47:34 +00003564 GetImageInfo(color_image_info);
3565 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003566
cristy3ed852e2009-09-05 21:47:34 +00003567 if (color_image == (Image *) NULL)
3568 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3569
3570 if (logging != MagickFalse)
3571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3572 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003573
cristy3ed852e2009-09-05 21:47:34 +00003574 (void) AcquireUniqueFilename(color_image->filename);
3575 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3576 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003577
cristy3ed852e2009-09-05 21:47:34 +00003578 if (status == MagickFalse)
3579 return((Image *) NULL);
3580
3581 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3582 {
3583 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00003584 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00003585
cristy3ed852e2009-09-05 21:47:34 +00003586 if (alpha_image_info == (ImageInfo *) NULL)
3587 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003588
cristy3ed852e2009-09-05 21:47:34 +00003589 GetImageInfo(alpha_image_info);
3590 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003591
cristy3ed852e2009-09-05 21:47:34 +00003592 if (alpha_image == (Image *) NULL)
3593 {
3594 alpha_image=DestroyImage(alpha_image);
3595 ThrowReaderException(ResourceLimitError,
3596 "MemoryAllocationFailed");
3597 }
glennrp0fe50b42010-11-16 03:52:51 +00003598
cristy3ed852e2009-09-05 21:47:34 +00003599 if (logging != MagickFalse)
3600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3601 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003602
cristy3ed852e2009-09-05 21:47:34 +00003603 (void) AcquireUniqueFilename(alpha_image->filename);
3604 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3605 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003606
cristy3ed852e2009-09-05 21:47:34 +00003607 if (status == MagickFalse)
3608 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003609
cristy3ed852e2009-09-05 21:47:34 +00003610 if (jng_alpha_compression_method == 0)
3611 {
3612 unsigned char
3613 data[18];
3614
3615 if (logging != MagickFalse)
3616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3617 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003618
cristy3ed852e2009-09-05 21:47:34 +00003619 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3620 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00003621
cristy3ed852e2009-09-05 21:47:34 +00003622 (void) WriteBlobMSBULong(alpha_image,13L);
3623 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00003624 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00003625 PNGLong(data+4,jng_width);
3626 PNGLong(data+8,jng_height);
3627 data[12]=jng_alpha_sample_depth;
3628 data[13]=0; /* color_type gray */
3629 data[14]=0; /* compression method 0 */
3630 data[15]=0; /* filter_method 0 */
3631 data[16]=0; /* interlace_method 0 */
3632 (void) WriteBlob(alpha_image,17,data);
3633 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3634 }
3635 }
3636 reading_idat=MagickTrue;
3637 }
3638
3639 if (memcmp(type,mng_JDAT,4) == 0)
3640 {
glennrp47b9dd52010-11-24 18:12:06 +00003641 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003642
3643 if (logging != MagickFalse)
3644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3645 " Copying JDAT chunk data to color_blob.");
3646
3647 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003648
cristy3ed852e2009-09-05 21:47:34 +00003649 if (length)
3650 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003651
cristy3ed852e2009-09-05 21:47:34 +00003652 continue;
3653 }
3654
3655 if (memcmp(type,mng_IDAT,4) == 0)
3656 {
3657 png_byte
3658 data[5];
3659
glennrp47b9dd52010-11-24 18:12:06 +00003660 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003661
3662 if (image_info->ping == MagickFalse)
3663 {
3664 if (logging != MagickFalse)
3665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3666 " Copying IDAT chunk data to alpha_blob.");
3667
cristybb503372010-05-27 20:51:26 +00003668 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003669 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00003670 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00003671 (void) WriteBlob(alpha_image,4,data);
3672 (void) WriteBlob(alpha_image,length,chunk);
3673 (void) WriteBlobMSBULong(alpha_image,
3674 crc32(crc32(0,data,4),chunk,(uInt) length));
3675 }
glennrp0fe50b42010-11-16 03:52:51 +00003676
cristy3ed852e2009-09-05 21:47:34 +00003677 if (length)
3678 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003679
cristy3ed852e2009-09-05 21:47:34 +00003680 continue;
3681 }
3682
3683 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3684 {
glennrp47b9dd52010-11-24 18:12:06 +00003685 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003686
3687 if (image_info->ping == MagickFalse)
3688 {
3689 if (logging != MagickFalse)
3690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3691 " Copying JDAA chunk data to alpha_blob.");
3692
3693 (void) WriteBlob(alpha_image,length,chunk);
3694 }
glennrp0fe50b42010-11-16 03:52:51 +00003695
cristy3ed852e2009-09-05 21:47:34 +00003696 if (length)
3697 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003698
cristy3ed852e2009-09-05 21:47:34 +00003699 continue;
3700 }
3701
3702 if (memcmp(type,mng_JSEP,4) == 0)
3703 {
3704 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00003705
cristy3ed852e2009-09-05 21:47:34 +00003706 if (length)
3707 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003708
cristy3ed852e2009-09-05 21:47:34 +00003709 continue;
3710 }
3711
3712 if (memcmp(type,mng_bKGD,4) == 0)
3713 {
3714 if (length == 2)
3715 {
3716 image->background_color.red=ScaleCharToQuantum(p[1]);
3717 image->background_color.green=image->background_color.red;
3718 image->background_color.blue=image->background_color.red;
3719 }
glennrp0fe50b42010-11-16 03:52:51 +00003720
cristy3ed852e2009-09-05 21:47:34 +00003721 if (length == 6)
3722 {
3723 image->background_color.red=ScaleCharToQuantum(p[1]);
3724 image->background_color.green=ScaleCharToQuantum(p[3]);
3725 image->background_color.blue=ScaleCharToQuantum(p[5]);
3726 }
glennrp0fe50b42010-11-16 03:52:51 +00003727
cristy3ed852e2009-09-05 21:47:34 +00003728 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3729 continue;
3730 }
3731
3732 if (memcmp(type,mng_gAMA,4) == 0)
3733 {
3734 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00003735 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00003736
cristy3ed852e2009-09-05 21:47:34 +00003737 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3738 continue;
3739 }
3740
3741 if (memcmp(type,mng_cHRM,4) == 0)
3742 {
3743 if (length == 32)
3744 {
cristy8182b072010-05-30 20:10:53 +00003745 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3746 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3747 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3748 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3749 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3750 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3751 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3752 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003753 }
glennrp47b9dd52010-11-24 18:12:06 +00003754
cristy3ed852e2009-09-05 21:47:34 +00003755 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3756 continue;
3757 }
3758
3759 if (memcmp(type,mng_sRGB,4) == 0)
3760 {
3761 if (length == 1)
3762 {
glennrpe610a072010-08-05 17:08:46 +00003763 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00003764 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00003765 image->gamma=0.45455f;
3766 image->chromaticity.red_primary.x=0.6400f;
3767 image->chromaticity.red_primary.y=0.3300f;
3768 image->chromaticity.green_primary.x=0.3000f;
3769 image->chromaticity.green_primary.y=0.6000f;
3770 image->chromaticity.blue_primary.x=0.1500f;
3771 image->chromaticity.blue_primary.y=0.0600f;
3772 image->chromaticity.white_point.x=0.3127f;
3773 image->chromaticity.white_point.y=0.3290f;
3774 }
glennrp47b9dd52010-11-24 18:12:06 +00003775
cristy3ed852e2009-09-05 21:47:34 +00003776 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3777 continue;
3778 }
3779
3780 if (memcmp(type,mng_oFFs,4) == 0)
3781 {
3782 if (length > 8)
3783 {
glennrp5eae7602011-02-22 15:21:32 +00003784 image->page.x=(ssize_t) mng_get_long(p);
3785 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00003786
cristy3ed852e2009-09-05 21:47:34 +00003787 if ((int) p[8] != 0)
3788 {
3789 image->page.x/=10000;
3790 image->page.y/=10000;
3791 }
3792 }
glennrp47b9dd52010-11-24 18:12:06 +00003793
cristy3ed852e2009-09-05 21:47:34 +00003794 if (length)
3795 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003796
cristy3ed852e2009-09-05 21:47:34 +00003797 continue;
3798 }
3799
3800 if (memcmp(type,mng_pHYs,4) == 0)
3801 {
3802 if (length > 8)
3803 {
cristy8182b072010-05-30 20:10:53 +00003804 image->x_resolution=(double) mng_get_long(p);
3805 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003806 if ((int) p[8] == PNG_RESOLUTION_METER)
3807 {
3808 image->units=PixelsPerCentimeterResolution;
3809 image->x_resolution=image->x_resolution/100.0f;
3810 image->y_resolution=image->y_resolution/100.0f;
3811 }
3812 }
glennrp0fe50b42010-11-16 03:52:51 +00003813
cristy3ed852e2009-09-05 21:47:34 +00003814 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3815 continue;
3816 }
3817
3818#if 0
3819 if (memcmp(type,mng_iCCP,4) == 0)
3820 {
glennrpfd05d622011-02-25 04:10:33 +00003821 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00003822 if (length)
3823 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003824
cristy3ed852e2009-09-05 21:47:34 +00003825 continue;
3826 }
3827#endif
3828
3829 if (length)
3830 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3831
3832 if (memcmp(type,mng_IEND,4))
3833 continue;
glennrp0fe50b42010-11-16 03:52:51 +00003834
cristy3ed852e2009-09-05 21:47:34 +00003835 break;
3836 }
3837
3838
3839 /* IEND found */
3840
3841 /*
3842 Finish up reading image data:
3843
3844 o read main image from color_blob.
3845
3846 o close color_blob.
3847
3848 o if (color_type has alpha)
3849 if alpha_encoding is PNG
3850 read secondary image from alpha_blob via ReadPNG
3851 if alpha_encoding is JPEG
3852 read secondary image from alpha_blob via ReadJPEG
3853
3854 o close alpha_blob.
3855
3856 o copy intensity of secondary image into
3857 opacity samples of main image.
3858
3859 o destroy the secondary image.
3860 */
3861
3862 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00003863
cristy3ed852e2009-09-05 21:47:34 +00003864 if (logging != MagickFalse)
3865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3866 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003867
cristy3ed852e2009-09-05 21:47:34 +00003868 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3869 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003870
cristy3ed852e2009-09-05 21:47:34 +00003871 color_image_info->ping=MagickFalse; /* To do: avoid this */
3872 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003873
cristy3ed852e2009-09-05 21:47:34 +00003874 if (jng_image == (Image *) NULL)
3875 return((Image *) NULL);
3876
3877 (void) RelinquishUniqueFileResource(color_image->filename);
3878 color_image=DestroyImage(color_image);
3879 color_image_info=DestroyImageInfo(color_image_info);
3880
3881 if (jng_image == (Image *) NULL)
3882 return((Image *) NULL);
3883
3884 if (logging != MagickFalse)
3885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3886 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00003887
cristy3ed852e2009-09-05 21:47:34 +00003888 image->rows=jng_height;
3889 image->columns=jng_width;
3890 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00003891
cristybb503372010-05-27 20:51:26 +00003892 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003893 {
3894 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3895 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3896 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00003897
cristy3ed852e2009-09-05 21:47:34 +00003898 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3899 break;
3900 }
glennrp0fe50b42010-11-16 03:52:51 +00003901
cristy3ed852e2009-09-05 21:47:34 +00003902 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00003903
cristy3ed852e2009-09-05 21:47:34 +00003904 if (image_info->ping == MagickFalse)
3905 {
3906 if (jng_color_type >= 12)
3907 {
3908 if (jng_alpha_compression_method == 0)
3909 {
3910 png_byte
3911 data[5];
3912 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3913 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00003914 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00003915 (void) WriteBlob(alpha_image,4,data);
3916 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3917 }
glennrp0fe50b42010-11-16 03:52:51 +00003918
cristy3ed852e2009-09-05 21:47:34 +00003919 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00003920
cristy3ed852e2009-09-05 21:47:34 +00003921 if (logging != MagickFalse)
3922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3923 " Reading opacity from alpha_blob.");
3924
3925 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3926 "%s",alpha_image->filename);
3927
3928 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003929
cristy3ed852e2009-09-05 21:47:34 +00003930 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003931 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003932 {
3933 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3934 &image->exception);
3935 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003936
cristy3ed852e2009-09-05 21:47:34 +00003937 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003938 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003939 q->opacity=(Quantum) QuantumRange-s->red;
glennrp0fe50b42010-11-16 03:52:51 +00003940
cristy3ed852e2009-09-05 21:47:34 +00003941 else
cristybb503372010-05-27 20:51:26 +00003942 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003943 {
3944 q->opacity=(Quantum) QuantumRange-s->red;
3945 if (q->opacity != OpaqueOpacity)
3946 image->matte=MagickTrue;
3947 }
glennrp0fe50b42010-11-16 03:52:51 +00003948
cristy3ed852e2009-09-05 21:47:34 +00003949 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3950 break;
3951 }
3952 (void) RelinquishUniqueFileResource(alpha_image->filename);
3953 alpha_image=DestroyImage(alpha_image);
3954 alpha_image_info=DestroyImageInfo(alpha_image_info);
3955 if (jng_image != (Image *) NULL)
3956 jng_image=DestroyImage(jng_image);
3957 }
3958 }
3959
glennrp47b9dd52010-11-24 18:12:06 +00003960 /* Read the JNG image. */
3961
cristy3ed852e2009-09-05 21:47:34 +00003962 if (mng_info->mng_type == 0)
3963 {
3964 mng_info->mng_width=jng_width;
3965 mng_info->mng_height=jng_height;
3966 }
glennrp0fe50b42010-11-16 03:52:51 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003969 {
3970 image->page.width=jng_width;
3971 image->page.height=jng_height;
3972 }
3973
cristy3ed852e2009-09-05 21:47:34 +00003974 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003975 {
3976 image->page.x=mng_info->x_off[mng_info->object_id];
3977 image->page.y=mng_info->y_off[mng_info->object_id];
3978 }
3979
cristy3ed852e2009-09-05 21:47:34 +00003980 else
glennrp0fe50b42010-11-16 03:52:51 +00003981 {
3982 image->page.y=mng_info->y_off[mng_info->object_id];
3983 }
3984
cristy3ed852e2009-09-05 21:47:34 +00003985 mng_info->image_found++;
3986 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3987 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 if (logging != MagickFalse)
3990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3991 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003992
cristy3ed852e2009-09-05 21:47:34 +00003993 return(image);
3994}
3995
3996/*
3997%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3998% %
3999% %
4000% %
4001% R e a d J N G I m a g e %
4002% %
4003% %
4004% %
4005%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4006%
4007% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4008% (including the 8-byte signature) and returns it. It allocates the memory
4009% necessary for the new Image structure and returns a pointer to the new
4010% image.
4011%
4012% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4013%
4014% The format of the ReadJNGImage method is:
4015%
4016% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4017% *exception)
4018%
4019% A description of each parameter follows:
4020%
4021% o image_info: the image info.
4022%
4023% o exception: return any errors or warnings in this structure.
4024%
4025*/
4026
4027static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4028{
4029 Image
4030 *image,
4031 *previous;
4032
4033 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004034 have_mng_structure,
4035 logging,
cristy3ed852e2009-09-05 21:47:34 +00004036 status;
4037
4038 MngInfo
4039 *mng_info;
4040
4041 char
4042 magic_number[MaxTextExtent];
4043
cristy3ed852e2009-09-05 21:47:34 +00004044 size_t
4045 count;
4046
4047 /*
4048 Open image file.
4049 */
4050 assert(image_info != (const ImageInfo *) NULL);
4051 assert(image_info->signature == MagickSignature);
4052 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4053 assert(exception != (ExceptionInfo *) NULL);
4054 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004055 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004056 image=AcquireImage(image_info);
4057 mng_info=(MngInfo *) NULL;
4058 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004059
cristy3ed852e2009-09-05 21:47:34 +00004060 if (status == MagickFalse)
4061 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004062
cristy3ed852e2009-09-05 21:47:34 +00004063 if (LocaleCompare(image_info->magick,"JNG") != 0)
4064 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004065
glennrp47b9dd52010-11-24 18:12:06 +00004066 /* Verify JNG signature. */
4067
cristy3ed852e2009-09-05 21:47:34 +00004068 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004069
glennrp3b8763e2011-02-21 12:08:18 +00004070 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004071 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004072
glennrp47b9dd52010-11-24 18:12:06 +00004073 /* Allocate a MngInfo structure. */
4074
cristy3ed852e2009-09-05 21:47:34 +00004075 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004076 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004077
cristy3ed852e2009-09-05 21:47:34 +00004078 if (mng_info == (MngInfo *) NULL)
4079 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004080
glennrp47b9dd52010-11-24 18:12:06 +00004081 /* Initialize members of the MngInfo structure. */
4082
cristy3ed852e2009-09-05 21:47:34 +00004083 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4084 have_mng_structure=MagickTrue;
4085
4086 mng_info->image=image;
4087 previous=image;
4088 image=ReadOneJNGImage(mng_info,image_info,exception);
4089 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004090
cristy3ed852e2009-09-05 21:47:34 +00004091 if (image == (Image *) NULL)
4092 {
4093 if (IsImageObject(previous) != MagickFalse)
4094 {
4095 (void) CloseBlob(previous);
4096 (void) DestroyImageList(previous);
4097 }
glennrp0fe50b42010-11-16 03:52:51 +00004098
cristy3ed852e2009-09-05 21:47:34 +00004099 if (logging != MagickFalse)
4100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4101 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004102
cristy3ed852e2009-09-05 21:47:34 +00004103 return((Image *) NULL);
4104 }
4105 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004106
cristy3ed852e2009-09-05 21:47:34 +00004107 if (image->columns == 0 || image->rows == 0)
4108 {
4109 if (logging != MagickFalse)
4110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4111 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004112
cristy3ed852e2009-09-05 21:47:34 +00004113 ThrowReaderException(CorruptImageError,"CorruptImage");
4114 }
glennrp0fe50b42010-11-16 03:52:51 +00004115
cristy3ed852e2009-09-05 21:47:34 +00004116 if (logging != MagickFalse)
4117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004118
cristy3ed852e2009-09-05 21:47:34 +00004119 return(image);
4120}
4121#endif
4122
4123static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4124{
4125 char
4126 page_geometry[MaxTextExtent];
4127
4128 Image
4129 *image,
4130 *previous;
4131
cristy4383ec82011-01-05 15:42:32 +00004132 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004133 logging,
4134 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004135
cristy3ed852e2009-09-05 21:47:34 +00004136 volatile int
4137 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004138 object_id,
4139 term_chunk_found,
4140 skip_to_iend;
4141
cristybb503372010-05-27 20:51:26 +00004142 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004143 image_count=0;
4144
4145 MagickBooleanType
4146 status;
4147
4148 MagickOffsetType
4149 offset;
4150
4151 MngInfo
4152 *mng_info;
4153
4154 MngBox
4155 default_fb,
4156 fb,
4157 previous_fb;
4158
4159#if defined(MNG_INSERT_LAYERS)
4160 PixelPacket
4161 mng_background_color;
4162#endif
4163
4164 register unsigned char
4165 *p;
4166
cristybb503372010-05-27 20:51:26 +00004167 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004168 i;
4169
4170 size_t
4171 count;
4172
cristybb503372010-05-27 20:51:26 +00004173 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004174 loop_level;
4175
4176 volatile short
4177 skipping_loop;
4178
4179#if defined(MNG_INSERT_LAYERS)
4180 unsigned int
4181 mandatory_back=0;
4182#endif
4183
4184 volatile unsigned int
4185#ifdef MNG_OBJECT_BUFFERS
4186 mng_background_object=0,
4187#endif
4188 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4189
cristybb503372010-05-27 20:51:26 +00004190 size_t
cristy3ed852e2009-09-05 21:47:34 +00004191 default_frame_timeout,
4192 frame_timeout,
4193#if defined(MNG_INSERT_LAYERS)
4194 image_height,
4195 image_width,
4196#endif
4197 length;
4198
glennrp38ea0832010-06-02 18:50:28 +00004199 /* These delays are all measured in image ticks_per_second,
4200 * not in MNG ticks_per_second
4201 */
cristybb503372010-05-27 20:51:26 +00004202 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004203 default_frame_delay,
4204 final_delay,
4205 final_image_delay,
4206 frame_delay,
4207#if defined(MNG_INSERT_LAYERS)
4208 insert_layers,
4209#endif
4210 mng_iterations=1,
4211 simplicity=0,
4212 subframe_height=0,
4213 subframe_width=0;
4214
4215 previous_fb.top=0;
4216 previous_fb.bottom=0;
4217 previous_fb.left=0;
4218 previous_fb.right=0;
4219 default_fb.top=0;
4220 default_fb.bottom=0;
4221 default_fb.left=0;
4222 default_fb.right=0;
4223
glennrp47b9dd52010-11-24 18:12:06 +00004224 /* Open image file. */
4225
cristy3ed852e2009-09-05 21:47:34 +00004226 assert(image_info != (const ImageInfo *) NULL);
4227 assert(image_info->signature == MagickSignature);
4228 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4229 assert(exception != (ExceptionInfo *) NULL);
4230 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004231 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004232 image=AcquireImage(image_info);
4233 mng_info=(MngInfo *) NULL;
4234 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004235
cristy3ed852e2009-09-05 21:47:34 +00004236 if (status == MagickFalse)
4237 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004238
cristy3ed852e2009-09-05 21:47:34 +00004239 first_mng_object=MagickFalse;
4240 skipping_loop=(-1);
4241 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004242
4243 /* Allocate a MngInfo structure. */
4244
cristy73bd4a52010-10-05 11:24:23 +00004245 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004246
cristy3ed852e2009-09-05 21:47:34 +00004247 if (mng_info == (MngInfo *) NULL)
4248 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004249
glennrp47b9dd52010-11-24 18:12:06 +00004250 /* Initialize members of the MngInfo structure. */
4251
cristy3ed852e2009-09-05 21:47:34 +00004252 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4253 mng_info->image=image;
4254 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004255
4256 if (LocaleCompare(image_info->magick,"MNG") == 0)
4257 {
4258 char
4259 magic_number[MaxTextExtent];
4260
glennrp47b9dd52010-11-24 18:12:06 +00004261 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004262 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4263 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4264 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004265
4266 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004267 for (i=0; i < MNG_MAX_OBJECTS; i++)
4268 {
cristybb503372010-05-27 20:51:26 +00004269 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4270 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004271 }
4272 mng_info->exists[0]=MagickTrue;
4273 }
glennrp47b9dd52010-11-24 18:12:06 +00004274
cristy3ed852e2009-09-05 21:47:34 +00004275 first_mng_object=MagickTrue;
4276 mng_type=0;
4277#if defined(MNG_INSERT_LAYERS)
4278 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4279#endif
4280 default_frame_delay=0;
4281 default_frame_timeout=0;
4282 frame_delay=0;
4283 final_delay=1;
4284 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4285 object_id=0;
4286 skip_to_iend=MagickFalse;
4287 term_chunk_found=MagickFalse;
4288 mng_info->framing_mode=1;
4289#if defined(MNG_INSERT_LAYERS)
4290 mandatory_back=MagickFalse;
4291#endif
4292#if defined(MNG_INSERT_LAYERS)
4293 mng_background_color=image->background_color;
4294#endif
4295 default_fb=mng_info->frame;
4296 previous_fb=mng_info->frame;
4297 do
4298 {
4299 char
4300 type[MaxTextExtent];
4301
4302 if (LocaleCompare(image_info->magick,"MNG") == 0)
4303 {
4304 unsigned char
4305 *chunk;
4306
4307 /*
4308 Read a new chunk.
4309 */
4310 type[0]='\0';
4311 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4312 length=ReadBlobMSBLong(image);
4313 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4314
4315 if (logging != MagickFalse)
4316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004317 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4318 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004319
4320 if (length > PNG_UINT_31_MAX)
4321 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004322
cristy3ed852e2009-09-05 21:47:34 +00004323 if (count == 0)
4324 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004325
cristy3ed852e2009-09-05 21:47:34 +00004326 p=NULL;
4327 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004328
cristy3ed852e2009-09-05 21:47:34 +00004329 if (length)
4330 {
4331 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004332
cristy3ed852e2009-09-05 21:47:34 +00004333 if (chunk == (unsigned char *) NULL)
4334 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004335
cristybb503372010-05-27 20:51:26 +00004336 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004337 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004338
cristy3ed852e2009-09-05 21:47:34 +00004339 p=chunk;
4340 }
glennrp0fe50b42010-11-16 03:52:51 +00004341
cristy3ed852e2009-09-05 21:47:34 +00004342 (void) ReadBlobMSBLong(image); /* read crc word */
4343
4344#if !defined(JNG_SUPPORTED)
4345 if (memcmp(type,mng_JHDR,4) == 0)
4346 {
4347 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004348
cristy3ed852e2009-09-05 21:47:34 +00004349 if (mng_info->jhdr_warning == 0)
4350 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4351 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004352
cristy3ed852e2009-09-05 21:47:34 +00004353 mng_info->jhdr_warning++;
4354 }
4355#endif
4356 if (memcmp(type,mng_DHDR,4) == 0)
4357 {
4358 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004359
cristy3ed852e2009-09-05 21:47:34 +00004360 if (mng_info->dhdr_warning == 0)
4361 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4362 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004363
cristy3ed852e2009-09-05 21:47:34 +00004364 mng_info->dhdr_warning++;
4365 }
4366 if (memcmp(type,mng_MEND,4) == 0)
4367 break;
glennrp47b9dd52010-11-24 18:12:06 +00004368
cristy3ed852e2009-09-05 21:47:34 +00004369 if (skip_to_iend)
4370 {
4371 if (memcmp(type,mng_IEND,4) == 0)
4372 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004373
cristy3ed852e2009-09-05 21:47:34 +00004374 if (length)
4375 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004376
cristy3ed852e2009-09-05 21:47:34 +00004377 if (logging != MagickFalse)
4378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4379 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004380
cristy3ed852e2009-09-05 21:47:34 +00004381 continue;
4382 }
glennrp0fe50b42010-11-16 03:52:51 +00004383
cristy3ed852e2009-09-05 21:47:34 +00004384 if (memcmp(type,mng_MHDR,4) == 0)
4385 {
cristybb503372010-05-27 20:51:26 +00004386 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004387 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004388
cristybb503372010-05-27 20:51:26 +00004389 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004390 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004391
cristy3ed852e2009-09-05 21:47:34 +00004392 if (logging != MagickFalse)
4393 {
4394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004395 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004397 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004398 }
glennrp0fe50b42010-11-16 03:52:51 +00004399
cristy3ed852e2009-09-05 21:47:34 +00004400 p+=8;
cristy8182b072010-05-30 20:10:53 +00004401 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004402
cristy3ed852e2009-09-05 21:47:34 +00004403 if (mng_info->ticks_per_second == 0)
4404 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004405
cristy3ed852e2009-09-05 21:47:34 +00004406 else
4407 default_frame_delay=1UL*image->ticks_per_second/
4408 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004409
cristy3ed852e2009-09-05 21:47:34 +00004410 frame_delay=default_frame_delay;
4411 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004412
cristy3ed852e2009-09-05 21:47:34 +00004413 if (length > 16)
4414 {
4415 p+=16;
cristy8182b072010-05-30 20:10:53 +00004416 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004417 }
glennrp0fe50b42010-11-16 03:52:51 +00004418
cristy3ed852e2009-09-05 21:47:34 +00004419 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004420
cristy3ed852e2009-09-05 21:47:34 +00004421 if ((simplicity != 0) && ((simplicity | 11) == 11))
4422 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004423
cristy3ed852e2009-09-05 21:47:34 +00004424 if ((simplicity != 0) && ((simplicity | 9) == 9))
4425 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004426
cristy3ed852e2009-09-05 21:47:34 +00004427#if defined(MNG_INSERT_LAYERS)
4428 if (mng_type != 3)
4429 insert_layers=MagickTrue;
4430#endif
4431 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4432 {
glennrp47b9dd52010-11-24 18:12:06 +00004433 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004434 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004435
cristy3ed852e2009-09-05 21:47:34 +00004436 if (GetNextImageInList(image) == (Image *) NULL)
4437 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004438
cristy3ed852e2009-09-05 21:47:34 +00004439 image=SyncNextImageInList(image);
4440 mng_info->image=image;
4441 }
4442
4443 if ((mng_info->mng_width > 65535L) ||
4444 (mng_info->mng_height > 65535L))
4445 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004446
cristye8c25f92010-06-03 00:53:06 +00004447 (void) FormatMagickString(page_geometry,MaxTextExtent,
4448 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004449 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004450
cristy3ed852e2009-09-05 21:47:34 +00004451 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004452 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004453 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004454 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004455 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004456
cristy3ed852e2009-09-05 21:47:34 +00004457 for (i=0; i < MNG_MAX_OBJECTS; i++)
4458 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004459
cristy3ed852e2009-09-05 21:47:34 +00004460 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4461 continue;
4462 }
4463
4464 if (memcmp(type,mng_TERM,4) == 0)
4465 {
4466 int
4467 repeat=0;
4468
4469
4470 if (length)
4471 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004472
cristy3ed852e2009-09-05 21:47:34 +00004473 if (repeat == 3)
4474 {
cristy8182b072010-05-30 20:10:53 +00004475 final_delay=(png_uint_32) mng_get_long(&p[2]);
4476 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004477
cristy3ed852e2009-09-05 21:47:34 +00004478 if (mng_iterations == PNG_UINT_31_MAX)
4479 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004480
cristy3ed852e2009-09-05 21:47:34 +00004481 image->iterations=mng_iterations;
4482 term_chunk_found=MagickTrue;
4483 }
glennrp0fe50b42010-11-16 03:52:51 +00004484
cristy3ed852e2009-09-05 21:47:34 +00004485 if (logging != MagickFalse)
4486 {
4487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4488 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004489
cristy3ed852e2009-09-05 21:47:34 +00004490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004491 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004492
cristy3ed852e2009-09-05 21:47:34 +00004493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004494 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004495 }
glennrp0fe50b42010-11-16 03:52:51 +00004496
cristy3ed852e2009-09-05 21:47:34 +00004497 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4498 continue;
4499 }
4500 if (memcmp(type,mng_DEFI,4) == 0)
4501 {
4502 if (mng_type == 3)
4503 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4504 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4505 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004506
cristy3ed852e2009-09-05 21:47:34 +00004507 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004508
cristy3ed852e2009-09-05 21:47:34 +00004509 if (mng_type == 2 && object_id != 0)
4510 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4511 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4512 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004513
cristy3ed852e2009-09-05 21:47:34 +00004514 if (object_id > MNG_MAX_OBJECTS)
4515 {
4516 /*
4517 Instead ofsuing a warning we should allocate a larger
4518 MngInfo structure and continue.
4519 */
4520 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4521 CoderError,"object id too large","`%s'",image->filename);
4522 object_id=MNG_MAX_OBJECTS;
4523 }
glennrp0fe50b42010-11-16 03:52:51 +00004524
cristy3ed852e2009-09-05 21:47:34 +00004525 if (mng_info->exists[object_id])
4526 if (mng_info->frozen[object_id])
4527 {
4528 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4529 (void) ThrowMagickException(&image->exception,
4530 GetMagickModule(),CoderError,
4531 "DEFI cannot redefine a frozen MNG object","`%s'",
4532 image->filename);
4533 continue;
4534 }
glennrp0fe50b42010-11-16 03:52:51 +00004535
cristy3ed852e2009-09-05 21:47:34 +00004536 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004537
cristy3ed852e2009-09-05 21:47:34 +00004538 if (length > 2)
4539 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004540
cristy3ed852e2009-09-05 21:47:34 +00004541 /*
4542 Extract object offset info.
4543 */
4544 if (length > 11)
4545 {
glennrp0fe50b42010-11-16 03:52:51 +00004546 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4547 (p[5] << 16) | (p[6] << 8) | p[7]);
4548
4549 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4550 (p[9] << 16) | (p[10] << 8) | p[11]);
4551
cristy3ed852e2009-09-05 21:47:34 +00004552 if (logging != MagickFalse)
4553 {
4554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004555 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004556 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004557
cristy3ed852e2009-09-05 21:47:34 +00004558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004559 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004560 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004561 }
4562 }
glennrp0fe50b42010-11-16 03:52:51 +00004563
cristy3ed852e2009-09-05 21:47:34 +00004564 /*
4565 Extract object clipping info.
4566 */
4567 if (length > 27)
4568 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4569 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004570
cristy3ed852e2009-09-05 21:47:34 +00004571 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4572 continue;
4573 }
4574 if (memcmp(type,mng_bKGD,4) == 0)
4575 {
4576 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004577
cristy3ed852e2009-09-05 21:47:34 +00004578 if (length > 5)
4579 {
4580 mng_info->mng_global_bkgd.red=
4581 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004582
cristy3ed852e2009-09-05 21:47:34 +00004583 mng_info->mng_global_bkgd.green=
4584 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004585
cristy3ed852e2009-09-05 21:47:34 +00004586 mng_info->mng_global_bkgd.blue=
4587 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004588
cristy3ed852e2009-09-05 21:47:34 +00004589 mng_info->have_global_bkgd=MagickTrue;
4590 }
glennrp0fe50b42010-11-16 03:52:51 +00004591
cristy3ed852e2009-09-05 21:47:34 +00004592 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4593 continue;
4594 }
4595 if (memcmp(type,mng_BACK,4) == 0)
4596 {
4597#if defined(MNG_INSERT_LAYERS)
4598 if (length > 6)
4599 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00004600
cristy3ed852e2009-09-05 21:47:34 +00004601 else
4602 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00004603
cristy3ed852e2009-09-05 21:47:34 +00004604 if (mandatory_back && length > 5)
4605 {
4606 mng_background_color.red=
4607 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004608
cristy3ed852e2009-09-05 21:47:34 +00004609 mng_background_color.green=
4610 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004611
cristy3ed852e2009-09-05 21:47:34 +00004612 mng_background_color.blue=
4613 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004614
cristy3ed852e2009-09-05 21:47:34 +00004615 mng_background_color.opacity=OpaqueOpacity;
4616 }
glennrp0fe50b42010-11-16 03:52:51 +00004617
cristy3ed852e2009-09-05 21:47:34 +00004618#ifdef MNG_OBJECT_BUFFERS
4619 if (length > 8)
4620 mng_background_object=(p[7] << 8) | p[8];
4621#endif
4622#endif
4623 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4624 continue;
4625 }
glennrp47b9dd52010-11-24 18:12:06 +00004626
cristy3ed852e2009-09-05 21:47:34 +00004627 if (memcmp(type,mng_PLTE,4) == 0)
4628 {
glennrp47b9dd52010-11-24 18:12:06 +00004629 /* Read global PLTE. */
4630
cristy3ed852e2009-09-05 21:47:34 +00004631 if (length && (length < 769))
4632 {
4633 if (mng_info->global_plte == (png_colorp) NULL)
4634 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4635 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00004636
cristybb503372010-05-27 20:51:26 +00004637 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004638 {
4639 mng_info->global_plte[i].red=p[3*i];
4640 mng_info->global_plte[i].green=p[3*i+1];
4641 mng_info->global_plte[i].blue=p[3*i+2];
4642 }
glennrp0fe50b42010-11-16 03:52:51 +00004643
cristy35ef8242010-06-03 16:24:13 +00004644 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00004645 }
4646#ifdef MNG_LOOSE
4647 for ( ; i < 256; i++)
4648 {
4649 mng_info->global_plte[i].red=i;
4650 mng_info->global_plte[i].green=i;
4651 mng_info->global_plte[i].blue=i;
4652 }
glennrp0fe50b42010-11-16 03:52:51 +00004653
cristy3ed852e2009-09-05 21:47:34 +00004654 if (length)
4655 mng_info->global_plte_length=256;
4656#endif
4657 else
4658 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00004659
cristy3ed852e2009-09-05 21:47:34 +00004660 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4661 continue;
4662 }
glennrp47b9dd52010-11-24 18:12:06 +00004663
cristy3ed852e2009-09-05 21:47:34 +00004664 if (memcmp(type,mng_tRNS,4) == 0)
4665 {
4666 /* read global tRNS */
4667
4668 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004669 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004670 mng_info->global_trns[i]=p[i];
4671
4672#ifdef MNG_LOOSE
4673 for ( ; i < 256; i++)
4674 mng_info->global_trns[i]=255;
4675#endif
cristy12560f32010-06-03 16:51:08 +00004676 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00004677 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4678 continue;
4679 }
4680 if (memcmp(type,mng_gAMA,4) == 0)
4681 {
4682 if (length == 4)
4683 {
cristybb503372010-05-27 20:51:26 +00004684 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004685 igamma;
4686
cristy8182b072010-05-30 20:10:53 +00004687 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004688 mng_info->global_gamma=((float) igamma)*0.00001;
4689 mng_info->have_global_gama=MagickTrue;
4690 }
glennrp0fe50b42010-11-16 03:52:51 +00004691
cristy3ed852e2009-09-05 21:47:34 +00004692 else
4693 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004694
cristy3ed852e2009-09-05 21:47:34 +00004695 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4696 continue;
4697 }
4698
4699 if (memcmp(type,mng_cHRM,4) == 0)
4700 {
glennrp47b9dd52010-11-24 18:12:06 +00004701 /* Read global cHRM */
4702
cristy3ed852e2009-09-05 21:47:34 +00004703 if (length == 32)
4704 {
cristy8182b072010-05-30 20:10:53 +00004705 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4706 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4707 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004708 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004709 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004710 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004711 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004712 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004713 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004714 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004715 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004716 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004717 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004718 mng_info->have_global_chrm=MagickTrue;
4719 }
4720 else
4721 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004722
cristy3ed852e2009-09-05 21:47:34 +00004723 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4724 continue;
4725 }
glennrp47b9dd52010-11-24 18:12:06 +00004726
cristy3ed852e2009-09-05 21:47:34 +00004727 if (memcmp(type,mng_sRGB,4) == 0)
4728 {
4729 /*
4730 Read global sRGB.
4731 */
4732 if (length)
4733 {
glennrpe610a072010-08-05 17:08:46 +00004734 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00004735 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004736 mng_info->have_global_srgb=MagickTrue;
4737 }
4738 else
4739 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004740
cristy3ed852e2009-09-05 21:47:34 +00004741 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4742 continue;
4743 }
glennrp47b9dd52010-11-24 18:12:06 +00004744
cristy3ed852e2009-09-05 21:47:34 +00004745 if (memcmp(type,mng_iCCP,4) == 0)
4746 {
glennrpfd05d622011-02-25 04:10:33 +00004747 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004748
4749 /*
4750 Read global iCCP.
4751 */
4752 if (length)
4753 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004754
cristy3ed852e2009-09-05 21:47:34 +00004755 continue;
4756 }
glennrp47b9dd52010-11-24 18:12:06 +00004757
cristy3ed852e2009-09-05 21:47:34 +00004758 if (memcmp(type,mng_FRAM,4) == 0)
4759 {
4760 if (mng_type == 3)
4761 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4762 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4763 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004764
cristy3ed852e2009-09-05 21:47:34 +00004765 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4766 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004767
cristy3ed852e2009-09-05 21:47:34 +00004768 frame_delay=default_frame_delay;
4769 frame_timeout=default_frame_timeout;
4770 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00004771
cristy3ed852e2009-09-05 21:47:34 +00004772 if (length)
4773 if (p[0])
4774 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004775
cristy3ed852e2009-09-05 21:47:34 +00004776 if (logging != MagickFalse)
4777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4778 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00004779
cristy3ed852e2009-09-05 21:47:34 +00004780 if (length > 6)
4781 {
glennrp47b9dd52010-11-24 18:12:06 +00004782 /* Note the delay and frame clipping boundaries. */
4783
cristy3ed852e2009-09-05 21:47:34 +00004784 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00004785
cristybb503372010-05-27 20:51:26 +00004786 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004787 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00004788
cristy3ed852e2009-09-05 21:47:34 +00004789 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00004790
cristybb503372010-05-27 20:51:26 +00004791 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004792 {
4793 int
4794 change_delay,
4795 change_timeout,
4796 change_clipping;
4797
4798 change_delay=(*p++);
4799 change_timeout=(*p++);
4800 change_clipping=(*p++);
4801 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00004802
cristy3ed852e2009-09-05 21:47:34 +00004803 if (change_delay)
4804 {
cristy8182b072010-05-30 20:10:53 +00004805 frame_delay=1UL*image->ticks_per_second*
4806 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004807
cristy8182b072010-05-30 20:10:53 +00004808 if (mng_info->ticks_per_second != 0)
4809 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004810
glennrpbb010dd2010-06-01 13:07:15 +00004811 else
4812 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004813
cristy3ed852e2009-09-05 21:47:34 +00004814 if (change_delay == 2)
4815 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004816
cristy3ed852e2009-09-05 21:47:34 +00004817 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 if (logging != MagickFalse)
4820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004821 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004822 }
glennrp47b9dd52010-11-24 18:12:06 +00004823
cristy3ed852e2009-09-05 21:47:34 +00004824 if (change_timeout)
4825 {
glennrpbb010dd2010-06-01 13:07:15 +00004826 frame_timeout=1UL*image->ticks_per_second*
4827 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004828
glennrpbb010dd2010-06-01 13:07:15 +00004829 if (mng_info->ticks_per_second != 0)
4830 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004831
glennrpbb010dd2010-06-01 13:07:15 +00004832 else
4833 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004834
cristy3ed852e2009-09-05 21:47:34 +00004835 if (change_delay == 2)
4836 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004839
cristy3ed852e2009-09-05 21:47:34 +00004840 if (logging != MagickFalse)
4841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004842 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004843 }
glennrp47b9dd52010-11-24 18:12:06 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 if (change_clipping)
4846 {
4847 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4848 p+=17;
4849 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00004850
cristy3ed852e2009-09-05 21:47:34 +00004851 if (logging != MagickFalse)
4852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004853 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004854 (double) fb.left,(double) fb.right,(double) fb.top,
4855 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00004856
cristy3ed852e2009-09-05 21:47:34 +00004857 if (change_clipping == 2)
4858 default_fb=fb;
4859 }
4860 }
4861 }
4862 mng_info->clip=fb;
4863 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00004864
cristybb503372010-05-27 20:51:26 +00004865 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004866 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00004867
cristybb503372010-05-27 20:51:26 +00004868 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004869 -mng_info->clip.top);
4870 /*
4871 Insert a background layer behind the frame if framing_mode is 4.
4872 */
4873#if defined(MNG_INSERT_LAYERS)
4874 if (logging != MagickFalse)
4875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004876 " subframe_width=%.20g, subframe_height=%.20g",(double)
4877 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00004878
cristy3ed852e2009-09-05 21:47:34 +00004879 if (insert_layers && (mng_info->framing_mode == 4) &&
4880 (subframe_width) && (subframe_height))
4881 {
glennrp47b9dd52010-11-24 18:12:06 +00004882 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004883 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4884 {
4885 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00004886
cristy3ed852e2009-09-05 21:47:34 +00004887 if (GetNextImageInList(image) == (Image *) NULL)
4888 {
4889 image=DestroyImageList(image);
4890 MngInfoFreeStruct(mng_info,&have_mng_structure);
4891 return((Image *) NULL);
4892 }
glennrp47b9dd52010-11-24 18:12:06 +00004893
cristy3ed852e2009-09-05 21:47:34 +00004894 image=SyncNextImageInList(image);
4895 }
glennrp0fe50b42010-11-16 03:52:51 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00004898
cristy3ed852e2009-09-05 21:47:34 +00004899 if (term_chunk_found)
4900 {
4901 image->start_loop=MagickTrue;
4902 image->iterations=mng_iterations;
4903 term_chunk_found=MagickFalse;
4904 }
glennrp0fe50b42010-11-16 03:52:51 +00004905
cristy3ed852e2009-09-05 21:47:34 +00004906 else
4907 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004908
cristy3ed852e2009-09-05 21:47:34 +00004909 image->columns=subframe_width;
4910 image->rows=subframe_height;
4911 image->page.width=subframe_width;
4912 image->page.height=subframe_height;
4913 image->page.x=mng_info->clip.left;
4914 image->page.y=mng_info->clip.top;
4915 image->background_color=mng_background_color;
4916 image->matte=MagickFalse;
4917 image->delay=0;
4918 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00004919
cristy3ed852e2009-09-05 21:47:34 +00004920 if (logging != MagickFalse)
4921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004922 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004923 (double) mng_info->clip.left,(double) mng_info->clip.right,
4924 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004925 }
4926#endif
4927 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4928 continue;
4929 }
4930 if (memcmp(type,mng_CLIP,4) == 0)
4931 {
4932 unsigned int
4933 first_object,
4934 last_object;
4935
4936 /*
4937 Read CLIP.
4938 */
4939 first_object=(p[0] << 8) | p[1];
4940 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 for (i=(int) first_object; i <= (int) last_object; i++)
4943 {
4944 if (mng_info->exists[i] && !mng_info->frozen[i])
4945 {
4946 MngBox
4947 box;
4948
4949 box=mng_info->object_clip[i];
4950 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4951 }
4952 }
glennrp47b9dd52010-11-24 18:12:06 +00004953
cristy3ed852e2009-09-05 21:47:34 +00004954 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4955 continue;
4956 }
4957 if (memcmp(type,mng_SAVE,4) == 0)
4958 {
4959 for (i=1; i < MNG_MAX_OBJECTS; i++)
4960 if (mng_info->exists[i])
4961 {
4962 mng_info->frozen[i]=MagickTrue;
4963#ifdef MNG_OBJECT_BUFFERS
4964 if (mng_info->ob[i] != (MngBuffer *) NULL)
4965 mng_info->ob[i]->frozen=MagickTrue;
4966#endif
4967 }
glennrp0fe50b42010-11-16 03:52:51 +00004968
cristy3ed852e2009-09-05 21:47:34 +00004969 if (length)
4970 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004971
cristy3ed852e2009-09-05 21:47:34 +00004972 continue;
4973 }
4974
4975 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4976 {
glennrp47b9dd52010-11-24 18:12:06 +00004977 /* Read DISC or SEEK. */
4978
cristy3ed852e2009-09-05 21:47:34 +00004979 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4980 {
4981 for (i=1; i < MNG_MAX_OBJECTS; i++)
4982 MngInfoDiscardObject(mng_info,i);
4983 }
glennrp0fe50b42010-11-16 03:52:51 +00004984
cristy3ed852e2009-09-05 21:47:34 +00004985 else
4986 {
cristybb503372010-05-27 20:51:26 +00004987 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004988 j;
4989
cristybb503372010-05-27 20:51:26 +00004990 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00004991 {
4992 i=p[j] << 8 | p[j+1];
4993 MngInfoDiscardObject(mng_info,i);
4994 }
4995 }
glennrp0fe50b42010-11-16 03:52:51 +00004996
cristy3ed852e2009-09-05 21:47:34 +00004997 if (length)
4998 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004999
cristy3ed852e2009-09-05 21:47:34 +00005000 continue;
5001 }
glennrp47b9dd52010-11-24 18:12:06 +00005002
cristy3ed852e2009-09-05 21:47:34 +00005003 if (memcmp(type,mng_MOVE,4) == 0)
5004 {
cristybb503372010-05-27 20:51:26 +00005005 size_t
cristy3ed852e2009-09-05 21:47:34 +00005006 first_object,
5007 last_object;
5008
glennrp47b9dd52010-11-24 18:12:06 +00005009 /* read MOVE */
5010
cristy3ed852e2009-09-05 21:47:34 +00005011 first_object=(p[0] << 8) | p[1];
5012 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005013 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005014 {
5015 if (mng_info->exists[i] && !mng_info->frozen[i])
5016 {
5017 MngPair
5018 new_pair;
5019
5020 MngPair
5021 old_pair;
5022
5023 old_pair.a=mng_info->x_off[i];
5024 old_pair.b=mng_info->y_off[i];
5025 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5026 mng_info->x_off[i]=new_pair.a;
5027 mng_info->y_off[i]=new_pair.b;
5028 }
5029 }
glennrp47b9dd52010-11-24 18:12:06 +00005030
cristy3ed852e2009-09-05 21:47:34 +00005031 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5032 continue;
5033 }
5034
5035 if (memcmp(type,mng_LOOP,4) == 0)
5036 {
cristybb503372010-05-27 20:51:26 +00005037 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005038 loop_level=chunk[0];
5039 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005040
5041 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005042 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005043
cristy3ed852e2009-09-05 21:47:34 +00005044 if (logging != MagickFalse)
5045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005046 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5047 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 if (loop_iters == 0)
5050 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005051
cristy3ed852e2009-09-05 21:47:34 +00005052 else
5053 {
5054 mng_info->loop_jump[loop_level]=TellBlob(image);
5055 mng_info->loop_count[loop_level]=loop_iters;
5056 }
glennrp0fe50b42010-11-16 03:52:51 +00005057
cristy3ed852e2009-09-05 21:47:34 +00005058 mng_info->loop_iteration[loop_level]=0;
5059 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5060 continue;
5061 }
glennrp47b9dd52010-11-24 18:12:06 +00005062
cristy3ed852e2009-09-05 21:47:34 +00005063 if (memcmp(type,mng_ENDL,4) == 0)
5064 {
5065 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005066
cristy3ed852e2009-09-05 21:47:34 +00005067 if (skipping_loop > 0)
5068 {
5069 if (skipping_loop == loop_level)
5070 {
5071 /*
5072 Found end of zero-iteration loop.
5073 */
5074 skipping_loop=(-1);
5075 mng_info->loop_active[loop_level]=0;
5076 }
5077 }
glennrp47b9dd52010-11-24 18:12:06 +00005078
cristy3ed852e2009-09-05 21:47:34 +00005079 else
5080 {
5081 if (mng_info->loop_active[loop_level] == 1)
5082 {
5083 mng_info->loop_count[loop_level]--;
5084 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005085
cristy3ed852e2009-09-05 21:47:34 +00005086 if (logging != MagickFalse)
5087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005088 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005089 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005090 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005091
cristy3ed852e2009-09-05 21:47:34 +00005092 if (mng_info->loop_count[loop_level] != 0)
5093 {
5094 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5095 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005096
cristy3ed852e2009-09-05 21:47:34 +00005097 if (offset < 0)
5098 ThrowReaderException(CorruptImageError,
5099 "ImproperImageHeader");
5100 }
glennrp47b9dd52010-11-24 18:12:06 +00005101
cristy3ed852e2009-09-05 21:47:34 +00005102 else
5103 {
5104 short
5105 last_level;
5106
5107 /*
5108 Finished loop.
5109 */
5110 mng_info->loop_active[loop_level]=0;
5111 last_level=(-1);
5112 for (i=0; i < loop_level; i++)
5113 if (mng_info->loop_active[i] == 1)
5114 last_level=(short) i;
5115 loop_level=last_level;
5116 }
5117 }
5118 }
glennrp47b9dd52010-11-24 18:12:06 +00005119
cristy3ed852e2009-09-05 21:47:34 +00005120 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5121 continue;
5122 }
glennrp47b9dd52010-11-24 18:12:06 +00005123
cristy3ed852e2009-09-05 21:47:34 +00005124 if (memcmp(type,mng_CLON,4) == 0)
5125 {
5126 if (mng_info->clon_warning == 0)
5127 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5128 CoderError,"CLON is not implemented yet","`%s'",
5129 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005130
cristy3ed852e2009-09-05 21:47:34 +00005131 mng_info->clon_warning++;
5132 }
glennrp47b9dd52010-11-24 18:12:06 +00005133
cristy3ed852e2009-09-05 21:47:34 +00005134 if (memcmp(type,mng_MAGN,4) == 0)
5135 {
5136 png_uint_16
5137 magn_first,
5138 magn_last,
5139 magn_mb,
5140 magn_ml,
5141 magn_mr,
5142 magn_mt,
5143 magn_mx,
5144 magn_my,
5145 magn_methx,
5146 magn_methy;
5147
5148 if (length > 1)
5149 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005150
cristy3ed852e2009-09-05 21:47:34 +00005151 else
5152 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005153
cristy3ed852e2009-09-05 21:47:34 +00005154 if (length > 3)
5155 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005156
cristy3ed852e2009-09-05 21:47:34 +00005157 else
5158 magn_last=magn_first;
5159#ifndef MNG_OBJECT_BUFFERS
5160 if (magn_first || magn_last)
5161 if (mng_info->magn_warning == 0)
5162 {
5163 (void) ThrowMagickException(&image->exception,
5164 GetMagickModule(),CoderError,
5165 "MAGN is not implemented yet for nonzero objects",
5166 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005167
cristy3ed852e2009-09-05 21:47:34 +00005168 mng_info->magn_warning++;
5169 }
5170#endif
5171 if (length > 4)
5172 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005173
cristy3ed852e2009-09-05 21:47:34 +00005174 else
5175 magn_methx=0;
5176
5177 if (length > 6)
5178 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005179
cristy3ed852e2009-09-05 21:47:34 +00005180 else
5181 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005182
cristy3ed852e2009-09-05 21:47:34 +00005183 if (magn_mx == 0)
5184 magn_mx=1;
5185
5186 if (length > 8)
5187 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005188
cristy3ed852e2009-09-05 21:47:34 +00005189 else
5190 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005191
cristy3ed852e2009-09-05 21:47:34 +00005192 if (magn_my == 0)
5193 magn_my=1;
5194
5195 if (length > 10)
5196 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005197
cristy3ed852e2009-09-05 21:47:34 +00005198 else
5199 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201 if (magn_ml == 0)
5202 magn_ml=1;
5203
5204 if (length > 12)
5205 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005206
cristy3ed852e2009-09-05 21:47:34 +00005207 else
5208 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 if (magn_mr == 0)
5211 magn_mr=1;
5212
5213 if (length > 14)
5214 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005215
cristy3ed852e2009-09-05 21:47:34 +00005216 else
5217 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005218
cristy3ed852e2009-09-05 21:47:34 +00005219 if (magn_mt == 0)
5220 magn_mt=1;
5221
5222 if (length > 16)
5223 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005224
cristy3ed852e2009-09-05 21:47:34 +00005225 else
5226 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005227
cristy3ed852e2009-09-05 21:47:34 +00005228 if (magn_mb == 0)
5229 magn_mb=1;
5230
5231 if (length > 17)
5232 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005233
cristy3ed852e2009-09-05 21:47:34 +00005234 else
5235 magn_methy=magn_methx;
5236
glennrp47b9dd52010-11-24 18:12:06 +00005237
cristy3ed852e2009-09-05 21:47:34 +00005238 if (magn_methx > 5 || magn_methy > 5)
5239 if (mng_info->magn_warning == 0)
5240 {
5241 (void) ThrowMagickException(&image->exception,
5242 GetMagickModule(),CoderError,
5243 "Unknown MAGN method in MNG datastream","`%s'",
5244 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005245
cristy3ed852e2009-09-05 21:47:34 +00005246 mng_info->magn_warning++;
5247 }
5248#ifdef MNG_OBJECT_BUFFERS
5249 /* Magnify existing objects in the range magn_first to magn_last */
5250#endif
5251 if (magn_first == 0 || magn_last == 0)
5252 {
5253 /* Save the magnification factors for object 0 */
5254 mng_info->magn_mb=magn_mb;
5255 mng_info->magn_ml=magn_ml;
5256 mng_info->magn_mr=magn_mr;
5257 mng_info->magn_mt=magn_mt;
5258 mng_info->magn_mx=magn_mx;
5259 mng_info->magn_my=magn_my;
5260 mng_info->magn_methx=magn_methx;
5261 mng_info->magn_methy=magn_methy;
5262 }
5263 }
glennrp47b9dd52010-11-24 18:12:06 +00005264
cristy3ed852e2009-09-05 21:47:34 +00005265 if (memcmp(type,mng_PAST,4) == 0)
5266 {
5267 if (mng_info->past_warning == 0)
5268 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5269 CoderError,"PAST is not implemented yet","`%s'",
5270 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005271
cristy3ed852e2009-09-05 21:47:34 +00005272 mng_info->past_warning++;
5273 }
glennrp47b9dd52010-11-24 18:12:06 +00005274
cristy3ed852e2009-09-05 21:47:34 +00005275 if (memcmp(type,mng_SHOW,4) == 0)
5276 {
5277 if (mng_info->show_warning == 0)
5278 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5279 CoderError,"SHOW is not implemented yet","`%s'",
5280 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005281
cristy3ed852e2009-09-05 21:47:34 +00005282 mng_info->show_warning++;
5283 }
glennrp47b9dd52010-11-24 18:12:06 +00005284
cristy3ed852e2009-09-05 21:47:34 +00005285 if (memcmp(type,mng_sBIT,4) == 0)
5286 {
5287 if (length < 4)
5288 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005289
cristy3ed852e2009-09-05 21:47:34 +00005290 else
5291 {
5292 mng_info->global_sbit.gray=p[0];
5293 mng_info->global_sbit.red=p[0];
5294 mng_info->global_sbit.green=p[1];
5295 mng_info->global_sbit.blue=p[2];
5296 mng_info->global_sbit.alpha=p[3];
5297 mng_info->have_global_sbit=MagickTrue;
5298 }
5299 }
5300 if (memcmp(type,mng_pHYs,4) == 0)
5301 {
5302 if (length > 8)
5303 {
5304 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005305 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005306 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005307 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005308 mng_info->global_phys_unit_type=p[8];
5309 mng_info->have_global_phys=MagickTrue;
5310 }
glennrp47b9dd52010-11-24 18:12:06 +00005311
cristy3ed852e2009-09-05 21:47:34 +00005312 else
5313 mng_info->have_global_phys=MagickFalse;
5314 }
5315 if (memcmp(type,mng_pHYg,4) == 0)
5316 {
5317 if (mng_info->phyg_warning == 0)
5318 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5319 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005320
cristy3ed852e2009-09-05 21:47:34 +00005321 mng_info->phyg_warning++;
5322 }
5323 if (memcmp(type,mng_BASI,4) == 0)
5324 {
5325 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005326
cristy3ed852e2009-09-05 21:47:34 +00005327 if (mng_info->basi_warning == 0)
5328 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5329 CoderError,"BASI is not implemented yet","`%s'",
5330 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005331
cristy3ed852e2009-09-05 21:47:34 +00005332 mng_info->basi_warning++;
5333#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005334 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005335 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005336 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005337 (p[6] << 8) | p[7]);
5338 basi_color_type=p[8];
5339 basi_compression_method=p[9];
5340 basi_filter_type=p[10];
5341 basi_interlace_method=p[11];
5342 if (length > 11)
5343 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005344
cristy3ed852e2009-09-05 21:47:34 +00005345 else
5346 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005347
cristy3ed852e2009-09-05 21:47:34 +00005348 if (length > 13)
5349 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005350
cristy3ed852e2009-09-05 21:47:34 +00005351 else
5352 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005353
cristy3ed852e2009-09-05 21:47:34 +00005354 if (length > 15)
5355 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005356
cristy3ed852e2009-09-05 21:47:34 +00005357 else
5358 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005359
cristy3ed852e2009-09-05 21:47:34 +00005360 if (length > 17)
5361 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005362
cristy3ed852e2009-09-05 21:47:34 +00005363 else
5364 {
5365 if (basi_sample_depth == 16)
5366 basi_alpha=65535L;
5367 else
5368 basi_alpha=255;
5369 }
glennrp47b9dd52010-11-24 18:12:06 +00005370
cristy3ed852e2009-09-05 21:47:34 +00005371 if (length > 19)
5372 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005373
cristy3ed852e2009-09-05 21:47:34 +00005374 else
5375 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005376
cristy3ed852e2009-09-05 21:47:34 +00005377#endif
5378 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5379 continue;
5380 }
glennrp47b9dd52010-11-24 18:12:06 +00005381
cristy3ed852e2009-09-05 21:47:34 +00005382 if (memcmp(type,mng_IHDR,4)
5383#if defined(JNG_SUPPORTED)
5384 && memcmp(type,mng_JHDR,4)
5385#endif
5386 )
5387 {
5388 /* Not an IHDR or JHDR chunk */
5389 if (length)
5390 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005391
cristy3ed852e2009-09-05 21:47:34 +00005392 continue;
5393 }
5394/* Process IHDR */
5395 if (logging != MagickFalse)
5396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5397 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 mng_info->exists[object_id]=MagickTrue;
5400 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005401
cristy3ed852e2009-09-05 21:47:34 +00005402 if (mng_info->invisible[object_id])
5403 {
5404 if (logging != MagickFalse)
5405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5406 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005407
cristy3ed852e2009-09-05 21:47:34 +00005408 skip_to_iend=MagickTrue;
5409 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5410 continue;
5411 }
5412#if defined(MNG_INSERT_LAYERS)
5413 if (length < 8)
5414 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005415
cristy8182b072010-05-30 20:10:53 +00005416 image_width=(size_t) mng_get_long(p);
5417 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005418#endif
5419 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5420
5421 /*
5422 Insert a transparent background layer behind the entire animation
5423 if it is not full screen.
5424 */
5425#if defined(MNG_INSERT_LAYERS)
5426 if (insert_layers && mng_type && first_mng_object)
5427 {
5428 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5429 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005430 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005431 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005432 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005433 {
5434 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5435 {
5436 /*
5437 Allocate next image structure.
5438 */
5439 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005440
cristy3ed852e2009-09-05 21:47:34 +00005441 if (GetNextImageInList(image) == (Image *) NULL)
5442 {
5443 image=DestroyImageList(image);
5444 MngInfoFreeStruct(mng_info,&have_mng_structure);
5445 return((Image *) NULL);
5446 }
glennrp47b9dd52010-11-24 18:12:06 +00005447
cristy3ed852e2009-09-05 21:47:34 +00005448 image=SyncNextImageInList(image);
5449 }
5450 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005451
cristy3ed852e2009-09-05 21:47:34 +00005452 if (term_chunk_found)
5453 {
5454 image->start_loop=MagickTrue;
5455 image->iterations=mng_iterations;
5456 term_chunk_found=MagickFalse;
5457 }
glennrp47b9dd52010-11-24 18:12:06 +00005458
cristy3ed852e2009-09-05 21:47:34 +00005459 else
5460 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005461
5462 /* Make a background rectangle. */
5463
cristy3ed852e2009-09-05 21:47:34 +00005464 image->delay=0;
5465 image->columns=mng_info->mng_width;
5466 image->rows=mng_info->mng_height;
5467 image->page.width=mng_info->mng_width;
5468 image->page.height=mng_info->mng_height;
5469 image->page.x=0;
5470 image->page.y=0;
5471 image->background_color=mng_background_color;
5472 (void) SetImageBackgroundColor(image);
5473 if (logging != MagickFalse)
5474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005475 " Inserted transparent background layer, W=%.20g, H=%.20g",
5476 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005477 }
5478 }
5479 /*
5480 Insert a background layer behind the upcoming image if
5481 framing_mode is 3, and we haven't already inserted one.
5482 */
5483 if (insert_layers && (mng_info->framing_mode == 3) &&
5484 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5485 (simplicity & 0x08)))
5486 {
5487 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5488 {
5489 /*
5490 Allocate next image structure.
5491 */
5492 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005493
cristy3ed852e2009-09-05 21:47:34 +00005494 if (GetNextImageInList(image) == (Image *) NULL)
5495 {
5496 image=DestroyImageList(image);
5497 MngInfoFreeStruct(mng_info,&have_mng_structure);
5498 return((Image *) NULL);
5499 }
glennrp47b9dd52010-11-24 18:12:06 +00005500
cristy3ed852e2009-09-05 21:47:34 +00005501 image=SyncNextImageInList(image);
5502 }
glennrp0fe50b42010-11-16 03:52:51 +00005503
cristy3ed852e2009-09-05 21:47:34 +00005504 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005505
cristy3ed852e2009-09-05 21:47:34 +00005506 if (term_chunk_found)
5507 {
5508 image->start_loop=MagickTrue;
5509 image->iterations=mng_iterations;
5510 term_chunk_found=MagickFalse;
5511 }
glennrp0fe50b42010-11-16 03:52:51 +00005512
cristy3ed852e2009-09-05 21:47:34 +00005513 else
5514 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005515
cristy3ed852e2009-09-05 21:47:34 +00005516 image->delay=0;
5517 image->columns=subframe_width;
5518 image->rows=subframe_height;
5519 image->page.width=subframe_width;
5520 image->page.height=subframe_height;
5521 image->page.x=mng_info->clip.left;
5522 image->page.y=mng_info->clip.top;
5523 image->background_color=mng_background_color;
5524 image->matte=MagickFalse;
5525 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005526
cristy3ed852e2009-09-05 21:47:34 +00005527 if (logging != MagickFalse)
5528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005529 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005530 (double) mng_info->clip.left,(double) mng_info->clip.right,
5531 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005532 }
5533#endif /* MNG_INSERT_LAYERS */
5534 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005535
cristy3ed852e2009-09-05 21:47:34 +00005536 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5537 {
5538 /*
5539 Allocate next image structure.
5540 */
5541 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005542
cristy3ed852e2009-09-05 21:47:34 +00005543 if (GetNextImageInList(image) == (Image *) NULL)
5544 {
5545 image=DestroyImageList(image);
5546 MngInfoFreeStruct(mng_info,&have_mng_structure);
5547 return((Image *) NULL);
5548 }
glennrp47b9dd52010-11-24 18:12:06 +00005549
cristy3ed852e2009-09-05 21:47:34 +00005550 image=SyncNextImageInList(image);
5551 }
5552 mng_info->image=image;
5553 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5554 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005555
cristy3ed852e2009-09-05 21:47:34 +00005556 if (status == MagickFalse)
5557 break;
glennrp0fe50b42010-11-16 03:52:51 +00005558
cristy3ed852e2009-09-05 21:47:34 +00005559 if (term_chunk_found)
5560 {
5561 image->start_loop=MagickTrue;
5562 term_chunk_found=MagickFalse;
5563 }
glennrp0fe50b42010-11-16 03:52:51 +00005564
cristy3ed852e2009-09-05 21:47:34 +00005565 else
5566 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005567
cristy3ed852e2009-09-05 21:47:34 +00005568 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5569 {
5570 image->delay=frame_delay;
5571 frame_delay=default_frame_delay;
5572 }
glennrp0fe50b42010-11-16 03:52:51 +00005573
cristy3ed852e2009-09-05 21:47:34 +00005574 else
5575 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005576
cristy3ed852e2009-09-05 21:47:34 +00005577 image->page.width=mng_info->mng_width;
5578 image->page.height=mng_info->mng_height;
5579 image->page.x=mng_info->x_off[object_id];
5580 image->page.y=mng_info->y_off[object_id];
5581 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00005582
cristy3ed852e2009-09-05 21:47:34 +00005583 /*
5584 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5585 */
glennrp47b9dd52010-11-24 18:12:06 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 if (logging != MagickFalse)
5588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5589 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5590 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005591
cristybb503372010-05-27 20:51:26 +00005592 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00005593
cristy3ed852e2009-09-05 21:47:34 +00005594 if (offset < 0)
5595 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5596 }
5597
5598 previous=image;
5599 mng_info->image=image;
5600 mng_info->mng_type=mng_type;
5601 mng_info->object_id=object_id;
5602
5603 if (memcmp(type,mng_IHDR,4) == 0)
5604 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005605
cristy3ed852e2009-09-05 21:47:34 +00005606#if defined(JNG_SUPPORTED)
5607 else
5608 image=ReadOneJNGImage(mng_info,image_info,exception);
5609#endif
5610
5611 if (image == (Image *) NULL)
5612 {
5613 if (IsImageObject(previous) != MagickFalse)
5614 {
5615 (void) DestroyImageList(previous);
5616 (void) CloseBlob(previous);
5617 }
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 MngInfoFreeStruct(mng_info,&have_mng_structure);
5620 return((Image *) NULL);
5621 }
glennrp0fe50b42010-11-16 03:52:51 +00005622
cristy3ed852e2009-09-05 21:47:34 +00005623 if (image->columns == 0 || image->rows == 0)
5624 {
5625 (void) CloseBlob(image);
5626 image=DestroyImageList(image);
5627 MngInfoFreeStruct(mng_info,&have_mng_structure);
5628 return((Image *) NULL);
5629 }
glennrp0fe50b42010-11-16 03:52:51 +00005630
cristy3ed852e2009-09-05 21:47:34 +00005631 mng_info->image=image;
5632
5633 if (mng_type)
5634 {
5635 MngBox
5636 crop_box;
5637
5638 if (mng_info->magn_methx || mng_info->magn_methy)
5639 {
5640 png_uint_32
5641 magnified_height,
5642 magnified_width;
5643
5644 if (logging != MagickFalse)
5645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5646 " Processing MNG MAGN chunk");
5647
5648 if (mng_info->magn_methx == 1)
5649 {
5650 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristy3ed852e2009-09-05 21:47:34 +00005652 if (image->columns > 1)
5653 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005654
cristy3ed852e2009-09-05 21:47:34 +00005655 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005656 magnified_width += (png_uint_32)
5657 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00005658 }
glennrp47b9dd52010-11-24 18:12:06 +00005659
cristy3ed852e2009-09-05 21:47:34 +00005660 else
5661 {
cristy4e5bc842010-06-09 13:56:01 +00005662 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 if (image->columns > 1)
5665 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00005666
cristy3ed852e2009-09-05 21:47:34 +00005667 if (image->columns > 2)
5668 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005671 magnified_width += (png_uint_32)
5672 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00005673 }
glennrp47b9dd52010-11-24 18:12:06 +00005674
cristy3ed852e2009-09-05 21:47:34 +00005675 if (mng_info->magn_methy == 1)
5676 {
5677 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005678
cristy3ed852e2009-09-05 21:47:34 +00005679 if (image->rows > 1)
5680 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005681
cristy3ed852e2009-09-05 21:47:34 +00005682 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005683 magnified_height += (png_uint_32)
5684 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00005685 }
glennrp47b9dd52010-11-24 18:12:06 +00005686
cristy3ed852e2009-09-05 21:47:34 +00005687 else
5688 {
cristy4e5bc842010-06-09 13:56:01 +00005689 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00005690
cristy3ed852e2009-09-05 21:47:34 +00005691 if (image->rows > 1)
5692 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00005693
cristy3ed852e2009-09-05 21:47:34 +00005694 if (image->rows > 2)
5695 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00005696
cristy3ed852e2009-09-05 21:47:34 +00005697 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005698 magnified_height += (png_uint_32)
5699 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00005700 }
glennrp47b9dd52010-11-24 18:12:06 +00005701
cristy3ed852e2009-09-05 21:47:34 +00005702 if (magnified_height > image->rows ||
5703 magnified_width > image->columns)
5704 {
5705 Image
5706 *large_image;
5707
5708 int
5709 yy;
5710
cristybb503372010-05-27 20:51:26 +00005711 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005712 m,
5713 y;
5714
cristybb503372010-05-27 20:51:26 +00005715 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005716 x;
5717
5718 register PixelPacket
5719 *n,
5720 *q;
5721
5722 PixelPacket
5723 *next,
5724 *prev;
5725
5726 png_uint_16
5727 magn_methx,
5728 magn_methy;
5729
glennrp47b9dd52010-11-24 18:12:06 +00005730 /* Allocate next image structure. */
5731
cristy3ed852e2009-09-05 21:47:34 +00005732 if (logging != MagickFalse)
5733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5734 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00005735
cristy3ed852e2009-09-05 21:47:34 +00005736 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005737
cristy3ed852e2009-09-05 21:47:34 +00005738 if (GetNextImageInList(image) == (Image *) NULL)
5739 {
5740 image=DestroyImageList(image);
5741 MngInfoFreeStruct(mng_info,&have_mng_structure);
5742 return((Image *) NULL);
5743 }
5744
5745 large_image=SyncNextImageInList(image);
5746
5747 large_image->columns=magnified_width;
5748 large_image->rows=magnified_height;
5749
5750 magn_methx=mng_info->magn_methx;
5751 magn_methy=mng_info->magn_methy;
5752
glennrp3faa9a32011-04-23 14:00:25 +00005753#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00005754#define QM unsigned short
5755 if (magn_methx != 1 || magn_methy != 1)
5756 {
5757 /*
5758 Scale pixels to unsigned shorts to prevent
5759 overflow of intermediate values of interpolations
5760 */
cristybb503372010-05-27 20:51:26 +00005761 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005762 {
5763 q=GetAuthenticPixels(image,0,y,image->columns,1,
5764 exception);
glennrp47b9dd52010-11-24 18:12:06 +00005765
cristybb503372010-05-27 20:51:26 +00005766 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005767 {
5768 q->red=ScaleQuantumToShort(q->red);
5769 q->green=ScaleQuantumToShort(q->green);
5770 q->blue=ScaleQuantumToShort(q->blue);
5771 q->opacity=ScaleQuantumToShort(q->opacity);
5772 q++;
5773 }
glennrp47b9dd52010-11-24 18:12:06 +00005774
cristy3ed852e2009-09-05 21:47:34 +00005775 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5776 break;
5777 }
5778 }
5779#else
5780#define QM Quantum
5781#endif
5782
5783 if (image->matte != MagickFalse)
5784 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005785
cristy3ed852e2009-09-05 21:47:34 +00005786 else
5787 {
5788 large_image->background_color.opacity=OpaqueOpacity;
5789 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005790
cristy3ed852e2009-09-05 21:47:34 +00005791 if (magn_methx == 4)
5792 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00005793
cristy3ed852e2009-09-05 21:47:34 +00005794 if (magn_methx == 5)
5795 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00005796
cristy3ed852e2009-09-05 21:47:34 +00005797 if (magn_methy == 4)
5798 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00005799
cristy3ed852e2009-09-05 21:47:34 +00005800 if (magn_methy == 5)
5801 magn_methy=3;
5802 }
5803
5804 /* magnify the rows into the right side of the large image */
5805
5806 if (logging != MagickFalse)
5807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005808 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00005809 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005810 yy=0;
5811 length=(size_t) image->columns;
5812 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5813 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00005814
cristy3ed852e2009-09-05 21:47:34 +00005815 if ((prev == (PixelPacket *) NULL) ||
5816 (next == (PixelPacket *) NULL))
5817 {
5818 image=DestroyImageList(image);
5819 MngInfoFreeStruct(mng_info,&have_mng_structure);
5820 ThrowReaderException(ResourceLimitError,
5821 "MemoryAllocationFailed");
5822 }
glennrp47b9dd52010-11-24 18:12:06 +00005823
cristy3ed852e2009-09-05 21:47:34 +00005824 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5825 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00005826
cristybb503372010-05-27 20:51:26 +00005827 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005828 {
5829 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005830 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005831
cristybb503372010-05-27 20:51:26 +00005832 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5833 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristybb503372010-05-27 20:51:26 +00005835 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5836 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristybb503372010-05-27 20:51:26 +00005838 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005839 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy3ed852e2009-09-05 21:47:34 +00005841 else
cristybb503372010-05-27 20:51:26 +00005842 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 n=prev;
5845 prev=next;
5846 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00005847
cristybb503372010-05-27 20:51:26 +00005848 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005849 {
5850 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5851 exception);
5852 (void) CopyMagickMemory(next,n,length);
5853 }
glennrp47b9dd52010-11-24 18:12:06 +00005854
cristy3ed852e2009-09-05 21:47:34 +00005855 for (i=0; i < m; i++, yy++)
5856 {
5857 register PixelPacket
5858 *pixels;
5859
cristybb503372010-05-27 20:51:26 +00005860 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005861 pixels=prev;
5862 n=next;
5863 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5864 1,exception);
5865 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00005866
cristybb503372010-05-27 20:51:26 +00005867 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005868 {
glennrpfd05d622011-02-25 04:10:33 +00005869 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005870 /*
5871 if (image->storage_class == PseudoClass)
5872 {
5873 }
5874 */
5875
5876 if (magn_methy <= 1)
5877 {
5878 *q=(*pixels); /* replicate previous */
5879 }
glennrp47b9dd52010-11-24 18:12:06 +00005880
cristy3ed852e2009-09-05 21:47:34 +00005881 else if (magn_methy == 2 || magn_methy == 4)
5882 {
5883 if (i == 0)
5884 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005885
cristy3ed852e2009-09-05 21:47:34 +00005886 else
5887 {
5888 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005889 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5890 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005891 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005892 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5893 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005894 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005895 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5896 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005897 +(*pixels).blue);
glennrp47b9dd52010-11-24 18:12:06 +00005898
cristy3ed852e2009-09-05 21:47:34 +00005899 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005900 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005901 (2*i*((*n).opacity
5902 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005903 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005904 }
glennrp47b9dd52010-11-24 18:12:06 +00005905
cristy3ed852e2009-09-05 21:47:34 +00005906 if (magn_methy == 4)
5907 {
5908 /* Replicate nearest */
5909 if (i <= ((m+1) << 1))
5910 (*q).opacity=(*pixels).opacity+0;
5911 else
5912 (*q).opacity=(*n).opacity+0;
5913 }
5914 }
glennrp47b9dd52010-11-24 18:12:06 +00005915
cristy3ed852e2009-09-05 21:47:34 +00005916 else /* if (magn_methy == 3 || magn_methy == 5) */
5917 {
5918 /* Replicate nearest */
5919 if (i <= ((m+1) << 1))
5920 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005921
cristy3ed852e2009-09-05 21:47:34 +00005922 else
5923 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00005924
cristy3ed852e2009-09-05 21:47:34 +00005925 if (magn_methy == 5)
5926 {
cristybb503372010-05-27 20:51:26 +00005927 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5928 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005929 +(*pixels).opacity);
5930 }
5931 }
5932 n++;
5933 q++;
5934 pixels++;
5935 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00005936
cristy3ed852e2009-09-05 21:47:34 +00005937 if (SyncAuthenticPixels(large_image,exception) == 0)
5938 break;
glennrp47b9dd52010-11-24 18:12:06 +00005939
cristy3ed852e2009-09-05 21:47:34 +00005940 } /* i */
5941 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00005942
cristy3ed852e2009-09-05 21:47:34 +00005943 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5944 next=(PixelPacket *) RelinquishMagickMemory(next);
5945
5946 length=image->columns;
5947
5948 if (logging != MagickFalse)
5949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5950 " Delete original image");
5951
5952 DeleteImageFromList(&image);
5953
5954 image=large_image;
5955
5956 mng_info->image=image;
5957
5958 /* magnify the columns */
5959 if (logging != MagickFalse)
5960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005961 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00005962
cristybb503372010-05-27 20:51:26 +00005963 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005964 {
5965 register PixelPacket
5966 *pixels;
5967
5968 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5969 pixels=q+(image->columns-length);
5970 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00005971
cristybb503372010-05-27 20:51:26 +00005972 for (x=(ssize_t) (image->columns-length);
5973 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005974 {
cristybb503372010-05-27 20:51:26 +00005975 if (x == (ssize_t) (image->columns-length))
5976 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005977
cristybb503372010-05-27 20:51:26 +00005978 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5979 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005980
cristybb503372010-05-27 20:51:26 +00005981 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5982 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005983
cristybb503372010-05-27 20:51:26 +00005984 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00005985 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005986
cristy3ed852e2009-09-05 21:47:34 +00005987 else
cristybb503372010-05-27 20:51:26 +00005988 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005989
cristy3ed852e2009-09-05 21:47:34 +00005990 for (i=0; i < m; i++)
5991 {
5992 if (magn_methx <= 1)
5993 {
5994 /* replicate previous */
5995 *q=(*pixels);
5996 }
glennrp47b9dd52010-11-24 18:12:06 +00005997
cristy3ed852e2009-09-05 21:47:34 +00005998 else if (magn_methx == 2 || magn_methx == 4)
5999 {
6000 if (i == 0)
6001 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006002
cristy3ed852e2009-09-05 21:47:34 +00006003 else
6004 {
6005 /* Interpolate */
6006 (*q).red=(QM) ((2*i*((*n).red
6007 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00006008 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00006009 (*q).green=(QM) ((2*i*((*n).green
6010 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00006011 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00006012 (*q).blue=(QM) ((2*i*((*n).blue
6013 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00006014 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00006015 if (image->matte != MagickFalse)
6016 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006017 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006018 +(*pixels).opacity);
6019 }
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristy3ed852e2009-09-05 21:47:34 +00006021 if (magn_methx == 4)
6022 {
6023 /* Replicate nearest */
6024 if (i <= ((m+1) << 1))
6025 (*q).opacity=(*pixels).opacity+0;
6026 else
6027 (*q).opacity=(*n).opacity+0;
6028 }
6029 }
glennrp47b9dd52010-11-24 18:12:06 +00006030
cristy3ed852e2009-09-05 21:47:34 +00006031 else /* if (magn_methx == 3 || magn_methx == 5) */
6032 {
6033 /* Replicate nearest */
6034 if (i <= ((m+1) << 1))
6035 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006036
cristy3ed852e2009-09-05 21:47:34 +00006037 else
6038 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00006039
cristy3ed852e2009-09-05 21:47:34 +00006040 if (magn_methx == 5)
6041 {
6042 /* Interpolate */
6043 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006044 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006045 +(*pixels).opacity);
6046 }
6047 }
6048 q++;
6049 }
6050 n++;
6051 p++;
6052 }
glennrp47b9dd52010-11-24 18:12:06 +00006053
cristy3ed852e2009-09-05 21:47:34 +00006054 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6055 break;
6056 }
glennrp3faa9a32011-04-23 14:00:25 +00006057#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006058 if (magn_methx != 1 || magn_methy != 1)
6059 {
6060 /*
6061 Rescale pixels to Quantum
6062 */
cristybb503372010-05-27 20:51:26 +00006063 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006064 {
6065 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006066
cristybb503372010-05-27 20:51:26 +00006067 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006068 {
6069 q->red=ScaleShortToQuantum(q->red);
6070 q->green=ScaleShortToQuantum(q->green);
6071 q->blue=ScaleShortToQuantum(q->blue);
6072 q->opacity=ScaleShortToQuantum(q->opacity);
6073 q++;
6074 }
glennrp47b9dd52010-11-24 18:12:06 +00006075
cristy3ed852e2009-09-05 21:47:34 +00006076 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6077 break;
6078 }
6079 }
6080#endif
6081 if (logging != MagickFalse)
6082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6083 " Finished MAGN processing");
6084 }
6085 }
6086
6087 /*
6088 Crop_box is with respect to the upper left corner of the MNG.
6089 */
6090 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6091 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6092 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6093 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6094 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6095 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6096 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6097 if ((crop_box.left != (mng_info->image_box.left
6098 +mng_info->x_off[object_id])) ||
6099 (crop_box.right != (mng_info->image_box.right
6100 +mng_info->x_off[object_id])) ||
6101 (crop_box.top != (mng_info->image_box.top
6102 +mng_info->y_off[object_id])) ||
6103 (crop_box.bottom != (mng_info->image_box.bottom
6104 +mng_info->y_off[object_id])))
6105 {
6106 if (logging != MagickFalse)
6107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6108 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006109
cristy3ed852e2009-09-05 21:47:34 +00006110 if ((crop_box.left < crop_box.right) &&
6111 (crop_box.top < crop_box.bottom))
6112 {
6113 Image
6114 *im;
6115
6116 RectangleInfo
6117 crop_info;
6118
6119 /*
6120 Crop_info is with respect to the upper left corner of
6121 the image.
6122 */
6123 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6124 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006125 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6126 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006127 image->page.width=image->columns;
6128 image->page.height=image->rows;
6129 image->page.x=0;
6130 image->page.y=0;
6131 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006132
cristy3ed852e2009-09-05 21:47:34 +00006133 if (im != (Image *) NULL)
6134 {
6135 image->columns=im->columns;
6136 image->rows=im->rows;
6137 im=DestroyImage(im);
6138 image->page.width=image->columns;
6139 image->page.height=image->rows;
6140 image->page.x=crop_box.left;
6141 image->page.y=crop_box.top;
6142 }
6143 }
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145 else
6146 {
6147 /*
6148 No pixels in crop area. The MNG spec still requires
6149 a layer, though, so make a single transparent pixel in
6150 the top left corner.
6151 */
6152 image->columns=1;
6153 image->rows=1;
6154 image->colors=2;
6155 (void) SetImageBackgroundColor(image);
6156 image->page.width=1;
6157 image->page.height=1;
6158 image->page.x=0;
6159 image->page.y=0;
6160 }
6161 }
6162#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6163 image=mng_info->image;
6164#endif
6165 }
6166
glennrp2b013e42010-11-24 16:55:50 +00006167#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6168 /* PNG does not handle depths greater than 16 so reduce it even
6169 * if lossy
6170 */
6171 if (image->depth > 16)
6172 image->depth=16;
6173#endif
6174
glennrp3faa9a32011-04-23 14:00:25 +00006175#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006176 if (LosslessReduceDepthOK(image) != MagickFalse)
6177 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006178#endif
glennrpd6afd542010-11-19 01:53:05 +00006179
cristy3ed852e2009-09-05 21:47:34 +00006180 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006181
cristy3ed852e2009-09-05 21:47:34 +00006182 if (image_info->number_scenes != 0)
6183 {
6184 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006185 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006186 break;
6187 }
glennrpd6afd542010-11-19 01:53:05 +00006188
cristy3ed852e2009-09-05 21:47:34 +00006189 if (logging != MagickFalse)
6190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6191 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006192
cristy3ed852e2009-09-05 21:47:34 +00006193 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006194
cristy3ed852e2009-09-05 21:47:34 +00006195 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006196
cristy3ed852e2009-09-05 21:47:34 +00006197 if (logging != MagickFalse)
6198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6199 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006200
cristy3ed852e2009-09-05 21:47:34 +00006201#if defined(MNG_INSERT_LAYERS)
6202 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6203 (mng_info->mng_height))
6204 {
6205 /*
6206 Insert a background layer if nothing else was found.
6207 */
6208 if (logging != MagickFalse)
6209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6210 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006211
cristy3ed852e2009-09-05 21:47:34 +00006212 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6213 {
6214 /*
6215 Allocate next image structure.
6216 */
6217 AcquireNextImage(image_info,image);
6218 if (GetNextImageInList(image) == (Image *) NULL)
6219 {
6220 image=DestroyImageList(image);
6221 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006222
cristy3ed852e2009-09-05 21:47:34 +00006223 if (logging != MagickFalse)
6224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6225 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006226
cristy3ed852e2009-09-05 21:47:34 +00006227 return((Image *) NULL);
6228 }
6229 image=SyncNextImageInList(image);
6230 }
6231 image->columns=mng_info->mng_width;
6232 image->rows=mng_info->mng_height;
6233 image->page.width=mng_info->mng_width;
6234 image->page.height=mng_info->mng_height;
6235 image->page.x=0;
6236 image->page.y=0;
6237 image->background_color=mng_background_color;
6238 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006239
cristy3ed852e2009-09-05 21:47:34 +00006240 if (image_info->ping == MagickFalse)
6241 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006242
cristy3ed852e2009-09-05 21:47:34 +00006243 mng_info->image_found++;
6244 }
6245#endif
6246 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006247
cristy3ed852e2009-09-05 21:47:34 +00006248 if (mng_iterations == 1)
6249 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006250
cristy3ed852e2009-09-05 21:47:34 +00006251 while (GetPreviousImageInList(image) != (Image *) NULL)
6252 {
6253 image_count++;
6254 if (image_count > 10*mng_info->image_found)
6255 {
6256 if (logging != MagickFalse)
6257 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006258
cristy3ed852e2009-09-05 21:47:34 +00006259 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6260 CoderError,"Linked list is corrupted, beginning of list not found",
6261 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006262
cristy3ed852e2009-09-05 21:47:34 +00006263 return((Image *) NULL);
6264 }
glennrp0fe50b42010-11-16 03:52:51 +00006265
cristy3ed852e2009-09-05 21:47:34 +00006266 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006267
cristy3ed852e2009-09-05 21:47:34 +00006268 if (GetNextImageInList(image) == (Image *) NULL)
6269 {
6270 if (logging != MagickFalse)
6271 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006272
cristy3ed852e2009-09-05 21:47:34 +00006273 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6274 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6275 image_info->filename);
6276 }
6277 }
glennrp47b9dd52010-11-24 18:12:06 +00006278
cristy3ed852e2009-09-05 21:47:34 +00006279 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6280 GetNextImageInList(image) ==
6281 (Image *) NULL)
6282 {
6283 if (logging != MagickFalse)
6284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6285 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006286
cristy3ed852e2009-09-05 21:47:34 +00006287 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6288 CoderError,"image->next for first image is NULL but shouldn't be.",
6289 "`%s'",image_info->filename);
6290 }
glennrp47b9dd52010-11-24 18:12:06 +00006291
cristy3ed852e2009-09-05 21:47:34 +00006292 if (mng_info->image_found == 0)
6293 {
6294 if (logging != MagickFalse)
6295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6296 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006297
cristy3ed852e2009-09-05 21:47:34 +00006298 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6299 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006300
cristy3ed852e2009-09-05 21:47:34 +00006301 if (image != (Image *) NULL)
6302 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006303
cristy3ed852e2009-09-05 21:47:34 +00006304 MngInfoFreeStruct(mng_info,&have_mng_structure);
6305 return((Image *) NULL);
6306 }
6307
6308 if (mng_info->ticks_per_second)
6309 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6310 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006311
cristy3ed852e2009-09-05 21:47:34 +00006312 else
6313 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006314
cristy3ed852e2009-09-05 21:47:34 +00006315 /* Find final nonzero image delay */
6316 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006317
cristy3ed852e2009-09-05 21:47:34 +00006318 while (GetNextImageInList(image) != (Image *) NULL)
6319 {
6320 if (image->delay)
6321 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006322
cristy3ed852e2009-09-05 21:47:34 +00006323 image=GetNextImageInList(image);
6324 }
glennrp0fe50b42010-11-16 03:52:51 +00006325
cristy3ed852e2009-09-05 21:47:34 +00006326 if (final_delay < final_image_delay)
6327 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006328
cristy3ed852e2009-09-05 21:47:34 +00006329 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006330
cristy3ed852e2009-09-05 21:47:34 +00006331 if (logging != MagickFalse)
6332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006333 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6334 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006335
cristy3ed852e2009-09-05 21:47:34 +00006336 if (logging != MagickFalse)
6337 {
6338 int
6339 scene;
6340
6341 scene=0;
6342 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006343
cristy3ed852e2009-09-05 21:47:34 +00006344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6345 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006346
cristy3ed852e2009-09-05 21:47:34 +00006347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006348 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006349
cristy3ed852e2009-09-05 21:47:34 +00006350 while (GetNextImageInList(image) != (Image *) NULL)
6351 {
6352 image=GetNextImageInList(image);
6353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006354 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006355 }
6356 }
6357
6358 image=GetFirstImageInList(image);
6359#ifdef MNG_COALESCE_LAYERS
6360 if (insert_layers)
6361 {
6362 Image
6363 *next_image,
6364 *next;
6365
cristybb503372010-05-27 20:51:26 +00006366 size_t
cristy3ed852e2009-09-05 21:47:34 +00006367 scene;
6368
6369 if (logging != MagickFalse)
6370 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006371
cristy3ed852e2009-09-05 21:47:34 +00006372 scene=image->scene;
6373 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006374
cristy3ed852e2009-09-05 21:47:34 +00006375 if (next_image == (Image *) NULL)
6376 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006377
cristy3ed852e2009-09-05 21:47:34 +00006378 image=DestroyImageList(image);
6379 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006380
cristy3ed852e2009-09-05 21:47:34 +00006381 for (next=image; next != (Image *) NULL; next=next_image)
6382 {
6383 next->page.width=mng_info->mng_width;
6384 next->page.height=mng_info->mng_height;
6385 next->page.x=0;
6386 next->page.y=0;
6387 next->scene=scene++;
6388 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006389
cristy3ed852e2009-09-05 21:47:34 +00006390 if (next_image == (Image *) NULL)
6391 break;
glennrp47b9dd52010-11-24 18:12:06 +00006392
cristy3ed852e2009-09-05 21:47:34 +00006393 if (next->delay == 0)
6394 {
6395 scene--;
6396 next_image->previous=GetPreviousImageInList(next);
6397 if (GetPreviousImageInList(next) == (Image *) NULL)
6398 image=next_image;
6399 else
6400 next->previous->next=next_image;
6401 next=DestroyImage(next);
6402 }
6403 }
6404 }
6405#endif
6406
6407 while (GetNextImageInList(image) != (Image *) NULL)
6408 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006409
cristy3ed852e2009-09-05 21:47:34 +00006410 image->dispose=BackgroundDispose;
6411
6412 if (logging != MagickFalse)
6413 {
6414 int
6415 scene;
6416
6417 scene=0;
6418 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006419
cristy3ed852e2009-09-05 21:47:34 +00006420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6421 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006422
cristy3ed852e2009-09-05 21:47:34 +00006423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006424 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6425 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006426
cristy3ed852e2009-09-05 21:47:34 +00006427 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006428 {
6429 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristyf2faecf2010-05-28 19:19:36 +00006431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006432 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6433 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006434 }
6435 }
glennrp47b9dd52010-11-24 18:12:06 +00006436
cristy3ed852e2009-09-05 21:47:34 +00006437 image=GetFirstImageInList(image);
6438 MngInfoFreeStruct(mng_info,&have_mng_structure);
6439 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006440
cristy3ed852e2009-09-05 21:47:34 +00006441 if (logging != MagickFalse)
6442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006443
cristy3ed852e2009-09-05 21:47:34 +00006444 return(GetFirstImageInList(image));
6445}
glennrp25c1e2b2010-03-25 01:39:56 +00006446#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006447static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6448{
6449 printf("Your PNG library is too old: You have libpng-%s\n",
6450 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006451
cristy3ed852e2009-09-05 21:47:34 +00006452 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6453 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006454
cristy3ed852e2009-09-05 21:47:34 +00006455 return(Image *) NULL;
6456}
glennrp47b9dd52010-11-24 18:12:06 +00006457
cristy3ed852e2009-09-05 21:47:34 +00006458static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6459{
6460 return(ReadPNGImage(image_info,exception));
6461}
glennrp25c1e2b2010-03-25 01:39:56 +00006462#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006463#endif
6464
6465/*
6466%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6467% %
6468% %
6469% %
6470% R e g i s t e r P N G I m a g e %
6471% %
6472% %
6473% %
6474%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6475%
6476% RegisterPNGImage() adds properties for the PNG image format to
6477% the list of supported formats. The properties include the image format
6478% tag, a method to read and/or write the format, whether the format
6479% supports the saving of more than one frame to the same file or blob,
6480% whether the format supports native in-memory I/O, and a brief
6481% description of the format.
6482%
6483% The format of the RegisterPNGImage method is:
6484%
cristybb503372010-05-27 20:51:26 +00006485% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006486%
6487*/
cristybb503372010-05-27 20:51:26 +00006488ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006489{
6490 char
6491 version[MaxTextExtent];
6492
6493 MagickInfo
6494 *entry;
6495
6496 static const char
6497 *PNGNote=
6498 {
6499 "See http://www.libpng.org/ for details about the PNG format."
6500 },
glennrp47b9dd52010-11-24 18:12:06 +00006501
cristy3ed852e2009-09-05 21:47:34 +00006502 *JNGNote=
6503 {
6504 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6505 "format."
6506 },
glennrp47b9dd52010-11-24 18:12:06 +00006507
cristy3ed852e2009-09-05 21:47:34 +00006508 *MNGNote=
6509 {
6510 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6511 "format."
6512 };
6513
6514 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006515
cristy3ed852e2009-09-05 21:47:34 +00006516#if defined(PNG_LIBPNG_VER_STRING)
6517 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6518 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006519
cristy3ed852e2009-09-05 21:47:34 +00006520 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6521 {
6522 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6523 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6524 MaxTextExtent);
6525 }
6526#endif
glennrp47b9dd52010-11-24 18:12:06 +00006527
cristy3ed852e2009-09-05 21:47:34 +00006528 entry=SetMagickInfo("MNG");
6529 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00006530
cristy3ed852e2009-09-05 21:47:34 +00006531#if defined(MAGICKCORE_PNG_DELEGATE)
6532 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6533 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6534#endif
glennrp47b9dd52010-11-24 18:12:06 +00006535
cristy3ed852e2009-09-05 21:47:34 +00006536 entry->magick=(IsImageFormatHandler *) IsMNG;
6537 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00006538
cristy3ed852e2009-09-05 21:47:34 +00006539 if (*version != '\0')
6540 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006541
cristy3ed852e2009-09-05 21:47:34 +00006542 entry->module=ConstantString("PNG");
6543 entry->note=ConstantString(MNGNote);
6544 (void) RegisterMagickInfo(entry);
6545
6546 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006547
cristy3ed852e2009-09-05 21:47:34 +00006548#if defined(MAGICKCORE_PNG_DELEGATE)
6549 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6550 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6551#endif
glennrp47b9dd52010-11-24 18:12:06 +00006552
cristy3ed852e2009-09-05 21:47:34 +00006553 entry->magick=(IsImageFormatHandler *) IsPNG;
6554 entry->adjoin=MagickFalse;
6555 entry->description=ConstantString("Portable Network Graphics");
6556 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006557
cristy3ed852e2009-09-05 21:47:34 +00006558 if (*version != '\0')
6559 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006560
cristy3ed852e2009-09-05 21:47:34 +00006561 entry->note=ConstantString(PNGNote);
6562 (void) RegisterMagickInfo(entry);
6563
6564 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00006565
cristy3ed852e2009-09-05 21:47:34 +00006566#if defined(MAGICKCORE_PNG_DELEGATE)
6567 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6568 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6569#endif
glennrp47b9dd52010-11-24 18:12:06 +00006570
cristy3ed852e2009-09-05 21:47:34 +00006571 entry->magick=(IsImageFormatHandler *) IsPNG;
6572 entry->adjoin=MagickFalse;
6573 entry->description=ConstantString(
6574 "8-bit indexed with optional binary transparency");
6575 entry->module=ConstantString("PNG");
6576 (void) RegisterMagickInfo(entry);
6577
6578 entry=SetMagickInfo("PNG24");
6579 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006580
cristy3ed852e2009-09-05 21:47:34 +00006581#if defined(ZLIB_VERSION)
6582 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6583 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006584
cristy3ed852e2009-09-05 21:47:34 +00006585 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6586 {
6587 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6588 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6589 }
6590#endif
glennrp47b9dd52010-11-24 18:12:06 +00006591
cristy3ed852e2009-09-05 21:47:34 +00006592 if (*version != '\0')
6593 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006594
cristy3ed852e2009-09-05 21:47:34 +00006595#if defined(MAGICKCORE_PNG_DELEGATE)
6596 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6597 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6598#endif
glennrp47b9dd52010-11-24 18:12:06 +00006599
cristy3ed852e2009-09-05 21:47:34 +00006600 entry->magick=(IsImageFormatHandler *) IsPNG;
6601 entry->adjoin=MagickFalse;
6602 entry->description=ConstantString("opaque 24-bit RGB");
6603 entry->module=ConstantString("PNG");
6604 (void) RegisterMagickInfo(entry);
6605
6606 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00006607
cristy3ed852e2009-09-05 21:47:34 +00006608#if defined(MAGICKCORE_PNG_DELEGATE)
6609 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6610 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6611#endif
glennrp47b9dd52010-11-24 18:12:06 +00006612
cristy3ed852e2009-09-05 21:47:34 +00006613 entry->magick=(IsImageFormatHandler *) IsPNG;
6614 entry->adjoin=MagickFalse;
6615 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6616 entry->module=ConstantString("PNG");
6617 (void) RegisterMagickInfo(entry);
6618
6619 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006620
cristy3ed852e2009-09-05 21:47:34 +00006621#if defined(JNG_SUPPORTED)
6622#if defined(MAGICKCORE_PNG_DELEGATE)
6623 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6624 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6625#endif
6626#endif
glennrp47b9dd52010-11-24 18:12:06 +00006627
cristy3ed852e2009-09-05 21:47:34 +00006628 entry->magick=(IsImageFormatHandler *) IsJNG;
6629 entry->adjoin=MagickFalse;
6630 entry->description=ConstantString("JPEG Network Graphics");
6631 entry->module=ConstantString("PNG");
6632 entry->note=ConstantString(JNGNote);
6633 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00006634
cristy18b17442009-10-25 18:36:48 +00006635#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006636 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00006637#endif
glennrp47b9dd52010-11-24 18:12:06 +00006638
cristy3ed852e2009-09-05 21:47:34 +00006639 return(MagickImageCoderSignature);
6640}
6641
6642/*
6643%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6644% %
6645% %
6646% %
6647% U n r e g i s t e r P N G I m a g e %
6648% %
6649% %
6650% %
6651%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6652%
6653% UnregisterPNGImage() removes format registrations made by the
6654% PNG module from the list of supported formats.
6655%
6656% The format of the UnregisterPNGImage method is:
6657%
6658% UnregisterPNGImage(void)
6659%
6660*/
6661ModuleExport void UnregisterPNGImage(void)
6662{
6663 (void) UnregisterMagickInfo("MNG");
6664 (void) UnregisterMagickInfo("PNG");
6665 (void) UnregisterMagickInfo("PNG8");
6666 (void) UnregisterMagickInfo("PNG24");
6667 (void) UnregisterMagickInfo("PNG32");
6668 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006669
cristy3ed852e2009-09-05 21:47:34 +00006670#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006671 if (ping_semaphore != (SemaphoreInfo *) NULL)
6672 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006673#endif
6674}
6675
6676#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006677#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006678/*
6679%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6680% %
6681% %
6682% %
6683% W r i t e M N G I m a g e %
6684% %
6685% %
6686% %
6687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6688%
6689% WriteMNGImage() writes an image in the Portable Network Graphics
6690% Group's "Multiple-image Network Graphics" encoded image format.
6691%
6692% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6693%
6694% The format of the WriteMNGImage method is:
6695%
6696% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6697%
6698% A description of each parameter follows.
6699%
6700% o image_info: the image info.
6701%
6702% o image: The image.
6703%
6704%
6705% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6706% "To do" under ReadPNGImage):
6707%
cristy3ed852e2009-09-05 21:47:34 +00006708% Preserve all unknown and not-yet-handled known chunks found in input
6709% PNG file and copy them into output PNG files according to the PNG
6710% copying rules.
6711%
6712% Write the iCCP chunk at MNG level when (icc profile length > 0)
6713%
6714% Improve selection of color type (use indexed-colour or indexed-colour
6715% with tRNS when 256 or fewer unique RGBA values are present).
6716%
6717% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6718% This will be complicated if we limit ourselves to generating MNG-LC
6719% files. For now we ignore disposal method 3 and simply overlay the next
6720% image on it.
6721%
6722% Check for identical PLTE's or PLTE/tRNS combinations and use a
6723% global MNG PLTE or PLTE/tRNS combination when appropriate.
6724% [mostly done 15 June 1999 but still need to take care of tRNS]
6725%
6726% Check for identical sRGB and replace with a global sRGB (and remove
6727% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6728% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6729% local gAMA/cHRM with local sRGB if appropriate).
6730%
6731% Check for identical sBIT chunks and write global ones.
6732%
6733% Provide option to skip writing the signature tEXt chunks.
6734%
6735% Use signatures to detect identical objects and reuse the first
6736% instance of such objects instead of writing duplicate objects.
6737%
6738% Use a smaller-than-32k value of compression window size when
6739% appropriate.
6740%
6741% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6742% ancillary text chunks and save profiles.
6743%
6744% Provide an option to force LC files (to ensure exact framing rate)
6745% instead of VLC.
6746%
6747% Provide an option to force VLC files instead of LC, even when offsets
6748% are present. This will involve expanding the embedded images with a
6749% transparent region at the top and/or left.
6750*/
6751
cristy3ed852e2009-09-05 21:47:34 +00006752static void
glennrpcf002022011-01-30 02:38:15 +00006753Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00006754 png_info *ping_info, unsigned char *profile_type, unsigned char
6755 *profile_description, unsigned char *profile_data, png_uint_32 length)
6756{
cristy3ed852e2009-09-05 21:47:34 +00006757 png_textp
6758 text;
6759
cristybb503372010-05-27 20:51:26 +00006760 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006761 i;
6762
6763 unsigned char
6764 *sp;
6765
6766 png_charp
6767 dp;
6768
6769 png_uint_32
6770 allocated_length,
6771 description_length;
6772
6773 unsigned char
6774 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006775
cristy3ed852e2009-09-05 21:47:34 +00006776 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6777 return;
6778
6779 if (image_info->verbose)
6780 {
glennrp0fe50b42010-11-16 03:52:51 +00006781 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6782 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006783 }
glennrp0fe50b42010-11-16 03:52:51 +00006784
cristy3ed852e2009-09-05 21:47:34 +00006785 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6786 description_length=(png_uint_32) strlen((const char *) profile_description);
6787 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6788 + description_length);
6789 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6790 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6791 text[0].key[0]='\0';
6792 (void) ConcatenateMagickString(text[0].key,
6793 "Raw profile type ",MaxTextExtent);
6794 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6795 sp=profile_data;
6796 dp=text[0].text;
6797 *dp++='\n';
6798 (void) CopyMagickString(dp,(const char *) profile_description,
6799 allocated_length);
6800 dp+=description_length;
6801 *dp++='\n';
6802 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006803 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006804 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00006805
cristybb503372010-05-27 20:51:26 +00006806 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006807 {
6808 if (i%36 == 0)
6809 *dp++='\n';
6810 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6811 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6812 }
glennrp47b9dd52010-11-24 18:12:06 +00006813
cristy3ed852e2009-09-05 21:47:34 +00006814 *dp++='\n';
6815 *dp='\0';
6816 text[0].text_length=(png_size_t) (dp-text[0].text);
6817 text[0].compression=image_info->compression == NoCompression ||
6818 (image_info->compression == UndefinedCompression &&
6819 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00006820
cristy3ed852e2009-09-05 21:47:34 +00006821 if (text[0].text_length <= allocated_length)
6822 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00006823
cristy3ed852e2009-09-05 21:47:34 +00006824 png_free(ping,text[0].text);
6825 png_free(ping,text[0].key);
6826 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006827}
6828
glennrpcf002022011-01-30 02:38:15 +00006829static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00006830 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00006831{
6832 char
6833 *name;
6834
6835 const StringInfo
6836 *profile;
6837
6838 unsigned char
6839 *data;
6840
6841 png_uint_32 length;
6842
6843 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00006844
6845 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6846 {
cristy3ed852e2009-09-05 21:47:34 +00006847 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00006848
cristy3ed852e2009-09-05 21:47:34 +00006849 if (profile != (const StringInfo *) NULL)
6850 {
6851 StringInfo
glennrpcf002022011-01-30 02:38:15 +00006852 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00006853
glennrp47b9dd52010-11-24 18:12:06 +00006854 if (LocaleNCompare(name,string,11) == 0)
6855 {
6856 if (logging != MagickFalse)
6857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6858 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00006859
glennrpcf002022011-01-30 02:38:15 +00006860 ping_profile=CloneStringInfo(profile);
6861 data=GetStringInfoDatum(ping_profile),
6862 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006863 data[4]=data[3];
6864 data[3]=data[2];
6865 data[2]=data[1];
6866 data[1]=data[0];
6867 (void) WriteBlobMSBULong(image,length-5); /* data length */
6868 (void) WriteBlob(image,length-1,data+1);
6869 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00006870 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006871 }
cristy3ed852e2009-09-05 21:47:34 +00006872 }
glennrp47b9dd52010-11-24 18:12:06 +00006873
cristy3ed852e2009-09-05 21:47:34 +00006874 name=GetNextImageProfile(image);
6875 }
glennrp47b9dd52010-11-24 18:12:06 +00006876
cristy3ed852e2009-09-05 21:47:34 +00006877 return(MagickTrue);
6878}
6879
glennrpb9cfe272010-12-21 15:08:06 +00006880
cristy3ed852e2009-09-05 21:47:34 +00006881/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00006882static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6883 const ImageInfo *IMimage_info,Image *IMimage)
6884{
6885 Image
6886 *image;
6887
6888 ImageInfo
6889 *image_info;
6890
cristy3ed852e2009-09-05 21:47:34 +00006891 char
6892 s[2];
6893
6894 const char
6895 *name,
6896 *property,
6897 *value;
6898
6899 const StringInfo
6900 *profile;
6901
cristy3ed852e2009-09-05 21:47:34 +00006902 int
cristy3ed852e2009-09-05 21:47:34 +00006903 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006904 pass;
6905
glennrpe9c26dc2010-05-30 01:56:35 +00006906 png_byte
6907 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006908
glennrp39992b42010-11-14 00:03:43 +00006909 png_color
6910 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00006911
glennrp5af765f2010-03-30 11:12:18 +00006912 png_color_16
6913 ping_background,
6914 ping_trans_color;
6915
cristy3ed852e2009-09-05 21:47:34 +00006916 png_info
6917 *ping_info;
6918
6919 png_struct
6920 *ping;
6921
glennrp5af765f2010-03-30 11:12:18 +00006922 png_uint_32
6923 ping_height,
6924 ping_width;
6925
cristybb503372010-05-27 20:51:26 +00006926 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006927 y;
6928
6929 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00006930 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00006931 logging,
glennrp58e01762011-01-07 15:28:54 +00006932 matte,
6933
glennrpda8f3a72011-02-27 23:54:12 +00006934 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00006935 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00006936 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00006937 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00006938 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00006939 ping_have_bKGD,
6940 ping_have_pHYs,
6941 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00006942
6943 ping_exclude_bKGD,
6944 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00006945 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00006946 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00006947 ping_exclude_gAMA,
6948 ping_exclude_iCCP,
6949 /* ping_exclude_iTXt, */
6950 ping_exclude_oFFs,
6951 ping_exclude_pHYs,
6952 ping_exclude_sRGB,
6953 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00006954 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00006955 ping_exclude_vpAg,
6956 ping_exclude_zCCP, /* hex-encoded iCCP */
6957 ping_exclude_zTXt,
6958
glennrp8d3d6e52011-04-19 04:39:51 +00006959 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00006960 ping_need_colortype_warning,
6961
glennrp82b3c532011-03-22 19:20:54 +00006962 status,
glennrpd3371642011-03-22 19:42:23 +00006963 tried_333,
6964 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00006965
6966 QuantumInfo
6967 *quantum_info;
6968
cristybb503372010-05-27 20:51:26 +00006969 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006970 i,
6971 x;
6972
6973 unsigned char
glennrpcf002022011-01-30 02:38:15 +00006974 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00006975
glennrp5af765f2010-03-30 11:12:18 +00006976 volatile int
glennrpf09bded2011-01-08 01:15:59 +00006977 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00006978 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00006979 ping_color_type,
6980 ping_interlace_method,
6981 ping_compression_method,
6982 ping_filter_method,
6983 ping_num_trans;
6984
cristybb503372010-05-27 20:51:26 +00006985 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00006986 image_depth,
6987 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006988
cristybb503372010-05-27 20:51:26 +00006989 size_t
cristy3ed852e2009-09-05 21:47:34 +00006990 quality,
6991 rowbytes,
6992 save_image_depth;
6993
glennrpdfd70802010-11-14 01:23:35 +00006994 int
glennrpfd05d622011-02-25 04:10:33 +00006995 j,
glennrpf09bded2011-01-08 01:15:59 +00006996 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00006997 number_opaque,
6998 number_semitransparent,
6999 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007000 ping_pHYs_unit_type;
7001
7002 png_uint_32
7003 ping_pHYs_x_resolution,
7004 ping_pHYs_y_resolution;
7005
cristy3ed852e2009-09-05 21:47:34 +00007006 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007007 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007008
glennrpb9cfe272010-12-21 15:08:06 +00007009 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7010 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007011 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007012 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007013
cristy3ed852e2009-09-05 21:47:34 +00007014#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007015 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007016#endif
7017
glennrp5af765f2010-03-30 11:12:18 +00007018 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007019 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007020 ping_color_type=0,
7021 ping_interlace_method=0,
7022 ping_compression_method=0,
7023 ping_filter_method=0,
7024 ping_num_trans = 0;
7025
7026 ping_background.red = 0;
7027 ping_background.green = 0;
7028 ping_background.blue = 0;
7029 ping_background.gray = 0;
7030 ping_background.index = 0;
7031
7032 ping_trans_color.red=0;
7033 ping_trans_color.green=0;
7034 ping_trans_color.blue=0;
7035 ping_trans_color.gray=0;
7036
glennrpdfd70802010-11-14 01:23:35 +00007037 ping_pHYs_unit_type = 0;
7038 ping_pHYs_x_resolution = 0;
7039 ping_pHYs_y_resolution = 0;
7040
glennrpda8f3a72011-02-27 23:54:12 +00007041 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007042 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007043 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007044 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007045 ping_have_bKGD=MagickFalse;
7046 ping_have_pHYs=MagickFalse;
7047 ping_have_tRNS=MagickFalse;
7048
glennrp0e8ea192010-12-24 18:00:33 +00007049 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7050 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007051 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007052 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007053 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007054 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7055 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7056 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7057 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7058 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7059 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007060 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007061 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7062 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7063 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7064
glennrp8d3d6e52011-04-19 04:39:51 +00007065 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007066 ping_need_colortype_warning = MagickFalse;
7067
glennrp8bb3a022010-12-13 20:40:04 +00007068 number_opaque = 0;
7069 number_semitransparent = 0;
7070 number_transparent = 0;
7071
glennrpfd05d622011-02-25 04:10:33 +00007072 if (logging != MagickFalse)
7073 {
7074 if (image->storage_class == UndefinedClass)
7075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7076 " storage_class=UndefinedClass");
7077 if (image->storage_class == DirectClass)
7078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7079 " storage_class=DirectClass");
7080 if (image->storage_class == PseudoClass)
7081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7082 " storage_class=PseudoClass");
7083 }
glennrp28af3712011-04-06 18:07:30 +00007084
7085 if (image->storage_class != PseudoClass && image->colormap != NULL)
7086 {
7087 /* Free the bogus colormap; it can cause trouble later */
7088 if (logging != MagickFalse)
7089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7090 " Freeing bogus colormap");
7091 (void *) RelinquishMagickMemory(image->colormap);
7092 image->colormap=NULL;
7093 }
glennrpfd05d622011-02-25 04:10:33 +00007094
cristy3ed852e2009-09-05 21:47:34 +00007095 if (image->colorspace != RGBColorspace)
7096 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007097
glennrp3241bd02010-12-12 04:36:28 +00007098 /*
7099 Sometimes we get PseudoClass images whose RGB values don't match
7100 the colors in the colormap. This code syncs the RGB values.
7101 */
7102 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7103 (void) SyncImage(image);
7104
glennrpa6a06632011-01-19 15:15:34 +00007105#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7106 if (image->depth > 8)
7107 {
7108 if (logging != MagickFalse)
7109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7110 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7111
7112 image->depth=8;
7113 }
7114#endif
7115
glennrp67b9c1a2011-04-22 18:47:36 +00007116#if 0 /* To do: Option to use the original colormap */
7117 if (ping_preserve_colormap != MagickFalse)
7118 {
7119 }
7120#endif
7121
glennrp3faa9a32011-04-23 14:00:25 +00007122#if 0 /* To do: respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007123 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7124 {
glennrp9d0ea4d2011-04-22 18:35:57 +00007125 }
glennrp67b9c1a2011-04-22 18:47:36 +00007126#endif
glennrp9d0ea4d2011-04-22 18:35:57 +00007127
glennrp67b9c1a2011-04-22 18:47:36 +00007128 /* To do: set to next higher multiple of 8 */
7129 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007130 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007131
glennrp2b013e42010-11-24 16:55:50 +00007132#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7133 /* PNG does not handle depths greater than 16 so reduce it even
7134 * if lossy
7135 */
7136 if (image->depth > 16)
7137 image->depth=16;
7138#endif
7139
glennrp3faa9a32011-04-23 14:00:25 +00007140#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007141 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007142 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007143 image->depth = 8;
7144#endif
7145
glennrpc8c2f062011-02-25 19:00:33 +00007146 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007147 * we reduce the transparency to binary and run again, then if there
7148 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7149 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7150 * palette. The final reduction can only fail if there are still 256
7151 * colors present and one of them has both transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007152 */
glennrp82b3c532011-03-22 19:20:54 +00007153
7154 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007155 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007156
glennrpd3371642011-03-22 19:42:23 +00007157 for (j=0; j<5; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007158 {
7159 /* BUILD_PALETTE
7160 *
7161 * Sometimes we get DirectClass images that have 256 colors or fewer.
7162 * This code will build a colormap.
7163 *
7164 * Also, sometimes we get PseudoClass images with an out-of-date
7165 * colormap. This code will replace the colormap with a new one.
7166 * Sometimes we get PseudoClass images that have more than 256 colors.
7167 * This code will delete the colormap and change the image to
7168 * DirectClass.
7169 *
7170 * If image->matte is MagickFalse, we ignore the opacity channel
7171 * even though it sometimes contains left-over non-opaque values.
7172 *
7173 * Also we gather some information (number of opaque, transparent,
7174 * and semitransparent pixels, and whether the image has any non-gray
7175 * pixels or only black-and-white pixels) that we might need later.
7176 *
7177 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7178 * we need to check for bogus non-opaque values, at least.
7179 */
glennrp3c218112010-11-27 15:31:26 +00007180
glennrpd71e86a2011-02-24 01:28:37 +00007181 ExceptionInfo
7182 *exception;
glennrp3c218112010-11-27 15:31:26 +00007183
glennrpd71e86a2011-02-24 01:28:37 +00007184 int
7185 n;
glennrp3c218112010-11-27 15:31:26 +00007186
glennrpd71e86a2011-02-24 01:28:37 +00007187 PixelPacket
7188 opaque[260],
7189 semitransparent[260],
7190 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007191
glennrpd71e86a2011-02-24 01:28:37 +00007192 register IndexPacket
7193 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007194
glennrpd71e86a2011-02-24 01:28:37 +00007195 register const PixelPacket
7196 *s,
7197 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007198
glennrpfd05d622011-02-25 04:10:33 +00007199 register PixelPacket
7200 *r;
7201
glennrpd71e86a2011-02-24 01:28:37 +00007202 if (logging != MagickFalse)
7203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7204 " Enter BUILD_PALETTE:");
7205
7206 if (logging != MagickFalse)
7207 {
glennrp03812ae2010-12-24 01:31:34 +00007208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007209 " image->columns=%.20g",(double) image->columns);
7210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7211 " image->rows=%.20g",(double) image->rows);
7212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7213 " image->matte=%.20g",(double) image->matte);
7214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7215 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007216
glennrpfd05d622011-02-25 04:10:33 +00007217 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007218 {
7219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007220 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007222 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007223
glennrpd71e86a2011-02-24 01:28:37 +00007224 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007225 {
glennrpd71e86a2011-02-24 01:28:37 +00007226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7227 " %d (%d,%d,%d,%d)",
7228 (int) i,
7229 (int) image->colormap[i].red,
7230 (int) image->colormap[i].green,
7231 (int) image->colormap[i].blue,
7232 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007233 }
glennrp2cc891a2010-12-24 13:44:32 +00007234
glennrpd71e86a2011-02-24 01:28:37 +00007235 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7236 {
7237 if (i > 255)
7238 {
7239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7240 " %d (%d,%d,%d,%d)",
7241 (int) i,
7242 (int) image->colormap[i].red,
7243 (int) image->colormap[i].green,
7244 (int) image->colormap[i].blue,
7245 (int) image->colormap[i].opacity);
7246 }
7247 }
glennrp03812ae2010-12-24 01:31:34 +00007248 }
glennrp7ddcc222010-12-11 05:01:05 +00007249
glennrpd71e86a2011-02-24 01:28:37 +00007250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7251 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007252
glennrpd71e86a2011-02-24 01:28:37 +00007253 if (image->colors == 0)
7254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7255 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007256
glennrp8d3d6e52011-04-19 04:39:51 +00007257 if (ping_preserve_colormap == MagickFalse)
7258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7259 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007260 }
7261
7262 exception=(&image->exception);
7263
7264 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007265 number_opaque = 0;
7266 number_semitransparent = 0;
7267 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007268
7269 for (y=0; y < (ssize_t) image->rows; y++)
7270 {
7271 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7272
7273 if (q == (PixelPacket *) NULL)
7274 break;
7275
7276 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007277 {
glennrpd71e86a2011-02-24 01:28:37 +00007278 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7279 {
7280 if (number_opaque < 259)
7281 {
7282 if (number_opaque == 0)
7283 {
7284 opaque[0]=*q;
7285 opaque[0].opacity=OpaqueOpacity;
7286 number_opaque=1;
7287 }
glennrp2cc891a2010-12-24 13:44:32 +00007288
glennrpd71e86a2011-02-24 01:28:37 +00007289 for (i=0; i< (ssize_t) number_opaque; i++)
7290 {
7291 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7292 break;
7293 }
glennrp7ddcc222010-12-11 05:01:05 +00007294
glennrpd71e86a2011-02-24 01:28:37 +00007295 if (i == (ssize_t) number_opaque &&
7296 number_opaque < 259)
7297 {
7298 number_opaque++;
7299 opaque[i] = *q;
7300 opaque[i].opacity = OpaqueOpacity;
7301 }
7302 }
7303 }
7304 else if (q->opacity == TransparentOpacity)
7305 {
7306 if (number_transparent < 259)
7307 {
7308 if (number_transparent == 0)
7309 {
7310 transparent[0]=*q;
glennrpa18d5bc2011-04-23 14:51:34 +00007311 ping_trans_color.red=
7312 (unsigned short) GetRedPixelComponent(q);
7313 ping_trans_color.green=
7314 (unsigned short) GetGreenPixelComponent(q);
7315 ping_trans_color.blue=
7316 (unsigned short) GetBluePixelComponent(q);
7317 ping_trans_color.gray=
7318 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007319 number_transparent = 1;
7320 }
7321
7322 for (i=0; i< (ssize_t) number_transparent; i++)
7323 {
7324 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7325 break;
7326 }
7327
7328 if (i == (ssize_t) number_transparent &&
7329 number_transparent < 259)
7330 {
7331 number_transparent++;
7332 transparent[i] = *q;
7333 }
7334 }
7335 }
7336 else
7337 {
7338 if (number_semitransparent < 259)
7339 {
7340 if (number_semitransparent == 0)
7341 {
7342 semitransparent[0]=*q;
7343 number_semitransparent = 1;
7344 }
7345
7346 for (i=0; i< (ssize_t) number_semitransparent; i++)
7347 {
7348 if (IsColorEqual(semitransparent+i,
7349 (PixelPacket *) q) &&
7350 q->opacity == semitransparent[i].opacity)
7351 break;
7352 }
7353
7354 if (i == (ssize_t) number_semitransparent &&
7355 number_semitransparent < 259)
7356 {
7357 number_semitransparent++;
7358 semitransparent[i] = *q;
7359 }
7360 }
7361 }
7362 q++;
7363 }
7364 }
7365
7366 if (ping_exclude_bKGD == MagickFalse)
7367 {
7368 /* Add the background color to the palette, if it
7369 * isn't already there.
7370 */
7371 for (i=0; i<number_opaque; i++)
7372 {
glennrpa080bc32011-03-11 18:03:44 +00007373 if (IsColorEqual(opaque+i, &image->background_color))
glennrpd71e86a2011-02-24 01:28:37 +00007374 break;
7375 }
7376
7377 if (number_opaque < 259 && i == number_opaque)
7378 {
7379 opaque[i]=image->background_color;
7380 opaque[i].opacity = OpaqueOpacity;
7381 number_opaque++;
7382 }
glennrpa080bc32011-03-11 18:03:44 +00007383 else if (logging != MagickFalse)
7384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7385 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007386 }
7387
7388 image_colors=number_opaque+number_transparent+number_semitransparent;
7389
glennrpa080bc32011-03-11 18:03:44 +00007390 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7391 {
7392 /* No room for the background color; remove it. */
7393 number_opaque--;
7394 image_colors--;
7395 }
7396
glennrpd71e86a2011-02-24 01:28:37 +00007397 if (logging != MagickFalse)
7398 {
7399 if (image_colors > 256)
7400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7401 " image has more than 256 colors");
7402
7403 else
7404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7405 " image has %d colors",image_colors);
7406 }
7407
glennrp8d3d6e52011-04-19 04:39:51 +00007408 if (ping_preserve_colormap != MagickFalse)
7409 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007410
glennrpfd05d622011-02-25 04:10:33 +00007411 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007412 {
7413 ping_have_color=MagickFalse;
7414 ping_have_non_bw=MagickFalse;
7415
7416 if(image_colors > 256)
7417 {
7418 for (y=0; y < (ssize_t) image->rows; y++)
7419 {
7420 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7421
7422 if (q == (PixelPacket *) NULL)
7423 break;
7424
7425 /* Worst case is black-and-white; we are looking at every
7426 * pixel twice.
7427 */
7428
7429 if (ping_have_color == MagickFalse)
7430 {
7431 s=q;
7432 for (x=0; x < (ssize_t) image->columns; x++)
7433 {
glennrpa18d5bc2011-04-23 14:51:34 +00007434 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7435 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00007436 {
7437 ping_have_color=MagickTrue;
7438 ping_have_non_bw=MagickTrue;
7439 break;
7440 }
7441 s++;
7442 }
7443 }
7444
7445 if (ping_have_non_bw == MagickFalse)
7446 {
7447 s=q;
7448 for (x=0; x < (ssize_t) image->columns; x++)
7449 {
glennrpa18d5bc2011-04-23 14:51:34 +00007450 if (GetRedPixelComponent(s) != 0 &&
7451 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00007452 {
7453 ping_have_non_bw=MagickTrue;
7454 }
7455 s++;
7456 }
7457 }
7458 }
7459 }
7460 }
7461
7462 if (image_colors < 257)
7463 {
7464 PixelPacket
7465 colormap[260];
7466
7467 /*
7468 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007469 */
7470
glennrpd71e86a2011-02-24 01:28:37 +00007471 if (logging != MagickFalse)
7472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7473 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007474
glennrpd71e86a2011-02-24 01:28:37 +00007475 /* Sort palette, transparent first */;
7476
7477 n = 0;
7478
7479 for (i=0; i<number_transparent; i++)
7480 colormap[n++] = transparent[i];
7481
7482 for (i=0; i<number_semitransparent; i++)
7483 colormap[n++] = semitransparent[i];
7484
7485 for (i=0; i<number_opaque; i++)
7486 colormap[n++] = opaque[i];
7487
7488
7489 /* image_colors < 257; search the colormap instead of the pixels
7490 * to get ping_have_color and ping_have_non_bw
7491 */
7492 for (i=0; i<n; i++)
7493 {
7494 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007495 {
glennrpd71e86a2011-02-24 01:28:37 +00007496 if (colormap[i].red != colormap[i].green ||
7497 colormap[i].red != colormap[i].blue)
7498 {
7499 ping_have_color=MagickTrue;
7500 ping_have_non_bw=MagickTrue;
7501 break;
7502 }
7503 }
7504
7505 if (ping_have_non_bw == MagickFalse)
7506 {
7507 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007508 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007509 }
glennrp8bb3a022010-12-13 20:40:04 +00007510 }
7511
glennrpd71e86a2011-02-24 01:28:37 +00007512 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7513 (number_transparent == 0 && number_semitransparent == 0)) &&
7514 (((mng_info->write_png_colortype-1) ==
7515 PNG_COLOR_TYPE_PALETTE) ||
7516 (mng_info->write_png_colortype == 0)))
7517 {
glennrp6185c532011-01-14 17:58:40 +00007518 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007519 {
glennrpd71e86a2011-02-24 01:28:37 +00007520 if (n != (ssize_t) image_colors)
7521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7522 " image_colors (%d) and n (%d) don't match",
7523 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007524
glennrpd71e86a2011-02-24 01:28:37 +00007525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7526 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007527 }
glennrp03812ae2010-12-24 01:31:34 +00007528
glennrpd71e86a2011-02-24 01:28:37 +00007529 image->colors = image_colors;
7530
7531 if (AcquireImageColormap(image,image_colors) ==
7532 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007533 ThrowWriterException(ResourceLimitError,
7534 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007535
7536 for (i=0; i< (ssize_t) image_colors; i++)
7537 image->colormap[i] = colormap[i];
7538
7539 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007540 {
glennrpd71e86a2011-02-24 01:28:37 +00007541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7542 " image->colors=%d (%d)",
7543 (int) image->colors, image_colors);
7544
7545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7546 " Update the pixel indexes");
7547 }
glennrp6185c532011-01-14 17:58:40 +00007548
glennrpfd05d622011-02-25 04:10:33 +00007549 /* Sync the pixel indices with the new colormap */
7550
glennrpd71e86a2011-02-24 01:28:37 +00007551 for (y=0; y < (ssize_t) image->rows; y++)
7552 {
7553 q=GetAuthenticPixels(image,0,y,image->columns,1,
7554 exception);
glennrp6185c532011-01-14 17:58:40 +00007555
glennrpd71e86a2011-02-24 01:28:37 +00007556 if (q == (PixelPacket *) NULL)
7557 break;
glennrp6185c532011-01-14 17:58:40 +00007558
glennrpd71e86a2011-02-24 01:28:37 +00007559 indexes=GetAuthenticIndexQueue(image);
7560
7561 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007562 {
glennrpd71e86a2011-02-24 01:28:37 +00007563 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007564 {
glennrpd71e86a2011-02-24 01:28:37 +00007565 if ((image->matte == MagickFalse ||
7566 image->colormap[i].opacity == q->opacity) &&
7567 (IsColorEqual(&image->colormap[i],
7568 (PixelPacket *) q)))
glennrp6185c532011-01-14 17:58:40 +00007569 {
glennrpd71e86a2011-02-24 01:28:37 +00007570 indexes[x]=(IndexPacket) i;
7571 break;
glennrp6185c532011-01-14 17:58:40 +00007572 }
glennrp6185c532011-01-14 17:58:40 +00007573 }
glennrpd71e86a2011-02-24 01:28:37 +00007574 q++;
7575 }
glennrp6185c532011-01-14 17:58:40 +00007576
glennrpd71e86a2011-02-24 01:28:37 +00007577 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7578 break;
7579 }
7580 }
7581 }
7582
7583 if (logging != MagickFalse)
7584 {
7585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7586 " image->colors=%d", (int) image->colors);
7587
7588 if (image->colormap != NULL)
7589 {
7590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7591 " i (red,green,blue,opacity)");
7592
7593 for (i=0; i < (ssize_t) image->colors; i++)
7594 {
cristy72988482011-03-29 16:34:38 +00007595 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007596 {
7597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7598 " %d (%d,%d,%d,%d)",
7599 (int) i,
7600 (int) image->colormap[i].red,
7601 (int) image->colormap[i].green,
7602 (int) image->colormap[i].blue,
7603 (int) image->colormap[i].opacity);
7604 }
glennrp6185c532011-01-14 17:58:40 +00007605 }
7606 }
glennrp03812ae2010-12-24 01:31:34 +00007607
glennrpd71e86a2011-02-24 01:28:37 +00007608 if (number_transparent < 257)
7609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7610 " number_transparent = %d",
7611 number_transparent);
7612 else
glennrp03812ae2010-12-24 01:31:34 +00007613
glennrpd71e86a2011-02-24 01:28:37 +00007614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7615 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007616
glennrpd71e86a2011-02-24 01:28:37 +00007617 if (number_opaque < 257)
7618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7619 " number_opaque = %d",
7620 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007621
glennrpd71e86a2011-02-24 01:28:37 +00007622 else
7623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7624 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007625
glennrpd71e86a2011-02-24 01:28:37 +00007626 if (number_semitransparent < 257)
7627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7628 " number_semitransparent = %d",
7629 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007630
glennrpd71e86a2011-02-24 01:28:37 +00007631 else
7632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7633 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007634
glennrpd71e86a2011-02-24 01:28:37 +00007635 if (ping_have_non_bw == MagickFalse)
7636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7637 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007638
glennrpd71e86a2011-02-24 01:28:37 +00007639 else if (ping_have_color == MagickFalse)
7640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7641 " All pixels and the background are gray");
7642
7643 else
7644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7645 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007646
glennrp03812ae2010-12-24 01:31:34 +00007647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7648 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007649 }
glennrpfd05d622011-02-25 04:10:33 +00007650
glennrpc8c2f062011-02-25 19:00:33 +00007651 if (mng_info->write_png8 == MagickFalse)
7652 break;
glennrpfd05d622011-02-25 04:10:33 +00007653
glennrpc8c2f062011-02-25 19:00:33 +00007654 /* Make any reductions necessary for the PNG8 format */
7655 if (image_colors <= 256 &&
7656 image_colors != 0 && image->colormap != NULL &&
7657 number_semitransparent == 0 &&
7658 number_transparent <= 1)
7659 break;
7660
7661 /* PNG8 can't have semitransparent colors so we threshold the
7662 * opacity to 0 or OpaqueOpacity
7663 */
7664 if (number_semitransparent != 0)
7665 {
7666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7667 " Thresholding the alpha channel to binary");
7668
7669 for (y=0; y < (ssize_t) image->rows; y++)
7670 {
7671 r=GetAuthenticPixels(image,0,y,image->columns,1,
7672 exception);
7673
7674 if (r == (PixelPacket *) NULL)
7675 break;
7676
7677 for (x=0; x < (ssize_t) image->columns; x++)
7678 {
glennrpa18d5bc2011-04-23 14:51:34 +00007679 SetOpacityPixelComponent(r,
7680 (GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7681 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00007682 r++;
7683 }
7684
7685 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7686 break;
7687
7688 if (image_colors != 0 && image_colors <= 256 &&
7689 image->colormap != NULL)
7690 for (i=0; i<image_colors; i++)
7691 image->colormap[i].opacity =
glennrp82b3c532011-03-22 19:20:54 +00007692 image->colormap[i].opacity > TransparentOpacity/2 ?
7693 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007694 }
7695 continue;
7696 }
7697
7698 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007699 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7700 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7701 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007702 */
glennrpd3371642011-03-22 19:42:23 +00007703 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7704 {
7705 if (logging != MagickFalse)
7706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7707 " Quantizing the background color to 4-4-4");
7708
7709 tried_444 = MagickTrue;
7710
7711 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007712 ScaleCharToQuantum(
7713 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7714 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007715 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007716 ScaleCharToQuantum(
7717 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7718 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007719 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007720 ScaleCharToQuantum(
7721 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7722 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007723
7724 if (logging != MagickFalse)
7725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7726 " Quantizing the pixel colors to 4-4-4");
7727
7728 if (image->colormap == NULL)
7729 {
7730 for (y=0; y < (ssize_t) image->rows; y++)
7731 {
7732 r=GetAuthenticPixels(image,0,y,image->columns,1,
7733 exception);
7734
7735 if (r == (PixelPacket *) NULL)
7736 break;
7737
7738 for (x=0; x < (ssize_t) image->columns; x++)
7739 {
7740 if (r->opacity == TransparentOpacity)
7741 {
7742 r->red = image->background_color.red;
7743 r->green = image->background_color.green;
7744 r->blue = image->background_color.blue;
7745 }
7746 else
7747 {
glennrp3faa9a32011-04-23 14:00:25 +00007748 r->red=ScaleCharToQuantum(
7749 (ScaleQuantumToChar(r->red) & 0xf0) |
7750 (ScaleQuantumToChar(r->red) & 0xf0) >> 4);
7751 r->green=ScaleCharToQuantum(
7752 (ScaleQuantumToChar(r->green) & 0xf0) |
7753 (ScaleQuantumToChar(r->green) & 0xf0) >> 4);
7754 r->blue=ScaleCharToQuantum(
7755 (ScaleQuantumToChar(r->blue) & 0xf0) |
7756 (ScaleQuantumToChar(r->blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007757 }
7758 r++;
7759 }
7760
7761 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7762 break;
7763 }
7764 }
7765
7766 else /* Should not reach this; colormap already exists and
7767 must be <= 256 */
7768 {
7769 if (logging != MagickFalse)
7770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7771 " Quantizing the colormap to 4-4-4");
7772 for (i=0; i<image_colors; i++)
7773 {
glennrp3faa9a32011-04-23 14:00:25 +00007774 image->colormap[i].red=ScaleCharToQuantum(
7775 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7776 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7777 image->colormap[i].green=ScaleCharToQuantum(
7778 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7779 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7780 image->colormap[i].blue=ScaleCharToQuantum(
7781 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7782 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
glennrpd3371642011-03-22 19:42:23 +00007783 }
7784 }
7785 continue;
7786 }
7787
glennrp82b3c532011-03-22 19:20:54 +00007788 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7789 {
7790 if (logging != MagickFalse)
7791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7792 " Quantizing the background color to 3-3-3");
7793
7794 tried_333 = MagickTrue;
7795
7796 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007797 ScaleCharToQuantum(
7798 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7799 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7800 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007801 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007802 ScaleCharToQuantum(
7803 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7804 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7805 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007806 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007807 ScaleCharToQuantum(
7808 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7809 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7810 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007811
7812 if (logging != MagickFalse)
7813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007814 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007815
7816 if (image->colormap == NULL)
7817 {
7818 for (y=0; y < (ssize_t) image->rows; y++)
7819 {
7820 r=GetAuthenticPixels(image,0,y,image->columns,1,
7821 exception);
7822
7823 if (r == (PixelPacket *) NULL)
7824 break;
7825
7826 for (x=0; x < (ssize_t) image->columns; x++)
7827 {
7828 if (r->opacity == TransparentOpacity)
7829 {
7830 r->red = image->background_color.red;
7831 r->green = image->background_color.green;
7832 r->blue = image->background_color.blue;
7833 }
7834 else
7835 {
glennrp3faa9a32011-04-23 14:00:25 +00007836 r->red=ScaleCharToQuantum(
7837 (ScaleQuantumToChar(r->red) & 0xe0) |
7838 (ScaleQuantumToChar(r->red) & 0xe0) >> 3 |
7839 (ScaleQuantumToChar(r->red) & 0xc0) >> 6);
7840 r->green=ScaleCharToQuantum(
7841 (ScaleQuantumToChar(r->green) & 0xe0) |
7842 (ScaleQuantumToChar(r->green) & 0xe0) >> 3 |
7843 (ScaleQuantumToChar(r->green) & 0xc0) >> 6);
7844 r->blue=ScaleCharToQuantum(
7845 (ScaleQuantumToChar(r->blue) & 0xe0) |
7846 (ScaleQuantumToChar(r->blue) & 0xe0) >> 3 |
7847 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007848 }
7849 r++;
7850 }
7851
7852 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7853 break;
7854 }
7855 }
7856
7857 else /* Should not reach this; colormap already exists and
7858 must be <= 256 */
7859 {
7860 if (logging != MagickFalse)
7861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007862 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007863 for (i=0; i<image_colors; i++)
7864 {
glennrp3faa9a32011-04-23 14:00:25 +00007865 image->colormap[i].red=ScaleCharToQuantum(
7866 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7867 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7868 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7869 image->colormap[i].green=ScaleCharToQuantum(
7870 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7871 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7872 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7873 image->colormap[i].blue=ScaleCharToQuantum(
7874 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7875 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7876 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007877 }
glennrpd3371642011-03-22 19:42:23 +00007878 }
7879 continue;
glennrp82b3c532011-03-22 19:20:54 +00007880 }
glennrpc8c2f062011-02-25 19:00:33 +00007881
glennrpc8c2f062011-02-25 19:00:33 +00007882 if (image_colors == 0 || image_colors > 256)
7883 {
7884 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007886 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007887
glennrp3faa9a32011-04-23 14:00:25 +00007888 /* Red and green were already done so we only quantize the blue
7889 * channel
7890 */
7891
7892 image->background_color.blue=ScaleCharToQuantum(
7893 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7894 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7895 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7896 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrpfd05d622011-02-25 04:10:33 +00007897
glennrpc8c2f062011-02-25 19:00:33 +00007898 if (logging != MagickFalse)
7899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007900 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007901
glennrpc8c2f062011-02-25 19:00:33 +00007902 if (image->colormap == NULL)
7903 {
7904 for (y=0; y < (ssize_t) image->rows; y++)
7905 {
7906 r=GetAuthenticPixels(image,0,y,image->columns,1,
7907 exception);
7908
7909 if (r == (PixelPacket *) NULL)
7910 break;
7911
7912 for (x=0; x < (ssize_t) image->columns; x++)
7913 {
glennrp82b3c532011-03-22 19:20:54 +00007914 if (r->opacity == TransparentOpacity)
7915 {
7916 r->red = image->background_color.red;
7917 r->green = image->background_color.green;
7918 r->blue = image->background_color.blue;
7919 }
7920 else
7921 {
glennrp3faa9a32011-04-23 14:00:25 +00007922 r->blue=ScaleCharToQuantum(
7923 (ScaleQuantumToChar(r->blue) & 0xc0) |
7924 (ScaleQuantumToChar(r->blue) & 0xc0) >> 2 |
7925 (ScaleQuantumToChar(r->blue) & 0xc0) >> 4 |
7926 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007927 }
glennrp52a479c2011-02-26 21:14:38 +00007928 r++;
glennrpc8c2f062011-02-25 19:00:33 +00007929 }
glennrpfd05d622011-02-25 04:10:33 +00007930
glennrpc8c2f062011-02-25 19:00:33 +00007931 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7932 break;
7933 }
7934 }
glennrpfd05d622011-02-25 04:10:33 +00007935
glennrpc8c2f062011-02-25 19:00:33 +00007936 else /* Should not reach this; colormap already exists and
7937 must be <= 256 */
7938 {
7939 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007941 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00007942 for (i=0; i<image_colors; i++)
7943 {
glennrp3faa9a32011-04-23 14:00:25 +00007944 image->colormap[i].blue=ScaleCharToQuantum(
7945 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
7946 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
7947 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
7948 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrpc8c2f062011-02-25 19:00:33 +00007949 }
7950 }
7951 continue;
7952 }
7953 break;
glennrpd71e86a2011-02-24 01:28:37 +00007954 }
glennrpfd05d622011-02-25 04:10:33 +00007955 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00007956
glennrpfd05d622011-02-25 04:10:33 +00007957 /* If we are excluding the tRNS chunk and there is transparency,
7958 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7959 * PNG.
glennrp8d579662011-02-23 02:05:02 +00007960 */
glennrp0e8ea192010-12-24 18:00:33 +00007961 if (mng_info->ping_exclude_tRNS != MagickFalse &&
7962 (number_transparent != 0 || number_semitransparent != 0))
7963 {
7964 int colortype=mng_info->write_png_colortype;
7965
7966 if (ping_have_color == MagickFalse)
7967 mng_info->write_png_colortype = 5;
7968
7969 else
7970 mng_info->write_png_colortype = 7;
7971
glennrp8d579662011-02-23 02:05:02 +00007972 if (colortype != 0 &&
7973 mng_info->write_png_colortype != (ssize_t) colortype)
glennrp0e8ea192010-12-24 18:00:33 +00007974 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00007975
glennrp0e8ea192010-12-24 18:00:33 +00007976 }
7977
glennrpfd05d622011-02-25 04:10:33 +00007978 /* See if cheap transparency is possible. It is only possible
7979 * when there is a single transparent color, no semitransparent
7980 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00007981 * as the transparent color. We only need this information if
7982 * we are writing a PNG with colortype 0 or 2, and we have not
7983 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00007984 */
glennrp5a39f372011-02-25 04:52:16 +00007985 if (number_transparent == 1 &&
7986 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00007987 {
7988 ping_have_cheap_transparency = MagickTrue;
7989
7990 if (number_semitransparent != 0)
7991 ping_have_cheap_transparency = MagickFalse;
7992
7993 else if (image_colors == 0 || image_colors > 256 ||
7994 image->colormap == NULL)
7995 {
7996 ExceptionInfo
7997 *exception;
7998
7999 register const PixelPacket
8000 *q;
8001
8002 exception=(&image->exception);
8003
8004 for (y=0; y < (ssize_t) image->rows; y++)
8005 {
8006 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8007
8008 if (q == (PixelPacket *) NULL)
8009 break;
8010
8011 for (x=0; x < (ssize_t) image->columns; x++)
8012 {
8013 if (q->opacity != TransparentOpacity &&
8014 (unsigned short) q->red == ping_trans_color.red &&
8015 (unsigned short) q->green == ping_trans_color.green &&
8016 (unsigned short) q->blue == ping_trans_color.blue)
8017 {
8018 ping_have_cheap_transparency = MagickFalse;
8019 break;
8020 }
8021
8022 q++;
8023 }
8024
8025 if (ping_have_cheap_transparency == MagickFalse)
8026 break;
8027 }
8028 }
8029 else
8030 {
glennrp67b9c1a2011-04-22 18:47:36 +00008031 /* Assuming that image->colormap[0] is the one transparent color
8032 * and that all others are opaque.
8033 */
glennrpfd05d622011-02-25 04:10:33 +00008034 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008035 for (i=1; i<image_colors; i++)
8036 if (image->colormap[i].red == image->colormap[0].red &&
8037 image->colormap[i].green == image->colormap[0].green &&
8038 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008039 {
glennrp67b9c1a2011-04-22 18:47:36 +00008040 ping_have_cheap_transparency = MagickFalse;
8041 break;
glennrpfd05d622011-02-25 04:10:33 +00008042 }
8043 }
8044
8045 if (logging != MagickFalse)
8046 {
8047 if (ping_have_cheap_transparency == MagickFalse)
8048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8049 " Cheap transparency is not possible.");
8050
8051 else
8052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8053 " Cheap transparency is possible.");
8054 }
8055 }
8056 else
8057 ping_have_cheap_transparency = MagickFalse;
8058
glennrp8640fb52010-11-23 15:48:26 +00008059 image_depth=image->depth;
8060
glennrp26c990a2010-11-23 02:23:20 +00008061 quantum_info = (QuantumInfo *) NULL;
8062 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008063 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008064 image_matte=image->matte;
8065
glennrp0fe50b42010-11-16 03:52:51 +00008066 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008067 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008068
glennrp52a479c2011-02-26 21:14:38 +00008069 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8070 (image->colors == 0 || image->colormap == NULL))
8071 {
glennrp52a479c2011-02-26 21:14:38 +00008072 image_info=DestroyImageInfo(image_info);
8073 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008074 (void) ThrowMagickException(&IMimage->exception,
8075 GetMagickModule(),CoderError,
8076 "Cannot write PNG8 or color-type 3; colormap is NULL",
8077 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008078#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8079 UnlockSemaphoreInfo(ping_semaphore);
8080#endif
8081 return(MagickFalse);
8082 }
8083
cristy3ed852e2009-09-05 21:47:34 +00008084 /*
8085 Allocate the PNG structures
8086 */
8087#ifdef PNG_USER_MEM_SUPPORTED
8088 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008089 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8090 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008091
cristy3ed852e2009-09-05 21:47:34 +00008092#else
8093 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008094 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008095
cristy3ed852e2009-09-05 21:47:34 +00008096#endif
8097 if (ping == (png_struct *) NULL)
8098 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008099
cristy3ed852e2009-09-05 21:47:34 +00008100 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008101
cristy3ed852e2009-09-05 21:47:34 +00008102 if (ping_info == (png_info *) NULL)
8103 {
8104 png_destroy_write_struct(&ping,(png_info **) NULL);
8105 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8106 }
glennrp0fe50b42010-11-16 03:52:51 +00008107
cristy3ed852e2009-09-05 21:47:34 +00008108 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008109 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008110
glennrp5af765f2010-03-30 11:12:18 +00008111 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008112 {
8113 /*
8114 PNG write failed.
8115 */
8116#ifdef PNG_DEBUG
8117 if (image_info->verbose)
8118 (void) printf("PNG write has failed.\n");
8119#endif
8120 png_destroy_write_struct(&ping,&ping_info);
8121#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008122 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008123#endif
glennrpda8f3a72011-02-27 23:54:12 +00008124 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008125 (void) CloseBlob(image);
8126 image_info=DestroyImageInfo(image_info);
8127 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008128 return(MagickFalse);
8129 }
8130 /*
8131 Prepare PNG for writing.
8132 */
8133#if defined(PNG_MNG_FEATURES_SUPPORTED)
8134 if (mng_info->write_mng)
8135 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008136
cristy3ed852e2009-09-05 21:47:34 +00008137#else
8138# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8139 if (mng_info->write_mng)
8140 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008141
cristy3ed852e2009-09-05 21:47:34 +00008142# endif
8143#endif
glennrp2b013e42010-11-24 16:55:50 +00008144
cristy3ed852e2009-09-05 21:47:34 +00008145 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008146
cristy4e5bc842010-06-09 13:56:01 +00008147 ping_width=(png_uint_32) image->columns;
8148 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008149
cristy3ed852e2009-09-05 21:47:34 +00008150 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8151 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008152
cristy3ed852e2009-09-05 21:47:34 +00008153 if (mng_info->write_png_depth != 0)
8154 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008155
cristy3ed852e2009-09-05 21:47:34 +00008156 /* Adjust requested depth to next higher valid depth if necessary */
8157 if (image_depth > 8)
8158 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008159
cristy3ed852e2009-09-05 21:47:34 +00008160 if ((image_depth > 4) && (image_depth < 8))
8161 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008162
cristy3ed852e2009-09-05 21:47:34 +00008163 if (image_depth == 3)
8164 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008165
cristy3ed852e2009-09-05 21:47:34 +00008166 if (logging != MagickFalse)
8167 {
8168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008169 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008171 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008173 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008175 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008177 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008178 }
glennrp8640fb52010-11-23 15:48:26 +00008179
cristy3ed852e2009-09-05 21:47:34 +00008180 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008181 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008182
glennrp26f37912010-12-23 16:22:42 +00008183
cristy3ed852e2009-09-05 21:47:34 +00008184#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008185 if (ping_exclude_pHYs == MagickFalse)
8186 {
cristy3ed852e2009-09-05 21:47:34 +00008187 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8188 (!mng_info->write_mng || !mng_info->equal_physs))
8189 {
glennrp0fe50b42010-11-16 03:52:51 +00008190 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008193
8194 if (image->units == PixelsPerInchResolution)
8195 {
glennrpdfd70802010-11-14 01:23:35 +00008196 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008197 ping_pHYs_x_resolution=
8198 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8199 ping_pHYs_y_resolution=
8200 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008201 }
glennrpdfd70802010-11-14 01:23:35 +00008202
cristy3ed852e2009-09-05 21:47:34 +00008203 else if (image->units == PixelsPerCentimeterResolution)
8204 {
glennrpdfd70802010-11-14 01:23:35 +00008205 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008206 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8207 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008208 }
glennrp991d11d2010-11-12 21:55:28 +00008209
cristy3ed852e2009-09-05 21:47:34 +00008210 else
8211 {
glennrpdfd70802010-11-14 01:23:35 +00008212 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8213 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8214 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008215 }
glennrp991d11d2010-11-12 21:55:28 +00008216
glennrp823b55c2011-03-14 18:46:46 +00008217 if (logging != MagickFalse)
8218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8219 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8220 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8221 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008222 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008223 }
glennrp26f37912010-12-23 16:22:42 +00008224 }
cristy3ed852e2009-09-05 21:47:34 +00008225#endif
glennrpa521b2f2010-10-29 04:11:03 +00008226
glennrp26f37912010-12-23 16:22:42 +00008227 if (ping_exclude_bKGD == MagickFalse)
8228 {
glennrpa521b2f2010-10-29 04:11:03 +00008229 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008230 {
glennrpa521b2f2010-10-29 04:11:03 +00008231 unsigned int
8232 mask;
cristy3ed852e2009-09-05 21:47:34 +00008233
glennrpa521b2f2010-10-29 04:11:03 +00008234 mask=0xffff;
8235 if (ping_bit_depth == 8)
8236 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008237
glennrpa521b2f2010-10-29 04:11:03 +00008238 if (ping_bit_depth == 4)
8239 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008240
glennrpa521b2f2010-10-29 04:11:03 +00008241 if (ping_bit_depth == 2)
8242 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008243
glennrpa521b2f2010-10-29 04:11:03 +00008244 if (ping_bit_depth == 1)
8245 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008246
glennrpa521b2f2010-10-29 04:11:03 +00008247 ping_background.red=(png_uint_16)
8248 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008249
glennrpa521b2f2010-10-29 04:11:03 +00008250 ping_background.green=(png_uint_16)
8251 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008252
glennrpa521b2f2010-10-29 04:11:03 +00008253 ping_background.blue=(png_uint_16)
8254 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008255 }
cristy3ed852e2009-09-05 21:47:34 +00008256
glennrp0fe50b42010-11-16 03:52:51 +00008257 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008258 {
8259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8260 " Setting up bKGD chunk (1)");
8261
8262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8263 " ping_bit_depth=%d",ping_bit_depth);
8264 }
glennrp0fe50b42010-11-16 03:52:51 +00008265
8266 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008267 }
glennrp0fe50b42010-11-16 03:52:51 +00008268
cristy3ed852e2009-09-05 21:47:34 +00008269 /*
8270 Select the color type.
8271 */
8272 matte=image_matte;
8273 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008274
glennrp1273f7b2011-02-24 03:20:30 +00008275 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008276 {
glennrp0fe50b42010-11-16 03:52:51 +00008277
glennrpfd05d622011-02-25 04:10:33 +00008278 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008279 for reducing the sample depth from 8. */
8280
glennrp0fe50b42010-11-16 03:52:51 +00008281 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008282
glennrp8bb3a022010-12-13 20:40:04 +00008283 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008284
8285 /*
8286 Set image palette.
8287 */
8288 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8289
glennrp0fe50b42010-11-16 03:52:51 +00008290 if (logging != MagickFalse)
8291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8292 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008293 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008294
8295 for (i=0; i < (ssize_t) number_colors; i++)
8296 {
8297 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8298 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8299 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8300 if (logging != MagickFalse)
8301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008302#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008303 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008304#else
8305 " %5ld (%5d,%5d,%5d)",
8306#endif
glennrp0fe50b42010-11-16 03:52:51 +00008307 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8308
8309 }
glennrp2b013e42010-11-24 16:55:50 +00008310
glennrp8bb3a022010-12-13 20:40:04 +00008311 ping_have_PLTE=MagickTrue;
8312 image_depth=ping_bit_depth;
8313 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008314
glennrp58e01762011-01-07 15:28:54 +00008315 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008316 {
glennrp0fe50b42010-11-16 03:52:51 +00008317 /*
8318 Identify which colormap entry is transparent.
8319 */
8320 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008321 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008322
glennrp8bb3a022010-12-13 20:40:04 +00008323 for (i=0; i < (ssize_t) number_transparent; i++)
8324 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008325
glennrp0fe50b42010-11-16 03:52:51 +00008326
glennrp2cc891a2010-12-24 13:44:32 +00008327 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008328 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008329
8330 if (ping_num_trans == 0)
8331 ping_have_tRNS=MagickFalse;
8332
glennrp8bb3a022010-12-13 20:40:04 +00008333 else
8334 ping_have_tRNS=MagickTrue;
8335 }
glennrp0fe50b42010-11-16 03:52:51 +00008336
glennrp1273f7b2011-02-24 03:20:30 +00008337 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008338 {
glennrp1273f7b2011-02-24 03:20:30 +00008339 /*
8340 * Identify which colormap entry is the background color.
8341 */
8342
glennrp4f25bd02011-01-01 18:51:28 +00008343 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8344 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8345 break;
glennrp0fe50b42010-11-16 03:52:51 +00008346
glennrp4f25bd02011-01-01 18:51:28 +00008347 ping_background.index=(png_byte) i;
8348 }
cristy3ed852e2009-09-05 21:47:34 +00008349 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008350
cristy3ed852e2009-09-05 21:47:34 +00008351 else if (mng_info->write_png24)
8352 {
8353 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008354 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008355 }
glennrp0fe50b42010-11-16 03:52:51 +00008356
cristy3ed852e2009-09-05 21:47:34 +00008357 else if (mng_info->write_png32)
8358 {
8359 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008360 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008361 }
glennrp0fe50b42010-11-16 03:52:51 +00008362
glennrp8bb3a022010-12-13 20:40:04 +00008363 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008364 {
glennrp5af765f2010-03-30 11:12:18 +00008365 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008366
glennrp8bb3a022010-12-13 20:40:04 +00008367 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008368 {
glennrp5af765f2010-03-30 11:12:18 +00008369 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008370
glennrp5af765f2010-03-30 11:12:18 +00008371 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8372 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008373 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008374
glennrp8bb3a022010-12-13 20:40:04 +00008375 else
8376 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008377
8378 if (logging != MagickFalse)
8379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8380 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008381 }
glennrp0fe50b42010-11-16 03:52:51 +00008382
glennrp7c4c9e62011-03-21 20:23:32 +00008383 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008384 {
8385 if (logging != MagickFalse)
8386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008387 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008388
glennrpd6bf1612010-12-17 17:28:54 +00008389 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008390 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008391
glennrpd6bf1612010-12-17 17:28:54 +00008392 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008393 {
glennrp5af765f2010-03-30 11:12:18 +00008394 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008395 image_matte=MagickFalse;
8396 }
glennrp0fe50b42010-11-16 03:52:51 +00008397
glennrpd6bf1612010-12-17 17:28:54 +00008398 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008399 {
glennrp5af765f2010-03-30 11:12:18 +00008400 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008401 image_matte=MagickTrue;
8402 }
glennrp0fe50b42010-11-16 03:52:51 +00008403
glennrp5aa37f62011-01-02 03:07:57 +00008404 if (image_info->type == PaletteType ||
8405 image_info->type == PaletteMatteType)
8406 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8407
glennrp7c4c9e62011-03-21 20:23:32 +00008408 if (mng_info->write_png_colortype == 0 &&
8409 (image_info->type == UndefinedType ||
8410 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008411 {
glennrp5aa37f62011-01-02 03:07:57 +00008412 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008413 {
glennrp5aa37f62011-01-02 03:07:57 +00008414 if (image_matte == MagickFalse)
8415 {
8416 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8417 image_matte=MagickFalse;
8418 }
glennrp0fe50b42010-11-16 03:52:51 +00008419
glennrp0b206f52011-01-07 04:55:32 +00008420 else
glennrp5aa37f62011-01-02 03:07:57 +00008421 {
8422 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8423 image_matte=MagickTrue;
8424 }
8425 }
8426 else
glennrp8bb3a022010-12-13 20:40:04 +00008427 {
glennrp5aa37f62011-01-02 03:07:57 +00008428 if (image_matte == MagickFalse)
8429 {
8430 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8431 image_matte=MagickFalse;
8432 }
glennrp8bb3a022010-12-13 20:40:04 +00008433
glennrp0b206f52011-01-07 04:55:32 +00008434 else
glennrp5aa37f62011-01-02 03:07:57 +00008435 {
8436 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8437 image_matte=MagickTrue;
8438 }
8439 }
glennrp0fe50b42010-11-16 03:52:51 +00008440 }
glennrp5aa37f62011-01-02 03:07:57 +00008441
cristy3ed852e2009-09-05 21:47:34 +00008442 }
glennrp0fe50b42010-11-16 03:52:51 +00008443
cristy3ed852e2009-09-05 21:47:34 +00008444 if (logging != MagickFalse)
8445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008446 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008447
glennrp5af765f2010-03-30 11:12:18 +00008448 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008449 {
8450 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8451 ping_color_type == PNG_COLOR_TYPE_RGB ||
8452 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8453 ping_bit_depth=8;
8454 }
cristy3ed852e2009-09-05 21:47:34 +00008455
glennrpd6bf1612010-12-17 17:28:54 +00008456 old_bit_depth=ping_bit_depth;
8457
glennrp5af765f2010-03-30 11:12:18 +00008458 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008459 {
glennrp8d579662011-02-23 02:05:02 +00008460 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8461 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008462 }
glennrp8640fb52010-11-23 15:48:26 +00008463
glennrp5af765f2010-03-30 11:12:18 +00008464 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008465 {
cristy35ef8242010-06-03 16:24:13 +00008466 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008467 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008468
8469 if (image->colors == 0)
8470 {
glennrp0fe50b42010-11-16 03:52:51 +00008471 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008472 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008473 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008474 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008475 }
8476
cristy35ef8242010-06-03 16:24:13 +00008477 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008478 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008479 }
glennrp2b013e42010-11-24 16:55:50 +00008480
glennrpd6bf1612010-12-17 17:28:54 +00008481 if (logging != MagickFalse)
8482 {
8483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8484 " Number of colors: %.20g",(double) image_colors);
8485
8486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8487 " Tentative PNG bit depth: %d",ping_bit_depth);
8488 }
8489
8490 if (ping_bit_depth < (int) mng_info->write_png_depth)
8491 ping_bit_depth = mng_info->write_png_depth;
8492 }
glennrp2cc891a2010-12-24 13:44:32 +00008493
glennrp5af765f2010-03-30 11:12:18 +00008494 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008495
cristy3ed852e2009-09-05 21:47:34 +00008496 if (logging != MagickFalse)
8497 {
8498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008499 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008500
cristy3ed852e2009-09-05 21:47:34 +00008501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008502 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008503
cristy3ed852e2009-09-05 21:47:34 +00008504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008505 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008506
cristy3ed852e2009-09-05 21:47:34 +00008507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008508
glennrp8640fb52010-11-23 15:48:26 +00008509 " image->depth: %.20g",(double) image->depth);
8510
8511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008512 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008513 }
8514
glennrp58e01762011-01-07 15:28:54 +00008515 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008516 {
glennrp4f25bd02011-01-01 18:51:28 +00008517 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008518 {
glennrp7c4c9e62011-03-21 20:23:32 +00008519 if (mng_info->write_png_colortype == 0)
8520 {
8521 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008522
glennrp7c4c9e62011-03-21 20:23:32 +00008523 if (ping_have_color != MagickFalse)
8524 ping_color_type=PNG_COLOR_TYPE_RGBA;
8525 }
glennrp4f25bd02011-01-01 18:51:28 +00008526
8527 /*
8528 * Determine if there is any transparent color.
8529 */
8530 if (number_transparent + number_semitransparent == 0)
8531 {
8532 /*
8533 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8534 */
glennrpa6a06632011-01-19 15:15:34 +00008535
glennrp4f25bd02011-01-01 18:51:28 +00008536 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008537
8538 if (mng_info->write_png_colortype == 0)
8539 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008540 }
8541
8542 else
8543 {
8544 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008545 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008546
8547 mask=0xffff;
8548
8549 if (ping_bit_depth == 8)
8550 mask=0x00ff;
8551
8552 if (ping_bit_depth == 4)
8553 mask=0x000f;
8554
8555 if (ping_bit_depth == 2)
8556 mask=0x0003;
8557
8558 if (ping_bit_depth == 1)
8559 mask=0x0001;
8560
8561 ping_trans_color.red=(png_uint_16)
8562 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8563
8564 ping_trans_color.green=(png_uint_16)
8565 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8566
8567 ping_trans_color.blue=(png_uint_16)
8568 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8569
8570 ping_trans_color.gray=(png_uint_16)
8571 (ScaleQuantumToShort(PixelIntensityToQuantum(
8572 image->colormap)) & mask);
8573
8574 ping_trans_color.index=(png_byte) 0;
8575
8576 ping_have_tRNS=MagickTrue;
8577 }
8578
8579 if (ping_have_tRNS != MagickFalse)
8580 {
8581 /*
glennrpfd05d622011-02-25 04:10:33 +00008582 * Determine if there is one and only one transparent color
8583 * and if so if it is fully transparent.
8584 */
8585 if (ping_have_cheap_transparency == MagickFalse)
8586 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008587 }
8588
8589 if (ping_have_tRNS != MagickFalse)
8590 {
glennrp7c4c9e62011-03-21 20:23:32 +00008591 if (mng_info->write_png_colortype == 0)
8592 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008593
8594 if (image_depth == 8)
8595 {
8596 ping_trans_color.red&=0xff;
8597 ping_trans_color.green&=0xff;
8598 ping_trans_color.blue&=0xff;
8599 ping_trans_color.gray&=0xff;
8600 }
8601 }
8602 }
cristy3ed852e2009-09-05 21:47:34 +00008603 else
8604 {
cristy3ed852e2009-09-05 21:47:34 +00008605 if (image_depth == 8)
8606 {
glennrp5af765f2010-03-30 11:12:18 +00008607 ping_trans_color.red&=0xff;
8608 ping_trans_color.green&=0xff;
8609 ping_trans_color.blue&=0xff;
8610 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008611 }
8612 }
8613 }
glennrp8640fb52010-11-23 15:48:26 +00008614
cristy3ed852e2009-09-05 21:47:34 +00008615 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008616
glennrp2e09f552010-11-14 00:38:48 +00008617 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008618 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008619
glennrp39992b42010-11-14 00:03:43 +00008620 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008621 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008622 ping_have_color == MagickFalse &&
8623 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008624 {
cristy35ef8242010-06-03 16:24:13 +00008625 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008626
cristy3ed852e2009-09-05 21:47:34 +00008627 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008628 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008629
glennrp7c4c9e62011-03-21 20:23:32 +00008630 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008631 {
glennrp5af765f2010-03-30 11:12:18 +00008632 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008633
cristy3ed852e2009-09-05 21:47:34 +00008634 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008635 {
8636 if (logging != MagickFalse)
8637 {
8638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8639 " Scaling ping_trans_color (0)");
8640 }
8641 ping_trans_color.gray*=0x0101;
8642 }
cristy3ed852e2009-09-05 21:47:34 +00008643 }
glennrp0fe50b42010-11-16 03:52:51 +00008644
cristy3ed852e2009-09-05 21:47:34 +00008645 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8646 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008647
cristy85380932011-04-25 17:11:52 +00008648 if ((image_colors == 0) || ((ssize_t) (image_colors-1) > MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008649 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008650
cristy3ed852e2009-09-05 21:47:34 +00008651 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008652 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008653
cristy3ed852e2009-09-05 21:47:34 +00008654 else
8655 {
glennrp5af765f2010-03-30 11:12:18 +00008656 ping_bit_depth=8;
8657 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008658 {
8659 if(!mng_info->write_png_depth)
8660 {
glennrp5af765f2010-03-30 11:12:18 +00008661 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008662
cristy35ef8242010-06-03 16:24:13 +00008663 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008664 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008665 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008666 }
8667 }
glennrp2b013e42010-11-24 16:55:50 +00008668
glennrp0fe50b42010-11-16 03:52:51 +00008669 else if (ping_color_type ==
8670 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008671 mng_info->IsPalette)
8672 {
cristy3ed852e2009-09-05 21:47:34 +00008673 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008674
cristy3ed852e2009-09-05 21:47:34 +00008675 int
8676 depth_4_ok=MagickTrue,
8677 depth_2_ok=MagickTrue,
8678 depth_1_ok=MagickTrue;
8679
cristybb503372010-05-27 20:51:26 +00008680 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008681 {
8682 unsigned char
8683 intensity;
8684
8685 intensity=ScaleQuantumToChar(image->colormap[i].red);
8686
8687 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8688 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8689 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8690 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008691 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008692 depth_1_ok=MagickFalse;
8693 }
glennrp2b013e42010-11-24 16:55:50 +00008694
cristy3ed852e2009-09-05 21:47:34 +00008695 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008696 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008697
cristy3ed852e2009-09-05 21:47:34 +00008698 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008699 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008700
cristy3ed852e2009-09-05 21:47:34 +00008701 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008702 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008703 }
8704 }
glennrp2b013e42010-11-24 16:55:50 +00008705
glennrp5af765f2010-03-30 11:12:18 +00008706 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008707 }
glennrp0fe50b42010-11-16 03:52:51 +00008708
cristy3ed852e2009-09-05 21:47:34 +00008709 else
glennrp0fe50b42010-11-16 03:52:51 +00008710
cristy3ed852e2009-09-05 21:47:34 +00008711 if (mng_info->IsPalette)
8712 {
glennrp17a14852010-05-10 03:01:59 +00008713 number_colors=image_colors;
8714
cristy3ed852e2009-09-05 21:47:34 +00008715 if (image_depth <= 8)
8716 {
cristy3ed852e2009-09-05 21:47:34 +00008717 /*
8718 Set image palette.
8719 */
glennrp5af765f2010-03-30 11:12:18 +00008720 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008721
glennrp58e01762011-01-07 15:28:54 +00008722 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008723 {
glennrp9c1eb072010-06-06 22:19:15 +00008724 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008725
glennrp3b51f0e2010-11-27 18:14:08 +00008726 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8728 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008729 }
glennrp0fe50b42010-11-16 03:52:51 +00008730
cristy3ed852e2009-09-05 21:47:34 +00008731 else
8732 {
cristybb503372010-05-27 20:51:26 +00008733 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008734 {
8735 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8736 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8737 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8738 }
glennrp0fe50b42010-11-16 03:52:51 +00008739
glennrp3b51f0e2010-11-27 18:14:08 +00008740 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008742 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008743 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008744
glennrp39992b42010-11-14 00:03:43 +00008745 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008746 }
glennrp0fe50b42010-11-16 03:52:51 +00008747
cristy3ed852e2009-09-05 21:47:34 +00008748 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008749 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008750 {
cristybefe4d22010-06-07 01:18:58 +00008751 size_t
8752 one;
8753
glennrp5af765f2010-03-30 11:12:18 +00008754 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008755 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008756
cristybefe4d22010-06-07 01:18:58 +00008757 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008758 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008759 }
glennrp0fe50b42010-11-16 03:52:51 +00008760
glennrp5af765f2010-03-30 11:12:18 +00008761 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008762
glennrp58e01762011-01-07 15:28:54 +00008763 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008764 {
glennrp0fe50b42010-11-16 03:52:51 +00008765 /*
glennrpd6bf1612010-12-17 17:28:54 +00008766 * Set up trans_colors array.
8767 */
glennrp0fe50b42010-11-16 03:52:51 +00008768 assert(number_colors <= 256);
8769
glennrpd6bf1612010-12-17 17:28:54 +00008770 ping_num_trans=(unsigned short) (number_transparent +
8771 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008772
8773 if (ping_num_trans == 0)
8774 ping_have_tRNS=MagickFalse;
8775
glennrpd6bf1612010-12-17 17:28:54 +00008776 else
glennrp0fe50b42010-11-16 03:52:51 +00008777 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008778 if (logging != MagickFalse)
8779 {
8780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8781 " Scaling ping_trans_color (1)");
8782 }
glennrpd6bf1612010-12-17 17:28:54 +00008783 ping_have_tRNS=MagickTrue;
8784
8785 for (i=0; i < ping_num_trans; i++)
8786 {
8787 ping_trans_alpha[i]= (png_byte) (255-
8788 ScaleQuantumToChar(image->colormap[i].opacity));
8789 }
glennrp0fe50b42010-11-16 03:52:51 +00008790 }
8791 }
cristy3ed852e2009-09-05 21:47:34 +00008792 }
8793 }
glennrp0fe50b42010-11-16 03:52:51 +00008794
cristy3ed852e2009-09-05 21:47:34 +00008795 else
8796 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008797
cristy3ed852e2009-09-05 21:47:34 +00008798 if (image_depth < 8)
8799 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008800
cristy3ed852e2009-09-05 21:47:34 +00008801 if ((save_image_depth == 16) && (image_depth == 8))
8802 {
glennrp4f25bd02011-01-01 18:51:28 +00008803 if (logging != MagickFalse)
8804 {
8805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8806 " Scaling ping_trans_color from (%d,%d,%d)",
8807 (int) ping_trans_color.red,
8808 (int) ping_trans_color.green,
8809 (int) ping_trans_color.blue);
8810 }
8811
glennrp5af765f2010-03-30 11:12:18 +00008812 ping_trans_color.red*=0x0101;
8813 ping_trans_color.green*=0x0101;
8814 ping_trans_color.blue*=0x0101;
8815 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008816
8817 if (logging != MagickFalse)
8818 {
8819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8820 " to (%d,%d,%d)",
8821 (int) ping_trans_color.red,
8822 (int) ping_trans_color.green,
8823 (int) ping_trans_color.blue);
8824 }
cristy3ed852e2009-09-05 21:47:34 +00008825 }
8826 }
8827
cristy4383ec82011-01-05 15:42:32 +00008828 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8829 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008830
cristy3ed852e2009-09-05 21:47:34 +00008831 /*
8832 Adjust background and transparency samples in sub-8-bit grayscale files.
8833 */
glennrp5af765f2010-03-30 11:12:18 +00008834 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008835 PNG_COLOR_TYPE_GRAY)
8836 {
8837 png_uint_16
8838 maxval;
8839
cristy35ef8242010-06-03 16:24:13 +00008840 size_t
8841 one=1;
8842
cristy22ffd972010-06-03 16:51:47 +00008843 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008844
glennrp4f25bd02011-01-01 18:51:28 +00008845 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008846 {
cristy3ed852e2009-09-05 21:47:34 +00008847
glennrpa521b2f2010-10-29 04:11:03 +00008848 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008849 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8850
8851 if (logging != MagickFalse)
8852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008853 " Setting up bKGD chunk (2)");
glennrp3b51f0e2010-11-27 18:14:08 +00008854
glennrp991d11d2010-11-12 21:55:28 +00008855 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008856 }
cristy3ed852e2009-09-05 21:47:34 +00008857
glennrp5af765f2010-03-30 11:12:18 +00008858 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8859 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008860 }
glennrp17a14852010-05-10 03:01:59 +00008861
glennrp26f37912010-12-23 16:22:42 +00008862 if (ping_exclude_bKGD == MagickFalse)
8863 {
glennrp1273f7b2011-02-24 03:20:30 +00008864 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008865 {
8866 /*
8867 Identify which colormap entry is the background color.
8868 */
8869
glennrp17a14852010-05-10 03:01:59 +00008870 number_colors=image_colors;
8871
glennrpa521b2f2010-10-29 04:11:03 +00008872 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8873 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008874 break;
8875
8876 ping_background.index=(png_byte) i;
8877
glennrp3b51f0e2010-11-27 18:14:08 +00008878 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008879 {
8880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008881 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008882 }
glennrp0fe50b42010-11-16 03:52:51 +00008883
cristy13d07042010-11-21 20:56:18 +00008884 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008885 {
8886 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008887
8888 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008889 {
8890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8891 " background =(%d,%d,%d)",
8892 (int) ping_background.red,
8893 (int) ping_background.green,
8894 (int) ping_background.blue);
8895 }
8896 }
glennrpa521b2f2010-10-29 04:11:03 +00008897
glennrpd6bf1612010-12-17 17:28:54 +00008898 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008899 {
glennrp3b51f0e2010-11-27 18:14:08 +00008900 if (logging != MagickFalse)
8901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008903 ping_have_bKGD = MagickFalse;
8904 }
glennrp17a14852010-05-10 03:01:59 +00008905 }
glennrp26f37912010-12-23 16:22:42 +00008906 }
glennrp17a14852010-05-10 03:01:59 +00008907
cristy3ed852e2009-09-05 21:47:34 +00008908 if (logging != MagickFalse)
8909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00008910 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008911 /*
8912 Initialize compression level and filtering.
8913 */
8914 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008915 {
8916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8917 " Setting up deflate compression");
8918
8919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920 " Compression buffer size: 32768");
8921 }
8922
cristy3ed852e2009-09-05 21:47:34 +00008923 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00008924
cristy3ed852e2009-09-05 21:47:34 +00008925 if (logging != MagickFalse)
8926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8927 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00008928
cristy3ed852e2009-09-05 21:47:34 +00008929 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00008930
cristy3ed852e2009-09-05 21:47:34 +00008931 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8932 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00008933
cristy3ed852e2009-09-05 21:47:34 +00008934 if (quality > 9)
8935 {
8936 int
8937 level;
8938
cristybb503372010-05-27 20:51:26 +00008939 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00008940
cristy3ed852e2009-09-05 21:47:34 +00008941 if (logging != MagickFalse)
8942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8943 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00008944
cristy3ed852e2009-09-05 21:47:34 +00008945 png_set_compression_level(ping,level);
8946 }
glennrp0fe50b42010-11-16 03:52:51 +00008947
cristy3ed852e2009-09-05 21:47:34 +00008948 else
8949 {
8950 if (logging != MagickFalse)
8951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8952 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00008953
cristy3ed852e2009-09-05 21:47:34 +00008954 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8955 }
glennrp0fe50b42010-11-16 03:52:51 +00008956
cristy3ed852e2009-09-05 21:47:34 +00008957 if (logging != MagickFalse)
8958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8959 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00008960
glennrp2b013e42010-11-24 16:55:50 +00008961#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00008962 /* This became available in libpng-1.0.9. Output must be a MNG. */
8963 if (mng_info->write_mng && ((quality % 10) == 7))
8964 {
8965 if (logging != MagickFalse)
8966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8967 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00008968
glennrp5af765f2010-03-30 11:12:18 +00008969 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00008970 }
glennrp0fe50b42010-11-16 03:52:51 +00008971
cristy3ed852e2009-09-05 21:47:34 +00008972 else
8973 if (logging != MagickFalse)
8974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8975 " Filter_type: 0");
8976#endif
glennrp2b013e42010-11-24 16:55:50 +00008977
cristy3ed852e2009-09-05 21:47:34 +00008978 {
8979 int
8980 base_filter;
8981
8982 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00008983 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00008984
glennrp26c990a2010-11-23 02:23:20 +00008985 else
glennrp8640fb52010-11-23 15:48:26 +00008986 if ((quality % 10) != 5)
8987 base_filter=(int) quality % 10;
8988
8989 else
8990 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8991 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8992 (quality < 50))
8993 base_filter=PNG_NO_FILTERS;
8994
8995 else
8996 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00008997
cristy3ed852e2009-09-05 21:47:34 +00008998 if (logging != MagickFalse)
8999 {
9000 if (base_filter == PNG_ALL_FILTERS)
9001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002 " Base filter method: ADAPTIVE");
9003 else
9004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9005 " Base filter method: NONE");
9006 }
glennrp2b013e42010-11-24 16:55:50 +00009007
cristy3ed852e2009-09-05 21:47:34 +00009008 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9009 }
9010
glennrp823b55c2011-03-14 18:46:46 +00009011 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9012 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009013 {
9014 ResetImageProfileIterator(image);
9015 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009016 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009017 profile=GetImageProfile(image,name);
9018
9019 if (profile != (StringInfo *) NULL)
9020 {
glennrp5af765f2010-03-30 11:12:18 +00009021#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009022 if ((LocaleCompare(name,"ICC") == 0) ||
9023 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009024 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009025
9026 if (ping_exclude_iCCP == MagickFalse)
9027 {
9028 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009029#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009030 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009031#else
9032 (png_const_bytep) GetStringInfoDatum(profile),
9033#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009034 (png_uint_32) GetStringInfoLength(profile));
9035 }
glennrp26f37912010-12-23 16:22:42 +00009036 }
glennrp0fe50b42010-11-16 03:52:51 +00009037
glennrpc8cbc5d2011-01-01 00:12:34 +00009038 else
cristy3ed852e2009-09-05 21:47:34 +00009039#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009040 if (ping_exclude_zCCP == MagickFalse)
9041 {
glennrpcf002022011-01-30 02:38:15 +00009042 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009043 (unsigned char *) name,(unsigned char *) name,
9044 GetStringInfoDatum(profile),
9045 (png_uint_32) GetStringInfoLength(profile));
9046 }
9047 }
glennrp0b206f52011-01-07 04:55:32 +00009048
glennrpc8cbc5d2011-01-01 00:12:34 +00009049 if (logging != MagickFalse)
9050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9051 " Setting up text chunk with %s profile",name);
9052
9053 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009054 }
cristy3ed852e2009-09-05 21:47:34 +00009055 }
9056
9057#if defined(PNG_WRITE_sRGB_SUPPORTED)
9058 if ((mng_info->have_write_global_srgb == 0) &&
9059 ((image->rendering_intent != UndefinedIntent) ||
9060 (image->colorspace == sRGBColorspace)))
9061 {
glennrp26f37912010-12-23 16:22:42 +00009062 if (ping_exclude_sRGB == MagickFalse)
9063 {
9064 /*
9065 Note image rendering intent.
9066 */
9067 if (logging != MagickFalse)
9068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9069 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009070
glennrp26f37912010-12-23 16:22:42 +00009071 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009072 Magick_RenderingIntent_to_PNG_RenderingIntent(
9073 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009074
glennrp26f37912010-12-23 16:22:42 +00009075 if (ping_exclude_gAMA == MagickFalse)
9076 png_set_gAMA(ping,ping_info,0.45455);
9077 }
cristy3ed852e2009-09-05 21:47:34 +00009078 }
glennrp26f37912010-12-23 16:22:42 +00009079
glennrp5af765f2010-03-30 11:12:18 +00009080 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009081#endif
9082 {
glennrp2cc891a2010-12-24 13:44:32 +00009083 if (ping_exclude_gAMA == MagickFalse &&
9084 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009085 (image->gamma < .45 || image->gamma > .46)))
9086 {
cristy3ed852e2009-09-05 21:47:34 +00009087 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9088 {
9089 /*
9090 Note image gamma.
9091 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9092 */
9093 if (logging != MagickFalse)
9094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9095 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009096
cristy3ed852e2009-09-05 21:47:34 +00009097 png_set_gAMA(ping,ping_info,image->gamma);
9098 }
glennrp26f37912010-12-23 16:22:42 +00009099 }
glennrp2b013e42010-11-24 16:55:50 +00009100
glennrp26f37912010-12-23 16:22:42 +00009101 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009102 {
glennrp26f37912010-12-23 16:22:42 +00009103 if ((mng_info->have_write_global_chrm == 0) &&
9104 (image->chromaticity.red_primary.x != 0.0))
9105 {
9106 /*
9107 Note image chromaticity.
9108 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9109 */
9110 PrimaryInfo
9111 bp,
9112 gp,
9113 rp,
9114 wp;
cristy3ed852e2009-09-05 21:47:34 +00009115
glennrp26f37912010-12-23 16:22:42 +00009116 wp=image->chromaticity.white_point;
9117 rp=image->chromaticity.red_primary;
9118 gp=image->chromaticity.green_primary;
9119 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009120
glennrp26f37912010-12-23 16:22:42 +00009121 if (logging != MagickFalse)
9122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9123 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009124
glennrp26f37912010-12-23 16:22:42 +00009125 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9126 bp.x,bp.y);
9127 }
9128 }
cristy3ed852e2009-09-05 21:47:34 +00009129 }
glennrpdfd70802010-11-14 01:23:35 +00009130
glennrp5af765f2010-03-30 11:12:18 +00009131 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009132
9133 if (mng_info->write_mng)
9134 png_set_sig_bytes(ping,8);
9135
9136 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9137
glennrpd6bf1612010-12-17 17:28:54 +00009138 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009139 {
9140 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009141 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009142 {
glennrp5af765f2010-03-30 11:12:18 +00009143 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009144
glennrp5af765f2010-03-30 11:12:18 +00009145 if (ping_bit_depth < 8)
9146 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009147 }
glennrp0fe50b42010-11-16 03:52:51 +00009148
cristy3ed852e2009-09-05 21:47:34 +00009149 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009150 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009151 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009152 }
9153
glennrp0e8ea192010-12-24 18:00:33 +00009154 if (ping_need_colortype_warning != MagickFalse ||
9155 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009156 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009157 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009158 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009159 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009160 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009161 {
9162 if (logging != MagickFalse)
9163 {
glennrp0e8ea192010-12-24 18:00:33 +00009164 if (ping_need_colortype_warning != MagickFalse)
9165 {
9166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9167 " Image has transparency but tRNS chunk was excluded");
9168 }
9169
cristy3ed852e2009-09-05 21:47:34 +00009170 if (mng_info->write_png_depth)
9171 {
9172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9173 " Defined PNG:bit-depth=%u, Computed depth=%u",
9174 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009175 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009176 }
glennrp0e8ea192010-12-24 18:00:33 +00009177
cristy3ed852e2009-09-05 21:47:34 +00009178 if (mng_info->write_png_colortype)
9179 {
9180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9181 " Defined PNG:color-type=%u, Computed color type=%u",
9182 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009183 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009184 }
9185 }
glennrp0e8ea192010-12-24 18:00:33 +00009186
glennrp3bd2e412010-08-10 13:34:52 +00009187 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009188 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9189 }
9190
glennrp58e01762011-01-07 15:28:54 +00009191 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009192 {
9193 /* Add an opaque matte channel */
9194 image->matte = MagickTrue;
9195 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009196
glennrpb4a13412010-05-05 12:47:19 +00009197 if (logging != MagickFalse)
9198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9199 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009200 }
9201
glennrp0e319732011-01-25 21:53:13 +00009202 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009203 {
glennrp991d11d2010-11-12 21:55:28 +00009204 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009205 {
glennrp991d11d2010-11-12 21:55:28 +00009206 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009207 if (logging != MagickFalse)
9208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9209 " Setting ping_have_tRNS=MagickTrue.");
9210 }
glennrpe9c26dc2010-05-30 01:56:35 +00009211 }
9212
cristy3ed852e2009-09-05 21:47:34 +00009213 if (logging != MagickFalse)
9214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9215 " Writing PNG header chunks");
9216
glennrp5af765f2010-03-30 11:12:18 +00009217 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9218 ping_bit_depth,ping_color_type,
9219 ping_interlace_method,ping_compression_method,
9220 ping_filter_method);
9221
glennrp39992b42010-11-14 00:03:43 +00009222 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9223 {
glennrpf09bded2011-01-08 01:15:59 +00009224 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009225
glennrp3b51f0e2010-11-27 18:14:08 +00009226 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009227 {
glennrp8640fb52010-11-23 15:48:26 +00009228 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009229 {
glennrpd6bf1612010-12-17 17:28:54 +00009230 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009232 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9233 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009234 (int) palette[i].red,
9235 (int) palette[i].green,
9236 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009237 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009238 (int) ping_trans_alpha[i]);
9239 else
9240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009241 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009242 (int) i,
9243 (int) palette[i].red,
9244 (int) palette[i].green,
9245 (int) palette[i].blue);
9246 }
glennrp39992b42010-11-14 00:03:43 +00009247 }
glennrp39992b42010-11-14 00:03:43 +00009248 }
9249
glennrp26f37912010-12-23 16:22:42 +00009250 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009251 {
glennrp26f37912010-12-23 16:22:42 +00009252 if (ping_have_bKGD != MagickFalse)
9253 png_set_bKGD(ping,ping_info,&ping_background);
9254 }
9255
9256 if (ping_exclude_pHYs == MagickFalse)
9257 {
9258 if (ping_have_pHYs != MagickFalse)
9259 {
9260 png_set_pHYs(ping,ping_info,
9261 ping_pHYs_x_resolution,
9262 ping_pHYs_y_resolution,
9263 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009264
9265 if (logging)
9266 {
9267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9268 " Setting up pHYs chunk");
9269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9270 " x_resolution=%lu",
9271 (unsigned long) ping_pHYs_x_resolution);
9272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9273 " y_resolution=%lu",
9274 (unsigned long) ping_pHYs_y_resolution);
9275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9276 " unit_type=%lu",
9277 (unsigned long) ping_pHYs_unit_type);
9278 }
glennrp26f37912010-12-23 16:22:42 +00009279 }
glennrpdfd70802010-11-14 01:23:35 +00009280 }
9281
9282#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009283 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009284 {
glennrp26f37912010-12-23 16:22:42 +00009285 if (image->page.x || image->page.y)
9286 {
9287 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9288 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009289
glennrp26f37912010-12-23 16:22:42 +00009290 if (logging != MagickFalse)
9291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9292 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9293 (int) image->page.x, (int) image->page.y);
9294 }
glennrpdfd70802010-11-14 01:23:35 +00009295 }
9296#endif
9297
glennrpda8f3a72011-02-27 23:54:12 +00009298 if (mng_info->need_blob != MagickFalse)
9299 {
9300 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9301 MagickFalse)
9302 png_error(ping,"WriteBlob Failed");
9303
9304 ping_have_blob=MagickTrue;
9305 }
9306
cristy3ed852e2009-09-05 21:47:34 +00009307 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009308
glennrp39992b42010-11-14 00:03:43 +00009309 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009310 {
glennrp3b51f0e2010-11-27 18:14:08 +00009311 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009312 {
9313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9314 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9315 }
9316
9317 if (ping_color_type == 3)
9318 (void) png_set_tRNS(ping, ping_info,
9319 ping_trans_alpha,
9320 ping_num_trans,
9321 NULL);
9322
9323 else
9324 {
9325 (void) png_set_tRNS(ping, ping_info,
9326 NULL,
9327 0,
9328 &ping_trans_color);
9329
glennrp3b51f0e2010-11-27 18:14:08 +00009330 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009331 {
9332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009333 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009334 (int) ping_trans_color.red,
9335 (int) ping_trans_color.green,
9336 (int) ping_trans_color.blue);
9337 }
9338 }
glennrp991d11d2010-11-12 21:55:28 +00009339 }
9340
cristy3ed852e2009-09-05 21:47:34 +00009341 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009342 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009343
cristy3ed852e2009-09-05 21:47:34 +00009344 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009345
cristy3ed852e2009-09-05 21:47:34 +00009346 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009347 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009348
glennrp26f37912010-12-23 16:22:42 +00009349 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009350 {
glennrp4f25bd02011-01-01 18:51:28 +00009351 if ((image->page.width != 0 && image->page.width != image->columns) ||
9352 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009353 {
9354 unsigned char
9355 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009356
glennrp26f37912010-12-23 16:22:42 +00009357 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9358 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009359 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009360 PNGLong(chunk+4,(png_uint_32) image->page.width);
9361 PNGLong(chunk+8,(png_uint_32) image->page.height);
9362 chunk[12]=0; /* unit = pixels */
9363 (void) WriteBlob(image,13,chunk);
9364 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9365 }
cristy3ed852e2009-09-05 21:47:34 +00009366 }
9367
9368#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009369 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009370#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009371 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009372#undef PNG_HAVE_IDAT
9373#endif
9374
9375 png_set_packing(ping);
9376 /*
9377 Allocate memory.
9378 */
9379 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009380 if (image_depth > 8)
9381 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009382 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009383 {
glennrpb4a13412010-05-05 12:47:19 +00009384 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009385 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009386 break;
glennrp0fe50b42010-11-16 03:52:51 +00009387
glennrpb4a13412010-05-05 12:47:19 +00009388 case PNG_COLOR_TYPE_GRAY_ALPHA:
9389 rowbytes*=2;
9390 break;
glennrp0fe50b42010-11-16 03:52:51 +00009391
glennrpb4a13412010-05-05 12:47:19 +00009392 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009393 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009394 break;
glennrp0fe50b42010-11-16 03:52:51 +00009395
glennrpb4a13412010-05-05 12:47:19 +00009396 default:
9397 break;
cristy3ed852e2009-09-05 21:47:34 +00009398 }
glennrp3b51f0e2010-11-27 18:14:08 +00009399
9400 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009401 {
9402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9403 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009404
glennrpb4a13412010-05-05 12:47:19 +00009405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009406 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009407 }
glennrpcf002022011-01-30 02:38:15 +00009408 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9409 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009410
glennrpcf002022011-01-30 02:38:15 +00009411 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009412 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009413
cristy3ed852e2009-09-05 21:47:34 +00009414 /*
9415 Initialize image scanlines.
9416 */
glennrp5af765f2010-03-30 11:12:18 +00009417 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009418 {
9419 /*
9420 PNG write failed.
9421 */
9422#ifdef PNG_DEBUG
9423 if (image_info->verbose)
9424 (void) printf("PNG write has failed.\n");
9425#endif
9426 png_destroy_write_struct(&ping,&ping_info);
9427 if (quantum_info != (QuantumInfo *) NULL)
9428 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009429 if (ping_pixels != (unsigned char *) NULL)
9430 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009431#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009432 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009433#endif
glennrpda8f3a72011-02-27 23:54:12 +00009434 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009435 (void) CloseBlob(image);
9436 image_info=DestroyImageInfo(image_info);
9437 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009438 return(MagickFalse);
9439 }
cristyed552522009-10-16 14:04:35 +00009440 quantum_info=AcquireQuantumInfo(image_info,image);
9441 if (quantum_info == (QuantumInfo *) NULL)
9442 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009443 quantum_info->format=UndefinedQuantumFormat;
9444 quantum_info->depth=image_depth;
9445 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009446
cristy3ed852e2009-09-05 21:47:34 +00009447 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009448 !mng_info->write_png32) &&
9449 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009450 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009451 image_matte == MagickFalse &&
9452 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009453 {
glennrp8bb3a022010-12-13 20:40:04 +00009454 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009455 register const PixelPacket
9456 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009457
cristy3ed852e2009-09-05 21:47:34 +00009458 quantum_info->depth=8;
9459 for (pass=0; pass < num_passes; pass++)
9460 {
9461 /*
9462 Convert PseudoClass image to a PNG monochrome image.
9463 */
cristybb503372010-05-27 20:51:26 +00009464 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009465 {
glennrpd71e86a2011-02-24 01:28:37 +00009466 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9468 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009469
cristy3ed852e2009-09-05 21:47:34 +00009470 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009471
cristy3ed852e2009-09-05 21:47:34 +00009472 if (p == (const PixelPacket *) NULL)
9473 break;
glennrp0fe50b42010-11-16 03:52:51 +00009474
cristy3ed852e2009-09-05 21:47:34 +00009475 if (mng_info->IsPalette)
9476 {
9477 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009478 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009479 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9480 mng_info->write_png_depth &&
9481 mng_info->write_png_depth != old_bit_depth)
9482 {
9483 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009484 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009485 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009486 >> (8-old_bit_depth));
9487 }
9488 }
glennrp0fe50b42010-11-16 03:52:51 +00009489
cristy3ed852e2009-09-05 21:47:34 +00009490 else
9491 {
9492 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009493 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009494 }
glennrp0fe50b42010-11-16 03:52:51 +00009495
cristy3ed852e2009-09-05 21:47:34 +00009496 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009497 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009498 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009499 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009500
glennrp3b51f0e2010-11-27 18:14:08 +00009501 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9503 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009504
glennrpcf002022011-01-30 02:38:15 +00009505 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009506 }
9507 if (image->previous == (Image *) NULL)
9508 {
9509 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9510 if (status == MagickFalse)
9511 break;
9512 }
9513 }
9514 }
glennrp0fe50b42010-11-16 03:52:51 +00009515
glennrp8bb3a022010-12-13 20:40:04 +00009516 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009517 {
glennrp0fe50b42010-11-16 03:52:51 +00009518 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009519 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009520 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009521 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009522 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009523 {
glennrp8bb3a022010-12-13 20:40:04 +00009524 register const PixelPacket
9525 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009526
glennrp8bb3a022010-12-13 20:40:04 +00009527 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009528 {
glennrp8bb3a022010-12-13 20:40:04 +00009529
cristybb503372010-05-27 20:51:26 +00009530 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009531 {
9532 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009533
cristy3ed852e2009-09-05 21:47:34 +00009534 if (p == (const PixelPacket *) NULL)
9535 break;
glennrp2cc891a2010-12-24 13:44:32 +00009536
glennrp5af765f2010-03-30 11:12:18 +00009537 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009538 {
glennrp8bb3a022010-12-13 20:40:04 +00009539 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009540 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009541 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009542
glennrp8bb3a022010-12-13 20:40:04 +00009543 else
9544 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009545 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009546
glennrp3b51f0e2010-11-27 18:14:08 +00009547 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009549 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009550 }
glennrp2cc891a2010-12-24 13:44:32 +00009551
glennrp8bb3a022010-12-13 20:40:04 +00009552 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9553 {
9554 if (logging != MagickFalse && y == 0)
9555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9556 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009557
glennrp8bb3a022010-12-13 20:40:04 +00009558 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009559 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009560 }
glennrp2cc891a2010-12-24 13:44:32 +00009561
glennrp3b51f0e2010-11-27 18:14:08 +00009562 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009564 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009565
glennrpcf002022011-01-30 02:38:15 +00009566 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009567 }
glennrp2cc891a2010-12-24 13:44:32 +00009568
glennrp8bb3a022010-12-13 20:40:04 +00009569 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009570 {
glennrp8bb3a022010-12-13 20:40:04 +00009571 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9572 if (status == MagickFalse)
9573 break;
cristy3ed852e2009-09-05 21:47:34 +00009574 }
cristy3ed852e2009-09-05 21:47:34 +00009575 }
9576 }
glennrp8bb3a022010-12-13 20:40:04 +00009577
9578 else
9579 {
9580 register const PixelPacket
9581 *p;
9582
9583 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009584 {
glennrp8bb3a022010-12-13 20:40:04 +00009585 if ((image_depth > 8) || (mng_info->write_png24 ||
9586 mng_info->write_png32 ||
9587 (!mng_info->write_png8 && !mng_info->IsPalette)))
9588 {
9589 for (y=0; y < (ssize_t) image->rows; y++)
9590 {
9591 p=GetVirtualPixels(image,0,y,image->columns,1,
9592 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009593
glennrp8bb3a022010-12-13 20:40:04 +00009594 if (p == (const PixelPacket *) NULL)
9595 break;
glennrp2cc891a2010-12-24 13:44:32 +00009596
glennrp8bb3a022010-12-13 20:40:04 +00009597 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9598 {
9599 if (image->storage_class == DirectClass)
9600 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009601 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009602
glennrp8bb3a022010-12-13 20:40:04 +00009603 else
9604 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009605 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009606 }
glennrp2cc891a2010-12-24 13:44:32 +00009607
glennrp8bb3a022010-12-13 20:40:04 +00009608 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9609 {
9610 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009611 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009612 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009613
glennrp8bb3a022010-12-13 20:40:04 +00009614 if (logging != MagickFalse && y == 0)
9615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9616 " Writing GRAY_ALPHA PNG pixels (3)");
9617 }
glennrp2cc891a2010-12-24 13:44:32 +00009618
glennrp8bb3a022010-12-13 20:40:04 +00009619 else if (image_matte != MagickFalse)
9620 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009621 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009622
glennrp8bb3a022010-12-13 20:40:04 +00009623 else
9624 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009625 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009626
glennrp8bb3a022010-12-13 20:40:04 +00009627 if (logging != MagickFalse && y == 0)
9628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9629 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009630
glennrpcf002022011-01-30 02:38:15 +00009631 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009632 }
9633 }
glennrp2cc891a2010-12-24 13:44:32 +00009634
glennrp8bb3a022010-12-13 20:40:04 +00009635 else
9636 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9637 mng_info->write_png32 ||
9638 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9639 {
9640 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9641 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9642 {
9643 if (logging != MagickFalse)
9644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9645 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009646
glennrp8bb3a022010-12-13 20:40:04 +00009647 quantum_info->depth=8;
9648 image_depth=8;
9649 }
glennrp2cc891a2010-12-24 13:44:32 +00009650
glennrp8bb3a022010-12-13 20:40:04 +00009651 for (y=0; y < (ssize_t) image->rows; y++)
9652 {
9653 if (logging != MagickFalse && y == 0)
9654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9655 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009656
glennrp770d1932011-03-06 22:11:17 +00009657 p=GetVirtualPixels(image,0,y,image->columns,1,
9658 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009659
glennrp8bb3a022010-12-13 20:40:04 +00009660 if (p == (const PixelPacket *) NULL)
9661 break;
glennrp2cc891a2010-12-24 13:44:32 +00009662
glennrp8bb3a022010-12-13 20:40:04 +00009663 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009664 {
glennrp4bf89732011-03-21 13:48:28 +00009665 quantum_info->depth=image->depth;
9666
glennrp44757ab2011-03-17 12:57:03 +00009667 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009668 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009669 }
glennrp2cc891a2010-12-24 13:44:32 +00009670
glennrp8bb3a022010-12-13 20:40:04 +00009671 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9672 {
9673 if (logging != MagickFalse && y == 0)
9674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9675 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009676
glennrp8bb3a022010-12-13 20:40:04 +00009677 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009678 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009679 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009680 }
glennrp2cc891a2010-12-24 13:44:32 +00009681
glennrp8bb3a022010-12-13 20:40:04 +00009682 else
glennrp8bb3a022010-12-13 20:40:04 +00009683 {
glennrp179d0752011-03-17 13:02:10 +00009684 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009685 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9686
9687 if (logging != MagickFalse && y <= 2)
9688 {
9689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009690 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009691
9692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9693 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9694 (int)ping_pixels[0],(int)ping_pixels[1]);
9695 }
glennrp8bb3a022010-12-13 20:40:04 +00009696 }
glennrpcf002022011-01-30 02:38:15 +00009697 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009698 }
9699 }
glennrp2cc891a2010-12-24 13:44:32 +00009700
glennrp8bb3a022010-12-13 20:40:04 +00009701 if (image->previous == (Image *) NULL)
9702 {
9703 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9704 if (status == MagickFalse)
9705 break;
9706 }
cristy3ed852e2009-09-05 21:47:34 +00009707 }
glennrp8bb3a022010-12-13 20:40:04 +00009708 }
9709 }
9710
cristyb32b90a2009-09-07 21:45:48 +00009711 if (quantum_info != (QuantumInfo *) NULL)
9712 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009713
9714 if (logging != MagickFalse)
9715 {
9716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009717 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009718
cristy3ed852e2009-09-05 21:47:34 +00009719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009720 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009721
cristy3ed852e2009-09-05 21:47:34 +00009722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009723 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009724
cristy3ed852e2009-09-05 21:47:34 +00009725 if (mng_info->write_png_depth)
9726 {
9727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9728 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9729 }
glennrp0fe50b42010-11-16 03:52:51 +00009730
cristy3ed852e2009-09-05 21:47:34 +00009731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009732 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009733
cristy3ed852e2009-09-05 21:47:34 +00009734 if (mng_info->write_png_colortype)
9735 {
9736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9737 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9738 }
glennrp0fe50b42010-11-16 03:52:51 +00009739
cristy3ed852e2009-09-05 21:47:34 +00009740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009741 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009742
cristy3ed852e2009-09-05 21:47:34 +00009743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009744 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009745 }
9746 /*
glennrpa0ed0092011-04-18 16:36:29 +00009747 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009748 */
glennrp823b55c2011-03-14 18:46:46 +00009749 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009750 {
glennrp26f37912010-12-23 16:22:42 +00009751 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009752 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009753 while (property != (const char *) NULL)
9754 {
9755 png_textp
9756 text;
glennrp2cc891a2010-12-24 13:44:32 +00009757
glennrp26f37912010-12-23 16:22:42 +00009758 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009759
9760 /* Don't write any "png:" properties; those are just for "identify" */
9761 if (LocaleNCompare(property,"png:",4) != 0 &&
9762
9763 /* Suppress density and units if we wrote a pHYs chunk */
9764 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009765 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009766 LocaleCompare(property,"units") != 0) &&
9767
9768 /* Suppress the IM-generated Date:create and Date:modify */
9769 (ping_exclude_date == MagickFalse ||
9770 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009771 {
glennrpc70af4a2011-03-07 00:08:23 +00009772 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009773 {
glennrpc70af4a2011-03-07 00:08:23 +00009774 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9775 text[0].key=(char *) property;
9776 text[0].text=(char *) value;
9777 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009778
glennrpc70af4a2011-03-07 00:08:23 +00009779 if (ping_exclude_tEXt != MagickFalse)
9780 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9781
9782 else if (ping_exclude_zTXt != MagickFalse)
9783 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9784
9785 else
glennrp26f37912010-12-23 16:22:42 +00009786 {
glennrpc70af4a2011-03-07 00:08:23 +00009787 text[0].compression=image_info->compression == NoCompression ||
9788 (image_info->compression == UndefinedCompression &&
9789 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9790 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009791 }
glennrp2cc891a2010-12-24 13:44:32 +00009792
glennrpc70af4a2011-03-07 00:08:23 +00009793 if (logging != MagickFalse)
9794 {
9795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9796 " Setting up text chunk");
9797
9798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9799 " keyword: %s",text[0].key);
9800 }
9801
9802 png_set_text(ping,ping_info,text,1);
9803 png_free(ping,text);
9804 }
glennrp26f37912010-12-23 16:22:42 +00009805 }
9806 property=GetNextImageProperty(image);
9807 }
cristy3ed852e2009-09-05 21:47:34 +00009808 }
9809
9810 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009811 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009812
9813 if (logging != MagickFalse)
9814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9815 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009816
cristy3ed852e2009-09-05 21:47:34 +00009817 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009818
cristy3ed852e2009-09-05 21:47:34 +00009819 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9820 {
9821 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009822 (ping_width != mng_info->page.width) ||
9823 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009824 {
9825 unsigned char
9826 chunk[32];
9827
9828 /*
9829 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9830 */
9831 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9832 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009833 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009834 chunk[4]=4;
9835 chunk[5]=0; /* frame name separator (no name) */
9836 chunk[6]=1; /* flag for changing delay, for next frame only */
9837 chunk[7]=0; /* flag for changing frame timeout */
9838 chunk[8]=1; /* flag for changing frame clipping for next frame */
9839 chunk[9]=0; /* flag for changing frame sync_id */
9840 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9841 chunk[14]=0; /* clipping boundaries delta type */
9842 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9843 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009844 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009845 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9846 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009847 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009848 (void) WriteBlob(image,31,chunk);
9849 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9850 mng_info->old_framing_mode=4;
9851 mng_info->framing_mode=1;
9852 }
glennrp0fe50b42010-11-16 03:52:51 +00009853
cristy3ed852e2009-09-05 21:47:34 +00009854 else
9855 mng_info->framing_mode=3;
9856 }
9857 if (mng_info->write_mng && !mng_info->need_fram &&
9858 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009859 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009860 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009861 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009862
cristy3ed852e2009-09-05 21:47:34 +00009863 /*
9864 Free PNG resources.
9865 */
glennrp5af765f2010-03-30 11:12:18 +00009866
cristy3ed852e2009-09-05 21:47:34 +00009867 png_destroy_write_struct(&ping,&ping_info);
9868
glennrpcf002022011-01-30 02:38:15 +00009869 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009870
9871#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009872 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009873#endif
9874
glennrpda8f3a72011-02-27 23:54:12 +00009875 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009876 (void) CloseBlob(image);
9877
9878 image_info=DestroyImageInfo(image_info);
9879 image=DestroyImage(image);
9880
9881 /* Store bit depth actually written */
9882 s[0]=(char) ping_bit_depth;
9883 s[1]='\0';
9884
9885 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9886
cristy3ed852e2009-09-05 21:47:34 +00009887 if (logging != MagickFalse)
9888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9889 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009890
cristy3ed852e2009-09-05 21:47:34 +00009891 return(MagickTrue);
9892/* End write one PNG image */
9893}
9894
9895/*
9896%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9897% %
9898% %
9899% %
9900% W r i t e P N G I m a g e %
9901% %
9902% %
9903% %
9904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9905%
9906% WritePNGImage() writes a Portable Network Graphics (PNG) or
9907% Multiple-image Network Graphics (MNG) image file.
9908%
9909% MNG support written by Glenn Randers-Pehrson, glennrp@image...
9910%
9911% The format of the WritePNGImage method is:
9912%
9913% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9914%
9915% A description of each parameter follows:
9916%
9917% o image_info: the image info.
9918%
9919% o image: The image.
9920%
9921% Returns MagickTrue on success, MagickFalse on failure.
9922%
9923% Communicating with the PNG encoder:
9924%
9925% While the datastream written is always in PNG format and normally would
9926% be given the "png" file extension, this method also writes the following
9927% pseudo-formats which are subsets of PNG:
9928%
glennrp5a39f372011-02-25 04:52:16 +00009929% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
9930% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +00009931% is present, the tRNS chunk must only have values 0 and 255
9932% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +00009933% transparent). If other values are present they will be
9934% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +00009935% colors are present, they will be quantized to the 4-4-4-1,
9936% 3-3-3-1, or 3-3-2-1 palette.
9937%
9938% If you want better quantization or dithering of the colors
9939% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +00009940% PNG encoder. The pixels contain 8-bit indices even if
9941% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +00009942% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +00009943% PNG grayscale type might be slightly more efficient. Please
9944% note that writing to the PNG8 format may result in loss
9945% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +00009946%
9947% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9948% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +00009949% one of the colors as transparent. The only loss incurred
9950% is reduction of sample depth to 8. If the image has more
9951% than one transparent color, has semitransparent pixels, or
9952% has an opaque pixel with the same RGB components as the
9953% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +00009954%
9955% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
9956% transparency is permitted, i.e., the alpha sample for
9957% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +00009958% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +00009959% The only loss in data is the reduction of the sample depth
9960% to 8.
cristy3ed852e2009-09-05 21:47:34 +00009961%
9962% o -define: For more precise control of the PNG output, you can use the
9963% Image options "png:bit-depth" and "png:color-type". These
9964% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +00009965% from the application programming interfaces. The options
9966% are case-independent and are converted to lowercase before
9967% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +00009968%
9969% png:color-type can be 0, 2, 3, 4, or 6.
9970%
9971% When png:color-type is 0 (Grayscale), png:bit-depth can
9972% be 1, 2, 4, 8, or 16.
9973%
9974% When png:color-type is 2 (RGB), png:bit-depth can
9975% be 8 or 16.
9976%
9977% When png:color-type is 3 (Indexed), png:bit-depth can
9978% be 1, 2, 4, or 8. This refers to the number of bits
9979% used to store the index. The color samples always have
9980% bit-depth 8 in indexed PNG files.
9981%
9982% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9983% png:bit-depth can be 8 or 16.
9984%
glennrp5a39f372011-02-25 04:52:16 +00009985% If the image cannot be written without loss with the requested bit-depth
9986% and color-type, a PNG file will not be written, and the encoder will
9987% return MagickFalse.
9988%
cristy3ed852e2009-09-05 21:47:34 +00009989% Since image encoders should not be responsible for the "heavy lifting",
9990% the user should make sure that ImageMagick has already reduced the
9991% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +00009992% transparency prior to attempting to write the image with depth, color,
9993% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +00009994%
glennrp97cefe22011-04-22 16:17:00 +00009995% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +00009996%
cristy3ed852e2009-09-05 21:47:34 +00009997% Note that another definition, "png:bit-depth-written" exists, but it
9998% is not intended for external use. It is only used internally by the
9999% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10000%
10001% It is possible to request that the PNG encoder write previously-formatted
10002% ancillary chunks in the output PNG file, using the "-profile" commandline
10003% option as shown below or by setting the profile via a programming
10004% interface:
10005%
10006% -profile PNG-chunk-x:<file>
10007%
10008% where x is a location flag and <file> is a file containing the chunk
10009% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010010% This encoder will compute the chunk length and CRC, so those must not
10011% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010012%
10013% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10014% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10015% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010016% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010017%
glennrpbb8a7332010-11-13 15:17:35 +000010018% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010019%
glennrp3241bd02010-12-12 04:36:28 +000010020% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010021%
glennrpd6afd542010-11-19 01:53:05 +000010022% o 32-bit depth is reduced to 16.
10023% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10024% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010025% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010026% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010027% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010028% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10029% this can be done without loss and a larger bit depth N was not
10030% requested via the "-define PNG:bit-depth=N" option.
10031% o If matte channel is present but only one transparent color is
10032% present, RGB+tRNS is written instead of RGBA
10033% o Opaque matte channel is removed (or added, if color-type 4 or 6
10034% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010035%
cristy3ed852e2009-09-05 21:47:34 +000010036%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10037*/
10038static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10039 Image *image)
10040{
10041 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010042 excluding,
10043 logging,
10044 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010045 status;
10046
10047 MngInfo
10048 *mng_info;
10049
10050 const char
10051 *value;
10052
10053 int
glennrp21f0e622011-01-07 16:20:57 +000010054 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010055 source;
10056
cristy3ed852e2009-09-05 21:47:34 +000010057 /*
10058 Open image file.
10059 */
10060 assert(image_info != (const ImageInfo *) NULL);
10061 assert(image_info->signature == MagickSignature);
10062 assert(image != (Image *) NULL);
10063 assert(image->signature == MagickSignature);
10064 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010065 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010066 /*
10067 Allocate a MngInfo structure.
10068 */
10069 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010070 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010071
cristy3ed852e2009-09-05 21:47:34 +000010072 if (mng_info == (MngInfo *) NULL)
10073 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010074
cristy3ed852e2009-09-05 21:47:34 +000010075 /*
10076 Initialize members of the MngInfo structure.
10077 */
10078 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10079 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010080 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010081 have_mng_structure=MagickTrue;
10082
10083 /* See if user has requested a specific PNG subformat */
10084
10085 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10086 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10087 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10088
10089 if (mng_info->write_png8)
10090 {
glennrp9c1eb072010-06-06 22:19:15 +000010091 mng_info->write_png_colortype = /* 3 */ 4;
10092 mng_info->write_png_depth = 8;
10093 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010094 }
10095
10096 if (mng_info->write_png24)
10097 {
glennrp9c1eb072010-06-06 22:19:15 +000010098 mng_info->write_png_colortype = /* 2 */ 3;
10099 mng_info->write_png_depth = 8;
10100 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010101
glennrp9c1eb072010-06-06 22:19:15 +000010102 if (image->matte == MagickTrue)
10103 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010104
glennrp9c1eb072010-06-06 22:19:15 +000010105 else
10106 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010107
glennrp9c1eb072010-06-06 22:19:15 +000010108 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010109 }
10110
10111 if (mng_info->write_png32)
10112 {
glennrp9c1eb072010-06-06 22:19:15 +000010113 mng_info->write_png_colortype = /* 6 */ 7;
10114 mng_info->write_png_depth = 8;
10115 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010116
glennrp9c1eb072010-06-06 22:19:15 +000010117 if (image->matte == MagickTrue)
10118 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010119
glennrp9c1eb072010-06-06 22:19:15 +000010120 else
10121 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010122
glennrp9c1eb072010-06-06 22:19:15 +000010123 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010124 }
10125
10126 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010127
cristy3ed852e2009-09-05 21:47:34 +000010128 if (value != (char *) NULL)
10129 {
10130 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010131 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010132
cristy3ed852e2009-09-05 21:47:34 +000010133 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010134 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010135
cristy3ed852e2009-09-05 21:47:34 +000010136 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010137 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010138
cristy3ed852e2009-09-05 21:47:34 +000010139 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010140 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010141
cristy3ed852e2009-09-05 21:47:34 +000010142 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010143 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010144
glennrpbb8a7332010-11-13 15:17:35 +000010145 else
10146 (void) ThrowMagickException(&image->exception,
10147 GetMagickModule(),CoderWarning,
10148 "ignoring invalid defined png:bit-depth",
10149 "=%s",value);
10150
cristy3ed852e2009-09-05 21:47:34 +000010151 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010153 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010154 }
glennrp0fe50b42010-11-16 03:52:51 +000010155
cristy3ed852e2009-09-05 21:47:34 +000010156 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010157
cristy3ed852e2009-09-05 21:47:34 +000010158 if (value != (char *) NULL)
10159 {
10160 /* We must store colortype+1 because 0 is a valid colortype */
10161 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010162 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010163
cristy3ed852e2009-09-05 21:47:34 +000010164 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010165 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010166
cristy3ed852e2009-09-05 21:47:34 +000010167 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010168 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010169
cristy3ed852e2009-09-05 21:47:34 +000010170 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010171 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010172
cristy3ed852e2009-09-05 21:47:34 +000010173 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010174 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010175
glennrpbb8a7332010-11-13 15:17:35 +000010176 else
10177 (void) ThrowMagickException(&image->exception,
10178 GetMagickModule(),CoderWarning,
10179 "ignoring invalid defined png:color-type",
10180 "=%s",value);
10181
cristy3ed852e2009-09-05 21:47:34 +000010182 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010184 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010185 }
10186
glennrp0e8ea192010-12-24 18:00:33 +000010187 /* Check for chunks to be excluded:
10188 *
glennrp0dff56c2011-01-29 19:10:02 +000010189 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010190 * listed in the "unused_chunks" array, above.
10191 *
10192 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10193 * define (in the image properties or in the image artifacts)
10194 * or via a mng_info member. For convenience, in addition
10195 * to or instead of a comma-separated list of chunks, the
10196 * "exclude-chunk" string can be simply "all" or "none".
10197 *
10198 * The exclude-chunk define takes priority over the mng_info.
10199 *
10200 * A "PNG:include-chunk" define takes priority over both the
10201 * mng_info and the "PNG:exclude-chunk" define. Like the
10202 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010203 * well as a comma-separated list. Chunks that are unknown to
10204 * ImageMagick are always excluded, regardless of their "copy-safe"
10205 * status according to the PNG specification, and even if they
10206 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010207 *
10208 * Finally, all chunks listed in the "unused_chunks" array are
10209 * automatically excluded, regardless of the other instructions
10210 * or lack thereof.
10211 *
10212 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10213 * will not be written and the gAMA chunk will only be written if it
10214 * is not between .45 and .46, or approximately (1.0/2.2).
10215 *
10216 * If you exclude tRNS and the image has transparency, the colortype
10217 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10218 *
10219 * The -strip option causes StripImage() to set the png:include-chunk
10220 * artifact to "none,gama".
10221 */
10222
glennrp26f37912010-12-23 16:22:42 +000010223 mng_info->ping_exclude_bKGD=MagickFalse;
10224 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010225 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010226 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10227 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010228 mng_info->ping_exclude_iCCP=MagickFalse;
10229 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10230 mng_info->ping_exclude_oFFs=MagickFalse;
10231 mng_info->ping_exclude_pHYs=MagickFalse;
10232 mng_info->ping_exclude_sRGB=MagickFalse;
10233 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010234 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010235 mng_info->ping_exclude_vpAg=MagickFalse;
10236 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10237 mng_info->ping_exclude_zTXt=MagickFalse;
10238
glennrp8d3d6e52011-04-19 04:39:51 +000010239 mng_info->ping_preserve_colormap=MagickFalse;
10240
10241 value=GetImageArtifact(image,"png:preserve-colormap");
10242 if (value == NULL)
10243 value=GetImageOption(image_info,"png:preserve-colormap");
10244 if (value != NULL)
10245 mng_info->ping_preserve_colormap=MagickTrue;
10246
glennrp03812ae2010-12-24 01:31:34 +000010247 excluding=MagickFalse;
10248
glennrp5c7cf4e2010-12-24 00:30:00 +000010249 for (source=0; source<1; source++)
10250 {
10251 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010252 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010253 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010254
10255 if (value == NULL)
10256 value=GetImageArtifact(image,"png:exclude-chunks");
10257 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010258 else
glennrpacba0042010-12-24 14:27:26 +000010259 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010260 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010261
glennrpacba0042010-12-24 14:27:26 +000010262 if (value == NULL)
10263 value=GetImageOption(image_info,"png:exclude-chunks");
10264 }
10265
glennrp03812ae2010-12-24 01:31:34 +000010266 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010267 {
glennrp03812ae2010-12-24 01:31:34 +000010268
10269 size_t
10270 last;
10271
10272 excluding=MagickTrue;
10273
10274 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010275 {
10276 if (source == 0)
10277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10278 " png:exclude-chunk=%s found in image artifacts.\n", value);
10279 else
10280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10281 " png:exclude-chunk=%s found in image properties.\n", value);
10282 }
glennrp03812ae2010-12-24 01:31:34 +000010283
10284 last=strlen(value);
10285
10286 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010287 {
glennrp03812ae2010-12-24 01:31:34 +000010288
10289 if (LocaleNCompare(value+i,"all",3) == 0)
10290 {
10291 mng_info->ping_exclude_bKGD=MagickTrue;
10292 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010293 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010294 mng_info->ping_exclude_EXIF=MagickTrue;
10295 mng_info->ping_exclude_gAMA=MagickTrue;
10296 mng_info->ping_exclude_iCCP=MagickTrue;
10297 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10298 mng_info->ping_exclude_oFFs=MagickTrue;
10299 mng_info->ping_exclude_pHYs=MagickTrue;
10300 mng_info->ping_exclude_sRGB=MagickTrue;
10301 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010302 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010303 mng_info->ping_exclude_vpAg=MagickTrue;
10304 mng_info->ping_exclude_zCCP=MagickTrue;
10305 mng_info->ping_exclude_zTXt=MagickTrue;
10306 i--;
10307 }
glennrp2cc891a2010-12-24 13:44:32 +000010308
glennrp03812ae2010-12-24 01:31:34 +000010309 if (LocaleNCompare(value+i,"none",4) == 0)
10310 {
10311 mng_info->ping_exclude_bKGD=MagickFalse;
10312 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010313 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010314 mng_info->ping_exclude_EXIF=MagickFalse;
10315 mng_info->ping_exclude_gAMA=MagickFalse;
10316 mng_info->ping_exclude_iCCP=MagickFalse;
10317 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10318 mng_info->ping_exclude_oFFs=MagickFalse;
10319 mng_info->ping_exclude_pHYs=MagickFalse;
10320 mng_info->ping_exclude_sRGB=MagickFalse;
10321 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010322 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010323 mng_info->ping_exclude_vpAg=MagickFalse;
10324 mng_info->ping_exclude_zCCP=MagickFalse;
10325 mng_info->ping_exclude_zTXt=MagickFalse;
10326 }
glennrp2cc891a2010-12-24 13:44:32 +000010327
glennrp03812ae2010-12-24 01:31:34 +000010328 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10329 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010330
glennrp03812ae2010-12-24 01:31:34 +000010331 if (LocaleNCompare(value+i,"chrm",4) == 0)
10332 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010333
glennrpa0ed0092011-04-18 16:36:29 +000010334 if (LocaleNCompare(value+i,"date",4) == 0)
10335 mng_info->ping_exclude_date=MagickTrue;
10336
glennrp03812ae2010-12-24 01:31:34 +000010337 if (LocaleNCompare(value+i,"exif",4) == 0)
10338 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010339
glennrp03812ae2010-12-24 01:31:34 +000010340 if (LocaleNCompare(value+i,"gama",4) == 0)
10341 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010342
glennrp03812ae2010-12-24 01:31:34 +000010343 if (LocaleNCompare(value+i,"iccp",4) == 0)
10344 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010345
glennrp03812ae2010-12-24 01:31:34 +000010346 /*
10347 if (LocaleNCompare(value+i,"itxt",4) == 0)
10348 mng_info->ping_exclude_iTXt=MagickTrue;
10349 */
glennrp2cc891a2010-12-24 13:44:32 +000010350
glennrp03812ae2010-12-24 01:31:34 +000010351 if (LocaleNCompare(value+i,"gama",4) == 0)
10352 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010353
glennrp03812ae2010-12-24 01:31:34 +000010354 if (LocaleNCompare(value+i,"offs",4) == 0)
10355 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010356
glennrp03812ae2010-12-24 01:31:34 +000010357 if (LocaleNCompare(value+i,"phys",4) == 0)
10358 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010359
glennrpa1e3b7b2010-12-24 16:37:33 +000010360 if (LocaleNCompare(value+i,"srgb",4) == 0)
10361 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010362
glennrp03812ae2010-12-24 01:31:34 +000010363 if (LocaleNCompare(value+i,"text",4) == 0)
10364 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010365
glennrpa1e3b7b2010-12-24 16:37:33 +000010366 if (LocaleNCompare(value+i,"trns",4) == 0)
10367 mng_info->ping_exclude_tRNS=MagickTrue;
10368
glennrp03812ae2010-12-24 01:31:34 +000010369 if (LocaleNCompare(value+i,"vpag",4) == 0)
10370 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010371
glennrp03812ae2010-12-24 01:31:34 +000010372 if (LocaleNCompare(value+i,"zccp",4) == 0)
10373 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010374
glennrp03812ae2010-12-24 01:31:34 +000010375 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10376 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010377
glennrp03812ae2010-12-24 01:31:34 +000010378 }
glennrpce91ed52010-12-23 22:37:49 +000010379 }
glennrp26f37912010-12-23 16:22:42 +000010380 }
10381
glennrp5c7cf4e2010-12-24 00:30:00 +000010382 for (source=0; source<1; source++)
10383 {
10384 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010385 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010386 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010387
10388 if (value == NULL)
10389 value=GetImageArtifact(image,"png:include-chunks");
10390 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010391 else
glennrpacba0042010-12-24 14:27:26 +000010392 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010393 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010394
glennrpacba0042010-12-24 14:27:26 +000010395 if (value == NULL)
10396 value=GetImageOption(image_info,"png:include-chunks");
10397 }
10398
glennrp03812ae2010-12-24 01:31:34 +000010399 if (value != NULL)
10400 {
10401 size_t
10402 last;
glennrp26f37912010-12-23 16:22:42 +000010403
glennrp03812ae2010-12-24 01:31:34 +000010404 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010405
glennrp03812ae2010-12-24 01:31:34 +000010406 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010407 {
10408 if (source == 0)
10409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10410 " png:include-chunk=%s found in image artifacts.\n", value);
10411 else
10412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10413 " png:include-chunk=%s found in image properties.\n", value);
10414 }
glennrp03812ae2010-12-24 01:31:34 +000010415
10416 last=strlen(value);
10417
10418 for (i=0; i<(int) last; i+=5)
10419 {
10420 if (LocaleNCompare(value+i,"all",3) == 0)
10421 {
10422 mng_info->ping_exclude_bKGD=MagickFalse;
10423 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010424 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010425 mng_info->ping_exclude_EXIF=MagickFalse;
10426 mng_info->ping_exclude_gAMA=MagickFalse;
10427 mng_info->ping_exclude_iCCP=MagickFalse;
10428 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10429 mng_info->ping_exclude_oFFs=MagickFalse;
10430 mng_info->ping_exclude_pHYs=MagickFalse;
10431 mng_info->ping_exclude_sRGB=MagickFalse;
10432 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010433 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010434 mng_info->ping_exclude_vpAg=MagickFalse;
10435 mng_info->ping_exclude_zCCP=MagickFalse;
10436 mng_info->ping_exclude_zTXt=MagickFalse;
10437 i--;
10438 }
glennrp2cc891a2010-12-24 13:44:32 +000010439
glennrp03812ae2010-12-24 01:31:34 +000010440 if (LocaleNCompare(value+i,"none",4) == 0)
10441 {
10442 mng_info->ping_exclude_bKGD=MagickTrue;
10443 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010444 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010445 mng_info->ping_exclude_EXIF=MagickTrue;
10446 mng_info->ping_exclude_gAMA=MagickTrue;
10447 mng_info->ping_exclude_iCCP=MagickTrue;
10448 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10449 mng_info->ping_exclude_oFFs=MagickTrue;
10450 mng_info->ping_exclude_pHYs=MagickTrue;
10451 mng_info->ping_exclude_sRGB=MagickTrue;
10452 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010453 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010454 mng_info->ping_exclude_vpAg=MagickTrue;
10455 mng_info->ping_exclude_zCCP=MagickTrue;
10456 mng_info->ping_exclude_zTXt=MagickTrue;
10457 }
glennrp2cc891a2010-12-24 13:44:32 +000010458
glennrp03812ae2010-12-24 01:31:34 +000010459 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10460 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010461
glennrp03812ae2010-12-24 01:31:34 +000010462 if (LocaleNCompare(value+i,"chrm",4) == 0)
10463 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010464
glennrpa0ed0092011-04-18 16:36:29 +000010465 if (LocaleNCompare(value+i,"date",4) == 0)
10466 mng_info->ping_exclude_date=MagickFalse;
10467
glennrp03812ae2010-12-24 01:31:34 +000010468 if (LocaleNCompare(value+i,"exif",4) == 0)
10469 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010470
glennrp03812ae2010-12-24 01:31:34 +000010471 if (LocaleNCompare(value+i,"gama",4) == 0)
10472 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010473
glennrp03812ae2010-12-24 01:31:34 +000010474 if (LocaleNCompare(value+i,"iccp",4) == 0)
10475 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010476
glennrp03812ae2010-12-24 01:31:34 +000010477 /*
10478 if (LocaleNCompare(value+i,"itxt",4) == 0)
10479 mng_info->ping_exclude_iTXt=MagickFalse;
10480 */
glennrp2cc891a2010-12-24 13:44:32 +000010481
glennrp03812ae2010-12-24 01:31:34 +000010482 if (LocaleNCompare(value+i,"gama",4) == 0)
10483 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010484
glennrp03812ae2010-12-24 01:31:34 +000010485 if (LocaleNCompare(value+i,"offs",4) == 0)
10486 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010487
glennrp03812ae2010-12-24 01:31:34 +000010488 if (LocaleNCompare(value+i,"phys",4) == 0)
10489 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010490
glennrpa1e3b7b2010-12-24 16:37:33 +000010491 if (LocaleNCompare(value+i,"srgb",4) == 0)
10492 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010493
glennrp03812ae2010-12-24 01:31:34 +000010494 if (LocaleNCompare(value+i,"text",4) == 0)
10495 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010496
glennrpa1e3b7b2010-12-24 16:37:33 +000010497 if (LocaleNCompare(value+i,"trns",4) == 0)
10498 mng_info->ping_exclude_tRNS=MagickFalse;
10499
glennrp03812ae2010-12-24 01:31:34 +000010500 if (LocaleNCompare(value+i,"vpag",4) == 0)
10501 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010502
glennrp03812ae2010-12-24 01:31:34 +000010503 if (LocaleNCompare(value+i,"zccp",4) == 0)
10504 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010505
glennrp03812ae2010-12-24 01:31:34 +000010506 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10507 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010508
glennrp03812ae2010-12-24 01:31:34 +000010509 }
glennrpce91ed52010-12-23 22:37:49 +000010510 }
glennrp26f37912010-12-23 16:22:42 +000010511 }
10512
glennrp03812ae2010-12-24 01:31:34 +000010513 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010514 {
10515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10516 " Chunks to be excluded from the output PNG:");
10517 if (mng_info->ping_exclude_bKGD != MagickFalse)
10518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10519 " bKGD");
10520 if (mng_info->ping_exclude_cHRM != MagickFalse)
10521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10522 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010523 if (mng_info->ping_exclude_date != MagickFalse)
10524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10525 " date");
glennrp26f37912010-12-23 16:22:42 +000010526 if (mng_info->ping_exclude_EXIF != MagickFalse)
10527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10528 " EXIF");
10529 if (mng_info->ping_exclude_gAMA != MagickFalse)
10530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10531 " gAMA");
10532 if (mng_info->ping_exclude_iCCP != MagickFalse)
10533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10534 " iCCP");
10535/*
10536 if (mng_info->ping_exclude_iTXt != MagickFalse)
10537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10538 " iTXt");
10539*/
10540 if (mng_info->ping_exclude_oFFs != MagickFalse)
10541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10542 " oFFs");
10543 if (mng_info->ping_exclude_pHYs != MagickFalse)
10544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10545 " pHYs");
10546 if (mng_info->ping_exclude_sRGB != MagickFalse)
10547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10548 " sRGB");
10549 if (mng_info->ping_exclude_tEXt != MagickFalse)
10550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010552 if (mng_info->ping_exclude_tRNS != MagickFalse)
10553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10554 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010555 if (mng_info->ping_exclude_vpAg != MagickFalse)
10556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10557 " vpAg");
10558 if (mng_info->ping_exclude_zCCP != MagickFalse)
10559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10560 " zCCP");
10561 if (mng_info->ping_exclude_zTXt != MagickFalse)
10562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10563 " zTXt");
10564 }
10565
glennrpb9cfe272010-12-21 15:08:06 +000010566 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010567
glennrpb9cfe272010-12-21 15:08:06 +000010568 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010569
10570 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010571
cristy3ed852e2009-09-05 21:47:34 +000010572 if (logging != MagickFalse)
10573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010574
cristy3ed852e2009-09-05 21:47:34 +000010575 return(status);
10576}
10577
10578#if defined(JNG_SUPPORTED)
10579
10580/* Write one JNG image */
10581static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10582 const ImageInfo *image_info,Image *image)
10583{
10584 Image
10585 *jpeg_image;
10586
10587 ImageInfo
10588 *jpeg_image_info;
10589
10590 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010591 logging,
cristy3ed852e2009-09-05 21:47:34 +000010592 status;
10593
10594 size_t
10595 length;
10596
10597 unsigned char
10598 *blob,
10599 chunk[80],
10600 *p;
10601
10602 unsigned int
10603 jng_alpha_compression_method,
10604 jng_alpha_sample_depth,
10605 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010606 transparent;
10607
cristybb503372010-05-27 20:51:26 +000010608 size_t
cristy3ed852e2009-09-05 21:47:34 +000010609 jng_quality;
10610
10611 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010612 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010613
10614 blob=(unsigned char *) NULL;
10615 jpeg_image=(Image *) NULL;
10616 jpeg_image_info=(ImageInfo *) NULL;
10617
10618 status=MagickTrue;
10619 transparent=image_info->type==GrayscaleMatteType ||
10620 image_info->type==TrueColorMatteType;
10621 jng_color_type=10;
10622 jng_alpha_sample_depth=0;
10623 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10624 jng_alpha_compression_method=0;
10625
10626 if (image->matte != MagickFalse)
10627 {
10628 /* if any pixels are transparent */
10629 transparent=MagickTrue;
10630 if (image_info->compression==JPEGCompression)
10631 jng_alpha_compression_method=8;
10632 }
10633
10634 if (transparent)
10635 {
10636 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010637
cristy3ed852e2009-09-05 21:47:34 +000010638 /* Create JPEG blob, image, and image_info */
10639 if (logging != MagickFalse)
10640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10641 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010642
cristy3ed852e2009-09-05 21:47:34 +000010643 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010644
cristy3ed852e2009-09-05 21:47:34 +000010645 if (jpeg_image_info == (ImageInfo *) NULL)
10646 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010647
cristy3ed852e2009-09-05 21:47:34 +000010648 if (logging != MagickFalse)
10649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10650 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010651
cristy3ed852e2009-09-05 21:47:34 +000010652 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010653
cristy3ed852e2009-09-05 21:47:34 +000010654 if (jpeg_image == (Image *) NULL)
10655 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010656
cristy3ed852e2009-09-05 21:47:34 +000010657 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10658 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10659 status=NegateImage(jpeg_image,MagickFalse);
10660 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010661
cristy3ed852e2009-09-05 21:47:34 +000010662 if (jng_quality >= 1000)
10663 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010664
cristy3ed852e2009-09-05 21:47:34 +000010665 else
10666 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010667
cristy3ed852e2009-09-05 21:47:34 +000010668 jpeg_image_info->type=GrayscaleType;
10669 (void) SetImageType(jpeg_image,GrayscaleType);
10670 (void) AcquireUniqueFilename(jpeg_image->filename);
10671 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10672 "%s",jpeg_image->filename);
10673 }
10674
10675 /* To do: check bit depth of PNG alpha channel */
10676
10677 /* Check if image is grayscale. */
10678 if (image_info->type != TrueColorMatteType && image_info->type !=
10679 TrueColorType && ImageIsGray(image))
10680 jng_color_type-=2;
10681
10682 if (transparent)
10683 {
10684 if (jng_alpha_compression_method==0)
10685 {
10686 const char
10687 *value;
10688
10689 /* Encode opacity as a grayscale PNG blob */
10690 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10691 &image->exception);
10692 if (logging != MagickFalse)
10693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10694 " Creating PNG blob.");
10695 length=0;
10696
10697 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10698 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10699 jpeg_image_info->interlace=NoInterlace;
10700
10701 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10702 &image->exception);
10703
10704 /* Retrieve sample depth used */
10705 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10706 if (value != (char *) NULL)
10707 jng_alpha_sample_depth= (unsigned int) value[0];
10708 }
10709 else
10710 {
10711 /* Encode opacity as a grayscale JPEG blob */
10712
10713 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10714 &image->exception);
10715
10716 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10717 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10718 jpeg_image_info->interlace=NoInterlace;
10719 if (logging != MagickFalse)
10720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10721 " Creating blob.");
10722 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010723 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010724 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010725
cristy3ed852e2009-09-05 21:47:34 +000010726 if (logging != MagickFalse)
10727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010728 " Successfully read jpeg_image into a blob, length=%.20g.",
10729 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010730
10731 }
10732 /* Destroy JPEG image and image_info */
10733 jpeg_image=DestroyImage(jpeg_image);
10734 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10735 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10736 }
10737
10738 /* Write JHDR chunk */
10739 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10740 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010741 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010742 PNGLong(chunk+4,(png_uint_32) image->columns);
10743 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010744 chunk[12]=jng_color_type;
10745 chunk[13]=8; /* sample depth */
10746 chunk[14]=8; /*jng_image_compression_method */
10747 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10748 chunk[16]=jng_alpha_sample_depth;
10749 chunk[17]=jng_alpha_compression_method;
10750 chunk[18]=0; /*jng_alpha_filter_method */
10751 chunk[19]=0; /*jng_alpha_interlace_method */
10752 (void) WriteBlob(image,20,chunk);
10753 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10754 if (logging != MagickFalse)
10755 {
10756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010757 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010758
cristy3ed852e2009-09-05 21:47:34 +000010759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010760 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010761
cristy3ed852e2009-09-05 21:47:34 +000010762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10763 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010764
cristy3ed852e2009-09-05 21:47:34 +000010765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10766 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010767
cristy3ed852e2009-09-05 21:47:34 +000010768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10769 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010770
cristy3ed852e2009-09-05 21:47:34 +000010771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10772 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010773
cristy3ed852e2009-09-05 21:47:34 +000010774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10775 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010776
cristy3ed852e2009-09-05 21:47:34 +000010777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10778 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010779
cristy3ed852e2009-09-05 21:47:34 +000010780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10781 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010782
cristy3ed852e2009-09-05 21:47:34 +000010783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10784 " JNG alpha interlace:%5d",0);
10785 }
10786
glennrp0fe50b42010-11-16 03:52:51 +000010787 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010788 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010789
10790 /*
10791 Write leading ancillary chunks
10792 */
10793
10794 if (transparent)
10795 {
10796 /*
10797 Write JNG bKGD chunk
10798 */
10799
10800 unsigned char
10801 blue,
10802 green,
10803 red;
10804
cristybb503372010-05-27 20:51:26 +000010805 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010806 num_bytes;
10807
10808 if (jng_color_type == 8 || jng_color_type == 12)
10809 num_bytes=6L;
10810 else
10811 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010812 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010813 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010814 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010815 red=ScaleQuantumToChar(image->background_color.red);
10816 green=ScaleQuantumToChar(image->background_color.green);
10817 blue=ScaleQuantumToChar(image->background_color.blue);
10818 *(chunk+4)=0;
10819 *(chunk+5)=red;
10820 *(chunk+6)=0;
10821 *(chunk+7)=green;
10822 *(chunk+8)=0;
10823 *(chunk+9)=blue;
10824 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10825 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10826 }
10827
10828 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10829 {
10830 /*
10831 Write JNG sRGB chunk
10832 */
10833 (void) WriteBlobMSBULong(image,1L);
10834 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010835 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010836
cristy3ed852e2009-09-05 21:47:34 +000010837 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010838 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010839 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010840 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010841
cristy3ed852e2009-09-05 21:47:34 +000010842 else
glennrpe610a072010-08-05 17:08:46 +000010843 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010844 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010845 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010846
cristy3ed852e2009-09-05 21:47:34 +000010847 (void) WriteBlob(image,5,chunk);
10848 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10849 }
10850 else
10851 {
10852 if (image->gamma != 0.0)
10853 {
10854 /*
10855 Write JNG gAMA chunk
10856 */
10857 (void) WriteBlobMSBULong(image,4L);
10858 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010859 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010860 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010861 (void) WriteBlob(image,8,chunk);
10862 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10863 }
glennrp0fe50b42010-11-16 03:52:51 +000010864
cristy3ed852e2009-09-05 21:47:34 +000010865 if ((mng_info->equal_chrms == MagickFalse) &&
10866 (image->chromaticity.red_primary.x != 0.0))
10867 {
10868 PrimaryInfo
10869 primary;
10870
10871 /*
10872 Write JNG cHRM chunk
10873 */
10874 (void) WriteBlobMSBULong(image,32L);
10875 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010876 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010877 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010878 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10879 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010880 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010881 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10882 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010883 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010884 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10885 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010886 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010887 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10888 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010889 (void) WriteBlob(image,36,chunk);
10890 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10891 }
10892 }
glennrp0fe50b42010-11-16 03:52:51 +000010893
cristy3ed852e2009-09-05 21:47:34 +000010894 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10895 {
10896 /*
10897 Write JNG pHYs chunk
10898 */
10899 (void) WriteBlobMSBULong(image,9L);
10900 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000010901 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000010902 if (image->units == PixelsPerInchResolution)
10903 {
cristy35ef8242010-06-03 16:24:13 +000010904 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010905 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010906
cristy35ef8242010-06-03 16:24:13 +000010907 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010908 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010909
cristy3ed852e2009-09-05 21:47:34 +000010910 chunk[12]=1;
10911 }
glennrp0fe50b42010-11-16 03:52:51 +000010912
cristy3ed852e2009-09-05 21:47:34 +000010913 else
10914 {
10915 if (image->units == PixelsPerCentimeterResolution)
10916 {
cristy35ef8242010-06-03 16:24:13 +000010917 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010918 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010919
cristy35ef8242010-06-03 16:24:13 +000010920 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010921 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010922
cristy3ed852e2009-09-05 21:47:34 +000010923 chunk[12]=1;
10924 }
glennrp0fe50b42010-11-16 03:52:51 +000010925
cristy3ed852e2009-09-05 21:47:34 +000010926 else
10927 {
cristy35ef8242010-06-03 16:24:13 +000010928 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10929 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010930 chunk[12]=0;
10931 }
10932 }
10933 (void) WriteBlob(image,13,chunk);
10934 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10935 }
10936
10937 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10938 {
10939 /*
10940 Write JNG oFFs chunk
10941 */
10942 (void) WriteBlobMSBULong(image,9L);
10943 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000010944 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000010945 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10946 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000010947 chunk[12]=0;
10948 (void) WriteBlob(image,13,chunk);
10949 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10950 }
10951 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10952 {
10953 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10954 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010955 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000010956 PNGLong(chunk+4,(png_uint_32) image->page.width);
10957 PNGLong(chunk+8,(png_uint_32) image->page.height);
10958 chunk[12]=0; /* unit = pixels */
10959 (void) WriteBlob(image,13,chunk);
10960 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10961 }
10962
10963
10964 if (transparent)
10965 {
10966 if (jng_alpha_compression_method==0)
10967 {
cristybb503372010-05-27 20:51:26 +000010968 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010969 i;
10970
cristybb503372010-05-27 20:51:26 +000010971 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010972 len;
10973
10974 /* Write IDAT chunk header */
10975 if (logging != MagickFalse)
10976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010977 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000010978 length);
cristy3ed852e2009-09-05 21:47:34 +000010979
10980 /* Copy IDAT chunks */
10981 len=0;
10982 p=blob+8;
cristybb503372010-05-27 20:51:26 +000010983 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000010984 {
10985 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10986 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000010987
cristy3ed852e2009-09-05 21:47:34 +000010988 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10989 {
10990 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000010991 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000010992 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000010993 (void) WriteBlob(image,(size_t) len+4,p);
10994 (void) WriteBlobMSBULong(image,
10995 crc32(0,p,(uInt) len+4));
10996 }
glennrp0fe50b42010-11-16 03:52:51 +000010997
cristy3ed852e2009-09-05 21:47:34 +000010998 else
10999 {
11000 if (logging != MagickFalse)
11001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011002 " Skipping %c%c%c%c chunk, length=%.20g.",
11003 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011004 }
11005 p+=(8+len);
11006 }
11007 }
11008 else
11009 {
11010 /* Write JDAA chunk header */
11011 if (logging != MagickFalse)
11012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011013 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011014 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011015 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011016 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011017 /* Write JDAT chunk(s) data */
11018 (void) WriteBlob(image,4,chunk);
11019 (void) WriteBlob(image,length,blob);
11020 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11021 (uInt) length));
11022 }
11023 blob=(unsigned char *) RelinquishMagickMemory(blob);
11024 }
11025
11026 /* Encode image as a JPEG blob */
11027 if (logging != MagickFalse)
11028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11029 " Creating jpeg_image_info.");
11030 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11031 if (jpeg_image_info == (ImageInfo *) NULL)
11032 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11033
11034 if (logging != MagickFalse)
11035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11036 " Creating jpeg_image.");
11037
11038 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11039 if (jpeg_image == (Image *) NULL)
11040 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11041 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11042
11043 (void) AcquireUniqueFilename(jpeg_image->filename);
11044 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11045 jpeg_image->filename);
11046
11047 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11048 &image->exception);
11049
11050 if (logging != MagickFalse)
11051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011052 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11053 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011054
11055 if (jng_color_type == 8 || jng_color_type == 12)
11056 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011057
cristy3ed852e2009-09-05 21:47:34 +000011058 jpeg_image_info->quality=jng_quality % 1000;
11059 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11060 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011061
cristy3ed852e2009-09-05 21:47:34 +000011062 if (logging != MagickFalse)
11063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11064 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011065
cristy3ed852e2009-09-05 21:47:34 +000011066 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011067
cristy3ed852e2009-09-05 21:47:34 +000011068 if (logging != MagickFalse)
11069 {
11070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011071 " Successfully read jpeg_image into a blob, length=%.20g.",
11072 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011073
11074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011075 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011076 }
glennrp0fe50b42010-11-16 03:52:51 +000011077
cristy3ed852e2009-09-05 21:47:34 +000011078 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011079 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011080 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011081 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011082 (void) WriteBlob(image,4,chunk);
11083 (void) WriteBlob(image,length,blob);
11084 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11085
11086 jpeg_image=DestroyImage(jpeg_image);
11087 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11088 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11089 blob=(unsigned char *) RelinquishMagickMemory(blob);
11090
11091 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011092 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011093
11094 /* Write IEND chunk */
11095 (void) WriteBlobMSBULong(image,0L);
11096 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011097 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011098 (void) WriteBlob(image,4,chunk);
11099 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11100
11101 if (logging != MagickFalse)
11102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11103 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011104
cristy3ed852e2009-09-05 21:47:34 +000011105 return(status);
11106}
11107
11108
11109/*
11110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11111% %
11112% %
11113% %
11114% W r i t e J N G I m a g e %
11115% %
11116% %
11117% %
11118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11119%
11120% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11121%
11122% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11123%
11124% The format of the WriteJNGImage method is:
11125%
11126% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11127%
11128% A description of each parameter follows:
11129%
11130% o image_info: the image info.
11131%
11132% o image: The image.
11133%
11134%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11135*/
11136static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11137{
11138 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011139 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011140 logging,
cristy3ed852e2009-09-05 21:47:34 +000011141 status;
11142
11143 MngInfo
11144 *mng_info;
11145
cristy3ed852e2009-09-05 21:47:34 +000011146 /*
11147 Open image file.
11148 */
11149 assert(image_info != (const ImageInfo *) NULL);
11150 assert(image_info->signature == MagickSignature);
11151 assert(image != (Image *) NULL);
11152 assert(image->signature == MagickSignature);
11153 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011154 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011155 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11156 if (status == MagickFalse)
11157 return(status);
11158
11159 /*
11160 Allocate a MngInfo structure.
11161 */
11162 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011163 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011164 if (mng_info == (MngInfo *) NULL)
11165 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11166 /*
11167 Initialize members of the MngInfo structure.
11168 */
11169 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11170 mng_info->image=image;
11171 have_mng_structure=MagickTrue;
11172
11173 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11174
11175 status=WriteOneJNGImage(mng_info,image_info,image);
11176 (void) CloseBlob(image);
11177
11178 (void) CatchImageException(image);
11179 MngInfoFreeStruct(mng_info,&have_mng_structure);
11180 if (logging != MagickFalse)
11181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11182 return(status);
11183}
11184#endif
11185
11186
11187
11188static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11189{
11190 const char
11191 *option;
11192
11193 Image
11194 *next_image;
11195
11196 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011197 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011198 status;
11199
glennrp03812ae2010-12-24 01:31:34 +000011200 volatile MagickBooleanType
11201 logging;
11202
cristy3ed852e2009-09-05 21:47:34 +000011203 MngInfo
11204 *mng_info;
11205
11206 int
cristy3ed852e2009-09-05 21:47:34 +000011207 image_count,
11208 need_iterations,
11209 need_matte;
11210
11211 volatile int
11212#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11213 defined(PNG_MNG_FEATURES_SUPPORTED)
11214 need_local_plte,
11215#endif
11216 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011217 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011218 use_global_plte;
11219
cristybb503372010-05-27 20:51:26 +000011220 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011221 i;
11222
11223 unsigned char
11224 chunk[800];
11225
11226 volatile unsigned int
11227 write_jng,
11228 write_mng;
11229
cristybb503372010-05-27 20:51:26 +000011230 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011231 scene;
11232
cristybb503372010-05-27 20:51:26 +000011233 size_t
cristy3ed852e2009-09-05 21:47:34 +000011234 final_delay=0,
11235 initial_delay;
11236
glennrpd5045b42010-03-24 12:40:35 +000011237#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011238 if (image_info->verbose)
11239 printf("Your PNG library (libpng-%s) is rather old.\n",
11240 PNG_LIBPNG_VER_STRING);
11241#endif
11242
11243 /*
11244 Open image file.
11245 */
11246 assert(image_info != (const ImageInfo *) NULL);
11247 assert(image_info->signature == MagickSignature);
11248 assert(image != (Image *) NULL);
11249 assert(image->signature == MagickSignature);
11250 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011251 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011252 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11253 if (status == MagickFalse)
11254 return(status);
11255
11256 /*
11257 Allocate a MngInfo structure.
11258 */
11259 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011260 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011261 if (mng_info == (MngInfo *) NULL)
11262 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11263 /*
11264 Initialize members of the MngInfo structure.
11265 */
11266 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11267 mng_info->image=image;
11268 have_mng_structure=MagickTrue;
11269 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11270
11271 /*
11272 * See if user has requested a specific PNG subformat to be used
11273 * for all of the PNGs in the MNG being written, e.g.,
11274 *
11275 * convert *.png png8:animation.mng
11276 *
11277 * To do: check -define png:bit_depth and png:color_type as well,
11278 * or perhaps use mng:bit_depth and mng:color_type instead for
11279 * global settings.
11280 */
11281
11282 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11283 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11284 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11285
11286 write_jng=MagickFalse;
11287 if (image_info->compression == JPEGCompression)
11288 write_jng=MagickTrue;
11289
11290 mng_info->adjoin=image_info->adjoin &&
11291 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11292
cristy3ed852e2009-09-05 21:47:34 +000011293 if (logging != MagickFalse)
11294 {
11295 /* Log some info about the input */
11296 Image
11297 *p;
11298
11299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11300 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011301
cristy3ed852e2009-09-05 21:47:34 +000011302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011303 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011304
cristy3ed852e2009-09-05 21:47:34 +000011305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11306 " Type: %d",image_info->type);
11307
11308 scene=0;
11309 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11310 {
11311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011312 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011313
cristy3ed852e2009-09-05 21:47:34 +000011314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011315 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011316
cristy3ed852e2009-09-05 21:47:34 +000011317 if (p->matte)
11318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11319 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011320
cristy3ed852e2009-09-05 21:47:34 +000011321 else
11322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11323 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011324
cristy3ed852e2009-09-05 21:47:34 +000011325 if (p->storage_class == PseudoClass)
11326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11327 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011328
cristy3ed852e2009-09-05 21:47:34 +000011329 else
11330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11331 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011332
cristy3ed852e2009-09-05 21:47:34 +000011333 if (p->colors)
11334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011335 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011336
cristy3ed852e2009-09-05 21:47:34 +000011337 else
11338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11339 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011340
cristy3ed852e2009-09-05 21:47:34 +000011341 if (mng_info->adjoin == MagickFalse)
11342 break;
11343 }
11344 }
11345
cristy3ed852e2009-09-05 21:47:34 +000011346 use_global_plte=MagickFalse;
11347 all_images_are_gray=MagickFalse;
11348#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11349 need_local_plte=MagickTrue;
11350#endif
11351 need_defi=MagickFalse;
11352 need_matte=MagickFalse;
11353 mng_info->framing_mode=1;
11354 mng_info->old_framing_mode=1;
11355
11356 if (write_mng)
11357 if (image_info->page != (char *) NULL)
11358 {
11359 /*
11360 Determine image bounding box.
11361 */
11362 SetGeometry(image,&mng_info->page);
11363 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11364 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11365 }
11366 if (write_mng)
11367 {
11368 unsigned int
11369 need_geom;
11370
11371 unsigned short
11372 red,
11373 green,
11374 blue;
11375
11376 mng_info->page=image->page;
11377 need_geom=MagickTrue;
11378 if (mng_info->page.width || mng_info->page.height)
11379 need_geom=MagickFalse;
11380 /*
11381 Check all the scenes.
11382 */
11383 initial_delay=image->delay;
11384 need_iterations=MagickFalse;
11385 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11386 mng_info->equal_physs=MagickTrue,
11387 mng_info->equal_gammas=MagickTrue;
11388 mng_info->equal_srgbs=MagickTrue;
11389 mng_info->equal_backgrounds=MagickTrue;
11390 image_count=0;
11391#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11392 defined(PNG_MNG_FEATURES_SUPPORTED)
11393 all_images_are_gray=MagickTrue;
11394 mng_info->equal_palettes=MagickFalse;
11395 need_local_plte=MagickFalse;
11396#endif
11397 for (next_image=image; next_image != (Image *) NULL; )
11398 {
11399 if (need_geom)
11400 {
11401 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11402 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011403
cristy3ed852e2009-09-05 21:47:34 +000011404 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11405 mng_info->page.height=next_image->rows+next_image->page.y;
11406 }
glennrp0fe50b42010-11-16 03:52:51 +000011407
cristy3ed852e2009-09-05 21:47:34 +000011408 if (next_image->page.x || next_image->page.y)
11409 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011410
cristy3ed852e2009-09-05 21:47:34 +000011411 if (next_image->matte)
11412 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011413
cristy3ed852e2009-09-05 21:47:34 +000011414 if ((int) next_image->dispose >= BackgroundDispose)
11415 if (next_image->matte || next_image->page.x || next_image->page.y ||
11416 ((next_image->columns < mng_info->page.width) &&
11417 (next_image->rows < mng_info->page.height)))
11418 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011419
cristy3ed852e2009-09-05 21:47:34 +000011420 if (next_image->iterations)
11421 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011422
cristy3ed852e2009-09-05 21:47:34 +000011423 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011424
cristy3ed852e2009-09-05 21:47:34 +000011425 if (final_delay != initial_delay || final_delay > 1UL*
11426 next_image->ticks_per_second)
11427 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011428
cristy3ed852e2009-09-05 21:47:34 +000011429#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11430 defined(PNG_MNG_FEATURES_SUPPORTED)
11431 /*
11432 check for global palette possibility.
11433 */
11434 if (image->matte != MagickFalse)
11435 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011436
cristy3ed852e2009-09-05 21:47:34 +000011437 if (need_local_plte == 0)
11438 {
11439 if (ImageIsGray(image) == MagickFalse)
11440 all_images_are_gray=MagickFalse;
11441 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11442 if (use_global_plte == 0)
11443 use_global_plte=mng_info->equal_palettes;
11444 need_local_plte=!mng_info->equal_palettes;
11445 }
11446#endif
11447 if (GetNextImageInList(next_image) != (Image *) NULL)
11448 {
11449 if (next_image->background_color.red !=
11450 next_image->next->background_color.red ||
11451 next_image->background_color.green !=
11452 next_image->next->background_color.green ||
11453 next_image->background_color.blue !=
11454 next_image->next->background_color.blue)
11455 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011456
cristy3ed852e2009-09-05 21:47:34 +000011457 if (next_image->gamma != next_image->next->gamma)
11458 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011459
cristy3ed852e2009-09-05 21:47:34 +000011460 if (next_image->rendering_intent !=
11461 next_image->next->rendering_intent)
11462 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011463
cristy3ed852e2009-09-05 21:47:34 +000011464 if ((next_image->units != next_image->next->units) ||
11465 (next_image->x_resolution != next_image->next->x_resolution) ||
11466 (next_image->y_resolution != next_image->next->y_resolution))
11467 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011468
cristy3ed852e2009-09-05 21:47:34 +000011469 if (mng_info->equal_chrms)
11470 {
11471 if (next_image->chromaticity.red_primary.x !=
11472 next_image->next->chromaticity.red_primary.x ||
11473 next_image->chromaticity.red_primary.y !=
11474 next_image->next->chromaticity.red_primary.y ||
11475 next_image->chromaticity.green_primary.x !=
11476 next_image->next->chromaticity.green_primary.x ||
11477 next_image->chromaticity.green_primary.y !=
11478 next_image->next->chromaticity.green_primary.y ||
11479 next_image->chromaticity.blue_primary.x !=
11480 next_image->next->chromaticity.blue_primary.x ||
11481 next_image->chromaticity.blue_primary.y !=
11482 next_image->next->chromaticity.blue_primary.y ||
11483 next_image->chromaticity.white_point.x !=
11484 next_image->next->chromaticity.white_point.x ||
11485 next_image->chromaticity.white_point.y !=
11486 next_image->next->chromaticity.white_point.y)
11487 mng_info->equal_chrms=MagickFalse;
11488 }
11489 }
11490 image_count++;
11491 next_image=GetNextImageInList(next_image);
11492 }
11493 if (image_count < 2)
11494 {
11495 mng_info->equal_backgrounds=MagickFalse;
11496 mng_info->equal_chrms=MagickFalse;
11497 mng_info->equal_gammas=MagickFalse;
11498 mng_info->equal_srgbs=MagickFalse;
11499 mng_info->equal_physs=MagickFalse;
11500 use_global_plte=MagickFalse;
11501#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11502 need_local_plte=MagickTrue;
11503#endif
11504 need_iterations=MagickFalse;
11505 }
glennrp0fe50b42010-11-16 03:52:51 +000011506
cristy3ed852e2009-09-05 21:47:34 +000011507 if (mng_info->need_fram == MagickFalse)
11508 {
11509 /*
11510 Only certain framing rates 100/n are exactly representable without
11511 the FRAM chunk but we'll allow some slop in VLC files
11512 */
11513 if (final_delay == 0)
11514 {
11515 if (need_iterations != MagickFalse)
11516 {
11517 /*
11518 It's probably a GIF with loop; don't run it *too* fast.
11519 */
glennrp02617122010-07-28 13:07:35 +000011520 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011521 {
11522 final_delay=10;
11523 (void) ThrowMagickException(&image->exception,
11524 GetMagickModule(),CoderWarning,
11525 "input has zero delay between all frames; assuming",
11526 " 10 cs `%s'","");
11527 }
cristy3ed852e2009-09-05 21:47:34 +000011528 }
11529 else
11530 mng_info->ticks_per_second=0;
11531 }
11532 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011533 mng_info->ticks_per_second=(png_uint_32)
11534 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011535 if (final_delay > 50)
11536 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011537
cristy3ed852e2009-09-05 21:47:34 +000011538 if (final_delay > 75)
11539 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011540
cristy3ed852e2009-09-05 21:47:34 +000011541 if (final_delay > 125)
11542 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011543
cristy3ed852e2009-09-05 21:47:34 +000011544 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11545 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11546 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11547 1UL*image->ticks_per_second))
11548 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11549 }
glennrp0fe50b42010-11-16 03:52:51 +000011550
cristy3ed852e2009-09-05 21:47:34 +000011551 if (mng_info->need_fram != MagickFalse)
11552 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11553 /*
11554 If pseudocolor, we should also check to see if all the
11555 palettes are identical and write a global PLTE if they are.
11556 ../glennrp Feb 99.
11557 */
11558 /*
11559 Write the MNG version 1.0 signature and MHDR chunk.
11560 */
11561 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11562 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11563 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011564 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011565 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11566 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011567 PNGLong(chunk+12,mng_info->ticks_per_second);
11568 PNGLong(chunk+16,0L); /* layer count=unknown */
11569 PNGLong(chunk+20,0L); /* frame count=unknown */
11570 PNGLong(chunk+24,0L); /* play time=unknown */
11571 if (write_jng)
11572 {
11573 if (need_matte)
11574 {
11575 if (need_defi || mng_info->need_fram || use_global_plte)
11576 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011577
cristy3ed852e2009-09-05 21:47:34 +000011578 else
11579 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11580 }
glennrp0fe50b42010-11-16 03:52:51 +000011581
cristy3ed852e2009-09-05 21:47:34 +000011582 else
11583 {
11584 if (need_defi || mng_info->need_fram || use_global_plte)
11585 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011586
cristy3ed852e2009-09-05 21:47:34 +000011587 else
11588 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11589 }
11590 }
glennrp0fe50b42010-11-16 03:52:51 +000011591
cristy3ed852e2009-09-05 21:47:34 +000011592 else
11593 {
11594 if (need_matte)
11595 {
11596 if (need_defi || mng_info->need_fram || use_global_plte)
11597 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011598
cristy3ed852e2009-09-05 21:47:34 +000011599 else
11600 PNGLong(chunk+28,9L); /* simplicity=VLC */
11601 }
glennrp0fe50b42010-11-16 03:52:51 +000011602
cristy3ed852e2009-09-05 21:47:34 +000011603 else
11604 {
11605 if (need_defi || mng_info->need_fram || use_global_plte)
11606 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011607
cristy3ed852e2009-09-05 21:47:34 +000011608 else
11609 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11610 }
11611 }
11612 (void) WriteBlob(image,32,chunk);
11613 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11614 option=GetImageOption(image_info,"mng:need-cacheoff");
11615 if (option != (const char *) NULL)
11616 {
11617 size_t
11618 length;
11619
11620 /*
11621 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11622 */
11623 PNGType(chunk,mng_nEED);
11624 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011625 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011626 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011627 length+=4;
11628 (void) WriteBlob(image,length,chunk);
11629 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11630 }
11631 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11632 (GetNextImageInList(image) != (Image *) NULL) &&
11633 (image->iterations != 1))
11634 {
11635 /*
11636 Write MNG TERM chunk
11637 */
11638 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11639 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011640 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011641 chunk[4]=3; /* repeat animation */
11642 chunk[5]=0; /* show last frame when done */
11643 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11644 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011645
cristy3ed852e2009-09-05 21:47:34 +000011646 if (image->iterations == 0)
11647 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011648
cristy3ed852e2009-09-05 21:47:34 +000011649 else
11650 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011651
cristy3ed852e2009-09-05 21:47:34 +000011652 if (logging != MagickFalse)
11653 {
11654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011655 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11656 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011657
cristy3ed852e2009-09-05 21:47:34 +000011658 if (image->iterations == 0)
11659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011660 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011661
cristy3ed852e2009-09-05 21:47:34 +000011662 else
11663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011664 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011665 }
11666 (void) WriteBlob(image,14,chunk);
11667 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11668 }
11669 /*
11670 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11671 */
11672 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11673 mng_info->equal_srgbs)
11674 {
11675 /*
11676 Write MNG sRGB chunk
11677 */
11678 (void) WriteBlobMSBULong(image,1L);
11679 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011680 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011681
cristy3ed852e2009-09-05 21:47:34 +000011682 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011683 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011684 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011685 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011686
cristy3ed852e2009-09-05 21:47:34 +000011687 else
glennrpe610a072010-08-05 17:08:46 +000011688 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011689 Magick_RenderingIntent_to_PNG_RenderingIntent(
11690 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011691
cristy3ed852e2009-09-05 21:47:34 +000011692 (void) WriteBlob(image,5,chunk);
11693 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11694 mng_info->have_write_global_srgb=MagickTrue;
11695 }
glennrp0fe50b42010-11-16 03:52:51 +000011696
cristy3ed852e2009-09-05 21:47:34 +000011697 else
11698 {
11699 if (image->gamma && mng_info->equal_gammas)
11700 {
11701 /*
11702 Write MNG gAMA chunk
11703 */
11704 (void) WriteBlobMSBULong(image,4L);
11705 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011706 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011707 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011708 (void) WriteBlob(image,8,chunk);
11709 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11710 mng_info->have_write_global_gama=MagickTrue;
11711 }
11712 if (mng_info->equal_chrms)
11713 {
11714 PrimaryInfo
11715 primary;
11716
11717 /*
11718 Write MNG cHRM chunk
11719 */
11720 (void) WriteBlobMSBULong(image,32L);
11721 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011722 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011723 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011724 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11725 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011726 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011727 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11728 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011729 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011730 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11731 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011732 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011733 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11734 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011735 (void) WriteBlob(image,36,chunk);
11736 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11737 mng_info->have_write_global_chrm=MagickTrue;
11738 }
11739 }
11740 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11741 {
11742 /*
11743 Write MNG pHYs chunk
11744 */
11745 (void) WriteBlobMSBULong(image,9L);
11746 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011747 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011748
cristy3ed852e2009-09-05 21:47:34 +000011749 if (image->units == PixelsPerInchResolution)
11750 {
cristy35ef8242010-06-03 16:24:13 +000011751 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011752 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011753
cristy35ef8242010-06-03 16:24:13 +000011754 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011755 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011756
cristy3ed852e2009-09-05 21:47:34 +000011757 chunk[12]=1;
11758 }
glennrp0fe50b42010-11-16 03:52:51 +000011759
cristy3ed852e2009-09-05 21:47:34 +000011760 else
11761 {
11762 if (image->units == PixelsPerCentimeterResolution)
11763 {
cristy35ef8242010-06-03 16:24:13 +000011764 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011765 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011766
cristy35ef8242010-06-03 16:24:13 +000011767 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011768 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011769
cristy3ed852e2009-09-05 21:47:34 +000011770 chunk[12]=1;
11771 }
glennrp0fe50b42010-11-16 03:52:51 +000011772
cristy3ed852e2009-09-05 21:47:34 +000011773 else
11774 {
cristy35ef8242010-06-03 16:24:13 +000011775 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11776 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011777 chunk[12]=0;
11778 }
11779 }
11780 (void) WriteBlob(image,13,chunk);
11781 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11782 }
11783 /*
11784 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11785 or does not cover the entire frame.
11786 */
11787 if (write_mng && (image->matte || image->page.x > 0 ||
11788 image->page.y > 0 || (image->page.width &&
11789 (image->page.width+image->page.x < mng_info->page.width))
11790 || (image->page.height && (image->page.height+image->page.y
11791 < mng_info->page.height))))
11792 {
11793 (void) WriteBlobMSBULong(image,6L);
11794 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011795 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011796 red=ScaleQuantumToShort(image->background_color.red);
11797 green=ScaleQuantumToShort(image->background_color.green);
11798 blue=ScaleQuantumToShort(image->background_color.blue);
11799 PNGShort(chunk+4,red);
11800 PNGShort(chunk+6,green);
11801 PNGShort(chunk+8,blue);
11802 (void) WriteBlob(image,10,chunk);
11803 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11804 if (mng_info->equal_backgrounds)
11805 {
11806 (void) WriteBlobMSBULong(image,6L);
11807 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011808 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011809 (void) WriteBlob(image,10,chunk);
11810 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11811 }
11812 }
11813
11814#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11815 if ((need_local_plte == MagickFalse) &&
11816 (image->storage_class == PseudoClass) &&
11817 (all_images_are_gray == MagickFalse))
11818 {
cristybb503372010-05-27 20:51:26 +000011819 size_t
cristy3ed852e2009-09-05 21:47:34 +000011820 data_length;
11821
11822 /*
11823 Write MNG PLTE chunk
11824 */
11825 data_length=3*image->colors;
11826 (void) WriteBlobMSBULong(image,data_length);
11827 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011828 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011829
cristybb503372010-05-27 20:51:26 +000011830 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011831 {
11832 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11833 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11834 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11835 }
glennrp0fe50b42010-11-16 03:52:51 +000011836
cristy3ed852e2009-09-05 21:47:34 +000011837 (void) WriteBlob(image,data_length+4,chunk);
11838 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11839 mng_info->have_write_global_plte=MagickTrue;
11840 }
11841#endif
11842 }
11843 scene=0;
11844 mng_info->delay=0;
11845#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11846 defined(PNG_MNG_FEATURES_SUPPORTED)
11847 mng_info->equal_palettes=MagickFalse;
11848#endif
11849 do
11850 {
11851 if (mng_info->adjoin)
11852 {
11853#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11854 defined(PNG_MNG_FEATURES_SUPPORTED)
11855 /*
11856 If we aren't using a global palette for the entire MNG, check to
11857 see if we can use one for two or more consecutive images.
11858 */
11859 if (need_local_plte && use_global_plte && !all_images_are_gray)
11860 {
11861 if (mng_info->IsPalette)
11862 {
11863 /*
11864 When equal_palettes is true, this image has the same palette
11865 as the previous PseudoClass image
11866 */
11867 mng_info->have_write_global_plte=mng_info->equal_palettes;
11868 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11869 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11870 {
11871 /*
11872 Write MNG PLTE chunk
11873 */
cristybb503372010-05-27 20:51:26 +000011874 size_t
cristy3ed852e2009-09-05 21:47:34 +000011875 data_length;
11876
11877 data_length=3*image->colors;
11878 (void) WriteBlobMSBULong(image,data_length);
11879 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011880 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011881
cristybb503372010-05-27 20:51:26 +000011882 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011883 {
11884 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11885 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11886 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11887 }
glennrp0fe50b42010-11-16 03:52:51 +000011888
cristy3ed852e2009-09-05 21:47:34 +000011889 (void) WriteBlob(image,data_length+4,chunk);
11890 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11891 (uInt) (data_length+4)));
11892 mng_info->have_write_global_plte=MagickTrue;
11893 }
11894 }
11895 else
11896 mng_info->have_write_global_plte=MagickFalse;
11897 }
11898#endif
11899 if (need_defi)
11900 {
cristybb503372010-05-27 20:51:26 +000011901 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011902 previous_x,
11903 previous_y;
11904
11905 if (scene)
11906 {
11907 previous_x=mng_info->page.x;
11908 previous_y=mng_info->page.y;
11909 }
11910 else
11911 {
11912 previous_x=0;
11913 previous_y=0;
11914 }
11915 mng_info->page=image->page;
11916 if ((mng_info->page.x != previous_x) ||
11917 (mng_info->page.y != previous_y))
11918 {
11919 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11920 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000011921 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000011922 chunk[4]=0; /* object 0 MSB */
11923 chunk[5]=0; /* object 0 LSB */
11924 chunk[6]=0; /* visible */
11925 chunk[7]=0; /* abstract */
11926 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11927 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11928 (void) WriteBlob(image,16,chunk);
11929 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11930 }
11931 }
11932 }
11933
11934 mng_info->write_mng=write_mng;
11935
11936 if ((int) image->dispose >= 3)
11937 mng_info->framing_mode=3;
11938
11939 if (mng_info->need_fram && mng_info->adjoin &&
11940 ((image->delay != mng_info->delay) ||
11941 (mng_info->framing_mode != mng_info->old_framing_mode)))
11942 {
11943 if (image->delay == mng_info->delay)
11944 {
11945 /*
11946 Write a MNG FRAM chunk with the new framing mode.
11947 */
11948 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11949 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011950 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000011951 chunk[4]=(unsigned char) mng_info->framing_mode;
11952 (void) WriteBlob(image,5,chunk);
11953 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11954 }
11955 else
11956 {
11957 /*
11958 Write a MNG FRAM chunk with the delay.
11959 */
11960 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11961 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011962 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011963 chunk[4]=(unsigned char) mng_info->framing_mode;
11964 chunk[5]=0; /* frame name separator (no name) */
11965 chunk[6]=2; /* flag for changing default delay */
11966 chunk[7]=0; /* flag for changing frame timeout */
11967 chunk[8]=0; /* flag for changing frame clipping */
11968 chunk[9]=0; /* flag for changing frame sync_id */
11969 PNGLong(chunk+10,(png_uint_32)
11970 ((mng_info->ticks_per_second*
11971 image->delay)/MagickMax(image->ticks_per_second,1)));
11972 (void) WriteBlob(image,14,chunk);
11973 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000011974 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000011975 }
11976 mng_info->old_framing_mode=mng_info->framing_mode;
11977 }
11978
11979#if defined(JNG_SUPPORTED)
11980 if (image_info->compression == JPEGCompression)
11981 {
11982 ImageInfo
11983 *write_info;
11984
11985 if (logging != MagickFalse)
11986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11987 " Writing JNG object.");
11988 /* To do: specify the desired alpha compression method. */
11989 write_info=CloneImageInfo(image_info);
11990 write_info->compression=UndefinedCompression;
11991 status=WriteOneJNGImage(mng_info,write_info,image);
11992 write_info=DestroyImageInfo(write_info);
11993 }
11994 else
11995#endif
11996 {
11997 if (logging != MagickFalse)
11998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11999 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012000
glennrpb9cfe272010-12-21 15:08:06 +000012001 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012002 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012003
12004 /* We don't want any ancillary chunks written */
12005 mng_info->ping_exclude_bKGD=MagickTrue;
12006 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012007 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012008 mng_info->ping_exclude_EXIF=MagickTrue;
12009 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012010 mng_info->ping_exclude_iCCP=MagickTrue;
12011 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12012 mng_info->ping_exclude_oFFs=MagickTrue;
12013 mng_info->ping_exclude_pHYs=MagickTrue;
12014 mng_info->ping_exclude_sRGB=MagickTrue;
12015 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012016 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012017 mng_info->ping_exclude_vpAg=MagickTrue;
12018 mng_info->ping_exclude_zCCP=MagickTrue;
12019 mng_info->ping_exclude_zTXt=MagickTrue;
12020
cristy3ed852e2009-09-05 21:47:34 +000012021 status=WriteOnePNGImage(mng_info,image_info,image);
12022 }
12023
12024 if (status == MagickFalse)
12025 {
12026 MngInfoFreeStruct(mng_info,&have_mng_structure);
12027 (void) CloseBlob(image);
12028 return(MagickFalse);
12029 }
12030 (void) CatchImageException(image);
12031 if (GetNextImageInList(image) == (Image *) NULL)
12032 break;
12033 image=SyncNextImageInList(image);
12034 status=SetImageProgress(image,SaveImagesTag,scene++,
12035 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012036
cristy3ed852e2009-09-05 21:47:34 +000012037 if (status == MagickFalse)
12038 break;
glennrp0fe50b42010-11-16 03:52:51 +000012039
cristy3ed852e2009-09-05 21:47:34 +000012040 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012041
cristy3ed852e2009-09-05 21:47:34 +000012042 if (write_mng)
12043 {
12044 while (GetPreviousImageInList(image) != (Image *) NULL)
12045 image=GetPreviousImageInList(image);
12046 /*
12047 Write the MEND chunk.
12048 */
12049 (void) WriteBlobMSBULong(image,0x00000000L);
12050 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012051 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012052 (void) WriteBlob(image,4,chunk);
12053 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12054 }
12055 /*
12056 Relinquish resources.
12057 */
12058 (void) CloseBlob(image);
12059 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012060
cristy3ed852e2009-09-05 21:47:34 +000012061 if (logging != MagickFalse)
12062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012063
cristy3ed852e2009-09-05 21:47:34 +000012064 return(MagickTrue);
12065}
glennrpd5045b42010-03-24 12:40:35 +000012066#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012067
cristy3ed852e2009-09-05 21:47:34 +000012068static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12069{
12070 image=image;
12071 printf("Your PNG library is too old: You have libpng-%s\n",
12072 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012073
cristy3ed852e2009-09-05 21:47:34 +000012074 ThrowBinaryException(CoderError,"PNG library is too old",
12075 image_info->filename);
12076}
glennrp39992b42010-11-14 00:03:43 +000012077
cristy3ed852e2009-09-05 21:47:34 +000012078static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12079{
12080 return(WritePNGImage(image_info,image));
12081}
glennrpd5045b42010-03-24 12:40:35 +000012082#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012083#endif