blob: 0eefea8d6da71b5209a4e09566bb52d239ff88dd [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% %
cristy16af1cb2009-12-11 21:38:29 +000021% Copyright 1999-2010 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"
cristy5a2ca482009-10-14 18:24:56 +000045#include "magick/attribute.h"
cristy3ed852e2009-09-05 21:47:34 +000046#include "magick/blob.h"
47#include "magick/blob-private.h"
48#include "magick/cache.h"
49#include "magick/color.h"
50#include "magick/color-private.h"
cristy4ccd4c02010-04-25 00:43:15 +000051#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000052#include "magick/colorspace.h"
53#include "magick/constitute.h"
54#include "magick/enhance.h"
55#include "magick/exception.h"
56#include "magick/exception-private.h"
57#include "magick/geometry.h"
cristyf2e11662009-10-14 01:24:43 +000058#include "magick/histogram.h"
cristy3ed852e2009-09-05 21:47:34 +000059#include "magick/image.h"
60#include "magick/image-private.h"
61#include "magick/layer.h"
62#include "magick/list.h"
63#include "magick/log.h"
64#include "magick/magick.h"
65#include "magick/memory_.h"
66#include "magick/module.h"
67#include "magick/monitor.h"
68#include "magick/monitor-private.h"
69#include "magick/option.h"
70#include "magick/quantum-private.h"
71#include "magick/profile.h"
72#include "magick/property.h"
73#include "magick/quantize.h"
74#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 */
94/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp286a6352009-11-09 02:58:50 +000095
cristy3ed852e2009-09-05 21:47:34 +000096#include "png.h"
97#include "zlib.h"
98
99/* ImageMagick differences */
100#define first_scene scene
101
glennrpd5045b42010-03-24 12:40:35 +0000102#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000103/*
104 Optional declarations. Define or undefine them as you like.
105*/
106/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
107
108/*
109 Features under construction. Define these to work on them.
110*/
111#undef MNG_OBJECT_BUFFERS
112#undef MNG_BASI_SUPPORTED
113#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
114#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
115#define PNG_BUILD_PALETTE /* This works as of 5.4.3. */
116#define PNG_SORT_PALETTE /* This works as of 5.4.0. */
117#if defined(MAGICKCORE_JPEG_DELEGATE)
118# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
119#endif
120#if !defined(RGBColorMatchExact)
121#define IsPNGColorEqual(color,target) \
122 (((color).red == (target).red) && \
123 ((color).green == (target).green) && \
124 ((color).blue == (target).blue))
125#endif
126
127/*
128 Establish thread safety.
129 setjmp/longjmp is claimed to be safe on these platforms:
130 setjmp/longjmp is alleged to be unsafe on these platforms:
131*/
132#ifndef SETJMP_IS_THREAD_SAFE
133#define PNG_SETJMP_NOT_THREAD_SAFE
134#endif
135
136#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
137static SemaphoreInfo
138 *png_semaphore = (SemaphoreInfo *) NULL;
139#endif
140
141/*
142 This temporary until I set up malloc'ed object attributes array.
143 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
144 waste more memory.
145*/
146#define MNG_MAX_OBJECTS 256
147
148/*
149 If this not defined, spec is interpreted strictly. If it is
150 defined, an attempt will be made to recover from some errors,
151 including
152 o global PLTE too short
153*/
154#undef MNG_LOOSE
155
156/*
157 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
158 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
159 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
160 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
161 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
162 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
163 will be enabled by default in libpng-1.2.0.
164*/
cristy3ed852e2009-09-05 21:47:34 +0000165#ifdef PNG_MNG_FEATURES_SUPPORTED
166# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
167# define PNG_READ_EMPTY_PLTE_SUPPORTED
168# endif
169# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
170# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
171# endif
172#endif
173
174/*
cristybb503372010-05-27 20:51:26 +0000175 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000176 This macro is only defined in libpng-1.0.3 and later.
177 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
178*/
179#ifndef PNG_UINT_31_MAX
180#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
181#endif
182
183/*
184 Constant strings for known chunk types. If you need to add a chunk,
185 add a string holding the name here. To make the code more
186 portable, we use ASCII numbers like this, not characters.
187*/
188
189static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
190static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
191static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
192static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
193static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
194static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
195static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
196static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
197static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
198static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
199static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
200static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
201static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
202static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
203static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
204static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
205static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
206static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
207static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
208static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
209static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
210static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
211static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
212static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
213static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
214static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
215static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
216static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
217static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
218static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
219static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
220static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
221static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
222static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
223
224#if defined(JNG_SUPPORTED)
225static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
226static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
227static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
228static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
229static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
230static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
231#endif
232
233/*
234Other known chunks that are not yet supported by ImageMagick:
235static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
236static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
237static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
238static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
239static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
240static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
241static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
242static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
243*/
244
245typedef struct _MngBox
246{
cristybb503372010-05-27 20:51:26 +0000247 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000248 left,
249 right,
250 top,
251 bottom;
252} MngBox;
253
254typedef struct _MngPair
255{
cristybb503372010-05-27 20:51:26 +0000256 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000257 a,
258 b;
259} MngPair;
260
261#ifdef MNG_OBJECT_BUFFERS
262typedef struct _MngBuffer
263{
264
cristybb503372010-05-27 20:51:26 +0000265 size_t
cristy3ed852e2009-09-05 21:47:34 +0000266 height,
267 width;
268
269 Image
270 *image;
271
272 png_color
273 plte[256];
274
275 int
276 reference_count;
277
278 unsigned char
279 alpha_sample_depth,
280 compression_method,
281 color_type,
282 concrete,
283 filter_method,
284 frozen,
285 image_type,
286 interlace_method,
287 pixel_sample_depth,
288 plte_length,
289 sample_depth,
290 viewable;
291} MngBuffer;
292#endif
293
294typedef struct _MngInfo
295{
296
297#ifdef MNG_OBJECT_BUFFERS
298 MngBuffer
299 *ob[MNG_MAX_OBJECTS];
300#endif
301
302 Image *
303 image;
304
305 RectangleInfo
306 page;
307
308 int
309 adjoin,
310#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
311 bytes_in_read_buffer,
312 found_empty_plte,
313#endif
314 equal_backgrounds,
315 equal_chrms,
316 equal_gammas,
317#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
318 defined(PNG_MNG_FEATURES_SUPPORTED)
319 equal_palettes,
320#endif
321 equal_physs,
322 equal_srgbs,
323 framing_mode,
324 have_global_bkgd,
325 have_global_chrm,
326 have_global_gama,
327 have_global_phys,
328 have_global_sbit,
329 have_global_srgb,
330 have_saved_bkgd_index,
331 have_write_global_chrm,
332 have_write_global_gama,
333 have_write_global_plte,
334 have_write_global_srgb,
335 need_fram,
336 object_id,
337 old_framing_mode,
338 optimize,
339 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
cristybb503372010-05-27 20:51:26 +0000390 size_t
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
400 unsigned int
401 IsPalette,
402 global_phys_unit_type,
403 basi_warning,
404 clon_warning,
405 dhdr_warning,
406 jhdr_warning,
407 magn_warning,
408 past_warning,
409 phyg_warning,
410 phys_warning,
411 sbit_warning,
412 show_warning,
413 mng_type,
414 write_mng,
415 write_png_colortype,
416 write_png_depth,
417 write_png8,
418 write_png24,
419 write_png32;
420
421#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000422 size_t
cristy3ed852e2009-09-05 21:47:34 +0000423 basi_width,
424 basi_height;
425
426 unsigned int
427 basi_depth,
428 basi_color_type,
429 basi_compression_method,
430 basi_filter_type,
431 basi_interlace_method,
432 basi_red,
433 basi_green,
434 basi_blue,
435 basi_alpha,
436 basi_viewable;
437#endif
438
439 png_uint_16
440 magn_first,
441 magn_last,
442 magn_mb,
443 magn_ml,
444 magn_mr,
445 magn_mt,
446 magn_mx,
447 magn_my,
448 magn_methx,
449 magn_methy;
450
451 PixelPacket
452 mng_global_bkgd;
453
454} MngInfo;
455#endif /* VER */
456
457/*
458 Forward declarations.
459*/
460static MagickBooleanType
461 WritePNGImage(const ImageInfo *,Image *);
462static MagickBooleanType
463 WriteMNGImage(const ImageInfo *,Image *);
464#if defined(JNG_SUPPORTED)
465static MagickBooleanType
466 WriteJNGImage(const ImageInfo *,Image *);
467#endif
468
cristybb503372010-05-27 20:51:26 +0000469static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000470{
471 if (x > y)
472 return(x);
473 return(y);
474}
cristybb503372010-05-27 20:51:26 +0000475static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000476{
477 if (x < y)
478 return(x);
479 return(y);
480}
481
glennrpd5045b42010-03-24 12:40:35 +0000482#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000483#if defined(PNG_SORT_PALETTE)
484/*
485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486% %
487% %
488% %
489% C o m p r e s s C o l o r m a p T r a n s F i r s t %
490% %
491% %
492% %
493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
494%
495% CompressColormapTransFirst compresses an image colormap removing
496% any duplicate and unused color entries and putting the transparent colors
497% first. Returns MagickTrue on success, MagickFalse on error.
498%
499% The format of the CompressColormapTransFirst method is:
500%
501% unsigned int CompressColormapTransFirst(Image *image)
502%
503% A description of each parameter follows:
504%
505% o image: the address of a structure of type Image.
glennrp98156a32009-12-09 15:32:44 +0000506% This function updates image->colors and image->colormap.
cristy3ed852e2009-09-05 21:47:34 +0000507%
508*/
509static MagickBooleanType CompressColormapTransFirst(Image *image)
510{
511 int
512 remap_needed,
513 k;
514
cristybb503372010-05-27 20:51:26 +0000515 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000516 j,
517 new_number_colors,
518 number_colors,
519 y;
520
521 PixelPacket
522 *colormap;
523
524 register const IndexPacket
cristy5c6f7892010-05-05 22:53:29 +0000525 *indexes;
cristy3ed852e2009-09-05 21:47:34 +0000526
527 register const PixelPacket
528 *p;
529
530 IndexPacket
531 top_used;
532
cristybb503372010-05-27 20:51:26 +0000533 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000534 i,
535 x;
536
537 IndexPacket
538 *map,
539 *opacity;
540
541 unsigned char
542 *marker,
543 have_transparency;
544
545 /*
546 Determine if colormap can be compressed.
547 */
548 assert(image != (Image *) NULL);
549 assert(image->signature == MagickSignature);
550 if (image->debug != MagickFalse)
glennrp98156a32009-12-09 15:32:44 +0000551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +0000552 " CompressColorMapTransFirst %s (%lu colors)",image->filename,
553 (unsigned long) image->colors);
cristy3ed852e2009-09-05 21:47:34 +0000554 if (image->storage_class != PseudoClass || image->colors > 256 ||
555 image->colors < 2)
glennrp98156a32009-12-09 15:32:44 +0000556 {
557 if (image->debug != MagickFalse)
558 {
559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
560 " Could not compress colormap");
561 if (image->colors > 256 || image->colors == 0)
562 return(MagickFalse);
563 else
564 return(MagickTrue);
565 }
566 }
cristy3ed852e2009-09-05 21:47:34 +0000567 marker=(unsigned char *) AcquireQuantumMemory(image->colors,sizeof(*marker));
568 if (marker == (unsigned char *) NULL)
569 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
570 image->filename);
571 opacity=(IndexPacket *) AcquireQuantumMemory(image->colors,sizeof(*opacity));
572 if (opacity == (IndexPacket *) NULL)
573 {
574 marker=(unsigned char *) RelinquishMagickMemory(marker);
575 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
576 image->filename);
577 }
578 /*
579 Mark colors that are present.
580 */
cristybb503372010-05-27 20:51:26 +0000581 number_colors=(ssize_t) image->colors;
cristy3ed852e2009-09-05 21:47:34 +0000582 for (i=0; i < number_colors; i++)
583 {
584 marker[i]=MagickFalse;
585 opacity[i]=OpaqueOpacity;
586 }
587 top_used=0;
cristybb503372010-05-27 20:51:26 +0000588 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000589 {
590 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
591 if (p == (const PixelPacket *) NULL)
592 break;
cristy5c6f7892010-05-05 22:53:29 +0000593 indexes=GetVirtualIndexQueue(image);
cristy3ed852e2009-09-05 21:47:34 +0000594 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +0000595 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000596 {
cristy5c6f7892010-05-05 22:53:29 +0000597 marker[(int) indexes[x]]=MagickTrue;
598 opacity[(int) indexes[x]]=GetOpacityPixelComponent(p);
599 if (indexes[x] > top_used)
600 top_used=indexes[x];
cristy3ed852e2009-09-05 21:47:34 +0000601 p++;
602 }
603 else
cristybb503372010-05-27 20:51:26 +0000604 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000605 {
cristy5c6f7892010-05-05 22:53:29 +0000606 marker[(int) indexes[x]]=MagickTrue;
607 if (indexes[x] > top_used)
608 top_used=indexes[x];
cristy3ed852e2009-09-05 21:47:34 +0000609 }
610 }
611
612 if (image->matte != MagickFalse)
613 {
614 /*
615 Mark background color, topmost occurrence if more than one.
616 */
617 for (i=number_colors-1; i; i--)
618 {
619 if (IsColorEqual(image->colormap+i,&image->background_color))
620 {
621 marker[i]=MagickTrue;
622 break;
623 }
624 }
625 }
626 /*
627 Unmark duplicates.
628 */
629 for (i=0; i < number_colors-1; i++)
630 if (marker[i])
631 {
632 for (j=i+1; j < number_colors; j++)
633 if ((opacity[i] == opacity[j]) &&
634 (IsColorEqual(image->colormap+i,image->colormap+j)))
635 marker[j]=MagickFalse;
636 }
637 /*
638 Count colors that still remain.
639 */
640 have_transparency=MagickFalse;
641 new_number_colors=0;
642 for (i=0; i < number_colors; i++)
643 if (marker[i])
644 {
645 new_number_colors++;
646 if (opacity[i] != OpaqueOpacity)
647 have_transparency=MagickTrue;
648 }
649 if ((!have_transparency || (marker[0] &&
650 (opacity[0] == (Quantum) TransparentOpacity)))
651 && (new_number_colors == number_colors))
652 {
653 /*
654 No duplicate or unused entries, and transparency-swap not needed.
655 */
656 marker=(unsigned char *) RelinquishMagickMemory(marker);
657 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
658 return(MagickTrue);
659 }
660
661 remap_needed=MagickFalse;
cristybb503372010-05-27 20:51:26 +0000662 if ((ssize_t) top_used >= new_number_colors)
cristy3ed852e2009-09-05 21:47:34 +0000663 remap_needed=MagickTrue;
664
665 /*
666 Compress colormap.
667 */
668
669 colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
670 sizeof(*colormap));
671 if (colormap == (PixelPacket *) NULL)
672 {
673 marker=(unsigned char *) RelinquishMagickMemory(marker);
674 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
675 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
676 image->filename);
677 }
678 /*
679 Eliminate unused colormap entries.
680 */
681 map=(IndexPacket *) AcquireQuantumMemory((size_t) number_colors,
682 sizeof(*map));
683 if (map == (IndexPacket *) NULL)
684 {
685 marker=(unsigned char *) RelinquishMagickMemory(marker);
686 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
687 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
688 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
689 image->filename);
690 }
691 k=0;
692 for (i=0; i < number_colors; i++)
693 {
694 map[i]=(IndexPacket) k;
695 if (marker[i])
696 {
697 for (j=i+1; j < number_colors; j++)
698 {
699 if ((opacity[i] == opacity[j]) &&
700 (IsColorEqual(image->colormap+i,image->colormap+j)))
701 {
702 map[j]=(IndexPacket) k;
703 marker[j]=MagickFalse;
704 }
705 }
706 k++;
707 }
708 }
709 j=0;
710 for (i=0; i < number_colors; i++)
711 {
712 if (marker[i])
713 {
714 colormap[j]=image->colormap[i];
715 j++;
716 }
717 }
718 if (have_transparency && (opacity[0] != (Quantum) TransparentOpacity))
719 {
720 /*
721 Move the first transparent color to palette entry 0.
722 */
723 for (i=1; i < number_colors; i++)
724 {
725 if (marker[i] && opacity[i] == (Quantum) TransparentOpacity)
726 {
727 PixelPacket
728 temp_colormap;
729
730 temp_colormap=colormap[0];
731 colormap[0]=colormap[(int) map[i]];
cristybb503372010-05-27 20:51:26 +0000732 colormap[(ssize_t) map[i]]=temp_colormap;
cristy3ed852e2009-09-05 21:47:34 +0000733 for (j=0; j < number_colors; j++)
734 {
735 if (map[j] == 0)
736 map[j]=map[i];
737 else if (map[j] == map[i])
738 map[j]=0;
739 }
740 remap_needed=MagickTrue;
741 break;
742 }
743 }
744 }
745
746 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
747 marker=(unsigned char *) RelinquishMagickMemory(marker);
748
749 if (remap_needed)
750 {
751 ExceptionInfo
752 *exception;
753
754 register IndexPacket
755 *pixels;
756
757 register PixelPacket
758 *q;
759
760 /*
761 Remap pixels.
762 */
763 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +0000764 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000765 {
766 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
767 if (q == (PixelPacket *) NULL)
768 break;
769 pixels=GetAuthenticIndexQueue(image);
cristybb503372010-05-27 20:51:26 +0000770 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000771 {
772 j=(int) pixels[x];
773 pixels[x]=map[j];
774 }
775 if (SyncAuthenticPixels(image,exception) == MagickFalse)
776 break;
777 }
778 for (i=0; i < new_number_colors; i++)
779 image->colormap[i]=colormap[i];
780 }
781 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
cristybb503372010-05-27 20:51:26 +0000782 image->colors=(size_t) new_number_colors;
cristy3ed852e2009-09-05 21:47:34 +0000783 map=(IndexPacket *) RelinquishMagickMemory(map);
784 return(MagickTrue);
785}
786#endif
787
788/*
789%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
790% %
791% %
792% %
793% I m a g e I s G r a y %
794% %
795% %
796% %
797%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798% %
799% Like IsGrayImage except does not change DirectClass to PseudoClass %
800% %
801%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
802*/
803static MagickBooleanType ImageIsGray(Image *image)
804{
805 register const PixelPacket
806 *p;
807
cristybb503372010-05-27 20:51:26 +0000808 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000809 i,
810 x,
811 y;
812
813 assert(image != (Image *) NULL);
814 assert(image->signature == MagickSignature);
815 if (image->debug != MagickFalse)
816 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
817
818 if (image->storage_class == PseudoClass)
819 {
cristybb503372010-05-27 20:51:26 +0000820 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000821 if (IsGray(image->colormap+i) == MagickFalse)
822 return(MagickFalse);
823 return(MagickTrue);
824 }
cristybb503372010-05-27 20:51:26 +0000825 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000826 {
827 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
828 if (p == (const PixelPacket *) NULL)
829 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000830 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +0000831 {
832 if (IsGray(p) == MagickFalse)
833 return(MagickFalse);
834 p++;
835 }
836 }
837 return(MagickTrue);
838}
839
840/*
841%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
842% %
843% %
844% %
845% I m a g e I s M o n o c h r o m e %
846% %
847% %
848% %
849%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
850% %
851% Like IsMonochromeImage except does not change DirectClass to PseudoClass %
852% and is more accurate. %
853% %
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855*/
856static MagickBooleanType ImageIsMonochrome(Image *image)
857{
858 register const PixelPacket
859 *p;
860
cristybb503372010-05-27 20:51:26 +0000861 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000862 i,
863 x,
864 y;
865
866 assert(image != (Image *) NULL);
867 assert(image->signature == MagickSignature);
868 if (image->debug != MagickFalse)
869 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
870 if (image->storage_class == PseudoClass)
871 {
cristybb503372010-05-27 20:51:26 +0000872 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000873 {
874 if ((IsGray(image->colormap+i) == MagickFalse) ||
875 ((image->colormap[i].red != 0) &&
876 (image->colormap[i].red != (Quantum) QuantumRange)))
877 return(MagickFalse);
878 }
879 return(MagickTrue);
880 }
cristybb503372010-05-27 20:51:26 +0000881 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000882 {
883 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
884 if (p == (const PixelPacket *) NULL)
885 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000886 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +0000887 {
888 if ((p->red != 0) && (p->red != (Quantum) QuantumRange))
889 return(MagickFalse);
890 if (IsGray(p) == MagickFalse)
891 return(MagickFalse);
892 if ((p->opacity != OpaqueOpacity) &&
893 (p->opacity != (Quantum) TransparentOpacity))
894 return(MagickFalse);
895 p++;
896 }
897 }
898 return(MagickTrue);
899}
glennrpd5045b42010-03-24 12:40:35 +0000900#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +0000901#endif /* MAGICKCORE_PNG_DELEGATE */
902
903/*
904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
905% %
906% %
907% %
908% I s M N G %
909% %
910% %
911% %
912%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
913%
914% IsMNG() returns MagickTrue if the image format type, identified by the
915% magick string, is MNG.
916%
917% The format of the IsMNG method is:
918%
919% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
920%
921% A description of each parameter follows:
922%
923% o magick: compare image format pattern against these bytes.
924%
925% o length: Specifies the length of the magick string.
926%
927%
928*/
929static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
930{
931 if (length < 8)
932 return(MagickFalse);
933 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
934 return(MagickTrue);
935 return(MagickFalse);
936}
937
938/*
939%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
940% %
941% %
942% %
943% I s J N G %
944% %
945% %
946% %
947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
948%
949% IsJNG() returns MagickTrue if the image format type, identified by the
950% magick string, is JNG.
951%
952% The format of the IsJNG method is:
953%
954% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
955%
956% A description of each parameter follows:
957%
958% o magick: compare image format pattern against these bytes.
959%
960% o length: Specifies the length of the magick string.
961%
962%
963*/
964static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
965{
966 if (length < 8)
967 return(MagickFalse);
968 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
969 return(MagickTrue);
970 return(MagickFalse);
971}
972
973/*
974%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
975% %
976% %
977% %
978% I s P N G %
979% %
980% %
981% %
982%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
983%
984% IsPNG() returns MagickTrue if the image format type, identified by the
985% magick string, is PNG.
986%
987% The format of the IsPNG method is:
988%
989% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
990%
991% A description of each parameter follows:
992%
993% o magick: compare image format pattern against these bytes.
994%
995% o length: Specifies the length of the magick string.
996%
997*/
998static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
999{
1000 if (length < 8)
1001 return(MagickFalse);
1002 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1003 return(MagickTrue);
1004 return(MagickFalse);
1005}
1006
1007#if defined(MAGICKCORE_PNG_DELEGATE)
1008#if defined(__cplusplus) || defined(c_plusplus)
1009extern "C" {
1010#endif
1011
glennrpd5045b42010-03-24 12:40:35 +00001012#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001013static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001014{
1015 unsigned char
1016 buffer[4];
1017
1018 assert(image != (Image *) NULL);
1019 assert(image->signature == MagickSignature);
1020 buffer[0]=(unsigned char) (value >> 24);
1021 buffer[1]=(unsigned char) (value >> 16);
1022 buffer[2]=(unsigned char) (value >> 8);
1023 buffer[3]=(unsigned char) value;
1024 return((size_t) WriteBlob(image,4,buffer));
1025}
1026
1027static void PNGLong(png_bytep p,png_uint_32 value)
1028{
1029 *p++=(png_byte) ((value >> 24) & 0xff);
1030 *p++=(png_byte) ((value >> 16) & 0xff);
1031 *p++=(png_byte) ((value >> 8) & 0xff);
1032 *p++=(png_byte) (value & 0xff);
1033}
1034
1035static void PNGsLong(png_bytep p,png_int_32 value)
1036{
1037 *p++=(png_byte) ((value >> 24) & 0xff);
1038 *p++=(png_byte) ((value >> 16) & 0xff);
1039 *p++=(png_byte) ((value >> 8) & 0xff);
1040 *p++=(png_byte) (value & 0xff);
1041}
1042
1043static void PNGShort(png_bytep p,png_uint_16 value)
1044{
1045 *p++=(png_byte) ((value >> 8) & 0xff);
1046 *p++=(png_byte) (value & 0xff);
1047}
1048
1049static void PNGType(png_bytep p,png_bytep type)
1050{
1051 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1052}
1053
1054static void LogPNGChunk(int logging, png_bytep type, size_t length)
1055{
1056 if (logging != MagickFalse)
1057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1058 " Writing %c%c%c%c chunk, length: %lu",
cristyf2faecf2010-05-28 19:19:36 +00001059 type[0],type[1],type[2],type[3],(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00001060}
glennrpd5045b42010-03-24 12:40:35 +00001061#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001062
1063#if defined(__cplusplus) || defined(c_plusplus)
1064}
1065#endif
1066
glennrpd5045b42010-03-24 12:40:35 +00001067#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001068/*
1069%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1070% %
1071% %
1072% %
1073% R e a d P N G I m a g e %
1074% %
1075% %
1076% %
1077%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1078%
1079% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1080% Multiple-image Network Graphics (MNG) image file and returns it. It
1081% allocates the memory necessary for the new Image structure and returns a
1082% pointer to the new image or set of images.
1083%
1084% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1085%
1086% The format of the ReadPNGImage method is:
1087%
1088% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1089%
1090% A description of each parameter follows:
1091%
1092% o image_info: the image info.
1093%
1094% o exception: return any errors or warnings in this structure.
1095%
1096% To do, more or less in chronological order (as of version 5.5.2,
1097% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1098%
1099% Get 16-bit cheap transparency working.
1100%
1101% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1102%
1103% Preserve all unknown and not-yet-handled known chunks found in input
1104% PNG file and copy them into output PNG files according to the PNG
1105% copying rules.
1106%
1107% (At this point, PNG encoding should be in full MNG compliance)
1108%
1109% Provide options for choice of background to use when the MNG BACK
1110% chunk is not present or is not mandatory (i.e., leave transparent,
1111% user specified, MNG BACK, PNG bKGD)
1112%
1113% Implement LOOP/ENDL [done, but could do discretionary loops more
1114% efficiently by linking in the duplicate frames.].
1115%
1116% Decode and act on the MHDR simplicity profile (offer option to reject
1117% files or attempt to process them anyway when the profile isn't LC or VLC).
1118%
1119% Upgrade to full MNG without Delta-PNG.
1120%
1121% o BACK [done a while ago except for background image ID]
1122% o MOVE [done 15 May 1999]
1123% o CLIP [done 15 May 1999]
1124% o DISC [done 19 May 1999]
1125% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1126% o SEEK [partially done 19 May 1999 (discard function only)]
1127% o SHOW
1128% o PAST
1129% o BASI
1130% o MNG-level tEXt/iTXt/zTXt
1131% o pHYg
1132% o pHYs
1133% o sBIT
1134% o bKGD
1135% o iTXt (wait for libpng implementation).
1136%
1137% Use the scene signature to discover when an identical scene is
1138% being reused, and just point to the original image->exception instead
1139% of storing another set of pixels. This not specific to MNG
1140% but could be applied generally.
1141%
1142% Upgrade to full MNG with Delta-PNG.
1143%
1144% JNG tEXt/iTXt/zTXt
1145%
1146% We will not attempt to read files containing the CgBI chunk.
1147% They are really Xcode files meant for display on the iPhone.
1148% These are not valid PNG files and it is impossible to recover
1149% the orginal PNG from files that have been converted to Xcode-PNG,
1150% since irretrievable loss of color data has occurred due to the
1151% use of premultiplied alpha.
1152*/
1153
1154#if defined(__cplusplus) || defined(c_plusplus)
1155extern "C" {
1156#endif
1157
1158/*
1159 This the function that does the actual reading of data. It is
1160 the same as the one supplied in libpng, except that it receives the
1161 datastream from the ReadBlob() function instead of standard input.
1162*/
1163static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1164{
1165 Image
1166 *image;
1167
1168 image=(Image *) png_get_io_ptr(png_ptr);
1169 if (length)
1170 {
1171 png_size_t
1172 check;
1173
1174 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1175 if (check != length)
1176 {
1177 char
1178 msg[MaxTextExtent];
1179
1180 (void) FormatMagickString(msg,MaxTextExtent,
cristyf2faecf2010-05-28 19:19:36 +00001181 "Expected %lu bytes; found %lu bytes",(unsigned long) length,
1182 (unsigned long) check);
cristy3ed852e2009-09-05 21:47:34 +00001183 png_warning(png_ptr,msg);
1184 png_error(png_ptr,"Read Exception");
1185 }
1186 }
1187}
1188
1189#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1190 !defined(PNG_MNG_FEATURES_SUPPORTED)
1191/* We use mng_get_data() instead of png_get_data() if we have a libpng
1192 * older than libpng-1.0.3a, which was the first to allow the empty
1193 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1194 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1195 * encountered after an empty PLTE, so we have to look ahead for bKGD
1196 * chunks and remove them from the datastream that is passed to libpng,
1197 * and store their contents for later use.
1198 */
1199static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1200{
1201 MngInfo
1202 *mng_info;
1203
1204 Image
1205 *image;
1206
1207 png_size_t
1208 check;
1209
cristybb503372010-05-27 20:51:26 +00001210 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001211 i;
1212
1213 i=0;
1214 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1215 image=(Image *) mng_info->image;
1216 while (mng_info->bytes_in_read_buffer && length)
1217 {
1218 data[i]=mng_info->read_buffer[i];
1219 mng_info->bytes_in_read_buffer--;
1220 length--;
1221 i++;
1222 }
1223 if (length)
1224 {
1225 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1226 if (check != length)
1227 png_error(png_ptr,"Read Exception");
1228 if (length == 4)
1229 {
1230 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1231 (data[3] == 0))
1232 {
1233 check=(png_size_t) ReadBlob(image,(size_t) length,
1234 (char *) mng_info->read_buffer);
1235 mng_info->read_buffer[4]=0;
1236 mng_info->bytes_in_read_buffer=4;
1237 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1238 mng_info->found_empty_plte=MagickTrue;
1239 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1240 {
1241 mng_info->found_empty_plte=MagickFalse;
1242 mng_info->have_saved_bkgd_index=MagickFalse;
1243 }
1244 }
1245 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1246 (data[3] == 1))
1247 {
1248 check=(png_size_t) ReadBlob(image,(size_t) length,
1249 (char *) mng_info->read_buffer);
1250 mng_info->read_buffer[4]=0;
1251 mng_info->bytes_in_read_buffer=4;
1252 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1253 if (mng_info->found_empty_plte)
1254 {
1255 /*
1256 Skip the bKGD data byte and CRC.
1257 */
1258 check=(png_size_t)
1259 ReadBlob(image,5,(char *) mng_info->read_buffer);
1260 check=(png_size_t) ReadBlob(image,(size_t) length,
1261 (char *) mng_info->read_buffer);
1262 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1263 mng_info->have_saved_bkgd_index=MagickTrue;
1264 mng_info->bytes_in_read_buffer=0;
1265 }
1266 }
1267 }
1268 }
1269}
1270#endif
1271
1272static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1273{
1274 Image
1275 *image;
1276
1277 image=(Image *) png_get_io_ptr(png_ptr);
1278 if (length)
1279 {
1280 png_size_t
1281 check;
1282
cristybb503372010-05-27 20:51:26 +00001283 check=(png_size_t) WriteBlob(image,(size_t) length,data);
cristy3ed852e2009-09-05 21:47:34 +00001284 if (check != length)
1285 png_error(png_ptr,"WriteBlob Failed");
1286 }
1287}
1288
1289static void png_flush_data(png_structp png_ptr)
1290{
1291 (void) png_ptr;
1292}
1293
1294#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1295static int PalettesAreEqual(Image *a,Image *b)
1296{
cristybb503372010-05-27 20:51:26 +00001297 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001298 i;
1299
1300 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1301 return((int) MagickFalse);
1302 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1303 return((int) MagickFalse);
1304 if (a->colors != b->colors)
1305 return((int) MagickFalse);
cristybb503372010-05-27 20:51:26 +00001306 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001307 {
1308 if ((a->colormap[i].red != b->colormap[i].red) ||
1309 (a->colormap[i].green != b->colormap[i].green) ||
1310 (a->colormap[i].blue != b->colormap[i].blue))
1311 return((int) MagickFalse);
1312 }
1313 return((int) MagickTrue);
1314}
1315#endif
1316
1317static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1318{
1319 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1320 mng_info->exists[i] && !mng_info->frozen[i])
1321 {
1322#ifdef MNG_OBJECT_BUFFERS
1323 if (mng_info->ob[i] != (MngBuffer *) NULL)
1324 {
1325 if (mng_info->ob[i]->reference_count > 0)
1326 mng_info->ob[i]->reference_count--;
1327 if (mng_info->ob[i]->reference_count == 0)
1328 {
1329 if (mng_info->ob[i]->image != (Image *) NULL)
1330 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1331 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1332 }
1333 }
1334 mng_info->ob[i]=(MngBuffer *) NULL;
1335#endif
1336 mng_info->exists[i]=MagickFalse;
1337 mng_info->invisible[i]=MagickFalse;
1338 mng_info->viewable[i]=MagickFalse;
1339 mng_info->frozen[i]=MagickFalse;
1340 mng_info->x_off[i]=0;
1341 mng_info->y_off[i]=0;
1342 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001343 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001344 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001345 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001346 }
1347}
1348
1349static void MngInfoFreeStruct(MngInfo *mng_info,int *have_mng_structure)
1350{
1351 if (*have_mng_structure && (mng_info != (MngInfo *) NULL))
1352 {
cristybb503372010-05-27 20:51:26 +00001353 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001354 i;
1355
1356 for (i=1; i < MNG_MAX_OBJECTS; i++)
1357 MngInfoDiscardObject(mng_info,i);
1358 if (mng_info->global_plte != (png_colorp) NULL)
1359 mng_info->global_plte=(png_colorp)
1360 RelinquishMagickMemory(mng_info->global_plte);
1361 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1362 *have_mng_structure=MagickFalse;
1363 }
1364}
1365
1366static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1367{
1368 MngBox
1369 box;
1370
1371 box=box1;
1372 if (box.left < box2.left)
1373 box.left=box2.left;
1374 if (box.top < box2.top)
1375 box.top=box2.top;
1376 if (box.right > box2.right)
1377 box.right=box2.right;
1378 if (box.bottom > box2.bottom)
1379 box.bottom=box2.bottom;
1380 return box;
1381}
1382
1383static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1384{
1385 MngBox
1386 box;
1387
1388 /*
1389 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1390 */
cristybb503372010-05-27 20:51:26 +00001391 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1392 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1393 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1394 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001395 if (delta_type != 0)
1396 {
1397 box.left+=previous_box.left;
1398 box.right+=previous_box.right;
1399 box.top+=previous_box.top;
1400 box.bottom+=previous_box.bottom;
1401 }
1402 return(box);
1403}
1404
1405static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1406 unsigned char *p)
1407{
1408 MngPair
1409 pair;
1410 /*
cristybb503372010-05-27 20:51:26 +00001411 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001412 */
cristybb503372010-05-27 20:51:26 +00001413 pair.a=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1414 pair.b=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
cristy3ed852e2009-09-05 21:47:34 +00001415 if (delta_type != 0)
1416 {
1417 pair.a+=previous_pair.a;
1418 pair.b+=previous_pair.b;
1419 }
1420 return(pair);
1421}
1422
cristybb503372010-05-27 20:51:26 +00001423static ssize_t mng_get_ssize_t(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001424{
cristybb503372010-05-27 20:51:26 +00001425 return((ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001426}
1427
1428static void PNGErrorHandler(png_struct *ping,png_const_charp message)
1429{
1430 Image
1431 *image;
1432
1433 image=(Image *) png_get_error_ptr(ping);
1434 if (image->debug != MagickFalse)
1435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1436 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1437 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1438 message,"`%s'",image->filename);
glennrpfaa852b2010-03-30 12:17:00 +00001439#if PNG_LIBPNG_VER < 10500
cristy3ed852e2009-09-05 21:47:34 +00001440 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001441#else
1442 png_longjmp(ping,1);
1443#endif
cristy3ed852e2009-09-05 21:47:34 +00001444}
1445
1446static void PNGWarningHandler(png_struct *ping,png_const_charp message)
1447{
1448 Image
1449 *image;
1450
1451 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1452 png_error(ping, message);
1453 image=(Image *) png_get_error_ptr(ping);
1454 if (image->debug != MagickFalse)
1455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001456 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
cristy3ed852e2009-09-05 21:47:34 +00001457 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1458 message,"`%s'",image->filename);
1459}
1460
1461#ifdef PNG_USER_MEM_SUPPORTED
1462static png_voidp png_IM_malloc(png_structp png_ptr,png_uint_32 size)
1463{
1464#if (PNG_LIBPNG_VER < 10011)
1465 png_voidp
1466 ret;
1467
1468 png_ptr=png_ptr;
1469 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1470 if (ret == NULL)
1471 png_error("Insufficient memory.");
1472 return(ret);
1473#else
1474 png_ptr=png_ptr;
1475 return((png_voidp) AcquireMagickMemory((size_t) size));
1476#endif
1477}
1478
1479/*
1480 Free a pointer. It is removed from the list at the same time.
1481*/
1482static png_free_ptr png_IM_free(png_structp png_ptr,png_voidp ptr)
1483{
1484 png_ptr=png_ptr;
1485 ptr=RelinquishMagickMemory(ptr);
1486 return((png_free_ptr) NULL);
1487}
1488#endif
1489
1490#if defined(__cplusplus) || defined(c_plusplus)
1491}
1492#endif
1493
1494static int
1495png_read_raw_profile(Image *image, const ImageInfo *image_info,
1496 png_textp text,int ii)
1497{
cristybb503372010-05-27 20:51:26 +00001498 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001499 i;
1500
1501 register unsigned char
1502 *dp;
1503
1504 register png_charp
1505 sp;
1506
1507 png_uint_32
1508 length,
1509 nibbles;
1510
1511 StringInfo
1512 *profile;
1513
1514 unsigned char
1515 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1516 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1517 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1518 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1519 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1520 13,14,15};
1521
1522 sp=text[ii].text+1;
1523 /* look for newline */
1524 while (*sp != '\n')
1525 sp++;
1526 /* look for length */
1527 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1528 sp++;
cristyf2f27272009-12-17 14:48:46 +00001529 length=(png_uint_32) StringToLong(sp);
cristy3ed852e2009-09-05 21:47:34 +00001530 while (*sp != ' ' && *sp != '\n')
1531 sp++;
1532 /* allocate space */
1533 if (length == 0)
1534 {
1535 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1536 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1537 return(MagickFalse);
1538 }
1539 profile=AcquireStringInfo(length);
1540 if (profile == (StringInfo *) NULL)
1541 {
1542 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1543 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1544 "unable to copy profile");
1545 return(MagickFalse);
1546 }
1547 /* copy profile, skipping white space and column 1 "=" signs */
1548 dp=GetStringInfoDatum(profile);
1549 nibbles=length*2;
cristybb503372010-05-27 20:51:26 +00001550 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001551 {
1552 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1553 {
1554 if (*sp == '\0')
1555 {
1556 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1557 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1558 profile=DestroyStringInfo(profile);
1559 return(MagickFalse);
1560 }
1561 sp++;
1562 }
1563 if (i%2 == 0)
1564 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1565 else
1566 (*dp++)+=unhex[(int) *sp++];
1567 }
1568 /*
1569 We have already read "Raw profile type.
1570 */
1571 (void) SetImageProfile(image,&text[ii].key[17],profile);
1572 profile=DestroyStringInfo(profile);
1573 if (image_info->verbose)
1574 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1575 return MagickTrue;
1576}
1577
1578#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1579static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1580{
1581 Image
1582 *image;
1583
1584
1585 /* The unknown chunk structure contains the chunk data:
1586 png_byte name[5];
1587 png_byte *data;
1588 png_size_t size;
1589
1590 Note that libpng has already taken care of the CRC handling.
1591 */
1592
1593
1594 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1595 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1596 return(0); /* Did not recognize */
1597
1598 /* recognized vpAg */
1599
1600 if (chunk->size != 9)
1601 return(-1); /* Error return */
1602
1603 if (chunk->data[8] != 0)
1604 return(0); /* ImageMagick requires pixel units */
1605
1606 image=(Image *) png_get_user_chunk_ptr(ping);
1607
cristybb503372010-05-27 20:51:26 +00001608 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001609 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
cristybb503372010-05-27 20:51:26 +00001610 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001611 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1612
1613 /* Return one of the following: */
1614 /* return(-n); chunk had an error */
1615 /* return(0); did not recognize */
1616 /* return(n); success */
1617
1618 return(1);
1619
1620}
1621#endif
1622
1623/*
1624%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625% %
1626% %
1627% %
1628% R e a d O n e P N G I m a g e %
1629% %
1630% %
1631% %
1632%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633%
1634% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1635% (minus the 8-byte signature) and returns it. It allocates the memory
1636% necessary for the new Image structure and returns a pointer to the new
1637% image.
1638%
1639% The format of the ReadOnePNGImage method is:
1640%
1641% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1642% ExceptionInfo *exception)
1643%
1644% A description of each parameter follows:
1645%
1646% o mng_info: Specifies a pointer to a MngInfo structure.
1647%
1648% o image_info: the image info.
1649%
1650% o exception: return any errors or warnings in this structure.
1651%
1652*/
1653static Image *ReadOnePNGImage(MngInfo *mng_info,
1654 const ImageInfo *image_info, ExceptionInfo *exception)
1655{
1656 /* Read one PNG image */
1657
1658 Image
1659 *image;
1660
1661 int
1662 logging,
1663 num_text,
1664 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001665 pass,
1666 ping_bit_depth,
1667 ping_color_type,
1668 ping_interlace_method,
1669 ping_compression_method,
1670 ping_filter_method,
1671 ping_num_trans;
cristy3ed852e2009-09-05 21:47:34 +00001672
1673 MagickBooleanType
1674 status;
1675
1676 PixelPacket
1677 transparent_color;
1678
glennrpfaa852b2010-03-30 12:17:00 +00001679 png_bytep
1680 ping_trans_alpha;
1681
1682 png_color_16p
1683 ping_background,
1684 ping_trans_color;
1685
cristy3ed852e2009-09-05 21:47:34 +00001686 png_info
1687 *end_info,
1688 *ping_info;
1689
1690 png_struct
1691 *ping;
1692
1693 png_textp
1694 text;
1695
glennrpfaa852b2010-03-30 12:17:00 +00001696 png_uint_32
1697 ping_height,
1698 ping_width,
1699 ping_rowbytes;
1700
cristy3ed852e2009-09-05 21:47:34 +00001701 QuantumInfo
1702 *quantum_info;
1703
1704 unsigned char
1705 *png_pixels;
1706
cristybb503372010-05-27 20:51:26 +00001707 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001708 y;
1709
1710 register unsigned char
1711 *p;
1712
1713 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00001714 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00001715
cristybb503372010-05-27 20:51:26 +00001716 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001717 i,
1718 x;
1719
1720 register PixelPacket
1721 *q;
1722
1723 size_t
1724 length;
1725
cristybb503372010-05-27 20:51:26 +00001726 size_t
cristy3ed852e2009-09-05 21:47:34 +00001727 row_offset;
1728
1729#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1730 png_byte unused_chunks[]=
1731 {
1732 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1733 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1734 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1735 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1736 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1737 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1738 };
1739#endif
1740
1741 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1742 " enter ReadOnePNGImage()");
1743
1744#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00001745 LockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001746#endif
1747
glennrp25c1e2b2010-03-25 01:39:56 +00001748#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00001749 if (image_info->verbose)
1750 printf("Your PNG library (libpng-%s) is rather old.\n",
1751 PNG_LIBPNG_VER_STRING);
1752#endif
1753
glennrp61b4c952009-11-10 20:40:41 +00001754#if (PNG_LIBPNG_VER >= 10400)
1755# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1756 if (image_info->verbose)
1757 {
1758 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1759 PNG_LIBPNG_VER_STRING);
1760 printf("Please update it.\n");
1761 }
1762# endif
1763#endif
1764
1765
cristyed552522009-10-16 14:04:35 +00001766 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00001767 image=mng_info->image;
1768
1769 /*
1770 Allocate the PNG structures
1771 */
1772#ifdef PNG_USER_MEM_SUPPORTED
1773 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1774 PNGErrorHandler,PNGWarningHandler, NULL,
1775 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
1776#else
1777 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1778 PNGErrorHandler,PNGWarningHandler);
1779#endif
1780 if (ping == (png_struct *) NULL)
1781 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1782 ping_info=png_create_info_struct(ping);
1783 if (ping_info == (png_info *) NULL)
1784 {
1785 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1786 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1787 }
1788 end_info=png_create_info_struct(ping);
1789 if (end_info == (png_info *) NULL)
1790 {
1791 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1792 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1793 }
1794 png_pixels=(unsigned char *) NULL;
glennrpfaa852b2010-03-30 12:17:00 +00001795 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00001796 {
1797 /*
1798 PNG image is corrupt.
1799 */
1800 png_destroy_read_struct(&ping,&ping_info,&end_info);
1801#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00001802 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001803#endif
1804 if (logging != MagickFalse)
1805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1806 " exit ReadOnePNGImage() with error.");
1807 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00001808 {
1809 InheritException(exception,&image->exception);
1810 image->columns=0;
1811 }
cristy3ed852e2009-09-05 21:47:34 +00001812 return(GetFirstImageInList(image));
1813 }
1814 /*
1815 Prepare PNG for reading.
1816 */
glennrpfaa852b2010-03-30 12:17:00 +00001817
cristy3ed852e2009-09-05 21:47:34 +00001818 mng_info->image_found++;
1819 png_set_sig_bytes(ping,8);
1820 if (LocaleCompare(image_info->magick,"MNG") == 0)
1821 {
1822#if defined(PNG_MNG_FEATURES_SUPPORTED)
1823 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1824 png_set_read_fn(ping,image,png_get_data);
1825#else
1826#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1827 png_permit_empty_plte(ping,MagickTrue);
1828 png_set_read_fn(ping,image,png_get_data);
1829#else
1830 mng_info->image=image;
1831 mng_info->bytes_in_read_buffer=0;
1832 mng_info->found_empty_plte=MagickFalse;
1833 mng_info->have_saved_bkgd_index=MagickFalse;
1834 png_set_read_fn(ping,mng_info,mng_get_data);
1835#endif
1836#endif
1837 }
1838 else
1839 png_set_read_fn(ping,image,png_get_data);
1840
1841#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1842 /* Ignore unused chunks and all unknown chunks except for vpAg */
1843 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1844 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1845 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1846 (int)sizeof(unused_chunks)/5);
1847 /* Callback for other unknown chunks */
1848 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1849#endif
1850
glennrp991e92a2010-01-28 03:09:00 +00001851#if (PNG_LIBPNG_VER < 10400)
1852# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1853 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00001854 /* Disable thread-unsafe features of pnggccrd */
1855 if (png_access_version_number() >= 10200)
1856 {
1857 png_uint_32 mmx_disable_mask=0;
1858 png_uint_32 asm_flags;
1859
1860 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1861 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1862 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1863 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1864 asm_flags=png_get_asm_flags(ping);
1865 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1866 }
glennrp991e92a2010-01-28 03:09:00 +00001867# endif
cristy3ed852e2009-09-05 21:47:34 +00001868#endif
1869
1870 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00001871
1872 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1873 &ping_bit_depth,&ping_color_type,
1874 &ping_interlace_method,&ping_compression_method,
1875 &ping_filter_method);
1876
1877 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1878 &ping_trans_color);
1879
1880 (void) png_get_bKGD(ping, ping_info, &ping_background);
1881
1882 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00001883 {
glennrpfaa852b2010-03-30 12:17:00 +00001884 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1885 {
1886 png_set_packing(ping);
1887 ping_bit_depth = 8;
1888 }
cristy3ed852e2009-09-05 21:47:34 +00001889 }
glennrpfaa852b2010-03-30 12:17:00 +00001890
1891 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00001892 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00001893 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00001894 if (logging != MagickFalse)
1895 {
1896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1897 " PNG width: %lu, height: %lu",
cristyf2faecf2010-05-28 19:19:36 +00001898 (unsigned long) ping_width, (unsigned long) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00001899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1900 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001901 ping_color_type, ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00001902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1903 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001904 ping_compression_method);
cristy3ed852e2009-09-05 21:47:34 +00001905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1906 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00001907 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00001908 }
1909
glennrpfaa852b2010-03-30 12:17:00 +00001910#ifdef PNG_READ_iCCP_SUPPORTED
1911 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00001912 {
1913 int
1914 compression;
1915
1916 png_charp
1917 info,
1918 name;
1919
1920 png_uint_32
1921 profile_length;
1922
1923 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1924 &profile_length);
1925 if (profile_length != 0)
1926 {
1927 StringInfo
1928 *profile;
1929
1930 if (logging != MagickFalse)
1931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1932 " Reading PNG iCCP chunk.");
1933 profile=AcquireStringInfo(profile_length);
1934 SetStringInfoDatum(profile,(const unsigned char *) info);
1935 (void) SetImageProfile(image,"icc",profile);
1936 profile=DestroyStringInfo(profile);
1937 }
1938 }
1939#endif
1940#if defined(PNG_READ_sRGB_SUPPORTED)
1941 {
1942 int
1943 intent;
1944
1945 if (mng_info->have_global_srgb)
1946 image->rendering_intent=(RenderingIntent)
1947 (mng_info->global_srgb_intent+1);
1948 if (png_get_sRGB(ping,ping_info,&intent))
1949 {
1950 image->rendering_intent=(RenderingIntent) (intent+1);
1951 if (logging != MagickFalse)
1952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1953 " Reading PNG sRGB chunk: rendering_intent: %d",intent+1);
1954 }
1955 }
1956#endif
1957 {
1958 double
1959 file_gamma;
1960
glennrpfaa852b2010-03-30 12:17:00 +00001961 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1962 if (mng_info->have_global_gama)
1963 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
cristy3ed852e2009-09-05 21:47:34 +00001964 if (png_get_gAMA(ping,ping_info,&file_gamma))
1965 {
1966 image->gamma=(float) file_gamma;
1967 if (logging != MagickFalse)
1968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1969 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1970 }
1971 }
glennrpfaa852b2010-03-30 12:17:00 +00001972 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1973 {
1974 if (mng_info->have_global_chrm != MagickFalse)
1975 {
1976 (void) png_set_cHRM(ping,ping_info,
1977 mng_info->global_chrm.white_point.x,
1978 mng_info->global_chrm.white_point.y,
1979 mng_info->global_chrm.red_primary.x,
1980 mng_info->global_chrm.red_primary.y,
1981 mng_info->global_chrm.green_primary.x,
1982 mng_info->global_chrm.green_primary.y,
1983 mng_info->global_chrm.blue_primary.x,
1984 mng_info->global_chrm.blue_primary.y);
1985 }
1986 }
1987 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00001988 {
1989 (void) png_get_cHRM(ping,ping_info,
1990 &image->chromaticity.white_point.x,
1991 &image->chromaticity.white_point.y,
1992 &image->chromaticity.red_primary.x,
1993 &image->chromaticity.red_primary.y,
1994 &image->chromaticity.green_primary.x,
1995 &image->chromaticity.green_primary.y,
1996 &image->chromaticity.blue_primary.x,
1997 &image->chromaticity.blue_primary.y);
1998 if (logging != MagickFalse)
1999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2000 " Reading PNG cHRM chunk.");
2001 }
2002 if (image->rendering_intent)
2003 {
glennrpfaa852b2010-03-30 12:17:00 +00002004 png_set_sRGB(ping,ping_info,image->rendering_intent-1);
2005 png_set_gAMA(ping,ping_info,0.45455f);
2006 png_set_cHRM(ping,ping_info,
2007 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2008 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002009 }
cristy3ed852e2009-09-05 21:47:34 +00002010#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002011 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002012 {
2013 image->page.x=png_get_x_offset_pixels(ping, ping_info);
2014 image->page.y=png_get_y_offset_pixels(ping, ping_info);
2015 if (logging != MagickFalse)
2016 if (image->page.x || image->page.y)
2017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00002018 " Reading PNG oFFs chunk: x: %ld, y: %ld.",(long) image->page.x,
2019 (long) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002020 }
2021#endif
2022#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002023 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2024 {
2025 if (mng_info->have_global_phys)
2026 {
2027 png_set_pHYs(ping,ping_info,
2028 mng_info->global_x_pixels_per_unit,
2029 mng_info->global_y_pixels_per_unit,
2030 mng_info->global_phys_unit_type);
2031 }
2032 }
2033
2034 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002035 {
2036 int
2037 unit_type;
2038
2039 png_uint_32
2040 x_resolution,
2041 y_resolution;
2042
2043 /*
2044 Set image resolution.
2045 */
2046 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002047 &unit_type);
2048 image->x_resolution=(double) x_resolution;
2049 image->y_resolution=(double) y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00002050 if (unit_type == PNG_RESOLUTION_METER)
2051 {
2052 image->units=PixelsPerCentimeterResolution;
2053 image->x_resolution=(double) x_resolution/100.0;
2054 image->y_resolution=(double) y_resolution/100.0;
2055 }
2056 if (logging != MagickFalse)
2057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2058 " Reading PNG pHYs chunk: xres: %lu, yres: %lu, units: %d.",
cristyf2faecf2010-05-28 19:19:36 +00002059 (unsigned long) x_resolution,(unsigned long) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002060 }
cristy3ed852e2009-09-05 21:47:34 +00002061#endif
glennrpfaa852b2010-03-30 12:17:00 +00002062 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002063 {
2064 int
2065 number_colors;
2066
2067 png_colorp
2068 palette;
2069
2070 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2071 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002072 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002073 {
2074 if (mng_info->global_plte_length)
2075 {
2076 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2077 (int) mng_info->global_plte_length);
glennrpfaa852b2010-03-30 12:17:00 +00002078 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002079 if (mng_info->global_trns_length)
2080 {
2081 if (mng_info->global_trns_length >
2082 mng_info->global_plte_length)
2083 (void) ThrowMagickException(&image->exception,
2084 GetMagickModule(),CoderError,
2085 "global tRNS has more entries than global PLTE",
2086 "`%s'",image_info->filename);
2087 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2088 (int) mng_info->global_trns_length,NULL);
2089 }
2090#if defined(PNG_READ_bKGD_SUPPORTED)
2091 if (
2092#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2093 mng_info->have_saved_bkgd_index ||
2094#endif
glennrpfaa852b2010-03-30 12:17:00 +00002095 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002096 {
2097 png_color_16
2098 background;
2099
2100#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2101 if (mng_info->have_saved_bkgd_index)
2102 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002103#endif
glennrpfaa852b2010-03-30 12:17:00 +00002104 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2105 background.index=ping_background->index;
cristy3ed852e2009-09-05 21:47:34 +00002106 background.red=(png_uint_16)
2107 mng_info->global_plte[background.index].red;
2108 background.green=(png_uint_16)
2109 mng_info->global_plte[background.index].green;
2110 background.blue=(png_uint_16)
2111 mng_info->global_plte[background.index].blue;
2112 png_set_bKGD(ping,ping_info,&background);
2113 }
2114#endif
2115 }
2116 else
2117 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2118 CoderError,"No global PLTE in file","`%s'",
2119 image_info->filename);
2120 }
2121 }
2122
2123#if defined(PNG_READ_bKGD_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002124 if (mng_info->have_global_bkgd &&
2125 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002126 image->background_color=mng_info->mng_global_bkgd;
glennrpfaa852b2010-03-30 12:17:00 +00002127 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002128 {
2129 /*
2130 Set image background color.
2131 */
2132 if (logging != MagickFalse)
2133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2134 " Reading PNG bKGD chunk.");
glennrpfaa852b2010-03-30 12:17:00 +00002135 if (ping_bit_depth <= MAGICKCORE_QUANTUM_DEPTH)
cristy3ed852e2009-09-05 21:47:34 +00002136 {
glennrpfaa852b2010-03-30 12:17:00 +00002137 image->background_color.red=ping_background->red;
2138 image->background_color.green=ping_background->green;
2139 image->background_color.blue=ping_background->blue;
cristy3ed852e2009-09-05 21:47:34 +00002140 }
2141 else
2142 {
2143 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002144 ScaleShortToQuantum(ping_background->red);
cristy3ed852e2009-09-05 21:47:34 +00002145 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002146 ScaleShortToQuantum(ping_background->green);
cristy3ed852e2009-09-05 21:47:34 +00002147 image->background_color.blue=
glennrpfaa852b2010-03-30 12:17:00 +00002148 ScaleShortToQuantum(ping_background->blue);
cristy3ed852e2009-09-05 21:47:34 +00002149 }
2150 }
2151#endif
2152 transparent_color.red=0;
2153 transparent_color.green=0;
2154 transparent_color.blue=0;
2155 transparent_color.opacity=0;
glennrpfaa852b2010-03-30 12:17:00 +00002156 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002157 {
2158 /*
2159 Image has a transparent background.
2160 */
2161 int
2162 max_sample;
2163
2164 if (logging != MagickFalse)
2165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2166 " Reading PNG tRNS chunk.");
2167
glennrpfaa852b2010-03-30 12:17:00 +00002168 max_sample = (1 << ping_bit_depth) - 1;
cristy3ed852e2009-09-05 21:47:34 +00002169
glennrpfaa852b2010-03-30 12:17:00 +00002170 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2171 (int)ping_trans_color->gray > max_sample) ||
2172 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2173 ((int)ping_trans_color->red > max_sample ||
2174 (int)ping_trans_color->green > max_sample ||
2175 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002176 {
2177 if (logging != MagickFalse)
2178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2179 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002180 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002181 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002182 image->matte=MagickFalse;
2183 }
2184 else
2185 {
glennrpfaa852b2010-03-30 12:17:00 +00002186 transparent_color.red= (Quantum)(ping_trans_color->red);
2187 transparent_color.green= (Quantum) (ping_trans_color->green);
2188 transparent_color.blue= (Quantum) (ping_trans_color->blue);
2189 transparent_color.opacity= (Quantum) (ping_trans_color->gray);
2190 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002191 {
glennrpfaa852b2010-03-30 12:17:00 +00002192 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002193 {
2194 transparent_color.opacity=(Quantum) (((
glennrpfaa852b2010-03-30 12:17:00 +00002195 ping_trans_color->gray)*255)/max_sample);
cristy3ed852e2009-09-05 21:47:34 +00002196 }
2197 transparent_color.red=transparent_color.opacity;
2198 transparent_color.green=transparent_color.opacity;
2199 transparent_color.blue=transparent_color.opacity;
2200 }
2201 }
2202 }
2203#if defined(PNG_READ_sBIT_SUPPORTED)
2204 if (mng_info->have_global_sbit)
2205 {
glennrpfaa852b2010-03-30 12:17:00 +00002206 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002207 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2208 }
2209#endif
2210 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002211
cristy3ed852e2009-09-05 21:47:34 +00002212 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002213
2214 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2215
cristy3ed852e2009-09-05 21:47:34 +00002216 /*
2217 Initialize image structure.
2218 */
2219 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002220 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002221 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002222 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002223 if (mng_info->mng_type == 0)
2224 {
glennrpfaa852b2010-03-30 12:17:00 +00002225 mng_info->mng_width=ping_width;
2226 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002227 mng_info->frame=mng_info->image_box;
2228 mng_info->clip=mng_info->image_box;
2229 }
2230 else
2231 {
2232 image->page.y=mng_info->y_off[mng_info->object_id];
2233 }
2234 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002235 image->columns=ping_width;
2236 image->rows=ping_height;
2237 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2238 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2239 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002240 {
2241 image->storage_class=PseudoClass;
glennrpfaa852b2010-03-30 12:17:00 +00002242 image->colors=1UL << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002243#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2244 if (image->colors > 256)
2245 image->colors=256;
2246#else
2247 if (image->colors > 65536L)
2248 image->colors=65536L;
2249#endif
glennrpfaa852b2010-03-30 12:17:00 +00002250 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002251 {
2252 int
2253 number_colors;
2254
2255 png_colorp
2256 palette;
2257
2258 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002259 image->colors=(size_t) number_colors;
cristy3ed852e2009-09-05 21:47:34 +00002260 if (logging != MagickFalse)
2261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2262 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2263 }
2264 }
2265
2266 if (image->storage_class == PseudoClass)
2267 {
2268 /*
2269 Initialize image colormap.
2270 */
2271 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2272 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrpfaa852b2010-03-30 12:17:00 +00002273 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002274 {
2275 int
2276 number_colors;
2277
2278 png_colorp
2279 palette;
2280
2281 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002282 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002283 {
2284 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2285 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2286 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2287 }
2288 }
2289 else
2290 {
cristybb503372010-05-27 20:51:26 +00002291 size_t
cristy3ed852e2009-09-05 21:47:34 +00002292 scale;
2293
glennrpfaa852b2010-03-30 12:17:00 +00002294 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
cristy3ed852e2009-09-05 21:47:34 +00002295 if (scale < 1)
2296 scale=1;
cristybb503372010-05-27 20:51:26 +00002297 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002298 {
2299 image->colormap[i].red=(Quantum) (i*scale);
2300 image->colormap[i].green=(Quantum) (i*scale);
2301 image->colormap[i].blue=(Quantum) (i*scale);
2302 }
2303 }
2304 }
2305 /*
2306 Read image scanlines.
2307 */
2308 if (image->delay != 0)
2309 mng_info->scenes_found++;
cristybb503372010-05-27 20:51:26 +00002310 if ((image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00002311 (image_info->first_scene+image_info->number_scenes)))
2312 {
2313 if (logging != MagickFalse)
2314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00002315 " Skipping PNG image data for scene %ld",(long)
cristy3ed852e2009-09-05 21:47:34 +00002316 mng_info->scenes_found-1);
2317 png_destroy_read_struct(&ping,&ping_info,&end_info);
2318#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00002319 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002320#endif
2321 if (logging != MagickFalse)
2322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2323 " exit ReadOnePNGImage().");
2324 return(image);
2325 }
2326 if (logging != MagickFalse)
2327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2328 " Reading PNG IDAT chunk(s)");
2329 if (num_passes > 1)
2330 png_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
glennrpfaa852b2010-03-30 12:17:00 +00002331 ping_rowbytes*sizeof(*png_pixels));
cristy3ed852e2009-09-05 21:47:34 +00002332 else
glennrpfaa852b2010-03-30 12:17:00 +00002333 png_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002334 sizeof(*png_pixels));
2335 if (png_pixels == (unsigned char *) NULL)
2336 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2337 if (logging != MagickFalse)
2338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2339 " Converting PNG pixels to pixel packets");
2340 /*
2341 Convert PNG pixels to pixel packets.
2342 */
glennrpfaa852b2010-03-30 12:17:00 +00002343 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002344 {
2345 /*
2346 PNG image is corrupt.
2347 */
2348 png_destroy_read_struct(&ping,&ping_info,&end_info);
2349#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00002350 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002351#endif
2352 if (quantum_info != (QuantumInfo *) NULL)
2353 quantum_info = DestroyQuantumInfo(quantum_info);
2354 if (png_pixels != (unsigned char *) NULL)
2355 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2356 if (logging != MagickFalse)
2357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2358 " exit ReadOnePNGImage() with error.");
2359 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002360 {
2361 InheritException(exception,&image->exception);
2362 image->columns=0;
2363 }
cristy3ed852e2009-09-05 21:47:34 +00002364 return(GetFirstImageInList(image));
2365 }
cristyed552522009-10-16 14:04:35 +00002366 quantum_info=AcquireQuantumInfo(image_info,image);
2367 if (quantum_info == (QuantumInfo *) NULL)
2368 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00002369 if (image->storage_class == DirectClass)
2370 for (pass=0; pass < num_passes; pass++)
2371 {
2372 /*
2373 Convert image to DirectClass pixel packets.
2374 */
2375#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2376 int
2377 depth;
2378
cristybb503372010-05-27 20:51:26 +00002379 depth=(ssize_t) ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002380#endif
glennrpfaa852b2010-03-30 12:17:00 +00002381 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2382 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2383 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2384 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002385
cristybb503372010-05-27 20:51:26 +00002386 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002387 {
2388 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002389 row_offset=ping_rowbytes*y;
cristy3ed852e2009-09-05 21:47:34 +00002390 else
2391 row_offset=0;
2392 png_read_row(ping,png_pixels+row_offset,NULL);
cristyabc8f402009-09-19 01:45:58 +00002393 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00002394 if (q == (PixelPacket *) NULL)
2395 break;
2396#if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2397 if (depth == 16)
2398 {
2399 register Quantum
2400 *p,
2401 *r;
2402
2403 r=png_pixels+row_offset;
2404 p=r;
glennrpfaa852b2010-03-30 12:17:00 +00002405 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002406 {
cristybb503372010-05-27 20:51:26 +00002407 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002408 {
2409 *r++=*p++;
2410 p++;
glennrpfaa852b2010-03-30 12:17:00 +00002411 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS)) &&
cristy3ed852e2009-09-05 21:47:34 +00002412 (((*(p-2) << 8)|*(p-1)) == transparent_color.opacity))
2413 {
2414 /* Cheap transparency */
2415 *r++=TransparentOpacity;
2416 }
2417 else
2418 *r++=OpaqueOpacity;
2419 }
2420 }
glennrpfaa852b2010-03-30 12:17:00 +00002421 else if (ping_color_type == PNG_COLOR_TYPE_RGB)
cristy3ed852e2009-09-05 21:47:34 +00002422 {
glennrpfaa852b2010-03-30 12:17:00 +00002423 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristybb503372010-05-27 20:51:26 +00002424 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002425 {
2426 *r++=*p++;
2427 p++;
2428 *r++=*p++;
2429 p++;
2430 *r++=*p++;
2431 p++;
2432 if ((((*(p-6) << 8)|*(p-5)) == transparent_color.red) &&
2433 (((*(p-4) << 8)|*(p-3)) == transparent_color.green) &&
2434 (((*(p-2) << 8)|*(p-1)) == transparent_color.blue))
2435 {
2436 /* Cheap transparency */
2437 *r++=TransparentOpacity;
2438 }
2439 else
2440 *r++=OpaqueOpacity;
2441 }
2442 else
cristybb503372010-05-27 20:51:26 +00002443 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002444 {
2445 *r++=*p++;
2446 p++;
2447 *r++=*p++;
2448 p++;
2449 *r++=*p++;
2450 p++;
2451 *r++=OpaqueOpacity;
2452 }
2453 }
glennrpfaa852b2010-03-30 12:17:00 +00002454 else if (ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristybb503372010-05-27 20:51:26 +00002455 for (x=(ssize_t) (4*image->columns); x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002456 {
2457 *r++=*p++;
2458 p++;
2459 }
glennrpfaa852b2010-03-30 12:17:00 +00002460 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
cristybb503372010-05-27 20:51:26 +00002461 for (x=(ssize_t) (2*image->columns); x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002462 {
2463 *r++=*p++;
2464 p++;
2465 }
2466 }
glennrpfaa852b2010-03-30 12:17:00 +00002467 if (depth == 8 && ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002468 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2469 GrayQuantum,png_pixels+row_offset);
glennrpfaa852b2010-03-30 12:17:00 +00002470 if (ping_color_type == PNG_COLOR_TYPE_GRAY ||
2471 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00002472 {
2473 quantum_info->depth=8;
2474 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2475 GrayAlphaQuantum,png_pixels+row_offset);
2476 }
glennrpfaa852b2010-03-30 12:17:00 +00002477 else if (depth == 8 && ping_color_type == PNG_COLOR_TYPE_RGB)
cristy3ed852e2009-09-05 21:47:34 +00002478 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2479 RGBQuantum,png_pixels+row_offset);
glennrpfaa852b2010-03-30 12:17:00 +00002480 else if (ping_color_type == PNG_COLOR_TYPE_RGB ||
2481 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00002482 {
2483 quantum_info->depth=8;
2484 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2485 RGBAQuantum,png_pixels+row_offset);
2486 }
glennrpfaa852b2010-03-30 12:17:00 +00002487 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002488 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2489 IndexQuantum,png_pixels+row_offset);
2490#else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
glennrpfaa852b2010-03-30 12:17:00 +00002491 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002492 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2493 GrayQuantum,png_pixels+row_offset,exception);
glennrpfaa852b2010-03-30 12:17:00 +00002494 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00002495 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2496 GrayAlphaQuantum,png_pixels+row_offset,exception);
glennrpfaa852b2010-03-30 12:17:00 +00002497 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00002498 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2499 RGBAQuantum,png_pixels+row_offset,exception);
glennrpfaa852b2010-03-30 12:17:00 +00002500 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002501 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2502 IndexQuantum,png_pixels+row_offset,exception);
2503 else
2504 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2505 RGBQuantum,png_pixels+row_offset,exception);
2506#endif
cristy7a287bf2010-02-14 02:18:09 +00002507 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2508 {
cristycee97112010-05-28 00:44:52 +00002509 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2510 image->rows);
cristy7a287bf2010-02-14 02:18:09 +00002511 if (status == MagickFalse)
2512 break;
2513 }
cristy3ed852e2009-09-05 21:47:34 +00002514 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2515 break;
2516 }
cristy7a287bf2010-02-14 02:18:09 +00002517 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002518 {
2519 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2520 if (status == MagickFalse)
2521 break;
2522 }
2523 }
2524 else /* image->storage_class != DirectClass */
2525 for (pass=0; pass < num_passes; pass++)
2526 {
2527 Quantum
2528 *quantum_scanline;
2529
2530 register Quantum
2531 *r;
2532
2533 /*
2534 Convert grayscale image to PseudoClass pixel packets.
2535 */
glennrpfaa852b2010-03-30 12:17:00 +00002536 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002537 MagickTrue : MagickFalse;
2538 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2539 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2540 if (quantum_scanline == (Quantum *) NULL)
2541 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00002542 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002543 {
2544 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002545 row_offset=ping_rowbytes*y;
cristy3ed852e2009-09-05 21:47:34 +00002546 else
2547 row_offset=0;
2548 png_read_row(ping,png_pixels+row_offset,NULL);
2549 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2550 if (q == (PixelPacket *) NULL)
2551 break;
cristy5c6f7892010-05-05 22:53:29 +00002552 indexes=GetAuthenticIndexQueue(image);
cristy3ed852e2009-09-05 21:47:34 +00002553 p=png_pixels+row_offset;
2554 r=quantum_scanline;
glennrpfaa852b2010-03-30 12:17:00 +00002555 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00002556 {
2557 case 1:
2558 {
cristybb503372010-05-27 20:51:26 +00002559 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002560 bit;
2561
cristybb503372010-05-27 20:51:26 +00002562 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00002563 {
2564 for (bit=7; bit >= 0; bit--)
2565 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2566 p++;
2567 }
2568 if ((image->columns % 8) != 0)
2569 {
cristybb503372010-05-27 20:51:26 +00002570 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
cristy3ed852e2009-09-05 21:47:34 +00002571 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2572 }
2573 break;
2574 }
2575 case 2:
2576 {
cristybb503372010-05-27 20:51:26 +00002577 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002578 {
2579 *r++=(*p >> 6) & 0x03;
2580 *r++=(*p >> 4) & 0x03;
2581 *r++=(*p >> 2) & 0x03;
2582 *r++=(*p++) & 0x03;
2583 }
2584 if ((image->columns % 4) != 0)
2585 {
cristybb503372010-05-27 20:51:26 +00002586 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
cristy3ed852e2009-09-05 21:47:34 +00002587 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2588 }
2589 break;
2590 }
2591 case 4:
2592 {
cristybb503372010-05-27 20:51:26 +00002593 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00002594 {
2595 *r++=(*p >> 4) & 0x0f;
2596 *r++=(*p++) & 0x0f;
2597 }
2598 if ((image->columns % 2) != 0)
2599 *r++=(*p++ >> 4) & 0x0f;
2600 break;
2601 }
2602 case 8:
2603 {
glennrpfaa852b2010-03-30 12:17:00 +00002604 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00002605 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002606 {
2607 *r++=*p++;
2608 /* In image.h, OpaqueOpacity is 0
2609 * TransparentOpacity is QuantumRange
2610 * In a PNG datastream, Opaque is QuantumRange
2611 * and Transparent is 0.
2612 */
2613 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2614 q++;
2615 }
2616 else
cristybb503372010-05-27 20:51:26 +00002617 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002618 *r++=*p++;
2619 break;
2620 }
2621 case 16:
2622 {
cristybb503372010-05-27 20:51:26 +00002623 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002624 {
2625#if (MAGICKCORE_QUANTUM_DEPTH == 16)
cristybb503372010-05-27 20:51:26 +00002626 size_t
cristy3ed852e2009-09-05 21:47:34 +00002627 quantum;
2628
2629 if (image->colors > 256)
2630 *r=((*p++) << 8);
2631 else
2632 *r=0;
2633 quantum=(*r);
2634 quantum|=(*p++);
2635 *r=(Quantum) quantum;
2636 r++;
glennrpfaa852b2010-03-30 12:17:00 +00002637 if (ping_color_type == 4)
cristy3ed852e2009-09-05 21:47:34 +00002638 {
2639 quantum=((*p++) << 8);
2640 quantum|=(*p++);
2641 q->opacity=(Quantum) (QuantumRange-quantum);
2642 q++;
2643 }
2644#else
2645#if (MAGICKCORE_QUANTUM_DEPTH == 32)
cristybb503372010-05-27 20:51:26 +00002646 size_t
cristy3ed852e2009-09-05 21:47:34 +00002647 quantum;
2648
2649 if (image->colors > 256)
2650 *r=((*p++) << 8);
2651 else
2652 *r=0;
2653 quantum=(*r);
2654 quantum|=(*p++);
2655 *r=quantum;
2656 r++;
glennrpfaa852b2010-03-30 12:17:00 +00002657 if (ping_color_type == 4)
cristy3ed852e2009-09-05 21:47:34 +00002658 {
2659 q->opacity=(*p << 8) | *(p+1);
2660 q->opacity*=65537L;
cristy46f08202010-01-10 04:04:21 +00002661 q->opacity=(Quantum) GetAlphaPixelComponent(q);
cristy3ed852e2009-09-05 21:47:34 +00002662 p+=2;
2663 q++;
2664 }
2665#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2666 *r++=(*p++);
2667 p++; /* strip low byte */
glennrpfaa852b2010-03-30 12:17:00 +00002668 if (ping_color_type == 4)
cristy3ed852e2009-09-05 21:47:34 +00002669 {
2670 q->opacity=(Quantum) (QuantumRange-(*p++));
2671 p++;
2672 q++;
2673 }
2674#endif
2675#endif
2676 }
2677 break;
2678 }
2679 default:
2680 break;
2681 }
2682 /*
2683 Transfer image scanline.
2684 */
2685 r=quantum_scanline;
cristybb503372010-05-27 20:51:26 +00002686 for (x=0; x < (ssize_t) image->columns; x++)
cristy80ac8b92010-05-08 13:36:57 +00002687 indexes[x]=(IndexPacket) (*r++);
cristy3ed852e2009-09-05 21:47:34 +00002688 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2689 break;
cristy7a287bf2010-02-14 02:18:09 +00002690 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2691 {
cristycee97112010-05-28 00:44:52 +00002692 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2693 image->rows);
cristy7a287bf2010-02-14 02:18:09 +00002694 if (status == MagickFalse)
2695 break;
2696 }
cristy3ed852e2009-09-05 21:47:34 +00002697 }
cristy7a287bf2010-02-14 02:18:09 +00002698 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002699 {
2700 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2701 if (status == MagickFalse)
2702 break;
2703 }
2704 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2705 }
cristyb32b90a2009-09-07 21:45:48 +00002706 if (quantum_info != (QuantumInfo *) NULL)
2707 quantum_info=DestroyQuantumInfo(quantum_info);
cristy5c6f7892010-05-05 22:53:29 +00002708 if (image->storage_class == PseudoClass)
2709 {
cristyaeb2cbc2010-05-07 13:28:58 +00002710 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002711 matte;
2712
2713 matte=image->matte;
2714 image->matte=MagickFalse;
2715 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002716 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002717 }
cristy3ed852e2009-09-05 21:47:34 +00002718 png_read_end(ping,ping_info);
2719
2720 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002721 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002722 {
2723 png_destroy_read_struct(&ping,&ping_info,&end_info);
2724 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2725 image->colors=2;
2726 (void) SetImageBackgroundColor(image);
2727#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00002728 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002729#endif
2730 if (logging != MagickFalse)
2731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2732 " exit ReadOnePNGImage() early.");
2733 return(image);
2734 }
glennrpfaa852b2010-03-30 12:17:00 +00002735 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002736 {
2737 ClassType
2738 storage_class;
2739
2740 /*
2741 Image has a transparent background.
2742 */
2743 storage_class=image->storage_class;
2744 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002745
cristye805a642010-04-21 00:48:31 +00002746#if 1 /* balfour fix */
glennrpc11cf6a2010-03-20 16:46:19 +00002747/* From imagemagick discourse server, 5 Feb 2010 */
2748
2749 if (storage_class == PseudoClass)
2750 {
glennrpfaa852b2010-03-30 12:17:00 +00002751 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002752 {
glennrpfaa852b2010-03-30 12:17:00 +00002753 for (x=0; x < ping_num_trans; x++)
glennrpc11cf6a2010-03-20 16:46:19 +00002754 {
glennrpfaa852b2010-03-30 12:17:00 +00002755 image->colormap[x].opacity = ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
glennrpc11cf6a2010-03-20 16:46:19 +00002756 }
2757 }
glennrpfaa852b2010-03-30 12:17:00 +00002758 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrpc11cf6a2010-03-20 16:46:19 +00002759 {
glennrp5af765f2010-03-30 11:12:18 +00002760 for (x=0; x < (int) image->colors; x++)
glennrpc11cf6a2010-03-20 16:46:19 +00002761 {
2762 if (image->colormap[x].red == transparent_color.opacity)
2763 {
2764 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2765 }
2766 }
2767 }
cristyd0272592010-04-21 01:01:49 +00002768 (void) SyncImage(image);
glennrpc11cf6a2010-03-20 16:46:19 +00002769 }
2770 else
2771 {
2772
cristybb503372010-05-27 20:51:26 +00002773 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002774 {
2775 image->storage_class=storage_class;
2776 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2777 if (q == (PixelPacket *) NULL)
2778 break;
cristy5c6f7892010-05-05 22:53:29 +00002779 indexes=GetAuthenticIndexQueue(image);
glennrpc11cf6a2010-03-20 16:46:19 +00002780
cristybb503372010-05-27 20:51:26 +00002781 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpc11cf6a2010-03-20 16:46:19 +00002782 {
2783 if (ScaleQuantumToChar(q->red) == transparent_color.red &&
2784 ScaleQuantumToChar(q->green) == transparent_color.green &&
2785 ScaleQuantumToChar(q->blue) == transparent_color.blue)
2786 q->opacity=(Quantum) TransparentOpacity;
2787 else
2788 SetOpacityPixelComponent(q,OpaqueOpacity);
2789 q++;
2790 }
2791 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2792 break;
2793 }
2794 }
2795
2796#else /* not balfour */
2797
2798
cristybb503372010-05-27 20:51:26 +00002799 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002800 {
2801 image->storage_class=storage_class;
2802 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2803 if (q == (PixelPacket *) NULL)
2804 break;
cristy5c6f7892010-05-05 22:53:29 +00002805 indexes=GetAuthenticIndexQueue(image);
cristy3ed852e2009-09-05 21:47:34 +00002806
2807 if (storage_class == PseudoClass)
2808 {
2809 IndexPacket
2810 indexpacket;
2811
glennrpfaa852b2010-03-30 12:17:00 +00002812 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00002813 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002814 {
cristy5c6f7892010-05-05 22:53:29 +00002815 indexpacket=indexes[x];
glennrpfaa852b2010-03-30 12:17:00 +00002816 if (indexpacket < ping_num_trans)
cristy3ed852e2009-09-05 21:47:34 +00002817 q->opacity=ScaleCharToQuantum((unsigned char)
cristybb503372010-05-27 20:51:26 +00002818 (255-ping_trans_alpha[(ssize_t) indexpacket]));
cristy3ed852e2009-09-05 21:47:34 +00002819 else
cristyce70c172010-01-07 17:15:30 +00002820 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00002821 q++;
2822 }
glennrpfaa852b2010-03-30 12:17:00 +00002823 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristybb503372010-05-27 20:51:26 +00002824 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002825 {
cristy5c6f7892010-05-05 22:53:29 +00002826 indexpacket=indexes[x];
cristybb503372010-05-27 20:51:26 +00002827 q->red=image->colormap[(ssize_t) indexpacket].red;
cristy3ed852e2009-09-05 21:47:34 +00002828 q->green=q->red;
2829 q->blue=q->red;
2830 if (q->red == transparent_color.opacity)
2831 q->opacity=(Quantum) TransparentOpacity;
2832 else
cristyce70c172010-01-07 17:15:30 +00002833 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00002834 q++;
2835 }
2836 }
2837 else
cristybb503372010-05-27 20:51:26 +00002838 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002839 {
2840 if (ScaleQuantumToChar(q->red) == transparent_color.red &&
2841 ScaleQuantumToChar(q->green) == transparent_color.green &&
2842 ScaleQuantumToChar(q->blue) == transparent_color.blue)
2843 q->opacity=(Quantum) TransparentOpacity;
2844 else
cristyce70c172010-01-07 17:15:30 +00002845 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00002846 q++;
2847 }
2848 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2849 break;
2850 }
glennrpc11cf6a2010-03-20 16:46:19 +00002851#endif /* not balfour */
cristy3ed852e2009-09-05 21:47:34 +00002852 image->storage_class=DirectClass;
2853 }
2854#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2855 if (image->depth > 8)
2856 image->depth=8;
2857#endif
2858 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
cristybb503372010-05-27 20:51:26 +00002859 for (i=0; i < (ssize_t) num_text; i++)
cristy3ed852e2009-09-05 21:47:34 +00002860 {
2861 /* Check for a profile */
2862
2863 if (logging != MagickFalse)
2864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2865 " Reading PNG text chunk");
2866 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2867 (void) png_read_raw_profile(image,image_info,text,(int) i);
2868 else
2869 {
2870 char
2871 *value;
2872
2873 length=text[i].text_length;
2874 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2875 sizeof(*value));
2876 if (value == (char *) NULL)
2877 {
2878 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2879 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2880 image->filename);
2881 break;
2882 }
2883 *value='\0';
2884 (void) ConcatenateMagickString(value,text[i].text,length+2);
2885 (void) SetImageProperty(image,text[i].key,value);
2886 if (logging != MagickFalse)
2887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2888 " Keyword: %s",text[i].key);
2889 value=DestroyString(value);
2890 }
2891 }
2892#ifdef MNG_OBJECT_BUFFERS
2893 /*
2894 Store the object if necessary.
2895 */
2896 if (object_id && !mng_info->frozen[object_id])
2897 {
2898 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2899 {
2900 /*
2901 create a new object buffer.
2902 */
2903 mng_info->ob[object_id]=(MngBuffer *)
cristy90823212009-12-12 20:48:33 +00002904 AcquireAlignedMemory(1,sizeof(MngBuffer));
cristy3ed852e2009-09-05 21:47:34 +00002905 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2906 {
2907 mng_info->ob[object_id]->image=(Image *) NULL;
2908 mng_info->ob[object_id]->reference_count=1;
2909 }
2910 }
2911 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2912 mng_info->ob[object_id]->frozen)
2913 {
2914 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2915 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2916 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2917 image->filename);
2918 if (mng_info->ob[object_id]->frozen)
2919 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2920 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2921 "`%s'",image->filename);
2922 }
2923 else
2924 {
cristy3ed852e2009-09-05 21:47:34 +00002925
2926 if (mng_info->ob[object_id]->image != (Image *) NULL)
2927 mng_info->ob[object_id]->image=DestroyImage
2928 (mng_info->ob[object_id]->image);
2929 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2930 &image->exception);
2931 if (mng_info->ob[object_id]->image != (Image *) NULL)
2932 mng_info->ob[object_id]->image->file=(FILE *) NULL;
2933 else
2934 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2935 ResourceLimitError,"Cloning image for object buffer failed",
2936 "`%s'",image->filename);
glennrpfaa852b2010-03-30 12:17:00 +00002937 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00002938 png_error(ping,"PNG Image dimensions are too large.");
glennrpfaa852b2010-03-30 12:17:00 +00002939 mng_info->ob[object_id]->width=ping_width;
2940 mng_info->ob[object_id]->height=ping_height;
2941 mng_info->ob[object_id]->color_type=ping_color_type;
2942 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
2943 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
2944 mng_info->ob[object_id]->compression_method=
2945 ping_compression_method;
2946 mng_info->ob[object_id]->filter_method=ping_filter_method;
2947 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002948 {
2949 int
2950 number_colors;
2951
2952 png_colorp
2953 plte;
2954
2955 /*
2956 Copy the PLTE to the object buffer.
2957 */
2958 png_get_PLTE(ping,ping_info,&plte,&number_colors);
2959 mng_info->ob[object_id]->plte_length=number_colors;
2960 for (i=0; i < number_colors; i++)
2961 {
2962 mng_info->ob[object_id]->plte[i]=plte[i];
2963 }
2964 }
2965 else
2966 mng_info->ob[object_id]->plte_length=0;
2967 }
2968 }
2969#endif
2970 /*
2971 Relinquish resources.
2972 */
2973 png_destroy_read_struct(&ping,&ping_info,&end_info);
2974
2975 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2976#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00002977 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002978#endif
2979
2980 if (logging != MagickFalse)
2981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2982 " exit ReadOnePNGImage()");
2983 return(image);
2984
2985/* end of reading one PNG image */
2986}
2987
2988static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2989{
2990 Image
2991 *image,
2992 *previous;
2993
2994 MagickBooleanType
2995 status;
2996
2997 MngInfo
2998 *mng_info;
2999
3000 char
3001 magic_number[MaxTextExtent];
3002
3003 int
3004 have_mng_structure,
3005 logging;
3006
3007 ssize_t
3008 count;
3009
3010 /*
3011 Open image file.
3012 */
3013 assert(image_info != (const ImageInfo *) NULL);
3014 assert(image_info->signature == MagickSignature);
3015 if (image_info->debug != MagickFalse)
3016 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3017 image_info->filename);
3018 assert(exception != (ExceptionInfo *) NULL);
3019 assert(exception->signature == MagickSignature);
3020 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()");
3021 image=AcquireImage(image_info);
3022 mng_info=(MngInfo *) NULL;
3023 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3024 if (status == MagickFalse)
3025 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3026 /*
3027 Verify PNG signature.
3028 */
3029 count=ReadBlob(image,8,(unsigned char *) magic_number);
3030 if (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3031 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3032 /*
3033 Allocate a MngInfo structure.
3034 */
3035 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00003036 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00003037 if (mng_info == (MngInfo *) NULL)
3038 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3039 /*
3040 Initialize members of the MngInfo structure.
3041 */
3042 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3043 mng_info->image=image;
3044 have_mng_structure=MagickTrue;
3045
3046 previous=image;
3047 image=ReadOnePNGImage(mng_info,image_info,exception);
3048 MngInfoFreeStruct(mng_info,&have_mng_structure);
3049 if (image == (Image *) NULL)
3050 {
3051 if (previous != (Image *) NULL)
3052 {
3053 if (previous->signature != MagickSignature)
3054 ThrowReaderException(CorruptImageError,"CorruptImage");
3055 (void) CloseBlob(previous);
3056 (void) DestroyImageList(previous);
3057 }
3058 if (logging != MagickFalse)
3059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3060 "exit ReadPNGImage() with error");
3061 return((Image *) NULL);
3062 }
3063 (void) CloseBlob(image);
3064 if ((image->columns == 0) || (image->rows == 0))
3065 {
3066 if (logging != MagickFalse)
3067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3068 "exit ReadPNGImage() with error.");
3069 ThrowReaderException(CorruptImageError,"CorruptImage");
3070 }
3071 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3072 {
3073 (void) SetImageType(image,PaletteType);
3074 if (image->matte != MagickFalse)
3075 {
3076 /* To do: Reduce to binary transparency */
3077 }
3078 }
3079 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3080 {
3081 (void) SetImageType(image,TrueColorType);
3082 image->matte=MagickFalse;
3083 }
3084 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3085 (void) SetImageType(image,TrueColorMatteType);
3086 if (logging != MagickFalse)
3087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3088 return(image);
3089}
3090
3091
3092
3093#if defined(JNG_SUPPORTED)
3094/*
3095%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3096% %
3097% %
3098% %
3099% R e a d O n e J N G I m a g e %
3100% %
3101% %
3102% %
3103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3104%
3105% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3106% (minus the 8-byte signature) and returns it. It allocates the memory
3107% necessary for the new Image structure and returns a pointer to the new
3108% image.
3109%
3110% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3111%
3112% The format of the ReadOneJNGImage method is:
3113%
3114% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3115% ExceptionInfo *exception)
3116%
3117% A description of each parameter follows:
3118%
3119% o mng_info: Specifies a pointer to a MngInfo structure.
3120%
3121% o image_info: the image info.
3122%
3123% o exception: return any errors or warnings in this structure.
3124%
3125*/
3126static Image *ReadOneJNGImage(MngInfo *mng_info,
3127 const ImageInfo *image_info, ExceptionInfo *exception)
3128{
3129 Image
3130 *alpha_image,
3131 *color_image,
3132 *image,
3133 *jng_image;
3134
3135 ImageInfo
3136 *alpha_image_info,
3137 *color_image_info;
3138
cristybb503372010-05-27 20:51:26 +00003139 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003140 y;
3141
3142 MagickBooleanType
3143 status;
3144
3145 png_uint_32
3146 jng_height,
3147 jng_width;
3148
3149 png_byte
3150 jng_color_type,
3151 jng_image_sample_depth,
3152 jng_image_compression_method,
3153 jng_image_interlace_method,
3154 jng_alpha_sample_depth,
3155 jng_alpha_compression_method,
3156 jng_alpha_filter_method,
3157 jng_alpha_interlace_method;
3158
3159 register const PixelPacket
3160 *s;
3161
cristybb503372010-05-27 20:51:26 +00003162 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003163 i,
3164 x;
3165
3166 register PixelPacket
3167 *q;
3168
3169 register unsigned char
3170 *p;
3171
3172 unsigned int
3173 logging,
3174 read_JSEP,
3175 reading_idat,
3176 skip_to_iend;
3177
cristybb503372010-05-27 20:51:26 +00003178 size_t
cristy3ed852e2009-09-05 21:47:34 +00003179 length;
3180
3181 jng_alpha_compression_method=0;
3182 jng_alpha_sample_depth=8;
3183 jng_color_type=0;
3184 jng_height=0;
3185 jng_width=0;
3186 alpha_image=(Image *) NULL;
3187 color_image=(Image *) NULL;
3188 alpha_image_info=(ImageInfo *) NULL;
3189 color_image_info=(ImageInfo *) NULL;
3190
3191 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3192 " enter ReadOneJNGImage()");
3193
3194 image=mng_info->image;
3195 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3196 {
3197 /*
3198 Allocate next image structure.
3199 */
3200 if (logging != MagickFalse)
3201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3202 " AcquireNextImage()");
3203 AcquireNextImage(image_info,image);
3204 if (GetNextImageInList(image) == (Image *) NULL)
3205 return((Image *) NULL);
3206 image=SyncNextImageInList(image);
3207 }
3208 mng_info->image=image;
3209
3210 /*
3211 Signature bytes have already been read.
3212 */
3213
3214 read_JSEP=MagickFalse;
3215 reading_idat=MagickFalse;
3216 skip_to_iend=MagickFalse;
3217 for (;;)
3218 {
3219 char
3220 type[MaxTextExtent];
3221
3222 unsigned char
3223 *chunk;
3224
3225 unsigned int
3226 count;
3227
3228 /*
3229 Read a new JNG chunk.
3230 */
3231 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3232 2*GetBlobSize(image));
3233 if (status == MagickFalse)
3234 break;
3235 type[0]='\0';
3236 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3237 length=ReadBlobMSBLong(image);
3238 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3239
3240 if (logging != MagickFalse)
3241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3242 " Reading JNG chunk type %c%c%c%c, length: %lu",
cristyf2faecf2010-05-28 19:19:36 +00003243 type[0],type[1],type[2],type[3],(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00003244
3245 if (length > PNG_UINT_31_MAX || count == 0)
3246 ThrowReaderException(CorruptImageError,"CorruptImage");
3247 p=NULL;
3248 chunk=(unsigned char *) NULL;
3249 if (length)
3250 {
3251 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3252 if (chunk == (unsigned char *) NULL)
3253 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00003254 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003255 chunk[i]=(unsigned char) ReadBlobByte(image);
3256 p=chunk;
3257 }
3258 (void) ReadBlobMSBLong(image); /* read crc word */
3259
3260 if (skip_to_iend)
3261 {
3262 if (length)
3263 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3264 continue;
3265 }
3266
3267 if (memcmp(type,mng_JHDR,4) == 0)
3268 {
3269 if (length == 16)
3270 {
cristybb503372010-05-27 20:51:26 +00003271 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003272 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003273 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003274 (p[6] << 8) | p[7]);
3275 jng_color_type=p[8];
3276 jng_image_sample_depth=p[9];
3277 jng_image_compression_method=p[10];
3278 jng_image_interlace_method=p[11];
3279 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3280 NoInterlace;
3281 jng_alpha_sample_depth=p[12];
3282 jng_alpha_compression_method=p[13];
3283 jng_alpha_filter_method=p[14];
3284 jng_alpha_interlace_method=p[15];
3285 if (logging != MagickFalse)
3286 {
3287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003288 " jng_width: %16lu",(unsigned long) jng_width);
cristy3ed852e2009-09-05 21:47:34 +00003289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003290 " jng_width: %16lu",(unsigned long) jng_height);
cristy3ed852e2009-09-05 21:47:34 +00003291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3292 " jng_color_type: %16d",jng_color_type);
3293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3294 " jng_image_sample_depth: %3d",
3295 jng_image_sample_depth);
3296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3297 " jng_image_compression_method:%3d",
3298 jng_image_compression_method);
3299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3300 " jng_image_interlace_method: %3d",
3301 jng_image_interlace_method);
3302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3303 " jng_alpha_sample_depth: %3d",
3304 jng_alpha_sample_depth);
3305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3306 " jng_alpha_compression_method:%3d",
3307 jng_alpha_compression_method);
3308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3309 " jng_alpha_filter_method: %3d",
3310 jng_alpha_filter_method);
3311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3312 " jng_alpha_interlace_method: %3d",
3313 jng_alpha_interlace_method);
3314 }
3315 }
3316 if (length)
3317 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3318 continue;
3319 }
3320
3321
3322 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3323 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3324 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3325 {
3326 /*
3327 o create color_image
3328 o open color_blob, attached to color_image
3329 o if (color type has alpha)
3330 open alpha_blob, attached to alpha_image
3331 */
3332
cristy90823212009-12-12 20:48:33 +00003333 color_image_info=(ImageInfo *)AcquireAlignedMemory(1,sizeof(ImageInfo));
cristy3ed852e2009-09-05 21:47:34 +00003334 if (color_image_info == (ImageInfo *) NULL)
3335 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3336 GetImageInfo(color_image_info);
3337 color_image=AcquireImage(color_image_info);
3338 if (color_image == (Image *) NULL)
3339 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3340
3341 if (logging != MagickFalse)
3342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3343 " Creating color_blob.");
3344 (void) AcquireUniqueFilename(color_image->filename);
3345 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3346 exception);
3347 if (status == MagickFalse)
3348 return((Image *) NULL);
3349
3350 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3351 {
3352 alpha_image_info=(ImageInfo *)
cristy90823212009-12-12 20:48:33 +00003353 AcquireAlignedMemory(1,sizeof(ImageInfo));
cristy3ed852e2009-09-05 21:47:34 +00003354 if (alpha_image_info == (ImageInfo *) NULL)
3355 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3356 GetImageInfo(alpha_image_info);
3357 alpha_image=AcquireImage(alpha_image_info);
3358 if (alpha_image == (Image *) NULL)
3359 {
3360 alpha_image=DestroyImage(alpha_image);
3361 ThrowReaderException(ResourceLimitError,
3362 "MemoryAllocationFailed");
3363 }
3364 if (logging != MagickFalse)
3365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3366 " Creating alpha_blob.");
3367 (void) AcquireUniqueFilename(alpha_image->filename);
3368 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3369 exception);
3370 if (status == MagickFalse)
3371 return((Image *) NULL);
3372 if (jng_alpha_compression_method == 0)
3373 {
3374 unsigned char
3375 data[18];
3376
3377 if (logging != MagickFalse)
3378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3379 " Writing IHDR chunk to alpha_blob.");
3380 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3381 "\211PNG\r\n\032\n");
3382 (void) WriteBlobMSBULong(alpha_image,13L);
3383 PNGType(data,mng_IHDR);
3384 LogPNGChunk((int) logging,mng_IHDR,13L);
3385 PNGLong(data+4,jng_width);
3386 PNGLong(data+8,jng_height);
3387 data[12]=jng_alpha_sample_depth;
3388 data[13]=0; /* color_type gray */
3389 data[14]=0; /* compression method 0 */
3390 data[15]=0; /* filter_method 0 */
3391 data[16]=0; /* interlace_method 0 */
3392 (void) WriteBlob(alpha_image,17,data);
3393 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3394 }
3395 }
3396 reading_idat=MagickTrue;
3397 }
3398
3399 if (memcmp(type,mng_JDAT,4) == 0)
3400 {
3401 /*
3402 Copy chunk to color_image->blob
3403 */
3404
3405 if (logging != MagickFalse)
3406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3407 " Copying JDAT chunk data to color_blob.");
3408
3409 (void) WriteBlob(color_image,length,chunk);
3410 if (length)
3411 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3412 continue;
3413 }
3414
3415 if (memcmp(type,mng_IDAT,4) == 0)
3416 {
3417 png_byte
3418 data[5];
3419
3420 /*
3421 Copy IDAT header and chunk data to alpha_image->blob
3422 */
3423
3424 if (image_info->ping == MagickFalse)
3425 {
3426 if (logging != MagickFalse)
3427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3428 " Copying IDAT chunk data to alpha_blob.");
3429
cristybb503372010-05-27 20:51:26 +00003430 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003431 PNGType(data,mng_IDAT);
3432 LogPNGChunk((int) logging,mng_IDAT,length);
3433 (void) WriteBlob(alpha_image,4,data);
3434 (void) WriteBlob(alpha_image,length,chunk);
3435 (void) WriteBlobMSBULong(alpha_image,
3436 crc32(crc32(0,data,4),chunk,(uInt) length));
3437 }
3438 if (length)
3439 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3440 continue;
3441 }
3442
3443 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3444 {
3445 /*
3446 Copy chunk data to alpha_image->blob
3447 */
3448
3449 if (image_info->ping == MagickFalse)
3450 {
3451 if (logging != MagickFalse)
3452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3453 " Copying JDAA chunk data to alpha_blob.");
3454
3455 (void) WriteBlob(alpha_image,length,chunk);
3456 }
3457 if (length)
3458 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3459 continue;
3460 }
3461
3462 if (memcmp(type,mng_JSEP,4) == 0)
3463 {
3464 read_JSEP=MagickTrue;
3465 if (length)
3466 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3467 continue;
3468 }
3469
3470 if (memcmp(type,mng_bKGD,4) == 0)
3471 {
3472 if (length == 2)
3473 {
3474 image->background_color.red=ScaleCharToQuantum(p[1]);
3475 image->background_color.green=image->background_color.red;
3476 image->background_color.blue=image->background_color.red;
3477 }
3478 if (length == 6)
3479 {
3480 image->background_color.red=ScaleCharToQuantum(p[1]);
3481 image->background_color.green=ScaleCharToQuantum(p[3]);
3482 image->background_color.blue=ScaleCharToQuantum(p[5]);
3483 }
3484 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3485 continue;
3486 }
3487
3488 if (memcmp(type,mng_gAMA,4) == 0)
3489 {
3490 if (length == 4)
cristybb503372010-05-27 20:51:26 +00003491 image->gamma=((float) mng_get_ssize_t(p))*0.00001;
cristy3ed852e2009-09-05 21:47:34 +00003492 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3493 continue;
3494 }
3495
3496 if (memcmp(type,mng_cHRM,4) == 0)
3497 {
3498 if (length == 32)
3499 {
cristybb503372010-05-27 20:51:26 +00003500 image->chromaticity.white_point.x=0.00001*mng_get_ssize_t(p);
3501 image->chromaticity.white_point.y=0.00001*mng_get_ssize_t(&p[4]);
3502 image->chromaticity.red_primary.x=0.00001*mng_get_ssize_t(&p[8]);
3503 image->chromaticity.red_primary.y=0.00001*mng_get_ssize_t(&p[12]);
3504 image->chromaticity.green_primary.x=0.00001*mng_get_ssize_t(&p[16]);
3505 image->chromaticity.green_primary.y=0.00001*mng_get_ssize_t(&p[20]);
3506 image->chromaticity.blue_primary.x=0.00001*mng_get_ssize_t(&p[24]);
3507 image->chromaticity.blue_primary.y=0.00001*mng_get_ssize_t(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003508 }
3509 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3510 continue;
3511 }
3512
3513 if (memcmp(type,mng_sRGB,4) == 0)
3514 {
3515 if (length == 1)
3516 {
3517 image->rendering_intent=(RenderingIntent) (p[0]+1);
3518 image->gamma=0.45455f;
3519 image->chromaticity.red_primary.x=0.6400f;
3520 image->chromaticity.red_primary.y=0.3300f;
3521 image->chromaticity.green_primary.x=0.3000f;
3522 image->chromaticity.green_primary.y=0.6000f;
3523 image->chromaticity.blue_primary.x=0.1500f;
3524 image->chromaticity.blue_primary.y=0.0600f;
3525 image->chromaticity.white_point.x=0.3127f;
3526 image->chromaticity.white_point.y=0.3290f;
3527 }
3528 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3529 continue;
3530 }
3531
3532 if (memcmp(type,mng_oFFs,4) == 0)
3533 {
3534 if (length > 8)
3535 {
cristybb503372010-05-27 20:51:26 +00003536 image->page.x=mng_get_ssize_t(p);
3537 image->page.y=mng_get_ssize_t(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003538 if ((int) p[8] != 0)
3539 {
3540 image->page.x/=10000;
3541 image->page.y/=10000;
3542 }
3543 }
3544 if (length)
3545 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3546 continue;
3547 }
3548
3549 if (memcmp(type,mng_pHYs,4) == 0)
3550 {
3551 if (length > 8)
3552 {
cristybb503372010-05-27 20:51:26 +00003553 image->x_resolution=(double) mng_get_ssize_t(p);
3554 image->y_resolution=(double) mng_get_ssize_t(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003555 if ((int) p[8] == PNG_RESOLUTION_METER)
3556 {
3557 image->units=PixelsPerCentimeterResolution;
3558 image->x_resolution=image->x_resolution/100.0f;
3559 image->y_resolution=image->y_resolution/100.0f;
3560 }
3561 }
3562 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3563 continue;
3564 }
3565
3566#if 0
3567 if (memcmp(type,mng_iCCP,4) == 0)
3568 {
3569 /* To do. */
3570 if (length)
3571 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3572 continue;
3573 }
3574#endif
3575
3576 if (length)
3577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3578
3579 if (memcmp(type,mng_IEND,4))
3580 continue;
3581 break;
3582 }
3583
3584
3585 /* IEND found */
3586
3587 /*
3588 Finish up reading image data:
3589
3590 o read main image from color_blob.
3591
3592 o close color_blob.
3593
3594 o if (color_type has alpha)
3595 if alpha_encoding is PNG
3596 read secondary image from alpha_blob via ReadPNG
3597 if alpha_encoding is JPEG
3598 read secondary image from alpha_blob via ReadJPEG
3599
3600 o close alpha_blob.
3601
3602 o copy intensity of secondary image into
3603 opacity samples of main image.
3604
3605 o destroy the secondary image.
3606 */
3607
3608 (void) CloseBlob(color_image);
3609 if (logging != MagickFalse)
3610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3611 " Reading jng_image from color_blob.");
3612 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3613 color_image->filename);
3614 color_image_info->ping=MagickFalse; /* To do: avoid this */
3615 jng_image=ReadImage(color_image_info,exception);
3616 if (jng_image == (Image *) NULL)
3617 return((Image *) NULL);
3618
3619 (void) RelinquishUniqueFileResource(color_image->filename);
3620 color_image=DestroyImage(color_image);
3621 color_image_info=DestroyImageInfo(color_image_info);
3622
3623 if (jng_image == (Image *) NULL)
3624 return((Image *) NULL);
3625
3626 if (logging != MagickFalse)
3627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3628 " Copying jng_image pixels to main image.");
3629 image->rows=jng_height;
3630 image->columns=jng_width;
3631 length=image->columns*sizeof(PixelPacket);
cristybb503372010-05-27 20:51:26 +00003632 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003633 {
3634 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3635 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3636 (void) CopyMagickMemory(q,s,length);
3637 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3638 break;
3639 }
3640 jng_image=DestroyImage(jng_image);
3641 if (image_info->ping == MagickFalse)
3642 {
3643 if (jng_color_type >= 12)
3644 {
3645 if (jng_alpha_compression_method == 0)
3646 {
3647 png_byte
3648 data[5];
3649 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3650 PNGType(data,mng_IEND);
3651 LogPNGChunk((int) logging,mng_IEND,0L);
3652 (void) WriteBlob(alpha_image,4,data);
3653 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3654 }
3655 (void) CloseBlob(alpha_image);
3656 if (logging != MagickFalse)
3657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3658 " Reading opacity from alpha_blob.");
3659
3660 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3661 "%s",alpha_image->filename);
3662
3663 jng_image=ReadImage(alpha_image_info,exception);
3664 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003665 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003666 {
3667 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3668 &image->exception);
3669 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3670 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003671 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003672 q->opacity=(Quantum) QuantumRange-s->red;
3673 else
cristybb503372010-05-27 20:51:26 +00003674 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003675 {
3676 q->opacity=(Quantum) QuantumRange-s->red;
3677 if (q->opacity != OpaqueOpacity)
3678 image->matte=MagickTrue;
3679 }
3680 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3681 break;
3682 }
3683 (void) RelinquishUniqueFileResource(alpha_image->filename);
3684 alpha_image=DestroyImage(alpha_image);
3685 alpha_image_info=DestroyImageInfo(alpha_image_info);
3686 if (jng_image != (Image *) NULL)
3687 jng_image=DestroyImage(jng_image);
3688 }
3689 }
3690
3691 /*
3692 Read the JNG image.
3693 */
3694 if (mng_info->mng_type == 0)
3695 {
3696 mng_info->mng_width=jng_width;
3697 mng_info->mng_height=jng_height;
3698 }
3699 if (image->page.width == 0 && image->page.height == 0)
3700 {
3701 image->page.width=jng_width;
3702 image->page.height=jng_height;
3703 }
3704 if (image->page.x == 0 && image->page.y == 0)
3705 {
3706 image->page.x=mng_info->x_off[mng_info->object_id];
3707 image->page.y=mng_info->y_off[mng_info->object_id];
3708 }
3709 else
3710 {
3711 image->page.y=mng_info->y_off[mng_info->object_id];
3712 }
3713 mng_info->image_found++;
3714 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3715 2*GetBlobSize(image));
3716 if (logging != MagickFalse)
3717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3718 " exit ReadOneJNGImage()");
3719 return(image);
3720}
3721
3722/*
3723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3724% %
3725% %
3726% %
3727% R e a d J N G I m a g e %
3728% %
3729% %
3730% %
3731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3732%
3733% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3734% (including the 8-byte signature) and returns it. It allocates the memory
3735% necessary for the new Image structure and returns a pointer to the new
3736% image.
3737%
3738% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3739%
3740% The format of the ReadJNGImage method is:
3741%
3742% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3743% *exception)
3744%
3745% A description of each parameter follows:
3746%
3747% o image_info: the image info.
3748%
3749% o exception: return any errors or warnings in this structure.
3750%
3751*/
3752
3753static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3754{
3755 Image
3756 *image,
3757 *previous;
3758
3759 MagickBooleanType
3760 status;
3761
3762 MngInfo
3763 *mng_info;
3764
3765 char
3766 magic_number[MaxTextExtent];
3767
3768 int
3769 have_mng_structure,
3770 logging;
3771
3772 size_t
3773 count;
3774
3775 /*
3776 Open image file.
3777 */
3778 assert(image_info != (const ImageInfo *) NULL);
3779 assert(image_info->signature == MagickSignature);
3780 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3781 assert(exception != (ExceptionInfo *) NULL);
3782 assert(exception->signature == MagickSignature);
3783 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()");
3784 image=AcquireImage(image_info);
3785 mng_info=(MngInfo *) NULL;
3786 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3787 if (status == MagickFalse)
3788 return((Image *) NULL);
3789 if (LocaleCompare(image_info->magick,"JNG") != 0)
3790 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3791 /*
3792 Verify JNG signature.
3793 */
3794 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3795 if (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3796 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3797 /*
3798 Allocate a MngInfo structure.
3799 */
3800 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00003801 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(*mng_info));
cristy3ed852e2009-09-05 21:47:34 +00003802 if (mng_info == (MngInfo *) NULL)
3803 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3804 /*
3805 Initialize members of the MngInfo structure.
3806 */
3807 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3808 have_mng_structure=MagickTrue;
3809
3810 mng_info->image=image;
3811 previous=image;
3812 image=ReadOneJNGImage(mng_info,image_info,exception);
3813 MngInfoFreeStruct(mng_info,&have_mng_structure);
3814 if (image == (Image *) NULL)
3815 {
3816 if (IsImageObject(previous) != MagickFalse)
3817 {
3818 (void) CloseBlob(previous);
3819 (void) DestroyImageList(previous);
3820 }
3821 if (logging != MagickFalse)
3822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3823 "exit ReadJNGImage() with error");
3824 return((Image *) NULL);
3825 }
3826 (void) CloseBlob(image);
3827 if (image->columns == 0 || image->rows == 0)
3828 {
3829 if (logging != MagickFalse)
3830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3831 "exit ReadJNGImage() with error");
3832 ThrowReaderException(CorruptImageError,"CorruptImage");
3833 }
3834 if (logging != MagickFalse)
3835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3836 return(image);
3837}
3838#endif
3839
3840static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3841{
3842 char
3843 page_geometry[MaxTextExtent];
3844
3845 Image
3846 *image,
3847 *previous;
3848
3849 int
3850 have_mng_structure;
3851
3852 volatile int
3853 first_mng_object,
3854 logging,
3855 object_id,
3856 term_chunk_found,
3857 skip_to_iend;
3858
cristybb503372010-05-27 20:51:26 +00003859 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003860 image_count=0;
3861
3862 MagickBooleanType
3863 status;
3864
3865 MagickOffsetType
3866 offset;
3867
3868 MngInfo
3869 *mng_info;
3870
3871 MngBox
3872 default_fb,
3873 fb,
3874 previous_fb;
3875
3876#if defined(MNG_INSERT_LAYERS)
3877 PixelPacket
3878 mng_background_color;
3879#endif
3880
3881 register unsigned char
3882 *p;
3883
cristybb503372010-05-27 20:51:26 +00003884 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003885 i;
3886
3887 size_t
3888 count;
3889
cristybb503372010-05-27 20:51:26 +00003890 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003891 loop_level;
3892
3893 volatile short
3894 skipping_loop;
3895
3896#if defined(MNG_INSERT_LAYERS)
3897 unsigned int
3898 mandatory_back=0;
3899#endif
3900
3901 volatile unsigned int
3902#ifdef MNG_OBJECT_BUFFERS
3903 mng_background_object=0,
3904#endif
3905 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
3906
cristybb503372010-05-27 20:51:26 +00003907 size_t
cristy3ed852e2009-09-05 21:47:34 +00003908 default_frame_timeout,
3909 frame_timeout,
3910#if defined(MNG_INSERT_LAYERS)
3911 image_height,
3912 image_width,
3913#endif
3914 length;
3915
cristybb503372010-05-27 20:51:26 +00003916 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00003917 default_frame_delay,
3918 final_delay,
3919 final_image_delay,
3920 frame_delay,
3921#if defined(MNG_INSERT_LAYERS)
3922 insert_layers,
3923#endif
3924 mng_iterations=1,
3925 simplicity=0,
3926 subframe_height=0,
3927 subframe_width=0;
3928
3929 previous_fb.top=0;
3930 previous_fb.bottom=0;
3931 previous_fb.left=0;
3932 previous_fb.right=0;
3933 default_fb.top=0;
3934 default_fb.bottom=0;
3935 default_fb.left=0;
3936 default_fb.right=0;
3937
3938 /*
3939 Set image_info->type=OptimizeType (new in version 5.4.0) to get the
3940 following optimizations:
3941
3942 o 16-bit depth is reduced to 8 if all pixels contain samples whose
3943 high byte and low byte are identical.
3944 o Opaque matte channel is removed.
3945 o If matte channel is present but only one transparent color is
3946 present, RGB+tRNS is written instead of RGBA
3947 o Grayscale images are reduced to 1, 2, or 4 bit depth if
3948 this can be done without loss.
3949 o Palette is sorted to remove unused entries and to put a
3950 transparent color first, if PNG_SORT_PALETTE is also defined.
3951 */
3952
3953 /*
3954 Open image file.
3955 */
3956 assert(image_info != (const ImageInfo *) NULL);
3957 assert(image_info->signature == MagickSignature);
3958 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3959 assert(exception != (ExceptionInfo *) NULL);
3960 assert(exception->signature == MagickSignature);
3961 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()");
3962 image=AcquireImage(image_info);
3963 mng_info=(MngInfo *) NULL;
3964 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3965 if (status == MagickFalse)
3966 return((Image *) NULL);
3967 first_mng_object=MagickFalse;
3968 skipping_loop=(-1);
3969 have_mng_structure=MagickFalse;
3970 /*
3971 Allocate a MngInfo structure.
3972 */
cristy90823212009-12-12 20:48:33 +00003973 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00003974 if (mng_info == (MngInfo *) NULL)
3975 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3976 /*
3977 Initialize members of the MngInfo structure.
3978 */
3979 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3980 mng_info->image=image;
3981 have_mng_structure=MagickTrue;
3982#if (MAGICKCORE_QUANTUM_DEPTH == 16)
3983 mng_info->optimize=image_info->type == OptimizeType;
3984#endif
3985
3986 if (LocaleCompare(image_info->magick,"MNG") == 0)
3987 {
3988 char
3989 magic_number[MaxTextExtent];
3990
3991 /*
3992 Verify MNG signature.
3993 */
3994 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3995 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
3996 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3997 /*
3998 Initialize some nonzero members of the MngInfo structure.
3999 */
4000 for (i=0; i < MNG_MAX_OBJECTS; i++)
4001 {
cristybb503372010-05-27 20:51:26 +00004002 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4003 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004004 }
4005 mng_info->exists[0]=MagickTrue;
4006 }
4007 first_mng_object=MagickTrue;
4008 mng_type=0;
4009#if defined(MNG_INSERT_LAYERS)
4010 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4011#endif
4012 default_frame_delay=0;
4013 default_frame_timeout=0;
4014 frame_delay=0;
4015 final_delay=1;
4016 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4017 object_id=0;
4018 skip_to_iend=MagickFalse;
4019 term_chunk_found=MagickFalse;
4020 mng_info->framing_mode=1;
4021#if defined(MNG_INSERT_LAYERS)
4022 mandatory_back=MagickFalse;
4023#endif
4024#if defined(MNG_INSERT_LAYERS)
4025 mng_background_color=image->background_color;
4026#endif
4027 default_fb=mng_info->frame;
4028 previous_fb=mng_info->frame;
4029 do
4030 {
4031 char
4032 type[MaxTextExtent];
4033
4034 if (LocaleCompare(image_info->magick,"MNG") == 0)
4035 {
4036 unsigned char
4037 *chunk;
4038
4039 /*
4040 Read a new chunk.
4041 */
4042 type[0]='\0';
4043 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4044 length=ReadBlobMSBLong(image);
4045 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4046
4047 if (logging != MagickFalse)
4048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4049 " Reading MNG chunk type %c%c%c%c, length: %lu",
cristyf2faecf2010-05-28 19:19:36 +00004050 type[0],type[1],type[2],type[3],(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00004051
4052 if (length > PNG_UINT_31_MAX)
4053 status=MagickFalse;
4054 if (count == 0)
4055 ThrowReaderException(CorruptImageError,"CorruptImage");
4056 p=NULL;
4057 chunk=(unsigned char *) NULL;
4058 if (length)
4059 {
4060 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4061 if (chunk == (unsigned char *) NULL)
4062 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00004063 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004064 chunk[i]=(unsigned char) ReadBlobByte(image);
4065 p=chunk;
4066 }
4067 (void) ReadBlobMSBLong(image); /* read crc word */
4068
4069#if !defined(JNG_SUPPORTED)
4070 if (memcmp(type,mng_JHDR,4) == 0)
4071 {
4072 skip_to_iend=MagickTrue;
4073 if (mng_info->jhdr_warning == 0)
4074 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4075 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4076 mng_info->jhdr_warning++;
4077 }
4078#endif
4079 if (memcmp(type,mng_DHDR,4) == 0)
4080 {
4081 skip_to_iend=MagickTrue;
4082 if (mng_info->dhdr_warning == 0)
4083 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4084 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4085 mng_info->dhdr_warning++;
4086 }
4087 if (memcmp(type,mng_MEND,4) == 0)
4088 break;
4089 if (skip_to_iend)
4090 {
4091 if (memcmp(type,mng_IEND,4) == 0)
4092 skip_to_iend=MagickFalse;
4093 if (length)
4094 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4095 if (logging != MagickFalse)
4096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4097 " Skip to IEND.");
4098 continue;
4099 }
4100 if (memcmp(type,mng_MHDR,4) == 0)
4101 {
cristybb503372010-05-27 20:51:26 +00004102 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004103 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004104 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004105 (p[6] << 8) | p[7]);
4106 if (logging != MagickFalse)
4107 {
4108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004109 " MNG width: %lu",(unsigned long) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004111 " MNG height: %lu",(unsigned long) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004112 }
4113 p+=8;
cristybb503372010-05-27 20:51:26 +00004114 mng_info->ticks_per_second=(size_t) mng_get_ssize_t(p);
cristy3ed852e2009-09-05 21:47:34 +00004115 if (mng_info->ticks_per_second == 0)
4116 default_frame_delay=0;
4117 else
4118 default_frame_delay=1UL*image->ticks_per_second/
4119 mng_info->ticks_per_second;
4120 frame_delay=default_frame_delay;
4121 simplicity=0;
4122 if (length > 16)
4123 {
4124 p+=16;
cristybb503372010-05-27 20:51:26 +00004125 simplicity=(size_t) mng_get_ssize_t(p);
cristy3ed852e2009-09-05 21:47:34 +00004126 }
4127 mng_type=1; /* Full MNG */
4128 if ((simplicity != 0) && ((simplicity | 11) == 11))
4129 mng_type=2; /* LC */
4130 if ((simplicity != 0) && ((simplicity | 9) == 9))
4131 mng_type=3; /* VLC */
4132#if defined(MNG_INSERT_LAYERS)
4133 if (mng_type != 3)
4134 insert_layers=MagickTrue;
4135#endif
4136 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4137 {
4138 /*
4139 Allocate next image structure.
4140 */
4141 AcquireNextImage(image_info,image);
4142 if (GetNextImageInList(image) == (Image *) NULL)
4143 return((Image *) NULL);
4144 image=SyncNextImageInList(image);
4145 mng_info->image=image;
4146 }
4147
4148 if ((mng_info->mng_width > 65535L) ||
4149 (mng_info->mng_height > 65535L))
4150 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4151 (void) FormatMagickString(page_geometry,MaxTextExtent,"%lux%lu+0+0",
cristyf2faecf2010-05-28 19:19:36 +00004152 (unsigned long) mng_info->mng_width,(unsigned long)
4153 mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004154 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004155 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004156 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004157 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004158 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4159 for (i=0; i < MNG_MAX_OBJECTS; i++)
4160 mng_info->object_clip[i]=mng_info->frame;
4161 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4162 continue;
4163 }
4164
4165 if (memcmp(type,mng_TERM,4) == 0)
4166 {
4167 int
4168 repeat=0;
4169
4170
4171 if (length)
4172 repeat=p[0];
4173 if (repeat == 3)
4174 {
cristybb503372010-05-27 20:51:26 +00004175 final_delay=(png_uint_32) mng_get_ssize_t(&p[2]);
4176 mng_iterations=(png_uint_32) mng_get_ssize_t(&p[6]);
cristy3ed852e2009-09-05 21:47:34 +00004177 if (mng_iterations == PNG_UINT_31_MAX)
4178 mng_iterations=0;
4179 image->iterations=mng_iterations;
4180 term_chunk_found=MagickTrue;
4181 }
4182 if (logging != MagickFalse)
4183 {
4184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4185 " repeat=%d",repeat);
4186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004187 " final_delay=%ld",(long) final_delay);
cristy3ed852e2009-09-05 21:47:34 +00004188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004189 " image->iterations=%ld",(long) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004190 }
4191 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4192 continue;
4193 }
4194 if (memcmp(type,mng_DEFI,4) == 0)
4195 {
4196 if (mng_type == 3)
4197 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4198 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4199 image->filename);
4200 object_id=(p[0] << 8) | p[1];
4201 if (mng_type == 2 && object_id != 0)
4202 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4203 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4204 image->filename);
4205 if (object_id > MNG_MAX_OBJECTS)
4206 {
4207 /*
4208 Instead ofsuing a warning we should allocate a larger
4209 MngInfo structure and continue.
4210 */
4211 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4212 CoderError,"object id too large","`%s'",image->filename);
4213 object_id=MNG_MAX_OBJECTS;
4214 }
4215 if (mng_info->exists[object_id])
4216 if (mng_info->frozen[object_id])
4217 {
4218 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4219 (void) ThrowMagickException(&image->exception,
4220 GetMagickModule(),CoderError,
4221 "DEFI cannot redefine a frozen MNG object","`%s'",
4222 image->filename);
4223 continue;
4224 }
4225 mng_info->exists[object_id]=MagickTrue;
4226 if (length > 2)
4227 mng_info->invisible[object_id]=p[2];
4228 /*
4229 Extract object offset info.
4230 */
4231 if (length > 11)
4232 {
cristybb503372010-05-27 20:51:26 +00004233 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004234 (p[6] << 8) | p[7]);
cristybb503372010-05-27 20:51:26 +00004235 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) | (p[9] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004236 (p[10] << 8) | p[11]);
4237 if (logging != MagickFalse)
4238 {
4239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004240 " x_off[%d]: %lu",object_id,(unsigned long)
4241 mng_info->x_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004243 " y_off[%d]: %lu",object_id,(unsigned long)
4244 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004245 }
4246 }
4247 /*
4248 Extract object clipping info.
4249 */
4250 if (length > 27)
4251 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4252 &p[12]);
4253 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4254 continue;
4255 }
4256 if (memcmp(type,mng_bKGD,4) == 0)
4257 {
4258 mng_info->have_global_bkgd=MagickFalse;
4259 if (length > 5)
4260 {
4261 mng_info->mng_global_bkgd.red=
4262 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4263 mng_info->mng_global_bkgd.green=
4264 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4265 mng_info->mng_global_bkgd.blue=
4266 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4267 mng_info->have_global_bkgd=MagickTrue;
4268 }
4269 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4270 continue;
4271 }
4272 if (memcmp(type,mng_BACK,4) == 0)
4273 {
4274#if defined(MNG_INSERT_LAYERS)
4275 if (length > 6)
4276 mandatory_back=p[6];
4277 else
4278 mandatory_back=0;
4279 if (mandatory_back && length > 5)
4280 {
4281 mng_background_color.red=
4282 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4283 mng_background_color.green=
4284 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4285 mng_background_color.blue=
4286 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4287 mng_background_color.opacity=OpaqueOpacity;
4288 }
4289#ifdef MNG_OBJECT_BUFFERS
4290 if (length > 8)
4291 mng_background_object=(p[7] << 8) | p[8];
4292#endif
4293#endif
4294 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4295 continue;
4296 }
4297 if (memcmp(type,mng_PLTE,4) == 0)
4298 {
4299 /*
4300 Read global PLTE.
4301 */
4302 if (length && (length < 769))
4303 {
4304 if (mng_info->global_plte == (png_colorp) NULL)
4305 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4306 sizeof(*mng_info->global_plte));
cristybb503372010-05-27 20:51:26 +00004307 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004308 {
4309 mng_info->global_plte[i].red=p[3*i];
4310 mng_info->global_plte[i].green=p[3*i+1];
4311 mng_info->global_plte[i].blue=p[3*i+2];
4312 }
4313 mng_info->global_plte_length=length/3;
4314 }
4315#ifdef MNG_LOOSE
4316 for ( ; i < 256; i++)
4317 {
4318 mng_info->global_plte[i].red=i;
4319 mng_info->global_plte[i].green=i;
4320 mng_info->global_plte[i].blue=i;
4321 }
4322 if (length)
4323 mng_info->global_plte_length=256;
4324#endif
4325 else
4326 mng_info->global_plte_length=0;
4327 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4328 continue;
4329 }
4330 if (memcmp(type,mng_tRNS,4) == 0)
4331 {
4332 /* read global tRNS */
4333
4334 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004335 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004336 mng_info->global_trns[i]=p[i];
4337
4338#ifdef MNG_LOOSE
4339 for ( ; i < 256; i++)
4340 mng_info->global_trns[i]=255;
4341#endif
4342 mng_info->global_trns_length=length;
4343 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4344 continue;
4345 }
4346 if (memcmp(type,mng_gAMA,4) == 0)
4347 {
4348 if (length == 4)
4349 {
cristybb503372010-05-27 20:51:26 +00004350 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004351 igamma;
4352
cristybb503372010-05-27 20:51:26 +00004353 igamma=mng_get_ssize_t(p);
cristy3ed852e2009-09-05 21:47:34 +00004354 mng_info->global_gamma=((float) igamma)*0.00001;
4355 mng_info->have_global_gama=MagickTrue;
4356 }
4357 else
4358 mng_info->have_global_gama=MagickFalse;
4359 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4360 continue;
4361 }
4362
4363 if (memcmp(type,mng_cHRM,4) == 0)
4364 {
4365 /*
4366 Read global cHRM
4367 */
4368 if (length == 32)
4369 {
cristybb503372010-05-27 20:51:26 +00004370 mng_info->global_chrm.white_point.x=0.00001*mng_get_ssize_t(p);
4371 mng_info->global_chrm.white_point.y=0.00001*mng_get_ssize_t(&p[4]);
4372 mng_info->global_chrm.red_primary.x=0.00001*mng_get_ssize_t(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004373 mng_info->global_chrm.red_primary.y=0.00001*
cristybb503372010-05-27 20:51:26 +00004374 mng_get_ssize_t(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004375 mng_info->global_chrm.green_primary.x=0.00001*
cristybb503372010-05-27 20:51:26 +00004376 mng_get_ssize_t(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004377 mng_info->global_chrm.green_primary.y=0.00001*
cristybb503372010-05-27 20:51:26 +00004378 mng_get_ssize_t(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004379 mng_info->global_chrm.blue_primary.x=0.00001*
cristybb503372010-05-27 20:51:26 +00004380 mng_get_ssize_t(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004381 mng_info->global_chrm.blue_primary.y=0.00001*
cristybb503372010-05-27 20:51:26 +00004382 mng_get_ssize_t(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004383 mng_info->have_global_chrm=MagickTrue;
4384 }
4385 else
4386 mng_info->have_global_chrm=MagickFalse;
4387 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4388 continue;
4389 }
4390 if (memcmp(type,mng_sRGB,4) == 0)
4391 {
4392 /*
4393 Read global sRGB.
4394 */
4395 if (length)
4396 {
4397 mng_info->global_srgb_intent=(RenderingIntent) (p[0]+1);
4398 mng_info->have_global_srgb=MagickTrue;
4399 }
4400 else
4401 mng_info->have_global_srgb=MagickFalse;
4402 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4403 continue;
4404 }
4405 if (memcmp(type,mng_iCCP,4) == 0)
4406 {
4407 /* To do. */
4408
4409 /*
4410 Read global iCCP.
4411 */
4412 if (length)
4413 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4414 continue;
4415 }
4416 if (memcmp(type,mng_FRAM,4) == 0)
4417 {
4418 if (mng_type == 3)
4419 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4420 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4421 image->filename);
4422 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4423 image->delay=frame_delay;
4424 frame_delay=default_frame_delay;
4425 frame_timeout=default_frame_timeout;
4426 fb=default_fb;
4427 if (length)
4428 if (p[0])
4429 mng_info->framing_mode=p[0];
4430 if (logging != MagickFalse)
4431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4432 " Framing_mode=%d",mng_info->framing_mode);
4433 if (length > 6)
4434 {
4435 /*
4436 Note the delay and frame clipping boundaries.
4437 */
4438 p++; /* framing mode */
cristybb503372010-05-27 20:51:26 +00004439 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004440 p++; /* frame name */
4441 p++; /* frame name terminator */
cristybb503372010-05-27 20:51:26 +00004442 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004443 {
4444 int
4445 change_delay,
4446 change_timeout,
4447 change_clipping;
4448
4449 change_delay=(*p++);
4450 change_timeout=(*p++);
4451 change_clipping=(*p++);
4452 p++; /* change_sync */
4453 if (change_delay)
4454 {
4455 frame_delay=(1UL*image->ticks_per_second*
cristybb503372010-05-27 20:51:26 +00004456 (mng_get_ssize_t(p))/mng_info->ticks_per_second);
cristy3ed852e2009-09-05 21:47:34 +00004457 if (change_delay == 2)
4458 default_frame_delay=frame_delay;
4459 p+=4;
4460 if (logging != MagickFalse)
4461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004462 " Framing_delay=%ld",(long) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004463 }
4464 if (change_timeout)
4465 {
4466 frame_timeout=(1UL*image->ticks_per_second*
cristybb503372010-05-27 20:51:26 +00004467 (mng_get_ssize_t(p))/mng_info->ticks_per_second);
cristy3ed852e2009-09-05 21:47:34 +00004468 if (change_delay == 2)
4469 default_frame_timeout=frame_timeout;
4470 p+=4;
4471 if (logging != MagickFalse)
4472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004473 " Framing_timeout=%ld",(long) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004474 }
4475 if (change_clipping)
4476 {
4477 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4478 p+=17;
4479 previous_fb=fb;
4480 if (logging != MagickFalse)
4481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4482 " Frame_clipping: L=%ld R=%ld T=%ld B=%ld",
cristyf2faecf2010-05-28 19:19:36 +00004483 (long) fb.left,(long) fb.right,(long) fb.top,
4484 (long) fb.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004485 if (change_clipping == 2)
4486 default_fb=fb;
4487 }
4488 }
4489 }
4490 mng_info->clip=fb;
4491 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
cristybb503372010-05-27 20:51:26 +00004492 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004493 -mng_info->clip.left);
cristybb503372010-05-27 20:51:26 +00004494 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004495 -mng_info->clip.top);
4496 /*
4497 Insert a background layer behind the frame if framing_mode is 4.
4498 */
4499#if defined(MNG_INSERT_LAYERS)
4500 if (logging != MagickFalse)
4501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004502 " subframe_width=%lu, subframe_height=%lu",(unsigned long)
4503 subframe_width,(unsigned long) subframe_height);
cristy3ed852e2009-09-05 21:47:34 +00004504 if (insert_layers && (mng_info->framing_mode == 4) &&
4505 (subframe_width) && (subframe_height))
4506 {
4507 /*
4508 Allocate next image structure.
4509 */
4510 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4511 {
4512 AcquireNextImage(image_info,image);
4513 if (GetNextImageInList(image) == (Image *) NULL)
4514 {
4515 image=DestroyImageList(image);
4516 MngInfoFreeStruct(mng_info,&have_mng_structure);
4517 return((Image *) NULL);
4518 }
4519 image=SyncNextImageInList(image);
4520 }
4521 mng_info->image=image;
4522 if (term_chunk_found)
4523 {
4524 image->start_loop=MagickTrue;
4525 image->iterations=mng_iterations;
4526 term_chunk_found=MagickFalse;
4527 }
4528 else
4529 image->start_loop=MagickFalse;
4530 image->columns=subframe_width;
4531 image->rows=subframe_height;
4532 image->page.width=subframe_width;
4533 image->page.height=subframe_height;
4534 image->page.x=mng_info->clip.left;
4535 image->page.y=mng_info->clip.top;
4536 image->background_color=mng_background_color;
4537 image->matte=MagickFalse;
4538 image->delay=0;
4539 (void) SetImageBackgroundColor(image);
4540 if (logging != MagickFalse)
4541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4542 " Inserted background layer, L=%ld, R=%ld, T=%ld, B=%ld",
cristyf2faecf2010-05-28 19:19:36 +00004543 (long) mng_info->clip.left,(long) mng_info->clip.right,
4544 (long) mng_info->clip.top,(long) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004545 }
4546#endif
4547 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4548 continue;
4549 }
4550 if (memcmp(type,mng_CLIP,4) == 0)
4551 {
4552 unsigned int
4553 first_object,
4554 last_object;
4555
4556 /*
4557 Read CLIP.
4558 */
4559 first_object=(p[0] << 8) | p[1];
4560 last_object=(p[2] << 8) | p[3];
4561 for (i=(int) first_object; i <= (int) last_object; i++)
4562 {
4563 if (mng_info->exists[i] && !mng_info->frozen[i])
4564 {
4565 MngBox
4566 box;
4567
4568 box=mng_info->object_clip[i];
4569 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4570 }
4571 }
4572 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4573 continue;
4574 }
4575 if (memcmp(type,mng_SAVE,4) == 0)
4576 {
4577 for (i=1; i < MNG_MAX_OBJECTS; i++)
4578 if (mng_info->exists[i])
4579 {
4580 mng_info->frozen[i]=MagickTrue;
4581#ifdef MNG_OBJECT_BUFFERS
4582 if (mng_info->ob[i] != (MngBuffer *) NULL)
4583 mng_info->ob[i]->frozen=MagickTrue;
4584#endif
4585 }
4586 if (length)
4587 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4588 continue;
4589 }
4590
4591 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4592 {
4593 /*
4594 Read DISC or SEEK.
4595 */
4596 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4597 {
4598 for (i=1; i < MNG_MAX_OBJECTS; i++)
4599 MngInfoDiscardObject(mng_info,i);
4600 }
4601 else
4602 {
cristybb503372010-05-27 20:51:26 +00004603 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004604 j;
4605
cristybb503372010-05-27 20:51:26 +00004606 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00004607 {
4608 i=p[j] << 8 | p[j+1];
4609 MngInfoDiscardObject(mng_info,i);
4610 }
4611 }
4612 if (length)
4613 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4614 continue;
4615 }
4616 if (memcmp(type,mng_MOVE,4) == 0)
4617 {
cristybb503372010-05-27 20:51:26 +00004618 size_t
cristy3ed852e2009-09-05 21:47:34 +00004619 first_object,
4620 last_object;
4621
4622 /*
4623 read MOVE
4624 */
4625 first_object=(p[0] << 8) | p[1];
4626 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00004627 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00004628 {
4629 if (mng_info->exists[i] && !mng_info->frozen[i])
4630 {
4631 MngPair
4632 new_pair;
4633
4634 MngPair
4635 old_pair;
4636
4637 old_pair.a=mng_info->x_off[i];
4638 old_pair.b=mng_info->y_off[i];
4639 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4640 mng_info->x_off[i]=new_pair.a;
4641 mng_info->y_off[i]=new_pair.b;
4642 }
4643 }
4644 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4645 continue;
4646 }
4647
4648 if (memcmp(type,mng_LOOP,4) == 0)
4649 {
cristybb503372010-05-27 20:51:26 +00004650 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00004651 loop_level=chunk[0];
4652 mng_info->loop_active[loop_level]=1; /* mark loop active */
4653 /*
4654 Record starting point.
4655 */
cristybb503372010-05-27 20:51:26 +00004656 loop_iters=mng_get_ssize_t(&chunk[1]);
cristy3ed852e2009-09-05 21:47:34 +00004657 if (logging != MagickFalse)
4658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004659 " LOOP level %ld has %ld iterations ",(long) loop_level,
4660 (long) loop_iters);
cristy3ed852e2009-09-05 21:47:34 +00004661 if (loop_iters == 0)
4662 skipping_loop=loop_level;
4663 else
4664 {
4665 mng_info->loop_jump[loop_level]=TellBlob(image);
4666 mng_info->loop_count[loop_level]=loop_iters;
4667 }
4668 mng_info->loop_iteration[loop_level]=0;
4669 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4670 continue;
4671 }
4672 if (memcmp(type,mng_ENDL,4) == 0)
4673 {
4674 loop_level=chunk[0];
4675 if (skipping_loop > 0)
4676 {
4677 if (skipping_loop == loop_level)
4678 {
4679 /*
4680 Found end of zero-iteration loop.
4681 */
4682 skipping_loop=(-1);
4683 mng_info->loop_active[loop_level]=0;
4684 }
4685 }
4686 else
4687 {
4688 if (mng_info->loop_active[loop_level] == 1)
4689 {
4690 mng_info->loop_count[loop_level]--;
4691 mng_info->loop_iteration[loop_level]++;
4692 if (logging != MagickFalse)
4693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4694 " ENDL: LOOP level %ld has %ld remaining iterations ",
cristyf2faecf2010-05-28 19:19:36 +00004695 (long) loop_level,(long)
4696 mng_info->loop_count[loop_level]);
cristy3ed852e2009-09-05 21:47:34 +00004697 if (mng_info->loop_count[loop_level] != 0)
4698 {
4699 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4700 SEEK_SET);
4701 if (offset < 0)
4702 ThrowReaderException(CorruptImageError,
4703 "ImproperImageHeader");
4704 }
4705 else
4706 {
4707 short
4708 last_level;
4709
4710 /*
4711 Finished loop.
4712 */
4713 mng_info->loop_active[loop_level]=0;
4714 last_level=(-1);
4715 for (i=0; i < loop_level; i++)
4716 if (mng_info->loop_active[i] == 1)
4717 last_level=(short) i;
4718 loop_level=last_level;
4719 }
4720 }
4721 }
4722 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4723 continue;
4724 }
4725 if (memcmp(type,mng_CLON,4) == 0)
4726 {
4727 if (mng_info->clon_warning == 0)
4728 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4729 CoderError,"CLON is not implemented yet","`%s'",
4730 image->filename);
4731 mng_info->clon_warning++;
4732 }
4733 if (memcmp(type,mng_MAGN,4) == 0)
4734 {
4735 png_uint_16
4736 magn_first,
4737 magn_last,
4738 magn_mb,
4739 magn_ml,
4740 magn_mr,
4741 magn_mt,
4742 magn_mx,
4743 magn_my,
4744 magn_methx,
4745 magn_methy;
4746
4747 if (length > 1)
4748 magn_first=(p[0] << 8) | p[1];
4749 else
4750 magn_first=0;
4751 if (length > 3)
4752 magn_last=(p[2] << 8) | p[3];
4753 else
4754 magn_last=magn_first;
4755#ifndef MNG_OBJECT_BUFFERS
4756 if (magn_first || magn_last)
4757 if (mng_info->magn_warning == 0)
4758 {
4759 (void) ThrowMagickException(&image->exception,
4760 GetMagickModule(),CoderError,
4761 "MAGN is not implemented yet for nonzero objects",
4762 "`%s'",image->filename);
4763 mng_info->magn_warning++;
4764 }
4765#endif
4766 if (length > 4)
4767 magn_methx=p[4];
4768 else
4769 magn_methx=0;
4770
4771 if (length > 6)
4772 magn_mx=(p[5] << 8) | p[6];
4773 else
4774 magn_mx=1;
4775 if (magn_mx == 0)
4776 magn_mx=1;
4777
4778 if (length > 8)
4779 magn_my=(p[7] << 8) | p[8];
4780 else
4781 magn_my=magn_mx;
4782 if (magn_my == 0)
4783 magn_my=1;
4784
4785 if (length > 10)
4786 magn_ml=(p[9] << 8) | p[10];
4787 else
4788 magn_ml=magn_mx;
4789 if (magn_ml == 0)
4790 magn_ml=1;
4791
4792 if (length > 12)
4793 magn_mr=(p[11] << 8) | p[12];
4794 else
4795 magn_mr=magn_mx;
4796 if (magn_mr == 0)
4797 magn_mr=1;
4798
4799 if (length > 14)
4800 magn_mt=(p[13] << 8) | p[14];
4801 else
4802 magn_mt=magn_my;
4803 if (magn_mt == 0)
4804 magn_mt=1;
4805
4806 if (length > 16)
4807 magn_mb=(p[15] << 8) | p[16];
4808 else
4809 magn_mb=magn_my;
4810 if (magn_mb == 0)
4811 magn_mb=1;
4812
4813 if (length > 17)
4814 magn_methy=p[17];
4815 else
4816 magn_methy=magn_methx;
4817
4818 if (magn_methx > 5 || magn_methy > 5)
4819 if (mng_info->magn_warning == 0)
4820 {
4821 (void) ThrowMagickException(&image->exception,
4822 GetMagickModule(),CoderError,
4823 "Unknown MAGN method in MNG datastream","`%s'",
4824 image->filename);
4825 mng_info->magn_warning++;
4826 }
4827#ifdef MNG_OBJECT_BUFFERS
4828 /* Magnify existing objects in the range magn_first to magn_last */
4829#endif
4830 if (magn_first == 0 || magn_last == 0)
4831 {
4832 /* Save the magnification factors for object 0 */
4833 mng_info->magn_mb=magn_mb;
4834 mng_info->magn_ml=magn_ml;
4835 mng_info->magn_mr=magn_mr;
4836 mng_info->magn_mt=magn_mt;
4837 mng_info->magn_mx=magn_mx;
4838 mng_info->magn_my=magn_my;
4839 mng_info->magn_methx=magn_methx;
4840 mng_info->magn_methy=magn_methy;
4841 }
4842 }
4843 if (memcmp(type,mng_PAST,4) == 0)
4844 {
4845 if (mng_info->past_warning == 0)
4846 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4847 CoderError,"PAST is not implemented yet","`%s'",
4848 image->filename);
4849 mng_info->past_warning++;
4850 }
4851 if (memcmp(type,mng_SHOW,4) == 0)
4852 {
4853 if (mng_info->show_warning == 0)
4854 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4855 CoderError,"SHOW is not implemented yet","`%s'",
4856 image->filename);
4857 mng_info->show_warning++;
4858 }
4859 if (memcmp(type,mng_sBIT,4) == 0)
4860 {
4861 if (length < 4)
4862 mng_info->have_global_sbit=MagickFalse;
4863 else
4864 {
4865 mng_info->global_sbit.gray=p[0];
4866 mng_info->global_sbit.red=p[0];
4867 mng_info->global_sbit.green=p[1];
4868 mng_info->global_sbit.blue=p[2];
4869 mng_info->global_sbit.alpha=p[3];
4870 mng_info->have_global_sbit=MagickTrue;
4871 }
4872 }
4873 if (memcmp(type,mng_pHYs,4) == 0)
4874 {
4875 if (length > 8)
4876 {
4877 mng_info->global_x_pixels_per_unit=
cristybb503372010-05-27 20:51:26 +00004878 (size_t) mng_get_ssize_t(p);
cristy3ed852e2009-09-05 21:47:34 +00004879 mng_info->global_y_pixels_per_unit=
cristybb503372010-05-27 20:51:26 +00004880 (size_t) mng_get_ssize_t(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004881 mng_info->global_phys_unit_type=p[8];
4882 mng_info->have_global_phys=MagickTrue;
4883 }
4884 else
4885 mng_info->have_global_phys=MagickFalse;
4886 }
4887 if (memcmp(type,mng_pHYg,4) == 0)
4888 {
4889 if (mng_info->phyg_warning == 0)
4890 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4891 CoderError,"pHYg is not implemented.","`%s'",image->filename);
4892 mng_info->phyg_warning++;
4893 }
4894 if (memcmp(type,mng_BASI,4) == 0)
4895 {
4896 skip_to_iend=MagickTrue;
4897 if (mng_info->basi_warning == 0)
4898 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4899 CoderError,"BASI is not implemented yet","`%s'",
4900 image->filename);
4901 mng_info->basi_warning++;
4902#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00004903 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004904 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004905 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004906 (p[6] << 8) | p[7]);
4907 basi_color_type=p[8];
4908 basi_compression_method=p[9];
4909 basi_filter_type=p[10];
4910 basi_interlace_method=p[11];
4911 if (length > 11)
4912 basi_red=(p[12] << 8) & p[13];
4913 else
4914 basi_red=0;
4915 if (length > 13)
4916 basi_green=(p[14] << 8) & p[15];
4917 else
4918 basi_green=0;
4919 if (length > 15)
4920 basi_blue=(p[16] << 8) & p[17];
4921 else
4922 basi_blue=0;
4923 if (length > 17)
4924 basi_alpha=(p[18] << 8) & p[19];
4925 else
4926 {
4927 if (basi_sample_depth == 16)
4928 basi_alpha=65535L;
4929 else
4930 basi_alpha=255;
4931 }
4932 if (length > 19)
4933 basi_viewable=p[20];
4934 else
4935 basi_viewable=0;
4936#endif
4937 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4938 continue;
4939 }
4940 if (memcmp(type,mng_IHDR,4)
4941#if defined(JNG_SUPPORTED)
4942 && memcmp(type,mng_JHDR,4)
4943#endif
4944 )
4945 {
4946 /* Not an IHDR or JHDR chunk */
4947 if (length)
4948 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4949 continue;
4950 }
4951/* Process IHDR */
4952 if (logging != MagickFalse)
4953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4954 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
4955 mng_info->exists[object_id]=MagickTrue;
4956 mng_info->viewable[object_id]=MagickTrue;
4957 if (mng_info->invisible[object_id])
4958 {
4959 if (logging != MagickFalse)
4960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4961 " Skipping invisible object");
4962 skip_to_iend=MagickTrue;
4963 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4964 continue;
4965 }
4966#if defined(MNG_INSERT_LAYERS)
4967 if (length < 8)
4968 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristybb503372010-05-27 20:51:26 +00004969 image_width=(size_t) mng_get_ssize_t(p);
4970 image_height=(size_t) mng_get_ssize_t(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004971#endif
4972 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4973
4974 /*
4975 Insert a transparent background layer behind the entire animation
4976 if it is not full screen.
4977 */
4978#if defined(MNG_INSERT_LAYERS)
4979 if (insert_layers && mng_type && first_mng_object)
4980 {
4981 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
4982 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00004983 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00004984 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00004985 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00004986 {
4987 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4988 {
4989 /*
4990 Allocate next image structure.
4991 */
4992 AcquireNextImage(image_info,image);
4993 if (GetNextImageInList(image) == (Image *) NULL)
4994 {
4995 image=DestroyImageList(image);
4996 MngInfoFreeStruct(mng_info,&have_mng_structure);
4997 return((Image *) NULL);
4998 }
4999 image=SyncNextImageInList(image);
5000 }
5001 mng_info->image=image;
5002 if (term_chunk_found)
5003 {
5004 image->start_loop=MagickTrue;
5005 image->iterations=mng_iterations;
5006 term_chunk_found=MagickFalse;
5007 }
5008 else
5009 image->start_loop=MagickFalse;
5010 /*
5011 Make a background rectangle.
5012 */
5013 image->delay=0;
5014 image->columns=mng_info->mng_width;
5015 image->rows=mng_info->mng_height;
5016 image->page.width=mng_info->mng_width;
5017 image->page.height=mng_info->mng_height;
5018 image->page.x=0;
5019 image->page.y=0;
5020 image->background_color=mng_background_color;
5021 (void) SetImageBackgroundColor(image);
5022 if (logging != MagickFalse)
5023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5024 " Inserted transparent background layer, W=%lud, H=%lud",
cristyf2faecf2010-05-28 19:19:36 +00005025 (unsigned long) mng_info->mng_width,(unsigned long)
5026 mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005027 }
5028 }
5029 /*
5030 Insert a background layer behind the upcoming image if
5031 framing_mode is 3, and we haven't already inserted one.
5032 */
5033 if (insert_layers && (mng_info->framing_mode == 3) &&
5034 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5035 (simplicity & 0x08)))
5036 {
5037 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5038 {
5039 /*
5040 Allocate next image structure.
5041 */
5042 AcquireNextImage(image_info,image);
5043 if (GetNextImageInList(image) == (Image *) NULL)
5044 {
5045 image=DestroyImageList(image);
5046 MngInfoFreeStruct(mng_info,&have_mng_structure);
5047 return((Image *) NULL);
5048 }
5049 image=SyncNextImageInList(image);
5050 }
5051 mng_info->image=image;
5052 if (term_chunk_found)
5053 {
5054 image->start_loop=MagickTrue;
5055 image->iterations=mng_iterations;
5056 term_chunk_found=MagickFalse;
5057 }
5058 else
5059 image->start_loop=MagickFalse;
5060 image->delay=0;
5061 image->columns=subframe_width;
5062 image->rows=subframe_height;
5063 image->page.width=subframe_width;
5064 image->page.height=subframe_height;
5065 image->page.x=mng_info->clip.left;
5066 image->page.y=mng_info->clip.top;
5067 image->background_color=mng_background_color;
5068 image->matte=MagickFalse;
5069 (void) SetImageBackgroundColor(image);
5070 if (logging != MagickFalse)
5071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5072 " Inserted background layer, L=%ld, R=%ld, T=%ld, B=%ld",
cristyf2faecf2010-05-28 19:19:36 +00005073 (long) mng_info->clip.left,(long) mng_info->clip.right,
5074 (long) mng_info->clip.top,(long) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005075 }
5076#endif /* MNG_INSERT_LAYERS */
5077 first_mng_object=MagickFalse;
5078 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5079 {
5080 /*
5081 Allocate next image structure.
5082 */
5083 AcquireNextImage(image_info,image);
5084 if (GetNextImageInList(image) == (Image *) NULL)
5085 {
5086 image=DestroyImageList(image);
5087 MngInfoFreeStruct(mng_info,&have_mng_structure);
5088 return((Image *) NULL);
5089 }
5090 image=SyncNextImageInList(image);
5091 }
5092 mng_info->image=image;
5093 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5094 GetBlobSize(image));
5095 if (status == MagickFalse)
5096 break;
5097 if (term_chunk_found)
5098 {
5099 image->start_loop=MagickTrue;
5100 term_chunk_found=MagickFalse;
5101 }
5102 else
5103 image->start_loop=MagickFalse;
5104 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5105 {
5106 image->delay=frame_delay;
5107 frame_delay=default_frame_delay;
5108 }
5109 else
5110 image->delay=0;
5111 image->page.width=mng_info->mng_width;
5112 image->page.height=mng_info->mng_height;
5113 image->page.x=mng_info->x_off[object_id];
5114 image->page.y=mng_info->y_off[object_id];
5115 image->iterations=mng_iterations;
5116 /*
5117 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5118 */
5119 if (logging != MagickFalse)
5120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5121 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5122 type[2],type[3]);
cristybb503372010-05-27 20:51:26 +00005123 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
cristy3ed852e2009-09-05 21:47:34 +00005124 if (offset < 0)
5125 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5126 }
5127
5128 previous=image;
5129 mng_info->image=image;
5130 mng_info->mng_type=mng_type;
5131 mng_info->object_id=object_id;
5132
5133 if (memcmp(type,mng_IHDR,4) == 0)
5134 image=ReadOnePNGImage(mng_info,image_info,exception);
5135#if defined(JNG_SUPPORTED)
5136 else
5137 image=ReadOneJNGImage(mng_info,image_info,exception);
5138#endif
5139
5140 if (image == (Image *) NULL)
5141 {
5142 if (IsImageObject(previous) != MagickFalse)
5143 {
5144 (void) DestroyImageList(previous);
5145 (void) CloseBlob(previous);
5146 }
5147 MngInfoFreeStruct(mng_info,&have_mng_structure);
5148 return((Image *) NULL);
5149 }
5150 if (image->columns == 0 || image->rows == 0)
5151 {
5152 (void) CloseBlob(image);
5153 image=DestroyImageList(image);
5154 MngInfoFreeStruct(mng_info,&have_mng_structure);
5155 return((Image *) NULL);
5156 }
5157 mng_info->image=image;
5158
5159 if (mng_type)
5160 {
5161 MngBox
5162 crop_box;
5163
5164 if (mng_info->magn_methx || mng_info->magn_methy)
5165 {
5166 png_uint_32
5167 magnified_height,
5168 magnified_width;
5169
5170 if (logging != MagickFalse)
5171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5172 " Processing MNG MAGN chunk");
5173
5174 if (mng_info->magn_methx == 1)
5175 {
5176 magnified_width=mng_info->magn_ml;
5177 if (image->columns > 1)
5178 magnified_width += mng_info->magn_mr;
5179 if (image->columns > 2)
5180 magnified_width += (image->columns-2)*(mng_info->magn_mx);
5181 }
5182 else
5183 {
5184 magnified_width=image->columns;
5185 if (image->columns > 1)
5186 magnified_width += mng_info->magn_ml-1;
5187 if (image->columns > 2)
5188 magnified_width += mng_info->magn_mr-1;
5189 if (image->columns > 3)
5190 magnified_width += (image->columns-3)*(mng_info->magn_mx-1);
5191 }
5192 if (mng_info->magn_methy == 1)
5193 {
5194 magnified_height=mng_info->magn_mt;
5195 if (image->rows > 1)
5196 magnified_height += mng_info->magn_mb;
5197 if (image->rows > 2)
5198 magnified_height += (image->rows-2)*(mng_info->magn_my);
5199 }
5200 else
5201 {
5202 magnified_height=image->rows;
5203 if (image->rows > 1)
5204 magnified_height += mng_info->magn_mt-1;
5205 if (image->rows > 2)
5206 magnified_height += mng_info->magn_mb-1;
5207 if (image->rows > 3)
5208 magnified_height += (image->rows-3)*(mng_info->magn_my-1);
5209 }
5210 if (magnified_height > image->rows ||
5211 magnified_width > image->columns)
5212 {
5213 Image
5214 *large_image;
5215
5216 int
5217 yy;
5218
cristybb503372010-05-27 20:51:26 +00005219 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005220 m,
5221 y;
5222
cristybb503372010-05-27 20:51:26 +00005223 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005224 x;
5225
5226 register PixelPacket
5227 *n,
5228 *q;
5229
5230 PixelPacket
5231 *next,
5232 *prev;
5233
5234 png_uint_16
5235 magn_methx,
5236 magn_methy;
5237
5238 /*
5239 Allocate next image structure.
5240 */
5241 if (logging != MagickFalse)
5242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5243 " Allocate magnified image");
5244 AcquireNextImage(image_info,image);
5245 if (GetNextImageInList(image) == (Image *) NULL)
5246 {
5247 image=DestroyImageList(image);
5248 MngInfoFreeStruct(mng_info,&have_mng_structure);
5249 return((Image *) NULL);
5250 }
5251
5252 large_image=SyncNextImageInList(image);
5253
5254 large_image->columns=magnified_width;
5255 large_image->rows=magnified_height;
5256
5257 magn_methx=mng_info->magn_methx;
5258 magn_methy=mng_info->magn_methy;
5259
5260#if (MAGICKCORE_QUANTUM_DEPTH == 32)
5261#define QM unsigned short
5262 if (magn_methx != 1 || magn_methy != 1)
5263 {
5264 /*
5265 Scale pixels to unsigned shorts to prevent
5266 overflow of intermediate values of interpolations
5267 */
cristybb503372010-05-27 20:51:26 +00005268 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005269 {
5270 q=GetAuthenticPixels(image,0,y,image->columns,1,
5271 exception);
cristybb503372010-05-27 20:51:26 +00005272 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005273 {
5274 q->red=ScaleQuantumToShort(q->red);
5275 q->green=ScaleQuantumToShort(q->green);
5276 q->blue=ScaleQuantumToShort(q->blue);
5277 q->opacity=ScaleQuantumToShort(q->opacity);
5278 q++;
5279 }
5280 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5281 break;
5282 }
5283 }
5284#else
5285#define QM Quantum
5286#endif
5287
5288 if (image->matte != MagickFalse)
5289 (void) SetImageBackgroundColor(large_image);
5290 else
5291 {
5292 large_image->background_color.opacity=OpaqueOpacity;
5293 (void) SetImageBackgroundColor(large_image);
5294 if (magn_methx == 4)
5295 magn_methx=2;
5296 if (magn_methx == 5)
5297 magn_methx=3;
5298 if (magn_methy == 4)
5299 magn_methy=2;
5300 if (magn_methy == 5)
5301 magn_methy=3;
5302 }
5303
5304 /* magnify the rows into the right side of the large image */
5305
5306 if (logging != MagickFalse)
5307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00005308 " Magnify the rows to %lu",(unsigned long)
5309 large_image->rows);
cristybb503372010-05-27 20:51:26 +00005310 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005311 yy=0;
5312 length=(size_t) image->columns;
5313 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5314 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5315 if ((prev == (PixelPacket *) NULL) ||
5316 (next == (PixelPacket *) NULL))
5317 {
5318 image=DestroyImageList(image);
5319 MngInfoFreeStruct(mng_info,&have_mng_structure);
5320 ThrowReaderException(ResourceLimitError,
5321 "MemoryAllocationFailed");
5322 }
5323 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5324 (void) CopyMagickMemory(next,n,length);
cristybb503372010-05-27 20:51:26 +00005325 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005326 {
5327 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005328 m=(ssize_t) mng_info->magn_mt;
5329 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5330 m=(ssize_t) mng_info->magn_mb;
5331 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5332 m=(ssize_t) mng_info->magn_mb;
5333 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005334 m=1;
5335 else
cristybb503372010-05-27 20:51:26 +00005336 m=(ssize_t) mng_info->magn_my;
cristy3ed852e2009-09-05 21:47:34 +00005337 n=prev;
5338 prev=next;
5339 next=n;
cristybb503372010-05-27 20:51:26 +00005340 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005341 {
5342 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5343 exception);
5344 (void) CopyMagickMemory(next,n,length);
5345 }
5346 for (i=0; i < m; i++, yy++)
5347 {
5348 register PixelPacket
5349 *pixels;
5350
cristybb503372010-05-27 20:51:26 +00005351 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005352 pixels=prev;
5353 n=next;
5354 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5355 1,exception);
5356 q+=(large_image->columns-image->columns);
cristybb503372010-05-27 20:51:26 +00005357 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005358 {
cristy5c6f7892010-05-05 22:53:29 +00005359 /* TO DO: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005360 /*
5361 if (image->storage_class == PseudoClass)
5362 {
5363 }
5364 */
5365
5366 if (magn_methy <= 1)
5367 {
5368 *q=(*pixels); /* replicate previous */
5369 }
5370 else if (magn_methy == 2 || magn_methy == 4)
5371 {
5372 if (i == 0)
5373 *q=(*pixels);
5374 else
5375 {
5376 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005377 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5378 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005379 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005380 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5381 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005382 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005383 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5384 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005385 +(*pixels).blue);
5386 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005387 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005388 (2*i*((*n).opacity
5389 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005390 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005391 }
5392 if (magn_methy == 4)
5393 {
5394 /* Replicate nearest */
5395 if (i <= ((m+1) << 1))
5396 (*q).opacity=(*pixels).opacity+0;
5397 else
5398 (*q).opacity=(*n).opacity+0;
5399 }
5400 }
5401 else /* if (magn_methy == 3 || magn_methy == 5) */
5402 {
5403 /* Replicate nearest */
5404 if (i <= ((m+1) << 1))
5405 *q=(*pixels);
5406 else
5407 *q=(*n);
5408 if (magn_methy == 5)
5409 {
cristybb503372010-05-27 20:51:26 +00005410 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5411 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005412 +(*pixels).opacity);
5413 }
5414 }
5415 n++;
5416 q++;
5417 pixels++;
5418 } /* x */
5419 if (SyncAuthenticPixels(large_image,exception) == 0)
5420 break;
5421 } /* i */
5422 } /* y */
5423 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5424 next=(PixelPacket *) RelinquishMagickMemory(next);
5425
5426 length=image->columns;
5427
5428 if (logging != MagickFalse)
5429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5430 " Delete original image");
5431
5432 DeleteImageFromList(&image);
5433
5434 image=large_image;
5435
5436 mng_info->image=image;
5437
5438 /* magnify the columns */
5439 if (logging != MagickFalse)
5440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00005441 " Magnify the columns to %lu",(unsigned long)
5442 image->columns);
cristy3ed852e2009-09-05 21:47:34 +00005443
cristybb503372010-05-27 20:51:26 +00005444 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005445 {
5446 register PixelPacket
5447 *pixels;
5448
5449 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5450 pixels=q+(image->columns-length);
5451 n=pixels+1;
cristybb503372010-05-27 20:51:26 +00005452 for (x=(ssize_t) (image->columns-length);
5453 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005454 {
cristybb503372010-05-27 20:51:26 +00005455 if (x == (ssize_t) (image->columns-length))
5456 m=(ssize_t) mng_info->magn_ml;
5457 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5458 m=(ssize_t) mng_info->magn_mr;
5459 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5460 m=(ssize_t) mng_info->magn_mr;
5461 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00005462 m=1;
5463 else
cristybb503372010-05-27 20:51:26 +00005464 m=(ssize_t) mng_info->magn_mx;
cristy3ed852e2009-09-05 21:47:34 +00005465 for (i=0; i < m; i++)
5466 {
5467 if (magn_methx <= 1)
5468 {
5469 /* replicate previous */
5470 *q=(*pixels);
5471 }
5472 else if (magn_methx == 2 || magn_methx == 4)
5473 {
5474 if (i == 0)
5475 *q=(*pixels);
5476 else
5477 {
5478 /* Interpolate */
5479 (*q).red=(QM) ((2*i*((*n).red
5480 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00005481 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00005482 (*q).green=(QM) ((2*i*((*n).green
5483 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00005484 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00005485 (*q).blue=(QM) ((2*i*((*n).blue
5486 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00005487 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00005488 if (image->matte != MagickFalse)
5489 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00005490 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005491 +(*pixels).opacity);
5492 }
5493 if (magn_methx == 4)
5494 {
5495 /* Replicate nearest */
5496 if (i <= ((m+1) << 1))
5497 (*q).opacity=(*pixels).opacity+0;
5498 else
5499 (*q).opacity=(*n).opacity+0;
5500 }
5501 }
5502 else /* if (magn_methx == 3 || magn_methx == 5) */
5503 {
5504 /* Replicate nearest */
5505 if (i <= ((m+1) << 1))
5506 *q=(*pixels);
5507 else
5508 *q=(*n);
5509 if (magn_methx == 5)
5510 {
5511 /* Interpolate */
5512 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00005513 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005514 +(*pixels).opacity);
5515 }
5516 }
5517 q++;
5518 }
5519 n++;
5520 p++;
5521 }
5522 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5523 break;
5524 }
5525#if (MAGICKCORE_QUANTUM_DEPTH == 32)
5526 if (magn_methx != 1 || magn_methy != 1)
5527 {
5528 /*
5529 Rescale pixels to Quantum
5530 */
cristybb503372010-05-27 20:51:26 +00005531 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005532 {
5533 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristybb503372010-05-27 20:51:26 +00005534 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005535 {
5536 q->red=ScaleShortToQuantum(q->red);
5537 q->green=ScaleShortToQuantum(q->green);
5538 q->blue=ScaleShortToQuantum(q->blue);
5539 q->opacity=ScaleShortToQuantum(q->opacity);
5540 q++;
5541 }
5542 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5543 break;
5544 }
5545 }
5546#endif
5547 if (logging != MagickFalse)
5548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5549 " Finished MAGN processing");
5550 }
5551 }
5552
5553 /*
5554 Crop_box is with respect to the upper left corner of the MNG.
5555 */
5556 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5557 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5558 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5559 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5560 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5561 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5562 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5563 if ((crop_box.left != (mng_info->image_box.left
5564 +mng_info->x_off[object_id])) ||
5565 (crop_box.right != (mng_info->image_box.right
5566 +mng_info->x_off[object_id])) ||
5567 (crop_box.top != (mng_info->image_box.top
5568 +mng_info->y_off[object_id])) ||
5569 (crop_box.bottom != (mng_info->image_box.bottom
5570 +mng_info->y_off[object_id])))
5571 {
5572 if (logging != MagickFalse)
5573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5574 " Crop the PNG image");
5575 if ((crop_box.left < crop_box.right) &&
5576 (crop_box.top < crop_box.bottom))
5577 {
5578 Image
5579 *im;
5580
5581 RectangleInfo
5582 crop_info;
5583
5584 /*
5585 Crop_info is with respect to the upper left corner of
5586 the image.
5587 */
5588 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5589 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00005590 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5591 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00005592 image->page.width=image->columns;
5593 image->page.height=image->rows;
5594 image->page.x=0;
5595 image->page.y=0;
5596 im=CropImage(image,&crop_info,exception);
5597 if (im != (Image *) NULL)
5598 {
5599 image->columns=im->columns;
5600 image->rows=im->rows;
5601 im=DestroyImage(im);
5602 image->page.width=image->columns;
5603 image->page.height=image->rows;
5604 image->page.x=crop_box.left;
5605 image->page.y=crop_box.top;
5606 }
5607 }
5608 else
5609 {
5610 /*
5611 No pixels in crop area. The MNG spec still requires
5612 a layer, though, so make a single transparent pixel in
5613 the top left corner.
5614 */
5615 image->columns=1;
5616 image->rows=1;
5617 image->colors=2;
5618 (void) SetImageBackgroundColor(image);
5619 image->page.width=1;
5620 image->page.height=1;
5621 image->page.x=0;
5622 image->page.y=0;
5623 }
5624 }
5625#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
5626 image=mng_info->image;
5627#endif
5628 }
5629
5630#if (MAGICKCORE_QUANTUM_DEPTH == 16) /* TO DO: treat Q:32 */
5631 /* Determine if bit depth can be reduced from 16 to 8.
5632 * Note that the method GetImageDepth doesn't check background
5633 * and doesn't handle PseudoClass specially. Also it uses
5634 * multiplication and division by 257 instead of shifting, so
5635 * might be slower.
5636 */
5637 if (mng_info->optimize && image->depth == 16)
5638 {
5639 int
5640 ok_to_reduce;
5641
5642 const PixelPacket
5643 *p;
5644
cristybb503372010-05-27 20:51:26 +00005645 ok_to_reduce=(((((size_t) image->background_color.red >> 8) &
cristy3ed852e2009-09-05 21:47:34 +00005646 0xff)
cristybb503372010-05-27 20:51:26 +00005647 == ((size_t) image->background_color.red & 0xff)) &&
5648 ((((size_t) image->background_color.green >> 8) & 0xff)
5649 == ((size_t) image->background_color.green & 0xff)) &&
5650 ((((size_t) image->background_color.blue >> 8) & 0xff)
5651 == ((size_t) image->background_color.blue & 0xff)));
cristy3ed852e2009-09-05 21:47:34 +00005652 if (ok_to_reduce && image->storage_class == PseudoClass)
5653 {
5654 int indx;
5655
cristybb503372010-05-27 20:51:26 +00005656 for (indx=0; indx < (ssize_t) image->colors; indx++)
cristy3ed852e2009-09-05 21:47:34 +00005657 {
cristybb503372010-05-27 20:51:26 +00005658 ok_to_reduce=(((((size_t) image->colormap[indx].red >>
cristy3ed852e2009-09-05 21:47:34 +00005659 8) & 0xff)
cristybb503372010-05-27 20:51:26 +00005660 == ((size_t) image->colormap[indx].red & 0xff)) &&
5661 ((((size_t) image->colormap[indx].green >> 8) & 0xff)
5662 == ((size_t) image->colormap[indx].green & 0xff)) &&
5663 ((((size_t) image->colormap[indx].blue >> 8) & 0xff)
5664 == ((size_t) image->colormap[indx].blue & 0xff)));
cristy3ed852e2009-09-05 21:47:34 +00005665 if (ok_to_reduce == MagickFalse)
5666 break;
5667 }
5668 }
5669 if ((ok_to_reduce != MagickFalse) &&
5670 (image->storage_class != PseudoClass))
5671 {
cristybb503372010-05-27 20:51:26 +00005672 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005673 y;
5674
cristybb503372010-05-27 20:51:26 +00005675 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005676 x;
5677
cristybb503372010-05-27 20:51:26 +00005678 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005679 {
5680 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
5681 if (p == (const PixelPacket *) NULL)
5682 break;
cristybb503372010-05-27 20:51:26 +00005683 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005684 {
5685 ok_to_reduce=((
cristybb503372010-05-27 20:51:26 +00005686 (((size_t) p->red >> 8) & 0xff) ==
5687 ((size_t) p->red & 0xff)) &&
5688 ((((size_t) p->green >> 8) & 0xff) ==
5689 ((size_t) p->green & 0xff)) &&
5690 ((((size_t) p->blue >> 8) & 0xff) ==
5691 ((size_t) p->blue & 0xff)) &&
cristy3ed852e2009-09-05 21:47:34 +00005692 (((!image->matte ||
cristybb503372010-05-27 20:51:26 +00005693 (((size_t) p->opacity >> 8) & 0xff) ==
5694 ((size_t) p->opacity & 0xff)))));
cristy3ed852e2009-09-05 21:47:34 +00005695 if (ok_to_reduce == 0)
5696 break;
5697 p++;
5698 }
5699 if (x != 0)
5700 break;
5701 }
5702 }
5703 if (ok_to_reduce)
5704 {
5705 image->depth=8;
5706 if (logging != MagickFalse)
5707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5708 " Reducing PNG bit depth to 8 without loss of info");
5709 }
5710 }
5711#endif
5712 GetImageException(image,exception);
5713 if (image_info->number_scenes != 0)
5714 {
5715 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00005716 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00005717 break;
5718 }
5719 if (logging != MagickFalse)
5720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5721 " Finished reading image datastream.");
5722 } while (LocaleCompare(image_info->magick,"MNG") == 0);
5723 (void) CloseBlob(image);
5724 if (logging != MagickFalse)
5725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5726 " Finished reading all image datastreams.");
5727#if defined(MNG_INSERT_LAYERS)
5728 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
5729 (mng_info->mng_height))
5730 {
5731 /*
5732 Insert a background layer if nothing else was found.
5733 */
5734 if (logging != MagickFalse)
5735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5736 " No images found. Inserting a background layer.");
5737 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5738 {
5739 /*
5740 Allocate next image structure.
5741 */
5742 AcquireNextImage(image_info,image);
5743 if (GetNextImageInList(image) == (Image *) NULL)
5744 {
5745 image=DestroyImageList(image);
5746 MngInfoFreeStruct(mng_info,&have_mng_structure);
5747 if (logging != MagickFalse)
5748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5749 " Allocation failed, returning NULL.");
5750 return((Image *) NULL);
5751 }
5752 image=SyncNextImageInList(image);
5753 }
5754 image->columns=mng_info->mng_width;
5755 image->rows=mng_info->mng_height;
5756 image->page.width=mng_info->mng_width;
5757 image->page.height=mng_info->mng_height;
5758 image->page.x=0;
5759 image->page.y=0;
5760 image->background_color=mng_background_color;
5761 image->matte=MagickFalse;
5762 if (image_info->ping == MagickFalse)
5763 (void) SetImageBackgroundColor(image);
5764 mng_info->image_found++;
5765 }
5766#endif
5767 image->iterations=mng_iterations;
5768 if (mng_iterations == 1)
5769 image->start_loop=MagickTrue;
5770 while (GetPreviousImageInList(image) != (Image *) NULL)
5771 {
5772 image_count++;
5773 if (image_count > 10*mng_info->image_found)
5774 {
5775 if (logging != MagickFalse)
5776 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
5777 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5778 CoderError,"Linked list is corrupted, beginning of list not found",
5779 "`%s'",image_info->filename);
5780 return((Image *) NULL);
5781 }
5782 image=GetPreviousImageInList(image);
5783 if (GetNextImageInList(image) == (Image *) NULL)
5784 {
5785 if (logging != MagickFalse)
5786 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
5787 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5788 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
5789 image_info->filename);
5790 }
5791 }
5792 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
5793 GetNextImageInList(image) ==
5794 (Image *) NULL)
5795 {
5796 if (logging != MagickFalse)
5797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5798 " First image null");
5799 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5800 CoderError,"image->next for first image is NULL but shouldn't be.",
5801 "`%s'",image_info->filename);
5802 }
5803 if (mng_info->image_found == 0)
5804 {
5805 if (logging != MagickFalse)
5806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5807 " No visible images found.");
5808 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5809 CoderError,"No visible images in file","`%s'",image_info->filename);
5810 if (image != (Image *) NULL)
5811 image=DestroyImageList(image);
5812 MngInfoFreeStruct(mng_info,&have_mng_structure);
5813 return((Image *) NULL);
5814 }
5815
5816 if (mng_info->ticks_per_second)
5817 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
5818 final_delay/mng_info->ticks_per_second;
5819 else
5820 image->start_loop=MagickTrue;
5821 /* Find final nonzero image delay */
5822 final_image_delay=0;
5823 while (GetNextImageInList(image) != (Image *) NULL)
5824 {
5825 if (image->delay)
5826 final_image_delay=image->delay;
5827 image=GetNextImageInList(image);
5828 }
5829 if (final_delay < final_image_delay)
5830 final_delay=final_image_delay;
5831 image->delay=final_delay;
5832 if (logging != MagickFalse)
5833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00005834 " image->delay=%lu, final_delay=%lu",(unsigned long) image->delay,
5835 (unsigned long) final_delay);
cristy3ed852e2009-09-05 21:47:34 +00005836 if (logging != MagickFalse)
5837 {
5838 int
5839 scene;
5840
5841 scene=0;
5842 image=GetFirstImageInList(image);
5843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5844 " Before coalesce:");
5845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00005846 " scene 0 delay=%lu",(unsigned long) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00005847 while (GetNextImageInList(image) != (Image *) NULL)
5848 {
5849 image=GetNextImageInList(image);
5850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00005851 " scene %ld delay=%lu",(long) scene++,(unsigned long)
5852 image->delay);
cristy3ed852e2009-09-05 21:47:34 +00005853 }
5854 }
5855
5856 image=GetFirstImageInList(image);
5857#ifdef MNG_COALESCE_LAYERS
5858 if (insert_layers)
5859 {
5860 Image
5861 *next_image,
5862 *next;
5863
cristybb503372010-05-27 20:51:26 +00005864 size_t
cristy3ed852e2009-09-05 21:47:34 +00005865 scene;
5866
5867 if (logging != MagickFalse)
5868 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
5869 scene=image->scene;
5870 next_image=CoalesceImages(image,&image->exception);
5871 if (next_image == (Image *) NULL)
5872 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5873 image=DestroyImageList(image);
5874 image=next_image;
5875 for (next=image; next != (Image *) NULL; next=next_image)
5876 {
5877 next->page.width=mng_info->mng_width;
5878 next->page.height=mng_info->mng_height;
5879 next->page.x=0;
5880 next->page.y=0;
5881 next->scene=scene++;
5882 next_image=GetNextImageInList(next);
5883 if (next_image == (Image *) NULL)
5884 break;
5885 if (next->delay == 0)
5886 {
5887 scene--;
5888 next_image->previous=GetPreviousImageInList(next);
5889 if (GetPreviousImageInList(next) == (Image *) NULL)
5890 image=next_image;
5891 else
5892 next->previous->next=next_image;
5893 next=DestroyImage(next);
5894 }
5895 }
5896 }
5897#endif
5898
5899 while (GetNextImageInList(image) != (Image *) NULL)
5900 image=GetNextImageInList(image);
5901 image->dispose=BackgroundDispose;
5902
5903 if (logging != MagickFalse)
5904 {
5905 int
5906 scene;
5907
5908 scene=0;
5909 image=GetFirstImageInList(image);
5910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5911 " After coalesce:");
5912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00005913 " scene 0 delay=%lu dispose=%ld",(unsigned long) image->delay,
5914 (long) image->dispose);
cristy3ed852e2009-09-05 21:47:34 +00005915 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00005916 {
5917 image=GetNextImageInList(image);
5918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5919 " scene %ld delay=%lu dispose=%ld",(long) scene++,
5920 (unsigned long) image->delay,(long) image->dispose);
5921 }
5922 }
cristy3ed852e2009-09-05 21:47:34 +00005923 image=GetFirstImageInList(image);
5924 MngInfoFreeStruct(mng_info,&have_mng_structure);
5925 have_mng_structure=MagickFalse;
5926 if (logging != MagickFalse)
5927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
5928 return(GetFirstImageInList(image));
5929}
glennrp25c1e2b2010-03-25 01:39:56 +00005930#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00005931static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5932{
5933 printf("Your PNG library is too old: You have libpng-%s\n",
5934 PNG_LIBPNG_VER_STRING);
5935 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
5936 "PNG library is too old","`%s'",image_info->filename);
5937 return(Image *) NULL;
5938}
5939static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5940{
5941 return(ReadPNGImage(image_info,exception));
5942}
glennrp25c1e2b2010-03-25 01:39:56 +00005943#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00005944#endif
5945
5946/*
5947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5948% %
5949% %
5950% %
5951% R e g i s t e r P N G I m a g e %
5952% %
5953% %
5954% %
5955%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5956%
5957% RegisterPNGImage() adds properties for the PNG image format to
5958% the list of supported formats. The properties include the image format
5959% tag, a method to read and/or write the format, whether the format
5960% supports the saving of more than one frame to the same file or blob,
5961% whether the format supports native in-memory I/O, and a brief
5962% description of the format.
5963%
5964% The format of the RegisterPNGImage method is:
5965%
cristybb503372010-05-27 20:51:26 +00005966% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00005967%
5968*/
cristybb503372010-05-27 20:51:26 +00005969ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00005970{
5971 char
5972 version[MaxTextExtent];
5973
5974 MagickInfo
5975 *entry;
5976
5977 static const char
5978 *PNGNote=
5979 {
5980 "See http://www.libpng.org/ for details about the PNG format."
5981 },
5982 *JNGNote=
5983 {
5984 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
5985 "format."
5986 },
5987 *MNGNote=
5988 {
5989 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
5990 "format."
5991 };
5992
5993 *version='\0';
5994#if defined(PNG_LIBPNG_VER_STRING)
5995 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
5996 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00005997 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
5998 {
5999 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6000 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6001 MaxTextExtent);
6002 }
6003#endif
cristy3ed852e2009-09-05 21:47:34 +00006004 entry=SetMagickInfo("MNG");
6005 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6006#if defined(MAGICKCORE_PNG_DELEGATE)
6007 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6008 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6009#endif
6010 entry->magick=(IsImageFormatHandler *) IsMNG;
6011 entry->description=ConstantString("Multiple-image Network Graphics");
6012 if (*version != '\0')
6013 entry->version=ConstantString(version);
6014 entry->module=ConstantString("PNG");
6015 entry->note=ConstantString(MNGNote);
6016 (void) RegisterMagickInfo(entry);
6017
6018 entry=SetMagickInfo("PNG");
6019#if defined(MAGICKCORE_PNG_DELEGATE)
6020 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6021 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6022#endif
6023 entry->magick=(IsImageFormatHandler *) IsPNG;
6024 entry->adjoin=MagickFalse;
6025 entry->description=ConstantString("Portable Network Graphics");
6026 entry->module=ConstantString("PNG");
6027 if (*version != '\0')
6028 entry->version=ConstantString(version);
6029 entry->note=ConstantString(PNGNote);
6030 (void) RegisterMagickInfo(entry);
6031
6032 entry=SetMagickInfo("PNG8");
6033#if defined(MAGICKCORE_PNG_DELEGATE)
6034 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6035 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6036#endif
6037 entry->magick=(IsImageFormatHandler *) IsPNG;
6038 entry->adjoin=MagickFalse;
6039 entry->description=ConstantString(
6040 "8-bit indexed with optional binary transparency");
6041 entry->module=ConstantString("PNG");
6042 (void) RegisterMagickInfo(entry);
6043
6044 entry=SetMagickInfo("PNG24");
6045 *version='\0';
6046#if defined(ZLIB_VERSION)
6047 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6048 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6049 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6050 {
6051 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6052 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6053 }
6054#endif
6055 if (*version != '\0')
6056 entry->version=ConstantString(version);
6057#if defined(MAGICKCORE_PNG_DELEGATE)
6058 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6059 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6060#endif
6061 entry->magick=(IsImageFormatHandler *) IsPNG;
6062 entry->adjoin=MagickFalse;
6063 entry->description=ConstantString("opaque 24-bit RGB");
6064 entry->module=ConstantString("PNG");
6065 (void) RegisterMagickInfo(entry);
6066
6067 entry=SetMagickInfo("PNG32");
6068#if defined(MAGICKCORE_PNG_DELEGATE)
6069 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6070 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6071#endif
6072 entry->magick=(IsImageFormatHandler *) IsPNG;
6073 entry->adjoin=MagickFalse;
6074 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6075 entry->module=ConstantString("PNG");
6076 (void) RegisterMagickInfo(entry);
6077
6078 entry=SetMagickInfo("JNG");
6079#if defined(JNG_SUPPORTED)
6080#if defined(MAGICKCORE_PNG_DELEGATE)
6081 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6082 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6083#endif
6084#endif
6085 entry->magick=(IsImageFormatHandler *) IsJNG;
6086 entry->adjoin=MagickFalse;
6087 entry->description=ConstantString("JPEG Network Graphics");
6088 entry->module=ConstantString("PNG");
6089 entry->note=ConstantString(JNGNote);
6090 (void) RegisterMagickInfo(entry);
cristy18b17442009-10-25 18:36:48 +00006091#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6092 png_semaphore=AllocateSemaphoreInfo();
6093#endif
cristy3ed852e2009-09-05 21:47:34 +00006094 return(MagickImageCoderSignature);
6095}
6096
6097/*
6098%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6099% %
6100% %
6101% %
6102% U n r e g i s t e r P N G I m a g e %
6103% %
6104% %
6105% %
6106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6107%
6108% UnregisterPNGImage() removes format registrations made by the
6109% PNG module from the list of supported formats.
6110%
6111% The format of the UnregisterPNGImage method is:
6112%
6113% UnregisterPNGImage(void)
6114%
6115*/
6116ModuleExport void UnregisterPNGImage(void)
6117{
6118 (void) UnregisterMagickInfo("MNG");
6119 (void) UnregisterMagickInfo("PNG");
6120 (void) UnregisterMagickInfo("PNG8");
6121 (void) UnregisterMagickInfo("PNG24");
6122 (void) UnregisterMagickInfo("PNG32");
6123 (void) UnregisterMagickInfo("JNG");
6124#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristy514e9e72009-11-20 02:12:08 +00006125 if (png_semaphore != (SemaphoreInfo *) NULL)
6126 DestroySemaphoreInfo(&png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006127#endif
6128}
6129
6130#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006131#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006132/*
6133%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6134% %
6135% %
6136% %
6137% W r i t e M N G I m a g e %
6138% %
6139% %
6140% %
6141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6142%
6143% WriteMNGImage() writes an image in the Portable Network Graphics
6144% Group's "Multiple-image Network Graphics" encoded image format.
6145%
6146% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6147%
6148% The format of the WriteMNGImage method is:
6149%
6150% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6151%
6152% A description of each parameter follows.
6153%
6154% o image_info: the image info.
6155%
6156% o image: The image.
6157%
6158%
6159% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6160% "To do" under ReadPNGImage):
6161%
6162% Fix problem with palette sorting (when PNG_SORT_PALETTE is enabled,
6163% some GIF animations don't convert properly)
6164%
6165% Preserve all unknown and not-yet-handled known chunks found in input
6166% PNG file and copy them into output PNG files according to the PNG
6167% copying rules.
6168%
6169% Write the iCCP chunk at MNG level when (icc profile length > 0)
6170%
6171% Improve selection of color type (use indexed-colour or indexed-colour
6172% with tRNS when 256 or fewer unique RGBA values are present).
6173%
6174% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6175% This will be complicated if we limit ourselves to generating MNG-LC
6176% files. For now we ignore disposal method 3 and simply overlay the next
6177% image on it.
6178%
6179% Check for identical PLTE's or PLTE/tRNS combinations and use a
6180% global MNG PLTE or PLTE/tRNS combination when appropriate.
6181% [mostly done 15 June 1999 but still need to take care of tRNS]
6182%
6183% Check for identical sRGB and replace with a global sRGB (and remove
6184% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6185% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6186% local gAMA/cHRM with local sRGB if appropriate).
6187%
6188% Check for identical sBIT chunks and write global ones.
6189%
6190% Provide option to skip writing the signature tEXt chunks.
6191%
6192% Use signatures to detect identical objects and reuse the first
6193% instance of such objects instead of writing duplicate objects.
6194%
6195% Use a smaller-than-32k value of compression window size when
6196% appropriate.
6197%
6198% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6199% ancillary text chunks and save profiles.
6200%
6201% Provide an option to force LC files (to ensure exact framing rate)
6202% instead of VLC.
6203%
6204% Provide an option to force VLC files instead of LC, even when offsets
6205% are present. This will involve expanding the embedded images with a
6206% transparent region at the top and/or left.
6207*/
6208
cristy3ed852e2009-09-05 21:47:34 +00006209static void
6210png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6211 png_info *ping_info, unsigned char *profile_type, unsigned char
6212 *profile_description, unsigned char *profile_data, png_uint_32 length)
6213{
cristy3ed852e2009-09-05 21:47:34 +00006214 png_textp
6215 text;
6216
cristybb503372010-05-27 20:51:26 +00006217 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006218 i;
6219
6220 unsigned char
6221 *sp;
6222
6223 png_charp
6224 dp;
6225
6226 png_uint_32
6227 allocated_length,
6228 description_length;
6229
6230 unsigned char
6231 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006232
cristy3ed852e2009-09-05 21:47:34 +00006233 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6234 return;
6235
6236 if (image_info->verbose)
6237 {
6238 (void) printf("writing raw profile: type=%s, length=%lu\n",
6239 (char *) profile_type, length);
6240 }
cristy3ed852e2009-09-05 21:47:34 +00006241 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6242 description_length=(png_uint_32) strlen((const char *) profile_description);
6243 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6244 + description_length);
6245 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6246 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6247 text[0].key[0]='\0';
6248 (void) ConcatenateMagickString(text[0].key,
6249 "Raw profile type ",MaxTextExtent);
6250 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6251 sp=profile_data;
6252 dp=text[0].text;
6253 *dp++='\n';
6254 (void) CopyMagickString(dp,(const char *) profile_description,
6255 allocated_length);
6256 dp+=description_length;
6257 *dp++='\n';
6258 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006259 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006260 dp+=8;
cristybb503372010-05-27 20:51:26 +00006261 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006262 {
6263 if (i%36 == 0)
6264 *dp++='\n';
6265 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6266 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6267 }
6268 *dp++='\n';
6269 *dp='\0';
6270 text[0].text_length=(png_size_t) (dp-text[0].text);
6271 text[0].compression=image_info->compression == NoCompression ||
6272 (image_info->compression == UndefinedCompression &&
6273 text[0].text_length < 128) ? -1 : 0;
6274 if (text[0].text_length <= allocated_length)
6275 png_set_text(ping,ping_info,text,1);
6276 png_free(ping,text[0].text);
6277 png_free(ping,text[0].key);
6278 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006279}
6280
6281static MagickBooleanType png_write_chunk_from_profile(Image *image,
6282 const char *string, int logging)
6283{
6284 char
6285 *name;
6286
6287 const StringInfo
6288 *profile;
6289
6290 unsigned char
6291 *data;
6292
6293 png_uint_32 length;
6294
6295 ResetImageProfileIterator(image);
6296 for (name=GetNextImageProfile(image); name != (const char *) NULL; ){
6297 profile=GetImageProfile(image,name);
6298 if (profile != (const StringInfo *) NULL)
6299 {
6300 StringInfo
6301 *png_profile;
6302
6303 if (LocaleNCompare(name,string,11) == 0) {
6304 if (logging != MagickFalse)
6305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6306 " Found %s profile",name);
6307
6308 png_profile=CloneStringInfo(profile);
6309 data=GetStringInfoDatum(png_profile),
6310 length=(png_uint_32) GetStringInfoLength(png_profile);
6311 data[4]=data[3];
6312 data[3]=data[2];
6313 data[2]=data[1];
6314 data[1]=data[0];
6315 (void) WriteBlobMSBULong(image,length-5); /* data length */
6316 (void) WriteBlob(image,length-1,data+1);
6317 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6318 png_profile=DestroyStringInfo(png_profile);
6319 }
6320 }
6321 name=GetNextImageProfile(image);
6322 }
6323 return(MagickTrue);
6324}
6325
6326static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6327 const ImageInfo *image_info,Image *image)
6328{
6329/* Write one PNG image */
6330 char
6331 s[2];
6332
6333 const char
6334 *name,
6335 *property,
6336 *value;
6337
6338 const StringInfo
6339 *profile;
6340
6341
6342 int
6343 image_matte,
6344 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006345 pass;
6346
glennrp5af765f2010-03-30 11:12:18 +00006347 png_bytep
6348 ping_trans_alpha;
6349
cristy3ed852e2009-09-05 21:47:34 +00006350 png_colorp
6351 palette;
6352
glennrp5af765f2010-03-30 11:12:18 +00006353 png_color_16
6354 ping_background,
6355 ping_trans_color;
6356
cristy3ed852e2009-09-05 21:47:34 +00006357 png_info
6358 *ping_info;
6359
6360 png_struct
6361 *ping;
6362
glennrp5af765f2010-03-30 11:12:18 +00006363 png_uint_32
6364 ping_height,
6365 ping_width;
6366
cristybb503372010-05-27 20:51:26 +00006367 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006368 y;
6369
6370 MagickBooleanType
6371 status;
6372
6373 QuantumInfo
6374 *quantum_info;
6375
6376 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00006377 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00006378
cristybb503372010-05-27 20:51:26 +00006379 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006380 i,
6381 x;
6382
6383 unsigned char
6384 *png_pixels;
6385
6386 unsigned int
6387 logging,
6388 matte;
6389
glennrp5af765f2010-03-30 11:12:18 +00006390 volatile int
6391 ping_bit_depth,
6392 ping_color_type,
6393 ping_interlace_method,
6394 ping_compression_method,
6395 ping_filter_method,
6396 ping_num_trans;
6397
cristybb503372010-05-27 20:51:26 +00006398 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00006399 image_colors,
glennrp5af765f2010-03-30 11:12:18 +00006400 image_depth,
6401 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006402
cristybb503372010-05-27 20:51:26 +00006403 size_t
cristy3ed852e2009-09-05 21:47:34 +00006404 quality,
6405 rowbytes,
6406 save_image_depth;
6407
6408 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6409 " enter WriteOnePNGImage()");
6410
6411#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00006412 LockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006413#endif
6414
glennrp5af765f2010-03-30 11:12:18 +00006415 /* Initialize some stuff */
6416 ping_bit_depth=0,
6417 ping_color_type=0,
6418 ping_interlace_method=0,
6419 ping_compression_method=0,
6420 ping_filter_method=0,
6421 ping_num_trans = 0;
6422
6423 ping_background.red = 0;
6424 ping_background.green = 0;
6425 ping_background.blue = 0;
6426 ping_background.gray = 0;
6427 ping_background.index = 0;
6428
6429 ping_trans_color.red=0;
6430 ping_trans_color.green=0;
6431 ping_trans_color.blue=0;
6432 ping_trans_color.gray=0;
6433
6434 ping_trans_alpha = NULL;
6435
cristyed552522009-10-16 14:04:35 +00006436 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00006437 image_colors=image->colors;
6438 image_depth=image->depth;
6439 image_matte=image->matte;
6440
6441 if (image->colorspace != RGBColorspace)
6442 (void) TransformImageColorspace(image,RGBColorspace);
6443 mng_info->IsPalette=image->storage_class == PseudoClass &&
cristy057310d2010-01-06 15:53:33 +00006444 image_colors <= 256 && !IsOpaqueImage(image,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00006445 mng_info->optimize=image_info->type == OptimizeType;
6446
6447 /*
6448 Allocate the PNG structures
6449 */
6450#ifdef PNG_USER_MEM_SUPPORTED
6451 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
6452 PNGErrorHandler,PNGWarningHandler,(void *) NULL,
6453 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
6454#else
6455 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
6456 PNGErrorHandler,PNGWarningHandler);
6457#endif
6458 if (ping == (png_struct *) NULL)
6459 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6460 ping_info=png_create_info_struct(ping);
6461 if (ping_info == (png_info *) NULL)
6462 {
6463 png_destroy_write_struct(&ping,(png_info **) NULL);
6464 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6465 }
6466 png_set_write_fn(ping,image,png_put_data,png_flush_data);
6467 png_pixels=(unsigned char *) NULL;
6468
glennrp5af765f2010-03-30 11:12:18 +00006469 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00006470 {
6471 /*
6472 PNG write failed.
6473 */
6474#ifdef PNG_DEBUG
6475 if (image_info->verbose)
6476 (void) printf("PNG write has failed.\n");
6477#endif
6478 png_destroy_write_struct(&ping,&ping_info);
6479#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00006480 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006481#endif
6482 return(MagickFalse);
6483 }
6484 /*
6485 Prepare PNG for writing.
6486 */
6487#if defined(PNG_MNG_FEATURES_SUPPORTED)
6488 if (mng_info->write_mng)
6489 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
6490#else
6491# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
6492 if (mng_info->write_mng)
6493 png_permit_empty_plte(ping,MagickTrue);
6494# endif
6495#endif
6496 x=0;
glennrp5af765f2010-03-30 11:12:18 +00006497 ping_width=image->columns;
6498 ping_height=image->rows;
cristy3ed852e2009-09-05 21:47:34 +00006499 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
6500 image_depth=8;
6501 if (mng_info->write_png_depth != 0)
6502 image_depth=mng_info->write_png_depth;
6503 /* Adjust requested depth to next higher valid depth if necessary */
6504 if (image_depth > 8)
6505 image_depth=16;
6506 if ((image_depth > 4) && (image_depth < 8))
6507 image_depth=8;
6508 if (image_depth == 3)
6509 image_depth=4;
6510 if (logging != MagickFalse)
6511 {
6512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00006513 " width=%lu",(unsigned long) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00006514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00006515 " height=%lu",(unsigned long) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00006516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00006517 " image_matte=%lu",(unsigned long) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00006518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00006519 " image_depth=%lu",(unsigned long) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00006520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00006521 " requested PNG image_depth=%lu",(unsigned long) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00006522 }
6523 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00006524 ping_bit_depth=(png_byte) save_image_depth;
cristy3ed852e2009-09-05 21:47:34 +00006525#if defined(PNG_pHYs_SUPPORTED)
6526 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
6527 (!mng_info->write_mng || !mng_info->equal_physs))
6528 {
6529 int
6530 unit_type;
6531
6532 png_uint_32
6533 x_resolution,
6534 y_resolution;
6535
6536 if (image->units == PixelsPerInchResolution)
6537 {
6538 unit_type=PNG_RESOLUTION_METER;
6539 x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
6540 y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
6541 }
6542 else if (image->units == PixelsPerCentimeterResolution)
6543 {
6544 unit_type=PNG_RESOLUTION_METER;
6545 x_resolution=(png_uint_32) (100.0*image->x_resolution);
6546 y_resolution=(png_uint_32) (100.0*image->y_resolution);
6547 }
6548 else
6549 {
6550 unit_type=PNG_RESOLUTION_UNKNOWN;
6551 x_resolution=(png_uint_32) image->x_resolution;
6552 y_resolution=(png_uint_32) image->y_resolution;
6553 }
6554 png_set_pHYs(ping,ping_info,x_resolution,y_resolution,unit_type);
6555 if (logging != MagickFalse)
6556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6557 " Setting up pHYs chunk");
6558 }
6559#endif
6560#if defined(PNG_oFFs_SUPPORTED)
6561 if (image->page.x || image->page.y)
6562 {
6563 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
6564 (png_int_32) image->page.y, 0);
6565 if (logging != MagickFalse)
6566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6567 " Setting up oFFs chunk");
6568 }
6569#endif
6570 if (image_matte && (!mng_info->adjoin || !mng_info->equal_backgrounds))
6571 {
6572 png_color_16
6573 background;
6574
6575 if (image_depth < MAGICKCORE_QUANTUM_DEPTH)
6576 {
cristybb503372010-05-27 20:51:26 +00006577 size_t
cristy3ed852e2009-09-05 21:47:34 +00006578 maxval;
6579
6580 maxval=(1UL << image_depth)-1;
6581 background.red=(png_uint_16)
6582 (QuantumScale*(maxval*image->background_color.red));
6583 background.green=(png_uint_16)
6584 (QuantumScale*(maxval*image->background_color.green));
6585 background.blue=(png_uint_16)
6586 (QuantumScale*(maxval*image->background_color.blue));
6587 background.gray=(png_uint_16)
6588 (QuantumScale*(maxval*PixelIntensity(&image->background_color)));
6589 }
6590 else
6591 {
6592 background.red=image->background_color.red;
6593 background.green=image->background_color.green;
6594 background.blue=image->background_color.blue;
6595 background.gray=
6596 (png_uint_16) PixelIntensity(&image->background_color);
6597 }
6598 background.index=(png_byte) background.gray;
6599 if (logging != MagickFalse)
6600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6601 " Setting up bKGd chunk");
6602 png_set_bKGD(ping,ping_info,&background);
6603 }
6604 /*
6605 Select the color type.
6606 */
6607 matte=image_matte;
6608 old_bit_depth=0;
6609 if (mng_info->write_png8)
6610 {
glennrp5af765f2010-03-30 11:12:18 +00006611 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6612 ping_bit_depth=8;
6613 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006614 {
6615 /* TO DO: make this a function cause it's used twice, except
6616 for reducing the sample depth from 8. */
6617
6618 QuantizeInfo
6619 quantize_info;
6620
cristybb503372010-05-27 20:51:26 +00006621 size_t
cristy3ed852e2009-09-05 21:47:34 +00006622 number_colors,
6623 save_number_colors;
6624
6625 number_colors=image_colors;
6626 if ((image->storage_class == DirectClass) || (number_colors > 256))
6627 {
6628 GetQuantizeInfo(&quantize_info);
6629 quantize_info.dither=IsPaletteImage(image,&image->exception) ==
6630 MagickFalse ? MagickTrue : MagickFalse;
6631 quantize_info.number_colors= (matte != MagickFalse ? 255UL :
6632 256UL);
6633 (void) QuantizeImage(&quantize_info,image);
6634 number_colors=image_colors;
6635 (void) SyncImage(image);
6636 if (logging != MagickFalse)
6637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00006638 " Colors quantized to %ld",(unsigned long) number_colors);
cristy3ed852e2009-09-05 21:47:34 +00006639 }
6640 if (matte)
glennrp5af765f2010-03-30 11:12:18 +00006641 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00006642 /*
6643 Set image palette.
6644 */
glennrp5af765f2010-03-30 11:12:18 +00006645 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
cristy3ed852e2009-09-05 21:47:34 +00006646#if defined(PNG_SORT_PALETTE)
6647 save_number_colors=image_colors;
6648 if (CompressColormapTransFirst(image) == MagickFalse)
6649 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp98156a32009-12-09 15:32:44 +00006650 number_colors=image->colors;
cristy3ed852e2009-09-05 21:47:34 +00006651 image_colors=save_number_colors;
6652#endif
6653 palette=(png_color *) AcquireQuantumMemory(257,
6654 sizeof(*palette));
6655 if (palette == (png_color *) NULL)
6656 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6657 if (logging != MagickFalse)
6658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00006659 " Setting up PLTE chunk with %d colors",
6660 (int) number_colors);
cristybb503372010-05-27 20:51:26 +00006661 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00006662 {
6663 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6664 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6665 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6666 if (logging != MagickFalse)
6667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6668#if MAGICKCORE_QUANTUM_DEPTH == 8
6669 " %3ld (%3d,%3d,%3d)",
6670#else
6671 " %5ld (%5d,%5d,%5d)",
6672#endif
cristyf2faecf2010-05-28 19:19:36 +00006673 (long) i,palette[i].red,palette[i].green,palette[i].blue);
cristy3ed852e2009-09-05 21:47:34 +00006674
6675 }
6676 if (matte)
6677 {
6678 number_colors++;
6679 palette[i].red=ScaleQuantumToChar((Quantum) QuantumRange);
6680 palette[i].green=ScaleQuantumToChar((Quantum) QuantumRange);
6681 palette[i].blue=ScaleQuantumToChar((Quantum) QuantumRange);
6682 }
6683 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
cristy3ed852e2009-09-05 21:47:34 +00006684 palette=(png_colorp) RelinquishMagickMemory(palette);
glennrp5af765f2010-03-30 11:12:18 +00006685 image_depth=ping_bit_depth;
6686 ping_num_trans=0;
cristy3ed852e2009-09-05 21:47:34 +00006687 if (matte)
6688 {
6689 ExceptionInfo
6690 *exception;
6691
glennrp5af765f2010-03-30 11:12:18 +00006692 int
6693 trans_alpha[256];
6694
cristy3ed852e2009-09-05 21:47:34 +00006695 /*
6696 Identify which colormap entry is transparent.
6697 */
cristy3ed852e2009-09-05 21:47:34 +00006698 assert(number_colors <= 256);
cristybb503372010-05-27 20:51:26 +00006699 for (i=0; i < (ssize_t) number_colors; i++)
glennrp5af765f2010-03-30 11:12:18 +00006700 trans_alpha[i]=255;
cristy3ed852e2009-09-05 21:47:34 +00006701 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +00006702 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006703 {
6704 register const PixelPacket
6705 *p;
6706
6707 p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6708 if (p == (PixelPacket *) NULL)
6709 break;
cristy5c6f7892010-05-05 22:53:29 +00006710 indexes=GetAuthenticIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00006711 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006712 {
6713 if (p->opacity != OpaqueOpacity)
6714 {
cristy5c6f7892010-05-05 22:53:29 +00006715 indexes[x]=(IndexPacket) (number_colors-1);
cristybb503372010-05-27 20:51:26 +00006716 trans_alpha[(ssize_t) indexes[x]]=(png_byte) (255-
cristyce70c172010-01-07 17:15:30 +00006717 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00006718 }
6719 p++;
6720 }
6721 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6722 break;
6723 }
cristybb503372010-05-27 20:51:26 +00006724 for (i=0; i < (ssize_t) number_colors; i++)
glennrp5af765f2010-03-30 11:12:18 +00006725 if (trans_alpha[i] != 255)
6726 ping_num_trans=(unsigned short) (i+1);
6727
6728 if (ping_num_trans == 0)
6729 png_set_invalid(ping, ping_info, PNG_INFO_tRNS);
6730 if (!png_get_valid(ping, ping_info, PNG_INFO_tRNS))
6731 ping_num_trans=0;
6732 if (ping_num_trans != 0)
6733 {
6734 for (i=0; i<256; i++)
6735 ping_trans_alpha[i]=(png_byte) trans_alpha[i];
6736 }
6737
6738 (void) png_set_tRNS(ping, ping_info,
6739 ping_trans_alpha,
6740 ping_num_trans,
6741 &ping_trans_color);
cristy3ed852e2009-09-05 21:47:34 +00006742 }
6743 /*
6744 Identify which colormap entry is the background color.
6745 */
cristybb503372010-05-27 20:51:26 +00006746 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
glennrp5af765f2010-03-30 11:12:18 +00006747 if (IsPNGColorEqual(ping_background,image->colormap[i]))
cristy3ed852e2009-09-05 21:47:34 +00006748 break;
glennrp5af765f2010-03-30 11:12:18 +00006749 ping_background.index=(png_byte) i;
cristy3ed852e2009-09-05 21:47:34 +00006750 }
6751 if (image_matte != MagickFalse)
6752 {
6753 /* TO DO: reduce to binary transparency */
6754 }
6755 } /* end of write_png8 */
6756 else if (mng_info->write_png24)
6757 {
6758 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00006759 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00006760 }
6761 else if (mng_info->write_png32)
6762 {
6763 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00006764 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006765 }
6766 else
6767 {
glennrp5af765f2010-03-30 11:12:18 +00006768 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006769 if (mng_info->write_png_colortype)
6770 {
glennrp5af765f2010-03-30 11:12:18 +00006771 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
6772 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6773 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00006774 image_matte=MagickTrue;
6775 }
6776 else
6777 {
6778 if (logging != MagickFalse)
6779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6780 "Selecting PNG colortype");
glennrp5af765f2010-03-30 11:12:18 +00006781 ping_color_type=(png_byte) ((matte == MagickTrue)?
cristy3ed852e2009-09-05 21:47:34 +00006782 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6783 if(image_info->type == TrueColorType)
6784 {
glennrp5af765f2010-03-30 11:12:18 +00006785 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00006786 image_matte=MagickFalse;
6787 }
6788 if(image_info->type == TrueColorMatteType)
6789 {
glennrp5af765f2010-03-30 11:12:18 +00006790 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006791 image_matte=MagickTrue;
6792 }
6793 if ((image_info->type == UndefinedType ||
6794 image_info->type == OptimizeType ||
6795 image_info->type == GrayscaleType) &&
6796 image_matte == MagickFalse && ImageIsGray(image))
6797 {
glennrp5af765f2010-03-30 11:12:18 +00006798 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
cristy3ed852e2009-09-05 21:47:34 +00006799 image_matte=MagickFalse;
6800 }
6801 if ((image_info->type == UndefinedType ||
6802 image_info->type == OptimizeType ||
6803 image_info->type == GrayscaleMatteType) &&
6804 image_matte == MagickTrue && ImageIsGray(image))
6805 {
glennrp5af765f2010-03-30 11:12:18 +00006806 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006807 image_matte=MagickTrue;
6808 }
6809 }
6810 if (logging != MagickFalse)
6811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00006812 "Selected PNG colortype=%d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00006813
glennrp5af765f2010-03-30 11:12:18 +00006814 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00006815 {
glennrp5af765f2010-03-30 11:12:18 +00006816 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6817 ping_color_type == PNG_COLOR_TYPE_RGB ||
6818 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6819 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00006820 }
6821
glennrp5af765f2010-03-30 11:12:18 +00006822 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00006823 {
6824 if (image->matte == MagickFalse && image->colors < 256)
6825 {
6826 if (ImageIsMonochrome(image))
6827 {
glennrp5af765f2010-03-30 11:12:18 +00006828 ping_bit_depth=1;
6829 if (ping_bit_depth < (int)mng_info->write_png_depth)
6830 ping_bit_depth = mng_info->write_png_depth;
cristy3ed852e2009-09-05 21:47:34 +00006831 }
6832 }
6833 }
glennrp5af765f2010-03-30 11:12:18 +00006834 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00006835 {
glennrp5af765f2010-03-30 11:12:18 +00006836 ping_bit_depth=1;
cristybb503372010-05-27 20:51:26 +00006837 while ((int) (1 << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00006838 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00006839
6840 if (logging != MagickFalse)
6841 {
6842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00006843 " Number of colors: %lu",(unsigned long) image_colors);
cristy3ed852e2009-09-05 21:47:34 +00006844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00006845 " Tentative PNG bit depth: %d",ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00006846 }
6847 if (mng_info->write_png_depth)
6848 {
glennrp5af765f2010-03-30 11:12:18 +00006849 old_bit_depth=ping_bit_depth;
6850 if (ping_bit_depth < (int)mng_info->write_png_depth)
cristy3ed852e2009-09-05 21:47:34 +00006851 {
glennrp5af765f2010-03-30 11:12:18 +00006852 ping_bit_depth = mng_info->write_png_depth;
6853 if (ping_bit_depth > 8)
6854 ping_bit_depth = 8;
6855 if (ping_bit_depth != (int) old_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00006856 {
6857 if (logging != MagickFalse)
6858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00006859 " Colors increased to %lu",(unsigned long)
6860 image_colors);
cristy3ed852e2009-09-05 21:47:34 +00006861 }
6862 }
6863 }
6864 }
6865 }
glennrp5af765f2010-03-30 11:12:18 +00006866 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006867 if (logging != MagickFalse)
6868 {
6869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00006870 " Tentative PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00006871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6872 " image_info->type: %d",image_info->type);
6873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00006874 " image_depth: %lu",(unsigned long) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00006875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00006876 " ping_bit_depth: %d",ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00006877 }
6878
6879 if (matte && (mng_info->optimize || mng_info->IsPalette))
6880 {
6881 register const PixelPacket
6882 *p;
6883
6884 p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
glennrp5af765f2010-03-30 11:12:18 +00006885 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
cristybb503372010-05-27 20:51:26 +00006886 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006887 {
6888 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6889 if (p == (const PixelPacket *) NULL)
6890 break;
cristybb503372010-05-27 20:51:26 +00006891 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006892 {
6893 if (IsGray(p) == MagickFalse)
6894 {
glennrp5af765f2010-03-30 11:12:18 +00006895 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006896 break;
6897 }
6898 p++;
6899 }
6900 }
6901 /*
6902 Determine if there is any transparent color.
6903 */
cristybb503372010-05-27 20:51:26 +00006904 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006905 {
6906 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6907 if (p == (const PixelPacket *) NULL)
6908 break;
cristybb503372010-05-27 20:51:26 +00006909 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006910 {
6911 if (p->opacity != OpaqueOpacity)
6912 break;
6913 p++;
6914 }
6915 if (x != 0)
6916 break;
6917 }
cristybb503372010-05-27 20:51:26 +00006918 if ((y == (ssize_t) image->rows) && (x == (ssize_t) image->columns))
cristy3ed852e2009-09-05 21:47:34 +00006919 {
6920 /*
glennrp5af765f2010-03-30 11:12:18 +00006921 No transparent pixels are present. Change 4 or 6 to 0 or 2.
cristy3ed852e2009-09-05 21:47:34 +00006922 */
6923 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00006924 ping_color_type&=0x03;
cristy3ed852e2009-09-05 21:47:34 +00006925 }
6926 else
6927 {
6928 unsigned int
6929 mask;
6930
6931 mask=0xffff;
glennrp5af765f2010-03-30 11:12:18 +00006932 if (ping_bit_depth == 8)
cristy3ed852e2009-09-05 21:47:34 +00006933 mask=0x00ff;
glennrp5af765f2010-03-30 11:12:18 +00006934 if (ping_bit_depth == 4)
cristy3ed852e2009-09-05 21:47:34 +00006935 mask=0x000f;
glennrp5af765f2010-03-30 11:12:18 +00006936 if (ping_bit_depth == 2)
cristy3ed852e2009-09-05 21:47:34 +00006937 mask=0x0003;
glennrp5af765f2010-03-30 11:12:18 +00006938 if (ping_bit_depth == 1)
cristy3ed852e2009-09-05 21:47:34 +00006939 mask=0x0001;
glennrp5af765f2010-03-30 11:12:18 +00006940 ping_trans_color.red=(png_uint_16)
cristyce70c172010-01-07 17:15:30 +00006941 (ScaleQuantumToShort(GetRedPixelComponent(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00006942 ping_trans_color.green=(png_uint_16)
cristyce70c172010-01-07 17:15:30 +00006943 (ScaleQuantumToShort(GetGreenPixelComponent(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00006944 ping_trans_color.blue=(png_uint_16)
cristyce70c172010-01-07 17:15:30 +00006945 (ScaleQuantumToShort(GetBluePixelComponent(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00006946 ping_trans_color.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00006947 (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00006948 ping_trans_color.index=(png_byte)
cristy46f08202010-01-10 04:04:21 +00006949 (ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p))));
glennrp5af765f2010-03-30 11:12:18 +00006950 (void) png_set_tRNS(ping, ping_info, NULL, 0,
6951 &ping_trans_color);
cristy3ed852e2009-09-05 21:47:34 +00006952 }
glennrp5af765f2010-03-30 11:12:18 +00006953 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00006954 {
6955 /*
6956 Determine if there is one and only one transparent color
6957 and if so if it is fully transparent.
6958 */
cristybb503372010-05-27 20:51:26 +00006959 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006960 {
6961 p=GetVirtualPixels(image,0,y,image->columns,1,
6962 &image->exception);
6963 x=0;
6964 if (p == (const PixelPacket *) NULL)
6965 break;
cristybb503372010-05-27 20:51:26 +00006966 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006967 {
6968 if (p->opacity != OpaqueOpacity)
6969 {
glennrp5af765f2010-03-30 11:12:18 +00006970 if (IsPNGColorEqual(ping_trans_color,*p) == 0)
cristy3ed852e2009-09-05 21:47:34 +00006971 {
6972 break; /* Can't use RGB + tRNS for multiple
6973 transparent colors. */
6974 }
6975 if (p->opacity != (Quantum) TransparentOpacity)
6976 {
6977 break; /* Can't use RGB + tRNS for
6978 semitransparency. */
6979 }
6980 }
6981 else
6982 {
glennrp5af765f2010-03-30 11:12:18 +00006983 if (IsPNGColorEqual(ping_trans_color,*p))
cristy3ed852e2009-09-05 21:47:34 +00006984 break; /* Can't use RGB + tRNS when another pixel
6985 having the same RGB samples is
6986 transparent. */
6987 }
6988 p++;
6989 }
6990 if (x != 0)
6991 break;
6992 }
6993 if (x != 0)
glennrp5af765f2010-03-30 11:12:18 +00006994 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00006995 }
glennrp5af765f2010-03-30 11:12:18 +00006996 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00006997 {
glennrp5af765f2010-03-30 11:12:18 +00006998 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
cristy3ed852e2009-09-05 21:47:34 +00006999 if (image_depth == 8)
7000 {
glennrp5af765f2010-03-30 11:12:18 +00007001 ping_trans_color.red&=0xff;
7002 ping_trans_color.green&=0xff;
7003 ping_trans_color.blue&=0xff;
7004 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00007005 }
7006 }
7007 }
7008 matte=image_matte;
glennrp5af765f2010-03-30 11:12:18 +00007009 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00007010 image_matte=MagickFalse;
7011 if ((mng_info->optimize || mng_info->IsPalette) &&
7012 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
7013 ImageIsGray(image) && (!image_matte || image_depth >= 8))
7014 {
7015 if (image_matte != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00007016 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00007017 else
7018 {
glennrp5af765f2010-03-30 11:12:18 +00007019 ping_color_type=PNG_COLOR_TYPE_GRAY;
cristy3ed852e2009-09-05 21:47:34 +00007020 if (save_image_depth == 16 && image_depth == 8)
glennrp5af765f2010-03-30 11:12:18 +00007021 ping_trans_color.gray*=0x0101;
cristy3ed852e2009-09-05 21:47:34 +00007022 }
7023 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
7024 image_depth=MAGICKCORE_QUANTUM_DEPTH;
7025 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
7026 image_colors=1 << image_depth;
7027 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00007028 ping_bit_depth=16;
cristy3ed852e2009-09-05 21:47:34 +00007029 else
7030 {
glennrp5af765f2010-03-30 11:12:18 +00007031 ping_bit_depth=8;
7032 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00007033 {
7034 if(!mng_info->write_png_depth)
7035 {
glennrp5af765f2010-03-30 11:12:18 +00007036 ping_bit_depth=1;
7037 while ((int) (1 << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00007038 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00007039 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00007040 }
7041 }
glennrp5af765f2010-03-30 11:12:18 +00007042 else if (mng_info->optimize && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00007043 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
7044 mng_info->IsPalette)
7045 {
7046
7047 /* Check if grayscale is reducible */
7048 int
7049 depth_4_ok=MagickTrue,
7050 depth_2_ok=MagickTrue,
7051 depth_1_ok=MagickTrue;
7052
cristybb503372010-05-27 20:51:26 +00007053 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007054 {
7055 unsigned char
7056 intensity;
7057
7058 intensity=ScaleQuantumToChar(image->colormap[i].red);
7059
7060 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
7061 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
7062 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
7063 depth_2_ok=depth_1_ok=MagickFalse;
7064 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
7065 depth_1_ok=MagickFalse;
7066 }
7067 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp5af765f2010-03-30 11:12:18 +00007068 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00007069 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp5af765f2010-03-30 11:12:18 +00007070 ping_bit_depth=2;
cristy3ed852e2009-09-05 21:47:34 +00007071 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp5af765f2010-03-30 11:12:18 +00007072 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00007073 }
7074 }
glennrp5af765f2010-03-30 11:12:18 +00007075 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007076 }
7077 else
7078 if (mng_info->IsPalette)
7079 {
cristybb503372010-05-27 20:51:26 +00007080 size_t
glennrp17a14852010-05-10 03:01:59 +00007081 number_colors;
7082
7083 number_colors=image_colors;
7084
cristy3ed852e2009-09-05 21:47:34 +00007085 if (image_depth <= 8)
7086 {
cristy3ed852e2009-09-05 21:47:34 +00007087 /*
7088 Set image palette.
7089 */
glennrp5af765f2010-03-30 11:12:18 +00007090 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
cristy3ed852e2009-09-05 21:47:34 +00007091 if (mng_info->have_write_global_plte && !matte)
7092 {
7093 png_set_PLTE(ping,ping_info,NULL,0);
7094 if (logging)
7095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7096 " Setting up empty PLTE chunk");
7097 }
7098 else
7099 {
7100#if defined(PNG_SORT_PALETTE)
cristybb503372010-05-27 20:51:26 +00007101 size_t
cristy3ed852e2009-09-05 21:47:34 +00007102 save_number_colors;
7103
7104 if (mng_info->optimize)
7105 {
7106 save_number_colors=image_colors;
7107 if (CompressColormapTransFirst(image) == MagickFalse)
7108 ThrowWriterException(ResourceLimitError,
7109 "MemoryAllocationFailed");
glennrp98156a32009-12-09 15:32:44 +00007110 number_colors=image->colors;
cristy3ed852e2009-09-05 21:47:34 +00007111 image_colors=save_number_colors;
7112 }
7113#endif
7114 palette=(png_color *) AcquireQuantumMemory(257,
7115 sizeof(*palette));
7116 if (palette == (png_color *) NULL)
7117 ThrowWriterException(ResourceLimitError,
7118 "MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00007119 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007120 {
7121 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7122 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7123 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7124 }
7125 if (logging)
7126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00007127 " Setting up PLTE chunk with %d colors",
7128 (int) number_colors);
cristy3ed852e2009-09-05 21:47:34 +00007129 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
cristy3ed852e2009-09-05 21:47:34 +00007130 palette=(png_colorp) RelinquishMagickMemory(palette);
cristy3ed852e2009-09-05 21:47:34 +00007131 }
7132 /* color_type is PNG_COLOR_TYPE_PALETTE */
7133 if (!mng_info->write_png_depth)
7134 {
glennrp5af765f2010-03-30 11:12:18 +00007135 ping_bit_depth=1;
7136 while ((1UL << ping_bit_depth) < number_colors)
7137 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00007138 }
glennrp5af765f2010-03-30 11:12:18 +00007139 ping_num_trans=0;
cristy3ed852e2009-09-05 21:47:34 +00007140 if (matte)
7141 {
7142 ExceptionInfo
7143 *exception;
7144
7145 register const PixelPacket
7146 *p;
7147
7148 int
7149 trans[256];
7150
7151 register const IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00007152 *packet_indexes;
cristy3ed852e2009-09-05 21:47:34 +00007153
7154 /*
7155 Identify which colormap entry is transparent.
7156 */
7157 assert(number_colors <= 256);
cristybb503372010-05-27 20:51:26 +00007158 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007159 trans[i]=256;
7160 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +00007161 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007162 {
7163 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7164 if (p == (const PixelPacket *) NULL)
7165 break;
cristy5c6f7892010-05-05 22:53:29 +00007166 packet_indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00007167 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00007168 {
7169 if (p->opacity != OpaqueOpacity)
7170 {
7171 IndexPacket
7172 packet_index;
7173
cristy5c6f7892010-05-05 22:53:29 +00007174 packet_index=packet_indexes[x];
cristybb503372010-05-27 20:51:26 +00007175 assert((size_t) packet_index < number_colors);
7176 if (trans[(ssize_t) packet_index] != 256)
cristy3ed852e2009-09-05 21:47:34 +00007177 {
cristybb503372010-05-27 20:51:26 +00007178 if (trans[(ssize_t) packet_index] != (png_byte) (255-
cristyce70c172010-01-07 17:15:30 +00007179 ScaleQuantumToChar(GetOpacityPixelComponent(p))))
cristy3ed852e2009-09-05 21:47:34 +00007180 {
glennrp5af765f2010-03-30 11:12:18 +00007181 ping_color_type=(png_byte)
cristy3ed852e2009-09-05 21:47:34 +00007182 PNG_COLOR_TYPE_RGB_ALPHA;
7183 break;
7184 }
7185 }
cristybb503372010-05-27 20:51:26 +00007186 trans[(ssize_t) packet_index]=(png_byte) (255-
cristyce70c172010-01-07 17:15:30 +00007187 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00007188 }
7189 p++;
7190 }
glennrp5af765f2010-03-30 11:12:18 +00007191 if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00007192 {
glennrp5af765f2010-03-30 11:12:18 +00007193 ping_num_trans=0;
7194 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7195 png_set_invalid(ping,ping_info,PNG_INFO_PLTE);
cristy3ed852e2009-09-05 21:47:34 +00007196 mng_info->IsPalette=MagickFalse;
7197 (void) SyncImage(image);
7198 if (logging)
7199 (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7200 " Cannot write image as indexed PNG, writing RGBA.");
7201 break;
7202 }
7203 }
glennrp5af765f2010-03-30 11:12:18 +00007204 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00007205 {
cristybb503372010-05-27 20:51:26 +00007206 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007207 {
7208 if (trans[i] == 256)
7209 trans[i]=255;
7210 if (trans[i] != 255)
glennrp5af765f2010-03-30 11:12:18 +00007211 ping_num_trans=(unsigned short) (i+1);
cristy3ed852e2009-09-05 21:47:34 +00007212 }
7213 }
glennrp5af765f2010-03-30 11:12:18 +00007214 if (ping_num_trans == 0)
7215 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7216 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7217 ping_num_trans=0;
7218 if (ping_num_trans != 0)
cristy3ed852e2009-09-05 21:47:34 +00007219 {
glennrp5af765f2010-03-30 11:12:18 +00007220 ping_trans_alpha=(unsigned char *) AcquireQuantumMemory(
7221 number_colors,sizeof(*ping_trans_alpha));
7222 if (ping_trans_alpha == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00007223 ThrowWriterException(ResourceLimitError,
7224 "MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00007225 for (i=0; i < (ssize_t) number_colors; i++)
glennrp5af765f2010-03-30 11:12:18 +00007226 ping_trans_alpha[i]=(png_byte) trans[i];
cristy3ed852e2009-09-05 21:47:34 +00007227 }
7228 }
7229
cristy3ed852e2009-09-05 21:47:34 +00007230 }
7231 }
7232 else
7233 {
7234 if (image_depth < 8)
7235 image_depth=8;
7236 if ((save_image_depth == 16) && (image_depth == 8))
7237 {
glennrp5af765f2010-03-30 11:12:18 +00007238 ping_trans_color.red*=0x0101;
7239 ping_trans_color.green*=0x0101;
7240 ping_trans_color.blue*=0x0101;
7241 ping_trans_color.gray*=0x0101;
cristy3ed852e2009-09-05 21:47:34 +00007242 }
7243 }
7244
7245 /*
7246 Adjust background and transparency samples in sub-8-bit grayscale files.
7247 */
glennrp5af765f2010-03-30 11:12:18 +00007248 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00007249 PNG_COLOR_TYPE_GRAY)
7250 {
7251 png_uint_16
7252 maxval;
7253
7254 png_color_16
7255 background;
7256
glennrp5af765f2010-03-30 11:12:18 +00007257 maxval=(png_uint_16) ((1 << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00007258
7259
7260 background.gray=(png_uint_16)
7261 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7262
7263 if (logging != MagickFalse)
7264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7265 " Setting up bKGD chunk");
7266 png_set_bKGD(ping,ping_info,&background);
7267
glennrp5af765f2010-03-30 11:12:18 +00007268 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7269 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00007270 }
glennrp17a14852010-05-10 03:01:59 +00007271
7272 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7273 {
7274 /*
7275 Identify which colormap entry is the background color.
7276 */
7277
cristybb503372010-05-27 20:51:26 +00007278 size_t
glennrp17a14852010-05-10 03:01:59 +00007279 number_colors;
7280
7281 number_colors=image_colors;
7282
cristybb503372010-05-27 20:51:26 +00007283 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
glennrp17a14852010-05-10 03:01:59 +00007284 if (IsPNGColorEqual(ping_background,image->colormap[i]))
7285 break;
7286
7287 ping_background.index=(png_byte) i;
7288
7289 if (logging)
7290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7291 " Setting up bKGD chunk with index=%d",(int) i);
7292
7293 png_set_bKGD(ping,ping_info,&ping_background);
7294 }
7295
cristy3ed852e2009-09-05 21:47:34 +00007296 if (logging != MagickFalse)
7297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007298 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00007299 /*
7300 Initialize compression level and filtering.
7301 */
7302 if (logging != MagickFalse)
7303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7304 " Setting up deflate compression");
cristy3ed852e2009-09-05 21:47:34 +00007305 if (logging != MagickFalse)
7306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7307 " Compression buffer size: 32768");
7308 png_set_compression_buffer_size(ping,32768L);
cristy3ed852e2009-09-05 21:47:34 +00007309 if (logging != MagickFalse)
7310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7311 " Compression mem level: 9");
7312 png_set_compression_mem_level(ping, 9);
7313 quality=image->quality == UndefinedCompressionQuality ? 75UL :
7314 image->quality;
7315 if (quality > 9)
7316 {
7317 int
7318 level;
7319
cristybb503372010-05-27 20:51:26 +00007320 level=(int) MagickMin((ssize_t) quality/10,9);
cristy3ed852e2009-09-05 21:47:34 +00007321 if (logging != MagickFalse)
7322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7323 " Compression level: %d",level);
7324 png_set_compression_level(ping,level);
7325 }
7326 else
7327 {
7328 if (logging != MagickFalse)
7329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7330 " Compression strategy: Z_HUFFMAN_ONLY");
7331 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7332 }
7333 if (logging != MagickFalse)
7334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7335 " Setting up filtering");
7336#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7337
7338 /* This became available in libpng-1.0.9. Output must be a MNG. */
7339 if (mng_info->write_mng && ((quality % 10) == 7))
7340 {
7341 if (logging != MagickFalse)
7342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7343 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp5af765f2010-03-30 11:12:18 +00007344 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00007345 }
7346 else
7347 if (logging != MagickFalse)
7348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7349 " Filter_type: 0");
7350#endif
7351 {
7352 int
7353 base_filter;
7354
7355 if ((quality % 10) > 5)
7356 base_filter=PNG_ALL_FILTERS;
7357 else
7358 if ((quality % 10) != 5)
7359 base_filter=(int) quality % 10;
7360 else
glennrp5af765f2010-03-30 11:12:18 +00007361 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
7362 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
cristy3ed852e2009-09-05 21:47:34 +00007363 (quality < 50))
7364 base_filter=PNG_NO_FILTERS;
7365 else
7366 base_filter=PNG_ALL_FILTERS;
7367 if (logging != MagickFalse)
7368 {
7369 if (base_filter == PNG_ALL_FILTERS)
7370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7371 " Base filter method: ADAPTIVE");
7372 else
7373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7374 " Base filter method: NONE");
7375 }
7376 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7377 }
7378
7379 ResetImageProfileIterator(image);
7380 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7381 {
7382 profile=GetImageProfile(image,name);
7383 if (profile != (StringInfo *) NULL)
7384 {
glennrp5af765f2010-03-30 11:12:18 +00007385#ifdef PNG_WRITE_iCCP_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00007386 if ((LocaleCompare(name,"ICC") == 0) ||
7387 (LocaleCompare(name,"ICM") == 0))
7388 png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7389 GetStringInfoDatum(profile),
7390 (png_uint_32) GetStringInfoLength(profile));
7391 else
7392#endif
7393 png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7394 name,(unsigned char *) name,GetStringInfoDatum(profile),
7395 (png_uint_32) GetStringInfoLength(profile));
7396 }
7397 if (logging != MagickFalse)
7398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7399 " Setting up text chunk with %s profile",name);
7400 name=GetNextImageProfile(image);
7401 }
7402
7403#if defined(PNG_WRITE_sRGB_SUPPORTED)
7404 if ((mng_info->have_write_global_srgb == 0) &&
7405 ((image->rendering_intent != UndefinedIntent) ||
7406 (image->colorspace == sRGBColorspace)))
7407 {
7408 /*
7409 Note image rendering intent.
7410 */
7411 if (logging != MagickFalse)
7412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7413 " Setting up sRGB chunk");
7414 (void) png_set_sRGB(ping,ping_info,(int) (image->rendering_intent-1));
7415 png_set_gAMA(ping,ping_info,0.45455);
7416 }
glennrp5af765f2010-03-30 11:12:18 +00007417 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00007418#endif
7419 {
7420 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7421 {
7422 /*
7423 Note image gamma.
7424 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7425 */
7426 if (logging != MagickFalse)
7427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7428 " Setting up gAMA chunk");
7429 png_set_gAMA(ping,ping_info,image->gamma);
7430 }
7431 if ((mng_info->have_write_global_chrm == 0) &&
7432 (image->chromaticity.red_primary.x != 0.0))
7433 {
7434 /*
7435 Note image chromaticity.
7436 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7437 */
7438 PrimaryInfo
7439 bp,
7440 gp,
7441 rp,
7442 wp;
7443
7444 wp=image->chromaticity.white_point;
7445 rp=image->chromaticity.red_primary;
7446 gp=image->chromaticity.green_primary;
7447 bp=image->chromaticity.blue_primary;
7448
7449 if (logging != MagickFalse)
7450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7451 " Setting up cHRM chunk");
7452 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7453 bp.x,bp.y);
7454 }
7455 }
glennrp5af765f2010-03-30 11:12:18 +00007456 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00007457
7458 if (mng_info->write_mng)
7459 png_set_sig_bytes(ping,8);
7460
7461 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7462
7463 if (mng_info->write_png_colortype)
7464 {
7465 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7466 if (ImageIsGray(image) == MagickFalse)
7467 {
glennrp5af765f2010-03-30 11:12:18 +00007468 ping_color_type = PNG_COLOR_TYPE_RGB;
7469 if (ping_bit_depth < 8)
7470 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00007471 }
7472
7473 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7474 if (ImageIsGray(image) == MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00007475 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00007476 }
7477
7478 if ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00007479 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00007480 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00007481 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00007482 mng_info->write_png_colortype != 7 &&
glennrp5af765f2010-03-30 11:12:18 +00007483 !(mng_info->write_png_colortype == 5 && ping_color_type == 0))))
cristy3ed852e2009-09-05 21:47:34 +00007484 {
7485 if (logging != MagickFalse)
7486 {
7487 if (mng_info->write_png_depth)
7488 {
7489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7490 " Defined PNG:bit-depth=%u, Computed depth=%u",
7491 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00007492 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00007493 }
7494 if (mng_info->write_png_colortype)
7495 {
7496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7497 " Defined PNG:color-type=%u, Computed color type=%u",
7498 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00007499 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00007500 }
7501 }
7502 png_error(ping,
7503 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7504 }
7505
7506 if (image_matte && !image->matte)
7507 {
7508 /* Add an opaque matte channel */
7509 image->matte = MagickTrue;
7510 (void) SetImageOpacity(image,0);
glennrpb4a13412010-05-05 12:47:19 +00007511 if (logging != MagickFalse)
7512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7513 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00007514 }
7515
7516 if (logging != MagickFalse)
7517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7518 " Writing PNG header chunks");
7519
glennrp5af765f2010-03-30 11:12:18 +00007520 png_set_IHDR(ping,ping_info,ping_width,ping_height,
7521 ping_bit_depth,ping_color_type,
7522 ping_interlace_method,ping_compression_method,
7523 ping_filter_method);
7524
cristy3ed852e2009-09-05 21:47:34 +00007525 png_write_info_before_PLTE(ping, ping_info);
7526 /* write any png-chunk-b profiles */
7527 (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7528 png_write_info(ping,ping_info);
7529 /* write any PNG-chunk-m profiles */
7530 (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7531
7532 if (image->page.width || image->page.height)
7533 {
7534 unsigned char
7535 chunk[14];
7536
7537 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
7538 PNGType(chunk,mng_vpAg);
7539 LogPNGChunk((int) logging,mng_vpAg,9L);
7540 PNGLong(chunk+4,(png_uint_32) image->page.width);
7541 PNGLong(chunk+8,(png_uint_32) image->page.height);
7542 chunk[12]=0; /* unit = pixels */
7543 (void) WriteBlob(image,13,chunk);
7544 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
7545 }
7546
7547#if (PNG_LIBPNG_VER == 10206)
7548 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
7549#define PNG_HAVE_IDAT 0x04
7550 ping->mode |= PNG_HAVE_IDAT;
7551#undef PNG_HAVE_IDAT
7552#endif
7553
7554 png_set_packing(ping);
7555 /*
7556 Allocate memory.
7557 */
7558 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00007559 if (image_depth > 8)
7560 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00007561 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00007562 {
glennrpb4a13412010-05-05 12:47:19 +00007563 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00007564 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00007565 break;
7566 case PNG_COLOR_TYPE_GRAY_ALPHA:
7567 rowbytes*=2;
7568 break;
7569 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00007570 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00007571 break;
7572 default:
7573 break;
cristy3ed852e2009-09-05 21:47:34 +00007574 }
7575 if (logging)
glennrpb4a13412010-05-05 12:47:19 +00007576 {
7577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7578 " Writing PNG image data");
7579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00007580 " Allocating %lu bytes of memory for pixels",(unsigned long)
7581 rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00007582 }
cristy3ed852e2009-09-05 21:47:34 +00007583 png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7584 sizeof(*png_pixels));
7585 if (png_pixels == (unsigned char *) NULL)
7586 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7587 /*
7588 Initialize image scanlines.
7589 */
glennrp5af765f2010-03-30 11:12:18 +00007590 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00007591 {
7592 /*
7593 PNG write failed.
7594 */
7595#ifdef PNG_DEBUG
7596 if (image_info->verbose)
7597 (void) printf("PNG write has failed.\n");
7598#endif
7599 png_destroy_write_struct(&ping,&ping_info);
7600 if (quantum_info != (QuantumInfo *) NULL)
7601 quantum_info=DestroyQuantumInfo(quantum_info);
7602 if (png_pixels != (unsigned char *) NULL)
7603 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7604#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00007605 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007606#endif
7607 return(MagickFalse);
7608 }
cristyed552522009-10-16 14:04:35 +00007609 quantum_info=AcquireQuantumInfo(image_info,image);
7610 if (quantum_info == (QuantumInfo *) NULL)
7611 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00007612 quantum_info->format=UndefinedQuantumFormat;
7613 quantum_info->depth=image_depth;
7614 num_passes=png_set_interlace_handling(ping);
7615 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7616 !mng_info->write_png32) &&
7617 (mng_info->optimize || mng_info->IsPalette ||
7618 (image_info->type == BilevelType)) &&
7619 !image_matte && ImageIsMonochrome(image))
7620 {
7621 register const PixelPacket
7622 *p;
7623
7624 quantum_info->depth=8;
7625 for (pass=0; pass < num_passes; pass++)
7626 {
7627 /*
7628 Convert PseudoClass image to a PNG monochrome image.
7629 */
cristybb503372010-05-27 20:51:26 +00007630 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007631 {
7632 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7633 if (p == (const PixelPacket *) NULL)
7634 break;
7635 if (mng_info->IsPalette)
7636 {
7637 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7638 quantum_info,GrayQuantum,png_pixels,&image->exception);
7639 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7640 mng_info->write_png_depth &&
7641 mng_info->write_png_depth != old_bit_depth)
7642 {
7643 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00007644 for (i=0; i < (ssize_t) image->columns; i++)
cristy3ed852e2009-09-05 21:47:34 +00007645 *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7646 >> (8-old_bit_depth));
7647 }
7648 }
7649 else
7650 {
7651 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7652 quantum_info,RedQuantum,png_pixels,&image->exception);
7653 }
7654 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00007655 for (i=0; i < (ssize_t) image->columns; i++)
cristy3ed852e2009-09-05 21:47:34 +00007656 *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7657 255 : 0);
glennrpb4a13412010-05-05 12:47:19 +00007658 if (logging && y == 0)
7659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7660 " Writing row of pixels (1)");
cristy3ed852e2009-09-05 21:47:34 +00007661 png_write_row(ping,png_pixels);
7662 }
7663 if (image->previous == (Image *) NULL)
7664 {
7665 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7666 if (status == MagickFalse)
7667 break;
7668 }
7669 }
7670 }
7671 else
7672 for (pass=0; pass < num_passes; pass++)
7673 {
7674 register const PixelPacket
7675 *p;
7676
7677 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7678 !mng_info->write_png32) &&
7679 (image_matte ||
glennrp5af765f2010-03-30 11:12:18 +00007680 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
cristy3ed852e2009-09-05 21:47:34 +00007681 (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7682 {
cristybb503372010-05-27 20:51:26 +00007683 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007684 {
7685 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7686 if (p == (const PixelPacket *) NULL)
7687 break;
glennrp5af765f2010-03-30 11:12:18 +00007688 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00007689 {
7690 if (mng_info->IsPalette)
7691 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7692 quantum_info,GrayQuantum,png_pixels,&image->exception);
7693 else
7694 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7695 quantum_info,RedQuantum,png_pixels,&image->exception);
glennrpb4a13412010-05-05 12:47:19 +00007696 if (logging && y == 0)
7697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7698 " Writing GRAY PNG pixels (2)");
cristy3ed852e2009-09-05 21:47:34 +00007699 }
7700 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7701 {
glennrpb4a13412010-05-05 12:47:19 +00007702 if (logging && y == 0)
7703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7704 " Writing GRAY_ALPHA PNG pixels (2)");
cristy3ed852e2009-09-05 21:47:34 +00007705 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7706 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7707 }
glennrpb4a13412010-05-05 12:47:19 +00007708 if (logging && y == 0)
7709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7710 " Writing row of pixels (2)");
cristy3ed852e2009-09-05 21:47:34 +00007711 png_write_row(ping,png_pixels);
7712 }
7713 if (image->previous == (Image *) NULL)
7714 {
7715 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7716 if (status == MagickFalse)
7717 break;
7718 }
7719 }
7720 else
7721 for (pass=0; pass < num_passes; pass++)
7722 {
7723 if ((image_depth > 8) || (mng_info->write_png24 ||
7724 mng_info->write_png32 ||
7725 (!mng_info->write_png8 && !mng_info->IsPalette)))
cristybb503372010-05-27 20:51:26 +00007726 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007727 {
7728 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7729 if (p == (const PixelPacket *) NULL)
7730 break;
glennrp5af765f2010-03-30 11:12:18 +00007731 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00007732 {
7733 if (image->storage_class == DirectClass)
7734 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7735 quantum_info,RedQuantum,png_pixels,&image->exception);
7736 else
7737 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7738 quantum_info,GrayQuantum,png_pixels,&image->exception);
7739 }
glennrp5af765f2010-03-30 11:12:18 +00007740 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrpb4a13412010-05-05 12:47:19 +00007741 {
7742 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7743 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7744 if (logging && y == 0)
7745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7746 " Writing GRAY_ALPHA PNG pixels (3)");
7747 }
cristy3ed852e2009-09-05 21:47:34 +00007748 else if (image_matte != MagickFalse)
7749 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7750 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7751 else
7752 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7753 quantum_info,RGBQuantum,png_pixels,&image->exception);
glennrpb4a13412010-05-05 12:47:19 +00007754 if (logging && y == 0)
7755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7756 " Writing row of pixels (3)");
cristy3ed852e2009-09-05 21:47:34 +00007757 png_write_row(ping,png_pixels);
7758 }
7759 else
7760 /* not ((image_depth > 8) || (mng_info->write_png24 ||
7761 mng_info->write_png32 ||
7762 (!mng_info->write_png8 && !mng_info->IsPalette))) */
7763 {
glennrp5af765f2010-03-30 11:12:18 +00007764 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
7765 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
cristy3ed852e2009-09-05 21:47:34 +00007766 {
7767 if (logging)
7768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7769 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7770 quantum_info->depth=8;
7771 image_depth=8;
7772 }
cristybb503372010-05-27 20:51:26 +00007773 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007774 {
7775 if (logging)
7776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7777 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7778 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7779 if (p == (const PixelPacket *) NULL)
7780 break;
glennrp5af765f2010-03-30 11:12:18 +00007781 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00007782 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7783 quantum_info,GrayQuantum,png_pixels,&image->exception);
glennrp5af765f2010-03-30 11:12:18 +00007784 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrpb4a13412010-05-05 12:47:19 +00007785 {
glennrp07cd77a2010-05-05 12:49:15 +00007786 if (logging && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00007787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7788 " Writing GRAY_ALPHA PNG pixels (4)");
7789 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7790 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7791 }
cristy3ed852e2009-09-05 21:47:34 +00007792 else
7793 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7794 quantum_info,IndexQuantum,png_pixels,&image->exception);
glennrp07cd77a2010-05-05 12:49:15 +00007795 if (logging && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00007796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7797 " Writing row of pixels (4)");
cristy3ed852e2009-09-05 21:47:34 +00007798 png_write_row(ping,png_pixels);
7799 }
7800 }
7801 if (image->previous == (Image *) NULL)
7802 {
7803 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7804 if (status == MagickFalse)
7805 break;
7806 }
7807 }
7808 }
cristyb32b90a2009-09-07 21:45:48 +00007809 if (quantum_info != (QuantumInfo *) NULL)
7810 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00007811
7812 if (logging != MagickFalse)
7813 {
7814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00007815 " Wrote PNG image data");
cristy3ed852e2009-09-05 21:47:34 +00007816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00007817 " Width: %lu",(unsigned long) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00007818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00007819 " Height: %lu",(unsigned long) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00007820 if (mng_info->write_png_depth)
7821 {
7822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7823 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7824 }
7825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007826 " PNG bit-depth written: %d",ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00007827 if (mng_info->write_png_colortype)
7828 {
7829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7830 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7831 }
7832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007833 " PNG color-type written: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00007834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007835 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00007836 }
7837 /*
7838 Generate text chunks.
7839 */
cristy3ed852e2009-09-05 21:47:34 +00007840 ResetImagePropertyIterator(image);
7841 property=GetNextImageProperty(image);
7842 while (property != (const char *) NULL)
7843 {
cristy3ed852e2009-09-05 21:47:34 +00007844 png_textp
7845 text;
cristy3ed852e2009-09-05 21:47:34 +00007846
7847 value=GetImageProperty(image,property);
7848 if (value != (const char *) NULL)
7849 {
cristy3ed852e2009-09-05 21:47:34 +00007850 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7851 text[0].key=(char *) property;
7852 text[0].text=(char *) value;
7853 text[0].text_length=strlen(value);
7854 text[0].compression=image_info->compression == NoCompression ||
7855 (image_info->compression == UndefinedCompression &&
7856 text[0].text_length < 128) ? -1 : 0;
7857 if (logging != MagickFalse)
7858 {
7859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7860 " Setting up text chunk");
7861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7862 " keyword: %s",text[0].key);
7863 }
7864 png_set_text(ping,ping_info,text,1);
7865 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007866 }
7867 property=GetNextImageProperty(image);
7868 }
7869
7870 /* write any PNG-chunk-e profiles */
7871 (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
7872
7873 if (logging != MagickFalse)
7874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7875 " Writing PNG end info");
7876 png_write_end(ping,ping_info);
7877 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
7878 {
7879 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00007880 (ping_width != mng_info->page.width) ||
7881 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00007882 {
7883 unsigned char
7884 chunk[32];
7885
7886 /*
7887 Write FRAM 4 with clipping boundaries followed by FRAM 1.
7888 */
7889 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
7890 PNGType(chunk,mng_FRAM);
7891 LogPNGChunk((int) logging,mng_FRAM,27L);
7892 chunk[4]=4;
7893 chunk[5]=0; /* frame name separator (no name) */
7894 chunk[6]=1; /* flag for changing delay, for next frame only */
7895 chunk[7]=0; /* flag for changing frame timeout */
7896 chunk[8]=1; /* flag for changing frame clipping for next frame */
7897 chunk[9]=0; /* flag for changing frame sync_id */
7898 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
7899 chunk[14]=0; /* clipping boundaries delta type */
7900 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
7901 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00007902 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00007903 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
7904 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00007905 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00007906 (void) WriteBlob(image,31,chunk);
7907 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
7908 mng_info->old_framing_mode=4;
7909 mng_info->framing_mode=1;
7910 }
7911 else
7912 mng_info->framing_mode=3;
7913 }
7914 if (mng_info->write_mng && !mng_info->need_fram &&
7915 ((int) image->dispose == 3))
7916 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7917 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
7918 "`%s'",image->filename);
7919 image_depth=save_image_depth;
7920
7921 /* Save depth actually written */
7922
glennrp5af765f2010-03-30 11:12:18 +00007923 s[0]=(char) ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007924 s[1]='\0';
7925
7926 (void) SetImageProperty(image,"png:bit-depth-written",s);
7927
7928 /*
7929 Free PNG resources.
7930 */
glennrp5af765f2010-03-30 11:12:18 +00007931
cristy3ed852e2009-09-05 21:47:34 +00007932 png_destroy_write_struct(&ping,&ping_info);
7933
7934 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7935
7936#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00007937 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007938#endif
7939
7940 if (logging != MagickFalse)
7941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7942 " exit WriteOnePNGImage()");
7943 return(MagickTrue);
7944/* End write one PNG image */
7945}
7946
7947/*
7948%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7949% %
7950% %
7951% %
7952% W r i t e P N G I m a g e %
7953% %
7954% %
7955% %
7956%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7957%
7958% WritePNGImage() writes a Portable Network Graphics (PNG) or
7959% Multiple-image Network Graphics (MNG) image file.
7960%
7961% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7962%
7963% The format of the WritePNGImage method is:
7964%
7965% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
7966%
7967% A description of each parameter follows:
7968%
7969% o image_info: the image info.
7970%
7971% o image: The image.
7972%
7973% Returns MagickTrue on success, MagickFalse on failure.
7974%
7975% Communicating with the PNG encoder:
7976%
7977% While the datastream written is always in PNG format and normally would
7978% be given the "png" file extension, this method also writes the following
7979% pseudo-formats which are subsets of PNG:
7980%
7981% o PNG8: An 8-bit indexed PNG datastream is written. If transparency
7982% is present, the tRNS chunk must only have values 0 and 255
7983% (i.e., transparency is binary: fully opaque or fully
7984% transparent). The pixels contain 8-bit indices even if
7985% they could be represented with 1, 2, or 4 bits. Note: grayscale
7986% images will be written as indexed PNG files even though the
7987% PNG grayscale type might be slightly more efficient.
7988%
7989% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
7990% chunk can be present to convey binary transparency by naming
7991% one of the colors as transparent.
7992%
7993% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
7994% transparency is permitted, i.e., the alpha sample for
7995% each pixel can have any value from 0 to 255. The alpha
7996% channel is present even if the image is fully opaque.
7997%
7998% o -define: For more precise control of the PNG output, you can use the
7999% Image options "png:bit-depth" and "png:color-type". These
8000% can be set from the commandline with "-define" and also
8001% from the application programming interfaces.
8002%
8003% png:color-type can be 0, 2, 3, 4, or 6.
8004%
8005% When png:color-type is 0 (Grayscale), png:bit-depth can
8006% be 1, 2, 4, 8, or 16.
8007%
8008% When png:color-type is 2 (RGB), png:bit-depth can
8009% be 8 or 16.
8010%
8011% When png:color-type is 3 (Indexed), png:bit-depth can
8012% be 1, 2, 4, or 8. This refers to the number of bits
8013% used to store the index. The color samples always have
8014% bit-depth 8 in indexed PNG files.
8015%
8016% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
8017% png:bit-depth can be 8 or 16.
8018%
8019% If the image cannot be written without loss in the requested PNG8, PNG24,
8020% or PNG32 format or with the requested bit-depth and color-type without loss,
8021% a PNG file will not be written, and the encoder will return MagickFalse.
8022% Since image encoders should not be responsible for the "heavy lifting",
8023% the user should make sure that ImageMagick has already reduced the
8024% image depth and number of colors and limit transparency to binary
8025% transparency prior to attempting to write the image in a format that
8026% is subject to depth, color, or transparency limitations.
8027%
8028% TODO: Enforce the previous paragraph.
8029%
8030% TODO: Allow all other PNG subformats to be requested via new
8031% "-define png:bit-depth -define png:color-type" options.
8032%
8033% Note that another definition, "png:bit-depth-written" exists, but it
8034% is not intended for external use. It is only used internally by the
8035% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
8036%
8037% It is possible to request that the PNG encoder write previously-formatted
8038% ancillary chunks in the output PNG file, using the "-profile" commandline
8039% option as shown below or by setting the profile via a programming
8040% interface:
8041%
8042% -profile PNG-chunk-x:<file>
8043%
8044% where x is a location flag and <file> is a file containing the chunk
8045% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
8046%
8047% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
8048% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
8049% of the same type, then add a short unique string after the "x" to prevent
8050% subsequent profiles from overwriting the preceding ones:
8051%
8052% -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
8053%
8054%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8055*/
8056static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
8057 Image *image)
8058{
8059 MagickBooleanType
8060 status;
8061
8062 MngInfo
8063 *mng_info;
8064
8065 const char
8066 *value;
8067
8068 int
8069 have_mng_structure;
8070
8071 unsigned int
8072 logging;
8073
8074 /*
8075 Open image file.
8076 */
8077 assert(image_info != (const ImageInfo *) NULL);
8078 assert(image_info->signature == MagickSignature);
8079 assert(image != (Image *) NULL);
8080 assert(image->signature == MagickSignature);
8081 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8082 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
8083 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8084 if (status == MagickFalse)
8085 return(MagickFalse);
8086 /*
8087 Allocate a MngInfo structure.
8088 */
8089 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00008090 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00008091 if (mng_info == (MngInfo *) NULL)
8092 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8093 /*
8094 Initialize members of the MngInfo structure.
8095 */
8096 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8097 mng_info->image=image;
8098 have_mng_structure=MagickTrue;
8099
8100 /* See if user has requested a specific PNG subformat */
8101
8102 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8103 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8104 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8105
8106 if (mng_info->write_png8)
8107 {
8108 mng_info->write_png_colortype = /* 3 */ 4;
8109 mng_info->write_png_depth = 8;
8110 image->depth = 8;
8111#if 0 /* this does not work */
8112 if (image->matte == MagickTrue)
8113 (void) SetImageType(image,PaletteMatteType);
8114 else
8115 (void) SetImageType(image,PaletteType);
8116 (void) SyncImage(image);
8117#endif
8118 }
8119
8120 if (mng_info->write_png24)
8121 {
8122 mng_info->write_png_colortype = /* 2 */ 3;
8123 mng_info->write_png_depth = 8;
8124 image->depth = 8;
8125 if (image->matte == MagickTrue)
8126 (void) SetImageType(image,TrueColorMatteType);
8127 else
8128 (void) SetImageType(image,TrueColorType);
8129 (void) SyncImage(image);
8130 }
8131
8132 if (mng_info->write_png32)
8133 {
8134 mng_info->write_png_colortype = /* 6 */ 7;
8135 mng_info->write_png_depth = 8;
8136 image->depth = 8;
8137 if (image->matte == MagickTrue)
8138 (void) SetImageType(image,TrueColorMatteType);
8139 else
8140 (void) SetImageType(image,TrueColorType);
8141 (void) SyncImage(image);
8142 }
8143
8144 value=GetImageOption(image_info,"png:bit-depth");
8145 if (value != (char *) NULL)
8146 {
8147 if (LocaleCompare(value,"1") == 0)
8148 mng_info->write_png_depth = 1;
8149 else if (LocaleCompare(value,"2") == 0)
8150 mng_info->write_png_depth = 2;
8151 else if (LocaleCompare(value,"4") == 0)
8152 mng_info->write_png_depth = 4;
8153 else if (LocaleCompare(value,"8") == 0)
8154 mng_info->write_png_depth = 8;
8155 else if (LocaleCompare(value,"16") == 0)
8156 mng_info->write_png_depth = 16;
8157 if (logging != MagickFalse)
8158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8159 "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
8160 }
8161 value=GetImageOption(image_info,"png:color-type");
8162 if (value != (char *) NULL)
8163 {
8164 /* We must store colortype+1 because 0 is a valid colortype */
8165 if (LocaleCompare(value,"0") == 0)
8166 mng_info->write_png_colortype = 1;
8167 else if (LocaleCompare(value,"2") == 0)
8168 mng_info->write_png_colortype = 3;
8169 else if (LocaleCompare(value,"3") == 0)
8170 mng_info->write_png_colortype = 4;
8171 else if (LocaleCompare(value,"4") == 0)
8172 mng_info->write_png_colortype = 5;
8173 else if (LocaleCompare(value,"6") == 0)
8174 mng_info->write_png_colortype = 7;
8175 if (logging != MagickFalse)
8176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8177 "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
8178 }
8179
8180 status=WriteOnePNGImage(mng_info,image_info,image);
8181
8182 (void) CloseBlob(image);
8183
8184 MngInfoFreeStruct(mng_info,&have_mng_structure);
8185 if (logging != MagickFalse)
8186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8187 return(status);
8188}
8189
8190#if defined(JNG_SUPPORTED)
8191
8192/* Write one JNG image */
8193static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8194 const ImageInfo *image_info,Image *image)
8195{
8196 Image
8197 *jpeg_image;
8198
8199 ImageInfo
8200 *jpeg_image_info;
8201
8202 MagickBooleanType
8203 status;
8204
8205 size_t
8206 length;
8207
8208 unsigned char
8209 *blob,
8210 chunk[80],
8211 *p;
8212
8213 unsigned int
8214 jng_alpha_compression_method,
8215 jng_alpha_sample_depth,
8216 jng_color_type,
8217 logging,
8218 transparent;
8219
cristybb503372010-05-27 20:51:26 +00008220 size_t
cristy3ed852e2009-09-05 21:47:34 +00008221 jng_quality;
8222
8223 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8224 " enter WriteOneJNGImage()");
8225
8226 blob=(unsigned char *) NULL;
8227 jpeg_image=(Image *) NULL;
8228 jpeg_image_info=(ImageInfo *) NULL;
8229
8230 status=MagickTrue;
8231 transparent=image_info->type==GrayscaleMatteType ||
8232 image_info->type==TrueColorMatteType;
8233 jng_color_type=10;
8234 jng_alpha_sample_depth=0;
8235 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8236 jng_alpha_compression_method=0;
8237
8238 if (image->matte != MagickFalse)
8239 {
8240 /* if any pixels are transparent */
8241 transparent=MagickTrue;
8242 if (image_info->compression==JPEGCompression)
8243 jng_alpha_compression_method=8;
8244 }
8245
8246 if (transparent)
8247 {
8248 jng_color_type=14;
8249 /* Create JPEG blob, image, and image_info */
8250 if (logging != MagickFalse)
8251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8252 " Creating jpeg_image_info for opacity.");
8253 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8254 if (jpeg_image_info == (ImageInfo *) NULL)
8255 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8256 if (logging != MagickFalse)
8257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8258 " Creating jpeg_image.");
8259 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8260 if (jpeg_image == (Image *) NULL)
8261 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8262 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8263 status=SeparateImageChannel(jpeg_image,OpacityChannel);
8264 status=NegateImage(jpeg_image,MagickFalse);
8265 jpeg_image->matte=MagickFalse;
8266 if (jng_quality >= 1000)
8267 jpeg_image_info->quality=jng_quality/1000;
8268 else
8269 jpeg_image_info->quality=jng_quality;
8270 jpeg_image_info->type=GrayscaleType;
8271 (void) SetImageType(jpeg_image,GrayscaleType);
8272 (void) AcquireUniqueFilename(jpeg_image->filename);
8273 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8274 "%s",jpeg_image->filename);
8275 }
8276
8277 /* To do: check bit depth of PNG alpha channel */
8278
8279 /* Check if image is grayscale. */
8280 if (image_info->type != TrueColorMatteType && image_info->type !=
8281 TrueColorType && ImageIsGray(image))
8282 jng_color_type-=2;
8283
8284 if (transparent)
8285 {
8286 if (jng_alpha_compression_method==0)
8287 {
8288 const char
8289 *value;
8290
8291 /* Encode opacity as a grayscale PNG blob */
8292 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8293 &image->exception);
8294 if (logging != MagickFalse)
8295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8296 " Creating PNG blob.");
8297 length=0;
8298
8299 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8300 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8301 jpeg_image_info->interlace=NoInterlace;
8302
8303 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8304 &image->exception);
8305
8306 /* Retrieve sample depth used */
8307 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8308 if (value != (char *) NULL)
8309 jng_alpha_sample_depth= (unsigned int) value[0];
8310 }
8311 else
8312 {
8313 /* Encode opacity as a grayscale JPEG blob */
8314
8315 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8316 &image->exception);
8317
8318 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8319 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8320 jpeg_image_info->interlace=NoInterlace;
8321 if (logging != MagickFalse)
8322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8323 " Creating blob.");
8324 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +00008325 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00008326 jng_alpha_sample_depth=8;
8327 if (logging != MagickFalse)
8328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8329 " Successfully read jpeg_image into a blob, length=%lu.",
cristyf2faecf2010-05-28 19:19:36 +00008330 (unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00008331
8332 }
8333 /* Destroy JPEG image and image_info */
8334 jpeg_image=DestroyImage(jpeg_image);
8335 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8336 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8337 }
8338
8339 /* Write JHDR chunk */
8340 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
8341 PNGType(chunk,mng_JHDR);
8342 LogPNGChunk((int) logging,mng_JHDR,16L);
8343 PNGLong(chunk+4,image->columns);
8344 PNGLong(chunk+8,image->rows);
8345 chunk[12]=jng_color_type;
8346 chunk[13]=8; /* sample depth */
8347 chunk[14]=8; /*jng_image_compression_method */
8348 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8349 chunk[16]=jng_alpha_sample_depth;
8350 chunk[17]=jng_alpha_compression_method;
8351 chunk[18]=0; /*jng_alpha_filter_method */
8352 chunk[19]=0; /*jng_alpha_interlace_method */
8353 (void) WriteBlob(image,20,chunk);
8354 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8355 if (logging != MagickFalse)
8356 {
8357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00008358 " JNG width:%15lu",(unsigned long) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00008359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00008360 " JNG height:%14lu",(unsigned long) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00008361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8362 " JNG color type:%10d",jng_color_type);
8363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8364 " JNG sample depth:%8d",8);
8365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8366 " JNG compression:%9d",8);
8367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8368 " JNG interlace:%11d",0);
8369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8370 " JNG alpha depth:%9d",jng_alpha_sample_depth);
8371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8372 " JNG alpha compression:%3d",jng_alpha_compression_method);
8373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8374 " JNG alpha filter:%8d",0);
8375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8376 " JNG alpha interlace:%5d",0);
8377 }
8378
8379 /* Write any JNG-chunk-b profiles */
8380 (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8381
8382 /*
8383 Write leading ancillary chunks
8384 */
8385
8386 if (transparent)
8387 {
8388 /*
8389 Write JNG bKGD chunk
8390 */
8391
8392 unsigned char
8393 blue,
8394 green,
8395 red;
8396
cristybb503372010-05-27 20:51:26 +00008397 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008398 num_bytes;
8399
8400 if (jng_color_type == 8 || jng_color_type == 12)
8401 num_bytes=6L;
8402 else
8403 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +00008404 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +00008405 PNGType(chunk,mng_bKGD);
cristybb503372010-05-27 20:51:26 +00008406 LogPNGChunk((int) logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +00008407 red=ScaleQuantumToChar(image->background_color.red);
8408 green=ScaleQuantumToChar(image->background_color.green);
8409 blue=ScaleQuantumToChar(image->background_color.blue);
8410 *(chunk+4)=0;
8411 *(chunk+5)=red;
8412 *(chunk+6)=0;
8413 *(chunk+7)=green;
8414 *(chunk+8)=0;
8415 *(chunk+9)=blue;
8416 (void) WriteBlob(image,(size_t) num_bytes,chunk);
8417 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8418 }
8419
8420 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8421 {
8422 /*
8423 Write JNG sRGB chunk
8424 */
8425 (void) WriteBlobMSBULong(image,1L);
8426 PNGType(chunk,mng_sRGB);
8427 LogPNGChunk((int) logging,mng_sRGB,1L);
8428 if (image->rendering_intent != UndefinedIntent)
8429 chunk[4]=(unsigned char) (image->rendering_intent-1);
8430 else
8431 chunk[4]=(unsigned char) (PerceptualIntent-1);
8432 (void) WriteBlob(image,5,chunk);
8433 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8434 }
8435 else
8436 {
8437 if (image->gamma != 0.0)
8438 {
8439 /*
8440 Write JNG gAMA chunk
8441 */
8442 (void) WriteBlobMSBULong(image,4L);
8443 PNGType(chunk,mng_gAMA);
8444 LogPNGChunk((int) logging,mng_gAMA,4L);
cristybb503372010-05-27 20:51:26 +00008445 PNGLong(chunk+4,(size_t) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008446 (void) WriteBlob(image,8,chunk);
8447 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8448 }
8449 if ((mng_info->equal_chrms == MagickFalse) &&
8450 (image->chromaticity.red_primary.x != 0.0))
8451 {
8452 PrimaryInfo
8453 primary;
8454
8455 /*
8456 Write JNG cHRM chunk
8457 */
8458 (void) WriteBlobMSBULong(image,32L);
8459 PNGType(chunk,mng_cHRM);
8460 LogPNGChunk((int) logging,mng_cHRM,32L);
8461 primary=image->chromaticity.white_point;
cristybb503372010-05-27 20:51:26 +00008462 PNGLong(chunk+4,(size_t) (100000*primary.x+0.5));
8463 PNGLong(chunk+8,(size_t) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008464 primary=image->chromaticity.red_primary;
cristybb503372010-05-27 20:51:26 +00008465 PNGLong(chunk+12,(size_t) (100000*primary.x+0.5));
8466 PNGLong(chunk+16,(size_t) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008467 primary=image->chromaticity.green_primary;
cristybb503372010-05-27 20:51:26 +00008468 PNGLong(chunk+20,(size_t) (100000*primary.x+0.5));
8469 PNGLong(chunk+24,(size_t) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008470 primary=image->chromaticity.blue_primary;
cristybb503372010-05-27 20:51:26 +00008471 PNGLong(chunk+28,(size_t) (100000*primary.x+0.5));
8472 PNGLong(chunk+32,(size_t) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008473 (void) WriteBlob(image,36,chunk);
8474 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8475 }
8476 }
8477 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8478 {
8479 /*
8480 Write JNG pHYs chunk
8481 */
8482 (void) WriteBlobMSBULong(image,9L);
8483 PNGType(chunk,mng_pHYs);
8484 LogPNGChunk((int) logging,mng_pHYs,9L);
8485 if (image->units == PixelsPerInchResolution)
8486 {
cristybb503372010-05-27 20:51:26 +00008487 PNGLong(chunk+4,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00008488 (image->x_resolution*100.0/2.54+0.5));
cristybb503372010-05-27 20:51:26 +00008489 PNGLong(chunk+8,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00008490 (image->y_resolution*100.0/2.54+0.5));
8491 chunk[12]=1;
8492 }
8493 else
8494 {
8495 if (image->units == PixelsPerCentimeterResolution)
8496 {
cristybb503372010-05-27 20:51:26 +00008497 PNGLong(chunk+4,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00008498 (image->x_resolution*100.0+0.5));
cristybb503372010-05-27 20:51:26 +00008499 PNGLong(chunk+8,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00008500 (image->y_resolution*100.0+0.5));
8501 chunk[12]=1;
8502 }
8503 else
8504 {
cristybb503372010-05-27 20:51:26 +00008505 PNGLong(chunk+4,(size_t) (image->x_resolution+0.5));
8506 PNGLong(chunk+8,(size_t) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008507 chunk[12]=0;
8508 }
8509 }
8510 (void) WriteBlob(image,13,chunk);
8511 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8512 }
8513
8514 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8515 {
8516 /*
8517 Write JNG oFFs chunk
8518 */
8519 (void) WriteBlobMSBULong(image,9L);
8520 PNGType(chunk,mng_oFFs);
8521 LogPNGChunk((int) logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +00008522 PNGsLong(chunk+4,(ssize_t) (image->page.x));
8523 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +00008524 chunk[12]=0;
8525 (void) WriteBlob(image,13,chunk);
8526 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8527 }
8528 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8529 {
8530 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8531 PNGType(chunk,mng_vpAg);
8532 LogPNGChunk((int) logging,mng_vpAg,9L);
8533 PNGLong(chunk+4,(png_uint_32) image->page.width);
8534 PNGLong(chunk+8,(png_uint_32) image->page.height);
8535 chunk[12]=0; /* unit = pixels */
8536 (void) WriteBlob(image,13,chunk);
8537 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8538 }
8539
8540
8541 if (transparent)
8542 {
8543 if (jng_alpha_compression_method==0)
8544 {
cristybb503372010-05-27 20:51:26 +00008545 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008546 i;
8547
cristybb503372010-05-27 20:51:26 +00008548 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008549 len;
8550
8551 /* Write IDAT chunk header */
8552 if (logging != MagickFalse)
8553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00008554 " Write IDAT chunks from blob, length=%lu.",(unsigned long)
8555 length);
cristy3ed852e2009-09-05 21:47:34 +00008556
8557 /* Copy IDAT chunks */
8558 len=0;
8559 p=blob+8;
cristybb503372010-05-27 20:51:26 +00008560 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +00008561 {
8562 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8563 p+=4;
8564 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8565 {
8566 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +00008567 (void) WriteBlobMSBULong(image,(size_t) len);
8568 LogPNGChunk((int) logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +00008569 (void) WriteBlob(image,(size_t) len+4,p);
8570 (void) WriteBlobMSBULong(image,
8571 crc32(0,p,(uInt) len+4));
8572 }
8573 else
8574 {
8575 if (logging != MagickFalse)
8576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8577 " Skipping %c%c%c%c chunk, length=%lu.",
cristyf1d91242010-05-28 02:23:19 +00008578 *(p),*(p+1),*(p+2),*(p+3),(unsigned long) len);
cristy3ed852e2009-09-05 21:47:34 +00008579 }
8580 p+=(8+len);
8581 }
8582 }
8583 else
8584 {
8585 /* Write JDAA chunk header */
8586 if (logging != MagickFalse)
8587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf1d91242010-05-28 02:23:19 +00008588 " Write JDAA chunk, length=%lu.",(unsigned long) length);
cristybb503372010-05-27 20:51:26 +00008589 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00008590 PNGType(chunk,mng_JDAA);
8591 LogPNGChunk((int) logging,mng_JDAA,length);
8592 /* Write JDAT chunk(s) data */
8593 (void) WriteBlob(image,4,chunk);
8594 (void) WriteBlob(image,length,blob);
8595 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8596 (uInt) length));
8597 }
8598 blob=(unsigned char *) RelinquishMagickMemory(blob);
8599 }
8600
8601 /* Encode image as a JPEG blob */
8602 if (logging != MagickFalse)
8603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8604 " Creating jpeg_image_info.");
8605 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8606 if (jpeg_image_info == (ImageInfo *) NULL)
8607 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8608
8609 if (logging != MagickFalse)
8610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8611 " Creating jpeg_image.");
8612
8613 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8614 if (jpeg_image == (Image *) NULL)
8615 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8616 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8617
8618 (void) AcquireUniqueFilename(jpeg_image->filename);
8619 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8620 jpeg_image->filename);
8621
8622 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8623 &image->exception);
8624
8625 if (logging != MagickFalse)
8626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf1d91242010-05-28 02:23:19 +00008627 " Created jpeg_image, %lu x %lu.",(unsigned long) jpeg_image->columns,
8628 (unsigned long) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00008629
8630 if (jng_color_type == 8 || jng_color_type == 12)
8631 jpeg_image_info->type=GrayscaleType;
8632 jpeg_image_info->quality=jng_quality % 1000;
8633 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8634 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8635 if (logging != MagickFalse)
8636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8637 " Creating blob.");
8638 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8639 if (logging != MagickFalse)
8640 {
8641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8642 " Successfully read jpeg_image into a blob, length=%lu.",
cristyf1d91242010-05-28 02:23:19 +00008643 (unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00008644
8645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf1d91242010-05-28 02:23:19 +00008646 " Write JDAT chunk, length=%lu.",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00008647 }
8648 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +00008649 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00008650 PNGType(chunk,mng_JDAT);
8651 LogPNGChunk((int) logging,mng_JDAT,length);
8652 (void) WriteBlob(image,4,chunk);
8653 (void) WriteBlob(image,length,blob);
8654 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8655
8656 jpeg_image=DestroyImage(jpeg_image);
8657 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8658 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8659 blob=(unsigned char *) RelinquishMagickMemory(blob);
8660
8661 /* Write any JNG-chunk-e profiles */
8662 (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8663
8664 /* Write IEND chunk */
8665 (void) WriteBlobMSBULong(image,0L);
8666 PNGType(chunk,mng_IEND);
8667 LogPNGChunk((int) logging,mng_IEND,0);
8668 (void) WriteBlob(image,4,chunk);
8669 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8670
8671 if (logging != MagickFalse)
8672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8673 " exit WriteOneJNGImage()");
8674 return(status);
8675}
8676
8677
8678/*
8679%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8680% %
8681% %
8682% %
8683% W r i t e J N G I m a g e %
8684% %
8685% %
8686% %
8687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8688%
8689% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8690%
8691% JNG support written by Glenn Randers-Pehrson, glennrp@image...
8692%
8693% The format of the WriteJNGImage method is:
8694%
8695% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8696%
8697% A description of each parameter follows:
8698%
8699% o image_info: the image info.
8700%
8701% o image: The image.
8702%
8703%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8704*/
8705static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8706{
8707 MagickBooleanType
8708 status;
8709
8710 MngInfo
8711 *mng_info;
8712
8713 int
8714 have_mng_structure;
8715
8716 unsigned int
8717 logging;
8718
8719 /*
8720 Open image file.
8721 */
8722 assert(image_info != (const ImageInfo *) NULL);
8723 assert(image_info->signature == MagickSignature);
8724 assert(image != (Image *) NULL);
8725 assert(image->signature == MagickSignature);
8726 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8727 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8728 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8729 if (status == MagickFalse)
8730 return(status);
8731
8732 /*
8733 Allocate a MngInfo structure.
8734 */
8735 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00008736 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00008737 if (mng_info == (MngInfo *) NULL)
8738 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8739 /*
8740 Initialize members of the MngInfo structure.
8741 */
8742 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8743 mng_info->image=image;
8744 have_mng_structure=MagickTrue;
8745
8746 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8747
8748 status=WriteOneJNGImage(mng_info,image_info,image);
8749 (void) CloseBlob(image);
8750
8751 (void) CatchImageException(image);
8752 MngInfoFreeStruct(mng_info,&have_mng_structure);
8753 if (logging != MagickFalse)
8754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8755 return(status);
8756}
8757#endif
8758
8759
8760
8761static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8762{
8763 const char
8764 *option;
8765
8766 Image
8767 *next_image;
8768
8769 MagickBooleanType
8770 status;
8771
8772 MngInfo
8773 *mng_info;
8774
8775 int
8776 have_mng_structure,
8777 image_count,
8778 need_iterations,
8779 need_matte;
8780
8781 volatile int
8782#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8783 defined(PNG_MNG_FEATURES_SUPPORTED)
8784 need_local_plte,
8785#endif
8786 all_images_are_gray,
8787 logging,
8788 need_defi,
8789 optimize,
8790 use_global_plte;
8791
cristybb503372010-05-27 20:51:26 +00008792 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008793 i;
8794
8795 unsigned char
8796 chunk[800];
8797
8798 volatile unsigned int
8799 write_jng,
8800 write_mng;
8801
cristybb503372010-05-27 20:51:26 +00008802 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00008803 scene;
8804
cristybb503372010-05-27 20:51:26 +00008805 size_t
cristy3ed852e2009-09-05 21:47:34 +00008806 final_delay=0,
8807 initial_delay;
8808
glennrpd5045b42010-03-24 12:40:35 +00008809#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00008810 if (image_info->verbose)
8811 printf("Your PNG library (libpng-%s) is rather old.\n",
8812 PNG_LIBPNG_VER_STRING);
8813#endif
8814
8815 /*
8816 Open image file.
8817 */
8818 assert(image_info != (const ImageInfo *) NULL);
8819 assert(image_info->signature == MagickSignature);
8820 assert(image != (Image *) NULL);
8821 assert(image->signature == MagickSignature);
8822 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8823 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8824 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8825 if (status == MagickFalse)
8826 return(status);
8827
8828 /*
8829 Allocate a MngInfo structure.
8830 */
8831 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00008832 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00008833 if (mng_info == (MngInfo *) NULL)
8834 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8835 /*
8836 Initialize members of the MngInfo structure.
8837 */
8838 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8839 mng_info->image=image;
8840 have_mng_structure=MagickTrue;
8841 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
8842
8843 /*
8844 * See if user has requested a specific PNG subformat to be used
8845 * for all of the PNGs in the MNG being written, e.g.,
8846 *
8847 * convert *.png png8:animation.mng
8848 *
8849 * To do: check -define png:bit_depth and png:color_type as well,
8850 * or perhaps use mng:bit_depth and mng:color_type instead for
8851 * global settings.
8852 */
8853
8854 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8855 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8856 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8857
8858 write_jng=MagickFalse;
8859 if (image_info->compression == JPEGCompression)
8860 write_jng=MagickTrue;
8861
8862 mng_info->adjoin=image_info->adjoin &&
8863 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
8864
8865 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8866 optimize=MagickFalse;
8867 else
8868 optimize=(image_info->type == OptimizeType || image_info->type ==
8869 UndefinedType);
8870
8871 if (logging != MagickFalse)
8872 {
8873 /* Log some info about the input */
8874 Image
8875 *p;
8876
8877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8878 " Checking input image(s)");
8879 if (optimize)
8880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8881 " Optimize: TRUE");
8882 else
8883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8884 " Optimize: FALSE");
8885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf1d91242010-05-28 02:23:19 +00008886 " Image_info depth: %lu",(unsigned long) image_info->depth);
cristy3ed852e2009-09-05 21:47:34 +00008887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8888 " Type: %d",image_info->type);
8889
8890 scene=0;
8891 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8892 {
8893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf1d91242010-05-28 02:23:19 +00008894 " Scene: %lu",(unsigned long) scene++);
cristy3ed852e2009-09-05 21:47:34 +00008895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf1d91242010-05-28 02:23:19 +00008896 " Image depth: %lu",(unsigned long) p->depth);
cristy3ed852e2009-09-05 21:47:34 +00008897 if (p->matte)
8898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8899 " Matte: True");
8900 else
8901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902 " Matte: False");
8903 if (p->storage_class == PseudoClass)
8904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8905 " Storage class: PseudoClass");
8906 else
8907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8908 " Storage class: DirectClass");
8909 if (p->colors)
8910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf1d91242010-05-28 02:23:19 +00008911 " Number of colors: %lu",(unsigned long) p->colors);
cristy3ed852e2009-09-05 21:47:34 +00008912 else
8913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8914 " Number of colors: unspecified");
8915 if (mng_info->adjoin == MagickFalse)
8916 break;
8917 }
8918 }
8919
8920 /*
8921 Sometimes we get PseudoClass images whose RGB values don't match
8922 the colors in the colormap. This code syncs the RGB values.
8923 */
8924 {
8925 Image
8926 *p;
8927
8928 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8929 {
8930 if (p->taint && p->storage_class == PseudoClass)
8931 (void) SyncImage(p);
8932 if (mng_info->adjoin == MagickFalse)
8933 break;
8934 }
8935 }
8936
8937#ifdef PNG_BUILD_PALETTE
8938 if (optimize)
8939 {
8940 /*
8941 Sometimes we get DirectClass images that have 256 colors or fewer.
8942 This code will convert them to PseudoClass and build a colormap.
8943 */
8944 Image
8945 *p;
8946
8947 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8948 {
8949 if (p->storage_class != PseudoClass)
8950 {
8951 p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
8952 if (p->colors <= 256)
8953 {
8954 p->colors=0;
8955 if (p->matte != MagickFalse)
8956 (void) SetImageType(p,PaletteMatteType);
8957 else
8958 (void) SetImageType(p,PaletteType);
8959 }
8960 }
8961 if (mng_info->adjoin == MagickFalse)
8962 break;
8963 }
8964 }
8965#endif
8966
8967 use_global_plte=MagickFalse;
8968 all_images_are_gray=MagickFalse;
8969#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8970 need_local_plte=MagickTrue;
8971#endif
8972 need_defi=MagickFalse;
8973 need_matte=MagickFalse;
8974 mng_info->framing_mode=1;
8975 mng_info->old_framing_mode=1;
8976
8977 if (write_mng)
8978 if (image_info->page != (char *) NULL)
8979 {
8980 /*
8981 Determine image bounding box.
8982 */
8983 SetGeometry(image,&mng_info->page);
8984 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
8985 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
8986 }
8987 if (write_mng)
8988 {
8989 unsigned int
8990 need_geom;
8991
8992 unsigned short
8993 red,
8994 green,
8995 blue;
8996
8997 mng_info->page=image->page;
8998 need_geom=MagickTrue;
8999 if (mng_info->page.width || mng_info->page.height)
9000 need_geom=MagickFalse;
9001 /*
9002 Check all the scenes.
9003 */
9004 initial_delay=image->delay;
9005 need_iterations=MagickFalse;
9006 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
9007 mng_info->equal_physs=MagickTrue,
9008 mng_info->equal_gammas=MagickTrue;
9009 mng_info->equal_srgbs=MagickTrue;
9010 mng_info->equal_backgrounds=MagickTrue;
9011 image_count=0;
9012#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9013 defined(PNG_MNG_FEATURES_SUPPORTED)
9014 all_images_are_gray=MagickTrue;
9015 mng_info->equal_palettes=MagickFalse;
9016 need_local_plte=MagickFalse;
9017#endif
9018 for (next_image=image; next_image != (Image *) NULL; )
9019 {
9020 if (need_geom)
9021 {
9022 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
9023 mng_info->page.width=next_image->columns+next_image->page.x;
9024 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
9025 mng_info->page.height=next_image->rows+next_image->page.y;
9026 }
9027 if (next_image->page.x || next_image->page.y)
9028 need_defi=MagickTrue;
9029 if (next_image->matte)
9030 need_matte=MagickTrue;
9031 if ((int) next_image->dispose >= BackgroundDispose)
9032 if (next_image->matte || next_image->page.x || next_image->page.y ||
9033 ((next_image->columns < mng_info->page.width) &&
9034 (next_image->rows < mng_info->page.height)))
9035 mng_info->need_fram=MagickTrue;
9036 if (next_image->iterations)
9037 need_iterations=MagickTrue;
9038 final_delay=next_image->delay;
9039 if (final_delay != initial_delay || final_delay > 1UL*
9040 next_image->ticks_per_second)
9041 mng_info->need_fram=1;
9042#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9043 defined(PNG_MNG_FEATURES_SUPPORTED)
9044 /*
9045 check for global palette possibility.
9046 */
9047 if (image->matte != MagickFalse)
9048 need_local_plte=MagickTrue;
9049 if (need_local_plte == 0)
9050 {
9051 if (ImageIsGray(image) == MagickFalse)
9052 all_images_are_gray=MagickFalse;
9053 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
9054 if (use_global_plte == 0)
9055 use_global_plte=mng_info->equal_palettes;
9056 need_local_plte=!mng_info->equal_palettes;
9057 }
9058#endif
9059 if (GetNextImageInList(next_image) != (Image *) NULL)
9060 {
9061 if (next_image->background_color.red !=
9062 next_image->next->background_color.red ||
9063 next_image->background_color.green !=
9064 next_image->next->background_color.green ||
9065 next_image->background_color.blue !=
9066 next_image->next->background_color.blue)
9067 mng_info->equal_backgrounds=MagickFalse;
9068 if (next_image->gamma != next_image->next->gamma)
9069 mng_info->equal_gammas=MagickFalse;
9070 if (next_image->rendering_intent !=
9071 next_image->next->rendering_intent)
9072 mng_info->equal_srgbs=MagickFalse;
9073 if ((next_image->units != next_image->next->units) ||
9074 (next_image->x_resolution != next_image->next->x_resolution) ||
9075 (next_image->y_resolution != next_image->next->y_resolution))
9076 mng_info->equal_physs=MagickFalse;
9077 if (mng_info->equal_chrms)
9078 {
9079 if (next_image->chromaticity.red_primary.x !=
9080 next_image->next->chromaticity.red_primary.x ||
9081 next_image->chromaticity.red_primary.y !=
9082 next_image->next->chromaticity.red_primary.y ||
9083 next_image->chromaticity.green_primary.x !=
9084 next_image->next->chromaticity.green_primary.x ||
9085 next_image->chromaticity.green_primary.y !=
9086 next_image->next->chromaticity.green_primary.y ||
9087 next_image->chromaticity.blue_primary.x !=
9088 next_image->next->chromaticity.blue_primary.x ||
9089 next_image->chromaticity.blue_primary.y !=
9090 next_image->next->chromaticity.blue_primary.y ||
9091 next_image->chromaticity.white_point.x !=
9092 next_image->next->chromaticity.white_point.x ||
9093 next_image->chromaticity.white_point.y !=
9094 next_image->next->chromaticity.white_point.y)
9095 mng_info->equal_chrms=MagickFalse;
9096 }
9097 }
9098 image_count++;
9099 next_image=GetNextImageInList(next_image);
9100 }
9101 if (image_count < 2)
9102 {
9103 mng_info->equal_backgrounds=MagickFalse;
9104 mng_info->equal_chrms=MagickFalse;
9105 mng_info->equal_gammas=MagickFalse;
9106 mng_info->equal_srgbs=MagickFalse;
9107 mng_info->equal_physs=MagickFalse;
9108 use_global_plte=MagickFalse;
9109#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9110 need_local_plte=MagickTrue;
9111#endif
9112 need_iterations=MagickFalse;
9113 }
9114 if (mng_info->need_fram == MagickFalse)
9115 {
9116 /*
9117 Only certain framing rates 100/n are exactly representable without
9118 the FRAM chunk but we'll allow some slop in VLC files
9119 */
9120 if (final_delay == 0)
9121 {
9122 if (need_iterations != MagickFalse)
9123 {
9124 /*
9125 It's probably a GIF with loop; don't run it *too* fast.
9126 */
9127 final_delay=10;
9128 (void) ThrowMagickException(&image->exception,
9129 GetMagickModule(),CoderError,
9130 "input has zero delay between all frames; assuming 10 cs",
9131 "`%s'","");
9132 }
9133 else
9134 mng_info->ticks_per_second=0;
9135 }
9136 if (final_delay != 0)
9137 mng_info->ticks_per_second=1UL*image->ticks_per_second/final_delay;
9138 if (final_delay > 50)
9139 mng_info->ticks_per_second=2;
9140 if (final_delay > 75)
9141 mng_info->ticks_per_second=1;
9142 if (final_delay > 125)
9143 mng_info->need_fram=MagickTrue;
9144 if (need_defi && final_delay > 2 && (final_delay != 4) &&
9145 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
9146 (final_delay != 25) && (final_delay != 50) && (final_delay !=
9147 1UL*image->ticks_per_second))
9148 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
9149 }
9150 if (mng_info->need_fram != MagickFalse)
9151 mng_info->ticks_per_second=1UL*image->ticks_per_second;
9152 /*
9153 If pseudocolor, we should also check to see if all the
9154 palettes are identical and write a global PLTE if they are.
9155 ../glennrp Feb 99.
9156 */
9157 /*
9158 Write the MNG version 1.0 signature and MHDR chunk.
9159 */
9160 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9161 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
9162 PNGType(chunk,mng_MHDR);
9163 LogPNGChunk((int) logging,mng_MHDR,28L);
9164 PNGLong(chunk+4,mng_info->page.width);
9165 PNGLong(chunk+8,mng_info->page.height);
9166 PNGLong(chunk+12,mng_info->ticks_per_second);
9167 PNGLong(chunk+16,0L); /* layer count=unknown */
9168 PNGLong(chunk+20,0L); /* frame count=unknown */
9169 PNGLong(chunk+24,0L); /* play time=unknown */
9170 if (write_jng)
9171 {
9172 if (need_matte)
9173 {
9174 if (need_defi || mng_info->need_fram || use_global_plte)
9175 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
9176 else
9177 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
9178 }
9179 else
9180 {
9181 if (need_defi || mng_info->need_fram || use_global_plte)
9182 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
9183 else
9184 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
9185 }
9186 }
9187 else
9188 {
9189 if (need_matte)
9190 {
9191 if (need_defi || mng_info->need_fram || use_global_plte)
9192 PNGLong(chunk+28,11L); /* simplicity=LC */
9193 else
9194 PNGLong(chunk+28,9L); /* simplicity=VLC */
9195 }
9196 else
9197 {
9198 if (need_defi || mng_info->need_fram || use_global_plte)
9199 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
9200 else
9201 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
9202 }
9203 }
9204 (void) WriteBlob(image,32,chunk);
9205 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9206 option=GetImageOption(image_info,"mng:need-cacheoff");
9207 if (option != (const char *) NULL)
9208 {
9209 size_t
9210 length;
9211
9212 /*
9213 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9214 */
9215 PNGType(chunk,mng_nEED);
9216 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +00009217 (void) WriteBlobMSBULong(image,(size_t) length);
9218 LogPNGChunk((int) logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00009219 length+=4;
9220 (void) WriteBlob(image,length,chunk);
9221 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9222 }
9223 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9224 (GetNextImageInList(image) != (Image *) NULL) &&
9225 (image->iterations != 1))
9226 {
9227 /*
9228 Write MNG TERM chunk
9229 */
9230 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9231 PNGType(chunk,mng_TERM);
9232 LogPNGChunk((int) logging,mng_TERM,10L);
9233 chunk[4]=3; /* repeat animation */
9234 chunk[5]=0; /* show last frame when done */
9235 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9236 final_delay/MagickMax(image->ticks_per_second,1)));
9237 if (image->iterations == 0)
9238 PNGLong(chunk+10,PNG_UINT_31_MAX);
9239 else
9240 PNGLong(chunk+10,(png_uint_32) image->iterations);
9241 if (logging != MagickFalse)
9242 {
9243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9244 " TERM delay: %lu",
cristyf1d91242010-05-28 02:23:19 +00009245 (unsigned long) (mng_info->ticks_per_second*
cristy3ed852e2009-09-05 21:47:34 +00009246 final_delay/MagickMax(image->ticks_per_second,1)));
9247 if (image->iterations == 0)
9248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf1d91242010-05-28 02:23:19 +00009249 " TERM iterations: %lu",(unsigned long) PNG_UINT_31_MAX);
cristy3ed852e2009-09-05 21:47:34 +00009250 else
9251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf1d91242010-05-28 02:23:19 +00009252 " Image iterations: %lu",(unsigned long) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00009253 }
9254 (void) WriteBlob(image,14,chunk);
9255 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9256 }
9257 /*
9258 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9259 */
9260 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9261 mng_info->equal_srgbs)
9262 {
9263 /*
9264 Write MNG sRGB chunk
9265 */
9266 (void) WriteBlobMSBULong(image,1L);
9267 PNGType(chunk,mng_sRGB);
9268 LogPNGChunk((int) logging,mng_sRGB,1L);
9269 if (image->rendering_intent != UndefinedIntent)
9270 chunk[4]=(unsigned char) (image->rendering_intent-1);
9271 else
9272 chunk[4]=(unsigned char) (PerceptualIntent-1);
9273 (void) WriteBlob(image,5,chunk);
9274 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9275 mng_info->have_write_global_srgb=MagickTrue;
9276 }
9277 else
9278 {
9279 if (image->gamma && mng_info->equal_gammas)
9280 {
9281 /*
9282 Write MNG gAMA chunk
9283 */
9284 (void) WriteBlobMSBULong(image,4L);
9285 PNGType(chunk,mng_gAMA);
9286 LogPNGChunk((int) logging,mng_gAMA,4L);
cristybb503372010-05-27 20:51:26 +00009287 PNGLong(chunk+4,(size_t) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009288 (void) WriteBlob(image,8,chunk);
9289 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9290 mng_info->have_write_global_gama=MagickTrue;
9291 }
9292 if (mng_info->equal_chrms)
9293 {
9294 PrimaryInfo
9295 primary;
9296
9297 /*
9298 Write MNG cHRM chunk
9299 */
9300 (void) WriteBlobMSBULong(image,32L);
9301 PNGType(chunk,mng_cHRM);
9302 LogPNGChunk((int) logging,mng_cHRM,32L);
9303 primary=image->chromaticity.white_point;
cristybb503372010-05-27 20:51:26 +00009304 PNGLong(chunk+4,(size_t) (100000*primary.x+0.5));
9305 PNGLong(chunk+8,(size_t) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009306 primary=image->chromaticity.red_primary;
cristybb503372010-05-27 20:51:26 +00009307 PNGLong(chunk+12,(size_t) (100000*primary.x+0.5));
9308 PNGLong(chunk+16,(size_t) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009309 primary=image->chromaticity.green_primary;
cristybb503372010-05-27 20:51:26 +00009310 PNGLong(chunk+20,(size_t) (100000*primary.x+0.5));
9311 PNGLong(chunk+24,(size_t) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009312 primary=image->chromaticity.blue_primary;
cristybb503372010-05-27 20:51:26 +00009313 PNGLong(chunk+28,(size_t) (100000*primary.x+0.5));
9314 PNGLong(chunk+32,(size_t) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009315 (void) WriteBlob(image,36,chunk);
9316 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9317 mng_info->have_write_global_chrm=MagickTrue;
9318 }
9319 }
9320 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9321 {
9322 /*
9323 Write MNG pHYs chunk
9324 */
9325 (void) WriteBlobMSBULong(image,9L);
9326 PNGType(chunk,mng_pHYs);
9327 LogPNGChunk((int) logging,mng_pHYs,9L);
9328 if (image->units == PixelsPerInchResolution)
9329 {
cristybb503372010-05-27 20:51:26 +00009330 PNGLong(chunk+4,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00009331 (image->x_resolution*100.0/2.54+0.5));
cristybb503372010-05-27 20:51:26 +00009332 PNGLong(chunk+8,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00009333 (image->y_resolution*100.0/2.54+0.5));
9334 chunk[12]=1;
9335 }
9336 else
9337 {
9338 if (image->units == PixelsPerCentimeterResolution)
9339 {
cristybb503372010-05-27 20:51:26 +00009340 PNGLong(chunk+4,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00009341 (image->x_resolution*100.0+0.5));
cristybb503372010-05-27 20:51:26 +00009342 PNGLong(chunk+8,(size_t)
cristy3ed852e2009-09-05 21:47:34 +00009343 (image->y_resolution*100.0+0.5));
9344 chunk[12]=1;
9345 }
9346 else
9347 {
cristybb503372010-05-27 20:51:26 +00009348 PNGLong(chunk+4,(size_t) (image->x_resolution+0.5));
9349 PNGLong(chunk+8,(size_t) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009350 chunk[12]=0;
9351 }
9352 }
9353 (void) WriteBlob(image,13,chunk);
9354 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9355 }
9356 /*
9357 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9358 or does not cover the entire frame.
9359 */
9360 if (write_mng && (image->matte || image->page.x > 0 ||
9361 image->page.y > 0 || (image->page.width &&
9362 (image->page.width+image->page.x < mng_info->page.width))
9363 || (image->page.height && (image->page.height+image->page.y
9364 < mng_info->page.height))))
9365 {
9366 (void) WriteBlobMSBULong(image,6L);
9367 PNGType(chunk,mng_BACK);
9368 LogPNGChunk((int) logging,mng_BACK,6L);
9369 red=ScaleQuantumToShort(image->background_color.red);
9370 green=ScaleQuantumToShort(image->background_color.green);
9371 blue=ScaleQuantumToShort(image->background_color.blue);
9372 PNGShort(chunk+4,red);
9373 PNGShort(chunk+6,green);
9374 PNGShort(chunk+8,blue);
9375 (void) WriteBlob(image,10,chunk);
9376 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9377 if (mng_info->equal_backgrounds)
9378 {
9379 (void) WriteBlobMSBULong(image,6L);
9380 PNGType(chunk,mng_bKGD);
9381 LogPNGChunk((int) logging,mng_bKGD,6L);
9382 (void) WriteBlob(image,10,chunk);
9383 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9384 }
9385 }
9386
9387#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9388 if ((need_local_plte == MagickFalse) &&
9389 (image->storage_class == PseudoClass) &&
9390 (all_images_are_gray == MagickFalse))
9391 {
cristybb503372010-05-27 20:51:26 +00009392 size_t
cristy3ed852e2009-09-05 21:47:34 +00009393 data_length;
9394
9395 /*
9396 Write MNG PLTE chunk
9397 */
9398 data_length=3*image->colors;
9399 (void) WriteBlobMSBULong(image,data_length);
9400 PNGType(chunk,mng_PLTE);
9401 LogPNGChunk((int) logging,mng_PLTE,data_length);
cristybb503372010-05-27 20:51:26 +00009402 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009403 {
9404 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9405 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9406 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9407 }
9408 (void) WriteBlob(image,data_length+4,chunk);
9409 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9410 mng_info->have_write_global_plte=MagickTrue;
9411 }
9412#endif
9413 }
9414 scene=0;
9415 mng_info->delay=0;
9416#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9417 defined(PNG_MNG_FEATURES_SUPPORTED)
9418 mng_info->equal_palettes=MagickFalse;
9419#endif
9420 do
9421 {
9422 if (mng_info->adjoin)
9423 {
9424#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9425 defined(PNG_MNG_FEATURES_SUPPORTED)
9426 /*
9427 If we aren't using a global palette for the entire MNG, check to
9428 see if we can use one for two or more consecutive images.
9429 */
9430 if (need_local_plte && use_global_plte && !all_images_are_gray)
9431 {
9432 if (mng_info->IsPalette)
9433 {
9434 /*
9435 When equal_palettes is true, this image has the same palette
9436 as the previous PseudoClass image
9437 */
9438 mng_info->have_write_global_plte=mng_info->equal_palettes;
9439 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9440 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9441 {
9442 /*
9443 Write MNG PLTE chunk
9444 */
cristybb503372010-05-27 20:51:26 +00009445 size_t
cristy3ed852e2009-09-05 21:47:34 +00009446 data_length;
9447
9448 data_length=3*image->colors;
9449 (void) WriteBlobMSBULong(image,data_length);
9450 PNGType(chunk,mng_PLTE);
9451 LogPNGChunk((int) logging,mng_PLTE,data_length);
cristybb503372010-05-27 20:51:26 +00009452 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009453 {
9454 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9455 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9456 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9457 }
9458 (void) WriteBlob(image,data_length+4,chunk);
9459 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9460 (uInt) (data_length+4)));
9461 mng_info->have_write_global_plte=MagickTrue;
9462 }
9463 }
9464 else
9465 mng_info->have_write_global_plte=MagickFalse;
9466 }
9467#endif
9468 if (need_defi)
9469 {
cristybb503372010-05-27 20:51:26 +00009470 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00009471 previous_x,
9472 previous_y;
9473
9474 if (scene)
9475 {
9476 previous_x=mng_info->page.x;
9477 previous_y=mng_info->page.y;
9478 }
9479 else
9480 {
9481 previous_x=0;
9482 previous_y=0;
9483 }
9484 mng_info->page=image->page;
9485 if ((mng_info->page.x != previous_x) ||
9486 (mng_info->page.y != previous_y))
9487 {
9488 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
9489 PNGType(chunk,mng_DEFI);
9490 LogPNGChunk((int) logging,mng_DEFI,12L);
9491 chunk[4]=0; /* object 0 MSB */
9492 chunk[5]=0; /* object 0 LSB */
9493 chunk[6]=0; /* visible */
9494 chunk[7]=0; /* abstract */
9495 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9496 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9497 (void) WriteBlob(image,16,chunk);
9498 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9499 }
9500 }
9501 }
9502
9503 mng_info->write_mng=write_mng;
9504
9505 if ((int) image->dispose >= 3)
9506 mng_info->framing_mode=3;
9507
9508 if (mng_info->need_fram && mng_info->adjoin &&
9509 ((image->delay != mng_info->delay) ||
9510 (mng_info->framing_mode != mng_info->old_framing_mode)))
9511 {
9512 if (image->delay == mng_info->delay)
9513 {
9514 /*
9515 Write a MNG FRAM chunk with the new framing mode.
9516 */
9517 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
9518 PNGType(chunk,mng_FRAM);
9519 LogPNGChunk((int) logging,mng_FRAM,1L);
9520 chunk[4]=(unsigned char) mng_info->framing_mode;
9521 (void) WriteBlob(image,5,chunk);
9522 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9523 }
9524 else
9525 {
9526 /*
9527 Write a MNG FRAM chunk with the delay.
9528 */
9529 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9530 PNGType(chunk,mng_FRAM);
9531 LogPNGChunk((int) logging,mng_FRAM,10L);
9532 chunk[4]=(unsigned char) mng_info->framing_mode;
9533 chunk[5]=0; /* frame name separator (no name) */
9534 chunk[6]=2; /* flag for changing default delay */
9535 chunk[7]=0; /* flag for changing frame timeout */
9536 chunk[8]=0; /* flag for changing frame clipping */
9537 chunk[9]=0; /* flag for changing frame sync_id */
9538 PNGLong(chunk+10,(png_uint_32)
9539 ((mng_info->ticks_per_second*
9540 image->delay)/MagickMax(image->ticks_per_second,1)));
9541 (void) WriteBlob(image,14,chunk);
9542 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9543 mng_info->delay=image->delay;
9544 }
9545 mng_info->old_framing_mode=mng_info->framing_mode;
9546 }
9547
9548#if defined(JNG_SUPPORTED)
9549 if (image_info->compression == JPEGCompression)
9550 {
9551 ImageInfo
9552 *write_info;
9553
9554 if (logging != MagickFalse)
9555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9556 " Writing JNG object.");
9557 /* To do: specify the desired alpha compression method. */
9558 write_info=CloneImageInfo(image_info);
9559 write_info->compression=UndefinedCompression;
9560 status=WriteOneJNGImage(mng_info,write_info,image);
9561 write_info=DestroyImageInfo(write_info);
9562 }
9563 else
9564#endif
9565 {
9566 if (logging != MagickFalse)
9567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9568 " Writing PNG object.");
9569 status=WriteOnePNGImage(mng_info,image_info,image);
9570 }
9571
9572 if (status == MagickFalse)
9573 {
9574 MngInfoFreeStruct(mng_info,&have_mng_structure);
9575 (void) CloseBlob(image);
9576 return(MagickFalse);
9577 }
9578 (void) CatchImageException(image);
9579 if (GetNextImageInList(image) == (Image *) NULL)
9580 break;
9581 image=SyncNextImageInList(image);
9582 status=SetImageProgress(image,SaveImagesTag,scene++,
9583 GetImageListLength(image));
9584 if (status == MagickFalse)
9585 break;
9586 } while (mng_info->adjoin);
9587 if (write_mng)
9588 {
9589 while (GetPreviousImageInList(image) != (Image *) NULL)
9590 image=GetPreviousImageInList(image);
9591 /*
9592 Write the MEND chunk.
9593 */
9594 (void) WriteBlobMSBULong(image,0x00000000L);
9595 PNGType(chunk,mng_MEND);
9596 LogPNGChunk((int) logging,mng_MEND,0L);
9597 (void) WriteBlob(image,4,chunk);
9598 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9599 }
9600 /*
9601 Relinquish resources.
9602 */
9603 (void) CloseBlob(image);
9604 MngInfoFreeStruct(mng_info,&have_mng_structure);
9605 if (logging != MagickFalse)
9606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9607 return(MagickTrue);
9608}
glennrpd5045b42010-03-24 12:40:35 +00009609#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00009610static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9611{
9612 image=image;
9613 printf("Your PNG library is too old: You have libpng-%s\n",
9614 PNG_LIBPNG_VER_STRING);
9615 ThrowBinaryException(CoderError,"PNG library is too old",
9616 image_info->filename);
9617}
9618static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9619{
9620 return(WritePNGImage(image_info,image));
9621}
glennrpd5045b42010-03-24 12:40:35 +00009622#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00009623#endif