blob: ce1f60cab01140c9995007d8bd6cb2bb5141c051 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
44#include "magick/studio.h"
glennrp5c7cf4e2010-12-24 00:30:00 +000045#include "magick/artifact.h"
cristy5a2ca482009-10-14 18:24:56 +000046#include "magick/attribute.h"
cristy3ed852e2009-09-05 21:47:34 +000047#include "magick/blob.h"
48#include "magick/blob-private.h"
49#include "magick/cache.h"
50#include "magick/color.h"
51#include "magick/color-private.h"
cristy4ccd4c02010-04-25 00:43:15 +000052#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000053#include "magick/colorspace.h"
54#include "magick/constitute.h"
55#include "magick/enhance.h"
56#include "magick/exception.h"
57#include "magick/exception-private.h"
58#include "magick/geometry.h"
cristyf2e11662009-10-14 01:24:43 +000059#include "magick/histogram.h"
cristy3ed852e2009-09-05 21:47:34 +000060#include "magick/image.h"
61#include "magick/image-private.h"
62#include "magick/layer.h"
63#include "magick/list.h"
64#include "magick/log.h"
65#include "magick/magick.h"
66#include "magick/memory_.h"
67#include "magick/module.h"
68#include "magick/monitor.h"
69#include "magick/monitor-private.h"
70#include "magick/option.h"
71#include "magick/quantum-private.h"
72#include "magick/profile.h"
73#include "magick/property.h"
cristy3ed852e2009-09-05 21:47:34 +000074#include "magick/resource_.h"
75#include "magick/semaphore.h"
76#include "magick/quantum-private.h"
77#include "magick/static.h"
78#include "magick/statistic.h"
79#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000080#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000081#include "magick/transform.h"
82#include "magick/utility.h"
83#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000084
glennrp7ef138c2009-11-10 13:50:20 +000085/* Suppress libpng pedantic warnings that were added in
86 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000087 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000088 * fix any code that generates warnings.
89 */
glennrp991e92a2010-01-28 03:09:00 +000090/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000091/* #define PNG_USE_RESULT The result of this function must be checked */
92/* #define PNG_NORETURN This function does not return */
93/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000094/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000095
96/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +000097#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +000098
cristy3ed852e2009-09-05 21:47:34 +000099#include "png.h"
100#include "zlib.h"
101
102/* ImageMagick differences */
103#define first_scene scene
104
glennrpd5045b42010-03-24 12:40:35 +0000105#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000106/*
107 Optional declarations. Define or undefine them as you like.
108*/
109/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
110
111/*
112 Features under construction. Define these to work on them.
113*/
114#undef MNG_OBJECT_BUFFERS
115#undef MNG_BASI_SUPPORTED
116#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
117#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000118#if defined(MAGICKCORE_JPEG_DELEGATE)
119# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
120#endif
121#if !defined(RGBColorMatchExact)
122#define IsPNGColorEqual(color,target) \
123 (((color).red == (target).red) && \
124 ((color).green == (target).green) && \
125 ((color).blue == (target).blue))
126#endif
127
128/*
129 Establish thread safety.
130 setjmp/longjmp is claimed to be safe on these platforms:
131 setjmp/longjmp is alleged to be unsafe on these platforms:
132*/
133#ifndef SETJMP_IS_THREAD_SAFE
134#define PNG_SETJMP_NOT_THREAD_SAFE
135#endif
136
137#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
138static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000139 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000140#endif
141
142/*
143 This temporary until I set up malloc'ed object attributes array.
144 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
145 waste more memory.
146*/
147#define MNG_MAX_OBJECTS 256
148
149/*
150 If this not defined, spec is interpreted strictly. If it is
151 defined, an attempt will be made to recover from some errors,
152 including
153 o global PLTE too short
154*/
155#undef MNG_LOOSE
156
157/*
158 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
159 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
160 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
161 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
162 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
163 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
164 will be enabled by default in libpng-1.2.0.
165*/
cristy3ed852e2009-09-05 21:47:34 +0000166#ifdef PNG_MNG_FEATURES_SUPPORTED
167# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
168# define PNG_READ_EMPTY_PLTE_SUPPORTED
169# endif
170# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
171# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
172# endif
173#endif
174
175/*
cristybb503372010-05-27 20:51:26 +0000176 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000177 This macro is only defined in libpng-1.0.3 and later.
178 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
179*/
180#ifndef PNG_UINT_31_MAX
181#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
182#endif
183
184/*
185 Constant strings for known chunk types. If you need to add a chunk,
186 add a string holding the name here. To make the code more
187 portable, we use ASCII numbers like this, not characters.
188*/
189
190static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
191static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
192static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
193static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
194static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
195static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
196static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
197static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
198static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
199static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
200static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
201static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
202static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
203static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
204static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
205static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
206static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
207static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
208static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
209static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
210static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
211static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
212static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
213static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
214static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
215static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
216static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
217static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
218static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
219static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
220static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
221static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
222static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
223static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
224
225#if defined(JNG_SUPPORTED)
226static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
227static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
228static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
229static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
230static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
231static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
232#endif
233
234/*
235Other known chunks that are not yet supported by ImageMagick:
236static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
237static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
238static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
239static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
240static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
241static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
242static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
243static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
244*/
245
246typedef struct _MngBox
247{
cristy8182b072010-05-30 20:10:53 +0000248 long
cristy3ed852e2009-09-05 21:47:34 +0000249 left,
250 right,
251 top,
252 bottom;
253} MngBox;
254
255typedef struct _MngPair
256{
cristy8182b072010-05-30 20:10:53 +0000257 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000258 a,
259 b;
260} MngPair;
261
262#ifdef MNG_OBJECT_BUFFERS
263typedef struct _MngBuffer
264{
265
cristybb503372010-05-27 20:51:26 +0000266 size_t
cristy3ed852e2009-09-05 21:47:34 +0000267 height,
268 width;
269
270 Image
271 *image;
272
273 png_color
274 plte[256];
275
276 int
277 reference_count;
278
279 unsigned char
280 alpha_sample_depth,
281 compression_method,
282 color_type,
283 concrete,
284 filter_method,
285 frozen,
286 image_type,
287 interlace_method,
288 pixel_sample_depth,
289 plte_length,
290 sample_depth,
291 viewable;
292} MngBuffer;
293#endif
294
295typedef struct _MngInfo
296{
297
298#ifdef MNG_OBJECT_BUFFERS
299 MngBuffer
300 *ob[MNG_MAX_OBJECTS];
301#endif
302
303 Image *
304 image;
305
306 RectangleInfo
307 page;
308
309 int
310 adjoin,
311#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
312 bytes_in_read_buffer,
313 found_empty_plte,
314#endif
315 equal_backgrounds,
316 equal_chrms,
317 equal_gammas,
318#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
319 defined(PNG_MNG_FEATURES_SUPPORTED)
320 equal_palettes,
321#endif
322 equal_physs,
323 equal_srgbs,
324 framing_mode,
325 have_global_bkgd,
326 have_global_chrm,
327 have_global_gama,
328 have_global_phys,
329 have_global_sbit,
330 have_global_srgb,
331 have_saved_bkgd_index,
332 have_write_global_chrm,
333 have_write_global_gama,
334 have_write_global_plte,
335 have_write_global_srgb,
336 need_fram,
337 object_id,
338 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000339 saved_bkgd_index;
340
341 int
342 new_number_colors;
343
cristybb503372010-05-27 20:51:26 +0000344 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000345 image_found,
346 loop_count[256],
347 loop_iteration[256],
348 scenes_found,
349 x_off[MNG_MAX_OBJECTS],
350 y_off[MNG_MAX_OBJECTS];
351
352 MngBox
353 clip,
354 frame,
355 image_box,
356 object_clip[MNG_MAX_OBJECTS];
357
358 unsigned char
359 /* These flags could be combined into one byte */
360 exists[MNG_MAX_OBJECTS],
361 frozen[MNG_MAX_OBJECTS],
362 loop_active[256],
363 invisible[MNG_MAX_OBJECTS],
364 viewable[MNG_MAX_OBJECTS];
365
366 MagickOffsetType
367 loop_jump[256];
368
369 png_colorp
370 global_plte;
371
372 png_color_8
373 global_sbit;
374
375 png_byte
376#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
377 read_buffer[8],
378#endif
379 global_trns[256];
380
381 float
382 global_gamma;
383
384 ChromaticityInfo
385 global_chrm;
386
387 RenderingIntent
388 global_srgb_intent;
389
cristy35ef8242010-06-03 16:24:13 +0000390 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000391 delay,
392 global_plte_length,
393 global_trns_length,
394 global_x_pixels_per_unit,
395 global_y_pixels_per_unit,
396 mng_width,
397 mng_height,
398 ticks_per_second;
399
glennrpb9cfe272010-12-21 15:08:06 +0000400 MagickBooleanType
401 need_blob;
402
cristy3ed852e2009-09-05 21:47:34 +0000403 unsigned int
404 IsPalette,
405 global_phys_unit_type,
406 basi_warning,
407 clon_warning,
408 dhdr_warning,
409 jhdr_warning,
410 magn_warning,
411 past_warning,
412 phyg_warning,
413 phys_warning,
414 sbit_warning,
415 show_warning,
416 mng_type,
417 write_mng,
418 write_png_colortype,
419 write_png_depth,
420 write_png8,
421 write_png24,
422 write_png32;
423
424#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000425 size_t
cristy3ed852e2009-09-05 21:47:34 +0000426 basi_width,
427 basi_height;
428
429 unsigned int
430 basi_depth,
431 basi_color_type,
432 basi_compression_method,
433 basi_filter_type,
434 basi_interlace_method,
435 basi_red,
436 basi_green,
437 basi_blue,
438 basi_alpha,
439 basi_viewable;
440#endif
441
442 png_uint_16
443 magn_first,
444 magn_last,
445 magn_mb,
446 magn_ml,
447 magn_mr,
448 magn_mt,
449 magn_mx,
450 magn_my,
451 magn_methx,
452 magn_methy;
453
454 PixelPacket
455 mng_global_bkgd;
456
glennrp26f37912010-12-23 16:22:42 +0000457 /* Added at version 6.6.6-7 */
458 MagickBooleanType
459 ping_exclude_bKGD,
460 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000461 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000462 ping_exclude_EXIF,
463 ping_exclude_gAMA,
464 ping_exclude_iCCP,
465 /* ping_exclude_iTXt, */
466 ping_exclude_oFFs,
467 ping_exclude_pHYs,
468 ping_exclude_sRGB,
469 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000470 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000471 ping_exclude_vpAg,
472 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000473 ping_exclude_zTXt,
474 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000475
cristy3ed852e2009-09-05 21:47:34 +0000476} MngInfo;
477#endif /* VER */
478
479/*
480 Forward declarations.
481*/
482static MagickBooleanType
483 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000484
cristy3ed852e2009-09-05 21:47:34 +0000485static MagickBooleanType
486 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000487
cristy3ed852e2009-09-05 21:47:34 +0000488#if defined(JNG_SUPPORTED)
489static MagickBooleanType
490 WriteJNGImage(const ImageInfo *,Image *);
491#endif
492
glennrp0c3e06b2010-11-19 13:45:02 +0000493#if PNG_LIBPNG_VER > 10011
494
glennrpfd05d622011-02-25 04:10:33 +0000495
glennrp0c3e06b2010-11-19 13:45:02 +0000496#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
497static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000498LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000499{
glennrp67b9c1a2011-04-22 18:47:36 +0000500 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
501 *
502 * This is true if the high byte and the next highest byte of
503 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000504 * are equal to each other. We check this by seeing if the samples
505 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000506 *
507 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000508 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000509 */
510
glennrp3faa9a32011-04-23 14:00:25 +0000511#define QuantumToCharToQuantumEqQuantum(quantum) \
512 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
513
glennrp0c3e06b2010-11-19 13:45:02 +0000514 MagickBooleanType
515 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000516
glennrp03e11f62011-04-22 13:30:16 +0000517 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000518 {
519
520 const PixelPacket
521 *p;
522
523 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000524 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
525 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
526 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
527 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000528
529 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
530 {
531 int indx;
532
533 for (indx=0; indx < (ssize_t) image->colors; indx++)
534 {
glennrp3faa9a32011-04-23 14:00:25 +0000535 ok_to_reduce=(
536 QuantumToCharToQuantumEqQuantum(
537 image->colormap[indx].red) &&
538 QuantumToCharToQuantumEqQuantum(
539 image->colormap[indx].green) &&
540 QuantumToCharToQuantumEqQuantum(
541 image->colormap[indx].blue)) ?
542 MagickTrue : MagickFalse;
543
glennrp0c3e06b2010-11-19 13:45:02 +0000544 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000545 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000546 }
547 }
548
549 if ((ok_to_reduce != MagickFalse) &&
550 (image->storage_class != PseudoClass))
551 {
552 ssize_t
553 y;
554
555 register ssize_t
556 x;
557
558 for (y=0; y < (ssize_t) image->rows; y++)
559 {
560 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
561
562 if (p == (const PixelPacket *) NULL)
563 {
564 ok_to_reduce = MagickFalse;
565 break;
566 }
567
568 for (x=(ssize_t) image->columns-1; x >= 0; x--)
569 {
glennrp3faa9a32011-04-23 14:00:25 +0000570 ok_to_reduce=
571 QuantumToCharToQuantumEqQuantum(GetRedPixelComponent(p)) &&
572 QuantumToCharToQuantumEqQuantum(GetGreenPixelComponent(p)) &&
573 QuantumToCharToQuantumEqQuantum(GetBluePixelComponent(p)) ?
574 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000575
576 if (ok_to_reduce == MagickFalse)
577 break;
578
579 p++;
580 }
glennrp8640fb52010-11-23 15:48:26 +0000581 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +0000582 break;
583 }
584 }
585
586 if (ok_to_reduce != MagickFalse)
587 {
glennrp0c3e06b2010-11-19 13:45:02 +0000588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000589 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +0000590 }
glennrpa6a06632011-01-19 15:15:34 +0000591 else
592 {
593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000594 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +0000595 }
glennrp0c3e06b2010-11-19 13:45:02 +0000596 }
597
598 return ok_to_reduce;
599}
600#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
601
glennrpe610a072010-08-05 17:08:46 +0000602static int
glennrpcf002022011-01-30 02:38:15 +0000603Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +0000604{
glennrpe610a072010-08-05 17:08:46 +0000605 switch (intent)
606 {
607 case PerceptualIntent:
608 return 0;
glennrp0fe50b42010-11-16 03:52:51 +0000609
glennrpe610a072010-08-05 17:08:46 +0000610 case RelativeIntent:
611 return 1;
glennrp0fe50b42010-11-16 03:52:51 +0000612
glennrpe610a072010-08-05 17:08:46 +0000613 case SaturationIntent:
614 return 2;
glennrp0fe50b42010-11-16 03:52:51 +0000615
glennrpe610a072010-08-05 17:08:46 +0000616 case AbsoluteIntent:
617 return 3;
glennrp0fe50b42010-11-16 03:52:51 +0000618
glennrpe610a072010-08-05 17:08:46 +0000619 default:
620 return -1;
621 }
622}
623
624static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +0000625Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +0000626{
glennrpcf002022011-01-30 02:38:15 +0000627 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +0000628 {
629 case 0:
630 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000631
glennrpe610a072010-08-05 17:08:46 +0000632 case 1:
633 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000634
glennrpe610a072010-08-05 17:08:46 +0000635 case 2:
636 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000637
glennrpe610a072010-08-05 17:08:46 +0000638 case 3:
639 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +0000640
glennrpe610a072010-08-05 17:08:46 +0000641 default:
642 return UndefinedIntent;
643 }
644}
645
cristybb503372010-05-27 20:51:26 +0000646static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000647{
648 if (x > y)
649 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000650
cristy3ed852e2009-09-05 21:47:34 +0000651 return(y);
652}
glennrp0c3e06b2010-11-19 13:45:02 +0000653
cristybb503372010-05-27 20:51:26 +0000654static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000655{
656 if (x < y)
657 return(x);
glennrp0fe50b42010-11-16 03:52:51 +0000658
cristy3ed852e2009-09-05 21:47:34 +0000659 return(y);
660}
glennrp0c3e06b2010-11-19 13:45:02 +0000661
cristy3ed852e2009-09-05 21:47:34 +0000662
663/*
664%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
665% %
666% %
667% %
668% I m a g e I s G r a y %
669% %
670% %
671% %
672%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
673% %
674% Like IsGrayImage except does not change DirectClass to PseudoClass %
675% %
676%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
677*/
678static MagickBooleanType ImageIsGray(Image *image)
679{
680 register const PixelPacket
681 *p;
682
cristybb503372010-05-27 20:51:26 +0000683 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000684 i,
685 x,
686 y;
687
688 assert(image != (Image *) NULL);
689 assert(image->signature == MagickSignature);
690 if (image->debug != MagickFalse)
691 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
692
693 if (image->storage_class == PseudoClass)
694 {
cristybb503372010-05-27 20:51:26 +0000695 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000696 if (IsGray(image->colormap+i) == MagickFalse)
697 return(MagickFalse);
698 return(MagickTrue);
699 }
cristybb503372010-05-27 20:51:26 +0000700 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000701 {
702 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
703 if (p == (const PixelPacket *) NULL)
704 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000705 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +0000706 {
707 if (IsGray(p) == MagickFalse)
708 return(MagickFalse);
709 p++;
710 }
711 }
712 return(MagickTrue);
713}
glennrpd5045b42010-03-24 12:40:35 +0000714#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000715#endif /* MAGICKCORE_PNG_DELEGATE */
716
717/*
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719% %
720% %
721% %
722% I s M N G %
723% %
724% %
725% %
726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727%
728% IsMNG() returns MagickTrue if the image format type, identified by the
729% magick string, is MNG.
730%
731% The format of the IsMNG method is:
732%
733% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
734%
735% A description of each parameter follows:
736%
737% o magick: compare image format pattern against these bytes.
738%
739% o length: Specifies the length of the magick string.
740%
741%
742*/
743static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
744{
745 if (length < 8)
746 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000747
cristy3ed852e2009-09-05 21:47:34 +0000748 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
749 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000750
cristy3ed852e2009-09-05 21:47:34 +0000751 return(MagickFalse);
752}
753
754/*
755%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756% %
757% %
758% %
759% I s J N G %
760% %
761% %
762% %
763%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
764%
765% IsJNG() returns MagickTrue if the image format type, identified by the
766% magick string, is JNG.
767%
768% The format of the IsJNG method is:
769%
770% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
771%
772% A description of each parameter follows:
773%
774% o magick: compare image format pattern against these bytes.
775%
776% o length: Specifies the length of the magick string.
777%
778%
779*/
780static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
781{
782 if (length < 8)
783 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000784
cristy3ed852e2009-09-05 21:47:34 +0000785 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
786 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000787
cristy3ed852e2009-09-05 21:47:34 +0000788 return(MagickFalse);
789}
790
791/*
792%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
793% %
794% %
795% %
796% I s P N G %
797% %
798% %
799% %
800%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
801%
802% IsPNG() returns MagickTrue if the image format type, identified by the
803% magick string, is PNG.
804%
805% The format of the IsPNG method is:
806%
807% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
808%
809% A description of each parameter follows:
810%
811% o magick: compare image format pattern against these bytes.
812%
813% o length: Specifies the length of the magick string.
814%
815*/
816static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
817{
818 if (length < 8)
819 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +0000820
cristy3ed852e2009-09-05 21:47:34 +0000821 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
822 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +0000823
cristy3ed852e2009-09-05 21:47:34 +0000824 return(MagickFalse);
825}
826
827#if defined(MAGICKCORE_PNG_DELEGATE)
828#if defined(__cplusplus) || defined(c_plusplus)
829extern "C" {
830#endif
831
glennrpd5045b42010-03-24 12:40:35 +0000832#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +0000833static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +0000834{
835 unsigned char
836 buffer[4];
837
838 assert(image != (Image *) NULL);
839 assert(image->signature == MagickSignature);
840 buffer[0]=(unsigned char) (value >> 24);
841 buffer[1]=(unsigned char) (value >> 16);
842 buffer[2]=(unsigned char) (value >> 8);
843 buffer[3]=(unsigned char) value;
844 return((size_t) WriteBlob(image,4,buffer));
845}
846
847static void PNGLong(png_bytep p,png_uint_32 value)
848{
849 *p++=(png_byte) ((value >> 24) & 0xff);
850 *p++=(png_byte) ((value >> 16) & 0xff);
851 *p++=(png_byte) ((value >> 8) & 0xff);
852 *p++=(png_byte) (value & 0xff);
853}
854
glennrpa521b2f2010-10-29 04:11:03 +0000855#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +0000856static void PNGsLong(png_bytep p,png_int_32 value)
857{
858 *p++=(png_byte) ((value >> 24) & 0xff);
859 *p++=(png_byte) ((value >> 16) & 0xff);
860 *p++=(png_byte) ((value >> 8) & 0xff);
861 *p++=(png_byte) (value & 0xff);
862}
glennrpa521b2f2010-10-29 04:11:03 +0000863#endif
cristy3ed852e2009-09-05 21:47:34 +0000864
865static void PNGShort(png_bytep p,png_uint_16 value)
866{
867 *p++=(png_byte) ((value >> 8) & 0xff);
868 *p++=(png_byte) (value & 0xff);
869}
870
871static void PNGType(png_bytep p,png_bytep type)
872{
873 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
874}
875
glennrp03812ae2010-12-24 01:31:34 +0000876static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
877 size_t length)
cristy3ed852e2009-09-05 21:47:34 +0000878{
879 if (logging != MagickFalse)
880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000881 " Writing %c%c%c%c chunk, length: %.20g",
882 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +0000883}
glennrpd5045b42010-03-24 12:40:35 +0000884#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000885
886#if defined(__cplusplus) || defined(c_plusplus)
887}
888#endif
889
glennrpd5045b42010-03-24 12:40:35 +0000890#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000891/*
892%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
893% %
894% %
895% %
896% R e a d P N G I m a g e %
897% %
898% %
899% %
900%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
901%
902% ReadPNGImage() reads a Portable Network Graphics (PNG) or
903% Multiple-image Network Graphics (MNG) image file and returns it. It
904% allocates the memory necessary for the new Image structure and returns a
905% pointer to the new image or set of images.
906%
907% MNG support written by Glenn Randers-Pehrson, glennrp@image...
908%
909% The format of the ReadPNGImage method is:
910%
911% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
912%
913% A description of each parameter follows:
914%
915% o image_info: the image info.
916%
917% o exception: return any errors or warnings in this structure.
918%
919% To do, more or less in chronological order (as of version 5.5.2,
920% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
921%
922% Get 16-bit cheap transparency working.
923%
924% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
925%
926% Preserve all unknown and not-yet-handled known chunks found in input
927% PNG file and copy them into output PNG files according to the PNG
928% copying rules.
929%
930% (At this point, PNG encoding should be in full MNG compliance)
931%
932% Provide options for choice of background to use when the MNG BACK
933% chunk is not present or is not mandatory (i.e., leave transparent,
934% user specified, MNG BACK, PNG bKGD)
935%
936% Implement LOOP/ENDL [done, but could do discretionary loops more
937% efficiently by linking in the duplicate frames.].
938%
939% Decode and act on the MHDR simplicity profile (offer option to reject
940% files or attempt to process them anyway when the profile isn't LC or VLC).
941%
942% Upgrade to full MNG without Delta-PNG.
943%
944% o BACK [done a while ago except for background image ID]
945% o MOVE [done 15 May 1999]
946% o CLIP [done 15 May 1999]
947% o DISC [done 19 May 1999]
948% o SAVE [partially done 19 May 1999 (marks objects frozen)]
949% o SEEK [partially done 19 May 1999 (discard function only)]
950% o SHOW
951% o PAST
952% o BASI
953% o MNG-level tEXt/iTXt/zTXt
954% o pHYg
955% o pHYs
956% o sBIT
957% o bKGD
958% o iTXt (wait for libpng implementation).
959%
960% Use the scene signature to discover when an identical scene is
961% being reused, and just point to the original image->exception instead
962% of storing another set of pixels. This not specific to MNG
963% but could be applied generally.
964%
965% Upgrade to full MNG with Delta-PNG.
966%
967% JNG tEXt/iTXt/zTXt
968%
969% We will not attempt to read files containing the CgBI chunk.
970% They are really Xcode files meant for display on the iPhone.
971% These are not valid PNG files and it is impossible to recover
972% the orginal PNG from files that have been converted to Xcode-PNG,
973% since irretrievable loss of color data has occurred due to the
974% use of premultiplied alpha.
975*/
976
977#if defined(__cplusplus) || defined(c_plusplus)
978extern "C" {
979#endif
980
981/*
982 This the function that does the actual reading of data. It is
983 the same as the one supplied in libpng, except that it receives the
984 datastream from the ReadBlob() function instead of standard input.
985*/
986static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
987{
988 Image
989 *image;
990
991 image=(Image *) png_get_io_ptr(png_ptr);
992 if (length)
993 {
994 png_size_t
995 check;
996
997 check=(png_size_t) ReadBlob(image,(size_t) length,data);
998 if (check != length)
999 {
1000 char
1001 msg[MaxTextExtent];
1002
1003 (void) FormatMagickString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001004 "Expected %.20g bytes; found %.20g bytes",(double) length,
1005 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001006 png_warning(png_ptr,msg);
1007 png_error(png_ptr,"Read Exception");
1008 }
1009 }
1010}
1011
1012#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1013 !defined(PNG_MNG_FEATURES_SUPPORTED)
1014/* We use mng_get_data() instead of png_get_data() if we have a libpng
1015 * older than libpng-1.0.3a, which was the first to allow the empty
1016 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1017 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1018 * encountered after an empty PLTE, so we have to look ahead for bKGD
1019 * chunks and remove them from the datastream that is passed to libpng,
1020 * and store their contents for later use.
1021 */
1022static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1023{
1024 MngInfo
1025 *mng_info;
1026
1027 Image
1028 *image;
1029
1030 png_size_t
1031 check;
1032
cristybb503372010-05-27 20:51:26 +00001033 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001034 i;
1035
1036 i=0;
1037 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1038 image=(Image *) mng_info->image;
1039 while (mng_info->bytes_in_read_buffer && length)
1040 {
1041 data[i]=mng_info->read_buffer[i];
1042 mng_info->bytes_in_read_buffer--;
1043 length--;
1044 i++;
1045 }
1046 if (length)
1047 {
1048 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001049
cristy3ed852e2009-09-05 21:47:34 +00001050 if (check != length)
1051 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001052
cristy3ed852e2009-09-05 21:47:34 +00001053 if (length == 4)
1054 {
1055 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1056 (data[3] == 0))
1057 {
1058 check=(png_size_t) ReadBlob(image,(size_t) length,
1059 (char *) mng_info->read_buffer);
1060 mng_info->read_buffer[4]=0;
1061 mng_info->bytes_in_read_buffer=4;
1062 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1063 mng_info->found_empty_plte=MagickTrue;
1064 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1065 {
1066 mng_info->found_empty_plte=MagickFalse;
1067 mng_info->have_saved_bkgd_index=MagickFalse;
1068 }
1069 }
glennrp0fe50b42010-11-16 03:52:51 +00001070
cristy3ed852e2009-09-05 21:47:34 +00001071 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1072 (data[3] == 1))
1073 {
1074 check=(png_size_t) ReadBlob(image,(size_t) length,
1075 (char *) mng_info->read_buffer);
1076 mng_info->read_buffer[4]=0;
1077 mng_info->bytes_in_read_buffer=4;
1078 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1079 if (mng_info->found_empty_plte)
1080 {
1081 /*
1082 Skip the bKGD data byte and CRC.
1083 */
1084 check=(png_size_t)
1085 ReadBlob(image,5,(char *) mng_info->read_buffer);
1086 check=(png_size_t) ReadBlob(image,(size_t) length,
1087 (char *) mng_info->read_buffer);
1088 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1089 mng_info->have_saved_bkgd_index=MagickTrue;
1090 mng_info->bytes_in_read_buffer=0;
1091 }
1092 }
1093 }
1094 }
1095}
1096#endif
1097
1098static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1099{
1100 Image
1101 *image;
1102
1103 image=(Image *) png_get_io_ptr(png_ptr);
1104 if (length)
1105 {
1106 png_size_t
1107 check;
1108
cristybb503372010-05-27 20:51:26 +00001109 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 if (check != length)
1112 png_error(png_ptr,"WriteBlob Failed");
1113 }
1114}
1115
1116static void png_flush_data(png_structp png_ptr)
1117{
1118 (void) png_ptr;
1119}
1120
1121#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1122static int PalettesAreEqual(Image *a,Image *b)
1123{
cristybb503372010-05-27 20:51:26 +00001124 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001125 i;
1126
1127 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1128 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001129
cristy3ed852e2009-09-05 21:47:34 +00001130 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1131 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001132
cristy3ed852e2009-09-05 21:47:34 +00001133 if (a->colors != b->colors)
1134 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001135
cristybb503372010-05-27 20:51:26 +00001136 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001137 {
1138 if ((a->colormap[i].red != b->colormap[i].red) ||
1139 (a->colormap[i].green != b->colormap[i].green) ||
1140 (a->colormap[i].blue != b->colormap[i].blue))
1141 return((int) MagickFalse);
1142 }
glennrp0fe50b42010-11-16 03:52:51 +00001143
cristy3ed852e2009-09-05 21:47:34 +00001144 return((int) MagickTrue);
1145}
1146#endif
1147
1148static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1149{
1150 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1151 mng_info->exists[i] && !mng_info->frozen[i])
1152 {
1153#ifdef MNG_OBJECT_BUFFERS
1154 if (mng_info->ob[i] != (MngBuffer *) NULL)
1155 {
1156 if (mng_info->ob[i]->reference_count > 0)
1157 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001158
cristy3ed852e2009-09-05 21:47:34 +00001159 if (mng_info->ob[i]->reference_count == 0)
1160 {
1161 if (mng_info->ob[i]->image != (Image *) NULL)
1162 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001163
cristy3ed852e2009-09-05 21:47:34 +00001164 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1165 }
1166 }
1167 mng_info->ob[i]=(MngBuffer *) NULL;
1168#endif
1169 mng_info->exists[i]=MagickFalse;
1170 mng_info->invisible[i]=MagickFalse;
1171 mng_info->viewable[i]=MagickFalse;
1172 mng_info->frozen[i]=MagickFalse;
1173 mng_info->x_off[i]=0;
1174 mng_info->y_off[i]=0;
1175 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001176 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001177 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001178 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001179 }
1180}
1181
glennrp21f0e622011-01-07 16:20:57 +00001182static void MngInfoFreeStruct(MngInfo *mng_info,
1183 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001184{
glennrp21f0e622011-01-07 16:20:57 +00001185 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001186 {
cristybb503372010-05-27 20:51:26 +00001187 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001188 i;
1189
1190 for (i=1; i < MNG_MAX_OBJECTS; i++)
1191 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001192
cristy3ed852e2009-09-05 21:47:34 +00001193 if (mng_info->global_plte != (png_colorp) NULL)
1194 mng_info->global_plte=(png_colorp)
1195 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001196
cristy3ed852e2009-09-05 21:47:34 +00001197 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1198 *have_mng_structure=MagickFalse;
1199 }
1200}
1201
1202static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1203{
1204 MngBox
1205 box;
1206
1207 box=box1;
1208 if (box.left < box2.left)
1209 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001210
cristy3ed852e2009-09-05 21:47:34 +00001211 if (box.top < box2.top)
1212 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001213
cristy3ed852e2009-09-05 21:47:34 +00001214 if (box.right > box2.right)
1215 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001216
cristy3ed852e2009-09-05 21:47:34 +00001217 if (box.bottom > box2.bottom)
1218 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001219
cristy3ed852e2009-09-05 21:47:34 +00001220 return box;
1221}
1222
1223static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1224{
1225 MngBox
1226 box;
1227
1228 /*
1229 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1230 */
cristybb503372010-05-27 20:51:26 +00001231 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1232 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1233 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1234 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001235 if (delta_type != 0)
1236 {
1237 box.left+=previous_box.left;
1238 box.right+=previous_box.right;
1239 box.top+=previous_box.top;
1240 box.bottom+=previous_box.bottom;
1241 }
glennrp0fe50b42010-11-16 03:52:51 +00001242
cristy3ed852e2009-09-05 21:47:34 +00001243 return(box);
1244}
1245
1246static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1247 unsigned char *p)
1248{
1249 MngPair
1250 pair;
1251 /*
cristybb503372010-05-27 20:51:26 +00001252 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001253 */
cristy8182b072010-05-30 20:10:53 +00001254 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1255 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001256
cristy3ed852e2009-09-05 21:47:34 +00001257 if (delta_type != 0)
1258 {
1259 pair.a+=previous_pair.a;
1260 pair.b+=previous_pair.b;
1261 }
glennrp0fe50b42010-11-16 03:52:51 +00001262
cristy3ed852e2009-09-05 21:47:34 +00001263 return(pair);
1264}
1265
cristy8182b072010-05-30 20:10:53 +00001266static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001267{
cristy8182b072010-05-30 20:10:53 +00001268 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001269}
1270
glennrpcf002022011-01-30 02:38:15 +00001271static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001272{
1273 Image
1274 *image;
1275
1276 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001277
cristy3ed852e2009-09-05 21:47:34 +00001278 if (image->debug != MagickFalse)
1279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1280 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001281
cristy3ed852e2009-09-05 21:47:34 +00001282 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1283 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001284
glennrpe4017e32011-01-08 17:16:09 +00001285#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001286 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1287 * are building with libpng-1.4.x and can be ignored.
1288 */
cristy3ed852e2009-09-05 21:47:34 +00001289 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001290#else
1291 png_longjmp(ping,1);
1292#endif
cristy3ed852e2009-09-05 21:47:34 +00001293}
1294
glennrpcf002022011-01-30 02:38:15 +00001295static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001296{
1297 Image
1298 *image;
1299
1300 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1301 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001302
cristy3ed852e2009-09-05 21:47:34 +00001303 image=(Image *) png_get_error_ptr(ping);
1304 if (image->debug != MagickFalse)
1305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001306 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001307
cristy3ed852e2009-09-05 21:47:34 +00001308 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1309 message,"`%s'",image->filename);
1310}
1311
1312#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001313static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001314{
1315#if (PNG_LIBPNG_VER < 10011)
1316 png_voidp
1317 ret;
1318
1319 png_ptr=png_ptr;
1320 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001321
cristy3ed852e2009-09-05 21:47:34 +00001322 if (ret == NULL)
1323 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001324
cristy3ed852e2009-09-05 21:47:34 +00001325 return(ret);
1326#else
1327 png_ptr=png_ptr;
1328 return((png_voidp) AcquireMagickMemory((size_t) size));
1329#endif
1330}
1331
1332/*
1333 Free a pointer. It is removed from the list at the same time.
1334*/
glennrpcf002022011-01-30 02:38:15 +00001335static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001336{
1337 png_ptr=png_ptr;
1338 ptr=RelinquishMagickMemory(ptr);
1339 return((png_free_ptr) NULL);
1340}
1341#endif
1342
1343#if defined(__cplusplus) || defined(c_plusplus)
1344}
1345#endif
1346
1347static int
glennrpcf002022011-01-30 02:38:15 +00001348Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001349 png_textp text,int ii)
1350{
cristybb503372010-05-27 20:51:26 +00001351 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001352 i;
1353
1354 register unsigned char
1355 *dp;
1356
1357 register png_charp
1358 sp;
1359
1360 png_uint_32
1361 length,
1362 nibbles;
1363
1364 StringInfo
1365 *profile;
1366
glennrp0c3e06b2010-11-19 13:45:02 +00001367 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001368 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1369 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1370 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1371 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1372 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1373 13,14,15};
1374
1375 sp=text[ii].text+1;
1376 /* look for newline */
1377 while (*sp != '\n')
1378 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001379
cristy3ed852e2009-09-05 21:47:34 +00001380 /* look for length */
1381 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1382 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001383
cristyf2f27272009-12-17 14:48:46 +00001384 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001385
glennrp97f90e22011-02-22 05:47:58 +00001386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1387 " length: %lu",(unsigned long) length);
1388
cristy3ed852e2009-09-05 21:47:34 +00001389 while (*sp != ' ' && *sp != '\n')
1390 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001391
cristy3ed852e2009-09-05 21:47:34 +00001392 /* allocate space */
1393 if (length == 0)
1394 {
1395 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1396 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1397 return(MagickFalse);
1398 }
glennrp0fe50b42010-11-16 03:52:51 +00001399
cristy3ed852e2009-09-05 21:47:34 +00001400 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001401
cristy3ed852e2009-09-05 21:47:34 +00001402 if (profile == (StringInfo *) NULL)
1403 {
1404 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1405 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1406 "unable to copy profile");
1407 return(MagickFalse);
1408 }
glennrp0fe50b42010-11-16 03:52:51 +00001409
cristy3ed852e2009-09-05 21:47:34 +00001410 /* copy profile, skipping white space and column 1 "=" signs */
1411 dp=GetStringInfoDatum(profile);
1412 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001413
cristybb503372010-05-27 20:51:26 +00001414 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001415 {
1416 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1417 {
1418 if (*sp == '\0')
1419 {
1420 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1421 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1422 profile=DestroyStringInfo(profile);
1423 return(MagickFalse);
1424 }
1425 sp++;
1426 }
glennrp0fe50b42010-11-16 03:52:51 +00001427
cristy3ed852e2009-09-05 21:47:34 +00001428 if (i%2 == 0)
1429 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001430
cristy3ed852e2009-09-05 21:47:34 +00001431 else
1432 (*dp++)+=unhex[(int) *sp++];
1433 }
1434 /*
1435 We have already read "Raw profile type.
1436 */
1437 (void) SetImageProfile(image,&text[ii].key[17],profile);
1438 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001439
cristy3ed852e2009-09-05 21:47:34 +00001440 if (image_info->verbose)
1441 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001442
cristy3ed852e2009-09-05 21:47:34 +00001443 return MagickTrue;
1444}
1445
1446#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1447static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1448{
1449 Image
1450 *image;
1451
1452
1453 /* The unknown chunk structure contains the chunk data:
1454 png_byte name[5];
1455 png_byte *data;
1456 png_size_t size;
1457
1458 Note that libpng has already taken care of the CRC handling.
1459 */
1460
1461
1462 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1463 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1464 return(0); /* Did not recognize */
1465
1466 /* recognized vpAg */
1467
1468 if (chunk->size != 9)
1469 return(-1); /* Error return */
1470
1471 if (chunk->data[8] != 0)
1472 return(0); /* ImageMagick requires pixel units */
1473
1474 image=(Image *) png_get_user_chunk_ptr(ping);
1475
cristybb503372010-05-27 20:51:26 +00001476 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001477 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001478
cristybb503372010-05-27 20:51:26 +00001479 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001480 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1481
1482 /* Return one of the following: */
1483 /* return(-n); chunk had an error */
1484 /* return(0); did not recognize */
1485 /* return(n); success */
1486
1487 return(1);
1488
1489}
1490#endif
1491
1492/*
1493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1494% %
1495% %
1496% %
1497% R e a d O n e P N G I m a g e %
1498% %
1499% %
1500% %
1501%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1502%
1503% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1504% (minus the 8-byte signature) and returns it. It allocates the memory
1505% necessary for the new Image structure and returns a pointer to the new
1506% image.
1507%
1508% The format of the ReadOnePNGImage method is:
1509%
1510% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1511% ExceptionInfo *exception)
1512%
1513% A description of each parameter follows:
1514%
1515% o mng_info: Specifies a pointer to a MngInfo structure.
1516%
1517% o image_info: the image info.
1518%
1519% o exception: return any errors or warnings in this structure.
1520%
1521*/
1522static Image *ReadOnePNGImage(MngInfo *mng_info,
1523 const ImageInfo *image_info, ExceptionInfo *exception)
1524{
1525 /* Read one PNG image */
1526
glennrpcc95c3f2011-04-18 16:46:48 +00001527 /* To do: Read the tIME chunk into the date:modify property */
1528 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1529
cristy3ed852e2009-09-05 21:47:34 +00001530 Image
1531 *image;
1532
1533 int
glennrp4eb39312011-03-30 21:34:55 +00001534 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001535 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001536 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001537 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001538 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001539 pass,
1540 ping_bit_depth,
1541 ping_color_type,
1542 ping_interlace_method,
1543 ping_compression_method,
1544 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001545 ping_num_trans,
1546 unit_type;
1547
1548 double
1549 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001550
glennrpa6a06632011-01-19 15:15:34 +00001551 LongPixelPacket
1552 transparent_color;
1553
cristy3ed852e2009-09-05 21:47:34 +00001554 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00001555 logging,
cristy3ed852e2009-09-05 21:47:34 +00001556 status;
1557
glennrpfaa852b2010-03-30 12:17:00 +00001558 png_bytep
1559 ping_trans_alpha;
1560
1561 png_color_16p
1562 ping_background,
1563 ping_trans_color;
1564
cristy3ed852e2009-09-05 21:47:34 +00001565 png_info
1566 *end_info,
1567 *ping_info;
1568
1569 png_struct
1570 *ping;
1571
1572 png_textp
1573 text;
1574
glennrpfaa852b2010-03-30 12:17:00 +00001575 png_uint_32
1576 ping_height,
1577 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00001578 ping_rowbytes,
1579 x_resolution,
1580 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 QuantumInfo
1583 *quantum_info;
1584
1585 unsigned char
glennrpcf002022011-01-30 02:38:15 +00001586 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00001587
cristybb503372010-05-27 20:51:26 +00001588 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001589 y;
1590
1591 register unsigned char
1592 *p;
1593
1594 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00001595 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00001596
cristybb503372010-05-27 20:51:26 +00001597 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001598 i,
1599 x;
1600
1601 register PixelPacket
1602 *q;
1603
1604 size_t
glennrp39992b42010-11-14 00:03:43 +00001605 length,
cristy3ed852e2009-09-05 21:47:34 +00001606 row_offset;
1607
cristyeb3b22a2011-03-31 20:16:11 +00001608 ssize_t
1609 j;
1610
cristy3ed852e2009-09-05 21:47:34 +00001611#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1612 png_byte unused_chunks[]=
1613 {
1614 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1615 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1616 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1617 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1618 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1619 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1620 };
1621#endif
1622
1623 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001624 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00001625
1626#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001627 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001628#endif
1629
glennrp25c1e2b2010-03-25 01:39:56 +00001630#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00001631 if (image_info->verbose)
1632 printf("Your PNG library (libpng-%s) is rather old.\n",
1633 PNG_LIBPNG_VER_STRING);
1634#endif
1635
glennrp61b4c952009-11-10 20:40:41 +00001636#if (PNG_LIBPNG_VER >= 10400)
1637# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1638 if (image_info->verbose)
1639 {
1640 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1641 PNG_LIBPNG_VER_STRING);
1642 printf("Please update it.\n");
1643 }
1644# endif
1645#endif
1646
1647
cristyed552522009-10-16 14:04:35 +00001648 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00001649 image=mng_info->image;
1650
glennrpa6a06632011-01-19 15:15:34 +00001651 if (logging != MagickFalse)
1652 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1653 " image->matte=%d",(int) image->matte);
1654
glennrp0e319732011-01-25 21:53:13 +00001655 /* Set to an out-of-range color unless tRNS chunk is present */
1656 transparent_color.red=65537;
1657 transparent_color.green=65537;
1658 transparent_color.blue=65537;
1659 transparent_color.opacity=65537;
1660
glennrpcb395ac2011-03-30 19:50:23 +00001661 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00001662 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00001663 num_raw_profiles = 0;
1664
cristy3ed852e2009-09-05 21:47:34 +00001665 /*
1666 Allocate the PNG structures
1667 */
1668#ifdef PNG_USER_MEM_SUPPORTED
1669 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00001670 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1671 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00001672#else
1673 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00001674 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00001675#endif
1676 if (ping == (png_struct *) NULL)
1677 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00001678
cristy3ed852e2009-09-05 21:47:34 +00001679 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001680
cristy3ed852e2009-09-05 21:47:34 +00001681 if (ping_info == (png_info *) NULL)
1682 {
1683 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1684 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1685 }
glennrp0fe50b42010-11-16 03:52:51 +00001686
cristy3ed852e2009-09-05 21:47:34 +00001687 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001688
cristy3ed852e2009-09-05 21:47:34 +00001689 if (end_info == (png_info *) NULL)
1690 {
1691 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1692 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
glennrpcf002022011-01-30 02:38:15 +00001695 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00001696
glennrpfaa852b2010-03-30 12:17:00 +00001697 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00001698 {
1699 /*
1700 PNG image is corrupt.
1701 */
1702 png_destroy_read_struct(&ping,&ping_info,&end_info);
1703#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00001704 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001705#endif
1706 if (logging != MagickFalse)
1707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1708 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00001709
cristy3ed852e2009-09-05 21:47:34 +00001710 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00001711 {
1712 InheritException(exception,&image->exception);
1713 image->columns=0;
1714 }
glennrp0fe50b42010-11-16 03:52:51 +00001715
cristy3ed852e2009-09-05 21:47:34 +00001716 return(GetFirstImageInList(image));
1717 }
1718 /*
1719 Prepare PNG for reading.
1720 */
glennrpfaa852b2010-03-30 12:17:00 +00001721
cristy3ed852e2009-09-05 21:47:34 +00001722 mng_info->image_found++;
1723 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00001724
cristy3ed852e2009-09-05 21:47:34 +00001725 if (LocaleCompare(image_info->magick,"MNG") == 0)
1726 {
1727#if defined(PNG_MNG_FEATURES_SUPPORTED)
1728 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1729 png_set_read_fn(ping,image,png_get_data);
1730#else
1731#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1732 png_permit_empty_plte(ping,MagickTrue);
1733 png_set_read_fn(ping,image,png_get_data);
1734#else
1735 mng_info->image=image;
1736 mng_info->bytes_in_read_buffer=0;
1737 mng_info->found_empty_plte=MagickFalse;
1738 mng_info->have_saved_bkgd_index=MagickFalse;
1739 png_set_read_fn(ping,mng_info,mng_get_data);
1740#endif
1741#endif
1742 }
glennrp0fe50b42010-11-16 03:52:51 +00001743
cristy3ed852e2009-09-05 21:47:34 +00001744 else
1745 png_set_read_fn(ping,image,png_get_data);
1746
1747#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1748 /* Ignore unused chunks and all unknown chunks except for vpAg */
1749 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1750 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1751 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1752 (int)sizeof(unused_chunks)/5);
1753 /* Callback for other unknown chunks */
1754 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1755#endif
1756
glennrp991e92a2010-01-28 03:09:00 +00001757#if (PNG_LIBPNG_VER < 10400)
1758# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1759 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00001760 /* Disable thread-unsafe features of pnggccrd */
1761 if (png_access_version_number() >= 10200)
1762 {
1763 png_uint_32 mmx_disable_mask=0;
1764 png_uint_32 asm_flags;
1765
1766 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1767 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1768 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1769 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1770 asm_flags=png_get_asm_flags(ping);
1771 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1772 }
glennrp991e92a2010-01-28 03:09:00 +00001773# endif
cristy3ed852e2009-09-05 21:47:34 +00001774#endif
1775
1776 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00001777
1778 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1779 &ping_bit_depth,&ping_color_type,
1780 &ping_interlace_method,&ping_compression_method,
1781 &ping_filter_method);
1782
1783 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1784 &ping_trans_color);
1785
1786 (void) png_get_bKGD(ping, ping_info, &ping_background);
1787
1788 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00001789 {
glennrpfaa852b2010-03-30 12:17:00 +00001790 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1791 {
1792 png_set_packing(ping);
1793 ping_bit_depth = 8;
1794 }
cristy3ed852e2009-09-05 21:47:34 +00001795 }
glennrpfaa852b2010-03-30 12:17:00 +00001796
1797 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00001798 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00001799 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00001800 if (logging != MagickFalse)
1801 {
1802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001803 " PNG width: %.20g, height: %.20g",
1804 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00001805
cristy3ed852e2009-09-05 21:47:34 +00001806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1807 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001808 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00001809
cristy3ed852e2009-09-05 21:47:34 +00001810 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1811 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001812 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00001813
cristy3ed852e2009-09-05 21:47:34 +00001814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1815 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001816 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00001817 }
1818
glennrpfaa852b2010-03-30 12:17:00 +00001819#ifdef PNG_READ_iCCP_SUPPORTED
1820 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00001821 {
1822 int
1823 compression;
1824
glennrpe4017e32011-01-08 17:16:09 +00001825#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00001826 png_charp
glennrpe4017e32011-01-08 17:16:09 +00001827 info;
1828#else
1829 png_bytep
1830 info;
1831#endif
1832
1833 png_charp
cristy3ed852e2009-09-05 21:47:34 +00001834 name;
1835
1836 png_uint_32
1837 profile_length;
1838
1839 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1840 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00001841
cristy3ed852e2009-09-05 21:47:34 +00001842 if (profile_length != 0)
1843 {
1844 StringInfo
1845 *profile;
1846
1847 if (logging != MagickFalse)
1848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1849 " Reading PNG iCCP chunk.");
1850 profile=AcquireStringInfo(profile_length);
1851 SetStringInfoDatum(profile,(const unsigned char *) info);
1852 (void) SetImageProfile(image,"icc",profile);
1853 profile=DestroyStringInfo(profile);
1854 }
1855 }
1856#endif
1857#if defined(PNG_READ_sRGB_SUPPORTED)
1858 {
cristy3ed852e2009-09-05 21:47:34 +00001859 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00001860 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1861 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00001862
cristy3ed852e2009-09-05 21:47:34 +00001863 if (png_get_sRGB(ping,ping_info,&intent))
1864 {
glennrpcf002022011-01-30 02:38:15 +00001865 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1866 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00001867
cristy3ed852e2009-09-05 21:47:34 +00001868 if (logging != MagickFalse)
1869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00001870 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00001871 }
1872 }
1873#endif
1874 {
glennrpfaa852b2010-03-30 12:17:00 +00001875 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1876 if (mng_info->have_global_gama)
1877 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00001878
cristy3ed852e2009-09-05 21:47:34 +00001879 if (png_get_gAMA(ping,ping_info,&file_gamma))
1880 {
1881 image->gamma=(float) file_gamma;
1882 if (logging != MagickFalse)
1883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1884 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1885 }
1886 }
glennrpfaa852b2010-03-30 12:17:00 +00001887 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1888 {
1889 if (mng_info->have_global_chrm != MagickFalse)
1890 {
1891 (void) png_set_cHRM(ping,ping_info,
1892 mng_info->global_chrm.white_point.x,
1893 mng_info->global_chrm.white_point.y,
1894 mng_info->global_chrm.red_primary.x,
1895 mng_info->global_chrm.red_primary.y,
1896 mng_info->global_chrm.green_primary.x,
1897 mng_info->global_chrm.green_primary.y,
1898 mng_info->global_chrm.blue_primary.x,
1899 mng_info->global_chrm.blue_primary.y);
1900 }
1901 }
glennrp0fe50b42010-11-16 03:52:51 +00001902
glennrpfaa852b2010-03-30 12:17:00 +00001903 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00001904 {
1905 (void) png_get_cHRM(ping,ping_info,
1906 &image->chromaticity.white_point.x,
1907 &image->chromaticity.white_point.y,
1908 &image->chromaticity.red_primary.x,
1909 &image->chromaticity.red_primary.y,
1910 &image->chromaticity.green_primary.x,
1911 &image->chromaticity.green_primary.y,
1912 &image->chromaticity.blue_primary.x,
1913 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy3ed852e2009-09-05 21:47:34 +00001915 if (logging != MagickFalse)
1916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1917 " Reading PNG cHRM chunk.");
1918 }
glennrp0fe50b42010-11-16 03:52:51 +00001919
glennrpe610a072010-08-05 17:08:46 +00001920 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00001921 {
glennrpe610a072010-08-05 17:08:46 +00001922 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00001923 Magick_RenderingIntent_to_PNG_RenderingIntent
1924 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00001925 png_set_gAMA(ping,ping_info,0.45455f);
1926 png_set_cHRM(ping,ping_info,
1927 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1928 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00001929 }
cristy3ed852e2009-09-05 21:47:34 +00001930#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001931 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00001932 {
cristy905ef802011-02-23 00:29:18 +00001933 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1934 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00001935
cristy3ed852e2009-09-05 21:47:34 +00001936 if (logging != MagickFalse)
1937 if (image->page.x || image->page.y)
1938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001939 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1940 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00001941 }
1942#endif
1943#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00001944 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1945 {
1946 if (mng_info->have_global_phys)
1947 {
1948 png_set_pHYs(ping,ping_info,
1949 mng_info->global_x_pixels_per_unit,
1950 mng_info->global_y_pixels_per_unit,
1951 mng_info->global_phys_unit_type);
1952 }
1953 }
1954
1955 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00001956 {
cristy3ed852e2009-09-05 21:47:34 +00001957 /*
1958 Set image resolution.
1959 */
1960 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00001961 &unit_type);
1962 image->x_resolution=(double) x_resolution;
1963 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00001964
cristy3ed852e2009-09-05 21:47:34 +00001965 if (unit_type == PNG_RESOLUTION_METER)
1966 {
1967 image->units=PixelsPerCentimeterResolution;
1968 image->x_resolution=(double) x_resolution/100.0;
1969 image->y_resolution=(double) y_resolution/100.0;
1970 }
glennrp0fe50b42010-11-16 03:52:51 +00001971
cristy3ed852e2009-09-05 21:47:34 +00001972 if (logging != MagickFalse)
1973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001974 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1975 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00001976 }
cristy3ed852e2009-09-05 21:47:34 +00001977#endif
glennrp823b55c2011-03-14 18:46:46 +00001978
glennrpfaa852b2010-03-30 12:17:00 +00001979 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00001980 {
1981 int
1982 number_colors;
1983
1984 png_colorp
1985 palette;
1986
1987 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00001988
cristy3ed852e2009-09-05 21:47:34 +00001989 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00001990 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00001991 {
1992 if (mng_info->global_plte_length)
1993 {
1994 png_set_PLTE(ping,ping_info,mng_info->global_plte,
1995 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00001996
glennrpfaa852b2010-03-30 12:17:00 +00001997 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00001998 if (mng_info->global_trns_length)
1999 {
2000 if (mng_info->global_trns_length >
2001 mng_info->global_plte_length)
2002 (void) ThrowMagickException(&image->exception,
2003 GetMagickModule(),CoderError,
2004 "global tRNS has more entries than global PLTE",
2005 "`%s'",image_info->filename);
2006 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2007 (int) mng_info->global_trns_length,NULL);
2008 }
glennrpbfd9e612011-04-22 14:02:20 +00002009#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002010 if (
2011#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2012 mng_info->have_saved_bkgd_index ||
2013#endif
glennrpfaa852b2010-03-30 12:17:00 +00002014 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002015 {
2016 png_color_16
2017 background;
2018
2019#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2020 if (mng_info->have_saved_bkgd_index)
2021 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002022#endif
glennrpfaa852b2010-03-30 12:17:00 +00002023 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2024 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002025
cristy3ed852e2009-09-05 21:47:34 +00002026 background.red=(png_uint_16)
2027 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002028
cristy3ed852e2009-09-05 21:47:34 +00002029 background.green=(png_uint_16)
2030 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002031
cristy3ed852e2009-09-05 21:47:34 +00002032 background.blue=(png_uint_16)
2033 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002034
cristy3ed852e2009-09-05 21:47:34 +00002035 png_set_bKGD(ping,ping_info,&background);
2036 }
2037#endif
2038 }
2039 else
2040 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2041 CoderError,"No global PLTE in file","`%s'",
2042 image_info->filename);
2043 }
2044 }
2045
glennrpbfd9e612011-04-22 14:02:20 +00002046#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002047 if (mng_info->have_global_bkgd &&
2048 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002049 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002050
glennrpfaa852b2010-03-30 12:17:00 +00002051 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002052 {
glennrpbfd9e612011-04-22 14:02:20 +00002053 unsigned int
2054 bkgd_scale;
2055
cristy3ed852e2009-09-05 21:47:34 +00002056 /*
2057 Set image background color.
2058 */
2059 if (logging != MagickFalse)
2060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2061 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002062
glennrpbfd9e612011-04-22 14:02:20 +00002063 /* Scale background components to 16-bit, then scale
2064 * to quantum depth
2065 */
2066 if (logging != MagickFalse)
2067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2068 " raw ping_background=(%d,%d,%d).",ping_background->red,
2069 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002070
glennrpbfd9e612011-04-22 14:02:20 +00002071 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002072
glennrpbfd9e612011-04-22 14:02:20 +00002073 if (ping_bit_depth == 1)
2074 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002075
glennrpbfd9e612011-04-22 14:02:20 +00002076 else if (ping_bit_depth == 2)
2077 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002078
glennrpbfd9e612011-04-22 14:02:20 +00002079 else if (ping_bit_depth == 4)
2080 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002081
glennrpbfd9e612011-04-22 14:02:20 +00002082 if (ping_bit_depth <= 8)
2083 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002084
glennrpbfd9e612011-04-22 14:02:20 +00002085 ping_background->red *= bkgd_scale;
2086 ping_background->green *= bkgd_scale;
2087 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002088
glennrpbfd9e612011-04-22 14:02:20 +00002089 if (logging != MagickFalse)
2090 {
glennrp2cbb4482010-06-02 04:37:24 +00002091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2092 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002093
glennrp2cbb4482010-06-02 04:37:24 +00002094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2095 " ping_background=(%d,%d,%d).",ping_background->red,
2096 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002097 }
glennrp2cbb4482010-06-02 04:37:24 +00002098
glennrpbfd9e612011-04-22 14:02:20 +00002099 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002100 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002101
glennrpbfd9e612011-04-22 14:02:20 +00002102 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002103 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002104
glennrpbfd9e612011-04-22 14:02:20 +00002105 image->background_color.blue=
2106 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002107
glennrpbfd9e612011-04-22 14:02:20 +00002108 image->background_color.opacity=OpaqueOpacity;
glennrp2cbb4482010-06-02 04:37:24 +00002109
glennrpbfd9e612011-04-22 14:02:20 +00002110 if (logging != MagickFalse)
2111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2112 " image->background_color=(%.20g,%.20g,%.20g).",
2113 (double) image->background_color.red,
2114 (double) image->background_color.green,
2115 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002116 }
glennrpbfd9e612011-04-22 14:02:20 +00002117#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002118
glennrpfaa852b2010-03-30 12:17:00 +00002119 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002120 {
2121 /*
glennrpa6a06632011-01-19 15:15:34 +00002122 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002123 */
2124 int
2125 max_sample;
2126
cristy35ef8242010-06-03 16:24:13 +00002127 size_t
2128 one=1;
2129
cristy3ed852e2009-09-05 21:47:34 +00002130 if (logging != MagickFalse)
2131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2132 " Reading PNG tRNS chunk.");
2133
cristyf9cca6a2010-06-04 23:49:28 +00002134 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002135
glennrpfaa852b2010-03-30 12:17:00 +00002136 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2137 (int)ping_trans_color->gray > max_sample) ||
2138 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2139 ((int)ping_trans_color->red > max_sample ||
2140 (int)ping_trans_color->green > max_sample ||
2141 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002142 {
2143 if (logging != MagickFalse)
2144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2145 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002146 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002147 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002148 image->matte=MagickFalse;
2149 }
2150 else
2151 {
glennrpa6a06632011-01-19 15:15:34 +00002152 int
2153 scale_to_short;
2154
2155 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2156
2157 /* Scale transparent_color to short */
2158 transparent_color.red= scale_to_short*ping_trans_color->red;
2159 transparent_color.green= scale_to_short*ping_trans_color->green;
2160 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2161 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002162
glennrpfaa852b2010-03-30 12:17:00 +00002163 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002164 {
glennrp0f111982010-07-07 20:18:33 +00002165 if (logging != MagickFalse)
2166 {
2167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2168 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002169
glennrp0f111982010-07-07 20:18:33 +00002170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2171 " scaled graylevel is %d.",transparent_color.opacity);
2172 }
cristy3ed852e2009-09-05 21:47:34 +00002173 transparent_color.red=transparent_color.opacity;
2174 transparent_color.green=transparent_color.opacity;
2175 transparent_color.blue=transparent_color.opacity;
2176 }
2177 }
2178 }
2179#if defined(PNG_READ_sBIT_SUPPORTED)
2180 if (mng_info->have_global_sbit)
2181 {
glennrpfaa852b2010-03-30 12:17:00 +00002182 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002183 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2184 }
2185#endif
2186 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002187
cristy3ed852e2009-09-05 21:47:34 +00002188 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002189
2190 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2191
cristy3ed852e2009-09-05 21:47:34 +00002192 /*
2193 Initialize image structure.
2194 */
2195 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002196 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002197 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002198 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002199 if (mng_info->mng_type == 0)
2200 {
glennrpfaa852b2010-03-30 12:17:00 +00002201 mng_info->mng_width=ping_width;
2202 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002203 mng_info->frame=mng_info->image_box;
2204 mng_info->clip=mng_info->image_box;
2205 }
glennrp0fe50b42010-11-16 03:52:51 +00002206
cristy3ed852e2009-09-05 21:47:34 +00002207 else
2208 {
2209 image->page.y=mng_info->y_off[mng_info->object_id];
2210 }
glennrp0fe50b42010-11-16 03:52:51 +00002211
cristy3ed852e2009-09-05 21:47:34 +00002212 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002213 image->columns=ping_width;
2214 image->rows=ping_height;
2215 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002216 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002217 {
cristybefe4d22010-06-07 01:18:58 +00002218 size_t
2219 one;
2220
cristy3ed852e2009-09-05 21:47:34 +00002221 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002222 one=1;
2223 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002224#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2225 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002226 image->colors=256;
2227#else
2228 if (image->colors > 65536L)
2229 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002230#endif
glennrpfaa852b2010-03-30 12:17:00 +00002231 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002232 {
2233 int
2234 number_colors;
2235
2236 png_colorp
2237 palette;
2238
2239 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002240 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002241
cristy3ed852e2009-09-05 21:47:34 +00002242 if (logging != MagickFalse)
2243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2244 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2245 }
2246 }
2247
2248 if (image->storage_class == PseudoClass)
2249 {
2250 /*
2251 Initialize image colormap.
2252 */
2253 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2254 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002255
glennrpfaa852b2010-03-30 12:17:00 +00002256 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002257 {
2258 int
2259 number_colors;
2260
2261 png_colorp
2262 palette;
2263
2264 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002265
glennrp6af6cf12011-04-22 13:05:16 +00002266 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002267 {
2268 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2269 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2270 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2271 }
glennrp6af6cf12011-04-22 13:05:16 +00002272
glennrp67b9c1a2011-04-22 18:47:36 +00002273 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002274 {
2275 image->colormap[i].red=0;
2276 image->colormap[i].green=0;
2277 image->colormap[i].blue=0;
2278 }
cristy3ed852e2009-09-05 21:47:34 +00002279 }
glennrp0fe50b42010-11-16 03:52:51 +00002280
cristy3ed852e2009-09-05 21:47:34 +00002281 else
2282 {
cristybb503372010-05-27 20:51:26 +00002283 size_t
cristy3ed852e2009-09-05 21:47:34 +00002284 scale;
2285
glennrpfaa852b2010-03-30 12:17:00 +00002286 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002287
cristy3ed852e2009-09-05 21:47:34 +00002288 if (scale < 1)
2289 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristybb503372010-05-27 20:51:26 +00002291 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002292 {
2293 image->colormap[i].red=(Quantum) (i*scale);
2294 image->colormap[i].green=(Quantum) (i*scale);
2295 image->colormap[i].blue=(Quantum) (i*scale);
2296 }
2297 }
2298 }
glennrp147bc912011-03-30 18:47:21 +00002299
glennrpcb395ac2011-03-30 19:50:23 +00002300 /* Set some properties for reporting by "identify" */
2301 {
glennrp147bc912011-03-30 18:47:21 +00002302 char
2303 msg[MaxTextExtent];
2304
2305 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2306 ping_interlace_method in value */
2307
glennrp7cdb11c2011-03-31 18:17:25 +00002308 (void) FormatMagickString(msg,MaxTextExtent,
2309 "%d, %d",(int) ping_width, (int) ping_height);
2310 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002311
2312 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2313 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2314
2315 (void) FormatMagickString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2316 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2317
2318 (void) FormatMagickString(msg,MaxTextExtent,"%d",
2319 (int) ping_interlace_method);
2320 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002321 }
glennrp147bc912011-03-30 18:47:21 +00002322
cristy3ed852e2009-09-05 21:47:34 +00002323 /*
2324 Read image scanlines.
2325 */
2326 if (image->delay != 0)
2327 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002328
glennrp0ca69b12010-07-26 01:57:52 +00002329 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002330 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2331 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002332 {
2333 if (logging != MagickFalse)
2334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002335 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002336 mng_info->scenes_found-1);
2337 png_destroy_read_struct(&ping,&ping_info,&end_info);
2338#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002339 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002340#endif
2341 if (logging != MagickFalse)
2342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2343 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002344
cristy3ed852e2009-09-05 21:47:34 +00002345 return(image);
2346 }
glennrp0fe50b42010-11-16 03:52:51 +00002347
cristy3ed852e2009-09-05 21:47:34 +00002348 if (logging != MagickFalse)
2349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2350 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002353 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2354 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002355
cristy3ed852e2009-09-05 21:47:34 +00002356 else
glennrpcf002022011-01-30 02:38:15 +00002357 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2358 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002359
glennrpcf002022011-01-30 02:38:15 +00002360 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002361 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002362
cristy3ed852e2009-09-05 21:47:34 +00002363 if (logging != MagickFalse)
2364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2365 " Converting PNG pixels to pixel packets");
2366 /*
2367 Convert PNG pixels to pixel packets.
2368 */
glennrpfaa852b2010-03-30 12:17:00 +00002369 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002370 {
2371 /*
2372 PNG image is corrupt.
2373 */
2374 png_destroy_read_struct(&ping,&ping_info,&end_info);
2375#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002376 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002377#endif
2378 if (quantum_info != (QuantumInfo *) NULL)
2379 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002380
glennrpcf002022011-01-30 02:38:15 +00002381 if (ping_pixels != (unsigned char *) NULL)
2382 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002383
cristy3ed852e2009-09-05 21:47:34 +00002384 if (logging != MagickFalse)
2385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2386 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002387
cristy3ed852e2009-09-05 21:47:34 +00002388 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002389 {
2390 InheritException(exception,&image->exception);
2391 image->columns=0;
2392 }
glennrp0fe50b42010-11-16 03:52:51 +00002393
cristy3ed852e2009-09-05 21:47:34 +00002394 return(GetFirstImageInList(image));
2395 }
glennrp0fe50b42010-11-16 03:52:51 +00002396
cristyed552522009-10-16 14:04:35 +00002397 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002398
cristyed552522009-10-16 14:04:35 +00002399 if (quantum_info == (QuantumInfo *) NULL)
2400 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002401
glennrpc8cbc5d2011-01-01 00:12:34 +00002402 {
2403
2404 MagickBooleanType
2405 found_transparent_pixel;
2406
2407 found_transparent_pixel=MagickFalse;
2408
cristy3ed852e2009-09-05 21:47:34 +00002409 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002410 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002411 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002412 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002413 /*
2414 Convert image to DirectClass pixel packets.
2415 */
glennrp67b9c1a2011-04-22 18:47:36 +00002416#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2417 int
2418 depth;
2419
2420 depth=(ssize_t) ping_bit_depth;
2421#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002422 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2423 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2424 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2425 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002426
glennrpc8cbc5d2011-01-01 00:12:34 +00002427 for (y=0; y < (ssize_t) image->rows; y++)
2428 {
2429 if (num_passes > 1)
2430 row_offset=ping_rowbytes*y;
2431
2432 else
2433 row_offset=0;
2434
glennrpcf002022011-01-30 02:38:15 +00002435 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002436 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2437
2438 if (q == (PixelPacket *) NULL)
2439 break;
2440
glennrpc8cbc5d2011-01-01 00:12:34 +00002441 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2442 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002443 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002444
2445 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2446 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002447 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002448
2449 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2450 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002451 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002452
2453 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2454 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002455 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002456
2457 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2458 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002459 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002460
glennrpc8cbc5d2011-01-01 00:12:34 +00002461 if (found_transparent_pixel == MagickFalse)
2462 {
2463 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002464 if (y== 0 && logging != MagickFalse)
2465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2466 " Looking for cheap transparent pixel");
2467
glennrpc8cbc5d2011-01-01 00:12:34 +00002468 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2469 {
glennrp5aa37f62011-01-02 03:07:57 +00002470 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2471 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
glennrp8b698592011-04-26 03:38:21 +00002472 (GetOpacityPixelComponent(q) != OpaqueOpacity))
glennrpc8cbc5d2011-01-01 00:12:34 +00002473 {
glennrpa6a06632011-01-19 15:15:34 +00002474 if (logging != MagickFalse)
2475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2476 " ...got one.");
2477
glennrpc8cbc5d2011-01-01 00:12:34 +00002478 found_transparent_pixel = MagickTrue;
2479 break;
2480 }
glennrp4f25bd02011-01-01 18:51:28 +00002481 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2482 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp8b698592011-04-26 03:38:21 +00002483 (ScaleQuantumToShort(GetRedPixelComponent(q))
2484 == transparent_color.red &&
2485 ScaleQuantumToShort(GetGreenPixelComponent(q))
2486 == transparent_color.green &&
2487 ScaleQuantumToShort(GetBluePixelComponent(q))
2488 == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002489 {
glennrpa6a06632011-01-19 15:15:34 +00002490 if (logging != MagickFalse)
2491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2492 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002493 found_transparent_pixel = MagickTrue;
2494 break;
2495 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002496 q++;
2497 }
2498 }
2499
2500 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2501 {
2502 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2503 image->rows);
2504
2505 if (status == MagickFalse)
2506 break;
2507 }
2508 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2509 break;
2510 }
2511
2512 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2513 {
2514 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002515 if (status == MagickFalse)
2516 break;
2517 }
cristy3ed852e2009-09-05 21:47:34 +00002518 }
cristy3ed852e2009-09-05 21:47:34 +00002519 }
glennrp0fe50b42010-11-16 03:52:51 +00002520
cristy3ed852e2009-09-05 21:47:34 +00002521 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002522
cristy3ed852e2009-09-05 21:47:34 +00002523 for (pass=0; pass < num_passes; pass++)
2524 {
2525 Quantum
2526 *quantum_scanline;
2527
2528 register Quantum
2529 *r;
2530
2531 /*
2532 Convert grayscale image to PseudoClass pixel packets.
2533 */
glennrpfaa852b2010-03-30 12:17:00 +00002534 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002535 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002536
cristy3ed852e2009-09-05 21:47:34 +00002537 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2538 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002539
cristy3ed852e2009-09-05 21:47:34 +00002540 if (quantum_scanline == (Quantum *) NULL)
2541 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002542
cristybb503372010-05-27 20:51:26 +00002543 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002544 {
2545 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002546 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002547
cristy3ed852e2009-09-05 21:47:34 +00002548 else
2549 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00002550
glennrpcf002022011-01-30 02:38:15 +00002551 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002552 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00002553
cristy3ed852e2009-09-05 21:47:34 +00002554 if (q == (PixelPacket *) NULL)
2555 break;
glennrp0fe50b42010-11-16 03:52:51 +00002556
cristy5c6f7892010-05-05 22:53:29 +00002557 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00002558 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00002559 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00002560
glennrpfaa852b2010-03-30 12:17:00 +00002561 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00002562 {
2563 case 1:
2564 {
cristybb503372010-05-27 20:51:26 +00002565 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002566 bit;
2567
cristybb503372010-05-27 20:51:26 +00002568 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00002569 {
2570 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002571 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002572 p++;
2573 }
glennrp0fe50b42010-11-16 03:52:51 +00002574
cristy3ed852e2009-09-05 21:47:34 +00002575 if ((image->columns % 8) != 0)
2576 {
cristybb503372010-05-27 20:51:26 +00002577 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00002578 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00002579 }
glennrp0fe50b42010-11-16 03:52:51 +00002580
cristy3ed852e2009-09-05 21:47:34 +00002581 break;
2582 }
glennrp47b9dd52010-11-24 18:12:06 +00002583
cristy3ed852e2009-09-05 21:47:34 +00002584 case 2:
2585 {
cristybb503372010-05-27 20:51:26 +00002586 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002587 {
glennrpa18d5bc2011-04-23 14:51:34 +00002588 *r++=(*p >> 6) & 0x03;
2589 *r++=(*p >> 4) & 0x03;
2590 *r++=(*p >> 2) & 0x03;
2591 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00002592 }
glennrp0fe50b42010-11-16 03:52:51 +00002593
cristy3ed852e2009-09-05 21:47:34 +00002594 if ((image->columns % 4) != 0)
2595 {
cristybb503372010-05-27 20:51:26 +00002596 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00002597 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00002598 }
glennrp0fe50b42010-11-16 03:52:51 +00002599
cristy3ed852e2009-09-05 21:47:34 +00002600 break;
2601 }
glennrp47b9dd52010-11-24 18:12:06 +00002602
cristy3ed852e2009-09-05 21:47:34 +00002603 case 4:
2604 {
cristybb503372010-05-27 20:51:26 +00002605 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00002606 {
glennrpa18d5bc2011-04-23 14:51:34 +00002607 *r++=(*p >> 4) & 0x0f;
2608 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00002609 }
glennrp0fe50b42010-11-16 03:52:51 +00002610
cristy3ed852e2009-09-05 21:47:34 +00002611 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00002612 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00002613
cristy3ed852e2009-09-05 21:47:34 +00002614 break;
2615 }
glennrp47b9dd52010-11-24 18:12:06 +00002616
cristy3ed852e2009-09-05 21:47:34 +00002617 case 8:
2618 {
glennrpfaa852b2010-03-30 12:17:00 +00002619 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00002620 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002621 {
glennrpa18d5bc2011-04-23 14:51:34 +00002622 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00002623 /* In image.h, OpaqueOpacity is 0
2624 * TransparentOpacity is QuantumRange
2625 * In a PNG datastream, Opaque is QuantumRange
2626 * and Transparent is 0.
2627 */
glennrp8b698592011-04-26 03:38:21 +00002628 SetOpacityPixelComponent(q,
2629 ScaleCharToQuantum((unsigned char) (255-(*p++))));
2630 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00002631 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002632 q++;
2633 }
glennrp0fe50b42010-11-16 03:52:51 +00002634
cristy3ed852e2009-09-05 21:47:34 +00002635 else
cristybb503372010-05-27 20:51:26 +00002636 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002637 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00002638
cristy3ed852e2009-09-05 21:47:34 +00002639 break;
2640 }
glennrp47b9dd52010-11-24 18:12:06 +00002641
cristy3ed852e2009-09-05 21:47:34 +00002642 case 16:
2643 {
cristybb503372010-05-27 20:51:26 +00002644 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00002645 {
glennrp58f77c72011-04-23 14:09:09 +00002646#if (MAGICKCORE_QUANTUM_DEPTH == 16)
2647 size_t
2648 quantum;
2649
2650 if (image->colors > 256)
2651 *r=((*p++) << 8);
2652
2653 else
2654 *r=0;
2655
2656 quantum=(*r);
2657 quantum|=(*p++);
2658 *r=(Quantum) quantum;
2659 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00002660
2661 if (ping_color_type == 4)
2662 {
glennrp58f77c72011-04-23 14:09:09 +00002663 quantum=((*p++) << 8);
2664 quantum|=(*p++);
glennrp8b698592011-04-26 03:38:21 +00002665 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-quantum));
2666 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002667 found_transparent_pixel = MagickTrue;
2668 q++;
2669 }
2670#else
2671#if (MAGICKCORE_QUANTUM_DEPTH == 32)
2672 size_t
2673 quantum;
2674
2675 if (image->colors > 256)
2676 *r=((*p++) << 8);
2677
2678 else
2679 *r=0;
2680
2681 quantum=(*r);
2682 quantum|=(*p++);
2683 *r=quantum;
2684 r++;
2685
2686 if (ping_color_type == 4)
2687 {
glennrp8b698592011-04-26 03:38:21 +00002688 quantum=(*p << 8) | *(p+1);
2689 quantum*=65537L;
2690 SetOpacityPixelComponent(q,
2691 (Quantum) GetAlphaPixelComponent(q));
2692 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00002693 found_transparent_pixel = MagickTrue;
2694 p+=2;
2695 q++;
2696 }
2697
2698#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2699 *r++=(*p++);
2700 p++; /* strip low byte */
2701
2702 if (ping_color_type == 4)
2703 {
glennrp8b698592011-04-26 03:38:21 +00002704 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-(*p++)));
2705 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp9d0ea4d2011-04-22 18:35:57 +00002706 found_transparent_pixel = MagickTrue;
2707 p++;
2708 q++;
2709 }
cristy3ed852e2009-09-05 21:47:34 +00002710#endif
glennrp58f77c72011-04-23 14:09:09 +00002711#endif
glennrpa18d5bc2011-04-23 14:51:34 +00002712 }
glennrp47b9dd52010-11-24 18:12:06 +00002713
cristy3ed852e2009-09-05 21:47:34 +00002714 break;
2715 }
glennrp47b9dd52010-11-24 18:12:06 +00002716
cristy3ed852e2009-09-05 21:47:34 +00002717 default:
2718 break;
2719 }
glennrp3faa9a32011-04-23 14:00:25 +00002720
cristy3ed852e2009-09-05 21:47:34 +00002721 /*
2722 Transfer image scanline.
2723 */
2724 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00002725
cristybb503372010-05-27 20:51:26 +00002726 for (x=0; x < (ssize_t) image->columns; x++)
cristy80ac8b92010-05-08 13:36:57 +00002727 indexes[x]=(IndexPacket) (*r++);
glennrp0fe50b42010-11-16 03:52:51 +00002728
cristy3ed852e2009-09-05 21:47:34 +00002729 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2730 break;
glennrp0fe50b42010-11-16 03:52:51 +00002731
cristy7a287bf2010-02-14 02:18:09 +00002732 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2733 {
cristycee97112010-05-28 00:44:52 +00002734 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2735 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00002736
cristy7a287bf2010-02-14 02:18:09 +00002737 if (status == MagickFalse)
2738 break;
2739 }
cristy3ed852e2009-09-05 21:47:34 +00002740 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002741
cristy7a287bf2010-02-14 02:18:09 +00002742 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002743 {
2744 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00002745
cristy3ed852e2009-09-05 21:47:34 +00002746 if (status == MagickFalse)
2747 break;
2748 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002749
cristy3ed852e2009-09-05 21:47:34 +00002750 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2751 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002752
2753 image->matte=found_transparent_pixel;
2754
2755 if (logging != MagickFalse)
2756 {
2757 if (found_transparent_pixel != MagickFalse)
2758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2759 " Found transparent pixel");
2760 else
glennrp5aa37f62011-01-02 03:07:57 +00002761 {
2762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2763 " No transparent pixel was found");
glennrpa6a06632011-01-19 15:15:34 +00002764
glennrp5aa37f62011-01-02 03:07:57 +00002765 ping_color_type&=0x03;
2766 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002767 }
2768 }
2769
cristyb32b90a2009-09-07 21:45:48 +00002770 if (quantum_info != (QuantumInfo *) NULL)
2771 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002772
cristy5c6f7892010-05-05 22:53:29 +00002773 if (image->storage_class == PseudoClass)
2774 {
cristyaeb2cbc2010-05-07 13:28:58 +00002775 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002776 matte;
2777
2778 matte=image->matte;
2779 image->matte=MagickFalse;
2780 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002781 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002782 }
glennrp47b9dd52010-11-24 18:12:06 +00002783
glennrp4eb39312011-03-30 21:34:55 +00002784 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00002785
2786 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002787 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002788 {
2789 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00002790 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00002791 image->colors=2;
2792 (void) SetImageBackgroundColor(image);
2793#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002794 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002795#endif
2796 if (logging != MagickFalse)
2797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2798 " exit ReadOnePNGImage() early.");
2799 return(image);
2800 }
glennrp47b9dd52010-11-24 18:12:06 +00002801
glennrpfaa852b2010-03-30 12:17:00 +00002802 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002803 {
2804 ClassType
2805 storage_class;
2806
2807 /*
2808 Image has a transparent background.
2809 */
2810 storage_class=image->storage_class;
2811 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002812
glennrp3c218112010-11-27 15:31:26 +00002813/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00002814
glennrp0fe50b42010-11-16 03:52:51 +00002815 if (storage_class == PseudoClass)
2816 {
2817 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002818 {
glennrp0fe50b42010-11-16 03:52:51 +00002819 for (x=0; x < ping_num_trans; x++)
2820 {
2821 image->colormap[x].opacity =
2822 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2823 }
glennrpc11cf6a2010-03-20 16:46:19 +00002824 }
glennrp47b9dd52010-11-24 18:12:06 +00002825
glennrp0fe50b42010-11-16 03:52:51 +00002826 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2827 {
2828 for (x=0; x < (int) image->colors; x++)
2829 {
2830 if (ScaleQuantumToShort(image->colormap[x].red) ==
2831 transparent_color.opacity)
2832 {
2833 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2834 }
2835 }
2836 }
2837 (void) SyncImage(image);
2838 }
glennrp47b9dd52010-11-24 18:12:06 +00002839
glennrpa6a06632011-01-19 15:15:34 +00002840#if 1 /* Should have already been done above, but glennrp problem P10
2841 * needs this.
2842 */
glennrp0fe50b42010-11-16 03:52:51 +00002843 else
2844 {
2845 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002846 {
glennrp0fe50b42010-11-16 03:52:51 +00002847 image->storage_class=storage_class;
2848 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2849
2850 if (q == (PixelPacket *) NULL)
2851 break;
2852
2853 indexes=GetAuthenticIndexQueue(image);
2854
glennrpa6a06632011-01-19 15:15:34 +00002855 /* Caution: on a Q8 build, this does not distinguish between
2856 * 16-bit colors that differ only in the low byte
2857 */
glennrp0fe50b42010-11-16 03:52:51 +00002858 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2859 {
glennrp8b698592011-04-26 03:38:21 +00002860 if (ScaleQuantumToShort(GetRedPixelComponent(q))
2861 == transparent_color.red &&
2862 ScaleQuantumToShort(GetGreenPixelComponent(q))
2863 == transparent_color.green &&
2864 ScaleQuantumToShort(GetBluePixelComponent(q))
2865 == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00002866 {
glennrp8b698592011-04-26 03:38:21 +00002867 SetOpacityPixelComponent(q,TransparentOpacity);
glennrp4f25bd02011-01-01 18:51:28 +00002868 }
glennrp0fe50b42010-11-16 03:52:51 +00002869
glennrp67b9c1a2011-04-22 18:47:36 +00002870#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00002871 else
glennrp4f25bd02011-01-01 18:51:28 +00002872 {
2873 q->opacity=(Quantum) OpaqueOpacity;
2874 }
glennrpa6a06632011-01-19 15:15:34 +00002875#endif
glennrp0fe50b42010-11-16 03:52:51 +00002876
2877 q++;
2878 }
2879
2880 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2881 break;
glennrpc11cf6a2010-03-20 16:46:19 +00002882 }
glennrp0fe50b42010-11-16 03:52:51 +00002883 }
glennrpa6a06632011-01-19 15:15:34 +00002884#endif
glennrpc11cf6a2010-03-20 16:46:19 +00002885
cristy3ed852e2009-09-05 21:47:34 +00002886 image->storage_class=DirectClass;
2887 }
glennrp3c218112010-11-27 15:31:26 +00002888
cristyb40fc462010-08-08 00:49:49 +00002889 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2890 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2891 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00002892
cristyeb3b22a2011-03-31 20:16:11 +00002893 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00002894 {
2895 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00002896 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
2897 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00002898 else
glennrpa0ed0092011-04-18 16:36:29 +00002899 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
2900 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002901
glennrp4eb39312011-03-30 21:34:55 +00002902 if (status != MagickFalse)
2903 for (i=0; i < (ssize_t) num_text; i++)
2904 {
2905 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00002906
glennrp4eb39312011-03-30 21:34:55 +00002907 if (logging != MagickFalse)
2908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2909 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00002910
glennrp4eb39312011-03-30 21:34:55 +00002911 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00002912 {
glennrp4eb39312011-03-30 21:34:55 +00002913 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2914 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00002915 }
glennrp0fe50b42010-11-16 03:52:51 +00002916
glennrp4eb39312011-03-30 21:34:55 +00002917 else
2918 {
2919 char
2920 *value;
2921
2922 length=text[i].text_length;
2923 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2924 sizeof(*value));
2925 if (value == (char *) NULL)
2926 {
2927 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2928 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2929 image->filename);
2930 break;
2931 }
2932 *value='\0';
2933 (void) ConcatenateMagickString(value,text[i].text,length+2);
2934
2935 /* Don't save "density" or "units" property if we have a pHYs
2936 * chunk
2937 */
2938 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2939 (LocaleCompare(text[i].key,"density") != 0 &&
2940 LocaleCompare(text[i].key,"units") != 0))
2941 (void) SetImageProperty(image,text[i].key,value);
2942
2943 if (logging != MagickFalse)
2944 {
2945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2946 " length: %lu",(unsigned long) length);
2947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2948 " Keyword: %s",text[i].key);
2949 }
2950
2951 value=DestroyString(value);
2952 }
2953 }
2954 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00002955 }
glennrp3c218112010-11-27 15:31:26 +00002956
cristy3ed852e2009-09-05 21:47:34 +00002957#ifdef MNG_OBJECT_BUFFERS
2958 /*
2959 Store the object if necessary.
2960 */
2961 if (object_id && !mng_info->frozen[object_id])
2962 {
2963 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2964 {
2965 /*
2966 create a new object buffer.
2967 */
2968 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00002969 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00002970
cristy3ed852e2009-09-05 21:47:34 +00002971 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2972 {
2973 mng_info->ob[object_id]->image=(Image *) NULL;
2974 mng_info->ob[object_id]->reference_count=1;
2975 }
2976 }
glennrp47b9dd52010-11-24 18:12:06 +00002977
cristy3ed852e2009-09-05 21:47:34 +00002978 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2979 mng_info->ob[object_id]->frozen)
2980 {
2981 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2982 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2983 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2984 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00002985
cristy3ed852e2009-09-05 21:47:34 +00002986 if (mng_info->ob[object_id]->frozen)
2987 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2988 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2989 "`%s'",image->filename);
2990 }
glennrp0fe50b42010-11-16 03:52:51 +00002991
cristy3ed852e2009-09-05 21:47:34 +00002992 else
2993 {
cristy3ed852e2009-09-05 21:47:34 +00002994
2995 if (mng_info->ob[object_id]->image != (Image *) NULL)
2996 mng_info->ob[object_id]->image=DestroyImage
2997 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00002998
cristy3ed852e2009-09-05 21:47:34 +00002999 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3000 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003001
cristy3ed852e2009-09-05 21:47:34 +00003002 if (mng_info->ob[object_id]->image != (Image *) NULL)
3003 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003004
cristy3ed852e2009-09-05 21:47:34 +00003005 else
3006 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3007 ResourceLimitError,"Cloning image for object buffer failed",
3008 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003009
glennrpfaa852b2010-03-30 12:17:00 +00003010 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003011 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003012
glennrpfaa852b2010-03-30 12:17:00 +00003013 mng_info->ob[object_id]->width=ping_width;
3014 mng_info->ob[object_id]->height=ping_height;
3015 mng_info->ob[object_id]->color_type=ping_color_type;
3016 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3017 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3018 mng_info->ob[object_id]->compression_method=
3019 ping_compression_method;
3020 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003021
glennrpfaa852b2010-03-30 12:17:00 +00003022 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003023 {
3024 int
3025 number_colors;
3026
3027 png_colorp
3028 plte;
3029
3030 /*
3031 Copy the PLTE to the object buffer.
3032 */
3033 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3034 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003035
cristy3ed852e2009-09-05 21:47:34 +00003036 for (i=0; i < number_colors; i++)
3037 {
3038 mng_info->ob[object_id]->plte[i]=plte[i];
3039 }
3040 }
glennrp47b9dd52010-11-24 18:12:06 +00003041
cristy3ed852e2009-09-05 21:47:34 +00003042 else
3043 mng_info->ob[object_id]->plte_length=0;
3044 }
3045 }
3046#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003047
3048 /* Set image->matte to MagickTrue if the input colortype supports
3049 * alpha or if a valid tRNS chunk is present, no matter whether there
3050 * is actual transparency present.
3051 */
3052 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3053 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3054 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3055 MagickTrue : MagickFalse;
3056
glennrpcb395ac2011-03-30 19:50:23 +00003057 /* Set more properties for identify to retrieve */
3058 {
3059 char
3060 msg[MaxTextExtent];
3061
glennrp4eb39312011-03-30 21:34:55 +00003062 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003063 {
3064 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3065 (void) FormatMagickString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003066 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003067 (void) SetImageProperty(image,"PNG:text ",msg);
3068 }
3069
3070 if (num_raw_profiles != 0)
3071 {
3072 (void) FormatMagickString(msg,MaxTextExtent,
3073 "%d were found", num_raw_profiles);
3074 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3075 }
3076
glennrpcb395ac2011-03-30 19:50:23 +00003077 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003078 {
3079 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3080 "chunk was found (see Chromaticity, above)");
3081 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3082 }
glennrpcb395ac2011-03-30 19:50:23 +00003083
3084 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003085 {
3086 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3087 "chunk was found (see Background color, above)");
3088 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3089 }
3090
3091 (void) FormatMagickString(msg,MaxTextExtent,"%s",
3092 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003093
3094 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3095 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3096
glennrpcb395ac2011-03-30 19:50:23 +00003097 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3098 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003099
3100#if defined(PNG_sRGB_SUPPORTED)
3101 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3102 {
glennrp07523c72011-03-31 18:12:10 +00003103 (void) FormatMagickString(msg,MaxTextExtent,
3104 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003105 (int) intent);
3106 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3107 }
3108#endif
3109
3110 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3111 {
glennrp07523c72011-03-31 18:12:10 +00003112 (void) FormatMagickString(msg,MaxTextExtent,
3113 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003114 file_gamma);
3115 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3116 }
3117
3118#if defined(PNG_pHYs_SUPPORTED)
3119 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3120 {
glennrp07523c72011-03-31 18:12:10 +00003121 (void) FormatMagickString(msg,MaxTextExtent,
3122 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003123 (double) x_resolution,(double) y_resolution, unit_type);
3124 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3125 }
3126#endif
3127
3128#if defined(PNG_oFFs_SUPPORTED)
3129 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3130 {
3131 (void) FormatMagickString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3132 (double) image->page.x,(double) image->page.y);
3133 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3134 }
3135#endif
3136
glennrp07523c72011-03-31 18:12:10 +00003137 if ((image->page.width != 0 && image->page.width != image->columns) ||
3138 (image->page.height != 0 && image->page.height != image->rows))
3139 {
3140 (void) FormatMagickString(msg,MaxTextExtent,
3141 "width=%.20g, height=%.20g",
3142 (double) image->page.width,(double) image->page.height);
3143 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3144 }
glennrpcb395ac2011-03-30 19:50:23 +00003145 }
3146
cristy3ed852e2009-09-05 21:47:34 +00003147 /*
3148 Relinquish resources.
3149 */
3150 png_destroy_read_struct(&ping,&ping_info,&end_info);
3151
glennrpcf002022011-01-30 02:38:15 +00003152 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003153#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003154 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003155#endif
3156
3157 if (logging != MagickFalse)
3158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3159 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003160
cristy3ed852e2009-09-05 21:47:34 +00003161 return(image);
3162
3163/* end of reading one PNG image */
3164}
3165
3166static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3167{
3168 Image
3169 *image,
3170 *previous;
3171
3172 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003173 have_mng_structure,
3174 logging,
cristy3ed852e2009-09-05 21:47:34 +00003175 status;
3176
3177 MngInfo
3178 *mng_info;
3179
3180 char
3181 magic_number[MaxTextExtent];
3182
cristy3ed852e2009-09-05 21:47:34 +00003183 ssize_t
3184 count;
3185
3186 /*
3187 Open image file.
3188 */
3189 assert(image_info != (const ImageInfo *) NULL);
3190 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003191
cristy3ed852e2009-09-05 21:47:34 +00003192 if (image_info->debug != MagickFalse)
3193 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3194 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003195
cristy3ed852e2009-09-05 21:47:34 +00003196 assert(exception != (ExceptionInfo *) NULL);
3197 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003198 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003199 image=AcquireImage(image_info);
3200 mng_info=(MngInfo *) NULL;
3201 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003202
cristy3ed852e2009-09-05 21:47:34 +00003203 if (status == MagickFalse)
3204 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003205
cristy3ed852e2009-09-05 21:47:34 +00003206 /*
3207 Verify PNG signature.
3208 */
3209 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003210
glennrpdde35db2011-02-21 12:06:32 +00003211 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003212 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003213
cristy3ed852e2009-09-05 21:47:34 +00003214 /*
3215 Allocate a MngInfo structure.
3216 */
3217 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003218 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003219
cristy3ed852e2009-09-05 21:47:34 +00003220 if (mng_info == (MngInfo *) NULL)
3221 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003222
cristy3ed852e2009-09-05 21:47:34 +00003223 /*
3224 Initialize members of the MngInfo structure.
3225 */
3226 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3227 mng_info->image=image;
3228 have_mng_structure=MagickTrue;
3229
3230 previous=image;
3231 image=ReadOnePNGImage(mng_info,image_info,exception);
3232 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003233
cristy3ed852e2009-09-05 21:47:34 +00003234 if (image == (Image *) NULL)
3235 {
3236 if (previous != (Image *) NULL)
3237 {
3238 if (previous->signature != MagickSignature)
3239 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003240
cristy3ed852e2009-09-05 21:47:34 +00003241 (void) CloseBlob(previous);
3242 (void) DestroyImageList(previous);
3243 }
glennrp0fe50b42010-11-16 03:52:51 +00003244
cristy3ed852e2009-09-05 21:47:34 +00003245 if (logging != MagickFalse)
3246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3247 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003248
cristy3ed852e2009-09-05 21:47:34 +00003249 return((Image *) NULL);
3250 }
glennrp47b9dd52010-11-24 18:12:06 +00003251
cristy3ed852e2009-09-05 21:47:34 +00003252 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003253
cristy3ed852e2009-09-05 21:47:34 +00003254 if ((image->columns == 0) || (image->rows == 0))
3255 {
3256 if (logging != MagickFalse)
3257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3258 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003259
cristy3ed852e2009-09-05 21:47:34 +00003260 ThrowReaderException(CorruptImageError,"CorruptImage");
3261 }
glennrp47b9dd52010-11-24 18:12:06 +00003262
glennrp3faa9a32011-04-23 14:00:25 +00003263#if 0 /* This is probably redundant now */
cristy3ed852e2009-09-05 21:47:34 +00003264 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3265 {
3266 (void) SetImageType(image,PaletteType);
glennrp0fe50b42010-11-16 03:52:51 +00003267
cristy3ed852e2009-09-05 21:47:34 +00003268 if (image->matte != MagickFalse)
3269 {
3270 /* To do: Reduce to binary transparency */
3271 }
3272 }
glennrp3faa9a32011-04-23 14:00:25 +00003273#endif
glennrp47b9dd52010-11-24 18:12:06 +00003274
cristy3ed852e2009-09-05 21:47:34 +00003275 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3276 {
3277 (void) SetImageType(image,TrueColorType);
3278 image->matte=MagickFalse;
3279 }
glennrp0fe50b42010-11-16 03:52:51 +00003280
cristy3ed852e2009-09-05 21:47:34 +00003281 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3282 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003283
cristy3ed852e2009-09-05 21:47:34 +00003284 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3286 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3287 (double) image->page.width,(double) image->page.height,
3288 (double) image->page.x,(double) image->page.y);
3289
3290 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003292
cristy3ed852e2009-09-05 21:47:34 +00003293 return(image);
3294}
3295
3296
3297
3298#if defined(JNG_SUPPORTED)
3299/*
3300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3301% %
3302% %
3303% %
3304% R e a d O n e J N G I m a g e %
3305% %
3306% %
3307% %
3308%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3309%
3310% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3311% (minus the 8-byte signature) and returns it. It allocates the memory
3312% necessary for the new Image structure and returns a pointer to the new
3313% image.
3314%
3315% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3316%
3317% The format of the ReadOneJNGImage method is:
3318%
3319% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3320% ExceptionInfo *exception)
3321%
3322% A description of each parameter follows:
3323%
3324% o mng_info: Specifies a pointer to a MngInfo structure.
3325%
3326% o image_info: the image info.
3327%
3328% o exception: return any errors or warnings in this structure.
3329%
3330*/
3331static Image *ReadOneJNGImage(MngInfo *mng_info,
3332 const ImageInfo *image_info, ExceptionInfo *exception)
3333{
3334 Image
3335 *alpha_image,
3336 *color_image,
3337 *image,
3338 *jng_image;
3339
3340 ImageInfo
3341 *alpha_image_info,
3342 *color_image_info;
3343
cristy4383ec82011-01-05 15:42:32 +00003344 MagickBooleanType
3345 logging;
3346
cristybb503372010-05-27 20:51:26 +00003347 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003348 y;
3349
3350 MagickBooleanType
3351 status;
3352
3353 png_uint_32
3354 jng_height,
3355 jng_width;
3356
3357 png_byte
3358 jng_color_type,
3359 jng_image_sample_depth,
3360 jng_image_compression_method,
3361 jng_image_interlace_method,
3362 jng_alpha_sample_depth,
3363 jng_alpha_compression_method,
3364 jng_alpha_filter_method,
3365 jng_alpha_interlace_method;
3366
3367 register const PixelPacket
3368 *s;
3369
cristybb503372010-05-27 20:51:26 +00003370 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003371 i,
3372 x;
3373
3374 register PixelPacket
3375 *q;
3376
3377 register unsigned char
3378 *p;
3379
3380 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003381 read_JSEP,
3382 reading_idat,
3383 skip_to_iend;
3384
cristybb503372010-05-27 20:51:26 +00003385 size_t
cristy3ed852e2009-09-05 21:47:34 +00003386 length;
3387
3388 jng_alpha_compression_method=0;
3389 jng_alpha_sample_depth=8;
3390 jng_color_type=0;
3391 jng_height=0;
3392 jng_width=0;
3393 alpha_image=(Image *) NULL;
3394 color_image=(Image *) NULL;
3395 alpha_image_info=(ImageInfo *) NULL;
3396 color_image_info=(ImageInfo *) NULL;
3397
3398 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003399 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003400
3401 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003402
cristy3ed852e2009-09-05 21:47:34 +00003403 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3404 {
3405 /*
3406 Allocate next image structure.
3407 */
3408 if (logging != MagickFalse)
3409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3410 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003411
cristy3ed852e2009-09-05 21:47:34 +00003412 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003413
cristy3ed852e2009-09-05 21:47:34 +00003414 if (GetNextImageInList(image) == (Image *) NULL)
3415 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003416
cristy3ed852e2009-09-05 21:47:34 +00003417 image=SyncNextImageInList(image);
3418 }
3419 mng_info->image=image;
3420
3421 /*
3422 Signature bytes have already been read.
3423 */
3424
3425 read_JSEP=MagickFalse;
3426 reading_idat=MagickFalse;
3427 skip_to_iend=MagickFalse;
3428 for (;;)
3429 {
3430 char
3431 type[MaxTextExtent];
3432
3433 unsigned char
3434 *chunk;
3435
3436 unsigned int
3437 count;
3438
3439 /*
3440 Read a new JNG chunk.
3441 */
3442 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3443 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003444
cristy3ed852e2009-09-05 21:47:34 +00003445 if (status == MagickFalse)
3446 break;
glennrp0fe50b42010-11-16 03:52:51 +00003447
cristy3ed852e2009-09-05 21:47:34 +00003448 type[0]='\0';
3449 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3450 length=ReadBlobMSBLong(image);
3451 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3452
3453 if (logging != MagickFalse)
3454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003455 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3456 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003457
3458 if (length > PNG_UINT_31_MAX || count == 0)
3459 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003460
cristy3ed852e2009-09-05 21:47:34 +00003461 p=NULL;
3462 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003463
cristy3ed852e2009-09-05 21:47:34 +00003464 if (length)
3465 {
3466 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003467
cristy3ed852e2009-09-05 21:47:34 +00003468 if (chunk == (unsigned char *) NULL)
3469 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003470
cristybb503372010-05-27 20:51:26 +00003471 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003472 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003473
cristy3ed852e2009-09-05 21:47:34 +00003474 p=chunk;
3475 }
glennrp47b9dd52010-11-24 18:12:06 +00003476
cristy3ed852e2009-09-05 21:47:34 +00003477 (void) ReadBlobMSBLong(image); /* read crc word */
3478
3479 if (skip_to_iend)
3480 {
3481 if (length)
3482 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003483
cristy3ed852e2009-09-05 21:47:34 +00003484 continue;
3485 }
3486
3487 if (memcmp(type,mng_JHDR,4) == 0)
3488 {
3489 if (length == 16)
3490 {
cristybb503372010-05-27 20:51:26 +00003491 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003492 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003493 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003494 (p[6] << 8) | p[7]);
3495 jng_color_type=p[8];
3496 jng_image_sample_depth=p[9];
3497 jng_image_compression_method=p[10];
3498 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003499
cristy3ed852e2009-09-05 21:47:34 +00003500 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3501 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003502
cristy3ed852e2009-09-05 21:47:34 +00003503 jng_alpha_sample_depth=p[12];
3504 jng_alpha_compression_method=p[13];
3505 jng_alpha_filter_method=p[14];
3506 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003507
cristy3ed852e2009-09-05 21:47:34 +00003508 if (logging != MagickFalse)
3509 {
3510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003511 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003512
cristy3ed852e2009-09-05 21:47:34 +00003513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003514 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003515
cristy3ed852e2009-09-05 21:47:34 +00003516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3517 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003518
cristy3ed852e2009-09-05 21:47:34 +00003519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3520 " jng_image_sample_depth: %3d",
3521 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003522
cristy3ed852e2009-09-05 21:47:34 +00003523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3524 " jng_image_compression_method:%3d",
3525 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003526
cristy3ed852e2009-09-05 21:47:34 +00003527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3528 " jng_image_interlace_method: %3d",
3529 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003530
cristy3ed852e2009-09-05 21:47:34 +00003531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3532 " jng_alpha_sample_depth: %3d",
3533 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003534
cristy3ed852e2009-09-05 21:47:34 +00003535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3536 " jng_alpha_compression_method:%3d",
3537 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003538
cristy3ed852e2009-09-05 21:47:34 +00003539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3540 " jng_alpha_filter_method: %3d",
3541 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003542
cristy3ed852e2009-09-05 21:47:34 +00003543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3544 " jng_alpha_interlace_method: %3d",
3545 jng_alpha_interlace_method);
3546 }
3547 }
glennrp47b9dd52010-11-24 18:12:06 +00003548
cristy3ed852e2009-09-05 21:47:34 +00003549 if (length)
3550 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003551
cristy3ed852e2009-09-05 21:47:34 +00003552 continue;
3553 }
3554
3555
3556 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3557 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3558 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3559 {
3560 /*
3561 o create color_image
3562 o open color_blob, attached to color_image
3563 o if (color type has alpha)
3564 open alpha_blob, attached to alpha_image
3565 */
3566
cristy73bd4a52010-10-05 11:24:23 +00003567 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003568
cristy3ed852e2009-09-05 21:47:34 +00003569 if (color_image_info == (ImageInfo *) NULL)
3570 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003571
cristy3ed852e2009-09-05 21:47:34 +00003572 GetImageInfo(color_image_info);
3573 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003574
cristy3ed852e2009-09-05 21:47:34 +00003575 if (color_image == (Image *) NULL)
3576 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3577
3578 if (logging != MagickFalse)
3579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3580 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003581
cristy3ed852e2009-09-05 21:47:34 +00003582 (void) AcquireUniqueFilename(color_image->filename);
3583 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3584 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003585
cristy3ed852e2009-09-05 21:47:34 +00003586 if (status == MagickFalse)
3587 return((Image *) NULL);
3588
3589 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3590 {
3591 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00003592 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00003593
cristy3ed852e2009-09-05 21:47:34 +00003594 if (alpha_image_info == (ImageInfo *) NULL)
3595 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003596
cristy3ed852e2009-09-05 21:47:34 +00003597 GetImageInfo(alpha_image_info);
3598 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003599
cristy3ed852e2009-09-05 21:47:34 +00003600 if (alpha_image == (Image *) NULL)
3601 {
3602 alpha_image=DestroyImage(alpha_image);
3603 ThrowReaderException(ResourceLimitError,
3604 "MemoryAllocationFailed");
3605 }
glennrp0fe50b42010-11-16 03:52:51 +00003606
cristy3ed852e2009-09-05 21:47:34 +00003607 if (logging != MagickFalse)
3608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3609 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003610
cristy3ed852e2009-09-05 21:47:34 +00003611 (void) AcquireUniqueFilename(alpha_image->filename);
3612 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3613 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003614
cristy3ed852e2009-09-05 21:47:34 +00003615 if (status == MagickFalse)
3616 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003617
cristy3ed852e2009-09-05 21:47:34 +00003618 if (jng_alpha_compression_method == 0)
3619 {
3620 unsigned char
3621 data[18];
3622
3623 if (logging != MagickFalse)
3624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3625 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003626
cristy3ed852e2009-09-05 21:47:34 +00003627 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3628 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00003629
cristy3ed852e2009-09-05 21:47:34 +00003630 (void) WriteBlobMSBULong(alpha_image,13L);
3631 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00003632 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00003633 PNGLong(data+4,jng_width);
3634 PNGLong(data+8,jng_height);
3635 data[12]=jng_alpha_sample_depth;
3636 data[13]=0; /* color_type gray */
3637 data[14]=0; /* compression method 0 */
3638 data[15]=0; /* filter_method 0 */
3639 data[16]=0; /* interlace_method 0 */
3640 (void) WriteBlob(alpha_image,17,data);
3641 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3642 }
3643 }
3644 reading_idat=MagickTrue;
3645 }
3646
3647 if (memcmp(type,mng_JDAT,4) == 0)
3648 {
glennrp47b9dd52010-11-24 18:12:06 +00003649 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003650
3651 if (logging != MagickFalse)
3652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3653 " Copying JDAT chunk data to color_blob.");
3654
3655 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003656
cristy3ed852e2009-09-05 21:47:34 +00003657 if (length)
3658 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003659
cristy3ed852e2009-09-05 21:47:34 +00003660 continue;
3661 }
3662
3663 if (memcmp(type,mng_IDAT,4) == 0)
3664 {
3665 png_byte
3666 data[5];
3667
glennrp47b9dd52010-11-24 18:12:06 +00003668 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003669
3670 if (image_info->ping == MagickFalse)
3671 {
3672 if (logging != MagickFalse)
3673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3674 " Copying IDAT chunk data to alpha_blob.");
3675
cristybb503372010-05-27 20:51:26 +00003676 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003677 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00003678 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00003679 (void) WriteBlob(alpha_image,4,data);
3680 (void) WriteBlob(alpha_image,length,chunk);
3681 (void) WriteBlobMSBULong(alpha_image,
3682 crc32(crc32(0,data,4),chunk,(uInt) length));
3683 }
glennrp0fe50b42010-11-16 03:52:51 +00003684
cristy3ed852e2009-09-05 21:47:34 +00003685 if (length)
3686 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688 continue;
3689 }
3690
3691 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3692 {
glennrp47b9dd52010-11-24 18:12:06 +00003693 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00003694
3695 if (image_info->ping == MagickFalse)
3696 {
3697 if (logging != MagickFalse)
3698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3699 " Copying JDAA chunk data to alpha_blob.");
3700
3701 (void) WriteBlob(alpha_image,length,chunk);
3702 }
glennrp0fe50b42010-11-16 03:52:51 +00003703
cristy3ed852e2009-09-05 21:47:34 +00003704 if (length)
3705 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003706
cristy3ed852e2009-09-05 21:47:34 +00003707 continue;
3708 }
3709
3710 if (memcmp(type,mng_JSEP,4) == 0)
3711 {
3712 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00003713
cristy3ed852e2009-09-05 21:47:34 +00003714 if (length)
3715 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003716
cristy3ed852e2009-09-05 21:47:34 +00003717 continue;
3718 }
3719
3720 if (memcmp(type,mng_bKGD,4) == 0)
3721 {
3722 if (length == 2)
3723 {
3724 image->background_color.red=ScaleCharToQuantum(p[1]);
3725 image->background_color.green=image->background_color.red;
3726 image->background_color.blue=image->background_color.red;
3727 }
glennrp0fe50b42010-11-16 03:52:51 +00003728
cristy3ed852e2009-09-05 21:47:34 +00003729 if (length == 6)
3730 {
3731 image->background_color.red=ScaleCharToQuantum(p[1]);
3732 image->background_color.green=ScaleCharToQuantum(p[3]);
3733 image->background_color.blue=ScaleCharToQuantum(p[5]);
3734 }
glennrp0fe50b42010-11-16 03:52:51 +00003735
cristy3ed852e2009-09-05 21:47:34 +00003736 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3737 continue;
3738 }
3739
3740 if (memcmp(type,mng_gAMA,4) == 0)
3741 {
3742 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00003743 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00003744
cristy3ed852e2009-09-05 21:47:34 +00003745 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3746 continue;
3747 }
3748
3749 if (memcmp(type,mng_cHRM,4) == 0)
3750 {
3751 if (length == 32)
3752 {
cristy8182b072010-05-30 20:10:53 +00003753 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3754 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3755 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3756 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3757 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3758 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3759 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3760 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003761 }
glennrp47b9dd52010-11-24 18:12:06 +00003762
cristy3ed852e2009-09-05 21:47:34 +00003763 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3764 continue;
3765 }
3766
3767 if (memcmp(type,mng_sRGB,4) == 0)
3768 {
3769 if (length == 1)
3770 {
glennrpe610a072010-08-05 17:08:46 +00003771 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00003772 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00003773 image->gamma=0.45455f;
3774 image->chromaticity.red_primary.x=0.6400f;
3775 image->chromaticity.red_primary.y=0.3300f;
3776 image->chromaticity.green_primary.x=0.3000f;
3777 image->chromaticity.green_primary.y=0.6000f;
3778 image->chromaticity.blue_primary.x=0.1500f;
3779 image->chromaticity.blue_primary.y=0.0600f;
3780 image->chromaticity.white_point.x=0.3127f;
3781 image->chromaticity.white_point.y=0.3290f;
3782 }
glennrp47b9dd52010-11-24 18:12:06 +00003783
cristy3ed852e2009-09-05 21:47:34 +00003784 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3785 continue;
3786 }
3787
3788 if (memcmp(type,mng_oFFs,4) == 0)
3789 {
3790 if (length > 8)
3791 {
glennrp5eae7602011-02-22 15:21:32 +00003792 image->page.x=(ssize_t) mng_get_long(p);
3793 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00003794
cristy3ed852e2009-09-05 21:47:34 +00003795 if ((int) p[8] != 0)
3796 {
3797 image->page.x/=10000;
3798 image->page.y/=10000;
3799 }
3800 }
glennrp47b9dd52010-11-24 18:12:06 +00003801
cristy3ed852e2009-09-05 21:47:34 +00003802 if (length)
3803 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003804
cristy3ed852e2009-09-05 21:47:34 +00003805 continue;
3806 }
3807
3808 if (memcmp(type,mng_pHYs,4) == 0)
3809 {
3810 if (length > 8)
3811 {
cristy8182b072010-05-30 20:10:53 +00003812 image->x_resolution=(double) mng_get_long(p);
3813 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003814 if ((int) p[8] == PNG_RESOLUTION_METER)
3815 {
3816 image->units=PixelsPerCentimeterResolution;
3817 image->x_resolution=image->x_resolution/100.0f;
3818 image->y_resolution=image->y_resolution/100.0f;
3819 }
3820 }
glennrp0fe50b42010-11-16 03:52:51 +00003821
cristy3ed852e2009-09-05 21:47:34 +00003822 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3823 continue;
3824 }
3825
3826#if 0
3827 if (memcmp(type,mng_iCCP,4) == 0)
3828 {
glennrpfd05d622011-02-25 04:10:33 +00003829 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00003830 if (length)
3831 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00003832
cristy3ed852e2009-09-05 21:47:34 +00003833 continue;
3834 }
3835#endif
3836
3837 if (length)
3838 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3839
3840 if (memcmp(type,mng_IEND,4))
3841 continue;
glennrp0fe50b42010-11-16 03:52:51 +00003842
cristy3ed852e2009-09-05 21:47:34 +00003843 break;
3844 }
3845
3846
3847 /* IEND found */
3848
3849 /*
3850 Finish up reading image data:
3851
3852 o read main image from color_blob.
3853
3854 o close color_blob.
3855
3856 o if (color_type has alpha)
3857 if alpha_encoding is PNG
3858 read secondary image from alpha_blob via ReadPNG
3859 if alpha_encoding is JPEG
3860 read secondary image from alpha_blob via ReadJPEG
3861
3862 o close alpha_blob.
3863
3864 o copy intensity of secondary image into
3865 opacity samples of main image.
3866
3867 o destroy the secondary image.
3868 */
3869
3870 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00003871
cristy3ed852e2009-09-05 21:47:34 +00003872 if (logging != MagickFalse)
3873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3874 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003875
cristy3ed852e2009-09-05 21:47:34 +00003876 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3877 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003878
cristy3ed852e2009-09-05 21:47:34 +00003879 color_image_info->ping=MagickFalse; /* To do: avoid this */
3880 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 if (jng_image == (Image *) NULL)
3883 return((Image *) NULL);
3884
3885 (void) RelinquishUniqueFileResource(color_image->filename);
3886 color_image=DestroyImage(color_image);
3887 color_image_info=DestroyImageInfo(color_image_info);
3888
3889 if (jng_image == (Image *) NULL)
3890 return((Image *) NULL);
3891
3892 if (logging != MagickFalse)
3893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3894 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00003895
cristy3ed852e2009-09-05 21:47:34 +00003896 image->rows=jng_height;
3897 image->columns=jng_width;
3898 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00003899
cristybb503372010-05-27 20:51:26 +00003900 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003901 {
3902 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3903 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3904 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00003905
cristy3ed852e2009-09-05 21:47:34 +00003906 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3907 break;
3908 }
glennrp0fe50b42010-11-16 03:52:51 +00003909
cristy3ed852e2009-09-05 21:47:34 +00003910 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00003911
cristy3ed852e2009-09-05 21:47:34 +00003912 if (image_info->ping == MagickFalse)
3913 {
3914 if (jng_color_type >= 12)
3915 {
3916 if (jng_alpha_compression_method == 0)
3917 {
3918 png_byte
3919 data[5];
3920 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3921 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00003922 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00003923 (void) WriteBlob(alpha_image,4,data);
3924 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3925 }
glennrp0fe50b42010-11-16 03:52:51 +00003926
cristy3ed852e2009-09-05 21:47:34 +00003927 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00003928
cristy3ed852e2009-09-05 21:47:34 +00003929 if (logging != MagickFalse)
3930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3931 " Reading opacity from alpha_blob.");
3932
3933 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3934 "%s",alpha_image->filename);
3935
3936 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003939 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003940 {
3941 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3942 &image->exception);
3943 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003944
cristy3ed852e2009-09-05 21:47:34 +00003945 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003946 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003947 q->opacity=(Quantum) QuantumRange-s->red;
glennrp0fe50b42010-11-16 03:52:51 +00003948
cristy3ed852e2009-09-05 21:47:34 +00003949 else
cristybb503372010-05-27 20:51:26 +00003950 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003951 {
3952 q->opacity=(Quantum) QuantumRange-s->red;
3953 if (q->opacity != OpaqueOpacity)
3954 image->matte=MagickTrue;
3955 }
glennrp0fe50b42010-11-16 03:52:51 +00003956
cristy3ed852e2009-09-05 21:47:34 +00003957 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3958 break;
3959 }
3960 (void) RelinquishUniqueFileResource(alpha_image->filename);
3961 alpha_image=DestroyImage(alpha_image);
3962 alpha_image_info=DestroyImageInfo(alpha_image_info);
3963 if (jng_image != (Image *) NULL)
3964 jng_image=DestroyImage(jng_image);
3965 }
3966 }
3967
glennrp47b9dd52010-11-24 18:12:06 +00003968 /* Read the JNG image. */
3969
cristy3ed852e2009-09-05 21:47:34 +00003970 if (mng_info->mng_type == 0)
3971 {
3972 mng_info->mng_width=jng_width;
3973 mng_info->mng_height=jng_height;
3974 }
glennrp0fe50b42010-11-16 03:52:51 +00003975
cristy3ed852e2009-09-05 21:47:34 +00003976 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003977 {
3978 image->page.width=jng_width;
3979 image->page.height=jng_height;
3980 }
3981
cristy3ed852e2009-09-05 21:47:34 +00003982 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00003983 {
3984 image->page.x=mng_info->x_off[mng_info->object_id];
3985 image->page.y=mng_info->y_off[mng_info->object_id];
3986 }
3987
cristy3ed852e2009-09-05 21:47:34 +00003988 else
glennrp0fe50b42010-11-16 03:52:51 +00003989 {
3990 image->page.y=mng_info->y_off[mng_info->object_id];
3991 }
3992
cristy3ed852e2009-09-05 21:47:34 +00003993 mng_info->image_found++;
3994 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3995 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003996
cristy3ed852e2009-09-05 21:47:34 +00003997 if (logging != MagickFalse)
3998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3999 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004000
cristy3ed852e2009-09-05 21:47:34 +00004001 return(image);
4002}
4003
4004/*
4005%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4006% %
4007% %
4008% %
4009% R e a d J N G I m a g e %
4010% %
4011% %
4012% %
4013%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4014%
4015% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4016% (including the 8-byte signature) and returns it. It allocates the memory
4017% necessary for the new Image structure and returns a pointer to the new
4018% image.
4019%
4020% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4021%
4022% The format of the ReadJNGImage method is:
4023%
4024% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4025% *exception)
4026%
4027% A description of each parameter follows:
4028%
4029% o image_info: the image info.
4030%
4031% o exception: return any errors or warnings in this structure.
4032%
4033*/
4034
4035static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4036{
4037 Image
4038 *image,
4039 *previous;
4040
4041 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004042 have_mng_structure,
4043 logging,
cristy3ed852e2009-09-05 21:47:34 +00004044 status;
4045
4046 MngInfo
4047 *mng_info;
4048
4049 char
4050 magic_number[MaxTextExtent];
4051
cristy3ed852e2009-09-05 21:47:34 +00004052 size_t
4053 count;
4054
4055 /*
4056 Open image file.
4057 */
4058 assert(image_info != (const ImageInfo *) NULL);
4059 assert(image_info->signature == MagickSignature);
4060 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4061 assert(exception != (ExceptionInfo *) NULL);
4062 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004063 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004064 image=AcquireImage(image_info);
4065 mng_info=(MngInfo *) NULL;
4066 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004067
cristy3ed852e2009-09-05 21:47:34 +00004068 if (status == MagickFalse)
4069 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004070
cristy3ed852e2009-09-05 21:47:34 +00004071 if (LocaleCompare(image_info->magick,"JNG") != 0)
4072 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004073
glennrp47b9dd52010-11-24 18:12:06 +00004074 /* Verify JNG signature. */
4075
cristy3ed852e2009-09-05 21:47:34 +00004076 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004077
glennrp3b8763e2011-02-21 12:08:18 +00004078 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004079 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004080
glennrp47b9dd52010-11-24 18:12:06 +00004081 /* Allocate a MngInfo structure. */
4082
cristy3ed852e2009-09-05 21:47:34 +00004083 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004084 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004085
cristy3ed852e2009-09-05 21:47:34 +00004086 if (mng_info == (MngInfo *) NULL)
4087 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004088
glennrp47b9dd52010-11-24 18:12:06 +00004089 /* Initialize members of the MngInfo structure. */
4090
cristy3ed852e2009-09-05 21:47:34 +00004091 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4092 have_mng_structure=MagickTrue;
4093
4094 mng_info->image=image;
4095 previous=image;
4096 image=ReadOneJNGImage(mng_info,image_info,exception);
4097 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004098
cristy3ed852e2009-09-05 21:47:34 +00004099 if (image == (Image *) NULL)
4100 {
4101 if (IsImageObject(previous) != MagickFalse)
4102 {
4103 (void) CloseBlob(previous);
4104 (void) DestroyImageList(previous);
4105 }
glennrp0fe50b42010-11-16 03:52:51 +00004106
cristy3ed852e2009-09-05 21:47:34 +00004107 if (logging != MagickFalse)
4108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4109 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004110
cristy3ed852e2009-09-05 21:47:34 +00004111 return((Image *) NULL);
4112 }
4113 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004114
cristy3ed852e2009-09-05 21:47:34 +00004115 if (image->columns == 0 || image->rows == 0)
4116 {
4117 if (logging != MagickFalse)
4118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4119 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004120
cristy3ed852e2009-09-05 21:47:34 +00004121 ThrowReaderException(CorruptImageError,"CorruptImage");
4122 }
glennrp0fe50b42010-11-16 03:52:51 +00004123
cristy3ed852e2009-09-05 21:47:34 +00004124 if (logging != MagickFalse)
4125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004126
cristy3ed852e2009-09-05 21:47:34 +00004127 return(image);
4128}
4129#endif
4130
4131static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4132{
4133 char
4134 page_geometry[MaxTextExtent];
4135
4136 Image
4137 *image,
4138 *previous;
4139
cristy4383ec82011-01-05 15:42:32 +00004140 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004141 logging,
4142 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004143
cristy3ed852e2009-09-05 21:47:34 +00004144 volatile int
4145 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004146 object_id,
4147 term_chunk_found,
4148 skip_to_iend;
4149
cristybb503372010-05-27 20:51:26 +00004150 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004151 image_count=0;
4152
4153 MagickBooleanType
4154 status;
4155
4156 MagickOffsetType
4157 offset;
4158
4159 MngInfo
4160 *mng_info;
4161
4162 MngBox
4163 default_fb,
4164 fb,
4165 previous_fb;
4166
4167#if defined(MNG_INSERT_LAYERS)
4168 PixelPacket
4169 mng_background_color;
4170#endif
4171
4172 register unsigned char
4173 *p;
4174
cristybb503372010-05-27 20:51:26 +00004175 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004176 i;
4177
4178 size_t
4179 count;
4180
cristybb503372010-05-27 20:51:26 +00004181 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004182 loop_level;
4183
4184 volatile short
4185 skipping_loop;
4186
4187#if defined(MNG_INSERT_LAYERS)
4188 unsigned int
4189 mandatory_back=0;
4190#endif
4191
4192 volatile unsigned int
4193#ifdef MNG_OBJECT_BUFFERS
4194 mng_background_object=0,
4195#endif
4196 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4197
cristybb503372010-05-27 20:51:26 +00004198 size_t
cristy3ed852e2009-09-05 21:47:34 +00004199 default_frame_timeout,
4200 frame_timeout,
4201#if defined(MNG_INSERT_LAYERS)
4202 image_height,
4203 image_width,
4204#endif
4205 length;
4206
glennrp38ea0832010-06-02 18:50:28 +00004207 /* These delays are all measured in image ticks_per_second,
4208 * not in MNG ticks_per_second
4209 */
cristybb503372010-05-27 20:51:26 +00004210 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004211 default_frame_delay,
4212 final_delay,
4213 final_image_delay,
4214 frame_delay,
4215#if defined(MNG_INSERT_LAYERS)
4216 insert_layers,
4217#endif
4218 mng_iterations=1,
4219 simplicity=0,
4220 subframe_height=0,
4221 subframe_width=0;
4222
4223 previous_fb.top=0;
4224 previous_fb.bottom=0;
4225 previous_fb.left=0;
4226 previous_fb.right=0;
4227 default_fb.top=0;
4228 default_fb.bottom=0;
4229 default_fb.left=0;
4230 default_fb.right=0;
4231
glennrp47b9dd52010-11-24 18:12:06 +00004232 /* Open image file. */
4233
cristy3ed852e2009-09-05 21:47:34 +00004234 assert(image_info != (const ImageInfo *) NULL);
4235 assert(image_info->signature == MagickSignature);
4236 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4237 assert(exception != (ExceptionInfo *) NULL);
4238 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004239 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004240 image=AcquireImage(image_info);
4241 mng_info=(MngInfo *) NULL;
4242 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004243
cristy3ed852e2009-09-05 21:47:34 +00004244 if (status == MagickFalse)
4245 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004246
cristy3ed852e2009-09-05 21:47:34 +00004247 first_mng_object=MagickFalse;
4248 skipping_loop=(-1);
4249 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004250
4251 /* Allocate a MngInfo structure. */
4252
cristy73bd4a52010-10-05 11:24:23 +00004253 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004254
cristy3ed852e2009-09-05 21:47:34 +00004255 if (mng_info == (MngInfo *) NULL)
4256 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004257
glennrp47b9dd52010-11-24 18:12:06 +00004258 /* Initialize members of the MngInfo structure. */
4259
cristy3ed852e2009-09-05 21:47:34 +00004260 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4261 mng_info->image=image;
4262 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004263
4264 if (LocaleCompare(image_info->magick,"MNG") == 0)
4265 {
4266 char
4267 magic_number[MaxTextExtent];
4268
glennrp47b9dd52010-11-24 18:12:06 +00004269 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004270 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4271 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4272 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004273
4274 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004275 for (i=0; i < MNG_MAX_OBJECTS; i++)
4276 {
cristybb503372010-05-27 20:51:26 +00004277 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4278 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004279 }
4280 mng_info->exists[0]=MagickTrue;
4281 }
glennrp47b9dd52010-11-24 18:12:06 +00004282
cristy3ed852e2009-09-05 21:47:34 +00004283 first_mng_object=MagickTrue;
4284 mng_type=0;
4285#if defined(MNG_INSERT_LAYERS)
4286 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4287#endif
4288 default_frame_delay=0;
4289 default_frame_timeout=0;
4290 frame_delay=0;
4291 final_delay=1;
4292 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4293 object_id=0;
4294 skip_to_iend=MagickFalse;
4295 term_chunk_found=MagickFalse;
4296 mng_info->framing_mode=1;
4297#if defined(MNG_INSERT_LAYERS)
4298 mandatory_back=MagickFalse;
4299#endif
4300#if defined(MNG_INSERT_LAYERS)
4301 mng_background_color=image->background_color;
4302#endif
4303 default_fb=mng_info->frame;
4304 previous_fb=mng_info->frame;
4305 do
4306 {
4307 char
4308 type[MaxTextExtent];
4309
4310 if (LocaleCompare(image_info->magick,"MNG") == 0)
4311 {
4312 unsigned char
4313 *chunk;
4314
4315 /*
4316 Read a new chunk.
4317 */
4318 type[0]='\0';
4319 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4320 length=ReadBlobMSBLong(image);
4321 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4322
4323 if (logging != MagickFalse)
4324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004325 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4326 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004327
4328 if (length > PNG_UINT_31_MAX)
4329 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004330
cristy3ed852e2009-09-05 21:47:34 +00004331 if (count == 0)
4332 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004333
cristy3ed852e2009-09-05 21:47:34 +00004334 p=NULL;
4335 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004336
cristy3ed852e2009-09-05 21:47:34 +00004337 if (length)
4338 {
4339 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004340
cristy3ed852e2009-09-05 21:47:34 +00004341 if (chunk == (unsigned char *) NULL)
4342 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004343
cristybb503372010-05-27 20:51:26 +00004344 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004345 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004346
cristy3ed852e2009-09-05 21:47:34 +00004347 p=chunk;
4348 }
glennrp0fe50b42010-11-16 03:52:51 +00004349
cristy3ed852e2009-09-05 21:47:34 +00004350 (void) ReadBlobMSBLong(image); /* read crc word */
4351
4352#if !defined(JNG_SUPPORTED)
4353 if (memcmp(type,mng_JHDR,4) == 0)
4354 {
4355 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004356
cristy3ed852e2009-09-05 21:47:34 +00004357 if (mng_info->jhdr_warning == 0)
4358 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4359 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004360
cristy3ed852e2009-09-05 21:47:34 +00004361 mng_info->jhdr_warning++;
4362 }
4363#endif
4364 if (memcmp(type,mng_DHDR,4) == 0)
4365 {
4366 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004367
cristy3ed852e2009-09-05 21:47:34 +00004368 if (mng_info->dhdr_warning == 0)
4369 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4370 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004371
cristy3ed852e2009-09-05 21:47:34 +00004372 mng_info->dhdr_warning++;
4373 }
4374 if (memcmp(type,mng_MEND,4) == 0)
4375 break;
glennrp47b9dd52010-11-24 18:12:06 +00004376
cristy3ed852e2009-09-05 21:47:34 +00004377 if (skip_to_iend)
4378 {
4379 if (memcmp(type,mng_IEND,4) == 0)
4380 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004381
cristy3ed852e2009-09-05 21:47:34 +00004382 if (length)
4383 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004384
cristy3ed852e2009-09-05 21:47:34 +00004385 if (logging != MagickFalse)
4386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4387 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004388
cristy3ed852e2009-09-05 21:47:34 +00004389 continue;
4390 }
glennrp0fe50b42010-11-16 03:52:51 +00004391
cristy3ed852e2009-09-05 21:47:34 +00004392 if (memcmp(type,mng_MHDR,4) == 0)
4393 {
cristybb503372010-05-27 20:51:26 +00004394 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004395 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004396
cristybb503372010-05-27 20:51:26 +00004397 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004398 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004399
cristy3ed852e2009-09-05 21:47:34 +00004400 if (logging != MagickFalse)
4401 {
4402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004403 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004405 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004406 }
glennrp0fe50b42010-11-16 03:52:51 +00004407
cristy3ed852e2009-09-05 21:47:34 +00004408 p+=8;
cristy8182b072010-05-30 20:10:53 +00004409 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004410
cristy3ed852e2009-09-05 21:47:34 +00004411 if (mng_info->ticks_per_second == 0)
4412 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004413
cristy3ed852e2009-09-05 21:47:34 +00004414 else
4415 default_frame_delay=1UL*image->ticks_per_second/
4416 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004417
cristy3ed852e2009-09-05 21:47:34 +00004418 frame_delay=default_frame_delay;
4419 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004420
cristy3ed852e2009-09-05 21:47:34 +00004421 if (length > 16)
4422 {
4423 p+=16;
cristy8182b072010-05-30 20:10:53 +00004424 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004425 }
glennrp0fe50b42010-11-16 03:52:51 +00004426
cristy3ed852e2009-09-05 21:47:34 +00004427 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004428
cristy3ed852e2009-09-05 21:47:34 +00004429 if ((simplicity != 0) && ((simplicity | 11) == 11))
4430 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004431
cristy3ed852e2009-09-05 21:47:34 +00004432 if ((simplicity != 0) && ((simplicity | 9) == 9))
4433 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004434
cristy3ed852e2009-09-05 21:47:34 +00004435#if defined(MNG_INSERT_LAYERS)
4436 if (mng_type != 3)
4437 insert_layers=MagickTrue;
4438#endif
4439 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4440 {
glennrp47b9dd52010-11-24 18:12:06 +00004441 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004442 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004443
cristy3ed852e2009-09-05 21:47:34 +00004444 if (GetNextImageInList(image) == (Image *) NULL)
4445 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004446
cristy3ed852e2009-09-05 21:47:34 +00004447 image=SyncNextImageInList(image);
4448 mng_info->image=image;
4449 }
4450
4451 if ((mng_info->mng_width > 65535L) ||
4452 (mng_info->mng_height > 65535L))
4453 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004454
cristye8c25f92010-06-03 00:53:06 +00004455 (void) FormatMagickString(page_geometry,MaxTextExtent,
4456 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004457 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004458
cristy3ed852e2009-09-05 21:47:34 +00004459 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004460 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004461 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004462 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004463 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004464
cristy3ed852e2009-09-05 21:47:34 +00004465 for (i=0; i < MNG_MAX_OBJECTS; i++)
4466 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004467
cristy3ed852e2009-09-05 21:47:34 +00004468 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4469 continue;
4470 }
4471
4472 if (memcmp(type,mng_TERM,4) == 0)
4473 {
4474 int
4475 repeat=0;
4476
4477
4478 if (length)
4479 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004480
cristy3ed852e2009-09-05 21:47:34 +00004481 if (repeat == 3)
4482 {
cristy8182b072010-05-30 20:10:53 +00004483 final_delay=(png_uint_32) mng_get_long(&p[2]);
4484 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004485
cristy3ed852e2009-09-05 21:47:34 +00004486 if (mng_iterations == PNG_UINT_31_MAX)
4487 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004488
cristy3ed852e2009-09-05 21:47:34 +00004489 image->iterations=mng_iterations;
4490 term_chunk_found=MagickTrue;
4491 }
glennrp0fe50b42010-11-16 03:52:51 +00004492
cristy3ed852e2009-09-05 21:47:34 +00004493 if (logging != MagickFalse)
4494 {
4495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4496 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004497
cristy3ed852e2009-09-05 21:47:34 +00004498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004499 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004500
cristy3ed852e2009-09-05 21:47:34 +00004501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004502 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004503 }
glennrp0fe50b42010-11-16 03:52:51 +00004504
cristy3ed852e2009-09-05 21:47:34 +00004505 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4506 continue;
4507 }
4508 if (memcmp(type,mng_DEFI,4) == 0)
4509 {
4510 if (mng_type == 3)
4511 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4512 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4513 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004514
cristy3ed852e2009-09-05 21:47:34 +00004515 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004516
cristy3ed852e2009-09-05 21:47:34 +00004517 if (mng_type == 2 && object_id != 0)
4518 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4519 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4520 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004521
cristy3ed852e2009-09-05 21:47:34 +00004522 if (object_id > MNG_MAX_OBJECTS)
4523 {
4524 /*
4525 Instead ofsuing a warning we should allocate a larger
4526 MngInfo structure and continue.
4527 */
4528 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4529 CoderError,"object id too large","`%s'",image->filename);
4530 object_id=MNG_MAX_OBJECTS;
4531 }
glennrp0fe50b42010-11-16 03:52:51 +00004532
cristy3ed852e2009-09-05 21:47:34 +00004533 if (mng_info->exists[object_id])
4534 if (mng_info->frozen[object_id])
4535 {
4536 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4537 (void) ThrowMagickException(&image->exception,
4538 GetMagickModule(),CoderError,
4539 "DEFI cannot redefine a frozen MNG object","`%s'",
4540 image->filename);
4541 continue;
4542 }
glennrp0fe50b42010-11-16 03:52:51 +00004543
cristy3ed852e2009-09-05 21:47:34 +00004544 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004545
cristy3ed852e2009-09-05 21:47:34 +00004546 if (length > 2)
4547 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004548
cristy3ed852e2009-09-05 21:47:34 +00004549 /*
4550 Extract object offset info.
4551 */
4552 if (length > 11)
4553 {
glennrp0fe50b42010-11-16 03:52:51 +00004554 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4555 (p[5] << 16) | (p[6] << 8) | p[7]);
4556
4557 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4558 (p[9] << 16) | (p[10] << 8) | p[11]);
4559
cristy3ed852e2009-09-05 21:47:34 +00004560 if (logging != MagickFalse)
4561 {
4562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004563 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004564 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004565
cristy3ed852e2009-09-05 21:47:34 +00004566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004567 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004568 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004569 }
4570 }
glennrp0fe50b42010-11-16 03:52:51 +00004571
cristy3ed852e2009-09-05 21:47:34 +00004572 /*
4573 Extract object clipping info.
4574 */
4575 if (length > 27)
4576 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4577 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004578
cristy3ed852e2009-09-05 21:47:34 +00004579 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4580 continue;
4581 }
4582 if (memcmp(type,mng_bKGD,4) == 0)
4583 {
4584 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004585
cristy3ed852e2009-09-05 21:47:34 +00004586 if (length > 5)
4587 {
4588 mng_info->mng_global_bkgd.red=
4589 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004590
cristy3ed852e2009-09-05 21:47:34 +00004591 mng_info->mng_global_bkgd.green=
4592 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004593
cristy3ed852e2009-09-05 21:47:34 +00004594 mng_info->mng_global_bkgd.blue=
4595 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004596
cristy3ed852e2009-09-05 21:47:34 +00004597 mng_info->have_global_bkgd=MagickTrue;
4598 }
glennrp0fe50b42010-11-16 03:52:51 +00004599
cristy3ed852e2009-09-05 21:47:34 +00004600 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4601 continue;
4602 }
4603 if (memcmp(type,mng_BACK,4) == 0)
4604 {
4605#if defined(MNG_INSERT_LAYERS)
4606 if (length > 6)
4607 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00004608
cristy3ed852e2009-09-05 21:47:34 +00004609 else
4610 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00004611
cristy3ed852e2009-09-05 21:47:34 +00004612 if (mandatory_back && length > 5)
4613 {
4614 mng_background_color.red=
4615 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00004616
cristy3ed852e2009-09-05 21:47:34 +00004617 mng_background_color.green=
4618 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00004619
cristy3ed852e2009-09-05 21:47:34 +00004620 mng_background_color.blue=
4621 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00004622
cristy3ed852e2009-09-05 21:47:34 +00004623 mng_background_color.opacity=OpaqueOpacity;
4624 }
glennrp0fe50b42010-11-16 03:52:51 +00004625
cristy3ed852e2009-09-05 21:47:34 +00004626#ifdef MNG_OBJECT_BUFFERS
4627 if (length > 8)
4628 mng_background_object=(p[7] << 8) | p[8];
4629#endif
4630#endif
4631 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4632 continue;
4633 }
glennrp47b9dd52010-11-24 18:12:06 +00004634
cristy3ed852e2009-09-05 21:47:34 +00004635 if (memcmp(type,mng_PLTE,4) == 0)
4636 {
glennrp47b9dd52010-11-24 18:12:06 +00004637 /* Read global PLTE. */
4638
cristy3ed852e2009-09-05 21:47:34 +00004639 if (length && (length < 769))
4640 {
4641 if (mng_info->global_plte == (png_colorp) NULL)
4642 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4643 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00004644
cristybb503372010-05-27 20:51:26 +00004645 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004646 {
4647 mng_info->global_plte[i].red=p[3*i];
4648 mng_info->global_plte[i].green=p[3*i+1];
4649 mng_info->global_plte[i].blue=p[3*i+2];
4650 }
glennrp0fe50b42010-11-16 03:52:51 +00004651
cristy35ef8242010-06-03 16:24:13 +00004652 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00004653 }
4654#ifdef MNG_LOOSE
4655 for ( ; i < 256; i++)
4656 {
4657 mng_info->global_plte[i].red=i;
4658 mng_info->global_plte[i].green=i;
4659 mng_info->global_plte[i].blue=i;
4660 }
glennrp0fe50b42010-11-16 03:52:51 +00004661
cristy3ed852e2009-09-05 21:47:34 +00004662 if (length)
4663 mng_info->global_plte_length=256;
4664#endif
4665 else
4666 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00004667
cristy3ed852e2009-09-05 21:47:34 +00004668 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4669 continue;
4670 }
glennrp47b9dd52010-11-24 18:12:06 +00004671
cristy3ed852e2009-09-05 21:47:34 +00004672 if (memcmp(type,mng_tRNS,4) == 0)
4673 {
4674 /* read global tRNS */
4675
4676 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004677 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004678 mng_info->global_trns[i]=p[i];
4679
4680#ifdef MNG_LOOSE
4681 for ( ; i < 256; i++)
4682 mng_info->global_trns[i]=255;
4683#endif
cristy12560f32010-06-03 16:51:08 +00004684 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00004685 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4686 continue;
4687 }
4688 if (memcmp(type,mng_gAMA,4) == 0)
4689 {
4690 if (length == 4)
4691 {
cristybb503372010-05-27 20:51:26 +00004692 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004693 igamma;
4694
cristy8182b072010-05-30 20:10:53 +00004695 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004696 mng_info->global_gamma=((float) igamma)*0.00001;
4697 mng_info->have_global_gama=MagickTrue;
4698 }
glennrp0fe50b42010-11-16 03:52:51 +00004699
cristy3ed852e2009-09-05 21:47:34 +00004700 else
4701 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004702
cristy3ed852e2009-09-05 21:47:34 +00004703 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4704 continue;
4705 }
4706
4707 if (memcmp(type,mng_cHRM,4) == 0)
4708 {
glennrp47b9dd52010-11-24 18:12:06 +00004709 /* Read global cHRM */
4710
cristy3ed852e2009-09-05 21:47:34 +00004711 if (length == 32)
4712 {
cristy8182b072010-05-30 20:10:53 +00004713 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4714 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4715 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004716 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004717 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004718 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004719 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004720 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004721 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004722 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004723 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004724 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004725 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004726 mng_info->have_global_chrm=MagickTrue;
4727 }
4728 else
4729 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004730
cristy3ed852e2009-09-05 21:47:34 +00004731 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4732 continue;
4733 }
glennrp47b9dd52010-11-24 18:12:06 +00004734
cristy3ed852e2009-09-05 21:47:34 +00004735 if (memcmp(type,mng_sRGB,4) == 0)
4736 {
4737 /*
4738 Read global sRGB.
4739 */
4740 if (length)
4741 {
glennrpe610a072010-08-05 17:08:46 +00004742 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00004743 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004744 mng_info->have_global_srgb=MagickTrue;
4745 }
4746 else
4747 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004748
cristy3ed852e2009-09-05 21:47:34 +00004749 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4750 continue;
4751 }
glennrp47b9dd52010-11-24 18:12:06 +00004752
cristy3ed852e2009-09-05 21:47:34 +00004753 if (memcmp(type,mng_iCCP,4) == 0)
4754 {
glennrpfd05d622011-02-25 04:10:33 +00004755 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004756
4757 /*
4758 Read global iCCP.
4759 */
4760 if (length)
4761 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004762
cristy3ed852e2009-09-05 21:47:34 +00004763 continue;
4764 }
glennrp47b9dd52010-11-24 18:12:06 +00004765
cristy3ed852e2009-09-05 21:47:34 +00004766 if (memcmp(type,mng_FRAM,4) == 0)
4767 {
4768 if (mng_type == 3)
4769 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4770 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4771 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004772
cristy3ed852e2009-09-05 21:47:34 +00004773 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4774 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004775
cristy3ed852e2009-09-05 21:47:34 +00004776 frame_delay=default_frame_delay;
4777 frame_timeout=default_frame_timeout;
4778 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00004779
cristy3ed852e2009-09-05 21:47:34 +00004780 if (length)
4781 if (p[0])
4782 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004783
cristy3ed852e2009-09-05 21:47:34 +00004784 if (logging != MagickFalse)
4785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4786 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00004787
cristy3ed852e2009-09-05 21:47:34 +00004788 if (length > 6)
4789 {
glennrp47b9dd52010-11-24 18:12:06 +00004790 /* Note the delay and frame clipping boundaries. */
4791
cristy3ed852e2009-09-05 21:47:34 +00004792 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00004793
cristybb503372010-05-27 20:51:26 +00004794 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004795 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00004796
cristy3ed852e2009-09-05 21:47:34 +00004797 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00004798
cristybb503372010-05-27 20:51:26 +00004799 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004800 {
4801 int
4802 change_delay,
4803 change_timeout,
4804 change_clipping;
4805
4806 change_delay=(*p++);
4807 change_timeout=(*p++);
4808 change_clipping=(*p++);
4809 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00004810
cristy3ed852e2009-09-05 21:47:34 +00004811 if (change_delay)
4812 {
cristy8182b072010-05-30 20:10:53 +00004813 frame_delay=1UL*image->ticks_per_second*
4814 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004815
cristy8182b072010-05-30 20:10:53 +00004816 if (mng_info->ticks_per_second != 0)
4817 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004818
glennrpbb010dd2010-06-01 13:07:15 +00004819 else
4820 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004821
cristy3ed852e2009-09-05 21:47:34 +00004822 if (change_delay == 2)
4823 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00004824
cristy3ed852e2009-09-05 21:47:34 +00004825 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 if (logging != MagickFalse)
4828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004829 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004830 }
glennrp47b9dd52010-11-24 18:12:06 +00004831
cristy3ed852e2009-09-05 21:47:34 +00004832 if (change_timeout)
4833 {
glennrpbb010dd2010-06-01 13:07:15 +00004834 frame_timeout=1UL*image->ticks_per_second*
4835 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004836
glennrpbb010dd2010-06-01 13:07:15 +00004837 if (mng_info->ticks_per_second != 0)
4838 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004839
glennrpbb010dd2010-06-01 13:07:15 +00004840 else
4841 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00004842
cristy3ed852e2009-09-05 21:47:34 +00004843 if (change_delay == 2)
4844 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00004845
cristy3ed852e2009-09-05 21:47:34 +00004846 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 if (logging != MagickFalse)
4849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004850 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004851 }
glennrp47b9dd52010-11-24 18:12:06 +00004852
cristy3ed852e2009-09-05 21:47:34 +00004853 if (change_clipping)
4854 {
4855 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4856 p+=17;
4857 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if (logging != MagickFalse)
4860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004861 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004862 (double) fb.left,(double) fb.right,(double) fb.top,
4863 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00004864
cristy3ed852e2009-09-05 21:47:34 +00004865 if (change_clipping == 2)
4866 default_fb=fb;
4867 }
4868 }
4869 }
4870 mng_info->clip=fb;
4871 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00004872
cristybb503372010-05-27 20:51:26 +00004873 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004874 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00004875
cristybb503372010-05-27 20:51:26 +00004876 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004877 -mng_info->clip.top);
4878 /*
4879 Insert a background layer behind the frame if framing_mode is 4.
4880 */
4881#if defined(MNG_INSERT_LAYERS)
4882 if (logging != MagickFalse)
4883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004884 " subframe_width=%.20g, subframe_height=%.20g",(double)
4885 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00004886
cristy3ed852e2009-09-05 21:47:34 +00004887 if (insert_layers && (mng_info->framing_mode == 4) &&
4888 (subframe_width) && (subframe_height))
4889 {
glennrp47b9dd52010-11-24 18:12:06 +00004890 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004891 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4892 {
4893 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00004894
cristy3ed852e2009-09-05 21:47:34 +00004895 if (GetNextImageInList(image) == (Image *) NULL)
4896 {
4897 image=DestroyImageList(image);
4898 MngInfoFreeStruct(mng_info,&have_mng_structure);
4899 return((Image *) NULL);
4900 }
glennrp47b9dd52010-11-24 18:12:06 +00004901
cristy3ed852e2009-09-05 21:47:34 +00004902 image=SyncNextImageInList(image);
4903 }
glennrp0fe50b42010-11-16 03:52:51 +00004904
cristy3ed852e2009-09-05 21:47:34 +00004905 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00004906
cristy3ed852e2009-09-05 21:47:34 +00004907 if (term_chunk_found)
4908 {
4909 image->start_loop=MagickTrue;
4910 image->iterations=mng_iterations;
4911 term_chunk_found=MagickFalse;
4912 }
glennrp0fe50b42010-11-16 03:52:51 +00004913
cristy3ed852e2009-09-05 21:47:34 +00004914 else
4915 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004916
cristy3ed852e2009-09-05 21:47:34 +00004917 image->columns=subframe_width;
4918 image->rows=subframe_height;
4919 image->page.width=subframe_width;
4920 image->page.height=subframe_height;
4921 image->page.x=mng_info->clip.left;
4922 image->page.y=mng_info->clip.top;
4923 image->background_color=mng_background_color;
4924 image->matte=MagickFalse;
4925 image->delay=0;
4926 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00004927
cristy3ed852e2009-09-05 21:47:34 +00004928 if (logging != MagickFalse)
4929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00004930 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00004931 (double) mng_info->clip.left,(double) mng_info->clip.right,
4932 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004933 }
4934#endif
4935 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4936 continue;
4937 }
4938 if (memcmp(type,mng_CLIP,4) == 0)
4939 {
4940 unsigned int
4941 first_object,
4942 last_object;
4943
4944 /*
4945 Read CLIP.
4946 */
4947 first_object=(p[0] << 8) | p[1];
4948 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00004949
cristy3ed852e2009-09-05 21:47:34 +00004950 for (i=(int) first_object; i <= (int) last_object; i++)
4951 {
4952 if (mng_info->exists[i] && !mng_info->frozen[i])
4953 {
4954 MngBox
4955 box;
4956
4957 box=mng_info->object_clip[i];
4958 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4959 }
4960 }
glennrp47b9dd52010-11-24 18:12:06 +00004961
cristy3ed852e2009-09-05 21:47:34 +00004962 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4963 continue;
4964 }
4965 if (memcmp(type,mng_SAVE,4) == 0)
4966 {
4967 for (i=1; i < MNG_MAX_OBJECTS; i++)
4968 if (mng_info->exists[i])
4969 {
4970 mng_info->frozen[i]=MagickTrue;
4971#ifdef MNG_OBJECT_BUFFERS
4972 if (mng_info->ob[i] != (MngBuffer *) NULL)
4973 mng_info->ob[i]->frozen=MagickTrue;
4974#endif
4975 }
glennrp0fe50b42010-11-16 03:52:51 +00004976
cristy3ed852e2009-09-05 21:47:34 +00004977 if (length)
4978 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004979
cristy3ed852e2009-09-05 21:47:34 +00004980 continue;
4981 }
4982
4983 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4984 {
glennrp47b9dd52010-11-24 18:12:06 +00004985 /* Read DISC or SEEK. */
4986
cristy3ed852e2009-09-05 21:47:34 +00004987 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4988 {
4989 for (i=1; i < MNG_MAX_OBJECTS; i++)
4990 MngInfoDiscardObject(mng_info,i);
4991 }
glennrp0fe50b42010-11-16 03:52:51 +00004992
cristy3ed852e2009-09-05 21:47:34 +00004993 else
4994 {
cristybb503372010-05-27 20:51:26 +00004995 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004996 j;
4997
cristybb503372010-05-27 20:51:26 +00004998 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00004999 {
5000 i=p[j] << 8 | p[j+1];
5001 MngInfoDiscardObject(mng_info,i);
5002 }
5003 }
glennrp0fe50b42010-11-16 03:52:51 +00005004
cristy3ed852e2009-09-05 21:47:34 +00005005 if (length)
5006 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005007
cristy3ed852e2009-09-05 21:47:34 +00005008 continue;
5009 }
glennrp47b9dd52010-11-24 18:12:06 +00005010
cristy3ed852e2009-09-05 21:47:34 +00005011 if (memcmp(type,mng_MOVE,4) == 0)
5012 {
cristybb503372010-05-27 20:51:26 +00005013 size_t
cristy3ed852e2009-09-05 21:47:34 +00005014 first_object,
5015 last_object;
5016
glennrp47b9dd52010-11-24 18:12:06 +00005017 /* read MOVE */
5018
cristy3ed852e2009-09-05 21:47:34 +00005019 first_object=(p[0] << 8) | p[1];
5020 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005021 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005022 {
5023 if (mng_info->exists[i] && !mng_info->frozen[i])
5024 {
5025 MngPair
5026 new_pair;
5027
5028 MngPair
5029 old_pair;
5030
5031 old_pair.a=mng_info->x_off[i];
5032 old_pair.b=mng_info->y_off[i];
5033 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5034 mng_info->x_off[i]=new_pair.a;
5035 mng_info->y_off[i]=new_pair.b;
5036 }
5037 }
glennrp47b9dd52010-11-24 18:12:06 +00005038
cristy3ed852e2009-09-05 21:47:34 +00005039 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5040 continue;
5041 }
5042
5043 if (memcmp(type,mng_LOOP,4) == 0)
5044 {
cristybb503372010-05-27 20:51:26 +00005045 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005046 loop_level=chunk[0];
5047 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005048
5049 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005050 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005051
cristy3ed852e2009-09-05 21:47:34 +00005052 if (logging != MagickFalse)
5053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005054 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5055 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005056
cristy3ed852e2009-09-05 21:47:34 +00005057 if (loop_iters == 0)
5058 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005059
cristy3ed852e2009-09-05 21:47:34 +00005060 else
5061 {
5062 mng_info->loop_jump[loop_level]=TellBlob(image);
5063 mng_info->loop_count[loop_level]=loop_iters;
5064 }
glennrp0fe50b42010-11-16 03:52:51 +00005065
cristy3ed852e2009-09-05 21:47:34 +00005066 mng_info->loop_iteration[loop_level]=0;
5067 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5068 continue;
5069 }
glennrp47b9dd52010-11-24 18:12:06 +00005070
cristy3ed852e2009-09-05 21:47:34 +00005071 if (memcmp(type,mng_ENDL,4) == 0)
5072 {
5073 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005074
cristy3ed852e2009-09-05 21:47:34 +00005075 if (skipping_loop > 0)
5076 {
5077 if (skipping_loop == loop_level)
5078 {
5079 /*
5080 Found end of zero-iteration loop.
5081 */
5082 skipping_loop=(-1);
5083 mng_info->loop_active[loop_level]=0;
5084 }
5085 }
glennrp47b9dd52010-11-24 18:12:06 +00005086
cristy3ed852e2009-09-05 21:47:34 +00005087 else
5088 {
5089 if (mng_info->loop_active[loop_level] == 1)
5090 {
5091 mng_info->loop_count[loop_level]--;
5092 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005093
cristy3ed852e2009-09-05 21:47:34 +00005094 if (logging != MagickFalse)
5095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005096 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005097 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005098 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005099
cristy3ed852e2009-09-05 21:47:34 +00005100 if (mng_info->loop_count[loop_level] != 0)
5101 {
5102 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5103 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005104
cristy3ed852e2009-09-05 21:47:34 +00005105 if (offset < 0)
5106 ThrowReaderException(CorruptImageError,
5107 "ImproperImageHeader");
5108 }
glennrp47b9dd52010-11-24 18:12:06 +00005109
cristy3ed852e2009-09-05 21:47:34 +00005110 else
5111 {
5112 short
5113 last_level;
5114
5115 /*
5116 Finished loop.
5117 */
5118 mng_info->loop_active[loop_level]=0;
5119 last_level=(-1);
5120 for (i=0; i < loop_level; i++)
5121 if (mng_info->loop_active[i] == 1)
5122 last_level=(short) i;
5123 loop_level=last_level;
5124 }
5125 }
5126 }
glennrp47b9dd52010-11-24 18:12:06 +00005127
cristy3ed852e2009-09-05 21:47:34 +00005128 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5129 continue;
5130 }
glennrp47b9dd52010-11-24 18:12:06 +00005131
cristy3ed852e2009-09-05 21:47:34 +00005132 if (memcmp(type,mng_CLON,4) == 0)
5133 {
5134 if (mng_info->clon_warning == 0)
5135 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5136 CoderError,"CLON is not implemented yet","`%s'",
5137 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005138
cristy3ed852e2009-09-05 21:47:34 +00005139 mng_info->clon_warning++;
5140 }
glennrp47b9dd52010-11-24 18:12:06 +00005141
cristy3ed852e2009-09-05 21:47:34 +00005142 if (memcmp(type,mng_MAGN,4) == 0)
5143 {
5144 png_uint_16
5145 magn_first,
5146 magn_last,
5147 magn_mb,
5148 magn_ml,
5149 magn_mr,
5150 magn_mt,
5151 magn_mx,
5152 magn_my,
5153 magn_methx,
5154 magn_methy;
5155
5156 if (length > 1)
5157 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005158
cristy3ed852e2009-09-05 21:47:34 +00005159 else
5160 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005161
cristy3ed852e2009-09-05 21:47:34 +00005162 if (length > 3)
5163 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005164
cristy3ed852e2009-09-05 21:47:34 +00005165 else
5166 magn_last=magn_first;
5167#ifndef MNG_OBJECT_BUFFERS
5168 if (magn_first || magn_last)
5169 if (mng_info->magn_warning == 0)
5170 {
5171 (void) ThrowMagickException(&image->exception,
5172 GetMagickModule(),CoderError,
5173 "MAGN is not implemented yet for nonzero objects",
5174 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005175
cristy3ed852e2009-09-05 21:47:34 +00005176 mng_info->magn_warning++;
5177 }
5178#endif
5179 if (length > 4)
5180 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005181
cristy3ed852e2009-09-05 21:47:34 +00005182 else
5183 magn_methx=0;
5184
5185 if (length > 6)
5186 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005187
cristy3ed852e2009-09-05 21:47:34 +00005188 else
5189 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005190
cristy3ed852e2009-09-05 21:47:34 +00005191 if (magn_mx == 0)
5192 magn_mx=1;
5193
5194 if (length > 8)
5195 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005196
cristy3ed852e2009-09-05 21:47:34 +00005197 else
5198 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005199
cristy3ed852e2009-09-05 21:47:34 +00005200 if (magn_my == 0)
5201 magn_my=1;
5202
5203 if (length > 10)
5204 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005205
cristy3ed852e2009-09-05 21:47:34 +00005206 else
5207 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005208
cristy3ed852e2009-09-05 21:47:34 +00005209 if (magn_ml == 0)
5210 magn_ml=1;
5211
5212 if (length > 12)
5213 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005214
cristy3ed852e2009-09-05 21:47:34 +00005215 else
5216 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005217
cristy3ed852e2009-09-05 21:47:34 +00005218 if (magn_mr == 0)
5219 magn_mr=1;
5220
5221 if (length > 14)
5222 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005223
cristy3ed852e2009-09-05 21:47:34 +00005224 else
5225 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005226
cristy3ed852e2009-09-05 21:47:34 +00005227 if (magn_mt == 0)
5228 magn_mt=1;
5229
5230 if (length > 16)
5231 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005232
cristy3ed852e2009-09-05 21:47:34 +00005233 else
5234 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005235
cristy3ed852e2009-09-05 21:47:34 +00005236 if (magn_mb == 0)
5237 magn_mb=1;
5238
5239 if (length > 17)
5240 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005241
cristy3ed852e2009-09-05 21:47:34 +00005242 else
5243 magn_methy=magn_methx;
5244
glennrp47b9dd52010-11-24 18:12:06 +00005245
cristy3ed852e2009-09-05 21:47:34 +00005246 if (magn_methx > 5 || magn_methy > 5)
5247 if (mng_info->magn_warning == 0)
5248 {
5249 (void) ThrowMagickException(&image->exception,
5250 GetMagickModule(),CoderError,
5251 "Unknown MAGN method in MNG datastream","`%s'",
5252 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005253
cristy3ed852e2009-09-05 21:47:34 +00005254 mng_info->magn_warning++;
5255 }
5256#ifdef MNG_OBJECT_BUFFERS
5257 /* Magnify existing objects in the range magn_first to magn_last */
5258#endif
5259 if (magn_first == 0 || magn_last == 0)
5260 {
5261 /* Save the magnification factors for object 0 */
5262 mng_info->magn_mb=magn_mb;
5263 mng_info->magn_ml=magn_ml;
5264 mng_info->magn_mr=magn_mr;
5265 mng_info->magn_mt=magn_mt;
5266 mng_info->magn_mx=magn_mx;
5267 mng_info->magn_my=magn_my;
5268 mng_info->magn_methx=magn_methx;
5269 mng_info->magn_methy=magn_methy;
5270 }
5271 }
glennrp47b9dd52010-11-24 18:12:06 +00005272
cristy3ed852e2009-09-05 21:47:34 +00005273 if (memcmp(type,mng_PAST,4) == 0)
5274 {
5275 if (mng_info->past_warning == 0)
5276 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5277 CoderError,"PAST is not implemented yet","`%s'",
5278 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005279
cristy3ed852e2009-09-05 21:47:34 +00005280 mng_info->past_warning++;
5281 }
glennrp47b9dd52010-11-24 18:12:06 +00005282
cristy3ed852e2009-09-05 21:47:34 +00005283 if (memcmp(type,mng_SHOW,4) == 0)
5284 {
5285 if (mng_info->show_warning == 0)
5286 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5287 CoderError,"SHOW is not implemented yet","`%s'",
5288 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005289
cristy3ed852e2009-09-05 21:47:34 +00005290 mng_info->show_warning++;
5291 }
glennrp47b9dd52010-11-24 18:12:06 +00005292
cristy3ed852e2009-09-05 21:47:34 +00005293 if (memcmp(type,mng_sBIT,4) == 0)
5294 {
5295 if (length < 4)
5296 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005297
cristy3ed852e2009-09-05 21:47:34 +00005298 else
5299 {
5300 mng_info->global_sbit.gray=p[0];
5301 mng_info->global_sbit.red=p[0];
5302 mng_info->global_sbit.green=p[1];
5303 mng_info->global_sbit.blue=p[2];
5304 mng_info->global_sbit.alpha=p[3];
5305 mng_info->have_global_sbit=MagickTrue;
5306 }
5307 }
5308 if (memcmp(type,mng_pHYs,4) == 0)
5309 {
5310 if (length > 8)
5311 {
5312 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005313 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005314 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005315 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005316 mng_info->global_phys_unit_type=p[8];
5317 mng_info->have_global_phys=MagickTrue;
5318 }
glennrp47b9dd52010-11-24 18:12:06 +00005319
cristy3ed852e2009-09-05 21:47:34 +00005320 else
5321 mng_info->have_global_phys=MagickFalse;
5322 }
5323 if (memcmp(type,mng_pHYg,4) == 0)
5324 {
5325 if (mng_info->phyg_warning == 0)
5326 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5327 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005328
cristy3ed852e2009-09-05 21:47:34 +00005329 mng_info->phyg_warning++;
5330 }
5331 if (memcmp(type,mng_BASI,4) == 0)
5332 {
5333 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005334
cristy3ed852e2009-09-05 21:47:34 +00005335 if (mng_info->basi_warning == 0)
5336 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5337 CoderError,"BASI is not implemented yet","`%s'",
5338 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005339
cristy3ed852e2009-09-05 21:47:34 +00005340 mng_info->basi_warning++;
5341#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005342 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005343 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005344 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005345 (p[6] << 8) | p[7]);
5346 basi_color_type=p[8];
5347 basi_compression_method=p[9];
5348 basi_filter_type=p[10];
5349 basi_interlace_method=p[11];
5350 if (length > 11)
5351 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005352
cristy3ed852e2009-09-05 21:47:34 +00005353 else
5354 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005355
cristy3ed852e2009-09-05 21:47:34 +00005356 if (length > 13)
5357 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005358
cristy3ed852e2009-09-05 21:47:34 +00005359 else
5360 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005361
cristy3ed852e2009-09-05 21:47:34 +00005362 if (length > 15)
5363 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005364
cristy3ed852e2009-09-05 21:47:34 +00005365 else
5366 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005367
cristy3ed852e2009-09-05 21:47:34 +00005368 if (length > 17)
5369 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005370
cristy3ed852e2009-09-05 21:47:34 +00005371 else
5372 {
5373 if (basi_sample_depth == 16)
5374 basi_alpha=65535L;
5375 else
5376 basi_alpha=255;
5377 }
glennrp47b9dd52010-11-24 18:12:06 +00005378
cristy3ed852e2009-09-05 21:47:34 +00005379 if (length > 19)
5380 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005381
cristy3ed852e2009-09-05 21:47:34 +00005382 else
5383 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005384
cristy3ed852e2009-09-05 21:47:34 +00005385#endif
5386 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5387 continue;
5388 }
glennrp47b9dd52010-11-24 18:12:06 +00005389
cristy3ed852e2009-09-05 21:47:34 +00005390 if (memcmp(type,mng_IHDR,4)
5391#if defined(JNG_SUPPORTED)
5392 && memcmp(type,mng_JHDR,4)
5393#endif
5394 )
5395 {
5396 /* Not an IHDR or JHDR chunk */
5397 if (length)
5398 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005399
cristy3ed852e2009-09-05 21:47:34 +00005400 continue;
5401 }
5402/* Process IHDR */
5403 if (logging != MagickFalse)
5404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5405 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005406
cristy3ed852e2009-09-05 21:47:34 +00005407 mng_info->exists[object_id]=MagickTrue;
5408 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005409
cristy3ed852e2009-09-05 21:47:34 +00005410 if (mng_info->invisible[object_id])
5411 {
5412 if (logging != MagickFalse)
5413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5414 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005415
cristy3ed852e2009-09-05 21:47:34 +00005416 skip_to_iend=MagickTrue;
5417 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5418 continue;
5419 }
5420#if defined(MNG_INSERT_LAYERS)
5421 if (length < 8)
5422 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005423
cristy8182b072010-05-30 20:10:53 +00005424 image_width=(size_t) mng_get_long(p);
5425 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005426#endif
5427 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5428
5429 /*
5430 Insert a transparent background layer behind the entire animation
5431 if it is not full screen.
5432 */
5433#if defined(MNG_INSERT_LAYERS)
5434 if (insert_layers && mng_type && first_mng_object)
5435 {
5436 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5437 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005438 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005439 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005440 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005441 {
5442 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5443 {
5444 /*
5445 Allocate next image structure.
5446 */
5447 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005448
cristy3ed852e2009-09-05 21:47:34 +00005449 if (GetNextImageInList(image) == (Image *) NULL)
5450 {
5451 image=DestroyImageList(image);
5452 MngInfoFreeStruct(mng_info,&have_mng_structure);
5453 return((Image *) NULL);
5454 }
glennrp47b9dd52010-11-24 18:12:06 +00005455
cristy3ed852e2009-09-05 21:47:34 +00005456 image=SyncNextImageInList(image);
5457 }
5458 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005459
cristy3ed852e2009-09-05 21:47:34 +00005460 if (term_chunk_found)
5461 {
5462 image->start_loop=MagickTrue;
5463 image->iterations=mng_iterations;
5464 term_chunk_found=MagickFalse;
5465 }
glennrp47b9dd52010-11-24 18:12:06 +00005466
cristy3ed852e2009-09-05 21:47:34 +00005467 else
5468 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005469
5470 /* Make a background rectangle. */
5471
cristy3ed852e2009-09-05 21:47:34 +00005472 image->delay=0;
5473 image->columns=mng_info->mng_width;
5474 image->rows=mng_info->mng_height;
5475 image->page.width=mng_info->mng_width;
5476 image->page.height=mng_info->mng_height;
5477 image->page.x=0;
5478 image->page.y=0;
5479 image->background_color=mng_background_color;
5480 (void) SetImageBackgroundColor(image);
5481 if (logging != MagickFalse)
5482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005483 " Inserted transparent background layer, W=%.20g, H=%.20g",
5484 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005485 }
5486 }
5487 /*
5488 Insert a background layer behind the upcoming image if
5489 framing_mode is 3, and we haven't already inserted one.
5490 */
5491 if (insert_layers && (mng_info->framing_mode == 3) &&
5492 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5493 (simplicity & 0x08)))
5494 {
5495 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5496 {
5497 /*
5498 Allocate next image structure.
5499 */
5500 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005501
cristy3ed852e2009-09-05 21:47:34 +00005502 if (GetNextImageInList(image) == (Image *) NULL)
5503 {
5504 image=DestroyImageList(image);
5505 MngInfoFreeStruct(mng_info,&have_mng_structure);
5506 return((Image *) NULL);
5507 }
glennrp47b9dd52010-11-24 18:12:06 +00005508
cristy3ed852e2009-09-05 21:47:34 +00005509 image=SyncNextImageInList(image);
5510 }
glennrp0fe50b42010-11-16 03:52:51 +00005511
cristy3ed852e2009-09-05 21:47:34 +00005512 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005513
cristy3ed852e2009-09-05 21:47:34 +00005514 if (term_chunk_found)
5515 {
5516 image->start_loop=MagickTrue;
5517 image->iterations=mng_iterations;
5518 term_chunk_found=MagickFalse;
5519 }
glennrp0fe50b42010-11-16 03:52:51 +00005520
cristy3ed852e2009-09-05 21:47:34 +00005521 else
5522 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005523
cristy3ed852e2009-09-05 21:47:34 +00005524 image->delay=0;
5525 image->columns=subframe_width;
5526 image->rows=subframe_height;
5527 image->page.width=subframe_width;
5528 image->page.height=subframe_height;
5529 image->page.x=mng_info->clip.left;
5530 image->page.y=mng_info->clip.top;
5531 image->background_color=mng_background_color;
5532 image->matte=MagickFalse;
5533 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005534
cristy3ed852e2009-09-05 21:47:34 +00005535 if (logging != MagickFalse)
5536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005537 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005538 (double) mng_info->clip.left,(double) mng_info->clip.right,
5539 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005540 }
5541#endif /* MNG_INSERT_LAYERS */
5542 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005543
cristy3ed852e2009-09-05 21:47:34 +00005544 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5545 {
5546 /*
5547 Allocate next image structure.
5548 */
5549 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005550
cristy3ed852e2009-09-05 21:47:34 +00005551 if (GetNextImageInList(image) == (Image *) NULL)
5552 {
5553 image=DestroyImageList(image);
5554 MngInfoFreeStruct(mng_info,&have_mng_structure);
5555 return((Image *) NULL);
5556 }
glennrp47b9dd52010-11-24 18:12:06 +00005557
cristy3ed852e2009-09-05 21:47:34 +00005558 image=SyncNextImageInList(image);
5559 }
5560 mng_info->image=image;
5561 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5562 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005563
cristy3ed852e2009-09-05 21:47:34 +00005564 if (status == MagickFalse)
5565 break;
glennrp0fe50b42010-11-16 03:52:51 +00005566
cristy3ed852e2009-09-05 21:47:34 +00005567 if (term_chunk_found)
5568 {
5569 image->start_loop=MagickTrue;
5570 term_chunk_found=MagickFalse;
5571 }
glennrp0fe50b42010-11-16 03:52:51 +00005572
cristy3ed852e2009-09-05 21:47:34 +00005573 else
5574 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5577 {
5578 image->delay=frame_delay;
5579 frame_delay=default_frame_delay;
5580 }
glennrp0fe50b42010-11-16 03:52:51 +00005581
cristy3ed852e2009-09-05 21:47:34 +00005582 else
5583 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005584
cristy3ed852e2009-09-05 21:47:34 +00005585 image->page.width=mng_info->mng_width;
5586 image->page.height=mng_info->mng_height;
5587 image->page.x=mng_info->x_off[object_id];
5588 image->page.y=mng_info->y_off[object_id];
5589 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00005590
cristy3ed852e2009-09-05 21:47:34 +00005591 /*
5592 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5593 */
glennrp47b9dd52010-11-24 18:12:06 +00005594
cristy3ed852e2009-09-05 21:47:34 +00005595 if (logging != MagickFalse)
5596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5597 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5598 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005599
cristybb503372010-05-27 20:51:26 +00005600 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00005601
cristy3ed852e2009-09-05 21:47:34 +00005602 if (offset < 0)
5603 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5604 }
5605
5606 previous=image;
5607 mng_info->image=image;
5608 mng_info->mng_type=mng_type;
5609 mng_info->object_id=object_id;
5610
5611 if (memcmp(type,mng_IHDR,4) == 0)
5612 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005613
cristy3ed852e2009-09-05 21:47:34 +00005614#if defined(JNG_SUPPORTED)
5615 else
5616 image=ReadOneJNGImage(mng_info,image_info,exception);
5617#endif
5618
5619 if (image == (Image *) NULL)
5620 {
5621 if (IsImageObject(previous) != MagickFalse)
5622 {
5623 (void) DestroyImageList(previous);
5624 (void) CloseBlob(previous);
5625 }
glennrp47b9dd52010-11-24 18:12:06 +00005626
cristy3ed852e2009-09-05 21:47:34 +00005627 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 if (image->columns == 0 || image->rows == 0)
5632 {
5633 (void) CloseBlob(image);
5634 image=DestroyImageList(image);
5635 MngInfoFreeStruct(mng_info,&have_mng_structure);
5636 return((Image *) NULL);
5637 }
glennrp0fe50b42010-11-16 03:52:51 +00005638
cristy3ed852e2009-09-05 21:47:34 +00005639 mng_info->image=image;
5640
5641 if (mng_type)
5642 {
5643 MngBox
5644 crop_box;
5645
5646 if (mng_info->magn_methx || mng_info->magn_methy)
5647 {
5648 png_uint_32
5649 magnified_height,
5650 magnified_width;
5651
5652 if (logging != MagickFalse)
5653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5654 " Processing MNG MAGN chunk");
5655
5656 if (mng_info->magn_methx == 1)
5657 {
5658 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005659
cristy3ed852e2009-09-05 21:47:34 +00005660 if (image->columns > 1)
5661 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005662
cristy3ed852e2009-09-05 21:47:34 +00005663 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005664 magnified_width += (png_uint_32)
5665 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00005666 }
glennrp47b9dd52010-11-24 18:12:06 +00005667
cristy3ed852e2009-09-05 21:47:34 +00005668 else
5669 {
cristy4e5bc842010-06-09 13:56:01 +00005670 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00005671
cristy3ed852e2009-09-05 21:47:34 +00005672 if (image->columns > 1)
5673 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00005674
cristy3ed852e2009-09-05 21:47:34 +00005675 if (image->columns > 2)
5676 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00005677
cristy3ed852e2009-09-05 21:47:34 +00005678 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005679 magnified_width += (png_uint_32)
5680 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00005681 }
glennrp47b9dd52010-11-24 18:12:06 +00005682
cristy3ed852e2009-09-05 21:47:34 +00005683 if (mng_info->magn_methy == 1)
5684 {
5685 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005686
cristy3ed852e2009-09-05 21:47:34 +00005687 if (image->rows > 1)
5688 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005689
cristy3ed852e2009-09-05 21:47:34 +00005690 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00005691 magnified_height += (png_uint_32)
5692 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00005693 }
glennrp47b9dd52010-11-24 18:12:06 +00005694
cristy3ed852e2009-09-05 21:47:34 +00005695 else
5696 {
cristy4e5bc842010-06-09 13:56:01 +00005697 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00005698
cristy3ed852e2009-09-05 21:47:34 +00005699 if (image->rows > 1)
5700 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00005701
cristy3ed852e2009-09-05 21:47:34 +00005702 if (image->rows > 2)
5703 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00005704
cristy3ed852e2009-09-05 21:47:34 +00005705 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00005706 magnified_height += (png_uint_32)
5707 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00005708 }
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 if (magnified_height > image->rows ||
5711 magnified_width > image->columns)
5712 {
5713 Image
5714 *large_image;
5715
5716 int
5717 yy;
5718
cristybb503372010-05-27 20:51:26 +00005719 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005720 m,
5721 y;
5722
cristybb503372010-05-27 20:51:26 +00005723 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005724 x;
5725
5726 register PixelPacket
5727 *n,
5728 *q;
5729
5730 PixelPacket
5731 *next,
5732 *prev;
5733
5734 png_uint_16
5735 magn_methx,
5736 magn_methy;
5737
glennrp47b9dd52010-11-24 18:12:06 +00005738 /* Allocate next image structure. */
5739
cristy3ed852e2009-09-05 21:47:34 +00005740 if (logging != MagickFalse)
5741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5742 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00005743
cristy3ed852e2009-09-05 21:47:34 +00005744 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005745
cristy3ed852e2009-09-05 21:47:34 +00005746 if (GetNextImageInList(image) == (Image *) NULL)
5747 {
5748 image=DestroyImageList(image);
5749 MngInfoFreeStruct(mng_info,&have_mng_structure);
5750 return((Image *) NULL);
5751 }
5752
5753 large_image=SyncNextImageInList(image);
5754
5755 large_image->columns=magnified_width;
5756 large_image->rows=magnified_height;
5757
5758 magn_methx=mng_info->magn_methx;
5759 magn_methy=mng_info->magn_methy;
5760
glennrp3faa9a32011-04-23 14:00:25 +00005761#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00005762#define QM unsigned short
5763 if (magn_methx != 1 || magn_methy != 1)
5764 {
5765 /*
5766 Scale pixels to unsigned shorts to prevent
5767 overflow of intermediate values of interpolations
5768 */
cristybb503372010-05-27 20:51:26 +00005769 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005770 {
5771 q=GetAuthenticPixels(image,0,y,image->columns,1,
5772 exception);
glennrp47b9dd52010-11-24 18:12:06 +00005773
cristybb503372010-05-27 20:51:26 +00005774 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005775 {
5776 q->red=ScaleQuantumToShort(q->red);
5777 q->green=ScaleQuantumToShort(q->green);
5778 q->blue=ScaleQuantumToShort(q->blue);
5779 q->opacity=ScaleQuantumToShort(q->opacity);
5780 q++;
5781 }
glennrp47b9dd52010-11-24 18:12:06 +00005782
cristy3ed852e2009-09-05 21:47:34 +00005783 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5784 break;
5785 }
5786 }
5787#else
5788#define QM Quantum
5789#endif
5790
5791 if (image->matte != MagickFalse)
5792 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005793
cristy3ed852e2009-09-05 21:47:34 +00005794 else
5795 {
5796 large_image->background_color.opacity=OpaqueOpacity;
5797 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 if (magn_methx == 4)
5800 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristy3ed852e2009-09-05 21:47:34 +00005802 if (magn_methx == 5)
5803 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 if (magn_methy == 4)
5806 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 if (magn_methy == 5)
5809 magn_methy=3;
5810 }
5811
5812 /* magnify the rows into the right side of the large image */
5813
5814 if (logging != MagickFalse)
5815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005816 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00005817 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005818 yy=0;
5819 length=(size_t) image->columns;
5820 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5821 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00005822
cristy3ed852e2009-09-05 21:47:34 +00005823 if ((prev == (PixelPacket *) NULL) ||
5824 (next == (PixelPacket *) NULL))
5825 {
5826 image=DestroyImageList(image);
5827 MngInfoFreeStruct(mng_info,&have_mng_structure);
5828 ThrowReaderException(ResourceLimitError,
5829 "MemoryAllocationFailed");
5830 }
glennrp47b9dd52010-11-24 18:12:06 +00005831
cristy3ed852e2009-09-05 21:47:34 +00005832 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5833 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristybb503372010-05-27 20:51:26 +00005835 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005836 {
5837 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005838 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00005839
cristybb503372010-05-27 20:51:26 +00005840 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5841 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005842
cristybb503372010-05-27 20:51:26 +00005843 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5844 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00005845
cristybb503372010-05-27 20:51:26 +00005846 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005847 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005848
cristy3ed852e2009-09-05 21:47:34 +00005849 else
cristybb503372010-05-27 20:51:26 +00005850 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005851
cristy3ed852e2009-09-05 21:47:34 +00005852 n=prev;
5853 prev=next;
5854 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00005855
cristybb503372010-05-27 20:51:26 +00005856 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005857 {
5858 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5859 exception);
5860 (void) CopyMagickMemory(next,n,length);
5861 }
glennrp47b9dd52010-11-24 18:12:06 +00005862
cristy3ed852e2009-09-05 21:47:34 +00005863 for (i=0; i < m; i++, yy++)
5864 {
5865 register PixelPacket
5866 *pixels;
5867
cristybb503372010-05-27 20:51:26 +00005868 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005869 pixels=prev;
5870 n=next;
5871 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5872 1,exception);
5873 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00005874
cristybb503372010-05-27 20:51:26 +00005875 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005876 {
glennrpfd05d622011-02-25 04:10:33 +00005877 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005878 /*
5879 if (image->storage_class == PseudoClass)
5880 {
5881 }
5882 */
5883
5884 if (magn_methy <= 1)
5885 {
5886 *q=(*pixels); /* replicate previous */
5887 }
glennrp47b9dd52010-11-24 18:12:06 +00005888
cristy3ed852e2009-09-05 21:47:34 +00005889 else if (magn_methy == 2 || magn_methy == 4)
5890 {
5891 if (i == 0)
5892 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005893
cristy3ed852e2009-09-05 21:47:34 +00005894 else
5895 {
5896 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005897 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5898 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005899 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005900 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5901 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005902 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005903 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5904 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005905 +(*pixels).blue);
glennrp47b9dd52010-11-24 18:12:06 +00005906
cristy3ed852e2009-09-05 21:47:34 +00005907 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005908 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005909 (2*i*((*n).opacity
5910 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005911 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005912 }
glennrp47b9dd52010-11-24 18:12:06 +00005913
cristy3ed852e2009-09-05 21:47:34 +00005914 if (magn_methy == 4)
5915 {
5916 /* Replicate nearest */
5917 if (i <= ((m+1) << 1))
5918 (*q).opacity=(*pixels).opacity+0;
5919 else
5920 (*q).opacity=(*n).opacity+0;
5921 }
5922 }
glennrp47b9dd52010-11-24 18:12:06 +00005923
cristy3ed852e2009-09-05 21:47:34 +00005924 else /* if (magn_methy == 3 || magn_methy == 5) */
5925 {
5926 /* Replicate nearest */
5927 if (i <= ((m+1) << 1))
5928 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00005929
cristy3ed852e2009-09-05 21:47:34 +00005930 else
5931 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00005932
cristy3ed852e2009-09-05 21:47:34 +00005933 if (magn_methy == 5)
5934 {
cristybb503372010-05-27 20:51:26 +00005935 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5936 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005937 +(*pixels).opacity);
5938 }
5939 }
5940 n++;
5941 q++;
5942 pixels++;
5943 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00005944
cristy3ed852e2009-09-05 21:47:34 +00005945 if (SyncAuthenticPixels(large_image,exception) == 0)
5946 break;
glennrp47b9dd52010-11-24 18:12:06 +00005947
cristy3ed852e2009-09-05 21:47:34 +00005948 } /* i */
5949 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00005950
cristy3ed852e2009-09-05 21:47:34 +00005951 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5952 next=(PixelPacket *) RelinquishMagickMemory(next);
5953
5954 length=image->columns;
5955
5956 if (logging != MagickFalse)
5957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5958 " Delete original image");
5959
5960 DeleteImageFromList(&image);
5961
5962 image=large_image;
5963
5964 mng_info->image=image;
5965
5966 /* magnify the columns */
5967 if (logging != MagickFalse)
5968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005969 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00005970
cristybb503372010-05-27 20:51:26 +00005971 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005972 {
5973 register PixelPacket
5974 *pixels;
5975
5976 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5977 pixels=q+(image->columns-length);
5978 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00005979
cristybb503372010-05-27 20:51:26 +00005980 for (x=(ssize_t) (image->columns-length);
5981 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005982 {
cristybb503372010-05-27 20:51:26 +00005983 if (x == (ssize_t) (image->columns-length))
5984 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00005985
cristybb503372010-05-27 20:51:26 +00005986 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5987 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005988
cristybb503372010-05-27 20:51:26 +00005989 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5990 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00005991
cristybb503372010-05-27 20:51:26 +00005992 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00005993 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00005994
cristy3ed852e2009-09-05 21:47:34 +00005995 else
cristybb503372010-05-27 20:51:26 +00005996 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005997
cristy3ed852e2009-09-05 21:47:34 +00005998 for (i=0; i < m; i++)
5999 {
6000 if (magn_methx <= 1)
6001 {
6002 /* replicate previous */
6003 *q=(*pixels);
6004 }
glennrp47b9dd52010-11-24 18:12:06 +00006005
cristy3ed852e2009-09-05 21:47:34 +00006006 else if (magn_methx == 2 || magn_methx == 4)
6007 {
6008 if (i == 0)
6009 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006010
cristy3ed852e2009-09-05 21:47:34 +00006011 else
6012 {
6013 /* Interpolate */
6014 (*q).red=(QM) ((2*i*((*n).red
6015 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00006016 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00006017 (*q).green=(QM) ((2*i*((*n).green
6018 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00006019 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00006020 (*q).blue=(QM) ((2*i*((*n).blue
6021 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00006022 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00006023 if (image->matte != MagickFalse)
6024 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006025 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006026 +(*pixels).opacity);
6027 }
glennrp47b9dd52010-11-24 18:12:06 +00006028
cristy3ed852e2009-09-05 21:47:34 +00006029 if (magn_methx == 4)
6030 {
6031 /* Replicate nearest */
6032 if (i <= ((m+1) << 1))
6033 (*q).opacity=(*pixels).opacity+0;
6034 else
6035 (*q).opacity=(*n).opacity+0;
6036 }
6037 }
glennrp47b9dd52010-11-24 18:12:06 +00006038
cristy3ed852e2009-09-05 21:47:34 +00006039 else /* if (magn_methx == 3 || magn_methx == 5) */
6040 {
6041 /* Replicate nearest */
6042 if (i <= ((m+1) << 1))
6043 *q=(*pixels);
glennrp47b9dd52010-11-24 18:12:06 +00006044
cristy3ed852e2009-09-05 21:47:34 +00006045 else
6046 *q=(*n);
glennrp47b9dd52010-11-24 18:12:06 +00006047
cristy3ed852e2009-09-05 21:47:34 +00006048 if (magn_methx == 5)
6049 {
6050 /* Interpolate */
6051 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00006052 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00006053 +(*pixels).opacity);
6054 }
6055 }
6056 q++;
6057 }
6058 n++;
6059 p++;
6060 }
glennrp47b9dd52010-11-24 18:12:06 +00006061
cristy3ed852e2009-09-05 21:47:34 +00006062 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6063 break;
6064 }
glennrp3faa9a32011-04-23 14:00:25 +00006065#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006066 if (magn_methx != 1 || magn_methy != 1)
6067 {
6068 /*
6069 Rescale pixels to Quantum
6070 */
cristybb503372010-05-27 20:51:26 +00006071 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006072 {
6073 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006074
cristybb503372010-05-27 20:51:26 +00006075 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006076 {
6077 q->red=ScaleShortToQuantum(q->red);
6078 q->green=ScaleShortToQuantum(q->green);
6079 q->blue=ScaleShortToQuantum(q->blue);
6080 q->opacity=ScaleShortToQuantum(q->opacity);
6081 q++;
6082 }
glennrp47b9dd52010-11-24 18:12:06 +00006083
cristy3ed852e2009-09-05 21:47:34 +00006084 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6085 break;
6086 }
6087 }
6088#endif
6089 if (logging != MagickFalse)
6090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6091 " Finished MAGN processing");
6092 }
6093 }
6094
6095 /*
6096 Crop_box is with respect to the upper left corner of the MNG.
6097 */
6098 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6099 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6100 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6101 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6102 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6103 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6104 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6105 if ((crop_box.left != (mng_info->image_box.left
6106 +mng_info->x_off[object_id])) ||
6107 (crop_box.right != (mng_info->image_box.right
6108 +mng_info->x_off[object_id])) ||
6109 (crop_box.top != (mng_info->image_box.top
6110 +mng_info->y_off[object_id])) ||
6111 (crop_box.bottom != (mng_info->image_box.bottom
6112 +mng_info->y_off[object_id])))
6113 {
6114 if (logging != MagickFalse)
6115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6116 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006117
cristy3ed852e2009-09-05 21:47:34 +00006118 if ((crop_box.left < crop_box.right) &&
6119 (crop_box.top < crop_box.bottom))
6120 {
6121 Image
6122 *im;
6123
6124 RectangleInfo
6125 crop_info;
6126
6127 /*
6128 Crop_info is with respect to the upper left corner of
6129 the image.
6130 */
6131 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6132 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006133 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6134 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006135 image->page.width=image->columns;
6136 image->page.height=image->rows;
6137 image->page.x=0;
6138 image->page.y=0;
6139 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006140
cristy3ed852e2009-09-05 21:47:34 +00006141 if (im != (Image *) NULL)
6142 {
6143 image->columns=im->columns;
6144 image->rows=im->rows;
6145 im=DestroyImage(im);
6146 image->page.width=image->columns;
6147 image->page.height=image->rows;
6148 image->page.x=crop_box.left;
6149 image->page.y=crop_box.top;
6150 }
6151 }
glennrp47b9dd52010-11-24 18:12:06 +00006152
cristy3ed852e2009-09-05 21:47:34 +00006153 else
6154 {
6155 /*
6156 No pixels in crop area. The MNG spec still requires
6157 a layer, though, so make a single transparent pixel in
6158 the top left corner.
6159 */
6160 image->columns=1;
6161 image->rows=1;
6162 image->colors=2;
6163 (void) SetImageBackgroundColor(image);
6164 image->page.width=1;
6165 image->page.height=1;
6166 image->page.x=0;
6167 image->page.y=0;
6168 }
6169 }
6170#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6171 image=mng_info->image;
6172#endif
6173 }
6174
glennrp2b013e42010-11-24 16:55:50 +00006175#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6176 /* PNG does not handle depths greater than 16 so reduce it even
6177 * if lossy
6178 */
6179 if (image->depth > 16)
6180 image->depth=16;
6181#endif
6182
glennrp3faa9a32011-04-23 14:00:25 +00006183#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006184 if (LosslessReduceDepthOK(image) != MagickFalse)
6185 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006186#endif
glennrpd6afd542010-11-19 01:53:05 +00006187
cristy3ed852e2009-09-05 21:47:34 +00006188 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006189
cristy3ed852e2009-09-05 21:47:34 +00006190 if (image_info->number_scenes != 0)
6191 {
6192 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006193 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006194 break;
6195 }
glennrpd6afd542010-11-19 01:53:05 +00006196
cristy3ed852e2009-09-05 21:47:34 +00006197 if (logging != MagickFalse)
6198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6199 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006200
cristy3ed852e2009-09-05 21:47:34 +00006201 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006202
cristy3ed852e2009-09-05 21:47:34 +00006203 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006204
cristy3ed852e2009-09-05 21:47:34 +00006205 if (logging != MagickFalse)
6206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6207 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006208
cristy3ed852e2009-09-05 21:47:34 +00006209#if defined(MNG_INSERT_LAYERS)
6210 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6211 (mng_info->mng_height))
6212 {
6213 /*
6214 Insert a background layer if nothing else was found.
6215 */
6216 if (logging != MagickFalse)
6217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6218 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006219
cristy3ed852e2009-09-05 21:47:34 +00006220 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6221 {
6222 /*
6223 Allocate next image structure.
6224 */
6225 AcquireNextImage(image_info,image);
6226 if (GetNextImageInList(image) == (Image *) NULL)
6227 {
6228 image=DestroyImageList(image);
6229 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006230
cristy3ed852e2009-09-05 21:47:34 +00006231 if (logging != MagickFalse)
6232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6233 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 return((Image *) NULL);
6236 }
6237 image=SyncNextImageInList(image);
6238 }
6239 image->columns=mng_info->mng_width;
6240 image->rows=mng_info->mng_height;
6241 image->page.width=mng_info->mng_width;
6242 image->page.height=mng_info->mng_height;
6243 image->page.x=0;
6244 image->page.y=0;
6245 image->background_color=mng_background_color;
6246 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006247
cristy3ed852e2009-09-05 21:47:34 +00006248 if (image_info->ping == MagickFalse)
6249 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006250
cristy3ed852e2009-09-05 21:47:34 +00006251 mng_info->image_found++;
6252 }
6253#endif
6254 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006255
cristy3ed852e2009-09-05 21:47:34 +00006256 if (mng_iterations == 1)
6257 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006258
cristy3ed852e2009-09-05 21:47:34 +00006259 while (GetPreviousImageInList(image) != (Image *) NULL)
6260 {
6261 image_count++;
6262 if (image_count > 10*mng_info->image_found)
6263 {
6264 if (logging != MagickFalse)
6265 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006266
cristy3ed852e2009-09-05 21:47:34 +00006267 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6268 CoderError,"Linked list is corrupted, beginning of list not found",
6269 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006270
cristy3ed852e2009-09-05 21:47:34 +00006271 return((Image *) NULL);
6272 }
glennrp0fe50b42010-11-16 03:52:51 +00006273
cristy3ed852e2009-09-05 21:47:34 +00006274 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006275
cristy3ed852e2009-09-05 21:47:34 +00006276 if (GetNextImageInList(image) == (Image *) NULL)
6277 {
6278 if (logging != MagickFalse)
6279 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006280
cristy3ed852e2009-09-05 21:47:34 +00006281 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6282 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6283 image_info->filename);
6284 }
6285 }
glennrp47b9dd52010-11-24 18:12:06 +00006286
cristy3ed852e2009-09-05 21:47:34 +00006287 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6288 GetNextImageInList(image) ==
6289 (Image *) NULL)
6290 {
6291 if (logging != MagickFalse)
6292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6293 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006294
cristy3ed852e2009-09-05 21:47:34 +00006295 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6296 CoderError,"image->next for first image is NULL but shouldn't be.",
6297 "`%s'",image_info->filename);
6298 }
glennrp47b9dd52010-11-24 18:12:06 +00006299
cristy3ed852e2009-09-05 21:47:34 +00006300 if (mng_info->image_found == 0)
6301 {
6302 if (logging != MagickFalse)
6303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6304 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006305
cristy3ed852e2009-09-05 21:47:34 +00006306 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6307 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006308
cristy3ed852e2009-09-05 21:47:34 +00006309 if (image != (Image *) NULL)
6310 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006311
cristy3ed852e2009-09-05 21:47:34 +00006312 MngInfoFreeStruct(mng_info,&have_mng_structure);
6313 return((Image *) NULL);
6314 }
6315
6316 if (mng_info->ticks_per_second)
6317 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6318 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006319
cristy3ed852e2009-09-05 21:47:34 +00006320 else
6321 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006322
cristy3ed852e2009-09-05 21:47:34 +00006323 /* Find final nonzero image delay */
6324 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006325
cristy3ed852e2009-09-05 21:47:34 +00006326 while (GetNextImageInList(image) != (Image *) NULL)
6327 {
6328 if (image->delay)
6329 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006330
cristy3ed852e2009-09-05 21:47:34 +00006331 image=GetNextImageInList(image);
6332 }
glennrp0fe50b42010-11-16 03:52:51 +00006333
cristy3ed852e2009-09-05 21:47:34 +00006334 if (final_delay < final_image_delay)
6335 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006336
cristy3ed852e2009-09-05 21:47:34 +00006337 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006338
cristy3ed852e2009-09-05 21:47:34 +00006339 if (logging != MagickFalse)
6340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006341 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6342 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006343
cristy3ed852e2009-09-05 21:47:34 +00006344 if (logging != MagickFalse)
6345 {
6346 int
6347 scene;
6348
6349 scene=0;
6350 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006351
cristy3ed852e2009-09-05 21:47:34 +00006352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6353 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006354
cristy3ed852e2009-09-05 21:47:34 +00006355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006356 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006357
cristy3ed852e2009-09-05 21:47:34 +00006358 while (GetNextImageInList(image) != (Image *) NULL)
6359 {
6360 image=GetNextImageInList(image);
6361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006362 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006363 }
6364 }
6365
6366 image=GetFirstImageInList(image);
6367#ifdef MNG_COALESCE_LAYERS
6368 if (insert_layers)
6369 {
6370 Image
6371 *next_image,
6372 *next;
6373
cristybb503372010-05-27 20:51:26 +00006374 size_t
cristy3ed852e2009-09-05 21:47:34 +00006375 scene;
6376
6377 if (logging != MagickFalse)
6378 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006379
cristy3ed852e2009-09-05 21:47:34 +00006380 scene=image->scene;
6381 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006382
cristy3ed852e2009-09-05 21:47:34 +00006383 if (next_image == (Image *) NULL)
6384 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006385
cristy3ed852e2009-09-05 21:47:34 +00006386 image=DestroyImageList(image);
6387 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006388
cristy3ed852e2009-09-05 21:47:34 +00006389 for (next=image; next != (Image *) NULL; next=next_image)
6390 {
6391 next->page.width=mng_info->mng_width;
6392 next->page.height=mng_info->mng_height;
6393 next->page.x=0;
6394 next->page.y=0;
6395 next->scene=scene++;
6396 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006397
cristy3ed852e2009-09-05 21:47:34 +00006398 if (next_image == (Image *) NULL)
6399 break;
glennrp47b9dd52010-11-24 18:12:06 +00006400
cristy3ed852e2009-09-05 21:47:34 +00006401 if (next->delay == 0)
6402 {
6403 scene--;
6404 next_image->previous=GetPreviousImageInList(next);
6405 if (GetPreviousImageInList(next) == (Image *) NULL)
6406 image=next_image;
6407 else
6408 next->previous->next=next_image;
6409 next=DestroyImage(next);
6410 }
6411 }
6412 }
6413#endif
6414
6415 while (GetNextImageInList(image) != (Image *) NULL)
6416 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006417
cristy3ed852e2009-09-05 21:47:34 +00006418 image->dispose=BackgroundDispose;
6419
6420 if (logging != MagickFalse)
6421 {
6422 int
6423 scene;
6424
6425 scene=0;
6426 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006427
cristy3ed852e2009-09-05 21:47:34 +00006428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6429 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristy3ed852e2009-09-05 21:47:34 +00006431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006432 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6433 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006434
cristy3ed852e2009-09-05 21:47:34 +00006435 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006436 {
6437 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006438
cristyf2faecf2010-05-28 19:19:36 +00006439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006440 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6441 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006442 }
6443 }
glennrp47b9dd52010-11-24 18:12:06 +00006444
cristy3ed852e2009-09-05 21:47:34 +00006445 image=GetFirstImageInList(image);
6446 MngInfoFreeStruct(mng_info,&have_mng_structure);
6447 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006448
cristy3ed852e2009-09-05 21:47:34 +00006449 if (logging != MagickFalse)
6450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006451
cristy3ed852e2009-09-05 21:47:34 +00006452 return(GetFirstImageInList(image));
6453}
glennrp25c1e2b2010-03-25 01:39:56 +00006454#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006455static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6456{
6457 printf("Your PNG library is too old: You have libpng-%s\n",
6458 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006459
cristy3ed852e2009-09-05 21:47:34 +00006460 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6461 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006462
cristy3ed852e2009-09-05 21:47:34 +00006463 return(Image *) NULL;
6464}
glennrp47b9dd52010-11-24 18:12:06 +00006465
cristy3ed852e2009-09-05 21:47:34 +00006466static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6467{
6468 return(ReadPNGImage(image_info,exception));
6469}
glennrp25c1e2b2010-03-25 01:39:56 +00006470#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006471#endif
6472
6473/*
6474%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6475% %
6476% %
6477% %
6478% R e g i s t e r P N G I m a g e %
6479% %
6480% %
6481% %
6482%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6483%
6484% RegisterPNGImage() adds properties for the PNG image format to
6485% the list of supported formats. The properties include the image format
6486% tag, a method to read and/or write the format, whether the format
6487% supports the saving of more than one frame to the same file or blob,
6488% whether the format supports native in-memory I/O, and a brief
6489% description of the format.
6490%
6491% The format of the RegisterPNGImage method is:
6492%
cristybb503372010-05-27 20:51:26 +00006493% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006494%
6495*/
cristybb503372010-05-27 20:51:26 +00006496ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006497{
6498 char
6499 version[MaxTextExtent];
6500
6501 MagickInfo
6502 *entry;
6503
6504 static const char
6505 *PNGNote=
6506 {
6507 "See http://www.libpng.org/ for details about the PNG format."
6508 },
glennrp47b9dd52010-11-24 18:12:06 +00006509
cristy3ed852e2009-09-05 21:47:34 +00006510 *JNGNote=
6511 {
6512 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6513 "format."
6514 },
glennrp47b9dd52010-11-24 18:12:06 +00006515
cristy3ed852e2009-09-05 21:47:34 +00006516 *MNGNote=
6517 {
6518 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6519 "format."
6520 };
6521
6522 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006523
cristy3ed852e2009-09-05 21:47:34 +00006524#if defined(PNG_LIBPNG_VER_STRING)
6525 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6526 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006527
cristy3ed852e2009-09-05 21:47:34 +00006528 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6529 {
6530 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6531 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6532 MaxTextExtent);
6533 }
6534#endif
glennrp47b9dd52010-11-24 18:12:06 +00006535
cristy3ed852e2009-09-05 21:47:34 +00006536 entry=SetMagickInfo("MNG");
6537 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00006538
cristy3ed852e2009-09-05 21:47:34 +00006539#if defined(MAGICKCORE_PNG_DELEGATE)
6540 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6541 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6542#endif
glennrp47b9dd52010-11-24 18:12:06 +00006543
cristy3ed852e2009-09-05 21:47:34 +00006544 entry->magick=(IsImageFormatHandler *) IsMNG;
6545 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00006546
cristy3ed852e2009-09-05 21:47:34 +00006547 if (*version != '\0')
6548 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006549
cristy3ed852e2009-09-05 21:47:34 +00006550 entry->module=ConstantString("PNG");
6551 entry->note=ConstantString(MNGNote);
6552 (void) RegisterMagickInfo(entry);
6553
6554 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006555
cristy3ed852e2009-09-05 21:47:34 +00006556#if defined(MAGICKCORE_PNG_DELEGATE)
6557 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6558 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6559#endif
glennrp47b9dd52010-11-24 18:12:06 +00006560
cristy3ed852e2009-09-05 21:47:34 +00006561 entry->magick=(IsImageFormatHandler *) IsPNG;
6562 entry->adjoin=MagickFalse;
6563 entry->description=ConstantString("Portable Network Graphics");
6564 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00006565
cristy3ed852e2009-09-05 21:47:34 +00006566 if (*version != '\0')
6567 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristy3ed852e2009-09-05 21:47:34 +00006569 entry->note=ConstantString(PNGNote);
6570 (void) RegisterMagickInfo(entry);
6571
6572 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00006573
cristy3ed852e2009-09-05 21:47:34 +00006574#if defined(MAGICKCORE_PNG_DELEGATE)
6575 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6576 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6577#endif
glennrp47b9dd52010-11-24 18:12:06 +00006578
cristy3ed852e2009-09-05 21:47:34 +00006579 entry->magick=(IsImageFormatHandler *) IsPNG;
6580 entry->adjoin=MagickFalse;
6581 entry->description=ConstantString(
6582 "8-bit indexed with optional binary transparency");
6583 entry->module=ConstantString("PNG");
6584 (void) RegisterMagickInfo(entry);
6585
6586 entry=SetMagickInfo("PNG24");
6587 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00006588
cristy3ed852e2009-09-05 21:47:34 +00006589#if defined(ZLIB_VERSION)
6590 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6591 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00006592
cristy3ed852e2009-09-05 21:47:34 +00006593 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6594 {
6595 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6596 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6597 }
6598#endif
glennrp47b9dd52010-11-24 18:12:06 +00006599
cristy3ed852e2009-09-05 21:47:34 +00006600 if (*version != '\0')
6601 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00006602
cristy3ed852e2009-09-05 21:47:34 +00006603#if defined(MAGICKCORE_PNG_DELEGATE)
6604 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6605 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6606#endif
glennrp47b9dd52010-11-24 18:12:06 +00006607
cristy3ed852e2009-09-05 21:47:34 +00006608 entry->magick=(IsImageFormatHandler *) IsPNG;
6609 entry->adjoin=MagickFalse;
6610 entry->description=ConstantString("opaque 24-bit RGB");
6611 entry->module=ConstantString("PNG");
6612 (void) RegisterMagickInfo(entry);
6613
6614 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00006615
cristy3ed852e2009-09-05 21:47:34 +00006616#if defined(MAGICKCORE_PNG_DELEGATE)
6617 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6618 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6619#endif
glennrp47b9dd52010-11-24 18:12:06 +00006620
cristy3ed852e2009-09-05 21:47:34 +00006621 entry->magick=(IsImageFormatHandler *) IsPNG;
6622 entry->adjoin=MagickFalse;
6623 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6624 entry->module=ConstantString("PNG");
6625 (void) RegisterMagickInfo(entry);
6626
6627 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006628
cristy3ed852e2009-09-05 21:47:34 +00006629#if defined(JNG_SUPPORTED)
6630#if defined(MAGICKCORE_PNG_DELEGATE)
6631 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6632 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6633#endif
6634#endif
glennrp47b9dd52010-11-24 18:12:06 +00006635
cristy3ed852e2009-09-05 21:47:34 +00006636 entry->magick=(IsImageFormatHandler *) IsJNG;
6637 entry->adjoin=MagickFalse;
6638 entry->description=ConstantString("JPEG Network Graphics");
6639 entry->module=ConstantString("PNG");
6640 entry->note=ConstantString(JNGNote);
6641 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00006642
cristy18b17442009-10-25 18:36:48 +00006643#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006644 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00006645#endif
glennrp47b9dd52010-11-24 18:12:06 +00006646
cristy3ed852e2009-09-05 21:47:34 +00006647 return(MagickImageCoderSignature);
6648}
6649
6650/*
6651%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6652% %
6653% %
6654% %
6655% U n r e g i s t e r P N G I m a g e %
6656% %
6657% %
6658% %
6659%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6660%
6661% UnregisterPNGImage() removes format registrations made by the
6662% PNG module from the list of supported formats.
6663%
6664% The format of the UnregisterPNGImage method is:
6665%
6666% UnregisterPNGImage(void)
6667%
6668*/
6669ModuleExport void UnregisterPNGImage(void)
6670{
6671 (void) UnregisterMagickInfo("MNG");
6672 (void) UnregisterMagickInfo("PNG");
6673 (void) UnregisterMagickInfo("PNG8");
6674 (void) UnregisterMagickInfo("PNG24");
6675 (void) UnregisterMagickInfo("PNG32");
6676 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00006677
cristy3ed852e2009-09-05 21:47:34 +00006678#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00006679 if (ping_semaphore != (SemaphoreInfo *) NULL)
6680 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006681#endif
6682}
6683
6684#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006685#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006686/*
6687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6688% %
6689% %
6690% %
6691% W r i t e M N G I m a g e %
6692% %
6693% %
6694% %
6695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6696%
6697% WriteMNGImage() writes an image in the Portable Network Graphics
6698% Group's "Multiple-image Network Graphics" encoded image format.
6699%
6700% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6701%
6702% The format of the WriteMNGImage method is:
6703%
6704% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6705%
6706% A description of each parameter follows.
6707%
6708% o image_info: the image info.
6709%
6710% o image: The image.
6711%
6712%
6713% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6714% "To do" under ReadPNGImage):
6715%
cristy3ed852e2009-09-05 21:47:34 +00006716% Preserve all unknown and not-yet-handled known chunks found in input
6717% PNG file and copy them into output PNG files according to the PNG
6718% copying rules.
6719%
6720% Write the iCCP chunk at MNG level when (icc profile length > 0)
6721%
6722% Improve selection of color type (use indexed-colour or indexed-colour
6723% with tRNS when 256 or fewer unique RGBA values are present).
6724%
6725% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6726% This will be complicated if we limit ourselves to generating MNG-LC
6727% files. For now we ignore disposal method 3 and simply overlay the next
6728% image on it.
6729%
6730% Check for identical PLTE's or PLTE/tRNS combinations and use a
6731% global MNG PLTE or PLTE/tRNS combination when appropriate.
6732% [mostly done 15 June 1999 but still need to take care of tRNS]
6733%
6734% Check for identical sRGB and replace with a global sRGB (and remove
6735% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6736% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6737% local gAMA/cHRM with local sRGB if appropriate).
6738%
6739% Check for identical sBIT chunks and write global ones.
6740%
6741% Provide option to skip writing the signature tEXt chunks.
6742%
6743% Use signatures to detect identical objects and reuse the first
6744% instance of such objects instead of writing duplicate objects.
6745%
6746% Use a smaller-than-32k value of compression window size when
6747% appropriate.
6748%
6749% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6750% ancillary text chunks and save profiles.
6751%
6752% Provide an option to force LC files (to ensure exact framing rate)
6753% instead of VLC.
6754%
6755% Provide an option to force VLC files instead of LC, even when offsets
6756% are present. This will involve expanding the embedded images with a
6757% transparent region at the top and/or left.
6758*/
6759
cristy3ed852e2009-09-05 21:47:34 +00006760static void
glennrpcf002022011-01-30 02:38:15 +00006761Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00006762 png_info *ping_info, unsigned char *profile_type, unsigned char
6763 *profile_description, unsigned char *profile_data, png_uint_32 length)
6764{
cristy3ed852e2009-09-05 21:47:34 +00006765 png_textp
6766 text;
6767
cristybb503372010-05-27 20:51:26 +00006768 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006769 i;
6770
6771 unsigned char
6772 *sp;
6773
6774 png_charp
6775 dp;
6776
6777 png_uint_32
6778 allocated_length,
6779 description_length;
6780
6781 unsigned char
6782 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006783
cristy3ed852e2009-09-05 21:47:34 +00006784 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6785 return;
6786
6787 if (image_info->verbose)
6788 {
glennrp0fe50b42010-11-16 03:52:51 +00006789 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6790 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006791 }
glennrp0fe50b42010-11-16 03:52:51 +00006792
cristy3ed852e2009-09-05 21:47:34 +00006793 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6794 description_length=(png_uint_32) strlen((const char *) profile_description);
6795 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6796 + description_length);
6797 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6798 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6799 text[0].key[0]='\0';
6800 (void) ConcatenateMagickString(text[0].key,
6801 "Raw profile type ",MaxTextExtent);
6802 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6803 sp=profile_data;
6804 dp=text[0].text;
6805 *dp++='\n';
6806 (void) CopyMagickString(dp,(const char *) profile_description,
6807 allocated_length);
6808 dp+=description_length;
6809 *dp++='\n';
6810 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006811 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006812 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00006813
cristybb503372010-05-27 20:51:26 +00006814 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006815 {
6816 if (i%36 == 0)
6817 *dp++='\n';
6818 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6819 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6820 }
glennrp47b9dd52010-11-24 18:12:06 +00006821
cristy3ed852e2009-09-05 21:47:34 +00006822 *dp++='\n';
6823 *dp='\0';
6824 text[0].text_length=(png_size_t) (dp-text[0].text);
6825 text[0].compression=image_info->compression == NoCompression ||
6826 (image_info->compression == UndefinedCompression &&
6827 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00006828
cristy3ed852e2009-09-05 21:47:34 +00006829 if (text[0].text_length <= allocated_length)
6830 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00006831
cristy3ed852e2009-09-05 21:47:34 +00006832 png_free(ping,text[0].text);
6833 png_free(ping,text[0].key);
6834 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006835}
6836
glennrpcf002022011-01-30 02:38:15 +00006837static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00006838 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00006839{
6840 char
6841 *name;
6842
6843 const StringInfo
6844 *profile;
6845
6846 unsigned char
6847 *data;
6848
6849 png_uint_32 length;
6850
6851 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00006852
6853 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6854 {
cristy3ed852e2009-09-05 21:47:34 +00006855 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00006856
cristy3ed852e2009-09-05 21:47:34 +00006857 if (profile != (const StringInfo *) NULL)
6858 {
6859 StringInfo
glennrpcf002022011-01-30 02:38:15 +00006860 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00006861
glennrp47b9dd52010-11-24 18:12:06 +00006862 if (LocaleNCompare(name,string,11) == 0)
6863 {
6864 if (logging != MagickFalse)
6865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6866 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00006867
glennrpcf002022011-01-30 02:38:15 +00006868 ping_profile=CloneStringInfo(profile);
6869 data=GetStringInfoDatum(ping_profile),
6870 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006871 data[4]=data[3];
6872 data[3]=data[2];
6873 data[2]=data[1];
6874 data[1]=data[0];
6875 (void) WriteBlobMSBULong(image,length-5); /* data length */
6876 (void) WriteBlob(image,length-1,data+1);
6877 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00006878 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00006879 }
cristy3ed852e2009-09-05 21:47:34 +00006880 }
glennrp47b9dd52010-11-24 18:12:06 +00006881
cristy3ed852e2009-09-05 21:47:34 +00006882 name=GetNextImageProfile(image);
6883 }
glennrp47b9dd52010-11-24 18:12:06 +00006884
cristy3ed852e2009-09-05 21:47:34 +00006885 return(MagickTrue);
6886}
6887
glennrpb9cfe272010-12-21 15:08:06 +00006888
cristy3ed852e2009-09-05 21:47:34 +00006889/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00006890static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6891 const ImageInfo *IMimage_info,Image *IMimage)
6892{
6893 Image
6894 *image;
6895
6896 ImageInfo
6897 *image_info;
6898
cristy3ed852e2009-09-05 21:47:34 +00006899 char
6900 s[2];
6901
6902 const char
6903 *name,
6904 *property,
6905 *value;
6906
6907 const StringInfo
6908 *profile;
6909
cristy3ed852e2009-09-05 21:47:34 +00006910 int
cristy3ed852e2009-09-05 21:47:34 +00006911 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006912 pass;
6913
glennrpe9c26dc2010-05-30 01:56:35 +00006914 png_byte
6915 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006916
glennrp39992b42010-11-14 00:03:43 +00006917 png_color
6918 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00006919
glennrp5af765f2010-03-30 11:12:18 +00006920 png_color_16
6921 ping_background,
6922 ping_trans_color;
6923
cristy3ed852e2009-09-05 21:47:34 +00006924 png_info
6925 *ping_info;
6926
6927 png_struct
6928 *ping;
6929
glennrp5af765f2010-03-30 11:12:18 +00006930 png_uint_32
6931 ping_height,
6932 ping_width;
6933
cristybb503372010-05-27 20:51:26 +00006934 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006935 y;
6936
6937 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00006938 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00006939 logging,
glennrp58e01762011-01-07 15:28:54 +00006940 matte,
6941
glennrpda8f3a72011-02-27 23:54:12 +00006942 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00006943 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00006944 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00006945 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00006946 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00006947 ping_have_bKGD,
6948 ping_have_pHYs,
6949 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00006950
6951 ping_exclude_bKGD,
6952 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00006953 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00006954 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00006955 ping_exclude_gAMA,
6956 ping_exclude_iCCP,
6957 /* ping_exclude_iTXt, */
6958 ping_exclude_oFFs,
6959 ping_exclude_pHYs,
6960 ping_exclude_sRGB,
6961 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00006962 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00006963 ping_exclude_vpAg,
6964 ping_exclude_zCCP, /* hex-encoded iCCP */
6965 ping_exclude_zTXt,
6966
glennrp8d3d6e52011-04-19 04:39:51 +00006967 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00006968 ping_need_colortype_warning,
6969
glennrp82b3c532011-03-22 19:20:54 +00006970 status,
glennrpd3371642011-03-22 19:42:23 +00006971 tried_333,
6972 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00006973
6974 QuantumInfo
6975 *quantum_info;
6976
cristybb503372010-05-27 20:51:26 +00006977 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006978 i,
6979 x;
6980
6981 unsigned char
glennrpcf002022011-01-30 02:38:15 +00006982 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00006983
glennrp5af765f2010-03-30 11:12:18 +00006984 volatile int
glennrpf09bded2011-01-08 01:15:59 +00006985 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00006986 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00006987 ping_color_type,
6988 ping_interlace_method,
6989 ping_compression_method,
6990 ping_filter_method,
6991 ping_num_trans;
6992
cristybb503372010-05-27 20:51:26 +00006993 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00006994 image_depth,
6995 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006996
cristybb503372010-05-27 20:51:26 +00006997 size_t
cristy3ed852e2009-09-05 21:47:34 +00006998 quality,
6999 rowbytes,
7000 save_image_depth;
7001
glennrpdfd70802010-11-14 01:23:35 +00007002 int
glennrpfd05d622011-02-25 04:10:33 +00007003 j,
glennrpf09bded2011-01-08 01:15:59 +00007004 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007005 number_opaque,
7006 number_semitransparent,
7007 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007008 ping_pHYs_unit_type;
7009
7010 png_uint_32
7011 ping_pHYs_x_resolution,
7012 ping_pHYs_y_resolution;
7013
cristy3ed852e2009-09-05 21:47:34 +00007014 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007015 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007016
glennrpb9cfe272010-12-21 15:08:06 +00007017 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7018 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007019 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007020 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007021
cristy3ed852e2009-09-05 21:47:34 +00007022#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007023 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007024#endif
7025
glennrp5af765f2010-03-30 11:12:18 +00007026 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007027 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007028 ping_color_type=0,
7029 ping_interlace_method=0,
7030 ping_compression_method=0,
7031 ping_filter_method=0,
7032 ping_num_trans = 0;
7033
7034 ping_background.red = 0;
7035 ping_background.green = 0;
7036 ping_background.blue = 0;
7037 ping_background.gray = 0;
7038 ping_background.index = 0;
7039
7040 ping_trans_color.red=0;
7041 ping_trans_color.green=0;
7042 ping_trans_color.blue=0;
7043 ping_trans_color.gray=0;
7044
glennrpdfd70802010-11-14 01:23:35 +00007045 ping_pHYs_unit_type = 0;
7046 ping_pHYs_x_resolution = 0;
7047 ping_pHYs_y_resolution = 0;
7048
glennrpda8f3a72011-02-27 23:54:12 +00007049 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007050 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007051 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007052 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007053 ping_have_bKGD=MagickFalse;
7054 ping_have_pHYs=MagickFalse;
7055 ping_have_tRNS=MagickFalse;
7056
glennrp0e8ea192010-12-24 18:00:33 +00007057 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7058 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007059 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007060 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007061 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007062 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7063 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7064 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7065 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7066 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7067 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007068 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007069 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7070 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7071 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7072
glennrp8d3d6e52011-04-19 04:39:51 +00007073 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007074 ping_need_colortype_warning = MagickFalse;
7075
glennrp8bb3a022010-12-13 20:40:04 +00007076 number_opaque = 0;
7077 number_semitransparent = 0;
7078 number_transparent = 0;
7079
glennrpfd05d622011-02-25 04:10:33 +00007080 if (logging != MagickFalse)
7081 {
7082 if (image->storage_class == UndefinedClass)
7083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7084 " storage_class=UndefinedClass");
7085 if (image->storage_class == DirectClass)
7086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7087 " storage_class=DirectClass");
7088 if (image->storage_class == PseudoClass)
7089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7090 " storage_class=PseudoClass");
7091 }
glennrp28af3712011-04-06 18:07:30 +00007092
7093 if (image->storage_class != PseudoClass && image->colormap != NULL)
7094 {
7095 /* Free the bogus colormap; it can cause trouble later */
7096 if (logging != MagickFalse)
7097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7098 " Freeing bogus colormap");
7099 (void *) RelinquishMagickMemory(image->colormap);
7100 image->colormap=NULL;
7101 }
glennrpfd05d622011-02-25 04:10:33 +00007102
cristy3ed852e2009-09-05 21:47:34 +00007103 if (image->colorspace != RGBColorspace)
7104 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007105
glennrp3241bd02010-12-12 04:36:28 +00007106 /*
7107 Sometimes we get PseudoClass images whose RGB values don't match
7108 the colors in the colormap. This code syncs the RGB values.
7109 */
7110 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7111 (void) SyncImage(image);
7112
glennrpa6a06632011-01-19 15:15:34 +00007113#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7114 if (image->depth > 8)
7115 {
7116 if (logging != MagickFalse)
7117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7118 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7119
7120 image->depth=8;
7121 }
7122#endif
7123
glennrp67b9c1a2011-04-22 18:47:36 +00007124#if 0 /* To do: Option to use the original colormap */
7125 if (ping_preserve_colormap != MagickFalse)
7126 {
7127 }
7128#endif
7129
glennrp3faa9a32011-04-23 14:00:25 +00007130#if 0 /* To do: respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007131 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7132 {
glennrp9d0ea4d2011-04-22 18:35:57 +00007133 }
glennrp67b9c1a2011-04-22 18:47:36 +00007134#endif
glennrp9d0ea4d2011-04-22 18:35:57 +00007135
glennrp67b9c1a2011-04-22 18:47:36 +00007136 /* To do: set to next higher multiple of 8 */
7137 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007138 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007139
glennrp2b013e42010-11-24 16:55:50 +00007140#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7141 /* PNG does not handle depths greater than 16 so reduce it even
7142 * if lossy
7143 */
7144 if (image->depth > 16)
7145 image->depth=16;
7146#endif
7147
glennrp3faa9a32011-04-23 14:00:25 +00007148#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007149 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007150 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007151 image->depth = 8;
7152#endif
7153
glennrpc8c2f062011-02-25 19:00:33 +00007154 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007155 * we reduce the transparency to binary and run again, then if there
7156 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
7157 * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
7158 * palette. The final reduction can only fail if there are still 256
7159 * colors present and one of them has both transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007160 */
glennrp82b3c532011-03-22 19:20:54 +00007161
7162 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007163 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007164
glennrpd3371642011-03-22 19:42:23 +00007165 for (j=0; j<5; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007166 {
7167 /* BUILD_PALETTE
7168 *
7169 * Sometimes we get DirectClass images that have 256 colors or fewer.
7170 * This code will build a colormap.
7171 *
7172 * Also, sometimes we get PseudoClass images with an out-of-date
7173 * colormap. This code will replace the colormap with a new one.
7174 * Sometimes we get PseudoClass images that have more than 256 colors.
7175 * This code will delete the colormap and change the image to
7176 * DirectClass.
7177 *
7178 * If image->matte is MagickFalse, we ignore the opacity channel
7179 * even though it sometimes contains left-over non-opaque values.
7180 *
7181 * Also we gather some information (number of opaque, transparent,
7182 * and semitransparent pixels, and whether the image has any non-gray
7183 * pixels or only black-and-white pixels) that we might need later.
7184 *
7185 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7186 * we need to check for bogus non-opaque values, at least.
7187 */
glennrp3c218112010-11-27 15:31:26 +00007188
glennrpd71e86a2011-02-24 01:28:37 +00007189 ExceptionInfo
7190 *exception;
glennrp3c218112010-11-27 15:31:26 +00007191
glennrpd71e86a2011-02-24 01:28:37 +00007192 int
7193 n;
glennrp3c218112010-11-27 15:31:26 +00007194
glennrpd71e86a2011-02-24 01:28:37 +00007195 PixelPacket
7196 opaque[260],
7197 semitransparent[260],
7198 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007199
glennrpd71e86a2011-02-24 01:28:37 +00007200 register IndexPacket
7201 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007202
glennrpd71e86a2011-02-24 01:28:37 +00007203 register const PixelPacket
7204 *s,
7205 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007206
glennrpfd05d622011-02-25 04:10:33 +00007207 register PixelPacket
7208 *r;
7209
glennrpd71e86a2011-02-24 01:28:37 +00007210 if (logging != MagickFalse)
7211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7212 " Enter BUILD_PALETTE:");
7213
7214 if (logging != MagickFalse)
7215 {
glennrp03812ae2010-12-24 01:31:34 +00007216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007217 " image->columns=%.20g",(double) image->columns);
7218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7219 " image->rows=%.20g",(double) image->rows);
7220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7221 " image->matte=%.20g",(double) image->matte);
7222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7223 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007224
glennrpfd05d622011-02-25 04:10:33 +00007225 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007226 {
7227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007228 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007230 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007231
glennrpd71e86a2011-02-24 01:28:37 +00007232 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007233 {
glennrpd71e86a2011-02-24 01:28:37 +00007234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7235 " %d (%d,%d,%d,%d)",
7236 (int) i,
7237 (int) image->colormap[i].red,
7238 (int) image->colormap[i].green,
7239 (int) image->colormap[i].blue,
7240 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007241 }
glennrp2cc891a2010-12-24 13:44:32 +00007242
glennrpd71e86a2011-02-24 01:28:37 +00007243 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7244 {
7245 if (i > 255)
7246 {
7247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7248 " %d (%d,%d,%d,%d)",
7249 (int) i,
7250 (int) image->colormap[i].red,
7251 (int) image->colormap[i].green,
7252 (int) image->colormap[i].blue,
7253 (int) image->colormap[i].opacity);
7254 }
7255 }
glennrp03812ae2010-12-24 01:31:34 +00007256 }
glennrp7ddcc222010-12-11 05:01:05 +00007257
glennrpd71e86a2011-02-24 01:28:37 +00007258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7259 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007260
glennrpd71e86a2011-02-24 01:28:37 +00007261 if (image->colors == 0)
7262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7263 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007264
glennrp8d3d6e52011-04-19 04:39:51 +00007265 if (ping_preserve_colormap == MagickFalse)
7266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7267 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007268 }
7269
7270 exception=(&image->exception);
7271
7272 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007273 number_opaque = 0;
7274 number_semitransparent = 0;
7275 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007276
7277 for (y=0; y < (ssize_t) image->rows; y++)
7278 {
7279 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7280
7281 if (q == (PixelPacket *) NULL)
7282 break;
7283
7284 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007285 {
glennrpd71e86a2011-02-24 01:28:37 +00007286 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7287 {
7288 if (number_opaque < 259)
7289 {
7290 if (number_opaque == 0)
7291 {
7292 opaque[0]=*q;
7293 opaque[0].opacity=OpaqueOpacity;
7294 number_opaque=1;
7295 }
glennrp2cc891a2010-12-24 13:44:32 +00007296
glennrpd71e86a2011-02-24 01:28:37 +00007297 for (i=0; i< (ssize_t) number_opaque; i++)
7298 {
7299 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7300 break;
7301 }
glennrp7ddcc222010-12-11 05:01:05 +00007302
glennrpd71e86a2011-02-24 01:28:37 +00007303 if (i == (ssize_t) number_opaque &&
7304 number_opaque < 259)
7305 {
7306 number_opaque++;
7307 opaque[i] = *q;
7308 opaque[i].opacity = OpaqueOpacity;
7309 }
7310 }
7311 }
7312 else if (q->opacity == TransparentOpacity)
7313 {
7314 if (number_transparent < 259)
7315 {
7316 if (number_transparent == 0)
7317 {
7318 transparent[0]=*q;
glennrpa18d5bc2011-04-23 14:51:34 +00007319 ping_trans_color.red=
7320 (unsigned short) GetRedPixelComponent(q);
7321 ping_trans_color.green=
7322 (unsigned short) GetGreenPixelComponent(q);
7323 ping_trans_color.blue=
7324 (unsigned short) GetBluePixelComponent(q);
7325 ping_trans_color.gray=
7326 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007327 number_transparent = 1;
7328 }
7329
7330 for (i=0; i< (ssize_t) number_transparent; i++)
7331 {
7332 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7333 break;
7334 }
7335
7336 if (i == (ssize_t) number_transparent &&
7337 number_transparent < 259)
7338 {
7339 number_transparent++;
7340 transparent[i] = *q;
7341 }
7342 }
7343 }
7344 else
7345 {
7346 if (number_semitransparent < 259)
7347 {
7348 if (number_semitransparent == 0)
7349 {
7350 semitransparent[0]=*q;
7351 number_semitransparent = 1;
7352 }
7353
7354 for (i=0; i< (ssize_t) number_semitransparent; i++)
7355 {
7356 if (IsColorEqual(semitransparent+i,
7357 (PixelPacket *) q) &&
7358 q->opacity == semitransparent[i].opacity)
7359 break;
7360 }
7361
7362 if (i == (ssize_t) number_semitransparent &&
7363 number_semitransparent < 259)
7364 {
7365 number_semitransparent++;
7366 semitransparent[i] = *q;
7367 }
7368 }
7369 }
7370 q++;
7371 }
7372 }
7373
7374 if (ping_exclude_bKGD == MagickFalse)
7375 {
7376 /* Add the background color to the palette, if it
7377 * isn't already there.
7378 */
7379 for (i=0; i<number_opaque; i++)
7380 {
glennrpa080bc32011-03-11 18:03:44 +00007381 if (IsColorEqual(opaque+i, &image->background_color))
glennrpd71e86a2011-02-24 01:28:37 +00007382 break;
7383 }
7384
7385 if (number_opaque < 259 && i == number_opaque)
7386 {
7387 opaque[i]=image->background_color;
7388 opaque[i].opacity = OpaqueOpacity;
7389 number_opaque++;
7390 }
glennrpa080bc32011-03-11 18:03:44 +00007391 else if (logging != MagickFalse)
7392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7393 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00007394 }
7395
7396 image_colors=number_opaque+number_transparent+number_semitransparent;
7397
glennrpa080bc32011-03-11 18:03:44 +00007398 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7399 {
7400 /* No room for the background color; remove it. */
7401 number_opaque--;
7402 image_colors--;
7403 }
7404
glennrpd71e86a2011-02-24 01:28:37 +00007405 if (logging != MagickFalse)
7406 {
7407 if (image_colors > 256)
7408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7409 " image has more than 256 colors");
7410
7411 else
7412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7413 " image has %d colors",image_colors);
7414 }
7415
glennrp8d3d6e52011-04-19 04:39:51 +00007416 if (ping_preserve_colormap != MagickFalse)
7417 break;
glennrp8d3d6e52011-04-19 04:39:51 +00007418
glennrpfd05d622011-02-25 04:10:33 +00007419 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00007420 {
7421 ping_have_color=MagickFalse;
7422 ping_have_non_bw=MagickFalse;
7423
7424 if(image_colors > 256)
7425 {
7426 for (y=0; y < (ssize_t) image->rows; y++)
7427 {
7428 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7429
7430 if (q == (PixelPacket *) NULL)
7431 break;
7432
7433 /* Worst case is black-and-white; we are looking at every
7434 * pixel twice.
7435 */
7436
7437 if (ping_have_color == MagickFalse)
7438 {
7439 s=q;
7440 for (x=0; x < (ssize_t) image->columns; x++)
7441 {
glennrpa18d5bc2011-04-23 14:51:34 +00007442 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
7443 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00007444 {
7445 ping_have_color=MagickTrue;
7446 ping_have_non_bw=MagickTrue;
7447 break;
7448 }
7449 s++;
7450 }
7451 }
7452
7453 if (ping_have_non_bw == MagickFalse)
7454 {
7455 s=q;
7456 for (x=0; x < (ssize_t) image->columns; x++)
7457 {
glennrpa18d5bc2011-04-23 14:51:34 +00007458 if (GetRedPixelComponent(s) != 0 &&
7459 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00007460 {
7461 ping_have_non_bw=MagickTrue;
7462 }
7463 s++;
7464 }
7465 }
7466 }
7467 }
7468 }
7469
7470 if (image_colors < 257)
7471 {
7472 PixelPacket
7473 colormap[260];
7474
7475 /*
7476 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00007477 */
7478
glennrpd71e86a2011-02-24 01:28:37 +00007479 if (logging != MagickFalse)
7480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7481 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00007482
glennrpd71e86a2011-02-24 01:28:37 +00007483 /* Sort palette, transparent first */;
7484
7485 n = 0;
7486
7487 for (i=0; i<number_transparent; i++)
7488 colormap[n++] = transparent[i];
7489
7490 for (i=0; i<number_semitransparent; i++)
7491 colormap[n++] = semitransparent[i];
7492
7493 for (i=0; i<number_opaque; i++)
7494 colormap[n++] = opaque[i];
7495
7496
7497 /* image_colors < 257; search the colormap instead of the pixels
7498 * to get ping_have_color and ping_have_non_bw
7499 */
7500 for (i=0; i<n; i++)
7501 {
7502 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00007503 {
glennrpd71e86a2011-02-24 01:28:37 +00007504 if (colormap[i].red != colormap[i].green ||
7505 colormap[i].red != colormap[i].blue)
7506 {
7507 ping_have_color=MagickTrue;
7508 ping_have_non_bw=MagickTrue;
7509 break;
7510 }
7511 }
7512
7513 if (ping_have_non_bw == MagickFalse)
7514 {
7515 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00007516 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00007517 }
glennrp8bb3a022010-12-13 20:40:04 +00007518 }
7519
glennrpd71e86a2011-02-24 01:28:37 +00007520 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7521 (number_transparent == 0 && number_semitransparent == 0)) &&
7522 (((mng_info->write_png_colortype-1) ==
7523 PNG_COLOR_TYPE_PALETTE) ||
7524 (mng_info->write_png_colortype == 0)))
7525 {
glennrp6185c532011-01-14 17:58:40 +00007526 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00007527 {
glennrpd71e86a2011-02-24 01:28:37 +00007528 if (n != (ssize_t) image_colors)
7529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7530 " image_colors (%d) and n (%d) don't match",
7531 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00007532
glennrpd71e86a2011-02-24 01:28:37 +00007533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7534 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00007535 }
glennrp03812ae2010-12-24 01:31:34 +00007536
glennrpd71e86a2011-02-24 01:28:37 +00007537 image->colors = image_colors;
7538
7539 if (AcquireImageColormap(image,image_colors) ==
7540 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00007541 ThrowWriterException(ResourceLimitError,
7542 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00007543
7544 for (i=0; i< (ssize_t) image_colors; i++)
7545 image->colormap[i] = colormap[i];
7546
7547 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00007548 {
glennrpd71e86a2011-02-24 01:28:37 +00007549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7550 " image->colors=%d (%d)",
7551 (int) image->colors, image_colors);
7552
7553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7554 " Update the pixel indexes");
7555 }
glennrp6185c532011-01-14 17:58:40 +00007556
glennrpfd05d622011-02-25 04:10:33 +00007557 /* Sync the pixel indices with the new colormap */
7558
glennrpd71e86a2011-02-24 01:28:37 +00007559 for (y=0; y < (ssize_t) image->rows; y++)
7560 {
7561 q=GetAuthenticPixels(image,0,y,image->columns,1,
7562 exception);
glennrp6185c532011-01-14 17:58:40 +00007563
glennrpd71e86a2011-02-24 01:28:37 +00007564 if (q == (PixelPacket *) NULL)
7565 break;
glennrp6185c532011-01-14 17:58:40 +00007566
glennrpd71e86a2011-02-24 01:28:37 +00007567 indexes=GetAuthenticIndexQueue(image);
7568
7569 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00007570 {
glennrpd71e86a2011-02-24 01:28:37 +00007571 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00007572 {
glennrpd71e86a2011-02-24 01:28:37 +00007573 if ((image->matte == MagickFalse ||
7574 image->colormap[i].opacity == q->opacity) &&
7575 (IsColorEqual(&image->colormap[i],
7576 (PixelPacket *) q)))
glennrp6185c532011-01-14 17:58:40 +00007577 {
glennrpd71e86a2011-02-24 01:28:37 +00007578 indexes[x]=(IndexPacket) i;
7579 break;
glennrp6185c532011-01-14 17:58:40 +00007580 }
glennrp6185c532011-01-14 17:58:40 +00007581 }
glennrpd71e86a2011-02-24 01:28:37 +00007582 q++;
7583 }
glennrp6185c532011-01-14 17:58:40 +00007584
glennrpd71e86a2011-02-24 01:28:37 +00007585 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7586 break;
7587 }
7588 }
7589 }
7590
7591 if (logging != MagickFalse)
7592 {
7593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7594 " image->colors=%d", (int) image->colors);
7595
7596 if (image->colormap != NULL)
7597 {
7598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7599 " i (red,green,blue,opacity)");
7600
7601 for (i=0; i < (ssize_t) image->colors; i++)
7602 {
cristy72988482011-03-29 16:34:38 +00007603 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00007604 {
7605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7606 " %d (%d,%d,%d,%d)",
7607 (int) i,
7608 (int) image->colormap[i].red,
7609 (int) image->colormap[i].green,
7610 (int) image->colormap[i].blue,
7611 (int) image->colormap[i].opacity);
7612 }
glennrp6185c532011-01-14 17:58:40 +00007613 }
7614 }
glennrp03812ae2010-12-24 01:31:34 +00007615
glennrpd71e86a2011-02-24 01:28:37 +00007616 if (number_transparent < 257)
7617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7618 " number_transparent = %d",
7619 number_transparent);
7620 else
glennrp03812ae2010-12-24 01:31:34 +00007621
glennrpd71e86a2011-02-24 01:28:37 +00007622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7623 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00007624
glennrpd71e86a2011-02-24 01:28:37 +00007625 if (number_opaque < 257)
7626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7627 " number_opaque = %d",
7628 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00007629
glennrpd71e86a2011-02-24 01:28:37 +00007630 else
7631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7632 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00007633
glennrpd71e86a2011-02-24 01:28:37 +00007634 if (number_semitransparent < 257)
7635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7636 " number_semitransparent = %d",
7637 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00007638
glennrpd71e86a2011-02-24 01:28:37 +00007639 else
7640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7641 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00007642
glennrpd71e86a2011-02-24 01:28:37 +00007643 if (ping_have_non_bw == MagickFalse)
7644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7645 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00007646
glennrpd71e86a2011-02-24 01:28:37 +00007647 else if (ping_have_color == MagickFalse)
7648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7649 " All pixels and the background are gray");
7650
7651 else
7652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7653 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00007654
glennrp03812ae2010-12-24 01:31:34 +00007655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7656 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00007657 }
glennrpfd05d622011-02-25 04:10:33 +00007658
glennrpc8c2f062011-02-25 19:00:33 +00007659 if (mng_info->write_png8 == MagickFalse)
7660 break;
glennrpfd05d622011-02-25 04:10:33 +00007661
glennrpc8c2f062011-02-25 19:00:33 +00007662 /* Make any reductions necessary for the PNG8 format */
7663 if (image_colors <= 256 &&
7664 image_colors != 0 && image->colormap != NULL &&
7665 number_semitransparent == 0 &&
7666 number_transparent <= 1)
7667 break;
7668
7669 /* PNG8 can't have semitransparent colors so we threshold the
7670 * opacity to 0 or OpaqueOpacity
7671 */
7672 if (number_semitransparent != 0)
7673 {
7674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7675 " Thresholding the alpha channel to binary");
7676
7677 for (y=0; y < (ssize_t) image->rows; y++)
7678 {
7679 r=GetAuthenticPixels(image,0,y,image->columns,1,
7680 exception);
7681
7682 if (r == (PixelPacket *) NULL)
7683 break;
7684
7685 for (x=0; x < (ssize_t) image->columns; x++)
7686 {
glennrpa18d5bc2011-04-23 14:51:34 +00007687 SetOpacityPixelComponent(r,
7688 (GetOpacityPixelComponent(r) > TransparentOpacity/2) ?
7689 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00007690 r++;
7691 }
7692
7693 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7694 break;
7695
7696 if (image_colors != 0 && image_colors <= 256 &&
7697 image->colormap != NULL)
7698 for (i=0; i<image_colors; i++)
7699 image->colormap[i].opacity =
glennrp82b3c532011-03-22 19:20:54 +00007700 image->colormap[i].opacity > TransparentOpacity/2 ?
7701 TransparentOpacity : OpaqueOpacity;
glennrpc8c2f062011-02-25 19:00:33 +00007702 }
7703 continue;
7704 }
7705
7706 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00007707 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
7708 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7709 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00007710 */
glennrpd3371642011-03-22 19:42:23 +00007711 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7712 {
7713 if (logging != MagickFalse)
7714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7715 " Quantizing the background color to 4-4-4");
7716
7717 tried_444 = MagickTrue;
7718
7719 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007720 ScaleCharToQuantum(
7721 (ScaleQuantumToChar(image->background_color.red) & 0xf0) |
7722 (ScaleQuantumToChar(image->background_color.red) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007723 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007724 ScaleCharToQuantum(
7725 (ScaleQuantumToChar(image->background_color.green) & 0xf0) |
7726 (ScaleQuantumToChar(image->background_color.green) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007727 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007728 ScaleCharToQuantum(
7729 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) |
7730 (ScaleQuantumToChar(image->background_color.blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007731
7732 if (logging != MagickFalse)
7733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7734 " Quantizing the pixel colors to 4-4-4");
7735
7736 if (image->colormap == NULL)
7737 {
7738 for (y=0; y < (ssize_t) image->rows; y++)
7739 {
7740 r=GetAuthenticPixels(image,0,y,image->columns,1,
7741 exception);
7742
7743 if (r == (PixelPacket *) NULL)
7744 break;
7745
7746 for (x=0; x < (ssize_t) image->columns; x++)
7747 {
7748 if (r->opacity == TransparentOpacity)
7749 {
7750 r->red = image->background_color.red;
7751 r->green = image->background_color.green;
7752 r->blue = image->background_color.blue;
7753 }
7754 else
7755 {
glennrp3faa9a32011-04-23 14:00:25 +00007756 r->red=ScaleCharToQuantum(
7757 (ScaleQuantumToChar(r->red) & 0xf0) |
7758 (ScaleQuantumToChar(r->red) & 0xf0) >> 4);
7759 r->green=ScaleCharToQuantum(
7760 (ScaleQuantumToChar(r->green) & 0xf0) |
7761 (ScaleQuantumToChar(r->green) & 0xf0) >> 4);
7762 r->blue=ScaleCharToQuantum(
7763 (ScaleQuantumToChar(r->blue) & 0xf0) |
7764 (ScaleQuantumToChar(r->blue) & 0xf0) >> 4);
glennrpd3371642011-03-22 19:42:23 +00007765 }
7766 r++;
7767 }
7768
7769 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7770 break;
7771 }
7772 }
7773
7774 else /* Should not reach this; colormap already exists and
7775 must be <= 256 */
7776 {
7777 if (logging != MagickFalse)
7778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7779 " Quantizing the colormap to 4-4-4");
7780 for (i=0; i<image_colors; i++)
7781 {
glennrp3faa9a32011-04-23 14:00:25 +00007782 image->colormap[i].red=ScaleCharToQuantum(
7783 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) |
7784 (ScaleQuantumToChar(image->colormap[i].red) & 0xf0) >> 4);
7785 image->colormap[i].green=ScaleCharToQuantum(
7786 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) |
7787 (ScaleQuantumToChar(image->colormap[i].green) & 0xf0) >> 4);
7788 image->colormap[i].blue=ScaleCharToQuantum(
7789 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0) |
7790 (ScaleQuantumToChar(image->colormap[i].blue) & 0xf0 >> 4));
glennrpd3371642011-03-22 19:42:23 +00007791 }
7792 }
7793 continue;
7794 }
7795
glennrp82b3c532011-03-22 19:20:54 +00007796 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7797 {
7798 if (logging != MagickFalse)
7799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7800 " Quantizing the background color to 3-3-3");
7801
7802 tried_333 = MagickTrue;
7803
7804 image->background_color.red=
glennrp3faa9a32011-04-23 14:00:25 +00007805 ScaleCharToQuantum(
7806 (ScaleQuantumToChar(image->background_color.red) & 0xe0) |
7807 (ScaleQuantumToChar(image->background_color.red) & 0xe0) >> 3 |
7808 (ScaleQuantumToChar(image->background_color.red) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007809 image->background_color.green=
glennrp3faa9a32011-04-23 14:00:25 +00007810 ScaleCharToQuantum(
7811 (ScaleQuantumToChar(image->background_color.green) & 0xe0) |
7812 (ScaleQuantumToChar(image->background_color.green) & 0xe0) >> 3 |
7813 (ScaleQuantumToChar(image->background_color.green) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007814 image->background_color.blue=
glennrp3faa9a32011-04-23 14:00:25 +00007815 ScaleCharToQuantum(
7816 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) |
7817 (ScaleQuantumToChar(image->background_color.blue) & 0xe0) >> 3 |
7818 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007819
7820 if (logging != MagickFalse)
7821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007822 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007823
7824 if (image->colormap == NULL)
7825 {
7826 for (y=0; y < (ssize_t) image->rows; y++)
7827 {
7828 r=GetAuthenticPixels(image,0,y,image->columns,1,
7829 exception);
7830
7831 if (r == (PixelPacket *) NULL)
7832 break;
7833
7834 for (x=0; x < (ssize_t) image->columns; x++)
7835 {
7836 if (r->opacity == TransparentOpacity)
7837 {
7838 r->red = image->background_color.red;
7839 r->green = image->background_color.green;
7840 r->blue = image->background_color.blue;
7841 }
7842 else
7843 {
glennrp3faa9a32011-04-23 14:00:25 +00007844 r->red=ScaleCharToQuantum(
7845 (ScaleQuantumToChar(r->red) & 0xe0) |
7846 (ScaleQuantumToChar(r->red) & 0xe0) >> 3 |
7847 (ScaleQuantumToChar(r->red) & 0xc0) >> 6);
7848 r->green=ScaleCharToQuantum(
7849 (ScaleQuantumToChar(r->green) & 0xe0) |
7850 (ScaleQuantumToChar(r->green) & 0xe0) >> 3 |
7851 (ScaleQuantumToChar(r->green) & 0xc0) >> 6);
7852 r->blue=ScaleCharToQuantum(
7853 (ScaleQuantumToChar(r->blue) & 0xe0) |
7854 (ScaleQuantumToChar(r->blue) & 0xe0) >> 3 |
7855 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007856 }
7857 r++;
7858 }
7859
7860 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7861 break;
7862 }
7863 }
7864
7865 else /* Should not reach this; colormap already exists and
7866 must be <= 256 */
7867 {
7868 if (logging != MagickFalse)
7869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007870 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00007871 for (i=0; i<image_colors; i++)
7872 {
glennrp3faa9a32011-04-23 14:00:25 +00007873 image->colormap[i].red=ScaleCharToQuantum(
7874 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) |
7875 (ScaleQuantumToChar(image->colormap[i].red) & 0xe0) >> 3 |
7876 (ScaleQuantumToChar(image->colormap[i].red) & 0xc0) >> 6);
7877 image->colormap[i].green=ScaleCharToQuantum(
7878 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) |
7879 (ScaleQuantumToChar(image->colormap[i].green) & 0xe0) >> 3 |
7880 (ScaleQuantumToChar(image->colormap[i].green) & 0xc0) >> 6);
7881 image->colormap[i].blue=ScaleCharToQuantum(
7882 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) |
7883 (ScaleQuantumToChar(image->colormap[i].blue) & 0xe0) >> 3 |
7884 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007885 }
glennrpd3371642011-03-22 19:42:23 +00007886 }
7887 continue;
glennrp82b3c532011-03-22 19:20:54 +00007888 }
glennrpc8c2f062011-02-25 19:00:33 +00007889
glennrpc8c2f062011-02-25 19:00:33 +00007890 if (image_colors == 0 || image_colors > 256)
7891 {
7892 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00007894 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00007895
glennrp3faa9a32011-04-23 14:00:25 +00007896 /* Red and green were already done so we only quantize the blue
7897 * channel
7898 */
7899
7900 image->background_color.blue=ScaleCharToQuantum(
7901 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) |
7902 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 2 |
7903 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 4 |
7904 (ScaleQuantumToChar(image->background_color.blue) & 0xc0) >> 6);
glennrpfd05d622011-02-25 04:10:33 +00007905
glennrpc8c2f062011-02-25 19:00:33 +00007906 if (logging != MagickFalse)
7907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007908 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00007909
glennrpc8c2f062011-02-25 19:00:33 +00007910 if (image->colormap == NULL)
7911 {
7912 for (y=0; y < (ssize_t) image->rows; y++)
7913 {
7914 r=GetAuthenticPixels(image,0,y,image->columns,1,
7915 exception);
7916
7917 if (r == (PixelPacket *) NULL)
7918 break;
7919
7920 for (x=0; x < (ssize_t) image->columns; x++)
7921 {
glennrp82b3c532011-03-22 19:20:54 +00007922 if (r->opacity == TransparentOpacity)
7923 {
7924 r->red = image->background_color.red;
7925 r->green = image->background_color.green;
7926 r->blue = image->background_color.blue;
7927 }
7928 else
7929 {
glennrp3faa9a32011-04-23 14:00:25 +00007930 r->blue=ScaleCharToQuantum(
7931 (ScaleQuantumToChar(r->blue) & 0xc0) |
7932 (ScaleQuantumToChar(r->blue) & 0xc0) >> 2 |
7933 (ScaleQuantumToChar(r->blue) & 0xc0) >> 4 |
7934 (ScaleQuantumToChar(r->blue) & 0xc0) >> 6);
glennrp82b3c532011-03-22 19:20:54 +00007935 }
glennrp52a479c2011-02-26 21:14:38 +00007936 r++;
glennrpc8c2f062011-02-25 19:00:33 +00007937 }
glennrpfd05d622011-02-25 04:10:33 +00007938
glennrpc8c2f062011-02-25 19:00:33 +00007939 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7940 break;
7941 }
7942 }
glennrpfd05d622011-02-25 04:10:33 +00007943
glennrpc8c2f062011-02-25 19:00:33 +00007944 else /* Should not reach this; colormap already exists and
7945 must be <= 256 */
7946 {
7947 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00007948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00007949 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00007950 for (i=0; i<image_colors; i++)
7951 {
glennrp3faa9a32011-04-23 14:00:25 +00007952 image->colormap[i].blue=ScaleCharToQuantum(
7953 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) |
7954 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 2 |
7955 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 4 |
7956 (ScaleQuantumToChar(image->colormap[i].blue) & 0xc0) >> 6);
glennrpc8c2f062011-02-25 19:00:33 +00007957 }
7958 }
7959 continue;
7960 }
7961 break;
glennrpd71e86a2011-02-24 01:28:37 +00007962 }
glennrpfd05d622011-02-25 04:10:33 +00007963 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00007964
glennrpfd05d622011-02-25 04:10:33 +00007965 /* If we are excluding the tRNS chunk and there is transparency,
7966 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7967 * PNG.
glennrp8d579662011-02-23 02:05:02 +00007968 */
glennrp0e8ea192010-12-24 18:00:33 +00007969 if (mng_info->ping_exclude_tRNS != MagickFalse &&
7970 (number_transparent != 0 || number_semitransparent != 0))
7971 {
7972 int colortype=mng_info->write_png_colortype;
7973
7974 if (ping_have_color == MagickFalse)
7975 mng_info->write_png_colortype = 5;
7976
7977 else
7978 mng_info->write_png_colortype = 7;
7979
glennrp8d579662011-02-23 02:05:02 +00007980 if (colortype != 0 &&
7981 mng_info->write_png_colortype != (ssize_t) colortype)
glennrp0e8ea192010-12-24 18:00:33 +00007982 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00007983
glennrp0e8ea192010-12-24 18:00:33 +00007984 }
7985
glennrpfd05d622011-02-25 04:10:33 +00007986 /* See if cheap transparency is possible. It is only possible
7987 * when there is a single transparent color, no semitransparent
7988 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00007989 * as the transparent color. We only need this information if
7990 * we are writing a PNG with colortype 0 or 2, and we have not
7991 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00007992 */
glennrp5a39f372011-02-25 04:52:16 +00007993 if (number_transparent == 1 &&
7994 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00007995 {
7996 ping_have_cheap_transparency = MagickTrue;
7997
7998 if (number_semitransparent != 0)
7999 ping_have_cheap_transparency = MagickFalse;
8000
8001 else if (image_colors == 0 || image_colors > 256 ||
8002 image->colormap == NULL)
8003 {
8004 ExceptionInfo
8005 *exception;
8006
8007 register const PixelPacket
8008 *q;
8009
8010 exception=(&image->exception);
8011
8012 for (y=0; y < (ssize_t) image->rows; y++)
8013 {
8014 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8015
8016 if (q == (PixelPacket *) NULL)
8017 break;
8018
8019 for (x=0; x < (ssize_t) image->columns; x++)
8020 {
8021 if (q->opacity != TransparentOpacity &&
8022 (unsigned short) q->red == ping_trans_color.red &&
8023 (unsigned short) q->green == ping_trans_color.green &&
8024 (unsigned short) q->blue == ping_trans_color.blue)
8025 {
8026 ping_have_cheap_transparency = MagickFalse;
8027 break;
8028 }
8029
8030 q++;
8031 }
8032
8033 if (ping_have_cheap_transparency == MagickFalse)
8034 break;
8035 }
8036 }
8037 else
8038 {
glennrp67b9c1a2011-04-22 18:47:36 +00008039 /* Assuming that image->colormap[0] is the one transparent color
8040 * and that all others are opaque.
8041 */
glennrpfd05d622011-02-25 04:10:33 +00008042 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008043 for (i=1; i<image_colors; i++)
8044 if (image->colormap[i].red == image->colormap[0].red &&
8045 image->colormap[i].green == image->colormap[0].green &&
8046 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008047 {
glennrp67b9c1a2011-04-22 18:47:36 +00008048 ping_have_cheap_transparency = MagickFalse;
8049 break;
glennrpfd05d622011-02-25 04:10:33 +00008050 }
8051 }
8052
8053 if (logging != MagickFalse)
8054 {
8055 if (ping_have_cheap_transparency == MagickFalse)
8056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8057 " Cheap transparency is not possible.");
8058
8059 else
8060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8061 " Cheap transparency is possible.");
8062 }
8063 }
8064 else
8065 ping_have_cheap_transparency = MagickFalse;
8066
glennrp8640fb52010-11-23 15:48:26 +00008067 image_depth=image->depth;
8068
glennrp26c990a2010-11-23 02:23:20 +00008069 quantum_info = (QuantumInfo *) NULL;
8070 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008071 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008072 image_matte=image->matte;
8073
glennrp0fe50b42010-11-16 03:52:51 +00008074 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008075 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008076
glennrp52a479c2011-02-26 21:14:38 +00008077 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8078 (image->colors == 0 || image->colormap == NULL))
8079 {
glennrp52a479c2011-02-26 21:14:38 +00008080 image_info=DestroyImageInfo(image_info);
8081 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008082 (void) ThrowMagickException(&IMimage->exception,
8083 GetMagickModule(),CoderError,
8084 "Cannot write PNG8 or color-type 3; colormap is NULL",
8085 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008086#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8087 UnlockSemaphoreInfo(ping_semaphore);
8088#endif
8089 return(MagickFalse);
8090 }
8091
cristy3ed852e2009-09-05 21:47:34 +00008092 /*
8093 Allocate the PNG structures
8094 */
8095#ifdef PNG_USER_MEM_SUPPORTED
8096 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008097 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8098 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008099
cristy3ed852e2009-09-05 21:47:34 +00008100#else
8101 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008102 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008103
cristy3ed852e2009-09-05 21:47:34 +00008104#endif
8105 if (ping == (png_struct *) NULL)
8106 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008107
cristy3ed852e2009-09-05 21:47:34 +00008108 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008109
cristy3ed852e2009-09-05 21:47:34 +00008110 if (ping_info == (png_info *) NULL)
8111 {
8112 png_destroy_write_struct(&ping,(png_info **) NULL);
8113 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8114 }
glennrp0fe50b42010-11-16 03:52:51 +00008115
cristy3ed852e2009-09-05 21:47:34 +00008116 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008117 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008118
glennrp5af765f2010-03-30 11:12:18 +00008119 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008120 {
8121 /*
8122 PNG write failed.
8123 */
8124#ifdef PNG_DEBUG
8125 if (image_info->verbose)
8126 (void) printf("PNG write has failed.\n");
8127#endif
8128 png_destroy_write_struct(&ping,&ping_info);
8129#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008130 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008131#endif
glennrpda8f3a72011-02-27 23:54:12 +00008132 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008133 (void) CloseBlob(image);
8134 image_info=DestroyImageInfo(image_info);
8135 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008136 return(MagickFalse);
8137 }
8138 /*
8139 Prepare PNG for writing.
8140 */
8141#if defined(PNG_MNG_FEATURES_SUPPORTED)
8142 if (mng_info->write_mng)
8143 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008144
cristy3ed852e2009-09-05 21:47:34 +00008145#else
8146# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8147 if (mng_info->write_mng)
8148 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008149
cristy3ed852e2009-09-05 21:47:34 +00008150# endif
8151#endif
glennrp2b013e42010-11-24 16:55:50 +00008152
cristy3ed852e2009-09-05 21:47:34 +00008153 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008154
cristy4e5bc842010-06-09 13:56:01 +00008155 ping_width=(png_uint_32) image->columns;
8156 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008157
cristy3ed852e2009-09-05 21:47:34 +00008158 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8159 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008160
cristy3ed852e2009-09-05 21:47:34 +00008161 if (mng_info->write_png_depth != 0)
8162 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008163
cristy3ed852e2009-09-05 21:47:34 +00008164 /* Adjust requested depth to next higher valid depth if necessary */
8165 if (image_depth > 8)
8166 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008167
cristy3ed852e2009-09-05 21:47:34 +00008168 if ((image_depth > 4) && (image_depth < 8))
8169 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008170
cristy3ed852e2009-09-05 21:47:34 +00008171 if (image_depth == 3)
8172 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008173
cristy3ed852e2009-09-05 21:47:34 +00008174 if (logging != MagickFalse)
8175 {
8176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008177 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008179 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008181 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008183 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008185 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008186 }
glennrp8640fb52010-11-23 15:48:26 +00008187
cristy3ed852e2009-09-05 21:47:34 +00008188 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008189 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008190
glennrp26f37912010-12-23 16:22:42 +00008191
cristy3ed852e2009-09-05 21:47:34 +00008192#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008193 if (ping_exclude_pHYs == MagickFalse)
8194 {
cristy3ed852e2009-09-05 21:47:34 +00008195 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8196 (!mng_info->write_mng || !mng_info->equal_physs))
8197 {
glennrp0fe50b42010-11-16 03:52:51 +00008198 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8200 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008201
8202 if (image->units == PixelsPerInchResolution)
8203 {
glennrpdfd70802010-11-14 01:23:35 +00008204 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008205 ping_pHYs_x_resolution=
8206 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8207 ping_pHYs_y_resolution=
8208 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008209 }
glennrpdfd70802010-11-14 01:23:35 +00008210
cristy3ed852e2009-09-05 21:47:34 +00008211 else if (image->units == PixelsPerCentimeterResolution)
8212 {
glennrpdfd70802010-11-14 01:23:35 +00008213 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008214 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8215 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008216 }
glennrp991d11d2010-11-12 21:55:28 +00008217
cristy3ed852e2009-09-05 21:47:34 +00008218 else
8219 {
glennrpdfd70802010-11-14 01:23:35 +00008220 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8221 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8222 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008223 }
glennrp991d11d2010-11-12 21:55:28 +00008224
glennrp823b55c2011-03-14 18:46:46 +00008225 if (logging != MagickFalse)
8226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8227 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8228 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8229 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008230 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008231 }
glennrp26f37912010-12-23 16:22:42 +00008232 }
cristy3ed852e2009-09-05 21:47:34 +00008233#endif
glennrpa521b2f2010-10-29 04:11:03 +00008234
glennrp26f37912010-12-23 16:22:42 +00008235 if (ping_exclude_bKGD == MagickFalse)
8236 {
glennrpa521b2f2010-10-29 04:11:03 +00008237 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008238 {
glennrpa521b2f2010-10-29 04:11:03 +00008239 unsigned int
8240 mask;
cristy3ed852e2009-09-05 21:47:34 +00008241
glennrpa521b2f2010-10-29 04:11:03 +00008242 mask=0xffff;
8243 if (ping_bit_depth == 8)
8244 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008245
glennrpa521b2f2010-10-29 04:11:03 +00008246 if (ping_bit_depth == 4)
8247 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008248
glennrpa521b2f2010-10-29 04:11:03 +00008249 if (ping_bit_depth == 2)
8250 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008251
glennrpa521b2f2010-10-29 04:11:03 +00008252 if (ping_bit_depth == 1)
8253 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008254
glennrpa521b2f2010-10-29 04:11:03 +00008255 ping_background.red=(png_uint_16)
8256 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008257
glennrpa521b2f2010-10-29 04:11:03 +00008258 ping_background.green=(png_uint_16)
8259 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008260
glennrpa521b2f2010-10-29 04:11:03 +00008261 ping_background.blue=(png_uint_16)
8262 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008263 }
cristy3ed852e2009-09-05 21:47:34 +00008264
glennrp0fe50b42010-11-16 03:52:51 +00008265 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008266 {
8267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8268 " Setting up bKGD chunk (1)");
8269
8270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8271 " ping_bit_depth=%d",ping_bit_depth);
8272 }
glennrp0fe50b42010-11-16 03:52:51 +00008273
8274 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008275 }
glennrp0fe50b42010-11-16 03:52:51 +00008276
cristy3ed852e2009-09-05 21:47:34 +00008277 /*
8278 Select the color type.
8279 */
8280 matte=image_matte;
8281 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008282
glennrp1273f7b2011-02-24 03:20:30 +00008283 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008284 {
glennrp0fe50b42010-11-16 03:52:51 +00008285
glennrpfd05d622011-02-25 04:10:33 +00008286 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008287 for reducing the sample depth from 8. */
8288
glennrp0fe50b42010-11-16 03:52:51 +00008289 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008290
glennrp8bb3a022010-12-13 20:40:04 +00008291 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008292
8293 /*
8294 Set image palette.
8295 */
8296 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8297
glennrp0fe50b42010-11-16 03:52:51 +00008298 if (logging != MagickFalse)
8299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8300 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008301 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008302
8303 for (i=0; i < (ssize_t) number_colors; i++)
8304 {
8305 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8306 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8307 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8308 if (logging != MagickFalse)
8309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008310#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008311 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008312#else
8313 " %5ld (%5d,%5d,%5d)",
8314#endif
glennrp0fe50b42010-11-16 03:52:51 +00008315 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8316
8317 }
glennrp2b013e42010-11-24 16:55:50 +00008318
glennrp8bb3a022010-12-13 20:40:04 +00008319 ping_have_PLTE=MagickTrue;
8320 image_depth=ping_bit_depth;
8321 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008322
glennrp58e01762011-01-07 15:28:54 +00008323 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008324 {
glennrp0fe50b42010-11-16 03:52:51 +00008325 /*
8326 Identify which colormap entry is transparent.
8327 */
8328 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008329 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008330
glennrp8bb3a022010-12-13 20:40:04 +00008331 for (i=0; i < (ssize_t) number_transparent; i++)
8332 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008333
glennrp0fe50b42010-11-16 03:52:51 +00008334
glennrp2cc891a2010-12-24 13:44:32 +00008335 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008336 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008337
8338 if (ping_num_trans == 0)
8339 ping_have_tRNS=MagickFalse;
8340
glennrp8bb3a022010-12-13 20:40:04 +00008341 else
8342 ping_have_tRNS=MagickTrue;
8343 }
glennrp0fe50b42010-11-16 03:52:51 +00008344
glennrp1273f7b2011-02-24 03:20:30 +00008345 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008346 {
glennrp1273f7b2011-02-24 03:20:30 +00008347 /*
8348 * Identify which colormap entry is the background color.
8349 */
8350
glennrp4f25bd02011-01-01 18:51:28 +00008351 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8352 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8353 break;
glennrp0fe50b42010-11-16 03:52:51 +00008354
glennrp4f25bd02011-01-01 18:51:28 +00008355 ping_background.index=(png_byte) i;
8356 }
cristy3ed852e2009-09-05 21:47:34 +00008357 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00008358
cristy3ed852e2009-09-05 21:47:34 +00008359 else if (mng_info->write_png24)
8360 {
8361 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00008362 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008363 }
glennrp0fe50b42010-11-16 03:52:51 +00008364
cristy3ed852e2009-09-05 21:47:34 +00008365 else if (mng_info->write_png32)
8366 {
8367 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00008368 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008369 }
glennrp0fe50b42010-11-16 03:52:51 +00008370
glennrp8bb3a022010-12-13 20:40:04 +00008371 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00008372 {
glennrp5af765f2010-03-30 11:12:18 +00008373 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008374
glennrp8bb3a022010-12-13 20:40:04 +00008375 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00008376 {
glennrp5af765f2010-03-30 11:12:18 +00008377 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00008378
glennrp5af765f2010-03-30 11:12:18 +00008379 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8380 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008381 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00008382
glennrp8bb3a022010-12-13 20:40:04 +00008383 else
8384 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008385
8386 if (logging != MagickFalse)
8387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8388 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008389 }
glennrp0fe50b42010-11-16 03:52:51 +00008390
glennrp7c4c9e62011-03-21 20:23:32 +00008391 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00008392 {
8393 if (logging != MagickFalse)
8394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008395 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00008396
glennrpd6bf1612010-12-17 17:28:54 +00008397 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00008398 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00008399
glennrpd6bf1612010-12-17 17:28:54 +00008400 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00008401 {
glennrp5af765f2010-03-30 11:12:18 +00008402 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00008403 image_matte=MagickFalse;
8404 }
glennrp0fe50b42010-11-16 03:52:51 +00008405
glennrpd6bf1612010-12-17 17:28:54 +00008406 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00008407 {
glennrp5af765f2010-03-30 11:12:18 +00008408 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00008409 image_matte=MagickTrue;
8410 }
glennrp0fe50b42010-11-16 03:52:51 +00008411
glennrp5aa37f62011-01-02 03:07:57 +00008412 if (image_info->type == PaletteType ||
8413 image_info->type == PaletteMatteType)
8414 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8415
glennrp7c4c9e62011-03-21 20:23:32 +00008416 if (mng_info->write_png_colortype == 0 &&
8417 (image_info->type == UndefinedType ||
8418 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00008419 {
glennrp5aa37f62011-01-02 03:07:57 +00008420 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008421 {
glennrp5aa37f62011-01-02 03:07:57 +00008422 if (image_matte == MagickFalse)
8423 {
8424 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8425 image_matte=MagickFalse;
8426 }
glennrp0fe50b42010-11-16 03:52:51 +00008427
glennrp0b206f52011-01-07 04:55:32 +00008428 else
glennrp5aa37f62011-01-02 03:07:57 +00008429 {
8430 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8431 image_matte=MagickTrue;
8432 }
8433 }
8434 else
glennrp8bb3a022010-12-13 20:40:04 +00008435 {
glennrp5aa37f62011-01-02 03:07:57 +00008436 if (image_matte == MagickFalse)
8437 {
8438 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8439 image_matte=MagickFalse;
8440 }
glennrp8bb3a022010-12-13 20:40:04 +00008441
glennrp0b206f52011-01-07 04:55:32 +00008442 else
glennrp5aa37f62011-01-02 03:07:57 +00008443 {
8444 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8445 image_matte=MagickTrue;
8446 }
8447 }
glennrp0fe50b42010-11-16 03:52:51 +00008448 }
glennrp5aa37f62011-01-02 03:07:57 +00008449
cristy3ed852e2009-09-05 21:47:34 +00008450 }
glennrp0fe50b42010-11-16 03:52:51 +00008451
cristy3ed852e2009-09-05 21:47:34 +00008452 if (logging != MagickFalse)
8453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008454 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00008455
glennrp5af765f2010-03-30 11:12:18 +00008456 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00008457 {
8458 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8459 ping_color_type == PNG_COLOR_TYPE_RGB ||
8460 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8461 ping_bit_depth=8;
8462 }
cristy3ed852e2009-09-05 21:47:34 +00008463
glennrpd6bf1612010-12-17 17:28:54 +00008464 old_bit_depth=ping_bit_depth;
8465
glennrp5af765f2010-03-30 11:12:18 +00008466 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00008467 {
glennrp8d579662011-02-23 02:05:02 +00008468 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8469 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00008470 }
glennrp8640fb52010-11-23 15:48:26 +00008471
glennrp5af765f2010-03-30 11:12:18 +00008472 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008473 {
cristy35ef8242010-06-03 16:24:13 +00008474 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00008475 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00008476
8477 if (image->colors == 0)
8478 {
glennrp0fe50b42010-11-16 03:52:51 +00008479 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00008480 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00008481 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00008482 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00008483 }
8484
cristy35ef8242010-06-03 16:24:13 +00008485 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008486 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008487 }
glennrp2b013e42010-11-24 16:55:50 +00008488
glennrpd6bf1612010-12-17 17:28:54 +00008489 if (logging != MagickFalse)
8490 {
8491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8492 " Number of colors: %.20g",(double) image_colors);
8493
8494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8495 " Tentative PNG bit depth: %d",ping_bit_depth);
8496 }
8497
8498 if (ping_bit_depth < (int) mng_info->write_png_depth)
8499 ping_bit_depth = mng_info->write_png_depth;
8500 }
glennrp2cc891a2010-12-24 13:44:32 +00008501
glennrp5af765f2010-03-30 11:12:18 +00008502 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00008503
cristy3ed852e2009-09-05 21:47:34 +00008504 if (logging != MagickFalse)
8505 {
8506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008507 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00008508
cristy3ed852e2009-09-05 21:47:34 +00008509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008510 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00008511
cristy3ed852e2009-09-05 21:47:34 +00008512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008513 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008514
cristy3ed852e2009-09-05 21:47:34 +00008515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008516
glennrp8640fb52010-11-23 15:48:26 +00008517 " image->depth: %.20g",(double) image->depth);
8518
8519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008520 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00008521 }
8522
glennrp58e01762011-01-07 15:28:54 +00008523 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008524 {
glennrp4f25bd02011-01-01 18:51:28 +00008525 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00008526 {
glennrp7c4c9e62011-03-21 20:23:32 +00008527 if (mng_info->write_png_colortype == 0)
8528 {
8529 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00008530
glennrp7c4c9e62011-03-21 20:23:32 +00008531 if (ping_have_color != MagickFalse)
8532 ping_color_type=PNG_COLOR_TYPE_RGBA;
8533 }
glennrp4f25bd02011-01-01 18:51:28 +00008534
8535 /*
8536 * Determine if there is any transparent color.
8537 */
8538 if (number_transparent + number_semitransparent == 0)
8539 {
8540 /*
8541 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8542 */
glennrpa6a06632011-01-19 15:15:34 +00008543
glennrp4f25bd02011-01-01 18:51:28 +00008544 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00008545
8546 if (mng_info->write_png_colortype == 0)
8547 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00008548 }
8549
8550 else
8551 {
8552 unsigned int
glennrp1273f7b2011-02-24 03:20:30 +00008553 mask;
glennrp4f25bd02011-01-01 18:51:28 +00008554
8555 mask=0xffff;
8556
8557 if (ping_bit_depth == 8)
8558 mask=0x00ff;
8559
8560 if (ping_bit_depth == 4)
8561 mask=0x000f;
8562
8563 if (ping_bit_depth == 2)
8564 mask=0x0003;
8565
8566 if (ping_bit_depth == 1)
8567 mask=0x0001;
8568
8569 ping_trans_color.red=(png_uint_16)
8570 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8571
8572 ping_trans_color.green=(png_uint_16)
8573 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8574
8575 ping_trans_color.blue=(png_uint_16)
8576 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8577
8578 ping_trans_color.gray=(png_uint_16)
8579 (ScaleQuantumToShort(PixelIntensityToQuantum(
8580 image->colormap)) & mask);
8581
8582 ping_trans_color.index=(png_byte) 0;
8583
8584 ping_have_tRNS=MagickTrue;
8585 }
8586
8587 if (ping_have_tRNS != MagickFalse)
8588 {
8589 /*
glennrpfd05d622011-02-25 04:10:33 +00008590 * Determine if there is one and only one transparent color
8591 * and if so if it is fully transparent.
8592 */
8593 if (ping_have_cheap_transparency == MagickFalse)
8594 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00008595 }
8596
8597 if (ping_have_tRNS != MagickFalse)
8598 {
glennrp7c4c9e62011-03-21 20:23:32 +00008599 if (mng_info->write_png_colortype == 0)
8600 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00008601
8602 if (image_depth == 8)
8603 {
8604 ping_trans_color.red&=0xff;
8605 ping_trans_color.green&=0xff;
8606 ping_trans_color.blue&=0xff;
8607 ping_trans_color.gray&=0xff;
8608 }
8609 }
8610 }
cristy3ed852e2009-09-05 21:47:34 +00008611 else
8612 {
cristy3ed852e2009-09-05 21:47:34 +00008613 if (image_depth == 8)
8614 {
glennrp5af765f2010-03-30 11:12:18 +00008615 ping_trans_color.red&=0xff;
8616 ping_trans_color.green&=0xff;
8617 ping_trans_color.blue&=0xff;
8618 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00008619 }
8620 }
8621 }
glennrp8640fb52010-11-23 15:48:26 +00008622
cristy3ed852e2009-09-05 21:47:34 +00008623 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00008624
glennrp2e09f552010-11-14 00:38:48 +00008625 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008626 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008627
glennrp39992b42010-11-14 00:03:43 +00008628 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00008629 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00008630 ping_have_color == MagickFalse &&
8631 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00008632 {
cristy35ef8242010-06-03 16:24:13 +00008633 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008634
cristy3ed852e2009-09-05 21:47:34 +00008635 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008636 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00008637
glennrp7c4c9e62011-03-21 20:23:32 +00008638 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00008639 {
glennrp5af765f2010-03-30 11:12:18 +00008640 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00008641
cristy3ed852e2009-09-05 21:47:34 +00008642 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00008643 {
8644 if (logging != MagickFalse)
8645 {
8646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8647 " Scaling ping_trans_color (0)");
8648 }
8649 ping_trans_color.gray*=0x0101;
8650 }
cristy3ed852e2009-09-05 21:47:34 +00008651 }
glennrp0fe50b42010-11-16 03:52:51 +00008652
cristy3ed852e2009-09-05 21:47:34 +00008653 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8654 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00008655
cristy85380932011-04-25 17:11:52 +00008656 if ((image_colors == 0) || ((ssize_t) (image_colors-1) > MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00008657 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00008658
cristy3ed852e2009-09-05 21:47:34 +00008659 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00008660 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008661
cristy3ed852e2009-09-05 21:47:34 +00008662 else
8663 {
glennrp5af765f2010-03-30 11:12:18 +00008664 ping_bit_depth=8;
8665 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00008666 {
8667 if(!mng_info->write_png_depth)
8668 {
glennrp5af765f2010-03-30 11:12:18 +00008669 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008670
cristy35ef8242010-06-03 16:24:13 +00008671 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00008672 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00008673 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008674 }
8675 }
glennrp2b013e42010-11-24 16:55:50 +00008676
glennrp0fe50b42010-11-16 03:52:51 +00008677 else if (ping_color_type ==
8678 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00008679 mng_info->IsPalette)
8680 {
cristy3ed852e2009-09-05 21:47:34 +00008681 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00008682
cristy3ed852e2009-09-05 21:47:34 +00008683 int
8684 depth_4_ok=MagickTrue,
8685 depth_2_ok=MagickTrue,
8686 depth_1_ok=MagickTrue;
8687
cristybb503372010-05-27 20:51:26 +00008688 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008689 {
8690 unsigned char
8691 intensity;
8692
8693 intensity=ScaleQuantumToChar(image->colormap[i].red);
8694
8695 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8696 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8697 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8698 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00008699 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00008700 depth_1_ok=MagickFalse;
8701 }
glennrp2b013e42010-11-24 16:55:50 +00008702
cristy3ed852e2009-09-05 21:47:34 +00008703 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00008704 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00008705
cristy3ed852e2009-09-05 21:47:34 +00008706 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00008707 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00008708
cristy3ed852e2009-09-05 21:47:34 +00008709 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00008710 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00008711 }
8712 }
glennrp2b013e42010-11-24 16:55:50 +00008713
glennrp5af765f2010-03-30 11:12:18 +00008714 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008715 }
glennrp0fe50b42010-11-16 03:52:51 +00008716
cristy3ed852e2009-09-05 21:47:34 +00008717 else
glennrp0fe50b42010-11-16 03:52:51 +00008718
cristy3ed852e2009-09-05 21:47:34 +00008719 if (mng_info->IsPalette)
8720 {
glennrp17a14852010-05-10 03:01:59 +00008721 number_colors=image_colors;
8722
cristy3ed852e2009-09-05 21:47:34 +00008723 if (image_depth <= 8)
8724 {
cristy3ed852e2009-09-05 21:47:34 +00008725 /*
8726 Set image palette.
8727 */
glennrp5af765f2010-03-30 11:12:18 +00008728 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00008729
glennrp58e01762011-01-07 15:28:54 +00008730 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008731 {
glennrp9c1eb072010-06-06 22:19:15 +00008732 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00008733
glennrp3b51f0e2010-11-27 18:14:08 +00008734 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8736 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00008737 }
glennrp0fe50b42010-11-16 03:52:51 +00008738
cristy3ed852e2009-09-05 21:47:34 +00008739 else
8740 {
cristybb503372010-05-27 20:51:26 +00008741 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00008742 {
8743 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8744 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8745 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8746 }
glennrp0fe50b42010-11-16 03:52:51 +00008747
glennrp3b51f0e2010-11-27 18:14:08 +00008748 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00008750 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00008751 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008752
glennrp39992b42010-11-14 00:03:43 +00008753 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008754 }
glennrp0fe50b42010-11-16 03:52:51 +00008755
cristy3ed852e2009-09-05 21:47:34 +00008756 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00008757 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00008758 {
cristybefe4d22010-06-07 01:18:58 +00008759 size_t
8760 one;
8761
glennrp5af765f2010-03-30 11:12:18 +00008762 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00008763 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00008764
cristybefe4d22010-06-07 01:18:58 +00008765 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00008766 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00008767 }
glennrp0fe50b42010-11-16 03:52:51 +00008768
glennrp5af765f2010-03-30 11:12:18 +00008769 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00008770
glennrp58e01762011-01-07 15:28:54 +00008771 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00008772 {
glennrp0fe50b42010-11-16 03:52:51 +00008773 /*
glennrpd6bf1612010-12-17 17:28:54 +00008774 * Set up trans_colors array.
8775 */
glennrp0fe50b42010-11-16 03:52:51 +00008776 assert(number_colors <= 256);
8777
glennrpd6bf1612010-12-17 17:28:54 +00008778 ping_num_trans=(unsigned short) (number_transparent +
8779 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008780
8781 if (ping_num_trans == 0)
8782 ping_have_tRNS=MagickFalse;
8783
glennrpd6bf1612010-12-17 17:28:54 +00008784 else
glennrp0fe50b42010-11-16 03:52:51 +00008785 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008786 if (logging != MagickFalse)
8787 {
8788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8789 " Scaling ping_trans_color (1)");
8790 }
glennrpd6bf1612010-12-17 17:28:54 +00008791 ping_have_tRNS=MagickTrue;
8792
8793 for (i=0; i < ping_num_trans; i++)
8794 {
8795 ping_trans_alpha[i]= (png_byte) (255-
8796 ScaleQuantumToChar(image->colormap[i].opacity));
8797 }
glennrp0fe50b42010-11-16 03:52:51 +00008798 }
8799 }
cristy3ed852e2009-09-05 21:47:34 +00008800 }
8801 }
glennrp0fe50b42010-11-16 03:52:51 +00008802
cristy3ed852e2009-09-05 21:47:34 +00008803 else
8804 {
glennrpc8cbc5d2011-01-01 00:12:34 +00008805
cristy3ed852e2009-09-05 21:47:34 +00008806 if (image_depth < 8)
8807 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008808
cristy3ed852e2009-09-05 21:47:34 +00008809 if ((save_image_depth == 16) && (image_depth == 8))
8810 {
glennrp4f25bd02011-01-01 18:51:28 +00008811 if (logging != MagickFalse)
8812 {
8813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8814 " Scaling ping_trans_color from (%d,%d,%d)",
8815 (int) ping_trans_color.red,
8816 (int) ping_trans_color.green,
8817 (int) ping_trans_color.blue);
8818 }
8819
glennrp5af765f2010-03-30 11:12:18 +00008820 ping_trans_color.red*=0x0101;
8821 ping_trans_color.green*=0x0101;
8822 ping_trans_color.blue*=0x0101;
8823 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00008824
8825 if (logging != MagickFalse)
8826 {
8827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8828 " to (%d,%d,%d)",
8829 (int) ping_trans_color.red,
8830 (int) ping_trans_color.green,
8831 (int) ping_trans_color.blue);
8832 }
cristy3ed852e2009-09-05 21:47:34 +00008833 }
8834 }
8835
cristy4383ec82011-01-05 15:42:32 +00008836 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8837 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00008838
cristy3ed852e2009-09-05 21:47:34 +00008839 /*
8840 Adjust background and transparency samples in sub-8-bit grayscale files.
8841 */
glennrp5af765f2010-03-30 11:12:18 +00008842 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00008843 PNG_COLOR_TYPE_GRAY)
8844 {
8845 png_uint_16
8846 maxval;
8847
cristy35ef8242010-06-03 16:24:13 +00008848 size_t
8849 one=1;
8850
cristy22ffd972010-06-03 16:51:47 +00008851 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00008852
glennrp4f25bd02011-01-01 18:51:28 +00008853 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00008854 {
cristy3ed852e2009-09-05 21:47:34 +00008855
glennrpa521b2f2010-10-29 04:11:03 +00008856 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00008857 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8858
8859 if (logging != MagickFalse)
8860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00008861 " Setting up bKGD chunk (2)");
glennrp3b51f0e2010-11-27 18:14:08 +00008862
glennrp991d11d2010-11-12 21:55:28 +00008863 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008864 }
cristy3ed852e2009-09-05 21:47:34 +00008865
glennrp5af765f2010-03-30 11:12:18 +00008866 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8867 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00008868 }
glennrp17a14852010-05-10 03:01:59 +00008869
glennrp26f37912010-12-23 16:22:42 +00008870 if (ping_exclude_bKGD == MagickFalse)
8871 {
glennrp1273f7b2011-02-24 03:20:30 +00008872 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00008873 {
8874 /*
8875 Identify which colormap entry is the background color.
8876 */
8877
glennrp17a14852010-05-10 03:01:59 +00008878 number_colors=image_colors;
8879
glennrpa521b2f2010-10-29 04:11:03 +00008880 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8881 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00008882 break;
8883
8884 ping_background.index=(png_byte) i;
8885
glennrp3b51f0e2010-11-27 18:14:08 +00008886 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00008887 {
8888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00008889 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00008890 }
glennrp0fe50b42010-11-16 03:52:51 +00008891
cristy13d07042010-11-21 20:56:18 +00008892 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00008893 {
8894 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00008895
8896 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008897 {
8898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8899 " background =(%d,%d,%d)",
8900 (int) ping_background.red,
8901 (int) ping_background.green,
8902 (int) ping_background.blue);
8903 }
8904 }
glennrpa521b2f2010-10-29 04:11:03 +00008905
glennrpd6bf1612010-12-17 17:28:54 +00008906 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00008907 {
glennrp3b51f0e2010-11-27 18:14:08 +00008908 if (logging != MagickFalse)
8909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8910 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00008911 ping_have_bKGD = MagickFalse;
8912 }
glennrp17a14852010-05-10 03:01:59 +00008913 }
glennrp26f37912010-12-23 16:22:42 +00008914 }
glennrp17a14852010-05-10 03:01:59 +00008915
cristy3ed852e2009-09-05 21:47:34 +00008916 if (logging != MagickFalse)
8917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00008918 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00008919 /*
8920 Initialize compression level and filtering.
8921 */
8922 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00008923 {
8924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8925 " Setting up deflate compression");
8926
8927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8928 " Compression buffer size: 32768");
8929 }
8930
cristy3ed852e2009-09-05 21:47:34 +00008931 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00008932
cristy3ed852e2009-09-05 21:47:34 +00008933 if (logging != MagickFalse)
8934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8935 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00008936
cristy3ed852e2009-09-05 21:47:34 +00008937 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00008938
cristy3ed852e2009-09-05 21:47:34 +00008939 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8940 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00008941
cristy3ed852e2009-09-05 21:47:34 +00008942 if (quality > 9)
8943 {
8944 int
8945 level;
8946
cristybb503372010-05-27 20:51:26 +00008947 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00008948
cristy3ed852e2009-09-05 21:47:34 +00008949 if (logging != MagickFalse)
8950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8951 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00008952
cristy3ed852e2009-09-05 21:47:34 +00008953 png_set_compression_level(ping,level);
8954 }
glennrp0fe50b42010-11-16 03:52:51 +00008955
cristy3ed852e2009-09-05 21:47:34 +00008956 else
8957 {
8958 if (logging != MagickFalse)
8959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8960 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00008961
cristy3ed852e2009-09-05 21:47:34 +00008962 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8963 }
glennrp0fe50b42010-11-16 03:52:51 +00008964
cristy3ed852e2009-09-05 21:47:34 +00008965 if (logging != MagickFalse)
8966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8967 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00008968
glennrp2b013e42010-11-24 16:55:50 +00008969#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00008970 /* This became available in libpng-1.0.9. Output must be a MNG. */
8971 if (mng_info->write_mng && ((quality % 10) == 7))
8972 {
8973 if (logging != MagickFalse)
8974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8975 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00008976
glennrp5af765f2010-03-30 11:12:18 +00008977 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00008978 }
glennrp0fe50b42010-11-16 03:52:51 +00008979
cristy3ed852e2009-09-05 21:47:34 +00008980 else
8981 if (logging != MagickFalse)
8982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8983 " Filter_type: 0");
8984#endif
glennrp2b013e42010-11-24 16:55:50 +00008985
cristy3ed852e2009-09-05 21:47:34 +00008986 {
8987 int
8988 base_filter;
8989
8990 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00008991 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00008992
glennrp26c990a2010-11-23 02:23:20 +00008993 else
glennrp8640fb52010-11-23 15:48:26 +00008994 if ((quality % 10) != 5)
8995 base_filter=(int) quality % 10;
8996
8997 else
8998 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8999 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9000 (quality < 50))
9001 base_filter=PNG_NO_FILTERS;
9002
9003 else
9004 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009005
cristy3ed852e2009-09-05 21:47:34 +00009006 if (logging != MagickFalse)
9007 {
9008 if (base_filter == PNG_ALL_FILTERS)
9009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9010 " Base filter method: ADAPTIVE");
9011 else
9012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9013 " Base filter method: NONE");
9014 }
glennrp2b013e42010-11-24 16:55:50 +00009015
cristy3ed852e2009-09-05 21:47:34 +00009016 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9017 }
9018
glennrp823b55c2011-03-14 18:46:46 +00009019 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9020 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009021 {
9022 ResetImageProfileIterator(image);
9023 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009024 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009025 profile=GetImageProfile(image,name);
9026
9027 if (profile != (StringInfo *) NULL)
9028 {
glennrp5af765f2010-03-30 11:12:18 +00009029#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009030 if ((LocaleCompare(name,"ICC") == 0) ||
9031 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009032 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009033
9034 if (ping_exclude_iCCP == MagickFalse)
9035 {
9036 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009037#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009038 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009039#else
9040 (png_const_bytep) GetStringInfoDatum(profile),
9041#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009042 (png_uint_32) GetStringInfoLength(profile));
9043 }
glennrp26f37912010-12-23 16:22:42 +00009044 }
glennrp0fe50b42010-11-16 03:52:51 +00009045
glennrpc8cbc5d2011-01-01 00:12:34 +00009046 else
cristy3ed852e2009-09-05 21:47:34 +00009047#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009048 if (ping_exclude_zCCP == MagickFalse)
9049 {
glennrpcf002022011-01-30 02:38:15 +00009050 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009051 (unsigned char *) name,(unsigned char *) name,
9052 GetStringInfoDatum(profile),
9053 (png_uint_32) GetStringInfoLength(profile));
9054 }
9055 }
glennrp0b206f52011-01-07 04:55:32 +00009056
glennrpc8cbc5d2011-01-01 00:12:34 +00009057 if (logging != MagickFalse)
9058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9059 " Setting up text chunk with %s profile",name);
9060
9061 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009062 }
cristy3ed852e2009-09-05 21:47:34 +00009063 }
9064
9065#if defined(PNG_WRITE_sRGB_SUPPORTED)
9066 if ((mng_info->have_write_global_srgb == 0) &&
9067 ((image->rendering_intent != UndefinedIntent) ||
9068 (image->colorspace == sRGBColorspace)))
9069 {
glennrp26f37912010-12-23 16:22:42 +00009070 if (ping_exclude_sRGB == MagickFalse)
9071 {
9072 /*
9073 Note image rendering intent.
9074 */
9075 if (logging != MagickFalse)
9076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9077 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009078
glennrp26f37912010-12-23 16:22:42 +00009079 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009080 Magick_RenderingIntent_to_PNG_RenderingIntent(
9081 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009082
glennrp26f37912010-12-23 16:22:42 +00009083 if (ping_exclude_gAMA == MagickFalse)
9084 png_set_gAMA(ping,ping_info,0.45455);
9085 }
cristy3ed852e2009-09-05 21:47:34 +00009086 }
glennrp26f37912010-12-23 16:22:42 +00009087
glennrp5af765f2010-03-30 11:12:18 +00009088 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009089#endif
9090 {
glennrp2cc891a2010-12-24 13:44:32 +00009091 if (ping_exclude_gAMA == MagickFalse &&
9092 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009093 (image->gamma < .45 || image->gamma > .46)))
9094 {
cristy3ed852e2009-09-05 21:47:34 +00009095 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9096 {
9097 /*
9098 Note image gamma.
9099 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9100 */
9101 if (logging != MagickFalse)
9102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9103 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009104
cristy3ed852e2009-09-05 21:47:34 +00009105 png_set_gAMA(ping,ping_info,image->gamma);
9106 }
glennrp26f37912010-12-23 16:22:42 +00009107 }
glennrp2b013e42010-11-24 16:55:50 +00009108
glennrp26f37912010-12-23 16:22:42 +00009109 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009110 {
glennrp26f37912010-12-23 16:22:42 +00009111 if ((mng_info->have_write_global_chrm == 0) &&
9112 (image->chromaticity.red_primary.x != 0.0))
9113 {
9114 /*
9115 Note image chromaticity.
9116 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9117 */
9118 PrimaryInfo
9119 bp,
9120 gp,
9121 rp,
9122 wp;
cristy3ed852e2009-09-05 21:47:34 +00009123
glennrp26f37912010-12-23 16:22:42 +00009124 wp=image->chromaticity.white_point;
9125 rp=image->chromaticity.red_primary;
9126 gp=image->chromaticity.green_primary;
9127 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009128
glennrp26f37912010-12-23 16:22:42 +00009129 if (logging != MagickFalse)
9130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9131 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009132
glennrp26f37912010-12-23 16:22:42 +00009133 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9134 bp.x,bp.y);
9135 }
9136 }
cristy3ed852e2009-09-05 21:47:34 +00009137 }
glennrpdfd70802010-11-14 01:23:35 +00009138
glennrp5af765f2010-03-30 11:12:18 +00009139 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009140
9141 if (mng_info->write_mng)
9142 png_set_sig_bytes(ping,8);
9143
9144 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9145
glennrpd6bf1612010-12-17 17:28:54 +00009146 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009147 {
9148 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009149 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009150 {
glennrp5af765f2010-03-30 11:12:18 +00009151 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009152
glennrp5af765f2010-03-30 11:12:18 +00009153 if (ping_bit_depth < 8)
9154 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009155 }
glennrp0fe50b42010-11-16 03:52:51 +00009156
cristy3ed852e2009-09-05 21:47:34 +00009157 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009158 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009159 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009160 }
9161
glennrp0e8ea192010-12-24 18:00:33 +00009162 if (ping_need_colortype_warning != MagickFalse ||
9163 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009164 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009165 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009166 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009167 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009168 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009169 {
9170 if (logging != MagickFalse)
9171 {
glennrp0e8ea192010-12-24 18:00:33 +00009172 if (ping_need_colortype_warning != MagickFalse)
9173 {
9174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9175 " Image has transparency but tRNS chunk was excluded");
9176 }
9177
cristy3ed852e2009-09-05 21:47:34 +00009178 if (mng_info->write_png_depth)
9179 {
9180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9181 " Defined PNG:bit-depth=%u, Computed depth=%u",
9182 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009183 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009184 }
glennrp0e8ea192010-12-24 18:00:33 +00009185
cristy3ed852e2009-09-05 21:47:34 +00009186 if (mng_info->write_png_colortype)
9187 {
9188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9189 " Defined PNG:color-type=%u, Computed color type=%u",
9190 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009191 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009192 }
9193 }
glennrp0e8ea192010-12-24 18:00:33 +00009194
glennrp3bd2e412010-08-10 13:34:52 +00009195 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009196 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9197 }
9198
glennrp58e01762011-01-07 15:28:54 +00009199 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009200 {
9201 /* Add an opaque matte channel */
9202 image->matte = MagickTrue;
9203 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009204
glennrpb4a13412010-05-05 12:47:19 +00009205 if (logging != MagickFalse)
9206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9207 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009208 }
9209
glennrp0e319732011-01-25 21:53:13 +00009210 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009211 {
glennrp991d11d2010-11-12 21:55:28 +00009212 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009213 {
glennrp991d11d2010-11-12 21:55:28 +00009214 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009215 if (logging != MagickFalse)
9216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9217 " Setting ping_have_tRNS=MagickTrue.");
9218 }
glennrpe9c26dc2010-05-30 01:56:35 +00009219 }
9220
cristy3ed852e2009-09-05 21:47:34 +00009221 if (logging != MagickFalse)
9222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9223 " Writing PNG header chunks");
9224
glennrp5af765f2010-03-30 11:12:18 +00009225 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9226 ping_bit_depth,ping_color_type,
9227 ping_interlace_method,ping_compression_method,
9228 ping_filter_method);
9229
glennrp39992b42010-11-14 00:03:43 +00009230 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9231 {
glennrpf09bded2011-01-08 01:15:59 +00009232 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009233
glennrp3b51f0e2010-11-27 18:14:08 +00009234 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009235 {
glennrp8640fb52010-11-23 15:48:26 +00009236 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009237 {
glennrpd6bf1612010-12-17 17:28:54 +00009238 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009240 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9241 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009242 (int) palette[i].red,
9243 (int) palette[i].green,
9244 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009245 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009246 (int) ping_trans_alpha[i]);
9247 else
9248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009249 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009250 (int) i,
9251 (int) palette[i].red,
9252 (int) palette[i].green,
9253 (int) palette[i].blue);
9254 }
glennrp39992b42010-11-14 00:03:43 +00009255 }
glennrp39992b42010-11-14 00:03:43 +00009256 }
9257
glennrp26f37912010-12-23 16:22:42 +00009258 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009259 {
glennrp26f37912010-12-23 16:22:42 +00009260 if (ping_have_bKGD != MagickFalse)
9261 png_set_bKGD(ping,ping_info,&ping_background);
9262 }
9263
9264 if (ping_exclude_pHYs == MagickFalse)
9265 {
9266 if (ping_have_pHYs != MagickFalse)
9267 {
9268 png_set_pHYs(ping,ping_info,
9269 ping_pHYs_x_resolution,
9270 ping_pHYs_y_resolution,
9271 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009272
9273 if (logging)
9274 {
9275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9276 " Setting up pHYs chunk");
9277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9278 " x_resolution=%lu",
9279 (unsigned long) ping_pHYs_x_resolution);
9280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9281 " y_resolution=%lu",
9282 (unsigned long) ping_pHYs_y_resolution);
9283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9284 " unit_type=%lu",
9285 (unsigned long) ping_pHYs_unit_type);
9286 }
glennrp26f37912010-12-23 16:22:42 +00009287 }
glennrpdfd70802010-11-14 01:23:35 +00009288 }
9289
9290#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009291 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009292 {
glennrp26f37912010-12-23 16:22:42 +00009293 if (image->page.x || image->page.y)
9294 {
9295 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9296 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009297
glennrp26f37912010-12-23 16:22:42 +00009298 if (logging != MagickFalse)
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9301 (int) image->page.x, (int) image->page.y);
9302 }
glennrpdfd70802010-11-14 01:23:35 +00009303 }
9304#endif
9305
glennrpda8f3a72011-02-27 23:54:12 +00009306 if (mng_info->need_blob != MagickFalse)
9307 {
9308 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9309 MagickFalse)
9310 png_error(ping,"WriteBlob Failed");
9311
9312 ping_have_blob=MagickTrue;
9313 }
9314
cristy3ed852e2009-09-05 21:47:34 +00009315 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009316
glennrp39992b42010-11-14 00:03:43 +00009317 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +00009318 {
glennrp3b51f0e2010-11-27 18:14:08 +00009319 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009320 {
9321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9322 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9323 }
9324
9325 if (ping_color_type == 3)
9326 (void) png_set_tRNS(ping, ping_info,
9327 ping_trans_alpha,
9328 ping_num_trans,
9329 NULL);
9330
9331 else
9332 {
9333 (void) png_set_tRNS(ping, ping_info,
9334 NULL,
9335 0,
9336 &ping_trans_color);
9337
glennrp3b51f0e2010-11-27 18:14:08 +00009338 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009339 {
9340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +00009341 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009342 (int) ping_trans_color.red,
9343 (int) ping_trans_color.green,
9344 (int) ping_trans_color.blue);
9345 }
9346 }
glennrp991d11d2010-11-12 21:55:28 +00009347 }
9348
cristy3ed852e2009-09-05 21:47:34 +00009349 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +00009350 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +00009351
cristy3ed852e2009-09-05 21:47:34 +00009352 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +00009353
cristy3ed852e2009-09-05 21:47:34 +00009354 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +00009355 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +00009356
glennrp26f37912010-12-23 16:22:42 +00009357 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009358 {
glennrp4f25bd02011-01-01 18:51:28 +00009359 if ((image->page.width != 0 && image->page.width != image->columns) ||
9360 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +00009361 {
9362 unsigned char
9363 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00009364
glennrp26f37912010-12-23 16:22:42 +00009365 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9366 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +00009367 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +00009368 PNGLong(chunk+4,(png_uint_32) image->page.width);
9369 PNGLong(chunk+8,(png_uint_32) image->page.height);
9370 chunk[12]=0; /* unit = pixels */
9371 (void) WriteBlob(image,13,chunk);
9372 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9373 }
cristy3ed852e2009-09-05 21:47:34 +00009374 }
9375
9376#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00009377 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00009378#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00009379 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00009380#undef PNG_HAVE_IDAT
9381#endif
9382
9383 png_set_packing(ping);
9384 /*
9385 Allocate memory.
9386 */
9387 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00009388 if (image_depth > 8)
9389 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00009390 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00009391 {
glennrpb4a13412010-05-05 12:47:19 +00009392 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00009393 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00009394 break;
glennrp0fe50b42010-11-16 03:52:51 +00009395
glennrpb4a13412010-05-05 12:47:19 +00009396 case PNG_COLOR_TYPE_GRAY_ALPHA:
9397 rowbytes*=2;
9398 break;
glennrp0fe50b42010-11-16 03:52:51 +00009399
glennrpb4a13412010-05-05 12:47:19 +00009400 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00009401 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00009402 break;
glennrp0fe50b42010-11-16 03:52:51 +00009403
glennrpb4a13412010-05-05 12:47:19 +00009404 default:
9405 break;
cristy3ed852e2009-09-05 21:47:34 +00009406 }
glennrp3b51f0e2010-11-27 18:14:08 +00009407
9408 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +00009409 {
9410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9411 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009412
glennrpb4a13412010-05-05 12:47:19 +00009413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009414 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00009415 }
glennrpcf002022011-01-30 02:38:15 +00009416 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9417 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00009418
glennrpcf002022011-01-30 02:38:15 +00009419 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009420 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009421
cristy3ed852e2009-09-05 21:47:34 +00009422 /*
9423 Initialize image scanlines.
9424 */
glennrp5af765f2010-03-30 11:12:18 +00009425 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009426 {
9427 /*
9428 PNG write failed.
9429 */
9430#ifdef PNG_DEBUG
9431 if (image_info->verbose)
9432 (void) printf("PNG write has failed.\n");
9433#endif
9434 png_destroy_write_struct(&ping,&ping_info);
9435 if (quantum_info != (QuantumInfo *) NULL)
9436 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +00009437 if (ping_pixels != (unsigned char *) NULL)
9438 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009439#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009440 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009441#endif
glennrpda8f3a72011-02-27 23:54:12 +00009442 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009443 (void) CloseBlob(image);
9444 image_info=DestroyImageInfo(image_info);
9445 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009446 return(MagickFalse);
9447 }
cristyed552522009-10-16 14:04:35 +00009448 quantum_info=AcquireQuantumInfo(image_info,image);
9449 if (quantum_info == (QuantumInfo *) NULL)
9450 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00009451 quantum_info->format=UndefinedQuantumFormat;
9452 quantum_info->depth=image_depth;
9453 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +00009454
cristy3ed852e2009-09-05 21:47:34 +00009455 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +00009456 !mng_info->write_png32) &&
9457 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +00009458 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +00009459 image_matte == MagickFalse &&
9460 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009461 {
glennrp8bb3a022010-12-13 20:40:04 +00009462 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009463 register const PixelPacket
9464 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009465
cristy3ed852e2009-09-05 21:47:34 +00009466 quantum_info->depth=8;
9467 for (pass=0; pass < num_passes; pass++)
9468 {
9469 /*
9470 Convert PseudoClass image to a PNG monochrome image.
9471 */
cristybb503372010-05-27 20:51:26 +00009472 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009473 {
glennrpd71e86a2011-02-24 01:28:37 +00009474 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +00009475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9476 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +00009477
cristy3ed852e2009-09-05 21:47:34 +00009478 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00009479
cristy3ed852e2009-09-05 21:47:34 +00009480 if (p == (const PixelPacket *) NULL)
9481 break;
glennrp0fe50b42010-11-16 03:52:51 +00009482
cristy3ed852e2009-09-05 21:47:34 +00009483 if (mng_info->IsPalette)
9484 {
9485 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009486 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009487 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9488 mng_info->write_png_depth &&
9489 mng_info->write_png_depth != old_bit_depth)
9490 {
9491 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00009492 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009493 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +00009494 >> (8-old_bit_depth));
9495 }
9496 }
glennrp0fe50b42010-11-16 03:52:51 +00009497
cristy3ed852e2009-09-05 21:47:34 +00009498 else
9499 {
9500 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009501 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00009502 }
glennrp0fe50b42010-11-16 03:52:51 +00009503
cristy3ed852e2009-09-05 21:47:34 +00009504 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00009505 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +00009506 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +00009507 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +00009508
glennrp3b51f0e2010-11-27 18:14:08 +00009509 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9511 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +00009512
glennrpcf002022011-01-30 02:38:15 +00009513 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009514 }
9515 if (image->previous == (Image *) NULL)
9516 {
9517 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9518 if (status == MagickFalse)
9519 break;
9520 }
9521 }
9522 }
glennrp0fe50b42010-11-16 03:52:51 +00009523
glennrp8bb3a022010-12-13 20:40:04 +00009524 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +00009525 {
glennrp0fe50b42010-11-16 03:52:51 +00009526 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +00009527 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +00009528 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +00009529 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +00009530 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009531 {
glennrp8bb3a022010-12-13 20:40:04 +00009532 register const PixelPacket
9533 *p;
glennrp0fe50b42010-11-16 03:52:51 +00009534
glennrp8bb3a022010-12-13 20:40:04 +00009535 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009536 {
glennrp8bb3a022010-12-13 20:40:04 +00009537
cristybb503372010-05-27 20:51:26 +00009538 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00009539 {
9540 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009541
cristy3ed852e2009-09-05 21:47:34 +00009542 if (p == (const PixelPacket *) NULL)
9543 break;
glennrp2cc891a2010-12-24 13:44:32 +00009544
glennrp5af765f2010-03-30 11:12:18 +00009545 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009546 {
glennrp8bb3a022010-12-13 20:40:04 +00009547 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009548 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009549 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009550
glennrp8bb3a022010-12-13 20:40:04 +00009551 else
9552 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009553 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009554
glennrp3b51f0e2010-11-27 18:14:08 +00009555 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009557 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +00009558 }
glennrp2cc891a2010-12-24 13:44:32 +00009559
glennrp8bb3a022010-12-13 20:40:04 +00009560 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9561 {
9562 if (logging != MagickFalse && y == 0)
9563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9564 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009565
glennrp8bb3a022010-12-13 20:40:04 +00009566 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009567 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009568 }
glennrp2cc891a2010-12-24 13:44:32 +00009569
glennrp3b51f0e2010-11-27 18:14:08 +00009570 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00009571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +00009572 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +00009573
glennrpcf002022011-01-30 02:38:15 +00009574 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009575 }
glennrp2cc891a2010-12-24 13:44:32 +00009576
glennrp8bb3a022010-12-13 20:40:04 +00009577 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00009578 {
glennrp8bb3a022010-12-13 20:40:04 +00009579 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9580 if (status == MagickFalse)
9581 break;
cristy3ed852e2009-09-05 21:47:34 +00009582 }
cristy3ed852e2009-09-05 21:47:34 +00009583 }
9584 }
glennrp8bb3a022010-12-13 20:40:04 +00009585
9586 else
9587 {
9588 register const PixelPacket
9589 *p;
9590
9591 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00009592 {
glennrp8bb3a022010-12-13 20:40:04 +00009593 if ((image_depth > 8) || (mng_info->write_png24 ||
9594 mng_info->write_png32 ||
9595 (!mng_info->write_png8 && !mng_info->IsPalette)))
9596 {
9597 for (y=0; y < (ssize_t) image->rows; y++)
9598 {
9599 p=GetVirtualPixels(image,0,y,image->columns,1,
9600 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009601
glennrp8bb3a022010-12-13 20:40:04 +00009602 if (p == (const PixelPacket *) NULL)
9603 break;
glennrp2cc891a2010-12-24 13:44:32 +00009604
glennrp8bb3a022010-12-13 20:40:04 +00009605 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9606 {
9607 if (image->storage_class == DirectClass)
9608 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009609 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009610
glennrp8bb3a022010-12-13 20:40:04 +00009611 else
9612 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009613 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009614 }
glennrp2cc891a2010-12-24 13:44:32 +00009615
glennrp8bb3a022010-12-13 20:40:04 +00009616 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9617 {
9618 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009619 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +00009620 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009621
glennrp8bb3a022010-12-13 20:40:04 +00009622 if (logging != MagickFalse && y == 0)
9623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9624 " Writing GRAY_ALPHA PNG pixels (3)");
9625 }
glennrp2cc891a2010-12-24 13:44:32 +00009626
glennrp8bb3a022010-12-13 20:40:04 +00009627 else if (image_matte != MagickFalse)
9628 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009629 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009630
glennrp8bb3a022010-12-13 20:40:04 +00009631 else
9632 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009633 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009634
glennrp8bb3a022010-12-13 20:40:04 +00009635 if (logging != MagickFalse && y == 0)
9636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9637 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +00009638
glennrpcf002022011-01-30 02:38:15 +00009639 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009640 }
9641 }
glennrp2cc891a2010-12-24 13:44:32 +00009642
glennrp8bb3a022010-12-13 20:40:04 +00009643 else
9644 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9645 mng_info->write_png32 ||
9646 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9647 {
9648 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9649 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9650 {
9651 if (logging != MagickFalse)
9652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9653 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009654
glennrp8bb3a022010-12-13 20:40:04 +00009655 quantum_info->depth=8;
9656 image_depth=8;
9657 }
glennrp2cc891a2010-12-24 13:44:32 +00009658
glennrp8bb3a022010-12-13 20:40:04 +00009659 for (y=0; y < (ssize_t) image->rows; y++)
9660 {
9661 if (logging != MagickFalse && y == 0)
9662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9663 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +00009664
glennrp770d1932011-03-06 22:11:17 +00009665 p=GetVirtualPixels(image,0,y,image->columns,1,
9666 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +00009667
glennrp8bb3a022010-12-13 20:40:04 +00009668 if (p == (const PixelPacket *) NULL)
9669 break;
glennrp2cc891a2010-12-24 13:44:32 +00009670
glennrp8bb3a022010-12-13 20:40:04 +00009671 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +00009672 {
glennrp4bf89732011-03-21 13:48:28 +00009673 quantum_info->depth=image->depth;
9674
glennrp44757ab2011-03-17 12:57:03 +00009675 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009676 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +00009677 }
glennrp2cc891a2010-12-24 13:44:32 +00009678
glennrp8bb3a022010-12-13 20:40:04 +00009679 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9680 {
9681 if (logging != MagickFalse && y == 0)
9682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9683 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +00009684
glennrp8bb3a022010-12-13 20:40:04 +00009685 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +00009686 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +00009687 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +00009688 }
glennrp2cc891a2010-12-24 13:44:32 +00009689
glennrp8bb3a022010-12-13 20:40:04 +00009690 else
glennrp8bb3a022010-12-13 20:40:04 +00009691 {
glennrp179d0752011-03-17 13:02:10 +00009692 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +00009693 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9694
9695 if (logging != MagickFalse && y <= 2)
9696 {
9697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +00009698 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +00009699
9700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9701 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9702 (int)ping_pixels[0],(int)ping_pixels[1]);
9703 }
glennrp8bb3a022010-12-13 20:40:04 +00009704 }
glennrpcf002022011-01-30 02:38:15 +00009705 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +00009706 }
9707 }
glennrp2cc891a2010-12-24 13:44:32 +00009708
glennrp8bb3a022010-12-13 20:40:04 +00009709 if (image->previous == (Image *) NULL)
9710 {
9711 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9712 if (status == MagickFalse)
9713 break;
9714 }
cristy3ed852e2009-09-05 21:47:34 +00009715 }
glennrp8bb3a022010-12-13 20:40:04 +00009716 }
9717 }
9718
cristyb32b90a2009-09-07 21:45:48 +00009719 if (quantum_info != (QuantumInfo *) NULL)
9720 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00009721
9722 if (logging != MagickFalse)
9723 {
9724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00009725 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +00009726
cristy3ed852e2009-09-05 21:47:34 +00009727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009728 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +00009729
cristy3ed852e2009-09-05 21:47:34 +00009730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009731 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00009732
cristy3ed852e2009-09-05 21:47:34 +00009733 if (mng_info->write_png_depth)
9734 {
9735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9736 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9737 }
glennrp0fe50b42010-11-16 03:52:51 +00009738
cristy3ed852e2009-09-05 21:47:34 +00009739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009740 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009741
cristy3ed852e2009-09-05 21:47:34 +00009742 if (mng_info->write_png_colortype)
9743 {
9744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9745 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9746 }
glennrp0fe50b42010-11-16 03:52:51 +00009747
cristy3ed852e2009-09-05 21:47:34 +00009748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009749 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009750
cristy3ed852e2009-09-05 21:47:34 +00009751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009752 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00009753 }
9754 /*
glennrpa0ed0092011-04-18 16:36:29 +00009755 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +00009756 */
glennrp823b55c2011-03-14 18:46:46 +00009757 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009758 {
glennrp26f37912010-12-23 16:22:42 +00009759 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +00009760 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +00009761 while (property != (const char *) NULL)
9762 {
9763 png_textp
9764 text;
glennrp2cc891a2010-12-24 13:44:32 +00009765
glennrp26f37912010-12-23 16:22:42 +00009766 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +00009767
9768 /* Don't write any "png:" properties; those are just for "identify" */
9769 if (LocaleNCompare(property,"png:",4) != 0 &&
9770
9771 /* Suppress density and units if we wrote a pHYs chunk */
9772 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +00009773 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +00009774 LocaleCompare(property,"units") != 0) &&
9775
9776 /* Suppress the IM-generated Date:create and Date:modify */
9777 (ping_exclude_date == MagickFalse ||
9778 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +00009779 {
glennrpc70af4a2011-03-07 00:08:23 +00009780 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +00009781 {
glennrpc70af4a2011-03-07 00:08:23 +00009782 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9783 text[0].key=(char *) property;
9784 text[0].text=(char *) value;
9785 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +00009786
glennrpc70af4a2011-03-07 00:08:23 +00009787 if (ping_exclude_tEXt != MagickFalse)
9788 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9789
9790 else if (ping_exclude_zTXt != MagickFalse)
9791 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9792
9793 else
glennrp26f37912010-12-23 16:22:42 +00009794 {
glennrpc70af4a2011-03-07 00:08:23 +00009795 text[0].compression=image_info->compression == NoCompression ||
9796 (image_info->compression == UndefinedCompression &&
9797 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9798 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +00009799 }
glennrp2cc891a2010-12-24 13:44:32 +00009800
glennrpc70af4a2011-03-07 00:08:23 +00009801 if (logging != MagickFalse)
9802 {
9803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9804 " Setting up text chunk");
9805
9806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9807 " keyword: %s",text[0].key);
9808 }
9809
9810 png_set_text(ping,ping_info,text,1);
9811 png_free(ping,text);
9812 }
glennrp26f37912010-12-23 16:22:42 +00009813 }
9814 property=GetNextImageProperty(image);
9815 }
cristy3ed852e2009-09-05 21:47:34 +00009816 }
9817
9818 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +00009819 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +00009820
9821 if (logging != MagickFalse)
9822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9823 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +00009824
cristy3ed852e2009-09-05 21:47:34 +00009825 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00009826
cristy3ed852e2009-09-05 21:47:34 +00009827 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9828 {
9829 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00009830 (ping_width != mng_info->page.width) ||
9831 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00009832 {
9833 unsigned char
9834 chunk[32];
9835
9836 /*
9837 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9838 */
9839 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9840 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +00009841 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +00009842 chunk[4]=4;
9843 chunk[5]=0; /* frame name separator (no name) */
9844 chunk[6]=1; /* flag for changing delay, for next frame only */
9845 chunk[7]=0; /* flag for changing frame timeout */
9846 chunk[8]=1; /* flag for changing frame clipping for next frame */
9847 chunk[9]=0; /* flag for changing frame sync_id */
9848 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9849 chunk[14]=0; /* clipping boundaries delta type */
9850 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9851 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00009852 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00009853 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9854 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00009855 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00009856 (void) WriteBlob(image,31,chunk);
9857 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9858 mng_info->old_framing_mode=4;
9859 mng_info->framing_mode=1;
9860 }
glennrp0fe50b42010-11-16 03:52:51 +00009861
cristy3ed852e2009-09-05 21:47:34 +00009862 else
9863 mng_info->framing_mode=3;
9864 }
9865 if (mng_info->write_mng && !mng_info->need_fram &&
9866 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +00009867 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00009868 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +00009869 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00009870
cristy3ed852e2009-09-05 21:47:34 +00009871 /*
9872 Free PNG resources.
9873 */
glennrp5af765f2010-03-30 11:12:18 +00009874
cristy3ed852e2009-09-05 21:47:34 +00009875 png_destroy_write_struct(&ping,&ping_info);
9876
glennrpcf002022011-01-30 02:38:15 +00009877 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00009878
9879#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00009880 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009881#endif
9882
glennrpda8f3a72011-02-27 23:54:12 +00009883 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00009884 (void) CloseBlob(image);
9885
9886 image_info=DestroyImageInfo(image_info);
9887 image=DestroyImage(image);
9888
9889 /* Store bit depth actually written */
9890 s[0]=(char) ping_bit_depth;
9891 s[1]='\0';
9892
9893 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9894
cristy3ed852e2009-09-05 21:47:34 +00009895 if (logging != MagickFalse)
9896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9897 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00009898
cristy3ed852e2009-09-05 21:47:34 +00009899 return(MagickTrue);
9900/* End write one PNG image */
9901}
9902
9903/*
9904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9905% %
9906% %
9907% %
9908% W r i t e P N G I m a g e %
9909% %
9910% %
9911% %
9912%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9913%
9914% WritePNGImage() writes a Portable Network Graphics (PNG) or
9915% Multiple-image Network Graphics (MNG) image file.
9916%
9917% MNG support written by Glenn Randers-Pehrson, glennrp@image...
9918%
9919% The format of the WritePNGImage method is:
9920%
9921% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9922%
9923% A description of each parameter follows:
9924%
9925% o image_info: the image info.
9926%
9927% o image: The image.
9928%
9929% Returns MagickTrue on success, MagickFalse on failure.
9930%
9931% Communicating with the PNG encoder:
9932%
9933% While the datastream written is always in PNG format and normally would
9934% be given the "png" file extension, this method also writes the following
9935% pseudo-formats which are subsets of PNG:
9936%
glennrp5a39f372011-02-25 04:52:16 +00009937% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
9938% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +00009939% is present, the tRNS chunk must only have values 0 and 255
9940% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +00009941% transparent). If other values are present they will be
9942% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +00009943% colors are present, they will be quantized to the 4-4-4-1,
9944% 3-3-3-1, or 3-3-2-1 palette.
9945%
9946% If you want better quantization or dithering of the colors
9947% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +00009948% PNG encoder. The pixels contain 8-bit indices even if
9949% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +00009950% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +00009951% PNG grayscale type might be slightly more efficient. Please
9952% note that writing to the PNG8 format may result in loss
9953% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +00009954%
9955% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9956% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +00009957% one of the colors as transparent. The only loss incurred
9958% is reduction of sample depth to 8. If the image has more
9959% than one transparent color, has semitransparent pixels, or
9960% has an opaque pixel with the same RGB components as the
9961% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +00009962%
9963% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
9964% transparency is permitted, i.e., the alpha sample for
9965% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +00009966% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +00009967% The only loss in data is the reduction of the sample depth
9968% to 8.
cristy3ed852e2009-09-05 21:47:34 +00009969%
9970% o -define: For more precise control of the PNG output, you can use the
9971% Image options "png:bit-depth" and "png:color-type". These
9972% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +00009973% from the application programming interfaces. The options
9974% are case-independent and are converted to lowercase before
9975% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +00009976%
9977% png:color-type can be 0, 2, 3, 4, or 6.
9978%
9979% When png:color-type is 0 (Grayscale), png:bit-depth can
9980% be 1, 2, 4, 8, or 16.
9981%
9982% When png:color-type is 2 (RGB), png:bit-depth can
9983% be 8 or 16.
9984%
9985% When png:color-type is 3 (Indexed), png:bit-depth can
9986% be 1, 2, 4, or 8. This refers to the number of bits
9987% used to store the index. The color samples always have
9988% bit-depth 8 in indexed PNG files.
9989%
9990% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9991% png:bit-depth can be 8 or 16.
9992%
glennrp5a39f372011-02-25 04:52:16 +00009993% If the image cannot be written without loss with the requested bit-depth
9994% and color-type, a PNG file will not be written, and the encoder will
9995% return MagickFalse.
9996%
cristy3ed852e2009-09-05 21:47:34 +00009997% Since image encoders should not be responsible for the "heavy lifting",
9998% the user should make sure that ImageMagick has already reduced the
9999% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010000% transparency prior to attempting to write the image with depth, color,
10001% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010002%
glennrp97cefe22011-04-22 16:17:00 +000010003% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010004%
cristy3ed852e2009-09-05 21:47:34 +000010005% Note that another definition, "png:bit-depth-written" exists, but it
10006% is not intended for external use. It is only used internally by the
10007% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10008%
10009% It is possible to request that the PNG encoder write previously-formatted
10010% ancillary chunks in the output PNG file, using the "-profile" commandline
10011% option as shown below or by setting the profile via a programming
10012% interface:
10013%
10014% -profile PNG-chunk-x:<file>
10015%
10016% where x is a location flag and <file> is a file containing the chunk
10017% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010018% This encoder will compute the chunk length and CRC, so those must not
10019% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010020%
10021% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10022% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10023% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010024% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010025%
glennrpbb8a7332010-11-13 15:17:35 +000010026% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010027%
glennrp3241bd02010-12-12 04:36:28 +000010028% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010029%
glennrpd6afd542010-11-19 01:53:05 +000010030% o 32-bit depth is reduced to 16.
10031% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10032% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010033% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010034% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010035% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010036% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10037% this can be done without loss and a larger bit depth N was not
10038% requested via the "-define PNG:bit-depth=N" option.
10039% o If matte channel is present but only one transparent color is
10040% present, RGB+tRNS is written instead of RGBA
10041% o Opaque matte channel is removed (or added, if color-type 4 or 6
10042% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010043%
cristy3ed852e2009-09-05 21:47:34 +000010044%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10045*/
10046static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10047 Image *image)
10048{
10049 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010050 excluding,
10051 logging,
10052 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010053 status;
10054
10055 MngInfo
10056 *mng_info;
10057
10058 const char
10059 *value;
10060
10061 int
glennrp21f0e622011-01-07 16:20:57 +000010062 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010063 source;
10064
cristy3ed852e2009-09-05 21:47:34 +000010065 /*
10066 Open image file.
10067 */
10068 assert(image_info != (const ImageInfo *) NULL);
10069 assert(image_info->signature == MagickSignature);
10070 assert(image != (Image *) NULL);
10071 assert(image->signature == MagickSignature);
10072 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010073 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010074 /*
10075 Allocate a MngInfo structure.
10076 */
10077 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010078 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010079
cristy3ed852e2009-09-05 21:47:34 +000010080 if (mng_info == (MngInfo *) NULL)
10081 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010082
cristy3ed852e2009-09-05 21:47:34 +000010083 /*
10084 Initialize members of the MngInfo structure.
10085 */
10086 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10087 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010088 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010089 have_mng_structure=MagickTrue;
10090
10091 /* See if user has requested a specific PNG subformat */
10092
10093 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10094 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10095 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10096
10097 if (mng_info->write_png8)
10098 {
glennrp9c1eb072010-06-06 22:19:15 +000010099 mng_info->write_png_colortype = /* 3 */ 4;
10100 mng_info->write_png_depth = 8;
10101 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010102 }
10103
10104 if (mng_info->write_png24)
10105 {
glennrp9c1eb072010-06-06 22:19:15 +000010106 mng_info->write_png_colortype = /* 2 */ 3;
10107 mng_info->write_png_depth = 8;
10108 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010109
glennrp9c1eb072010-06-06 22:19:15 +000010110 if (image->matte == MagickTrue)
10111 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010112
glennrp9c1eb072010-06-06 22:19:15 +000010113 else
10114 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010115
glennrp9c1eb072010-06-06 22:19:15 +000010116 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010117 }
10118
10119 if (mng_info->write_png32)
10120 {
glennrp9c1eb072010-06-06 22:19:15 +000010121 mng_info->write_png_colortype = /* 6 */ 7;
10122 mng_info->write_png_depth = 8;
10123 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010124
glennrp9c1eb072010-06-06 22:19:15 +000010125 if (image->matte == MagickTrue)
10126 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010127
glennrp9c1eb072010-06-06 22:19:15 +000010128 else
10129 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010130
glennrp9c1eb072010-06-06 22:19:15 +000010131 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010132 }
10133
10134 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010135
cristy3ed852e2009-09-05 21:47:34 +000010136 if (value != (char *) NULL)
10137 {
10138 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010139 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010140
cristy3ed852e2009-09-05 21:47:34 +000010141 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010142 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010143
cristy3ed852e2009-09-05 21:47:34 +000010144 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010145 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010146
cristy3ed852e2009-09-05 21:47:34 +000010147 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010148 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010149
cristy3ed852e2009-09-05 21:47:34 +000010150 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010151 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010152
glennrpbb8a7332010-11-13 15:17:35 +000010153 else
10154 (void) ThrowMagickException(&image->exception,
10155 GetMagickModule(),CoderWarning,
10156 "ignoring invalid defined png:bit-depth",
10157 "=%s",value);
10158
cristy3ed852e2009-09-05 21:47:34 +000010159 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010161 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010162 }
glennrp0fe50b42010-11-16 03:52:51 +000010163
cristy3ed852e2009-09-05 21:47:34 +000010164 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010165
cristy3ed852e2009-09-05 21:47:34 +000010166 if (value != (char *) NULL)
10167 {
10168 /* We must store colortype+1 because 0 is a valid colortype */
10169 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010170 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010171
cristy3ed852e2009-09-05 21:47:34 +000010172 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010173 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010174
cristy3ed852e2009-09-05 21:47:34 +000010175 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010176 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010177
cristy3ed852e2009-09-05 21:47:34 +000010178 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010179 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010180
cristy3ed852e2009-09-05 21:47:34 +000010181 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010182 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010183
glennrpbb8a7332010-11-13 15:17:35 +000010184 else
10185 (void) ThrowMagickException(&image->exception,
10186 GetMagickModule(),CoderWarning,
10187 "ignoring invalid defined png:color-type",
10188 "=%s",value);
10189
cristy3ed852e2009-09-05 21:47:34 +000010190 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010192 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010193 }
10194
glennrp0e8ea192010-12-24 18:00:33 +000010195 /* Check for chunks to be excluded:
10196 *
glennrp0dff56c2011-01-29 19:10:02 +000010197 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010198 * listed in the "unused_chunks" array, above.
10199 *
10200 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10201 * define (in the image properties or in the image artifacts)
10202 * or via a mng_info member. For convenience, in addition
10203 * to or instead of a comma-separated list of chunks, the
10204 * "exclude-chunk" string can be simply "all" or "none".
10205 *
10206 * The exclude-chunk define takes priority over the mng_info.
10207 *
10208 * A "PNG:include-chunk" define takes priority over both the
10209 * mng_info and the "PNG:exclude-chunk" define. Like the
10210 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010211 * well as a comma-separated list. Chunks that are unknown to
10212 * ImageMagick are always excluded, regardless of their "copy-safe"
10213 * status according to the PNG specification, and even if they
10214 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010215 *
10216 * Finally, all chunks listed in the "unused_chunks" array are
10217 * automatically excluded, regardless of the other instructions
10218 * or lack thereof.
10219 *
10220 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10221 * will not be written and the gAMA chunk will only be written if it
10222 * is not between .45 and .46, or approximately (1.0/2.2).
10223 *
10224 * If you exclude tRNS and the image has transparency, the colortype
10225 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10226 *
10227 * The -strip option causes StripImage() to set the png:include-chunk
10228 * artifact to "none,gama".
10229 */
10230
glennrp26f37912010-12-23 16:22:42 +000010231 mng_info->ping_exclude_bKGD=MagickFalse;
10232 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010233 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010234 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10235 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010236 mng_info->ping_exclude_iCCP=MagickFalse;
10237 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10238 mng_info->ping_exclude_oFFs=MagickFalse;
10239 mng_info->ping_exclude_pHYs=MagickFalse;
10240 mng_info->ping_exclude_sRGB=MagickFalse;
10241 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010242 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010243 mng_info->ping_exclude_vpAg=MagickFalse;
10244 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10245 mng_info->ping_exclude_zTXt=MagickFalse;
10246
glennrp8d3d6e52011-04-19 04:39:51 +000010247 mng_info->ping_preserve_colormap=MagickFalse;
10248
10249 value=GetImageArtifact(image,"png:preserve-colormap");
10250 if (value == NULL)
10251 value=GetImageOption(image_info,"png:preserve-colormap");
10252 if (value != NULL)
10253 mng_info->ping_preserve_colormap=MagickTrue;
10254
glennrp03812ae2010-12-24 01:31:34 +000010255 excluding=MagickFalse;
10256
glennrp5c7cf4e2010-12-24 00:30:00 +000010257 for (source=0; source<1; source++)
10258 {
10259 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010260 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010261 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010262
10263 if (value == NULL)
10264 value=GetImageArtifact(image,"png:exclude-chunks");
10265 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010266 else
glennrpacba0042010-12-24 14:27:26 +000010267 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010268 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010269
glennrpacba0042010-12-24 14:27:26 +000010270 if (value == NULL)
10271 value=GetImageOption(image_info,"png:exclude-chunks");
10272 }
10273
glennrp03812ae2010-12-24 01:31:34 +000010274 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010275 {
glennrp03812ae2010-12-24 01:31:34 +000010276
10277 size_t
10278 last;
10279
10280 excluding=MagickTrue;
10281
10282 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010283 {
10284 if (source == 0)
10285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10286 " png:exclude-chunk=%s found in image artifacts.\n", value);
10287 else
10288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10289 " png:exclude-chunk=%s found in image properties.\n", value);
10290 }
glennrp03812ae2010-12-24 01:31:34 +000010291
10292 last=strlen(value);
10293
10294 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010295 {
glennrp03812ae2010-12-24 01:31:34 +000010296
10297 if (LocaleNCompare(value+i,"all",3) == 0)
10298 {
10299 mng_info->ping_exclude_bKGD=MagickTrue;
10300 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010301 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010302 mng_info->ping_exclude_EXIF=MagickTrue;
10303 mng_info->ping_exclude_gAMA=MagickTrue;
10304 mng_info->ping_exclude_iCCP=MagickTrue;
10305 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10306 mng_info->ping_exclude_oFFs=MagickTrue;
10307 mng_info->ping_exclude_pHYs=MagickTrue;
10308 mng_info->ping_exclude_sRGB=MagickTrue;
10309 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010310 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010311 mng_info->ping_exclude_vpAg=MagickTrue;
10312 mng_info->ping_exclude_zCCP=MagickTrue;
10313 mng_info->ping_exclude_zTXt=MagickTrue;
10314 i--;
10315 }
glennrp2cc891a2010-12-24 13:44:32 +000010316
glennrp03812ae2010-12-24 01:31:34 +000010317 if (LocaleNCompare(value+i,"none",4) == 0)
10318 {
10319 mng_info->ping_exclude_bKGD=MagickFalse;
10320 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010321 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010322 mng_info->ping_exclude_EXIF=MagickFalse;
10323 mng_info->ping_exclude_gAMA=MagickFalse;
10324 mng_info->ping_exclude_iCCP=MagickFalse;
10325 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10326 mng_info->ping_exclude_oFFs=MagickFalse;
10327 mng_info->ping_exclude_pHYs=MagickFalse;
10328 mng_info->ping_exclude_sRGB=MagickFalse;
10329 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010330 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010331 mng_info->ping_exclude_vpAg=MagickFalse;
10332 mng_info->ping_exclude_zCCP=MagickFalse;
10333 mng_info->ping_exclude_zTXt=MagickFalse;
10334 }
glennrp2cc891a2010-12-24 13:44:32 +000010335
glennrp03812ae2010-12-24 01:31:34 +000010336 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10337 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010338
glennrp03812ae2010-12-24 01:31:34 +000010339 if (LocaleNCompare(value+i,"chrm",4) == 0)
10340 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010341
glennrpa0ed0092011-04-18 16:36:29 +000010342 if (LocaleNCompare(value+i,"date",4) == 0)
10343 mng_info->ping_exclude_date=MagickTrue;
10344
glennrp03812ae2010-12-24 01:31:34 +000010345 if (LocaleNCompare(value+i,"exif",4) == 0)
10346 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010347
glennrp03812ae2010-12-24 01:31:34 +000010348 if (LocaleNCompare(value+i,"gama",4) == 0)
10349 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010350
glennrp03812ae2010-12-24 01:31:34 +000010351 if (LocaleNCompare(value+i,"iccp",4) == 0)
10352 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010353
glennrp03812ae2010-12-24 01:31:34 +000010354 /*
10355 if (LocaleNCompare(value+i,"itxt",4) == 0)
10356 mng_info->ping_exclude_iTXt=MagickTrue;
10357 */
glennrp2cc891a2010-12-24 13:44:32 +000010358
glennrp03812ae2010-12-24 01:31:34 +000010359 if (LocaleNCompare(value+i,"gama",4) == 0)
10360 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010361
glennrp03812ae2010-12-24 01:31:34 +000010362 if (LocaleNCompare(value+i,"offs",4) == 0)
10363 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010364
glennrp03812ae2010-12-24 01:31:34 +000010365 if (LocaleNCompare(value+i,"phys",4) == 0)
10366 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010367
glennrpa1e3b7b2010-12-24 16:37:33 +000010368 if (LocaleNCompare(value+i,"srgb",4) == 0)
10369 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010370
glennrp03812ae2010-12-24 01:31:34 +000010371 if (LocaleNCompare(value+i,"text",4) == 0)
10372 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010373
glennrpa1e3b7b2010-12-24 16:37:33 +000010374 if (LocaleNCompare(value+i,"trns",4) == 0)
10375 mng_info->ping_exclude_tRNS=MagickTrue;
10376
glennrp03812ae2010-12-24 01:31:34 +000010377 if (LocaleNCompare(value+i,"vpag",4) == 0)
10378 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010379
glennrp03812ae2010-12-24 01:31:34 +000010380 if (LocaleNCompare(value+i,"zccp",4) == 0)
10381 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010382
glennrp03812ae2010-12-24 01:31:34 +000010383 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10384 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000010385
glennrp03812ae2010-12-24 01:31:34 +000010386 }
glennrpce91ed52010-12-23 22:37:49 +000010387 }
glennrp26f37912010-12-23 16:22:42 +000010388 }
10389
glennrp5c7cf4e2010-12-24 00:30:00 +000010390 for (source=0; source<1; source++)
10391 {
10392 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010393 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010394 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000010395
10396 if (value == NULL)
10397 value=GetImageArtifact(image,"png:include-chunks");
10398 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010399 else
glennrpacba0042010-12-24 14:27:26 +000010400 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010401 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000010402
glennrpacba0042010-12-24 14:27:26 +000010403 if (value == NULL)
10404 value=GetImageOption(image_info,"png:include-chunks");
10405 }
10406
glennrp03812ae2010-12-24 01:31:34 +000010407 if (value != NULL)
10408 {
10409 size_t
10410 last;
glennrp26f37912010-12-23 16:22:42 +000010411
glennrp03812ae2010-12-24 01:31:34 +000010412 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010413
glennrp03812ae2010-12-24 01:31:34 +000010414 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010415 {
10416 if (source == 0)
10417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10418 " png:include-chunk=%s found in image artifacts.\n", value);
10419 else
10420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10421 " png:include-chunk=%s found in image properties.\n", value);
10422 }
glennrp03812ae2010-12-24 01:31:34 +000010423
10424 last=strlen(value);
10425
10426 for (i=0; i<(int) last; i+=5)
10427 {
10428 if (LocaleNCompare(value+i,"all",3) == 0)
10429 {
10430 mng_info->ping_exclude_bKGD=MagickFalse;
10431 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010432 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010433 mng_info->ping_exclude_EXIF=MagickFalse;
10434 mng_info->ping_exclude_gAMA=MagickFalse;
10435 mng_info->ping_exclude_iCCP=MagickFalse;
10436 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10437 mng_info->ping_exclude_oFFs=MagickFalse;
10438 mng_info->ping_exclude_pHYs=MagickFalse;
10439 mng_info->ping_exclude_sRGB=MagickFalse;
10440 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010441 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000010442 mng_info->ping_exclude_vpAg=MagickFalse;
10443 mng_info->ping_exclude_zCCP=MagickFalse;
10444 mng_info->ping_exclude_zTXt=MagickFalse;
10445 i--;
10446 }
glennrp2cc891a2010-12-24 13:44:32 +000010447
glennrp03812ae2010-12-24 01:31:34 +000010448 if (LocaleNCompare(value+i,"none",4) == 0)
10449 {
10450 mng_info->ping_exclude_bKGD=MagickTrue;
10451 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010452 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010453 mng_info->ping_exclude_EXIF=MagickTrue;
10454 mng_info->ping_exclude_gAMA=MagickTrue;
10455 mng_info->ping_exclude_iCCP=MagickTrue;
10456 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10457 mng_info->ping_exclude_oFFs=MagickTrue;
10458 mng_info->ping_exclude_pHYs=MagickTrue;
10459 mng_info->ping_exclude_sRGB=MagickTrue;
10460 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010461 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010462 mng_info->ping_exclude_vpAg=MagickTrue;
10463 mng_info->ping_exclude_zCCP=MagickTrue;
10464 mng_info->ping_exclude_zTXt=MagickTrue;
10465 }
glennrp2cc891a2010-12-24 13:44:32 +000010466
glennrp03812ae2010-12-24 01:31:34 +000010467 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10468 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010469
glennrp03812ae2010-12-24 01:31:34 +000010470 if (LocaleNCompare(value+i,"chrm",4) == 0)
10471 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010472
glennrpa0ed0092011-04-18 16:36:29 +000010473 if (LocaleNCompare(value+i,"date",4) == 0)
10474 mng_info->ping_exclude_date=MagickFalse;
10475
glennrp03812ae2010-12-24 01:31:34 +000010476 if (LocaleNCompare(value+i,"exif",4) == 0)
10477 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010478
glennrp03812ae2010-12-24 01:31:34 +000010479 if (LocaleNCompare(value+i,"gama",4) == 0)
10480 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010481
glennrp03812ae2010-12-24 01:31:34 +000010482 if (LocaleNCompare(value+i,"iccp",4) == 0)
10483 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010484
glennrp03812ae2010-12-24 01:31:34 +000010485 /*
10486 if (LocaleNCompare(value+i,"itxt",4) == 0)
10487 mng_info->ping_exclude_iTXt=MagickFalse;
10488 */
glennrp2cc891a2010-12-24 13:44:32 +000010489
glennrp03812ae2010-12-24 01:31:34 +000010490 if (LocaleNCompare(value+i,"gama",4) == 0)
10491 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010492
glennrp03812ae2010-12-24 01:31:34 +000010493 if (LocaleNCompare(value+i,"offs",4) == 0)
10494 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010495
glennrp03812ae2010-12-24 01:31:34 +000010496 if (LocaleNCompare(value+i,"phys",4) == 0)
10497 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010498
glennrpa1e3b7b2010-12-24 16:37:33 +000010499 if (LocaleNCompare(value+i,"srgb",4) == 0)
10500 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010501
glennrp03812ae2010-12-24 01:31:34 +000010502 if (LocaleNCompare(value+i,"text",4) == 0)
10503 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010504
glennrpa1e3b7b2010-12-24 16:37:33 +000010505 if (LocaleNCompare(value+i,"trns",4) == 0)
10506 mng_info->ping_exclude_tRNS=MagickFalse;
10507
glennrp03812ae2010-12-24 01:31:34 +000010508 if (LocaleNCompare(value+i,"vpag",4) == 0)
10509 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010510
glennrp03812ae2010-12-24 01:31:34 +000010511 if (LocaleNCompare(value+i,"zccp",4) == 0)
10512 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010513
glennrp03812ae2010-12-24 01:31:34 +000010514 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10515 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000010516
glennrp03812ae2010-12-24 01:31:34 +000010517 }
glennrpce91ed52010-12-23 22:37:49 +000010518 }
glennrp26f37912010-12-23 16:22:42 +000010519 }
10520
glennrp03812ae2010-12-24 01:31:34 +000010521 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010522 {
10523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10524 " Chunks to be excluded from the output PNG:");
10525 if (mng_info->ping_exclude_bKGD != MagickFalse)
10526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10527 " bKGD");
10528 if (mng_info->ping_exclude_cHRM != MagickFalse)
10529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10530 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000010531 if (mng_info->ping_exclude_date != MagickFalse)
10532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10533 " date");
glennrp26f37912010-12-23 16:22:42 +000010534 if (mng_info->ping_exclude_EXIF != MagickFalse)
10535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10536 " EXIF");
10537 if (mng_info->ping_exclude_gAMA != MagickFalse)
10538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10539 " gAMA");
10540 if (mng_info->ping_exclude_iCCP != MagickFalse)
10541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10542 " iCCP");
10543/*
10544 if (mng_info->ping_exclude_iTXt != MagickFalse)
10545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10546 " iTXt");
10547*/
10548 if (mng_info->ping_exclude_oFFs != MagickFalse)
10549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10550 " oFFs");
10551 if (mng_info->ping_exclude_pHYs != MagickFalse)
10552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10553 " pHYs");
10554 if (mng_info->ping_exclude_sRGB != MagickFalse)
10555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10556 " sRGB");
10557 if (mng_info->ping_exclude_tEXt != MagickFalse)
10558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10559 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000010560 if (mng_info->ping_exclude_tRNS != MagickFalse)
10561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10562 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000010563 if (mng_info->ping_exclude_vpAg != MagickFalse)
10564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10565 " vpAg");
10566 if (mng_info->ping_exclude_zCCP != MagickFalse)
10567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10568 " zCCP");
10569 if (mng_info->ping_exclude_zTXt != MagickFalse)
10570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10571 " zTXt");
10572 }
10573
glennrpb9cfe272010-12-21 15:08:06 +000010574 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010575
glennrpb9cfe272010-12-21 15:08:06 +000010576 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000010577
10578 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000010579
cristy3ed852e2009-09-05 21:47:34 +000010580 if (logging != MagickFalse)
10581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010582
cristy3ed852e2009-09-05 21:47:34 +000010583 return(status);
10584}
10585
10586#if defined(JNG_SUPPORTED)
10587
10588/* Write one JNG image */
10589static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10590 const ImageInfo *image_info,Image *image)
10591{
10592 Image
10593 *jpeg_image;
10594
10595 ImageInfo
10596 *jpeg_image_info;
10597
10598 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000010599 logging,
cristy3ed852e2009-09-05 21:47:34 +000010600 status;
10601
10602 size_t
10603 length;
10604
10605 unsigned char
10606 *blob,
10607 chunk[80],
10608 *p;
10609
10610 unsigned int
10611 jng_alpha_compression_method,
10612 jng_alpha_sample_depth,
10613 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000010614 transparent;
10615
cristybb503372010-05-27 20:51:26 +000010616 size_t
cristy3ed852e2009-09-05 21:47:34 +000010617 jng_quality;
10618
10619 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000010620 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010621
10622 blob=(unsigned char *) NULL;
10623 jpeg_image=(Image *) NULL;
10624 jpeg_image_info=(ImageInfo *) NULL;
10625
10626 status=MagickTrue;
10627 transparent=image_info->type==GrayscaleMatteType ||
10628 image_info->type==TrueColorMatteType;
10629 jng_color_type=10;
10630 jng_alpha_sample_depth=0;
10631 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10632 jng_alpha_compression_method=0;
10633
10634 if (image->matte != MagickFalse)
10635 {
10636 /* if any pixels are transparent */
10637 transparent=MagickTrue;
10638 if (image_info->compression==JPEGCompression)
10639 jng_alpha_compression_method=8;
10640 }
10641
10642 if (transparent)
10643 {
10644 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000010645
cristy3ed852e2009-09-05 21:47:34 +000010646 /* Create JPEG blob, image, and image_info */
10647 if (logging != MagickFalse)
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000010650
cristy3ed852e2009-09-05 21:47:34 +000010651 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000010652
cristy3ed852e2009-09-05 21:47:34 +000010653 if (jpeg_image_info == (ImageInfo *) NULL)
10654 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010655
cristy3ed852e2009-09-05 21:47:34 +000010656 if (logging != MagickFalse)
10657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10658 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000010659
cristy3ed852e2009-09-05 21:47:34 +000010660 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010661
cristy3ed852e2009-09-05 21:47:34 +000010662 if (jpeg_image == (Image *) NULL)
10663 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010664
cristy3ed852e2009-09-05 21:47:34 +000010665 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10666 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10667 status=NegateImage(jpeg_image,MagickFalse);
10668 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010669
cristy3ed852e2009-09-05 21:47:34 +000010670 if (jng_quality >= 1000)
10671 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000010672
cristy3ed852e2009-09-05 21:47:34 +000010673 else
10674 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000010675
cristy3ed852e2009-09-05 21:47:34 +000010676 jpeg_image_info->type=GrayscaleType;
10677 (void) SetImageType(jpeg_image,GrayscaleType);
10678 (void) AcquireUniqueFilename(jpeg_image->filename);
10679 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10680 "%s",jpeg_image->filename);
10681 }
10682
10683 /* To do: check bit depth of PNG alpha channel */
10684
10685 /* Check if image is grayscale. */
10686 if (image_info->type != TrueColorMatteType && image_info->type !=
10687 TrueColorType && ImageIsGray(image))
10688 jng_color_type-=2;
10689
10690 if (transparent)
10691 {
10692 if (jng_alpha_compression_method==0)
10693 {
10694 const char
10695 *value;
10696
10697 /* Encode opacity as a grayscale PNG blob */
10698 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10699 &image->exception);
10700 if (logging != MagickFalse)
10701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10702 " Creating PNG blob.");
10703 length=0;
10704
10705 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10706 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10707 jpeg_image_info->interlace=NoInterlace;
10708
10709 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10710 &image->exception);
10711
10712 /* Retrieve sample depth used */
10713 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10714 if (value != (char *) NULL)
10715 jng_alpha_sample_depth= (unsigned int) value[0];
10716 }
10717 else
10718 {
10719 /* Encode opacity as a grayscale JPEG blob */
10720
10721 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10722 &image->exception);
10723
10724 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10725 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10726 jpeg_image_info->interlace=NoInterlace;
10727 if (logging != MagickFalse)
10728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10729 " Creating blob.");
10730 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000010731 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010732 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010733
cristy3ed852e2009-09-05 21:47:34 +000010734 if (logging != MagickFalse)
10735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010736 " Successfully read jpeg_image into a blob, length=%.20g.",
10737 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000010738
10739 }
10740 /* Destroy JPEG image and image_info */
10741 jpeg_image=DestroyImage(jpeg_image);
10742 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10743 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10744 }
10745
10746 /* Write JHDR chunk */
10747 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10748 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000010749 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000010750 PNGLong(chunk+4,(png_uint_32) image->columns);
10751 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000010752 chunk[12]=jng_color_type;
10753 chunk[13]=8; /* sample depth */
10754 chunk[14]=8; /*jng_image_compression_method */
10755 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10756 chunk[16]=jng_alpha_sample_depth;
10757 chunk[17]=jng_alpha_compression_method;
10758 chunk[18]=0; /*jng_alpha_filter_method */
10759 chunk[19]=0; /*jng_alpha_interlace_method */
10760 (void) WriteBlob(image,20,chunk);
10761 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10762 if (logging != MagickFalse)
10763 {
10764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010765 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000010766
cristy3ed852e2009-09-05 21:47:34 +000010767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000010768 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000010769
cristy3ed852e2009-09-05 21:47:34 +000010770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10771 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010772
cristy3ed852e2009-09-05 21:47:34 +000010773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10774 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010775
cristy3ed852e2009-09-05 21:47:34 +000010776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10777 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000010778
cristy3ed852e2009-09-05 21:47:34 +000010779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10780 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010781
cristy3ed852e2009-09-05 21:47:34 +000010782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10783 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010784
cristy3ed852e2009-09-05 21:47:34 +000010785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10786 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000010787
cristy3ed852e2009-09-05 21:47:34 +000010788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10789 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000010790
cristy3ed852e2009-09-05 21:47:34 +000010791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10792 " JNG alpha interlace:%5d",0);
10793 }
10794
glennrp0fe50b42010-11-16 03:52:51 +000010795 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010796 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000010797
10798 /*
10799 Write leading ancillary chunks
10800 */
10801
10802 if (transparent)
10803 {
10804 /*
10805 Write JNG bKGD chunk
10806 */
10807
10808 unsigned char
10809 blue,
10810 green,
10811 red;
10812
cristybb503372010-05-27 20:51:26 +000010813 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010814 num_bytes;
10815
10816 if (jng_color_type == 8 || jng_color_type == 12)
10817 num_bytes=6L;
10818 else
10819 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000010820 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010821 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000010822 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000010823 red=ScaleQuantumToChar(image->background_color.red);
10824 green=ScaleQuantumToChar(image->background_color.green);
10825 blue=ScaleQuantumToChar(image->background_color.blue);
10826 *(chunk+4)=0;
10827 *(chunk+5)=red;
10828 *(chunk+6)=0;
10829 *(chunk+7)=green;
10830 *(chunk+8)=0;
10831 *(chunk+9)=blue;
10832 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10833 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10834 }
10835
10836 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10837 {
10838 /*
10839 Write JNG sRGB chunk
10840 */
10841 (void) WriteBlobMSBULong(image,1L);
10842 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000010843 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000010844
cristy3ed852e2009-09-05 21:47:34 +000010845 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000010846 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010847 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010848 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000010849
cristy3ed852e2009-09-05 21:47:34 +000010850 else
glennrpe610a072010-08-05 17:08:46 +000010851 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000010852 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000010853 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000010854
cristy3ed852e2009-09-05 21:47:34 +000010855 (void) WriteBlob(image,5,chunk);
10856 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10857 }
10858 else
10859 {
10860 if (image->gamma != 0.0)
10861 {
10862 /*
10863 Write JNG gAMA chunk
10864 */
10865 (void) WriteBlobMSBULong(image,4L);
10866 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000010867 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000010868 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010869 (void) WriteBlob(image,8,chunk);
10870 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10871 }
glennrp0fe50b42010-11-16 03:52:51 +000010872
cristy3ed852e2009-09-05 21:47:34 +000010873 if ((mng_info->equal_chrms == MagickFalse) &&
10874 (image->chromaticity.red_primary.x != 0.0))
10875 {
10876 PrimaryInfo
10877 primary;
10878
10879 /*
10880 Write JNG cHRM chunk
10881 */
10882 (void) WriteBlobMSBULong(image,32L);
10883 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000010884 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000010885 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000010886 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10887 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010888 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000010889 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10890 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010891 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000010892 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10893 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010894 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000010895 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10896 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010897 (void) WriteBlob(image,36,chunk);
10898 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10899 }
10900 }
glennrp0fe50b42010-11-16 03:52:51 +000010901
cristy3ed852e2009-09-05 21:47:34 +000010902 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10903 {
10904 /*
10905 Write JNG pHYs chunk
10906 */
10907 (void) WriteBlobMSBULong(image,9L);
10908 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000010909 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000010910 if (image->units == PixelsPerInchResolution)
10911 {
cristy35ef8242010-06-03 16:24:13 +000010912 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010913 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010914
cristy35ef8242010-06-03 16:24:13 +000010915 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010916 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010917
cristy3ed852e2009-09-05 21:47:34 +000010918 chunk[12]=1;
10919 }
glennrp0fe50b42010-11-16 03:52:51 +000010920
cristy3ed852e2009-09-05 21:47:34 +000010921 else
10922 {
10923 if (image->units == PixelsPerCentimeterResolution)
10924 {
cristy35ef8242010-06-03 16:24:13 +000010925 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010926 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010927
cristy35ef8242010-06-03 16:24:13 +000010928 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000010929 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000010930
cristy3ed852e2009-09-05 21:47:34 +000010931 chunk[12]=1;
10932 }
glennrp0fe50b42010-11-16 03:52:51 +000010933
cristy3ed852e2009-09-05 21:47:34 +000010934 else
10935 {
cristy35ef8242010-06-03 16:24:13 +000010936 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10937 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000010938 chunk[12]=0;
10939 }
10940 }
10941 (void) WriteBlob(image,13,chunk);
10942 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10943 }
10944
10945 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10946 {
10947 /*
10948 Write JNG oFFs chunk
10949 */
10950 (void) WriteBlobMSBULong(image,9L);
10951 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000010952 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000010953 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10954 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000010955 chunk[12]=0;
10956 (void) WriteBlob(image,13,chunk);
10957 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10958 }
10959 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10960 {
10961 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10962 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010963 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000010964 PNGLong(chunk+4,(png_uint_32) image->page.width);
10965 PNGLong(chunk+8,(png_uint_32) image->page.height);
10966 chunk[12]=0; /* unit = pixels */
10967 (void) WriteBlob(image,13,chunk);
10968 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10969 }
10970
10971
10972 if (transparent)
10973 {
10974 if (jng_alpha_compression_method==0)
10975 {
cristybb503372010-05-27 20:51:26 +000010976 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010977 i;
10978
cristybb503372010-05-27 20:51:26 +000010979 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000010980 len;
10981
10982 /* Write IDAT chunk header */
10983 if (logging != MagickFalse)
10984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010985 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000010986 length);
cristy3ed852e2009-09-05 21:47:34 +000010987
10988 /* Copy IDAT chunks */
10989 len=0;
10990 p=blob+8;
cristybb503372010-05-27 20:51:26 +000010991 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000010992 {
10993 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10994 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000010995
cristy3ed852e2009-09-05 21:47:34 +000010996 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10997 {
10998 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000010999 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011000 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011001 (void) WriteBlob(image,(size_t) len+4,p);
11002 (void) WriteBlobMSBULong(image,
11003 crc32(0,p,(uInt) len+4));
11004 }
glennrp0fe50b42010-11-16 03:52:51 +000011005
cristy3ed852e2009-09-05 21:47:34 +000011006 else
11007 {
11008 if (logging != MagickFalse)
11009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011010 " Skipping %c%c%c%c chunk, length=%.20g.",
11011 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011012 }
11013 p+=(8+len);
11014 }
11015 }
11016 else
11017 {
11018 /* Write JDAA chunk header */
11019 if (logging != MagickFalse)
11020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011021 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011022 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011023 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011024 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011025 /* Write JDAT chunk(s) data */
11026 (void) WriteBlob(image,4,chunk);
11027 (void) WriteBlob(image,length,blob);
11028 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11029 (uInt) length));
11030 }
11031 blob=(unsigned char *) RelinquishMagickMemory(blob);
11032 }
11033
11034 /* Encode image as a JPEG blob */
11035 if (logging != MagickFalse)
11036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11037 " Creating jpeg_image_info.");
11038 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11039 if (jpeg_image_info == (ImageInfo *) NULL)
11040 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11041
11042 if (logging != MagickFalse)
11043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11044 " Creating jpeg_image.");
11045
11046 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11047 if (jpeg_image == (Image *) NULL)
11048 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11049 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11050
11051 (void) AcquireUniqueFilename(jpeg_image->filename);
11052 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
11053 jpeg_image->filename);
11054
11055 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11056 &image->exception);
11057
11058 if (logging != MagickFalse)
11059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011060 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11061 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011062
11063 if (jng_color_type == 8 || jng_color_type == 12)
11064 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011065
cristy3ed852e2009-09-05 21:47:34 +000011066 jpeg_image_info->quality=jng_quality % 1000;
11067 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11068 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011069
cristy3ed852e2009-09-05 21:47:34 +000011070 if (logging != MagickFalse)
11071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11072 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011073
cristy3ed852e2009-09-05 21:47:34 +000011074 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011075
cristy3ed852e2009-09-05 21:47:34 +000011076 if (logging != MagickFalse)
11077 {
11078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011079 " Successfully read jpeg_image into a blob, length=%.20g.",
11080 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011081
11082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011083 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011084 }
glennrp0fe50b42010-11-16 03:52:51 +000011085
cristy3ed852e2009-09-05 21:47:34 +000011086 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011087 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011088 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011089 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011090 (void) WriteBlob(image,4,chunk);
11091 (void) WriteBlob(image,length,blob);
11092 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11093
11094 jpeg_image=DestroyImage(jpeg_image);
11095 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11096 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11097 blob=(unsigned char *) RelinquishMagickMemory(blob);
11098
11099 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011100 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011101
11102 /* Write IEND chunk */
11103 (void) WriteBlobMSBULong(image,0L);
11104 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011105 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011106 (void) WriteBlob(image,4,chunk);
11107 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11108
11109 if (logging != MagickFalse)
11110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11111 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011112
cristy3ed852e2009-09-05 21:47:34 +000011113 return(status);
11114}
11115
11116
11117/*
11118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11119% %
11120% %
11121% %
11122% W r i t e J N G I m a g e %
11123% %
11124% %
11125% %
11126%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11127%
11128% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11129%
11130% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11131%
11132% The format of the WriteJNGImage method is:
11133%
11134% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11135%
11136% A description of each parameter follows:
11137%
11138% o image_info: the image info.
11139%
11140% o image: The image.
11141%
11142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11143*/
11144static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11145{
11146 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011147 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011148 logging,
cristy3ed852e2009-09-05 21:47:34 +000011149 status;
11150
11151 MngInfo
11152 *mng_info;
11153
cristy3ed852e2009-09-05 21:47:34 +000011154 /*
11155 Open image file.
11156 */
11157 assert(image_info != (const ImageInfo *) NULL);
11158 assert(image_info->signature == MagickSignature);
11159 assert(image != (Image *) NULL);
11160 assert(image->signature == MagickSignature);
11161 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011162 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011163 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11164 if (status == MagickFalse)
11165 return(status);
11166
11167 /*
11168 Allocate a MngInfo structure.
11169 */
11170 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011171 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011172 if (mng_info == (MngInfo *) NULL)
11173 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11174 /*
11175 Initialize members of the MngInfo structure.
11176 */
11177 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11178 mng_info->image=image;
11179 have_mng_structure=MagickTrue;
11180
11181 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11182
11183 status=WriteOneJNGImage(mng_info,image_info,image);
11184 (void) CloseBlob(image);
11185
11186 (void) CatchImageException(image);
11187 MngInfoFreeStruct(mng_info,&have_mng_structure);
11188 if (logging != MagickFalse)
11189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11190 return(status);
11191}
11192#endif
11193
11194
11195
11196static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11197{
11198 const char
11199 *option;
11200
11201 Image
11202 *next_image;
11203
11204 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011205 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011206 status;
11207
glennrp03812ae2010-12-24 01:31:34 +000011208 volatile MagickBooleanType
11209 logging;
11210
cristy3ed852e2009-09-05 21:47:34 +000011211 MngInfo
11212 *mng_info;
11213
11214 int
cristy3ed852e2009-09-05 21:47:34 +000011215 image_count,
11216 need_iterations,
11217 need_matte;
11218
11219 volatile int
11220#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11221 defined(PNG_MNG_FEATURES_SUPPORTED)
11222 need_local_plte,
11223#endif
11224 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011225 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011226 use_global_plte;
11227
cristybb503372010-05-27 20:51:26 +000011228 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011229 i;
11230
11231 unsigned char
11232 chunk[800];
11233
11234 volatile unsigned int
11235 write_jng,
11236 write_mng;
11237
cristybb503372010-05-27 20:51:26 +000011238 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011239 scene;
11240
cristybb503372010-05-27 20:51:26 +000011241 size_t
cristy3ed852e2009-09-05 21:47:34 +000011242 final_delay=0,
11243 initial_delay;
11244
glennrpd5045b42010-03-24 12:40:35 +000011245#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011246 if (image_info->verbose)
11247 printf("Your PNG library (libpng-%s) is rather old.\n",
11248 PNG_LIBPNG_VER_STRING);
11249#endif
11250
11251 /*
11252 Open image file.
11253 */
11254 assert(image_info != (const ImageInfo *) NULL);
11255 assert(image_info->signature == MagickSignature);
11256 assert(image != (Image *) NULL);
11257 assert(image->signature == MagickSignature);
11258 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011259 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011260 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11261 if (status == MagickFalse)
11262 return(status);
11263
11264 /*
11265 Allocate a MngInfo structure.
11266 */
11267 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011268 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011269 if (mng_info == (MngInfo *) NULL)
11270 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11271 /*
11272 Initialize members of the MngInfo structure.
11273 */
11274 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11275 mng_info->image=image;
11276 have_mng_structure=MagickTrue;
11277 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11278
11279 /*
11280 * See if user has requested a specific PNG subformat to be used
11281 * for all of the PNGs in the MNG being written, e.g.,
11282 *
11283 * convert *.png png8:animation.mng
11284 *
11285 * To do: check -define png:bit_depth and png:color_type as well,
11286 * or perhaps use mng:bit_depth and mng:color_type instead for
11287 * global settings.
11288 */
11289
11290 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11291 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11292 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11293
11294 write_jng=MagickFalse;
11295 if (image_info->compression == JPEGCompression)
11296 write_jng=MagickTrue;
11297
11298 mng_info->adjoin=image_info->adjoin &&
11299 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11300
cristy3ed852e2009-09-05 21:47:34 +000011301 if (logging != MagickFalse)
11302 {
11303 /* Log some info about the input */
11304 Image
11305 *p;
11306
11307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11308 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011309
cristy3ed852e2009-09-05 21:47:34 +000011310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011311 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011312
cristy3ed852e2009-09-05 21:47:34 +000011313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11314 " Type: %d",image_info->type);
11315
11316 scene=0;
11317 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11318 {
11319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011320 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000011321
cristy3ed852e2009-09-05 21:47:34 +000011322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011323 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011324
cristy3ed852e2009-09-05 21:47:34 +000011325 if (p->matte)
11326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11327 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000011328
cristy3ed852e2009-09-05 21:47:34 +000011329 else
11330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11331 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000011332
cristy3ed852e2009-09-05 21:47:34 +000011333 if (p->storage_class == PseudoClass)
11334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11335 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000011336
cristy3ed852e2009-09-05 21:47:34 +000011337 else
11338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11339 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000011340
cristy3ed852e2009-09-05 21:47:34 +000011341 if (p->colors)
11342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011343 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000011344
cristy3ed852e2009-09-05 21:47:34 +000011345 else
11346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11347 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000011348
cristy3ed852e2009-09-05 21:47:34 +000011349 if (mng_info->adjoin == MagickFalse)
11350 break;
11351 }
11352 }
11353
cristy3ed852e2009-09-05 21:47:34 +000011354 use_global_plte=MagickFalse;
11355 all_images_are_gray=MagickFalse;
11356#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11357 need_local_plte=MagickTrue;
11358#endif
11359 need_defi=MagickFalse;
11360 need_matte=MagickFalse;
11361 mng_info->framing_mode=1;
11362 mng_info->old_framing_mode=1;
11363
11364 if (write_mng)
11365 if (image_info->page != (char *) NULL)
11366 {
11367 /*
11368 Determine image bounding box.
11369 */
11370 SetGeometry(image,&mng_info->page);
11371 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11372 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11373 }
11374 if (write_mng)
11375 {
11376 unsigned int
11377 need_geom;
11378
11379 unsigned short
11380 red,
11381 green,
11382 blue;
11383
11384 mng_info->page=image->page;
11385 need_geom=MagickTrue;
11386 if (mng_info->page.width || mng_info->page.height)
11387 need_geom=MagickFalse;
11388 /*
11389 Check all the scenes.
11390 */
11391 initial_delay=image->delay;
11392 need_iterations=MagickFalse;
11393 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11394 mng_info->equal_physs=MagickTrue,
11395 mng_info->equal_gammas=MagickTrue;
11396 mng_info->equal_srgbs=MagickTrue;
11397 mng_info->equal_backgrounds=MagickTrue;
11398 image_count=0;
11399#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11400 defined(PNG_MNG_FEATURES_SUPPORTED)
11401 all_images_are_gray=MagickTrue;
11402 mng_info->equal_palettes=MagickFalse;
11403 need_local_plte=MagickFalse;
11404#endif
11405 for (next_image=image; next_image != (Image *) NULL; )
11406 {
11407 if (need_geom)
11408 {
11409 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11410 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000011411
cristy3ed852e2009-09-05 21:47:34 +000011412 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11413 mng_info->page.height=next_image->rows+next_image->page.y;
11414 }
glennrp0fe50b42010-11-16 03:52:51 +000011415
cristy3ed852e2009-09-05 21:47:34 +000011416 if (next_image->page.x || next_image->page.y)
11417 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011418
cristy3ed852e2009-09-05 21:47:34 +000011419 if (next_image->matte)
11420 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011421
cristy3ed852e2009-09-05 21:47:34 +000011422 if ((int) next_image->dispose >= BackgroundDispose)
11423 if (next_image->matte || next_image->page.x || next_image->page.y ||
11424 ((next_image->columns < mng_info->page.width) &&
11425 (next_image->rows < mng_info->page.height)))
11426 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011427
cristy3ed852e2009-09-05 21:47:34 +000011428 if (next_image->iterations)
11429 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011430
cristy3ed852e2009-09-05 21:47:34 +000011431 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000011432
cristy3ed852e2009-09-05 21:47:34 +000011433 if (final_delay != initial_delay || final_delay > 1UL*
11434 next_image->ticks_per_second)
11435 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000011436
cristy3ed852e2009-09-05 21:47:34 +000011437#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11438 defined(PNG_MNG_FEATURES_SUPPORTED)
11439 /*
11440 check for global palette possibility.
11441 */
11442 if (image->matte != MagickFalse)
11443 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011444
cristy3ed852e2009-09-05 21:47:34 +000011445 if (need_local_plte == 0)
11446 {
11447 if (ImageIsGray(image) == MagickFalse)
11448 all_images_are_gray=MagickFalse;
11449 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11450 if (use_global_plte == 0)
11451 use_global_plte=mng_info->equal_palettes;
11452 need_local_plte=!mng_info->equal_palettes;
11453 }
11454#endif
11455 if (GetNextImageInList(next_image) != (Image *) NULL)
11456 {
11457 if (next_image->background_color.red !=
11458 next_image->next->background_color.red ||
11459 next_image->background_color.green !=
11460 next_image->next->background_color.green ||
11461 next_image->background_color.blue !=
11462 next_image->next->background_color.blue)
11463 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011464
cristy3ed852e2009-09-05 21:47:34 +000011465 if (next_image->gamma != next_image->next->gamma)
11466 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011467
cristy3ed852e2009-09-05 21:47:34 +000011468 if (next_image->rendering_intent !=
11469 next_image->next->rendering_intent)
11470 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011471
cristy3ed852e2009-09-05 21:47:34 +000011472 if ((next_image->units != next_image->next->units) ||
11473 (next_image->x_resolution != next_image->next->x_resolution) ||
11474 (next_image->y_resolution != next_image->next->y_resolution))
11475 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011476
cristy3ed852e2009-09-05 21:47:34 +000011477 if (mng_info->equal_chrms)
11478 {
11479 if (next_image->chromaticity.red_primary.x !=
11480 next_image->next->chromaticity.red_primary.x ||
11481 next_image->chromaticity.red_primary.y !=
11482 next_image->next->chromaticity.red_primary.y ||
11483 next_image->chromaticity.green_primary.x !=
11484 next_image->next->chromaticity.green_primary.x ||
11485 next_image->chromaticity.green_primary.y !=
11486 next_image->next->chromaticity.green_primary.y ||
11487 next_image->chromaticity.blue_primary.x !=
11488 next_image->next->chromaticity.blue_primary.x ||
11489 next_image->chromaticity.blue_primary.y !=
11490 next_image->next->chromaticity.blue_primary.y ||
11491 next_image->chromaticity.white_point.x !=
11492 next_image->next->chromaticity.white_point.x ||
11493 next_image->chromaticity.white_point.y !=
11494 next_image->next->chromaticity.white_point.y)
11495 mng_info->equal_chrms=MagickFalse;
11496 }
11497 }
11498 image_count++;
11499 next_image=GetNextImageInList(next_image);
11500 }
11501 if (image_count < 2)
11502 {
11503 mng_info->equal_backgrounds=MagickFalse;
11504 mng_info->equal_chrms=MagickFalse;
11505 mng_info->equal_gammas=MagickFalse;
11506 mng_info->equal_srgbs=MagickFalse;
11507 mng_info->equal_physs=MagickFalse;
11508 use_global_plte=MagickFalse;
11509#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11510 need_local_plte=MagickTrue;
11511#endif
11512 need_iterations=MagickFalse;
11513 }
glennrp0fe50b42010-11-16 03:52:51 +000011514
cristy3ed852e2009-09-05 21:47:34 +000011515 if (mng_info->need_fram == MagickFalse)
11516 {
11517 /*
11518 Only certain framing rates 100/n are exactly representable without
11519 the FRAM chunk but we'll allow some slop in VLC files
11520 */
11521 if (final_delay == 0)
11522 {
11523 if (need_iterations != MagickFalse)
11524 {
11525 /*
11526 It's probably a GIF with loop; don't run it *too* fast.
11527 */
glennrp02617122010-07-28 13:07:35 +000011528 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000011529 {
11530 final_delay=10;
11531 (void) ThrowMagickException(&image->exception,
11532 GetMagickModule(),CoderWarning,
11533 "input has zero delay between all frames; assuming",
11534 " 10 cs `%s'","");
11535 }
cristy3ed852e2009-09-05 21:47:34 +000011536 }
11537 else
11538 mng_info->ticks_per_second=0;
11539 }
11540 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000011541 mng_info->ticks_per_second=(png_uint_32)
11542 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000011543 if (final_delay > 50)
11544 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000011545
cristy3ed852e2009-09-05 21:47:34 +000011546 if (final_delay > 75)
11547 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000011548
cristy3ed852e2009-09-05 21:47:34 +000011549 if (final_delay > 125)
11550 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000011551
cristy3ed852e2009-09-05 21:47:34 +000011552 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11553 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11554 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11555 1UL*image->ticks_per_second))
11556 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11557 }
glennrp0fe50b42010-11-16 03:52:51 +000011558
cristy3ed852e2009-09-05 21:47:34 +000011559 if (mng_info->need_fram != MagickFalse)
11560 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11561 /*
11562 If pseudocolor, we should also check to see if all the
11563 palettes are identical and write a global PLTE if they are.
11564 ../glennrp Feb 99.
11565 */
11566 /*
11567 Write the MNG version 1.0 signature and MHDR chunk.
11568 */
11569 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11570 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11571 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000011572 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000011573 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11574 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000011575 PNGLong(chunk+12,mng_info->ticks_per_second);
11576 PNGLong(chunk+16,0L); /* layer count=unknown */
11577 PNGLong(chunk+20,0L); /* frame count=unknown */
11578 PNGLong(chunk+24,0L); /* play time=unknown */
11579 if (write_jng)
11580 {
11581 if (need_matte)
11582 {
11583 if (need_defi || mng_info->need_fram || use_global_plte)
11584 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000011585
cristy3ed852e2009-09-05 21:47:34 +000011586 else
11587 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11588 }
glennrp0fe50b42010-11-16 03:52:51 +000011589
cristy3ed852e2009-09-05 21:47:34 +000011590 else
11591 {
11592 if (need_defi || mng_info->need_fram || use_global_plte)
11593 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011594
cristy3ed852e2009-09-05 21:47:34 +000011595 else
11596 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11597 }
11598 }
glennrp0fe50b42010-11-16 03:52:51 +000011599
cristy3ed852e2009-09-05 21:47:34 +000011600 else
11601 {
11602 if (need_matte)
11603 {
11604 if (need_defi || mng_info->need_fram || use_global_plte)
11605 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000011606
cristy3ed852e2009-09-05 21:47:34 +000011607 else
11608 PNGLong(chunk+28,9L); /* simplicity=VLC */
11609 }
glennrp0fe50b42010-11-16 03:52:51 +000011610
cristy3ed852e2009-09-05 21:47:34 +000011611 else
11612 {
11613 if (need_defi || mng_info->need_fram || use_global_plte)
11614 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000011615
cristy3ed852e2009-09-05 21:47:34 +000011616 else
11617 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11618 }
11619 }
11620 (void) WriteBlob(image,32,chunk);
11621 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11622 option=GetImageOption(image_info,"mng:need-cacheoff");
11623 if (option != (const char *) NULL)
11624 {
11625 size_t
11626 length;
11627
11628 /*
11629 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11630 */
11631 PNGType(chunk,mng_nEED);
11632 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000011633 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000011634 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011635 length+=4;
11636 (void) WriteBlob(image,length,chunk);
11637 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11638 }
11639 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11640 (GetNextImageInList(image) != (Image *) NULL) &&
11641 (image->iterations != 1))
11642 {
11643 /*
11644 Write MNG TERM chunk
11645 */
11646 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11647 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000011648 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011649 chunk[4]=3; /* repeat animation */
11650 chunk[5]=0; /* show last frame when done */
11651 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11652 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011653
cristy3ed852e2009-09-05 21:47:34 +000011654 if (image->iterations == 0)
11655 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011656
cristy3ed852e2009-09-05 21:47:34 +000011657 else
11658 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000011659
cristy3ed852e2009-09-05 21:47:34 +000011660 if (logging != MagickFalse)
11661 {
11662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011663 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11664 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000011665
cristy3ed852e2009-09-05 21:47:34 +000011666 if (image->iterations == 0)
11667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011668 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000011669
cristy3ed852e2009-09-05 21:47:34 +000011670 else
11671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011672 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000011673 }
11674 (void) WriteBlob(image,14,chunk);
11675 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11676 }
11677 /*
11678 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11679 */
11680 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11681 mng_info->equal_srgbs)
11682 {
11683 /*
11684 Write MNG sRGB chunk
11685 */
11686 (void) WriteBlobMSBULong(image,1L);
11687 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011688 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011689
cristy3ed852e2009-09-05 21:47:34 +000011690 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011691 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011692 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011693 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011694
cristy3ed852e2009-09-05 21:47:34 +000011695 else
glennrpe610a072010-08-05 17:08:46 +000011696 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011697 Magick_RenderingIntent_to_PNG_RenderingIntent(
11698 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011699
cristy3ed852e2009-09-05 21:47:34 +000011700 (void) WriteBlob(image,5,chunk);
11701 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11702 mng_info->have_write_global_srgb=MagickTrue;
11703 }
glennrp0fe50b42010-11-16 03:52:51 +000011704
cristy3ed852e2009-09-05 21:47:34 +000011705 else
11706 {
11707 if (image->gamma && mng_info->equal_gammas)
11708 {
11709 /*
11710 Write MNG gAMA chunk
11711 */
11712 (void) WriteBlobMSBULong(image,4L);
11713 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011714 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011715 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011716 (void) WriteBlob(image,8,chunk);
11717 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11718 mng_info->have_write_global_gama=MagickTrue;
11719 }
11720 if (mng_info->equal_chrms)
11721 {
11722 PrimaryInfo
11723 primary;
11724
11725 /*
11726 Write MNG cHRM chunk
11727 */
11728 (void) WriteBlobMSBULong(image,32L);
11729 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011730 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011731 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011732 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11733 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011734 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011735 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11736 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011737 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011738 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11739 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011740 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011741 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11742 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011743 (void) WriteBlob(image,36,chunk);
11744 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11745 mng_info->have_write_global_chrm=MagickTrue;
11746 }
11747 }
11748 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11749 {
11750 /*
11751 Write MNG pHYs chunk
11752 */
11753 (void) WriteBlobMSBULong(image,9L);
11754 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011755 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000011756
cristy3ed852e2009-09-05 21:47:34 +000011757 if (image->units == PixelsPerInchResolution)
11758 {
cristy35ef8242010-06-03 16:24:13 +000011759 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011760 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011761
cristy35ef8242010-06-03 16:24:13 +000011762 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011763 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011764
cristy3ed852e2009-09-05 21:47:34 +000011765 chunk[12]=1;
11766 }
glennrp0fe50b42010-11-16 03:52:51 +000011767
cristy3ed852e2009-09-05 21:47:34 +000011768 else
11769 {
11770 if (image->units == PixelsPerCentimeterResolution)
11771 {
cristy35ef8242010-06-03 16:24:13 +000011772 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011773 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011774
cristy35ef8242010-06-03 16:24:13 +000011775 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011776 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011777
cristy3ed852e2009-09-05 21:47:34 +000011778 chunk[12]=1;
11779 }
glennrp0fe50b42010-11-16 03:52:51 +000011780
cristy3ed852e2009-09-05 21:47:34 +000011781 else
11782 {
cristy35ef8242010-06-03 16:24:13 +000011783 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11784 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011785 chunk[12]=0;
11786 }
11787 }
11788 (void) WriteBlob(image,13,chunk);
11789 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11790 }
11791 /*
11792 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11793 or does not cover the entire frame.
11794 */
11795 if (write_mng && (image->matte || image->page.x > 0 ||
11796 image->page.y > 0 || (image->page.width &&
11797 (image->page.width+image->page.x < mng_info->page.width))
11798 || (image->page.height && (image->page.height+image->page.y
11799 < mng_info->page.height))))
11800 {
11801 (void) WriteBlobMSBULong(image,6L);
11802 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000011803 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000011804 red=ScaleQuantumToShort(image->background_color.red);
11805 green=ScaleQuantumToShort(image->background_color.green);
11806 blue=ScaleQuantumToShort(image->background_color.blue);
11807 PNGShort(chunk+4,red);
11808 PNGShort(chunk+6,green);
11809 PNGShort(chunk+8,blue);
11810 (void) WriteBlob(image,10,chunk);
11811 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11812 if (mng_info->equal_backgrounds)
11813 {
11814 (void) WriteBlobMSBULong(image,6L);
11815 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011816 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000011817 (void) WriteBlob(image,10,chunk);
11818 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11819 }
11820 }
11821
11822#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11823 if ((need_local_plte == MagickFalse) &&
11824 (image->storage_class == PseudoClass) &&
11825 (all_images_are_gray == MagickFalse))
11826 {
cristybb503372010-05-27 20:51:26 +000011827 size_t
cristy3ed852e2009-09-05 21:47:34 +000011828 data_length;
11829
11830 /*
11831 Write MNG PLTE chunk
11832 */
11833 data_length=3*image->colors;
11834 (void) WriteBlobMSBULong(image,data_length);
11835 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011836 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011837
cristybb503372010-05-27 20:51:26 +000011838 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011839 {
11840 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11841 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11842 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11843 }
glennrp0fe50b42010-11-16 03:52:51 +000011844
cristy3ed852e2009-09-05 21:47:34 +000011845 (void) WriteBlob(image,data_length+4,chunk);
11846 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11847 mng_info->have_write_global_plte=MagickTrue;
11848 }
11849#endif
11850 }
11851 scene=0;
11852 mng_info->delay=0;
11853#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11854 defined(PNG_MNG_FEATURES_SUPPORTED)
11855 mng_info->equal_palettes=MagickFalse;
11856#endif
11857 do
11858 {
11859 if (mng_info->adjoin)
11860 {
11861#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11862 defined(PNG_MNG_FEATURES_SUPPORTED)
11863 /*
11864 If we aren't using a global palette for the entire MNG, check to
11865 see if we can use one for two or more consecutive images.
11866 */
11867 if (need_local_plte && use_global_plte && !all_images_are_gray)
11868 {
11869 if (mng_info->IsPalette)
11870 {
11871 /*
11872 When equal_palettes is true, this image has the same palette
11873 as the previous PseudoClass image
11874 */
11875 mng_info->have_write_global_plte=mng_info->equal_palettes;
11876 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11877 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11878 {
11879 /*
11880 Write MNG PLTE chunk
11881 */
cristybb503372010-05-27 20:51:26 +000011882 size_t
cristy3ed852e2009-09-05 21:47:34 +000011883 data_length;
11884
11885 data_length=3*image->colors;
11886 (void) WriteBlobMSBULong(image,data_length);
11887 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000011888 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000011889
cristybb503372010-05-27 20:51:26 +000011890 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000011891 {
11892 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11893 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11894 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11895 }
glennrp0fe50b42010-11-16 03:52:51 +000011896
cristy3ed852e2009-09-05 21:47:34 +000011897 (void) WriteBlob(image,data_length+4,chunk);
11898 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11899 (uInt) (data_length+4)));
11900 mng_info->have_write_global_plte=MagickTrue;
11901 }
11902 }
11903 else
11904 mng_info->have_write_global_plte=MagickFalse;
11905 }
11906#endif
11907 if (need_defi)
11908 {
cristybb503372010-05-27 20:51:26 +000011909 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011910 previous_x,
11911 previous_y;
11912
11913 if (scene)
11914 {
11915 previous_x=mng_info->page.x;
11916 previous_y=mng_info->page.y;
11917 }
11918 else
11919 {
11920 previous_x=0;
11921 previous_y=0;
11922 }
11923 mng_info->page=image->page;
11924 if ((mng_info->page.x != previous_x) ||
11925 (mng_info->page.y != previous_y))
11926 {
11927 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11928 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000011929 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000011930 chunk[4]=0; /* object 0 MSB */
11931 chunk[5]=0; /* object 0 LSB */
11932 chunk[6]=0; /* visible */
11933 chunk[7]=0; /* abstract */
11934 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11935 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11936 (void) WriteBlob(image,16,chunk);
11937 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11938 }
11939 }
11940 }
11941
11942 mng_info->write_mng=write_mng;
11943
11944 if ((int) image->dispose >= 3)
11945 mng_info->framing_mode=3;
11946
11947 if (mng_info->need_fram && mng_info->adjoin &&
11948 ((image->delay != mng_info->delay) ||
11949 (mng_info->framing_mode != mng_info->old_framing_mode)))
11950 {
11951 if (image->delay == mng_info->delay)
11952 {
11953 /*
11954 Write a MNG FRAM chunk with the new framing mode.
11955 */
11956 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11957 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011958 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000011959 chunk[4]=(unsigned char) mng_info->framing_mode;
11960 (void) WriteBlob(image,5,chunk);
11961 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11962 }
11963 else
11964 {
11965 /*
11966 Write a MNG FRAM chunk with the delay.
11967 */
11968 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11969 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011970 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000011971 chunk[4]=(unsigned char) mng_info->framing_mode;
11972 chunk[5]=0; /* frame name separator (no name) */
11973 chunk[6]=2; /* flag for changing default delay */
11974 chunk[7]=0; /* flag for changing frame timeout */
11975 chunk[8]=0; /* flag for changing frame clipping */
11976 chunk[9]=0; /* flag for changing frame sync_id */
11977 PNGLong(chunk+10,(png_uint_32)
11978 ((mng_info->ticks_per_second*
11979 image->delay)/MagickMax(image->ticks_per_second,1)));
11980 (void) WriteBlob(image,14,chunk);
11981 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000011982 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000011983 }
11984 mng_info->old_framing_mode=mng_info->framing_mode;
11985 }
11986
11987#if defined(JNG_SUPPORTED)
11988 if (image_info->compression == JPEGCompression)
11989 {
11990 ImageInfo
11991 *write_info;
11992
11993 if (logging != MagickFalse)
11994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11995 " Writing JNG object.");
11996 /* To do: specify the desired alpha compression method. */
11997 write_info=CloneImageInfo(image_info);
11998 write_info->compression=UndefinedCompression;
11999 status=WriteOneJNGImage(mng_info,write_info,image);
12000 write_info=DestroyImageInfo(write_info);
12001 }
12002 else
12003#endif
12004 {
12005 if (logging != MagickFalse)
12006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12007 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012008
glennrpb9cfe272010-12-21 15:08:06 +000012009 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012010 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012011
12012 /* We don't want any ancillary chunks written */
12013 mng_info->ping_exclude_bKGD=MagickTrue;
12014 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012015 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012016 mng_info->ping_exclude_EXIF=MagickTrue;
12017 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012018 mng_info->ping_exclude_iCCP=MagickTrue;
12019 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12020 mng_info->ping_exclude_oFFs=MagickTrue;
12021 mng_info->ping_exclude_pHYs=MagickTrue;
12022 mng_info->ping_exclude_sRGB=MagickTrue;
12023 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012024 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012025 mng_info->ping_exclude_vpAg=MagickTrue;
12026 mng_info->ping_exclude_zCCP=MagickTrue;
12027 mng_info->ping_exclude_zTXt=MagickTrue;
12028
cristy3ed852e2009-09-05 21:47:34 +000012029 status=WriteOnePNGImage(mng_info,image_info,image);
12030 }
12031
12032 if (status == MagickFalse)
12033 {
12034 MngInfoFreeStruct(mng_info,&have_mng_structure);
12035 (void) CloseBlob(image);
12036 return(MagickFalse);
12037 }
12038 (void) CatchImageException(image);
12039 if (GetNextImageInList(image) == (Image *) NULL)
12040 break;
12041 image=SyncNextImageInList(image);
12042 status=SetImageProgress(image,SaveImagesTag,scene++,
12043 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012044
cristy3ed852e2009-09-05 21:47:34 +000012045 if (status == MagickFalse)
12046 break;
glennrp0fe50b42010-11-16 03:52:51 +000012047
cristy3ed852e2009-09-05 21:47:34 +000012048 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012049
cristy3ed852e2009-09-05 21:47:34 +000012050 if (write_mng)
12051 {
12052 while (GetPreviousImageInList(image) != (Image *) NULL)
12053 image=GetPreviousImageInList(image);
12054 /*
12055 Write the MEND chunk.
12056 */
12057 (void) WriteBlobMSBULong(image,0x00000000L);
12058 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012059 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012060 (void) WriteBlob(image,4,chunk);
12061 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12062 }
12063 /*
12064 Relinquish resources.
12065 */
12066 (void) CloseBlob(image);
12067 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012068
cristy3ed852e2009-09-05 21:47:34 +000012069 if (logging != MagickFalse)
12070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012071
cristy3ed852e2009-09-05 21:47:34 +000012072 return(MagickTrue);
12073}
glennrpd5045b42010-03-24 12:40:35 +000012074#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012075
cristy3ed852e2009-09-05 21:47:34 +000012076static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12077{
12078 image=image;
12079 printf("Your PNG library is too old: You have libpng-%s\n",
12080 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012081
cristy3ed852e2009-09-05 21:47:34 +000012082 ThrowBinaryException(CoderError,"PNG library is too old",
12083 image_info->filename);
12084}
glennrp39992b42010-11-14 00:03:43 +000012085
cristy3ed852e2009-09-05 21:47:34 +000012086static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12087{
12088 return(WritePNGImage(image_info,image));
12089}
glennrpd5045b42010-03-24 12:40:35 +000012090#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012091#endif