blob: 4d1304ad81611eb0d6d572f57bed3b1c75c461e8 [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{
cristy8182b072010-05-30 20:10:53 +0000247 long
cristy3ed852e2009-09-05 21:47:34 +0000248 left,
249 right,
250 top,
251 bottom;
252} MngBox;
253
254typedef struct _MngPair
255{
cristy8182b072010-05-30 20:10:53 +0000256 volatile long
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
cristy35ef8242010-06-03 16:24:13 +0000390 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000391 delay,
392 global_plte_length,
393 global_trns_length,
394 global_x_pixels_per_unit,
395 global_y_pixels_per_unit,
396 mng_width,
397 mng_height,
398 ticks_per_second;
399
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(),
cristye8c25f92010-06-03 00:53:06 +0000552 " CompressColorMapTransFirst %s (%.20g colors)",image->filename,
553 (double) 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(),
cristye8c25f92010-06-03 00:53:06 +00001058 " Writing %c%c%c%c chunk, length: %.20g",
1059 type[0],type[1],type[2],type[3],(double) 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,
cristye8c25f92010-06-03 00:53:06 +00001181 "Expected %.20g bytes; found %.20g bytes",(double) length,
1182 (double) 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 */
cristy8182b072010-05-30 20:10:53 +00001413 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1414 pair.b=(long) ((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
cristy8182b072010-05-30 20:10:53 +00001423static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001424{
cristy8182b072010-05-30 20:10:53 +00001425 return((long) ((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(),
cristye8c25f92010-06-03 00:53:06 +00001897 " PNG width: %.20g, height: %.20g",
1898 (double) ping_width, (double) 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(),
cristye8c25f92010-06-03 00:53:06 +00002018 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2019 image->page.x,(double) 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(),
cristye8c25f92010-06-03 00:53:06 +00002058 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2059 (double) x_resolution,(double) 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.");
glennrp2cbb4482010-06-02 04:37:24 +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 }
glennrp2cbb4482010-06-02 04:37:24 +00002141 else /* Scale background components to 16-bit */
cristy3ed852e2009-09-05 21:47:34 +00002142 {
glennrp2cbb4482010-06-02 04:37:24 +00002143 unsigned int
2144 bkgd_scale;
2145
2146 if (logging != MagickFalse)
2147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2148 " raw ping_background=(%d,%d,%d).",ping_background->red,
2149 ping_background->green,ping_background->blue);
2150
2151 bkgd_scale = 1;
2152 if (ping_bit_depth == 1)
2153 bkgd_scale = 255;
2154 else if (ping_bit_depth == 2)
2155 bkgd_scale = 85;
2156 else if (ping_bit_depth == 4)
2157 bkgd_scale = 17;
2158 if (ping_bit_depth <= 8)
2159 bkgd_scale *= 257;
2160
2161 ping_background->red *= bkgd_scale;
2162 ping_background->green *= bkgd_scale;
2163 ping_background->blue *= bkgd_scale;
2164
2165 if (logging != MagickFalse)
2166 {
2167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2168 " bkgd_scale=%d.",bkgd_scale);
2169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2170 " ping_background=(%d,%d,%d).",ping_background->red,
2171 ping_background->green,ping_background->blue);
2172 }
2173
cristy3ed852e2009-09-05 21:47:34 +00002174 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002175 ScaleShortToQuantum(ping_background->red);
cristy3ed852e2009-09-05 21:47:34 +00002176 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002177 ScaleShortToQuantum(ping_background->green);
cristy3ed852e2009-09-05 21:47:34 +00002178 image->background_color.blue=
glennrpfaa852b2010-03-30 12:17:00 +00002179 ScaleShortToQuantum(ping_background->blue);
glennrp2cbb4482010-06-02 04:37:24 +00002180
2181 if (logging != MagickFalse)
2182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002183 " image->background_color=(%.20g,%.20g,%.20g).",
2184 (double) image->background_color.red,
2185 (double) image->background_color.green,
2186 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002187 }
2188 }
2189#endif
2190 transparent_color.red=0;
2191 transparent_color.green=0;
2192 transparent_color.blue=0;
2193 transparent_color.opacity=0;
glennrpfaa852b2010-03-30 12:17:00 +00002194 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002195 {
2196 /*
2197 Image has a transparent background.
2198 */
2199 int
2200 max_sample;
2201
cristy35ef8242010-06-03 16:24:13 +00002202 size_t
2203 one=1;
2204
cristy3ed852e2009-09-05 21:47:34 +00002205 if (logging != MagickFalse)
2206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2207 " Reading PNG tRNS chunk.");
2208
cristyf9cca6a2010-06-04 23:49:28 +00002209 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002210
glennrpfaa852b2010-03-30 12:17:00 +00002211 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2212 (int)ping_trans_color->gray > max_sample) ||
2213 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2214 ((int)ping_trans_color->red > max_sample ||
2215 (int)ping_trans_color->green > max_sample ||
2216 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002217 {
2218 if (logging != MagickFalse)
2219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2220 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002221 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002222 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002223 image->matte=MagickFalse;
2224 }
2225 else
2226 {
glennrpfaa852b2010-03-30 12:17:00 +00002227 transparent_color.red= (Quantum)(ping_trans_color->red);
2228 transparent_color.green= (Quantum) (ping_trans_color->green);
2229 transparent_color.blue= (Quantum) (ping_trans_color->blue);
2230 transparent_color.opacity= (Quantum) (ping_trans_color->gray);
2231 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002232 {
glennrpfaa852b2010-03-30 12:17:00 +00002233 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002234 {
2235 transparent_color.opacity=(Quantum) (((
glennrpfaa852b2010-03-30 12:17:00 +00002236 ping_trans_color->gray)*255)/max_sample);
cristy3ed852e2009-09-05 21:47:34 +00002237 }
2238 transparent_color.red=transparent_color.opacity;
2239 transparent_color.green=transparent_color.opacity;
2240 transparent_color.blue=transparent_color.opacity;
2241 }
2242 }
2243 }
2244#if defined(PNG_READ_sBIT_SUPPORTED)
2245 if (mng_info->have_global_sbit)
2246 {
glennrpfaa852b2010-03-30 12:17:00 +00002247 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002248 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2249 }
2250#endif
2251 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002252
cristy3ed852e2009-09-05 21:47:34 +00002253 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002254
2255 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2256
cristy3ed852e2009-09-05 21:47:34 +00002257 /*
2258 Initialize image structure.
2259 */
2260 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002261 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002262 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002263 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002264 if (mng_info->mng_type == 0)
2265 {
glennrpfaa852b2010-03-30 12:17:00 +00002266 mng_info->mng_width=ping_width;
2267 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002268 mng_info->frame=mng_info->image_box;
2269 mng_info->clip=mng_info->image_box;
2270 }
2271 else
2272 {
2273 image->page.y=mng_info->y_off[mng_info->object_id];
2274 }
2275 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002276 image->columns=ping_width;
2277 image->rows=ping_height;
2278 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2279 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2280 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002281 {
cristybefe4d22010-06-07 01:18:58 +00002282 size_t
2283 one;
2284
cristy3ed852e2009-09-05 21:47:34 +00002285 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002286 one=1;
2287 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002288#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2289 if (image->colors > 256)
2290 image->colors=256;
2291#else
2292 if (image->colors > 65536L)
2293 image->colors=65536L;
2294#endif
glennrpfaa852b2010-03-30 12:17:00 +00002295 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002296 {
2297 int
2298 number_colors;
2299
2300 png_colorp
2301 palette;
2302
2303 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002304 image->colors=(size_t) number_colors;
cristy3ed852e2009-09-05 21:47:34 +00002305 if (logging != MagickFalse)
2306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2307 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2308 }
2309 }
2310
2311 if (image->storage_class == PseudoClass)
2312 {
2313 /*
2314 Initialize image colormap.
2315 */
2316 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2317 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrpfaa852b2010-03-30 12:17:00 +00002318 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002319 {
2320 int
2321 number_colors;
2322
2323 png_colorp
2324 palette;
2325
2326 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002327 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002328 {
2329 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2330 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2331 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2332 }
2333 }
2334 else
2335 {
cristybb503372010-05-27 20:51:26 +00002336 size_t
cristy3ed852e2009-09-05 21:47:34 +00002337 scale;
2338
glennrpfaa852b2010-03-30 12:17:00 +00002339 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
cristy3ed852e2009-09-05 21:47:34 +00002340 if (scale < 1)
2341 scale=1;
cristybb503372010-05-27 20:51:26 +00002342 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002343 {
2344 image->colormap[i].red=(Quantum) (i*scale);
2345 image->colormap[i].green=(Quantum) (i*scale);
2346 image->colormap[i].blue=(Quantum) (i*scale);
2347 }
2348 }
2349 }
2350 /*
2351 Read image scanlines.
2352 */
2353 if (image->delay != 0)
2354 mng_info->scenes_found++;
glennrp347e40f2010-06-06 11:27:30 +00002355 if ((image->ping != MagickFalse) || (
2356 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2357 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002358 {
2359 if (logging != MagickFalse)
2360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002361 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002362 mng_info->scenes_found-1);
2363 png_destroy_read_struct(&ping,&ping_info,&end_info);
2364#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00002365 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002366#endif
2367 if (logging != MagickFalse)
2368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2369 " exit ReadOnePNGImage().");
2370 return(image);
2371 }
2372 if (logging != MagickFalse)
2373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2374 " Reading PNG IDAT chunk(s)");
2375 if (num_passes > 1)
2376 png_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
glennrpfaa852b2010-03-30 12:17:00 +00002377 ping_rowbytes*sizeof(*png_pixels));
cristy3ed852e2009-09-05 21:47:34 +00002378 else
glennrpfaa852b2010-03-30 12:17:00 +00002379 png_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002380 sizeof(*png_pixels));
2381 if (png_pixels == (unsigned char *) NULL)
2382 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2383 if (logging != MagickFalse)
2384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2385 " Converting PNG pixels to pixel packets");
2386 /*
2387 Convert PNG pixels to pixel packets.
2388 */
glennrpfaa852b2010-03-30 12:17:00 +00002389 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002390 {
2391 /*
2392 PNG image is corrupt.
2393 */
2394 png_destroy_read_struct(&ping,&ping_info,&end_info);
2395#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00002396 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002397#endif
2398 if (quantum_info != (QuantumInfo *) NULL)
2399 quantum_info = DestroyQuantumInfo(quantum_info);
2400 if (png_pixels != (unsigned char *) NULL)
2401 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2402 if (logging != MagickFalse)
2403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2404 " exit ReadOnePNGImage() with error.");
2405 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002406 {
2407 InheritException(exception,&image->exception);
2408 image->columns=0;
2409 }
cristy3ed852e2009-09-05 21:47:34 +00002410 return(GetFirstImageInList(image));
2411 }
cristyed552522009-10-16 14:04:35 +00002412 quantum_info=AcquireQuantumInfo(image_info,image);
2413 if (quantum_info == (QuantumInfo *) NULL)
2414 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00002415 if (image->storage_class == DirectClass)
2416 for (pass=0; pass < num_passes; pass++)
2417 {
2418 /*
2419 Convert image to DirectClass pixel packets.
2420 */
2421#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2422 int
2423 depth;
2424
cristybb503372010-05-27 20:51:26 +00002425 depth=(ssize_t) ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002426#endif
glennrpfaa852b2010-03-30 12:17:00 +00002427 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2428 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2429 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2430 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00002431
cristybb503372010-05-27 20:51:26 +00002432 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002433 {
2434 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002435 row_offset=ping_rowbytes*y;
cristy3ed852e2009-09-05 21:47:34 +00002436 else
2437 row_offset=0;
2438 png_read_row(ping,png_pixels+row_offset,NULL);
cristyabc8f402009-09-19 01:45:58 +00002439 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00002440 if (q == (PixelPacket *) NULL)
2441 break;
2442#if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2443 if (depth == 16)
2444 {
2445 register Quantum
2446 *p,
2447 *r;
2448
2449 r=png_pixels+row_offset;
2450 p=r;
glennrpfaa852b2010-03-30 12:17:00 +00002451 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002452 {
cristybb503372010-05-27 20:51:26 +00002453 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002454 {
2455 *r++=*p++;
2456 p++;
glennrpfaa852b2010-03-30 12:17:00 +00002457 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS)) &&
cristy3ed852e2009-09-05 21:47:34 +00002458 (((*(p-2) << 8)|*(p-1)) == transparent_color.opacity))
2459 {
2460 /* Cheap transparency */
2461 *r++=TransparentOpacity;
2462 }
2463 else
2464 *r++=OpaqueOpacity;
2465 }
2466 }
glennrpfaa852b2010-03-30 12:17:00 +00002467 else if (ping_color_type == PNG_COLOR_TYPE_RGB)
cristy3ed852e2009-09-05 21:47:34 +00002468 {
glennrpfaa852b2010-03-30 12:17:00 +00002469 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristybb503372010-05-27 20:51:26 +00002470 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002471 {
2472 *r++=*p++;
2473 p++;
2474 *r++=*p++;
2475 p++;
2476 *r++=*p++;
2477 p++;
2478 if ((((*(p-6) << 8)|*(p-5)) == transparent_color.red) &&
2479 (((*(p-4) << 8)|*(p-3)) == transparent_color.green) &&
2480 (((*(p-2) << 8)|*(p-1)) == transparent_color.blue))
2481 {
2482 /* Cheap transparency */
2483 *r++=TransparentOpacity;
2484 }
2485 else
2486 *r++=OpaqueOpacity;
2487 }
2488 else
cristybb503372010-05-27 20:51:26 +00002489 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002490 {
2491 *r++=*p++;
2492 p++;
2493 *r++=*p++;
2494 p++;
2495 *r++=*p++;
2496 p++;
2497 *r++=OpaqueOpacity;
2498 }
2499 }
glennrpfaa852b2010-03-30 12:17:00 +00002500 else if (ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristybb503372010-05-27 20:51:26 +00002501 for (x=(ssize_t) (4*image->columns); x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002502 {
2503 *r++=*p++;
2504 p++;
2505 }
glennrpfaa852b2010-03-30 12:17:00 +00002506 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
cristybb503372010-05-27 20:51:26 +00002507 for (x=(ssize_t) (2*image->columns); x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002508 {
2509 *r++=*p++;
2510 p++;
2511 }
2512 }
glennrpfaa852b2010-03-30 12:17:00 +00002513 if (depth == 8 && ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002514 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2515 GrayQuantum,png_pixels+row_offset);
glennrpfaa852b2010-03-30 12:17:00 +00002516 if (ping_color_type == PNG_COLOR_TYPE_GRAY ||
2517 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00002518 {
2519 quantum_info->depth=8;
2520 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2521 GrayAlphaQuantum,png_pixels+row_offset);
2522 }
glennrpfaa852b2010-03-30 12:17:00 +00002523 else if (depth == 8 && ping_color_type == PNG_COLOR_TYPE_RGB)
cristy3ed852e2009-09-05 21:47:34 +00002524 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2525 RGBQuantum,png_pixels+row_offset);
glennrpfaa852b2010-03-30 12:17:00 +00002526 else if (ping_color_type == PNG_COLOR_TYPE_RGB ||
2527 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00002528 {
2529 quantum_info->depth=8;
2530 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2531 RGBAQuantum,png_pixels+row_offset);
2532 }
glennrpfaa852b2010-03-30 12:17:00 +00002533 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002534 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2535 IndexQuantum,png_pixels+row_offset);
2536#else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
glennrpfaa852b2010-03-30 12:17:00 +00002537 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002538 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2539 GrayQuantum,png_pixels+row_offset,exception);
glennrpfaa852b2010-03-30 12:17:00 +00002540 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00002541 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2542 GrayAlphaQuantum,png_pixels+row_offset,exception);
glennrpfaa852b2010-03-30 12:17:00 +00002543 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00002544 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2545 RGBAQuantum,png_pixels+row_offset,exception);
glennrpfaa852b2010-03-30 12:17:00 +00002546 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002547 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2548 IndexQuantum,png_pixels+row_offset,exception);
2549 else
2550 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2551 RGBQuantum,png_pixels+row_offset,exception);
2552#endif
cristy7a287bf2010-02-14 02:18:09 +00002553 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2554 {
cristycee97112010-05-28 00:44:52 +00002555 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2556 image->rows);
cristy7a287bf2010-02-14 02:18:09 +00002557 if (status == MagickFalse)
2558 break;
2559 }
cristy3ed852e2009-09-05 21:47:34 +00002560 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2561 break;
2562 }
cristy7a287bf2010-02-14 02:18:09 +00002563 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002564 {
2565 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2566 if (status == MagickFalse)
2567 break;
2568 }
2569 }
2570 else /* image->storage_class != DirectClass */
2571 for (pass=0; pass < num_passes; pass++)
2572 {
2573 Quantum
2574 *quantum_scanline;
2575
2576 register Quantum
2577 *r;
2578
2579 /*
2580 Convert grayscale image to PseudoClass pixel packets.
2581 */
glennrpfaa852b2010-03-30 12:17:00 +00002582 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002583 MagickTrue : MagickFalse;
2584 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2585 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2586 if (quantum_scanline == (Quantum *) NULL)
2587 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00002588 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002589 {
2590 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002591 row_offset=ping_rowbytes*y;
cristy3ed852e2009-09-05 21:47:34 +00002592 else
2593 row_offset=0;
2594 png_read_row(ping,png_pixels+row_offset,NULL);
2595 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2596 if (q == (PixelPacket *) NULL)
2597 break;
cristy5c6f7892010-05-05 22:53:29 +00002598 indexes=GetAuthenticIndexQueue(image);
cristy3ed852e2009-09-05 21:47:34 +00002599 p=png_pixels+row_offset;
2600 r=quantum_scanline;
glennrpfaa852b2010-03-30 12:17:00 +00002601 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00002602 {
2603 case 1:
2604 {
cristybb503372010-05-27 20:51:26 +00002605 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002606 bit;
2607
cristybb503372010-05-27 20:51:26 +00002608 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00002609 {
2610 for (bit=7; bit >= 0; bit--)
2611 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2612 p++;
2613 }
2614 if ((image->columns % 8) != 0)
2615 {
cristybb503372010-05-27 20:51:26 +00002616 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
cristy3ed852e2009-09-05 21:47:34 +00002617 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2618 }
2619 break;
2620 }
2621 case 2:
2622 {
cristybb503372010-05-27 20:51:26 +00002623 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00002624 {
2625 *r++=(*p >> 6) & 0x03;
2626 *r++=(*p >> 4) & 0x03;
2627 *r++=(*p >> 2) & 0x03;
2628 *r++=(*p++) & 0x03;
2629 }
2630 if ((image->columns % 4) != 0)
2631 {
cristybb503372010-05-27 20:51:26 +00002632 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
cristy3ed852e2009-09-05 21:47:34 +00002633 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2634 }
2635 break;
2636 }
2637 case 4:
2638 {
cristybb503372010-05-27 20:51:26 +00002639 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00002640 {
2641 *r++=(*p >> 4) & 0x0f;
2642 *r++=(*p++) & 0x0f;
2643 }
2644 if ((image->columns % 2) != 0)
2645 *r++=(*p++ >> 4) & 0x0f;
2646 break;
2647 }
2648 case 8:
2649 {
glennrpfaa852b2010-03-30 12:17:00 +00002650 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00002651 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002652 {
2653 *r++=*p++;
2654 /* In image.h, OpaqueOpacity is 0
2655 * TransparentOpacity is QuantumRange
2656 * In a PNG datastream, Opaque is QuantumRange
2657 * and Transparent is 0.
2658 */
2659 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2660 q++;
2661 }
2662 else
cristybb503372010-05-27 20:51:26 +00002663 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002664 *r++=*p++;
2665 break;
2666 }
2667 case 16:
2668 {
cristybb503372010-05-27 20:51:26 +00002669 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002670 {
2671#if (MAGICKCORE_QUANTUM_DEPTH == 16)
cristybb503372010-05-27 20:51:26 +00002672 size_t
cristy3ed852e2009-09-05 21:47:34 +00002673 quantum;
2674
2675 if (image->colors > 256)
2676 *r=((*p++) << 8);
2677 else
2678 *r=0;
2679 quantum=(*r);
2680 quantum|=(*p++);
2681 *r=(Quantum) quantum;
2682 r++;
glennrpfaa852b2010-03-30 12:17:00 +00002683 if (ping_color_type == 4)
cristy3ed852e2009-09-05 21:47:34 +00002684 {
2685 quantum=((*p++) << 8);
2686 quantum|=(*p++);
2687 q->opacity=(Quantum) (QuantumRange-quantum);
2688 q++;
2689 }
2690#else
2691#if (MAGICKCORE_QUANTUM_DEPTH == 32)
cristybb503372010-05-27 20:51:26 +00002692 size_t
cristy3ed852e2009-09-05 21:47:34 +00002693 quantum;
2694
2695 if (image->colors > 256)
2696 *r=((*p++) << 8);
2697 else
2698 *r=0;
2699 quantum=(*r);
2700 quantum|=(*p++);
2701 *r=quantum;
2702 r++;
glennrpfaa852b2010-03-30 12:17:00 +00002703 if (ping_color_type == 4)
cristy3ed852e2009-09-05 21:47:34 +00002704 {
2705 q->opacity=(*p << 8) | *(p+1);
2706 q->opacity*=65537L;
cristy46f08202010-01-10 04:04:21 +00002707 q->opacity=(Quantum) GetAlphaPixelComponent(q);
cristy3ed852e2009-09-05 21:47:34 +00002708 p+=2;
2709 q++;
2710 }
2711#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2712 *r++=(*p++);
2713 p++; /* strip low byte */
glennrpfaa852b2010-03-30 12:17:00 +00002714 if (ping_color_type == 4)
cristy3ed852e2009-09-05 21:47:34 +00002715 {
2716 q->opacity=(Quantum) (QuantumRange-(*p++));
2717 p++;
2718 q++;
2719 }
2720#endif
2721#endif
2722 }
2723 break;
2724 }
2725 default:
2726 break;
2727 }
2728 /*
2729 Transfer image scanline.
2730 */
2731 r=quantum_scanline;
cristybb503372010-05-27 20:51:26 +00002732 for (x=0; x < (ssize_t) image->columns; x++)
cristy80ac8b92010-05-08 13:36:57 +00002733 indexes[x]=(IndexPacket) (*r++);
cristy3ed852e2009-09-05 21:47:34 +00002734 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2735 break;
cristy7a287bf2010-02-14 02:18:09 +00002736 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2737 {
cristycee97112010-05-28 00:44:52 +00002738 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2739 image->rows);
cristy7a287bf2010-02-14 02:18:09 +00002740 if (status == MagickFalse)
2741 break;
2742 }
cristy3ed852e2009-09-05 21:47:34 +00002743 }
cristy7a287bf2010-02-14 02:18:09 +00002744 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00002745 {
2746 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2747 if (status == MagickFalse)
2748 break;
2749 }
2750 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2751 }
cristyb32b90a2009-09-07 21:45:48 +00002752 if (quantum_info != (QuantumInfo *) NULL)
2753 quantum_info=DestroyQuantumInfo(quantum_info);
cristy5c6f7892010-05-05 22:53:29 +00002754 if (image->storage_class == PseudoClass)
2755 {
cristyaeb2cbc2010-05-07 13:28:58 +00002756 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00002757 matte;
2758
2759 matte=image->matte;
2760 image->matte=MagickFalse;
2761 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00002762 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00002763 }
cristy3ed852e2009-09-05 21:47:34 +00002764 png_read_end(ping,ping_info);
2765
2766 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00002767 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00002768 {
2769 png_destroy_read_struct(&ping,&ping_info,&end_info);
2770 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2771 image->colors=2;
2772 (void) SetImageBackgroundColor(image);
2773#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00002774 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002775#endif
2776 if (logging != MagickFalse)
2777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2778 " exit ReadOnePNGImage() early.");
2779 return(image);
2780 }
glennrpfaa852b2010-03-30 12:17:00 +00002781 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002782 {
2783 ClassType
2784 storage_class;
2785
2786 /*
2787 Image has a transparent background.
2788 */
2789 storage_class=image->storage_class;
2790 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00002791
cristye805a642010-04-21 00:48:31 +00002792#if 1 /* balfour fix */
glennrpc11cf6a2010-03-20 16:46:19 +00002793/* From imagemagick discourse server, 5 Feb 2010 */
2794
2795 if (storage_class == PseudoClass)
2796 {
glennrpfaa852b2010-03-30 12:17:00 +00002797 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00002798 {
glennrpfaa852b2010-03-30 12:17:00 +00002799 for (x=0; x < ping_num_trans; x++)
glennrpc11cf6a2010-03-20 16:46:19 +00002800 {
glennrpe9c26dc2010-05-30 01:56:35 +00002801 image->colormap[x].opacity =
2802 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
glennrpc11cf6a2010-03-20 16:46:19 +00002803 }
2804 }
glennrpfaa852b2010-03-30 12:17:00 +00002805 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrpc11cf6a2010-03-20 16:46:19 +00002806 {
glennrp5af765f2010-03-30 11:12:18 +00002807 for (x=0; x < (int) image->colors; x++)
glennrpc11cf6a2010-03-20 16:46:19 +00002808 {
2809 if (image->colormap[x].red == transparent_color.opacity)
2810 {
2811 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2812 }
2813 }
2814 }
cristyd0272592010-04-21 01:01:49 +00002815 (void) SyncImage(image);
glennrpc11cf6a2010-03-20 16:46:19 +00002816 }
2817 else
2818 {
2819
cristybb503372010-05-27 20:51:26 +00002820 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00002821 {
2822 image->storage_class=storage_class;
2823 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2824 if (q == (PixelPacket *) NULL)
2825 break;
cristy5c6f7892010-05-05 22:53:29 +00002826 indexes=GetAuthenticIndexQueue(image);
glennrpc11cf6a2010-03-20 16:46:19 +00002827
cristybb503372010-05-27 20:51:26 +00002828 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpc11cf6a2010-03-20 16:46:19 +00002829 {
2830 if (ScaleQuantumToChar(q->red) == transparent_color.red &&
2831 ScaleQuantumToChar(q->green) == transparent_color.green &&
2832 ScaleQuantumToChar(q->blue) == transparent_color.blue)
2833 q->opacity=(Quantum) TransparentOpacity;
2834 else
2835 SetOpacityPixelComponent(q,OpaqueOpacity);
2836 q++;
2837 }
2838 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2839 break;
2840 }
2841 }
2842
2843#else /* not balfour */
2844
2845
cristybb503372010-05-27 20:51:26 +00002846 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002847 {
2848 image->storage_class=storage_class;
2849 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2850 if (q == (PixelPacket *) NULL)
2851 break;
cristy5c6f7892010-05-05 22:53:29 +00002852 indexes=GetAuthenticIndexQueue(image);
cristy3ed852e2009-09-05 21:47:34 +00002853
2854 if (storage_class == PseudoClass)
2855 {
2856 IndexPacket
2857 indexpacket;
2858
glennrpfaa852b2010-03-30 12:17:00 +00002859 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00002860 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002861 {
cristy5c6f7892010-05-05 22:53:29 +00002862 indexpacket=indexes[x];
glennrpfaa852b2010-03-30 12:17:00 +00002863 if (indexpacket < ping_num_trans)
cristy3ed852e2009-09-05 21:47:34 +00002864 q->opacity=ScaleCharToQuantum((unsigned char)
cristybb503372010-05-27 20:51:26 +00002865 (255-ping_trans_alpha[(ssize_t) indexpacket]));
cristy3ed852e2009-09-05 21:47:34 +00002866 else
cristyce70c172010-01-07 17:15:30 +00002867 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00002868 q++;
2869 }
glennrpfaa852b2010-03-30 12:17:00 +00002870 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristybb503372010-05-27 20:51:26 +00002871 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00002872 {
cristy5c6f7892010-05-05 22:53:29 +00002873 indexpacket=indexes[x];
cristybb503372010-05-27 20:51:26 +00002874 q->red=image->colormap[(ssize_t) indexpacket].red;
cristy3ed852e2009-09-05 21:47:34 +00002875 q->green=q->red;
2876 q->blue=q->red;
2877 if (q->red == transparent_color.opacity)
2878 q->opacity=(Quantum) TransparentOpacity;
2879 else
cristyce70c172010-01-07 17:15:30 +00002880 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00002881 q++;
2882 }
2883 }
2884 else
cristybb503372010-05-27 20:51:26 +00002885 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00002886 {
2887 if (ScaleQuantumToChar(q->red) == transparent_color.red &&
2888 ScaleQuantumToChar(q->green) == transparent_color.green &&
2889 ScaleQuantumToChar(q->blue) == transparent_color.blue)
2890 q->opacity=(Quantum) TransparentOpacity;
2891 else
cristyce70c172010-01-07 17:15:30 +00002892 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +00002893 q++;
2894 }
2895 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2896 break;
2897 }
glennrpc11cf6a2010-03-20 16:46:19 +00002898#endif /* not balfour */
cristy3ed852e2009-09-05 21:47:34 +00002899 image->storage_class=DirectClass;
2900 }
2901#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2902 if (image->depth > 8)
2903 image->depth=8;
2904#endif
2905 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
cristybb503372010-05-27 20:51:26 +00002906 for (i=0; i < (ssize_t) num_text; i++)
cristy3ed852e2009-09-05 21:47:34 +00002907 {
2908 /* Check for a profile */
2909
2910 if (logging != MagickFalse)
2911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2912 " Reading PNG text chunk");
2913 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2914 (void) png_read_raw_profile(image,image_info,text,(int) i);
2915 else
2916 {
2917 char
2918 *value;
2919
2920 length=text[i].text_length;
2921 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2922 sizeof(*value));
2923 if (value == (char *) NULL)
2924 {
2925 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2926 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2927 image->filename);
2928 break;
2929 }
2930 *value='\0';
2931 (void) ConcatenateMagickString(value,text[i].text,length+2);
2932 (void) SetImageProperty(image,text[i].key,value);
2933 if (logging != MagickFalse)
2934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2935 " Keyword: %s",text[i].key);
2936 value=DestroyString(value);
2937 }
2938 }
2939#ifdef MNG_OBJECT_BUFFERS
2940 /*
2941 Store the object if necessary.
2942 */
2943 if (object_id && !mng_info->frozen[object_id])
2944 {
2945 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2946 {
2947 /*
2948 create a new object buffer.
2949 */
2950 mng_info->ob[object_id]=(MngBuffer *)
cristy90823212009-12-12 20:48:33 +00002951 AcquireAlignedMemory(1,sizeof(MngBuffer));
cristy3ed852e2009-09-05 21:47:34 +00002952 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2953 {
2954 mng_info->ob[object_id]->image=(Image *) NULL;
2955 mng_info->ob[object_id]->reference_count=1;
2956 }
2957 }
2958 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2959 mng_info->ob[object_id]->frozen)
2960 {
2961 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2962 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2963 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2964 image->filename);
2965 if (mng_info->ob[object_id]->frozen)
2966 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2967 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2968 "`%s'",image->filename);
2969 }
2970 else
2971 {
cristy3ed852e2009-09-05 21:47:34 +00002972
2973 if (mng_info->ob[object_id]->image != (Image *) NULL)
2974 mng_info->ob[object_id]->image=DestroyImage
2975 (mng_info->ob[object_id]->image);
2976 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2977 &image->exception);
2978 if (mng_info->ob[object_id]->image != (Image *) NULL)
2979 mng_info->ob[object_id]->image->file=(FILE *) NULL;
2980 else
2981 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2982 ResourceLimitError,"Cloning image for object buffer failed",
2983 "`%s'",image->filename);
glennrpfaa852b2010-03-30 12:17:00 +00002984 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00002985 png_error(ping,"PNG Image dimensions are too large.");
glennrpfaa852b2010-03-30 12:17:00 +00002986 mng_info->ob[object_id]->width=ping_width;
2987 mng_info->ob[object_id]->height=ping_height;
2988 mng_info->ob[object_id]->color_type=ping_color_type;
2989 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
2990 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
2991 mng_info->ob[object_id]->compression_method=
2992 ping_compression_method;
2993 mng_info->ob[object_id]->filter_method=ping_filter_method;
2994 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002995 {
2996 int
2997 number_colors;
2998
2999 png_colorp
3000 plte;
3001
3002 /*
3003 Copy the PLTE to the object buffer.
3004 */
3005 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3006 mng_info->ob[object_id]->plte_length=number_colors;
3007 for (i=0; i < number_colors; i++)
3008 {
3009 mng_info->ob[object_id]->plte[i]=plte[i];
3010 }
3011 }
3012 else
3013 mng_info->ob[object_id]->plte_length=0;
3014 }
3015 }
3016#endif
3017 /*
3018 Relinquish resources.
3019 */
3020 png_destroy_read_struct(&ping,&ping_info,&end_info);
3021
3022 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
3023#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00003024 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003025#endif
3026
3027 if (logging != MagickFalse)
3028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3029 " exit ReadOnePNGImage()");
3030 return(image);
3031
3032/* end of reading one PNG image */
3033}
3034
3035static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3036{
3037 Image
3038 *image,
3039 *previous;
3040
3041 MagickBooleanType
3042 status;
3043
3044 MngInfo
3045 *mng_info;
3046
3047 char
3048 magic_number[MaxTextExtent];
3049
3050 int
3051 have_mng_structure,
3052 logging;
3053
3054 ssize_t
3055 count;
3056
3057 /*
3058 Open image file.
3059 */
3060 assert(image_info != (const ImageInfo *) NULL);
3061 assert(image_info->signature == MagickSignature);
3062 if (image_info->debug != MagickFalse)
3063 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3064 image_info->filename);
3065 assert(exception != (ExceptionInfo *) NULL);
3066 assert(exception->signature == MagickSignature);
3067 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()");
3068 image=AcquireImage(image_info);
3069 mng_info=(MngInfo *) NULL;
3070 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3071 if (status == MagickFalse)
3072 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3073 /*
3074 Verify PNG signature.
3075 */
3076 count=ReadBlob(image,8,(unsigned char *) magic_number);
3077 if (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3078 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3079 /*
3080 Allocate a MngInfo structure.
3081 */
3082 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00003083 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00003084 if (mng_info == (MngInfo *) NULL)
3085 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3086 /*
3087 Initialize members of the MngInfo structure.
3088 */
3089 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3090 mng_info->image=image;
3091 have_mng_structure=MagickTrue;
3092
3093 previous=image;
3094 image=ReadOnePNGImage(mng_info,image_info,exception);
3095 MngInfoFreeStruct(mng_info,&have_mng_structure);
3096 if (image == (Image *) NULL)
3097 {
3098 if (previous != (Image *) NULL)
3099 {
3100 if (previous->signature != MagickSignature)
3101 ThrowReaderException(CorruptImageError,"CorruptImage");
3102 (void) CloseBlob(previous);
3103 (void) DestroyImageList(previous);
3104 }
3105 if (logging != MagickFalse)
3106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3107 "exit ReadPNGImage() with error");
3108 return((Image *) NULL);
3109 }
3110 (void) CloseBlob(image);
3111 if ((image->columns == 0) || (image->rows == 0))
3112 {
3113 if (logging != MagickFalse)
3114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3115 "exit ReadPNGImage() with error.");
3116 ThrowReaderException(CorruptImageError,"CorruptImage");
3117 }
3118 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3119 {
3120 (void) SetImageType(image,PaletteType);
3121 if (image->matte != MagickFalse)
3122 {
3123 /* To do: Reduce to binary transparency */
3124 }
3125 }
3126 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3127 {
3128 (void) SetImageType(image,TrueColorType);
3129 image->matte=MagickFalse;
3130 }
3131 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3132 (void) SetImageType(image,TrueColorMatteType);
3133 if (logging != MagickFalse)
3134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3135 return(image);
3136}
3137
3138
3139
3140#if defined(JNG_SUPPORTED)
3141/*
3142%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3143% %
3144% %
3145% %
3146% R e a d O n e J N G I m a g e %
3147% %
3148% %
3149% %
3150%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3151%
3152% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3153% (minus the 8-byte signature) and returns it. It allocates the memory
3154% necessary for the new Image structure and returns a pointer to the new
3155% image.
3156%
3157% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3158%
3159% The format of the ReadOneJNGImage method is:
3160%
3161% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3162% ExceptionInfo *exception)
3163%
3164% A description of each parameter follows:
3165%
3166% o mng_info: Specifies a pointer to a MngInfo structure.
3167%
3168% o image_info: the image info.
3169%
3170% o exception: return any errors or warnings in this structure.
3171%
3172*/
3173static Image *ReadOneJNGImage(MngInfo *mng_info,
3174 const ImageInfo *image_info, ExceptionInfo *exception)
3175{
3176 Image
3177 *alpha_image,
3178 *color_image,
3179 *image,
3180 *jng_image;
3181
3182 ImageInfo
3183 *alpha_image_info,
3184 *color_image_info;
3185
cristybb503372010-05-27 20:51:26 +00003186 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003187 y;
3188
3189 MagickBooleanType
3190 status;
3191
3192 png_uint_32
3193 jng_height,
3194 jng_width;
3195
3196 png_byte
3197 jng_color_type,
3198 jng_image_sample_depth,
3199 jng_image_compression_method,
3200 jng_image_interlace_method,
3201 jng_alpha_sample_depth,
3202 jng_alpha_compression_method,
3203 jng_alpha_filter_method,
3204 jng_alpha_interlace_method;
3205
3206 register const PixelPacket
3207 *s;
3208
cristybb503372010-05-27 20:51:26 +00003209 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003210 i,
3211 x;
3212
3213 register PixelPacket
3214 *q;
3215
3216 register unsigned char
3217 *p;
3218
3219 unsigned int
3220 logging,
3221 read_JSEP,
3222 reading_idat,
3223 skip_to_iend;
3224
cristybb503372010-05-27 20:51:26 +00003225 size_t
cristy3ed852e2009-09-05 21:47:34 +00003226 length;
3227
3228 jng_alpha_compression_method=0;
3229 jng_alpha_sample_depth=8;
3230 jng_color_type=0;
3231 jng_height=0;
3232 jng_width=0;
3233 alpha_image=(Image *) NULL;
3234 color_image=(Image *) NULL;
3235 alpha_image_info=(ImageInfo *) NULL;
3236 color_image_info=(ImageInfo *) NULL;
3237
3238 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3239 " enter ReadOneJNGImage()");
3240
3241 image=mng_info->image;
3242 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3243 {
3244 /*
3245 Allocate next image structure.
3246 */
3247 if (logging != MagickFalse)
3248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3249 " AcquireNextImage()");
3250 AcquireNextImage(image_info,image);
3251 if (GetNextImageInList(image) == (Image *) NULL)
3252 return((Image *) NULL);
3253 image=SyncNextImageInList(image);
3254 }
3255 mng_info->image=image;
3256
3257 /*
3258 Signature bytes have already been read.
3259 */
3260
3261 read_JSEP=MagickFalse;
3262 reading_idat=MagickFalse;
3263 skip_to_iend=MagickFalse;
3264 for (;;)
3265 {
3266 char
3267 type[MaxTextExtent];
3268
3269 unsigned char
3270 *chunk;
3271
3272 unsigned int
3273 count;
3274
3275 /*
3276 Read a new JNG chunk.
3277 */
3278 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3279 2*GetBlobSize(image));
3280 if (status == MagickFalse)
3281 break;
3282 type[0]='\0';
3283 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3284 length=ReadBlobMSBLong(image);
3285 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3286
3287 if (logging != MagickFalse)
3288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003289 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3290 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003291
3292 if (length > PNG_UINT_31_MAX || count == 0)
3293 ThrowReaderException(CorruptImageError,"CorruptImage");
3294 p=NULL;
3295 chunk=(unsigned char *) NULL;
3296 if (length)
3297 {
3298 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3299 if (chunk == (unsigned char *) NULL)
3300 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00003301 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003302 chunk[i]=(unsigned char) ReadBlobByte(image);
3303 p=chunk;
3304 }
3305 (void) ReadBlobMSBLong(image); /* read crc word */
3306
3307 if (skip_to_iend)
3308 {
3309 if (length)
3310 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3311 continue;
3312 }
3313
3314 if (memcmp(type,mng_JHDR,4) == 0)
3315 {
3316 if (length == 16)
3317 {
cristybb503372010-05-27 20:51:26 +00003318 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003319 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003320 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003321 (p[6] << 8) | p[7]);
3322 jng_color_type=p[8];
3323 jng_image_sample_depth=p[9];
3324 jng_image_compression_method=p[10];
3325 jng_image_interlace_method=p[11];
3326 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3327 NoInterlace;
3328 jng_alpha_sample_depth=p[12];
3329 jng_alpha_compression_method=p[13];
3330 jng_alpha_filter_method=p[14];
3331 jng_alpha_interlace_method=p[15];
3332 if (logging != MagickFalse)
3333 {
3334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003335 " jng_width: %16lu",(unsigned long) jng_width);
cristy3ed852e2009-09-05 21:47:34 +00003336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003337 " jng_width: %16lu",(unsigned long) jng_height);
cristy3ed852e2009-09-05 21:47:34 +00003338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3339 " jng_color_type: %16d",jng_color_type);
3340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3341 " jng_image_sample_depth: %3d",
3342 jng_image_sample_depth);
3343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3344 " jng_image_compression_method:%3d",
3345 jng_image_compression_method);
3346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3347 " jng_image_interlace_method: %3d",
3348 jng_image_interlace_method);
3349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3350 " jng_alpha_sample_depth: %3d",
3351 jng_alpha_sample_depth);
3352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3353 " jng_alpha_compression_method:%3d",
3354 jng_alpha_compression_method);
3355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3356 " jng_alpha_filter_method: %3d",
3357 jng_alpha_filter_method);
3358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3359 " jng_alpha_interlace_method: %3d",
3360 jng_alpha_interlace_method);
3361 }
3362 }
3363 if (length)
3364 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3365 continue;
3366 }
3367
3368
3369 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3370 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3371 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3372 {
3373 /*
3374 o create color_image
3375 o open color_blob, attached to color_image
3376 o if (color type has alpha)
3377 open alpha_blob, attached to alpha_image
3378 */
3379
cristy90823212009-12-12 20:48:33 +00003380 color_image_info=(ImageInfo *)AcquireAlignedMemory(1,sizeof(ImageInfo));
cristy3ed852e2009-09-05 21:47:34 +00003381 if (color_image_info == (ImageInfo *) NULL)
3382 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3383 GetImageInfo(color_image_info);
3384 color_image=AcquireImage(color_image_info);
3385 if (color_image == (Image *) NULL)
3386 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3387
3388 if (logging != MagickFalse)
3389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3390 " Creating color_blob.");
3391 (void) AcquireUniqueFilename(color_image->filename);
3392 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3393 exception);
3394 if (status == MagickFalse)
3395 return((Image *) NULL);
3396
3397 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3398 {
3399 alpha_image_info=(ImageInfo *)
cristy90823212009-12-12 20:48:33 +00003400 AcquireAlignedMemory(1,sizeof(ImageInfo));
cristy3ed852e2009-09-05 21:47:34 +00003401 if (alpha_image_info == (ImageInfo *) NULL)
3402 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3403 GetImageInfo(alpha_image_info);
3404 alpha_image=AcquireImage(alpha_image_info);
3405 if (alpha_image == (Image *) NULL)
3406 {
3407 alpha_image=DestroyImage(alpha_image);
3408 ThrowReaderException(ResourceLimitError,
3409 "MemoryAllocationFailed");
3410 }
3411 if (logging != MagickFalse)
3412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3413 " Creating alpha_blob.");
3414 (void) AcquireUniqueFilename(alpha_image->filename);
3415 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3416 exception);
3417 if (status == MagickFalse)
3418 return((Image *) NULL);
3419 if (jng_alpha_compression_method == 0)
3420 {
3421 unsigned char
3422 data[18];
3423
3424 if (logging != MagickFalse)
3425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3426 " Writing IHDR chunk to alpha_blob.");
3427 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3428 "\211PNG\r\n\032\n");
3429 (void) WriteBlobMSBULong(alpha_image,13L);
3430 PNGType(data,mng_IHDR);
3431 LogPNGChunk((int) logging,mng_IHDR,13L);
3432 PNGLong(data+4,jng_width);
3433 PNGLong(data+8,jng_height);
3434 data[12]=jng_alpha_sample_depth;
3435 data[13]=0; /* color_type gray */
3436 data[14]=0; /* compression method 0 */
3437 data[15]=0; /* filter_method 0 */
3438 data[16]=0; /* interlace_method 0 */
3439 (void) WriteBlob(alpha_image,17,data);
3440 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3441 }
3442 }
3443 reading_idat=MagickTrue;
3444 }
3445
3446 if (memcmp(type,mng_JDAT,4) == 0)
3447 {
3448 /*
3449 Copy chunk to color_image->blob
3450 */
3451
3452 if (logging != MagickFalse)
3453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3454 " Copying JDAT chunk data to color_blob.");
3455
3456 (void) WriteBlob(color_image,length,chunk);
3457 if (length)
3458 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3459 continue;
3460 }
3461
3462 if (memcmp(type,mng_IDAT,4) == 0)
3463 {
3464 png_byte
3465 data[5];
3466
3467 /*
3468 Copy IDAT header and chunk data to alpha_image->blob
3469 */
3470
3471 if (image_info->ping == MagickFalse)
3472 {
3473 if (logging != MagickFalse)
3474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3475 " Copying IDAT chunk data to alpha_blob.");
3476
cristybb503372010-05-27 20:51:26 +00003477 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00003478 PNGType(data,mng_IDAT);
3479 LogPNGChunk((int) logging,mng_IDAT,length);
3480 (void) WriteBlob(alpha_image,4,data);
3481 (void) WriteBlob(alpha_image,length,chunk);
3482 (void) WriteBlobMSBULong(alpha_image,
3483 crc32(crc32(0,data,4),chunk,(uInt) length));
3484 }
3485 if (length)
3486 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3487 continue;
3488 }
3489
3490 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3491 {
3492 /*
3493 Copy chunk data to alpha_image->blob
3494 */
3495
3496 if (image_info->ping == MagickFalse)
3497 {
3498 if (logging != MagickFalse)
3499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3500 " Copying JDAA chunk data to alpha_blob.");
3501
3502 (void) WriteBlob(alpha_image,length,chunk);
3503 }
3504 if (length)
3505 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3506 continue;
3507 }
3508
3509 if (memcmp(type,mng_JSEP,4) == 0)
3510 {
3511 read_JSEP=MagickTrue;
3512 if (length)
3513 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3514 continue;
3515 }
3516
3517 if (memcmp(type,mng_bKGD,4) == 0)
3518 {
3519 if (length == 2)
3520 {
3521 image->background_color.red=ScaleCharToQuantum(p[1]);
3522 image->background_color.green=image->background_color.red;
3523 image->background_color.blue=image->background_color.red;
3524 }
3525 if (length == 6)
3526 {
3527 image->background_color.red=ScaleCharToQuantum(p[1]);
3528 image->background_color.green=ScaleCharToQuantum(p[3]);
3529 image->background_color.blue=ScaleCharToQuantum(p[5]);
3530 }
3531 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3532 continue;
3533 }
3534
3535 if (memcmp(type,mng_gAMA,4) == 0)
3536 {
3537 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00003538 image->gamma=((float) mng_get_long(p))*0.00001;
cristy3ed852e2009-09-05 21:47:34 +00003539 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3540 continue;
3541 }
3542
3543 if (memcmp(type,mng_cHRM,4) == 0)
3544 {
3545 if (length == 32)
3546 {
cristy8182b072010-05-30 20:10:53 +00003547 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3548 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3549 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3550 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3551 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3552 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3553 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3554 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00003555 }
3556 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3557 continue;
3558 }
3559
3560 if (memcmp(type,mng_sRGB,4) == 0)
3561 {
3562 if (length == 1)
3563 {
3564 image->rendering_intent=(RenderingIntent) (p[0]+1);
3565 image->gamma=0.45455f;
3566 image->chromaticity.red_primary.x=0.6400f;
3567 image->chromaticity.red_primary.y=0.3300f;
3568 image->chromaticity.green_primary.x=0.3000f;
3569 image->chromaticity.green_primary.y=0.6000f;
3570 image->chromaticity.blue_primary.x=0.1500f;
3571 image->chromaticity.blue_primary.y=0.0600f;
3572 image->chromaticity.white_point.x=0.3127f;
3573 image->chromaticity.white_point.y=0.3290f;
3574 }
3575 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3576 continue;
3577 }
3578
3579 if (memcmp(type,mng_oFFs,4) == 0)
3580 {
3581 if (length > 8)
3582 {
cristy8182b072010-05-30 20:10:53 +00003583 image->page.x=mng_get_long(p);
3584 image->page.y=mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003585 if ((int) p[8] != 0)
3586 {
3587 image->page.x/=10000;
3588 image->page.y/=10000;
3589 }
3590 }
3591 if (length)
3592 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3593 continue;
3594 }
3595
3596 if (memcmp(type,mng_pHYs,4) == 0)
3597 {
3598 if (length > 8)
3599 {
cristy8182b072010-05-30 20:10:53 +00003600 image->x_resolution=(double) mng_get_long(p);
3601 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00003602 if ((int) p[8] == PNG_RESOLUTION_METER)
3603 {
3604 image->units=PixelsPerCentimeterResolution;
3605 image->x_resolution=image->x_resolution/100.0f;
3606 image->y_resolution=image->y_resolution/100.0f;
3607 }
3608 }
3609 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3610 continue;
3611 }
3612
3613#if 0
3614 if (memcmp(type,mng_iCCP,4) == 0)
3615 {
3616 /* To do. */
3617 if (length)
3618 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3619 continue;
3620 }
3621#endif
3622
3623 if (length)
3624 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3625
3626 if (memcmp(type,mng_IEND,4))
3627 continue;
3628 break;
3629 }
3630
3631
3632 /* IEND found */
3633
3634 /*
3635 Finish up reading image data:
3636
3637 o read main image from color_blob.
3638
3639 o close color_blob.
3640
3641 o if (color_type has alpha)
3642 if alpha_encoding is PNG
3643 read secondary image from alpha_blob via ReadPNG
3644 if alpha_encoding is JPEG
3645 read secondary image from alpha_blob via ReadJPEG
3646
3647 o close alpha_blob.
3648
3649 o copy intensity of secondary image into
3650 opacity samples of main image.
3651
3652 o destroy the secondary image.
3653 */
3654
3655 (void) CloseBlob(color_image);
3656 if (logging != MagickFalse)
3657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3658 " Reading jng_image from color_blob.");
3659 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3660 color_image->filename);
3661 color_image_info->ping=MagickFalse; /* To do: avoid this */
3662 jng_image=ReadImage(color_image_info,exception);
3663 if (jng_image == (Image *) NULL)
3664 return((Image *) NULL);
3665
3666 (void) RelinquishUniqueFileResource(color_image->filename);
3667 color_image=DestroyImage(color_image);
3668 color_image_info=DestroyImageInfo(color_image_info);
3669
3670 if (jng_image == (Image *) NULL)
3671 return((Image *) NULL);
3672
3673 if (logging != MagickFalse)
3674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3675 " Copying jng_image pixels to main image.");
3676 image->rows=jng_height;
3677 image->columns=jng_width;
3678 length=image->columns*sizeof(PixelPacket);
cristybb503372010-05-27 20:51:26 +00003679 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003680 {
3681 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3682 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3683 (void) CopyMagickMemory(q,s,length);
3684 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3685 break;
3686 }
3687 jng_image=DestroyImage(jng_image);
3688 if (image_info->ping == MagickFalse)
3689 {
3690 if (jng_color_type >= 12)
3691 {
3692 if (jng_alpha_compression_method == 0)
3693 {
3694 png_byte
3695 data[5];
3696 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3697 PNGType(data,mng_IEND);
3698 LogPNGChunk((int) logging,mng_IEND,0L);
3699 (void) WriteBlob(alpha_image,4,data);
3700 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3701 }
3702 (void) CloseBlob(alpha_image);
3703 if (logging != MagickFalse)
3704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3705 " Reading opacity from alpha_blob.");
3706
3707 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3708 "%s",alpha_image->filename);
3709
3710 jng_image=ReadImage(alpha_image_info,exception);
3711 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00003712 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003713 {
3714 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3715 &image->exception);
3716 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3717 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00003718 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003719 q->opacity=(Quantum) QuantumRange-s->red;
3720 else
cristybb503372010-05-27 20:51:26 +00003721 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00003722 {
3723 q->opacity=(Quantum) QuantumRange-s->red;
3724 if (q->opacity != OpaqueOpacity)
3725 image->matte=MagickTrue;
3726 }
3727 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3728 break;
3729 }
3730 (void) RelinquishUniqueFileResource(alpha_image->filename);
3731 alpha_image=DestroyImage(alpha_image);
3732 alpha_image_info=DestroyImageInfo(alpha_image_info);
3733 if (jng_image != (Image *) NULL)
3734 jng_image=DestroyImage(jng_image);
3735 }
3736 }
3737
3738 /*
3739 Read the JNG image.
3740 */
3741 if (mng_info->mng_type == 0)
3742 {
3743 mng_info->mng_width=jng_width;
3744 mng_info->mng_height=jng_height;
3745 }
3746 if (image->page.width == 0 && image->page.height == 0)
3747 {
3748 image->page.width=jng_width;
3749 image->page.height=jng_height;
3750 }
3751 if (image->page.x == 0 && image->page.y == 0)
3752 {
3753 image->page.x=mng_info->x_off[mng_info->object_id];
3754 image->page.y=mng_info->y_off[mng_info->object_id];
3755 }
3756 else
3757 {
3758 image->page.y=mng_info->y_off[mng_info->object_id];
3759 }
3760 mng_info->image_found++;
3761 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3762 2*GetBlobSize(image));
3763 if (logging != MagickFalse)
3764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3765 " exit ReadOneJNGImage()");
3766 return(image);
3767}
3768
3769/*
3770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3771% %
3772% %
3773% %
3774% R e a d J N G I m a g e %
3775% %
3776% %
3777% %
3778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3779%
3780% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3781% (including the 8-byte signature) and returns it. It allocates the memory
3782% necessary for the new Image structure and returns a pointer to the new
3783% image.
3784%
3785% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3786%
3787% The format of the ReadJNGImage method is:
3788%
3789% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3790% *exception)
3791%
3792% A description of each parameter follows:
3793%
3794% o image_info: the image info.
3795%
3796% o exception: return any errors or warnings in this structure.
3797%
3798*/
3799
3800static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3801{
3802 Image
3803 *image,
3804 *previous;
3805
3806 MagickBooleanType
3807 status;
3808
3809 MngInfo
3810 *mng_info;
3811
3812 char
3813 magic_number[MaxTextExtent];
3814
3815 int
3816 have_mng_structure,
3817 logging;
3818
3819 size_t
3820 count;
3821
3822 /*
3823 Open image file.
3824 */
3825 assert(image_info != (const ImageInfo *) NULL);
3826 assert(image_info->signature == MagickSignature);
3827 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3828 assert(exception != (ExceptionInfo *) NULL);
3829 assert(exception->signature == MagickSignature);
3830 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()");
3831 image=AcquireImage(image_info);
3832 mng_info=(MngInfo *) NULL;
3833 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3834 if (status == MagickFalse)
3835 return((Image *) NULL);
3836 if (LocaleCompare(image_info->magick,"JNG") != 0)
3837 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3838 /*
3839 Verify JNG signature.
3840 */
3841 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3842 if (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3843 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3844 /*
3845 Allocate a MngInfo structure.
3846 */
3847 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00003848 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(*mng_info));
cristy3ed852e2009-09-05 21:47:34 +00003849 if (mng_info == (MngInfo *) NULL)
3850 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3851 /*
3852 Initialize members of the MngInfo structure.
3853 */
3854 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3855 have_mng_structure=MagickTrue;
3856
3857 mng_info->image=image;
3858 previous=image;
3859 image=ReadOneJNGImage(mng_info,image_info,exception);
3860 MngInfoFreeStruct(mng_info,&have_mng_structure);
3861 if (image == (Image *) NULL)
3862 {
3863 if (IsImageObject(previous) != MagickFalse)
3864 {
3865 (void) CloseBlob(previous);
3866 (void) DestroyImageList(previous);
3867 }
3868 if (logging != MagickFalse)
3869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3870 "exit ReadJNGImage() with error");
3871 return((Image *) NULL);
3872 }
3873 (void) CloseBlob(image);
3874 if (image->columns == 0 || image->rows == 0)
3875 {
3876 if (logging != MagickFalse)
3877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3878 "exit ReadJNGImage() with error");
3879 ThrowReaderException(CorruptImageError,"CorruptImage");
3880 }
3881 if (logging != MagickFalse)
3882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3883 return(image);
3884}
3885#endif
3886
3887static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3888{
3889 char
3890 page_geometry[MaxTextExtent];
3891
3892 Image
3893 *image,
3894 *previous;
3895
3896 int
3897 have_mng_structure;
3898
3899 volatile int
3900 first_mng_object,
3901 logging,
3902 object_id,
3903 term_chunk_found,
3904 skip_to_iend;
3905
cristybb503372010-05-27 20:51:26 +00003906 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003907 image_count=0;
3908
3909 MagickBooleanType
3910 status;
3911
3912 MagickOffsetType
3913 offset;
3914
3915 MngInfo
3916 *mng_info;
3917
3918 MngBox
3919 default_fb,
3920 fb,
3921 previous_fb;
3922
3923#if defined(MNG_INSERT_LAYERS)
3924 PixelPacket
3925 mng_background_color;
3926#endif
3927
3928 register unsigned char
3929 *p;
3930
cristybb503372010-05-27 20:51:26 +00003931 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003932 i;
3933
3934 size_t
3935 count;
3936
cristybb503372010-05-27 20:51:26 +00003937 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003938 loop_level;
3939
3940 volatile short
3941 skipping_loop;
3942
3943#if defined(MNG_INSERT_LAYERS)
3944 unsigned int
3945 mandatory_back=0;
3946#endif
3947
3948 volatile unsigned int
3949#ifdef MNG_OBJECT_BUFFERS
3950 mng_background_object=0,
3951#endif
3952 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
3953
cristybb503372010-05-27 20:51:26 +00003954 size_t
cristy3ed852e2009-09-05 21:47:34 +00003955 default_frame_timeout,
3956 frame_timeout,
3957#if defined(MNG_INSERT_LAYERS)
3958 image_height,
3959 image_width,
3960#endif
3961 length;
3962
glennrp38ea0832010-06-02 18:50:28 +00003963 /* These delays are all measured in image ticks_per_second,
3964 * not in MNG ticks_per_second
3965 */
cristybb503372010-05-27 20:51:26 +00003966 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00003967 default_frame_delay,
3968 final_delay,
3969 final_image_delay,
3970 frame_delay,
3971#if defined(MNG_INSERT_LAYERS)
3972 insert_layers,
3973#endif
3974 mng_iterations=1,
3975 simplicity=0,
3976 subframe_height=0,
3977 subframe_width=0;
3978
3979 previous_fb.top=0;
3980 previous_fb.bottom=0;
3981 previous_fb.left=0;
3982 previous_fb.right=0;
3983 default_fb.top=0;
3984 default_fb.bottom=0;
3985 default_fb.left=0;
3986 default_fb.right=0;
3987
3988 /*
3989 Set image_info->type=OptimizeType (new in version 5.4.0) to get the
3990 following optimizations:
3991
3992 o 16-bit depth is reduced to 8 if all pixels contain samples whose
3993 high byte and low byte are identical.
3994 o Opaque matte channel is removed.
3995 o If matte channel is present but only one transparent color is
3996 present, RGB+tRNS is written instead of RGBA
3997 o Grayscale images are reduced to 1, 2, or 4 bit depth if
3998 this can be done without loss.
3999 o Palette is sorted to remove unused entries and to put a
4000 transparent color first, if PNG_SORT_PALETTE is also defined.
4001 */
4002
4003 /*
4004 Open image file.
4005 */
4006 assert(image_info != (const ImageInfo *) NULL);
4007 assert(image_info->signature == MagickSignature);
4008 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4009 assert(exception != (ExceptionInfo *) NULL);
4010 assert(exception->signature == MagickSignature);
4011 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()");
4012 image=AcquireImage(image_info);
4013 mng_info=(MngInfo *) NULL;
4014 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4015 if (status == MagickFalse)
4016 return((Image *) NULL);
4017 first_mng_object=MagickFalse;
4018 skipping_loop=(-1);
4019 have_mng_structure=MagickFalse;
4020 /*
4021 Allocate a MngInfo structure.
4022 */
cristy90823212009-12-12 20:48:33 +00004023 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00004024 if (mng_info == (MngInfo *) NULL)
4025 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4026 /*
4027 Initialize members of the MngInfo structure.
4028 */
4029 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4030 mng_info->image=image;
4031 have_mng_structure=MagickTrue;
4032#if (MAGICKCORE_QUANTUM_DEPTH == 16)
4033 mng_info->optimize=image_info->type == OptimizeType;
4034#endif
4035
4036 if (LocaleCompare(image_info->magick,"MNG") == 0)
4037 {
4038 char
4039 magic_number[MaxTextExtent];
4040
4041 /*
4042 Verify MNG signature.
4043 */
4044 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4045 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4046 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4047 /*
4048 Initialize some nonzero members of the MngInfo structure.
4049 */
4050 for (i=0; i < MNG_MAX_OBJECTS; i++)
4051 {
cristybb503372010-05-27 20:51:26 +00004052 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4053 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004054 }
4055 mng_info->exists[0]=MagickTrue;
4056 }
4057 first_mng_object=MagickTrue;
4058 mng_type=0;
4059#if defined(MNG_INSERT_LAYERS)
4060 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4061#endif
4062 default_frame_delay=0;
4063 default_frame_timeout=0;
4064 frame_delay=0;
4065 final_delay=1;
4066 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4067 object_id=0;
4068 skip_to_iend=MagickFalse;
4069 term_chunk_found=MagickFalse;
4070 mng_info->framing_mode=1;
4071#if defined(MNG_INSERT_LAYERS)
4072 mandatory_back=MagickFalse;
4073#endif
4074#if defined(MNG_INSERT_LAYERS)
4075 mng_background_color=image->background_color;
4076#endif
4077 default_fb=mng_info->frame;
4078 previous_fb=mng_info->frame;
4079 do
4080 {
4081 char
4082 type[MaxTextExtent];
4083
4084 if (LocaleCompare(image_info->magick,"MNG") == 0)
4085 {
4086 unsigned char
4087 *chunk;
4088
4089 /*
4090 Read a new chunk.
4091 */
4092 type[0]='\0';
4093 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4094 length=ReadBlobMSBLong(image);
4095 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4096
4097 if (logging != MagickFalse)
4098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004099 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4100 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004101
4102 if (length > PNG_UINT_31_MAX)
4103 status=MagickFalse;
4104 if (count == 0)
4105 ThrowReaderException(CorruptImageError,"CorruptImage");
4106 p=NULL;
4107 chunk=(unsigned char *) NULL;
4108 if (length)
4109 {
4110 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4111 if (chunk == (unsigned char *) NULL)
4112 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00004113 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004114 chunk[i]=(unsigned char) ReadBlobByte(image);
4115 p=chunk;
4116 }
4117 (void) ReadBlobMSBLong(image); /* read crc word */
4118
4119#if !defined(JNG_SUPPORTED)
4120 if (memcmp(type,mng_JHDR,4) == 0)
4121 {
4122 skip_to_iend=MagickTrue;
4123 if (mng_info->jhdr_warning == 0)
4124 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4125 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4126 mng_info->jhdr_warning++;
4127 }
4128#endif
4129 if (memcmp(type,mng_DHDR,4) == 0)
4130 {
4131 skip_to_iend=MagickTrue;
4132 if (mng_info->dhdr_warning == 0)
4133 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4134 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4135 mng_info->dhdr_warning++;
4136 }
4137 if (memcmp(type,mng_MEND,4) == 0)
4138 break;
4139 if (skip_to_iend)
4140 {
4141 if (memcmp(type,mng_IEND,4) == 0)
4142 skip_to_iend=MagickFalse;
4143 if (length)
4144 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4145 if (logging != MagickFalse)
4146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4147 " Skip to IEND.");
4148 continue;
4149 }
4150 if (memcmp(type,mng_MHDR,4) == 0)
4151 {
cristybb503372010-05-27 20:51:26 +00004152 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004153 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004154 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004155 (p[6] << 8) | p[7]);
4156 if (logging != MagickFalse)
4157 {
4158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004159 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004161 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004162 }
4163 p+=8;
cristy8182b072010-05-30 20:10:53 +00004164 mng_info->ticks_per_second=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004165 if (mng_info->ticks_per_second == 0)
4166 default_frame_delay=0;
4167 else
4168 default_frame_delay=1UL*image->ticks_per_second/
4169 mng_info->ticks_per_second;
4170 frame_delay=default_frame_delay;
4171 simplicity=0;
4172 if (length > 16)
4173 {
4174 p+=16;
cristy8182b072010-05-30 20:10:53 +00004175 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004176 }
4177 mng_type=1; /* Full MNG */
4178 if ((simplicity != 0) && ((simplicity | 11) == 11))
4179 mng_type=2; /* LC */
4180 if ((simplicity != 0) && ((simplicity | 9) == 9))
4181 mng_type=3; /* VLC */
4182#if defined(MNG_INSERT_LAYERS)
4183 if (mng_type != 3)
4184 insert_layers=MagickTrue;
4185#endif
4186 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4187 {
4188 /*
4189 Allocate next image structure.
4190 */
4191 AcquireNextImage(image_info,image);
4192 if (GetNextImageInList(image) == (Image *) NULL)
4193 return((Image *) NULL);
4194 image=SyncNextImageInList(image);
4195 mng_info->image=image;
4196 }
4197
4198 if ((mng_info->mng_width > 65535L) ||
4199 (mng_info->mng_height > 65535L))
4200 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
cristye8c25f92010-06-03 00:53:06 +00004201 (void) FormatMagickString(page_geometry,MaxTextExtent,
4202 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004203 mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004204 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004205 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004206 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004207 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004208 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4209 for (i=0; i < MNG_MAX_OBJECTS; i++)
4210 mng_info->object_clip[i]=mng_info->frame;
4211 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4212 continue;
4213 }
4214
4215 if (memcmp(type,mng_TERM,4) == 0)
4216 {
4217 int
4218 repeat=0;
4219
4220
4221 if (length)
4222 repeat=p[0];
4223 if (repeat == 3)
4224 {
cristy8182b072010-05-30 20:10:53 +00004225 final_delay=(png_uint_32) mng_get_long(&p[2]);
4226 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
cristy3ed852e2009-09-05 21:47:34 +00004227 if (mng_iterations == PNG_UINT_31_MAX)
4228 mng_iterations=0;
4229 image->iterations=mng_iterations;
4230 term_chunk_found=MagickTrue;
4231 }
4232 if (logging != MagickFalse)
4233 {
4234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4235 " repeat=%d",repeat);
4236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004237 " final_delay=%.20g",(double) final_delay);
cristy3ed852e2009-09-05 21:47:34 +00004238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004239 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004240 }
4241 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4242 continue;
4243 }
4244 if (memcmp(type,mng_DEFI,4) == 0)
4245 {
4246 if (mng_type == 3)
4247 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4248 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4249 image->filename);
4250 object_id=(p[0] << 8) | p[1];
4251 if (mng_type == 2 && object_id != 0)
4252 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4253 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4254 image->filename);
4255 if (object_id > MNG_MAX_OBJECTS)
4256 {
4257 /*
4258 Instead ofsuing a warning we should allocate a larger
4259 MngInfo structure and continue.
4260 */
4261 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4262 CoderError,"object id too large","`%s'",image->filename);
4263 object_id=MNG_MAX_OBJECTS;
4264 }
4265 if (mng_info->exists[object_id])
4266 if (mng_info->frozen[object_id])
4267 {
4268 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4269 (void) ThrowMagickException(&image->exception,
4270 GetMagickModule(),CoderError,
4271 "DEFI cannot redefine a frozen MNG object","`%s'",
4272 image->filename);
4273 continue;
4274 }
4275 mng_info->exists[object_id]=MagickTrue;
4276 if (length > 2)
4277 mng_info->invisible[object_id]=p[2];
4278 /*
4279 Extract object offset info.
4280 */
4281 if (length > 11)
4282 {
cristybb503372010-05-27 20:51:26 +00004283 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004284 (p[6] << 8) | p[7]);
cristybb503372010-05-27 20:51:26 +00004285 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) | (p[9] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004286 (p[10] << 8) | p[11]);
4287 if (logging != MagickFalse)
4288 {
4289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004290 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004291 mng_info->x_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004293 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004294 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004295 }
4296 }
4297 /*
4298 Extract object clipping info.
4299 */
4300 if (length > 27)
4301 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4302 &p[12]);
4303 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4304 continue;
4305 }
4306 if (memcmp(type,mng_bKGD,4) == 0)
4307 {
4308 mng_info->have_global_bkgd=MagickFalse;
4309 if (length > 5)
4310 {
4311 mng_info->mng_global_bkgd.red=
4312 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4313 mng_info->mng_global_bkgd.green=
4314 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4315 mng_info->mng_global_bkgd.blue=
4316 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4317 mng_info->have_global_bkgd=MagickTrue;
4318 }
4319 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4320 continue;
4321 }
4322 if (memcmp(type,mng_BACK,4) == 0)
4323 {
4324#if defined(MNG_INSERT_LAYERS)
4325 if (length > 6)
4326 mandatory_back=p[6];
4327 else
4328 mandatory_back=0;
4329 if (mandatory_back && length > 5)
4330 {
4331 mng_background_color.red=
4332 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4333 mng_background_color.green=
4334 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4335 mng_background_color.blue=
4336 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4337 mng_background_color.opacity=OpaqueOpacity;
4338 }
4339#ifdef MNG_OBJECT_BUFFERS
4340 if (length > 8)
4341 mng_background_object=(p[7] << 8) | p[8];
4342#endif
4343#endif
4344 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4345 continue;
4346 }
4347 if (memcmp(type,mng_PLTE,4) == 0)
4348 {
4349 /*
4350 Read global PLTE.
4351 */
4352 if (length && (length < 769))
4353 {
4354 if (mng_info->global_plte == (png_colorp) NULL)
4355 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4356 sizeof(*mng_info->global_plte));
cristybb503372010-05-27 20:51:26 +00004357 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00004358 {
4359 mng_info->global_plte[i].red=p[3*i];
4360 mng_info->global_plte[i].green=p[3*i+1];
4361 mng_info->global_plte[i].blue=p[3*i+2];
4362 }
cristy35ef8242010-06-03 16:24:13 +00004363 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00004364 }
4365#ifdef MNG_LOOSE
4366 for ( ; i < 256; i++)
4367 {
4368 mng_info->global_plte[i].red=i;
4369 mng_info->global_plte[i].green=i;
4370 mng_info->global_plte[i].blue=i;
4371 }
4372 if (length)
4373 mng_info->global_plte_length=256;
4374#endif
4375 else
4376 mng_info->global_plte_length=0;
4377 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4378 continue;
4379 }
4380 if (memcmp(type,mng_tRNS,4) == 0)
4381 {
4382 /* read global tRNS */
4383
4384 if (length < 257)
cristybb503372010-05-27 20:51:26 +00004385 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004386 mng_info->global_trns[i]=p[i];
4387
4388#ifdef MNG_LOOSE
4389 for ( ; i < 256; i++)
4390 mng_info->global_trns[i]=255;
4391#endif
cristy12560f32010-06-03 16:51:08 +00004392 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00004393 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4394 continue;
4395 }
4396 if (memcmp(type,mng_gAMA,4) == 0)
4397 {
4398 if (length == 4)
4399 {
cristybb503372010-05-27 20:51:26 +00004400 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004401 igamma;
4402
cristy8182b072010-05-30 20:10:53 +00004403 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004404 mng_info->global_gamma=((float) igamma)*0.00001;
4405 mng_info->have_global_gama=MagickTrue;
4406 }
4407 else
4408 mng_info->have_global_gama=MagickFalse;
4409 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4410 continue;
4411 }
4412
4413 if (memcmp(type,mng_cHRM,4) == 0)
4414 {
4415 /*
4416 Read global cHRM
4417 */
4418 if (length == 32)
4419 {
cristy8182b072010-05-30 20:10:53 +00004420 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4421 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4422 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00004423 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004424 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00004425 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004426 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00004427 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004428 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00004429 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00004430 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00004431 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00004432 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004433 mng_info->have_global_chrm=MagickTrue;
4434 }
4435 else
4436 mng_info->have_global_chrm=MagickFalse;
4437 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4438 continue;
4439 }
4440 if (memcmp(type,mng_sRGB,4) == 0)
4441 {
4442 /*
4443 Read global sRGB.
4444 */
4445 if (length)
4446 {
4447 mng_info->global_srgb_intent=(RenderingIntent) (p[0]+1);
4448 mng_info->have_global_srgb=MagickTrue;
4449 }
4450 else
4451 mng_info->have_global_srgb=MagickFalse;
4452 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4453 continue;
4454 }
4455 if (memcmp(type,mng_iCCP,4) == 0)
4456 {
4457 /* To do. */
4458
4459 /*
4460 Read global iCCP.
4461 */
4462 if (length)
4463 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4464 continue;
4465 }
4466 if (memcmp(type,mng_FRAM,4) == 0)
4467 {
4468 if (mng_type == 3)
4469 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4470 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4471 image->filename);
4472 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4473 image->delay=frame_delay;
4474 frame_delay=default_frame_delay;
4475 frame_timeout=default_frame_timeout;
4476 fb=default_fb;
4477 if (length)
4478 if (p[0])
4479 mng_info->framing_mode=p[0];
4480 if (logging != MagickFalse)
4481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4482 " Framing_mode=%d",mng_info->framing_mode);
4483 if (length > 6)
4484 {
4485 /*
4486 Note the delay and frame clipping boundaries.
4487 */
4488 p++; /* framing mode */
cristybb503372010-05-27 20:51:26 +00004489 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00004490 p++; /* frame name */
4491 p++; /* frame name terminator */
cristybb503372010-05-27 20:51:26 +00004492 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00004493 {
4494 int
4495 change_delay,
4496 change_timeout,
4497 change_clipping;
4498
4499 change_delay=(*p++);
4500 change_timeout=(*p++);
4501 change_clipping=(*p++);
4502 p++; /* change_sync */
4503 if (change_delay)
4504 {
cristy8182b072010-05-30 20:10:53 +00004505 frame_delay=1UL*image->ticks_per_second*
4506 mng_get_long(p);
4507 if (mng_info->ticks_per_second != 0)
4508 frame_delay/=mng_info->ticks_per_second;
glennrpbb010dd2010-06-01 13:07:15 +00004509 else
4510 frame_delay=PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004511 if (change_delay == 2)
4512 default_frame_delay=frame_delay;
4513 p+=4;
4514 if (logging != MagickFalse)
4515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004516 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00004517 }
4518 if (change_timeout)
4519 {
glennrpbb010dd2010-06-01 13:07:15 +00004520 frame_timeout=1UL*image->ticks_per_second*
4521 mng_get_long(p);
4522 if (mng_info->ticks_per_second != 0)
4523 frame_timeout/=mng_info->ticks_per_second;
4524 else
4525 frame_timeout=PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004526 if (change_delay == 2)
4527 default_frame_timeout=frame_timeout;
4528 p+=4;
4529 if (logging != MagickFalse)
4530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004531 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00004532 }
4533 if (change_clipping)
4534 {
4535 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4536 p+=17;
4537 previous_fb=fb;
4538 if (logging != MagickFalse)
4539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004540 " Frame_clipping: L=%.20g R=%.20g T=%.20g B=%.20g",
4541 (double) fb.left,(double) fb.right,(double) fb.top,
4542 (double) fb.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004543 if (change_clipping == 2)
4544 default_fb=fb;
4545 }
4546 }
4547 }
4548 mng_info->clip=fb;
4549 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
cristybb503372010-05-27 20:51:26 +00004550 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00004551 -mng_info->clip.left);
cristybb503372010-05-27 20:51:26 +00004552 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00004553 -mng_info->clip.top);
4554 /*
4555 Insert a background layer behind the frame if framing_mode is 4.
4556 */
4557#if defined(MNG_INSERT_LAYERS)
4558 if (logging != MagickFalse)
4559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004560 " subframe_width=%.20g, subframe_height=%.20g",(double)
4561 subframe_width,(double) subframe_height);
cristy3ed852e2009-09-05 21:47:34 +00004562 if (insert_layers && (mng_info->framing_mode == 4) &&
4563 (subframe_width) && (subframe_height))
4564 {
4565 /*
4566 Allocate next image structure.
4567 */
4568 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4569 {
4570 AcquireNextImage(image_info,image);
4571 if (GetNextImageInList(image) == (Image *) NULL)
4572 {
4573 image=DestroyImageList(image);
4574 MngInfoFreeStruct(mng_info,&have_mng_structure);
4575 return((Image *) NULL);
4576 }
4577 image=SyncNextImageInList(image);
4578 }
4579 mng_info->image=image;
4580 if (term_chunk_found)
4581 {
4582 image->start_loop=MagickTrue;
4583 image->iterations=mng_iterations;
4584 term_chunk_found=MagickFalse;
4585 }
4586 else
4587 image->start_loop=MagickFalse;
4588 image->columns=subframe_width;
4589 image->rows=subframe_height;
4590 image->page.width=subframe_width;
4591 image->page.height=subframe_height;
4592 image->page.x=mng_info->clip.left;
4593 image->page.y=mng_info->clip.top;
4594 image->background_color=mng_background_color;
4595 image->matte=MagickFalse;
4596 image->delay=0;
4597 (void) SetImageBackgroundColor(image);
4598 if (logging != MagickFalse)
4599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004600 " Inserted background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4601 (double) mng_info->clip.left,(double) mng_info->clip.right,
4602 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00004603 }
4604#endif
4605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4606 continue;
4607 }
4608 if (memcmp(type,mng_CLIP,4) == 0)
4609 {
4610 unsigned int
4611 first_object,
4612 last_object;
4613
4614 /*
4615 Read CLIP.
4616 */
4617 first_object=(p[0] << 8) | p[1];
4618 last_object=(p[2] << 8) | p[3];
4619 for (i=(int) first_object; i <= (int) last_object; i++)
4620 {
4621 if (mng_info->exists[i] && !mng_info->frozen[i])
4622 {
4623 MngBox
4624 box;
4625
4626 box=mng_info->object_clip[i];
4627 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4628 }
4629 }
4630 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4631 continue;
4632 }
4633 if (memcmp(type,mng_SAVE,4) == 0)
4634 {
4635 for (i=1; i < MNG_MAX_OBJECTS; i++)
4636 if (mng_info->exists[i])
4637 {
4638 mng_info->frozen[i]=MagickTrue;
4639#ifdef MNG_OBJECT_BUFFERS
4640 if (mng_info->ob[i] != (MngBuffer *) NULL)
4641 mng_info->ob[i]->frozen=MagickTrue;
4642#endif
4643 }
4644 if (length)
4645 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4646 continue;
4647 }
4648
4649 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4650 {
4651 /*
4652 Read DISC or SEEK.
4653 */
4654 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4655 {
4656 for (i=1; i < MNG_MAX_OBJECTS; i++)
4657 MngInfoDiscardObject(mng_info,i);
4658 }
4659 else
4660 {
cristybb503372010-05-27 20:51:26 +00004661 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004662 j;
4663
cristybb503372010-05-27 20:51:26 +00004664 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00004665 {
4666 i=p[j] << 8 | p[j+1];
4667 MngInfoDiscardObject(mng_info,i);
4668 }
4669 }
4670 if (length)
4671 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4672 continue;
4673 }
4674 if (memcmp(type,mng_MOVE,4) == 0)
4675 {
cristybb503372010-05-27 20:51:26 +00004676 size_t
cristy3ed852e2009-09-05 21:47:34 +00004677 first_object,
4678 last_object;
4679
4680 /*
4681 read MOVE
4682 */
4683 first_object=(p[0] << 8) | p[1];
4684 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00004685 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00004686 {
4687 if (mng_info->exists[i] && !mng_info->frozen[i])
4688 {
4689 MngPair
4690 new_pair;
4691
4692 MngPair
4693 old_pair;
4694
4695 old_pair.a=mng_info->x_off[i];
4696 old_pair.b=mng_info->y_off[i];
4697 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4698 mng_info->x_off[i]=new_pair.a;
4699 mng_info->y_off[i]=new_pair.b;
4700 }
4701 }
4702 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4703 continue;
4704 }
4705
4706 if (memcmp(type,mng_LOOP,4) == 0)
4707 {
cristybb503372010-05-27 20:51:26 +00004708 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00004709 loop_level=chunk[0];
4710 mng_info->loop_active[loop_level]=1; /* mark loop active */
4711 /*
4712 Record starting point.
4713 */
cristy8182b072010-05-30 20:10:53 +00004714 loop_iters=mng_get_long(&chunk[1]);
cristy3ed852e2009-09-05 21:47:34 +00004715 if (logging != MagickFalse)
4716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004717 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
4718 (double) loop_iters);
cristy3ed852e2009-09-05 21:47:34 +00004719 if (loop_iters == 0)
4720 skipping_loop=loop_level;
4721 else
4722 {
4723 mng_info->loop_jump[loop_level]=TellBlob(image);
4724 mng_info->loop_count[loop_level]=loop_iters;
4725 }
4726 mng_info->loop_iteration[loop_level]=0;
4727 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4728 continue;
4729 }
4730 if (memcmp(type,mng_ENDL,4) == 0)
4731 {
4732 loop_level=chunk[0];
4733 if (skipping_loop > 0)
4734 {
4735 if (skipping_loop == loop_level)
4736 {
4737 /*
4738 Found end of zero-iteration loop.
4739 */
4740 skipping_loop=(-1);
4741 mng_info->loop_active[loop_level]=0;
4742 }
4743 }
4744 else
4745 {
4746 if (mng_info->loop_active[loop_level] == 1)
4747 {
4748 mng_info->loop_count[loop_level]--;
4749 mng_info->loop_iteration[loop_level]++;
4750 if (logging != MagickFalse)
4751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004752 " ENDL: LOOP level %.20g has %.20g remaining iterations ",
4753 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00004754 mng_info->loop_count[loop_level]);
cristy3ed852e2009-09-05 21:47:34 +00004755 if (mng_info->loop_count[loop_level] != 0)
4756 {
4757 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4758 SEEK_SET);
4759 if (offset < 0)
4760 ThrowReaderException(CorruptImageError,
4761 "ImproperImageHeader");
4762 }
4763 else
4764 {
4765 short
4766 last_level;
4767
4768 /*
4769 Finished loop.
4770 */
4771 mng_info->loop_active[loop_level]=0;
4772 last_level=(-1);
4773 for (i=0; i < loop_level; i++)
4774 if (mng_info->loop_active[i] == 1)
4775 last_level=(short) i;
4776 loop_level=last_level;
4777 }
4778 }
4779 }
4780 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4781 continue;
4782 }
4783 if (memcmp(type,mng_CLON,4) == 0)
4784 {
4785 if (mng_info->clon_warning == 0)
4786 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4787 CoderError,"CLON is not implemented yet","`%s'",
4788 image->filename);
4789 mng_info->clon_warning++;
4790 }
4791 if (memcmp(type,mng_MAGN,4) == 0)
4792 {
4793 png_uint_16
4794 magn_first,
4795 magn_last,
4796 magn_mb,
4797 magn_ml,
4798 magn_mr,
4799 magn_mt,
4800 magn_mx,
4801 magn_my,
4802 magn_methx,
4803 magn_methy;
4804
4805 if (length > 1)
4806 magn_first=(p[0] << 8) | p[1];
4807 else
4808 magn_first=0;
4809 if (length > 3)
4810 magn_last=(p[2] << 8) | p[3];
4811 else
4812 magn_last=magn_first;
4813#ifndef MNG_OBJECT_BUFFERS
4814 if (magn_first || magn_last)
4815 if (mng_info->magn_warning == 0)
4816 {
4817 (void) ThrowMagickException(&image->exception,
4818 GetMagickModule(),CoderError,
4819 "MAGN is not implemented yet for nonzero objects",
4820 "`%s'",image->filename);
4821 mng_info->magn_warning++;
4822 }
4823#endif
4824 if (length > 4)
4825 magn_methx=p[4];
4826 else
4827 magn_methx=0;
4828
4829 if (length > 6)
4830 magn_mx=(p[5] << 8) | p[6];
4831 else
4832 magn_mx=1;
4833 if (magn_mx == 0)
4834 magn_mx=1;
4835
4836 if (length > 8)
4837 magn_my=(p[7] << 8) | p[8];
4838 else
4839 magn_my=magn_mx;
4840 if (magn_my == 0)
4841 magn_my=1;
4842
4843 if (length > 10)
4844 magn_ml=(p[9] << 8) | p[10];
4845 else
4846 magn_ml=magn_mx;
4847 if (magn_ml == 0)
4848 magn_ml=1;
4849
4850 if (length > 12)
4851 magn_mr=(p[11] << 8) | p[12];
4852 else
4853 magn_mr=magn_mx;
4854 if (magn_mr == 0)
4855 magn_mr=1;
4856
4857 if (length > 14)
4858 magn_mt=(p[13] << 8) | p[14];
4859 else
4860 magn_mt=magn_my;
4861 if (magn_mt == 0)
4862 magn_mt=1;
4863
4864 if (length > 16)
4865 magn_mb=(p[15] << 8) | p[16];
4866 else
4867 magn_mb=magn_my;
4868 if (magn_mb == 0)
4869 magn_mb=1;
4870
4871 if (length > 17)
4872 magn_methy=p[17];
4873 else
4874 magn_methy=magn_methx;
4875
4876 if (magn_methx > 5 || magn_methy > 5)
4877 if (mng_info->magn_warning == 0)
4878 {
4879 (void) ThrowMagickException(&image->exception,
4880 GetMagickModule(),CoderError,
4881 "Unknown MAGN method in MNG datastream","`%s'",
4882 image->filename);
4883 mng_info->magn_warning++;
4884 }
4885#ifdef MNG_OBJECT_BUFFERS
4886 /* Magnify existing objects in the range magn_first to magn_last */
4887#endif
4888 if (magn_first == 0 || magn_last == 0)
4889 {
4890 /* Save the magnification factors for object 0 */
4891 mng_info->magn_mb=magn_mb;
4892 mng_info->magn_ml=magn_ml;
4893 mng_info->magn_mr=magn_mr;
4894 mng_info->magn_mt=magn_mt;
4895 mng_info->magn_mx=magn_mx;
4896 mng_info->magn_my=magn_my;
4897 mng_info->magn_methx=magn_methx;
4898 mng_info->magn_methy=magn_methy;
4899 }
4900 }
4901 if (memcmp(type,mng_PAST,4) == 0)
4902 {
4903 if (mng_info->past_warning == 0)
4904 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4905 CoderError,"PAST is not implemented yet","`%s'",
4906 image->filename);
4907 mng_info->past_warning++;
4908 }
4909 if (memcmp(type,mng_SHOW,4) == 0)
4910 {
4911 if (mng_info->show_warning == 0)
4912 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4913 CoderError,"SHOW is not implemented yet","`%s'",
4914 image->filename);
4915 mng_info->show_warning++;
4916 }
4917 if (memcmp(type,mng_sBIT,4) == 0)
4918 {
4919 if (length < 4)
4920 mng_info->have_global_sbit=MagickFalse;
4921 else
4922 {
4923 mng_info->global_sbit.gray=p[0];
4924 mng_info->global_sbit.red=p[0];
4925 mng_info->global_sbit.green=p[1];
4926 mng_info->global_sbit.blue=p[2];
4927 mng_info->global_sbit.alpha=p[3];
4928 mng_info->have_global_sbit=MagickTrue;
4929 }
4930 }
4931 if (memcmp(type,mng_pHYs,4) == 0)
4932 {
4933 if (length > 8)
4934 {
4935 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00004936 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004937 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00004938 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004939 mng_info->global_phys_unit_type=p[8];
4940 mng_info->have_global_phys=MagickTrue;
4941 }
4942 else
4943 mng_info->have_global_phys=MagickFalse;
4944 }
4945 if (memcmp(type,mng_pHYg,4) == 0)
4946 {
4947 if (mng_info->phyg_warning == 0)
4948 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4949 CoderError,"pHYg is not implemented.","`%s'",image->filename);
4950 mng_info->phyg_warning++;
4951 }
4952 if (memcmp(type,mng_BASI,4) == 0)
4953 {
4954 skip_to_iend=MagickTrue;
4955 if (mng_info->basi_warning == 0)
4956 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4957 CoderError,"BASI is not implemented yet","`%s'",
4958 image->filename);
4959 mng_info->basi_warning++;
4960#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00004961 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004962 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004963 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004964 (p[6] << 8) | p[7]);
4965 basi_color_type=p[8];
4966 basi_compression_method=p[9];
4967 basi_filter_type=p[10];
4968 basi_interlace_method=p[11];
4969 if (length > 11)
4970 basi_red=(p[12] << 8) & p[13];
4971 else
4972 basi_red=0;
4973 if (length > 13)
4974 basi_green=(p[14] << 8) & p[15];
4975 else
4976 basi_green=0;
4977 if (length > 15)
4978 basi_blue=(p[16] << 8) & p[17];
4979 else
4980 basi_blue=0;
4981 if (length > 17)
4982 basi_alpha=(p[18] << 8) & p[19];
4983 else
4984 {
4985 if (basi_sample_depth == 16)
4986 basi_alpha=65535L;
4987 else
4988 basi_alpha=255;
4989 }
4990 if (length > 19)
4991 basi_viewable=p[20];
4992 else
4993 basi_viewable=0;
4994#endif
4995 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4996 continue;
4997 }
4998 if (memcmp(type,mng_IHDR,4)
4999#if defined(JNG_SUPPORTED)
5000 && memcmp(type,mng_JHDR,4)
5001#endif
5002 )
5003 {
5004 /* Not an IHDR or JHDR chunk */
5005 if (length)
5006 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5007 continue;
5008 }
5009/* Process IHDR */
5010 if (logging != MagickFalse)
5011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5012 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5013 mng_info->exists[object_id]=MagickTrue;
5014 mng_info->viewable[object_id]=MagickTrue;
5015 if (mng_info->invisible[object_id])
5016 {
5017 if (logging != MagickFalse)
5018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5019 " Skipping invisible object");
5020 skip_to_iend=MagickTrue;
5021 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5022 continue;
5023 }
5024#if defined(MNG_INSERT_LAYERS)
5025 if (length < 8)
5026 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy8182b072010-05-30 20:10:53 +00005027 image_width=(size_t) mng_get_long(p);
5028 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005029#endif
5030 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5031
5032 /*
5033 Insert a transparent background layer behind the entire animation
5034 if it is not full screen.
5035 */
5036#if defined(MNG_INSERT_LAYERS)
5037 if (insert_layers && mng_type && first_mng_object)
5038 {
5039 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5040 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005041 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005042 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005043 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005044 {
5045 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5046 {
5047 /*
5048 Allocate next image structure.
5049 */
5050 AcquireNextImage(image_info,image);
5051 if (GetNextImageInList(image) == (Image *) NULL)
5052 {
5053 image=DestroyImageList(image);
5054 MngInfoFreeStruct(mng_info,&have_mng_structure);
5055 return((Image *) NULL);
5056 }
5057 image=SyncNextImageInList(image);
5058 }
5059 mng_info->image=image;
5060 if (term_chunk_found)
5061 {
5062 image->start_loop=MagickTrue;
5063 image->iterations=mng_iterations;
5064 term_chunk_found=MagickFalse;
5065 }
5066 else
5067 image->start_loop=MagickFalse;
5068 /*
5069 Make a background rectangle.
5070 */
5071 image->delay=0;
5072 image->columns=mng_info->mng_width;
5073 image->rows=mng_info->mng_height;
5074 image->page.width=mng_info->mng_width;
5075 image->page.height=mng_info->mng_height;
5076 image->page.x=0;
5077 image->page.y=0;
5078 image->background_color=mng_background_color;
5079 (void) SetImageBackgroundColor(image);
5080 if (logging != MagickFalse)
5081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005082 " Inserted transparent background layer, W=%.20g, H=%.20g",
5083 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005084 }
5085 }
5086 /*
5087 Insert a background layer behind the upcoming image if
5088 framing_mode is 3, and we haven't already inserted one.
5089 */
5090 if (insert_layers && (mng_info->framing_mode == 3) &&
5091 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5092 (simplicity & 0x08)))
5093 {
5094 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5095 {
5096 /*
5097 Allocate next image structure.
5098 */
5099 AcquireNextImage(image_info,image);
5100 if (GetNextImageInList(image) == (Image *) NULL)
5101 {
5102 image=DestroyImageList(image);
5103 MngInfoFreeStruct(mng_info,&have_mng_structure);
5104 return((Image *) NULL);
5105 }
5106 image=SyncNextImageInList(image);
5107 }
5108 mng_info->image=image;
5109 if (term_chunk_found)
5110 {
5111 image->start_loop=MagickTrue;
5112 image->iterations=mng_iterations;
5113 term_chunk_found=MagickFalse;
5114 }
5115 else
5116 image->start_loop=MagickFalse;
5117 image->delay=0;
5118 image->columns=subframe_width;
5119 image->rows=subframe_height;
5120 image->page.width=subframe_width;
5121 image->page.height=subframe_height;
5122 image->page.x=mng_info->clip.left;
5123 image->page.y=mng_info->clip.top;
5124 image->background_color=mng_background_color;
5125 image->matte=MagickFalse;
5126 (void) SetImageBackgroundColor(image);
5127 if (logging != MagickFalse)
5128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005129 " Inserted background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5130 (double) mng_info->clip.left,(double) mng_info->clip.right,
5131 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005132 }
5133#endif /* MNG_INSERT_LAYERS */
5134 first_mng_object=MagickFalse;
5135 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5136 {
5137 /*
5138 Allocate next image structure.
5139 */
5140 AcquireNextImage(image_info,image);
5141 if (GetNextImageInList(image) == (Image *) NULL)
5142 {
5143 image=DestroyImageList(image);
5144 MngInfoFreeStruct(mng_info,&have_mng_structure);
5145 return((Image *) NULL);
5146 }
5147 image=SyncNextImageInList(image);
5148 }
5149 mng_info->image=image;
5150 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5151 GetBlobSize(image));
5152 if (status == MagickFalse)
5153 break;
5154 if (term_chunk_found)
5155 {
5156 image->start_loop=MagickTrue;
5157 term_chunk_found=MagickFalse;
5158 }
5159 else
5160 image->start_loop=MagickFalse;
5161 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5162 {
5163 image->delay=frame_delay;
5164 frame_delay=default_frame_delay;
5165 }
5166 else
5167 image->delay=0;
5168 image->page.width=mng_info->mng_width;
5169 image->page.height=mng_info->mng_height;
5170 image->page.x=mng_info->x_off[object_id];
5171 image->page.y=mng_info->y_off[object_id];
5172 image->iterations=mng_iterations;
5173 /*
5174 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5175 */
5176 if (logging != MagickFalse)
5177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5178 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5179 type[2],type[3]);
cristybb503372010-05-27 20:51:26 +00005180 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
cristy3ed852e2009-09-05 21:47:34 +00005181 if (offset < 0)
5182 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5183 }
5184
5185 previous=image;
5186 mng_info->image=image;
5187 mng_info->mng_type=mng_type;
5188 mng_info->object_id=object_id;
5189
5190 if (memcmp(type,mng_IHDR,4) == 0)
5191 image=ReadOnePNGImage(mng_info,image_info,exception);
5192#if defined(JNG_SUPPORTED)
5193 else
5194 image=ReadOneJNGImage(mng_info,image_info,exception);
5195#endif
5196
5197 if (image == (Image *) NULL)
5198 {
5199 if (IsImageObject(previous) != MagickFalse)
5200 {
5201 (void) DestroyImageList(previous);
5202 (void) CloseBlob(previous);
5203 }
5204 MngInfoFreeStruct(mng_info,&have_mng_structure);
5205 return((Image *) NULL);
5206 }
5207 if (image->columns == 0 || image->rows == 0)
5208 {
5209 (void) CloseBlob(image);
5210 image=DestroyImageList(image);
5211 MngInfoFreeStruct(mng_info,&have_mng_structure);
5212 return((Image *) NULL);
5213 }
5214 mng_info->image=image;
5215
5216 if (mng_type)
5217 {
5218 MngBox
5219 crop_box;
5220
5221 if (mng_info->magn_methx || mng_info->magn_methy)
5222 {
5223 png_uint_32
5224 magnified_height,
5225 magnified_width;
5226
5227 if (logging != MagickFalse)
5228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5229 " Processing MNG MAGN chunk");
5230
5231 if (mng_info->magn_methx == 1)
5232 {
5233 magnified_width=mng_info->magn_ml;
5234 if (image->columns > 1)
5235 magnified_width += mng_info->magn_mr;
5236 if (image->columns > 2)
5237 magnified_width += (image->columns-2)*(mng_info->magn_mx);
5238 }
5239 else
5240 {
5241 magnified_width=image->columns;
5242 if (image->columns > 1)
5243 magnified_width += mng_info->magn_ml-1;
5244 if (image->columns > 2)
5245 magnified_width += mng_info->magn_mr-1;
5246 if (image->columns > 3)
5247 magnified_width += (image->columns-3)*(mng_info->magn_mx-1);
5248 }
5249 if (mng_info->magn_methy == 1)
5250 {
5251 magnified_height=mng_info->magn_mt;
5252 if (image->rows > 1)
5253 magnified_height += mng_info->magn_mb;
5254 if (image->rows > 2)
5255 magnified_height += (image->rows-2)*(mng_info->magn_my);
5256 }
5257 else
5258 {
5259 magnified_height=image->rows;
5260 if (image->rows > 1)
5261 magnified_height += mng_info->magn_mt-1;
5262 if (image->rows > 2)
5263 magnified_height += mng_info->magn_mb-1;
5264 if (image->rows > 3)
5265 magnified_height += (image->rows-3)*(mng_info->magn_my-1);
5266 }
5267 if (magnified_height > image->rows ||
5268 magnified_width > image->columns)
5269 {
5270 Image
5271 *large_image;
5272
5273 int
5274 yy;
5275
cristybb503372010-05-27 20:51:26 +00005276 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005277 m,
5278 y;
5279
cristybb503372010-05-27 20:51:26 +00005280 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005281 x;
5282
5283 register PixelPacket
5284 *n,
5285 *q;
5286
5287 PixelPacket
5288 *next,
5289 *prev;
5290
5291 png_uint_16
5292 magn_methx,
5293 magn_methy;
5294
5295 /*
5296 Allocate next image structure.
5297 */
5298 if (logging != MagickFalse)
5299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5300 " Allocate magnified image");
5301 AcquireNextImage(image_info,image);
5302 if (GetNextImageInList(image) == (Image *) NULL)
5303 {
5304 image=DestroyImageList(image);
5305 MngInfoFreeStruct(mng_info,&have_mng_structure);
5306 return((Image *) NULL);
5307 }
5308
5309 large_image=SyncNextImageInList(image);
5310
5311 large_image->columns=magnified_width;
5312 large_image->rows=magnified_height;
5313
5314 magn_methx=mng_info->magn_methx;
5315 magn_methy=mng_info->magn_methy;
5316
5317#if (MAGICKCORE_QUANTUM_DEPTH == 32)
5318#define QM unsigned short
5319 if (magn_methx != 1 || magn_methy != 1)
5320 {
5321 /*
5322 Scale pixels to unsigned shorts to prevent
5323 overflow of intermediate values of interpolations
5324 */
cristybb503372010-05-27 20:51:26 +00005325 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005326 {
5327 q=GetAuthenticPixels(image,0,y,image->columns,1,
5328 exception);
cristybb503372010-05-27 20:51:26 +00005329 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005330 {
5331 q->red=ScaleQuantumToShort(q->red);
5332 q->green=ScaleQuantumToShort(q->green);
5333 q->blue=ScaleQuantumToShort(q->blue);
5334 q->opacity=ScaleQuantumToShort(q->opacity);
5335 q++;
5336 }
5337 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5338 break;
5339 }
5340 }
5341#else
5342#define QM Quantum
5343#endif
5344
5345 if (image->matte != MagickFalse)
5346 (void) SetImageBackgroundColor(large_image);
5347 else
5348 {
5349 large_image->background_color.opacity=OpaqueOpacity;
5350 (void) SetImageBackgroundColor(large_image);
5351 if (magn_methx == 4)
5352 magn_methx=2;
5353 if (magn_methx == 5)
5354 magn_methx=3;
5355 if (magn_methy == 4)
5356 magn_methy=2;
5357 if (magn_methy == 5)
5358 magn_methy=3;
5359 }
5360
5361 /* magnify the rows into the right side of the large image */
5362
5363 if (logging != MagickFalse)
5364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005365 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00005366 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00005367 yy=0;
5368 length=(size_t) image->columns;
5369 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5370 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5371 if ((prev == (PixelPacket *) NULL) ||
5372 (next == (PixelPacket *) NULL))
5373 {
5374 image=DestroyImageList(image);
5375 MngInfoFreeStruct(mng_info,&have_mng_structure);
5376 ThrowReaderException(ResourceLimitError,
5377 "MemoryAllocationFailed");
5378 }
5379 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5380 (void) CopyMagickMemory(next,n,length);
cristybb503372010-05-27 20:51:26 +00005381 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005382 {
5383 if (y == 0)
cristybb503372010-05-27 20:51:26 +00005384 m=(ssize_t) mng_info->magn_mt;
5385 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5386 m=(ssize_t) mng_info->magn_mb;
5387 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5388 m=(ssize_t) mng_info->magn_mb;
5389 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005390 m=1;
5391 else
cristybb503372010-05-27 20:51:26 +00005392 m=(ssize_t) mng_info->magn_my;
cristy3ed852e2009-09-05 21:47:34 +00005393 n=prev;
5394 prev=next;
5395 next=n;
cristybb503372010-05-27 20:51:26 +00005396 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00005397 {
5398 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5399 exception);
5400 (void) CopyMagickMemory(next,n,length);
5401 }
5402 for (i=0; i < m; i++, yy++)
5403 {
5404 register PixelPacket
5405 *pixels;
5406
cristybb503372010-05-27 20:51:26 +00005407 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00005408 pixels=prev;
5409 n=next;
5410 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5411 1,exception);
5412 q+=(large_image->columns-image->columns);
cristybb503372010-05-27 20:51:26 +00005413 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005414 {
cristy5c6f7892010-05-05 22:53:29 +00005415 /* TO DO: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00005416 /*
5417 if (image->storage_class == PseudoClass)
5418 {
5419 }
5420 */
5421
5422 if (magn_methy <= 1)
5423 {
5424 *q=(*pixels); /* replicate previous */
5425 }
5426 else if (magn_methy == 2 || magn_methy == 4)
5427 {
5428 if (i == 0)
5429 *q=(*pixels);
5430 else
5431 {
5432 /* Interpolate */
cristybb503372010-05-27 20:51:26 +00005433 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5434 -(*pixels).red)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005435 +(*pixels).red);
cristybb503372010-05-27 20:51:26 +00005436 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5437 -(*pixels).green)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005438 +(*pixels).green);
cristybb503372010-05-27 20:51:26 +00005439 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5440 -(*pixels).blue)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005441 +(*pixels).blue);
5442 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00005443 (*q).opacity=(QM) (((ssize_t)
cristy3ed852e2009-09-05 21:47:34 +00005444 (2*i*((*n).opacity
5445 -(*pixels).opacity)+m))
cristybb503372010-05-27 20:51:26 +00005446 /((ssize_t) (m*2))+(*pixels).opacity);
cristy3ed852e2009-09-05 21:47:34 +00005447 }
5448 if (magn_methy == 4)
5449 {
5450 /* Replicate nearest */
5451 if (i <= ((m+1) << 1))
5452 (*q).opacity=(*pixels).opacity+0;
5453 else
5454 (*q).opacity=(*n).opacity+0;
5455 }
5456 }
5457 else /* if (magn_methy == 3 || magn_methy == 5) */
5458 {
5459 /* Replicate nearest */
5460 if (i <= ((m+1) << 1))
5461 *q=(*pixels);
5462 else
5463 *q=(*n);
5464 if (magn_methy == 5)
5465 {
cristybb503372010-05-27 20:51:26 +00005466 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5467 -(*pixels).opacity)+m))/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005468 +(*pixels).opacity);
5469 }
5470 }
5471 n++;
5472 q++;
5473 pixels++;
5474 } /* x */
5475 if (SyncAuthenticPixels(large_image,exception) == 0)
5476 break;
5477 } /* i */
5478 } /* y */
5479 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5480 next=(PixelPacket *) RelinquishMagickMemory(next);
5481
5482 length=image->columns;
5483
5484 if (logging != MagickFalse)
5485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5486 " Delete original image");
5487
5488 DeleteImageFromList(&image);
5489
5490 image=large_image;
5491
5492 mng_info->image=image;
5493
5494 /* magnify the columns */
5495 if (logging != MagickFalse)
5496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005497 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00005498
cristybb503372010-05-27 20:51:26 +00005499 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005500 {
5501 register PixelPacket
5502 *pixels;
5503
5504 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5505 pixels=q+(image->columns-length);
5506 n=pixels+1;
cristybb503372010-05-27 20:51:26 +00005507 for (x=(ssize_t) (image->columns-length);
5508 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00005509 {
cristybb503372010-05-27 20:51:26 +00005510 if (x == (ssize_t) (image->columns-length))
5511 m=(ssize_t) mng_info->magn_ml;
5512 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5513 m=(ssize_t) mng_info->magn_mr;
5514 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5515 m=(ssize_t) mng_info->magn_mr;
5516 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00005517 m=1;
5518 else
cristybb503372010-05-27 20:51:26 +00005519 m=(ssize_t) mng_info->magn_mx;
cristy3ed852e2009-09-05 21:47:34 +00005520 for (i=0; i < m; i++)
5521 {
5522 if (magn_methx <= 1)
5523 {
5524 /* replicate previous */
5525 *q=(*pixels);
5526 }
5527 else if (magn_methx == 2 || magn_methx == 4)
5528 {
5529 if (i == 0)
5530 *q=(*pixels);
5531 else
5532 {
5533 /* Interpolate */
5534 (*q).red=(QM) ((2*i*((*n).red
5535 -(*pixels).red)+m)
cristybb503372010-05-27 20:51:26 +00005536 /((ssize_t) (m*2))+(*pixels).red);
cristy3ed852e2009-09-05 21:47:34 +00005537 (*q).green=(QM) ((2*i*((*n).green
5538 -(*pixels).green)
cristybb503372010-05-27 20:51:26 +00005539 +m)/((ssize_t) (m*2))+(*pixels).green);
cristy3ed852e2009-09-05 21:47:34 +00005540 (*q).blue=(QM) ((2*i*((*n).blue
5541 -(*pixels).blue)+m)
cristybb503372010-05-27 20:51:26 +00005542 /((ssize_t) (m*2))+(*pixels).blue);
cristy3ed852e2009-09-05 21:47:34 +00005543 if (image->matte != MagickFalse)
5544 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00005545 -(*pixels).opacity)+m)/((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005546 +(*pixels).opacity);
5547 }
5548 if (magn_methx == 4)
5549 {
5550 /* Replicate nearest */
5551 if (i <= ((m+1) << 1))
5552 (*q).opacity=(*pixels).opacity+0;
5553 else
5554 (*q).opacity=(*n).opacity+0;
5555 }
5556 }
5557 else /* if (magn_methx == 3 || magn_methx == 5) */
5558 {
5559 /* Replicate nearest */
5560 if (i <= ((m+1) << 1))
5561 *q=(*pixels);
5562 else
5563 *q=(*n);
5564 if (magn_methx == 5)
5565 {
5566 /* Interpolate */
5567 (*q).opacity=(QM) ((2*i*((*n).opacity
cristybb503372010-05-27 20:51:26 +00005568 -(*pixels).opacity)+m) /((ssize_t) (m*2))
cristy3ed852e2009-09-05 21:47:34 +00005569 +(*pixels).opacity);
5570 }
5571 }
5572 q++;
5573 }
5574 n++;
5575 p++;
5576 }
5577 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5578 break;
5579 }
5580#if (MAGICKCORE_QUANTUM_DEPTH == 32)
5581 if (magn_methx != 1 || magn_methy != 1)
5582 {
5583 /*
5584 Rescale pixels to Quantum
5585 */
cristybb503372010-05-27 20:51:26 +00005586 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005587 {
5588 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristybb503372010-05-27 20:51:26 +00005589 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005590 {
5591 q->red=ScaleShortToQuantum(q->red);
5592 q->green=ScaleShortToQuantum(q->green);
5593 q->blue=ScaleShortToQuantum(q->blue);
5594 q->opacity=ScaleShortToQuantum(q->opacity);
5595 q++;
5596 }
5597 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5598 break;
5599 }
5600 }
5601#endif
5602 if (logging != MagickFalse)
5603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5604 " Finished MAGN processing");
5605 }
5606 }
5607
5608 /*
5609 Crop_box is with respect to the upper left corner of the MNG.
5610 */
5611 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5612 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5613 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5614 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5615 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5616 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5617 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5618 if ((crop_box.left != (mng_info->image_box.left
5619 +mng_info->x_off[object_id])) ||
5620 (crop_box.right != (mng_info->image_box.right
5621 +mng_info->x_off[object_id])) ||
5622 (crop_box.top != (mng_info->image_box.top
5623 +mng_info->y_off[object_id])) ||
5624 (crop_box.bottom != (mng_info->image_box.bottom
5625 +mng_info->y_off[object_id])))
5626 {
5627 if (logging != MagickFalse)
5628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5629 " Crop the PNG image");
5630 if ((crop_box.left < crop_box.right) &&
5631 (crop_box.top < crop_box.bottom))
5632 {
5633 Image
5634 *im;
5635
5636 RectangleInfo
5637 crop_info;
5638
5639 /*
5640 Crop_info is with respect to the upper left corner of
5641 the image.
5642 */
5643 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5644 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00005645 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5646 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00005647 image->page.width=image->columns;
5648 image->page.height=image->rows;
5649 image->page.x=0;
5650 image->page.y=0;
5651 im=CropImage(image,&crop_info,exception);
5652 if (im != (Image *) NULL)
5653 {
5654 image->columns=im->columns;
5655 image->rows=im->rows;
5656 im=DestroyImage(im);
5657 image->page.width=image->columns;
5658 image->page.height=image->rows;
5659 image->page.x=crop_box.left;
5660 image->page.y=crop_box.top;
5661 }
5662 }
5663 else
5664 {
5665 /*
5666 No pixels in crop area. The MNG spec still requires
5667 a layer, though, so make a single transparent pixel in
5668 the top left corner.
5669 */
5670 image->columns=1;
5671 image->rows=1;
5672 image->colors=2;
5673 (void) SetImageBackgroundColor(image);
5674 image->page.width=1;
5675 image->page.height=1;
5676 image->page.x=0;
5677 image->page.y=0;
5678 }
5679 }
5680#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
5681 image=mng_info->image;
5682#endif
5683 }
5684
5685#if (MAGICKCORE_QUANTUM_DEPTH == 16) /* TO DO: treat Q:32 */
5686 /* Determine if bit depth can be reduced from 16 to 8.
5687 * Note that the method GetImageDepth doesn't check background
5688 * and doesn't handle PseudoClass specially. Also it uses
5689 * multiplication and division by 257 instead of shifting, so
5690 * might be slower.
5691 */
5692 if (mng_info->optimize && image->depth == 16)
5693 {
5694 int
5695 ok_to_reduce;
5696
5697 const PixelPacket
5698 *p;
5699
cristybb503372010-05-27 20:51:26 +00005700 ok_to_reduce=(((((size_t) image->background_color.red >> 8) &
cristy3ed852e2009-09-05 21:47:34 +00005701 0xff)
cristybb503372010-05-27 20:51:26 +00005702 == ((size_t) image->background_color.red & 0xff)) &&
5703 ((((size_t) image->background_color.green >> 8) & 0xff)
5704 == ((size_t) image->background_color.green & 0xff)) &&
5705 ((((size_t) image->background_color.blue >> 8) & 0xff)
5706 == ((size_t) image->background_color.blue & 0xff)));
cristy3ed852e2009-09-05 21:47:34 +00005707 if (ok_to_reduce && image->storage_class == PseudoClass)
5708 {
5709 int indx;
5710
cristybb503372010-05-27 20:51:26 +00005711 for (indx=0; indx < (ssize_t) image->colors; indx++)
cristy3ed852e2009-09-05 21:47:34 +00005712 {
cristybb503372010-05-27 20:51:26 +00005713 ok_to_reduce=(((((size_t) image->colormap[indx].red >>
cristy3ed852e2009-09-05 21:47:34 +00005714 8) & 0xff)
cristybb503372010-05-27 20:51:26 +00005715 == ((size_t) image->colormap[indx].red & 0xff)) &&
5716 ((((size_t) image->colormap[indx].green >> 8) & 0xff)
5717 == ((size_t) image->colormap[indx].green & 0xff)) &&
5718 ((((size_t) image->colormap[indx].blue >> 8) & 0xff)
5719 == ((size_t) image->colormap[indx].blue & 0xff)));
cristy3ed852e2009-09-05 21:47:34 +00005720 if (ok_to_reduce == MagickFalse)
5721 break;
5722 }
5723 }
5724 if ((ok_to_reduce != MagickFalse) &&
5725 (image->storage_class != PseudoClass))
5726 {
cristybb503372010-05-27 20:51:26 +00005727 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005728 y;
5729
cristybb503372010-05-27 20:51:26 +00005730 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005731 x;
5732
cristybb503372010-05-27 20:51:26 +00005733 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00005734 {
5735 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
5736 if (p == (const PixelPacket *) NULL)
5737 break;
cristybb503372010-05-27 20:51:26 +00005738 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00005739 {
5740 ok_to_reduce=((
cristybb503372010-05-27 20:51:26 +00005741 (((size_t) p->red >> 8) & 0xff) ==
5742 ((size_t) p->red & 0xff)) &&
5743 ((((size_t) p->green >> 8) & 0xff) ==
5744 ((size_t) p->green & 0xff)) &&
5745 ((((size_t) p->blue >> 8) & 0xff) ==
5746 ((size_t) p->blue & 0xff)) &&
cristy3ed852e2009-09-05 21:47:34 +00005747 (((!image->matte ||
cristybb503372010-05-27 20:51:26 +00005748 (((size_t) p->opacity >> 8) & 0xff) ==
5749 ((size_t) p->opacity & 0xff)))));
cristy3ed852e2009-09-05 21:47:34 +00005750 if (ok_to_reduce == 0)
5751 break;
5752 p++;
5753 }
5754 if (x != 0)
5755 break;
5756 }
5757 }
5758 if (ok_to_reduce)
5759 {
5760 image->depth=8;
5761 if (logging != MagickFalse)
5762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5763 " Reducing PNG bit depth to 8 without loss of info");
5764 }
5765 }
5766#endif
5767 GetImageException(image,exception);
5768 if (image_info->number_scenes != 0)
5769 {
5770 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00005771 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00005772 break;
5773 }
5774 if (logging != MagickFalse)
5775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5776 " Finished reading image datastream.");
5777 } while (LocaleCompare(image_info->magick,"MNG") == 0);
5778 (void) CloseBlob(image);
5779 if (logging != MagickFalse)
5780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5781 " Finished reading all image datastreams.");
5782#if defined(MNG_INSERT_LAYERS)
5783 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
5784 (mng_info->mng_height))
5785 {
5786 /*
5787 Insert a background layer if nothing else was found.
5788 */
5789 if (logging != MagickFalse)
5790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5791 " No images found. Inserting a background layer.");
5792 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5793 {
5794 /*
5795 Allocate next image structure.
5796 */
5797 AcquireNextImage(image_info,image);
5798 if (GetNextImageInList(image) == (Image *) NULL)
5799 {
5800 image=DestroyImageList(image);
5801 MngInfoFreeStruct(mng_info,&have_mng_structure);
5802 if (logging != MagickFalse)
5803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5804 " Allocation failed, returning NULL.");
5805 return((Image *) NULL);
5806 }
5807 image=SyncNextImageInList(image);
5808 }
5809 image->columns=mng_info->mng_width;
5810 image->rows=mng_info->mng_height;
5811 image->page.width=mng_info->mng_width;
5812 image->page.height=mng_info->mng_height;
5813 image->page.x=0;
5814 image->page.y=0;
5815 image->background_color=mng_background_color;
5816 image->matte=MagickFalse;
5817 if (image_info->ping == MagickFalse)
5818 (void) SetImageBackgroundColor(image);
5819 mng_info->image_found++;
5820 }
5821#endif
5822 image->iterations=mng_iterations;
5823 if (mng_iterations == 1)
5824 image->start_loop=MagickTrue;
5825 while (GetPreviousImageInList(image) != (Image *) NULL)
5826 {
5827 image_count++;
5828 if (image_count > 10*mng_info->image_found)
5829 {
5830 if (logging != MagickFalse)
5831 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
5832 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5833 CoderError,"Linked list is corrupted, beginning of list not found",
5834 "`%s'",image_info->filename);
5835 return((Image *) NULL);
5836 }
5837 image=GetPreviousImageInList(image);
5838 if (GetNextImageInList(image) == (Image *) NULL)
5839 {
5840 if (logging != MagickFalse)
5841 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
5842 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5843 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
5844 image_info->filename);
5845 }
5846 }
5847 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
5848 GetNextImageInList(image) ==
5849 (Image *) NULL)
5850 {
5851 if (logging != MagickFalse)
5852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5853 " First image null");
5854 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5855 CoderError,"image->next for first image is NULL but shouldn't be.",
5856 "`%s'",image_info->filename);
5857 }
5858 if (mng_info->image_found == 0)
5859 {
5860 if (logging != MagickFalse)
5861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5862 " No visible images found.");
5863 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5864 CoderError,"No visible images in file","`%s'",image_info->filename);
5865 if (image != (Image *) NULL)
5866 image=DestroyImageList(image);
5867 MngInfoFreeStruct(mng_info,&have_mng_structure);
5868 return((Image *) NULL);
5869 }
5870
5871 if (mng_info->ticks_per_second)
5872 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
5873 final_delay/mng_info->ticks_per_second;
5874 else
5875 image->start_loop=MagickTrue;
5876 /* Find final nonzero image delay */
5877 final_image_delay=0;
5878 while (GetNextImageInList(image) != (Image *) NULL)
5879 {
5880 if (image->delay)
5881 final_image_delay=image->delay;
5882 image=GetNextImageInList(image);
5883 }
5884 if (final_delay < final_image_delay)
5885 final_delay=final_image_delay;
5886 image->delay=final_delay;
5887 if (logging != MagickFalse)
5888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005889 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
5890 (double) final_delay);
cristy3ed852e2009-09-05 21:47:34 +00005891 if (logging != MagickFalse)
5892 {
5893 int
5894 scene;
5895
5896 scene=0;
5897 image=GetFirstImageInList(image);
5898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5899 " Before coalesce:");
5900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005901 " scene 0 delay=%.20g",(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00005902 while (GetNextImageInList(image) != (Image *) NULL)
5903 {
5904 image=GetNextImageInList(image);
5905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005906 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00005907 }
5908 }
5909
5910 image=GetFirstImageInList(image);
5911#ifdef MNG_COALESCE_LAYERS
5912 if (insert_layers)
5913 {
5914 Image
5915 *next_image,
5916 *next;
5917
cristybb503372010-05-27 20:51:26 +00005918 size_t
cristy3ed852e2009-09-05 21:47:34 +00005919 scene;
5920
5921 if (logging != MagickFalse)
5922 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
5923 scene=image->scene;
5924 next_image=CoalesceImages(image,&image->exception);
5925 if (next_image == (Image *) NULL)
5926 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5927 image=DestroyImageList(image);
5928 image=next_image;
5929 for (next=image; next != (Image *) NULL; next=next_image)
5930 {
5931 next->page.width=mng_info->mng_width;
5932 next->page.height=mng_info->mng_height;
5933 next->page.x=0;
5934 next->page.y=0;
5935 next->scene=scene++;
5936 next_image=GetNextImageInList(next);
5937 if (next_image == (Image *) NULL)
5938 break;
5939 if (next->delay == 0)
5940 {
5941 scene--;
5942 next_image->previous=GetPreviousImageInList(next);
5943 if (GetPreviousImageInList(next) == (Image *) NULL)
5944 image=next_image;
5945 else
5946 next->previous->next=next_image;
5947 next=DestroyImage(next);
5948 }
5949 }
5950 }
5951#endif
5952
5953 while (GetNextImageInList(image) != (Image *) NULL)
5954 image=GetNextImageInList(image);
5955 image->dispose=BackgroundDispose;
5956
5957 if (logging != MagickFalse)
5958 {
5959 int
5960 scene;
5961
5962 scene=0;
5963 image=GetFirstImageInList(image);
5964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5965 " After coalesce:");
5966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005967 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
5968 (double) image->dispose);
cristy3ed852e2009-09-05 21:47:34 +00005969 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00005970 {
5971 image=GetNextImageInList(image);
5972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005973 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
5974 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00005975 }
5976 }
cristy3ed852e2009-09-05 21:47:34 +00005977 image=GetFirstImageInList(image);
5978 MngInfoFreeStruct(mng_info,&have_mng_structure);
5979 have_mng_structure=MagickFalse;
5980 if (logging != MagickFalse)
5981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
5982 return(GetFirstImageInList(image));
5983}
glennrp25c1e2b2010-03-25 01:39:56 +00005984#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00005985static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5986{
5987 printf("Your PNG library is too old: You have libpng-%s\n",
5988 PNG_LIBPNG_VER_STRING);
5989 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
5990 "PNG library is too old","`%s'",image_info->filename);
5991 return(Image *) NULL;
5992}
5993static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5994{
5995 return(ReadPNGImage(image_info,exception));
5996}
glennrp25c1e2b2010-03-25 01:39:56 +00005997#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00005998#endif
5999
6000/*
6001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6002% %
6003% %
6004% %
6005% R e g i s t e r P N G I m a g e %
6006% %
6007% %
6008% %
6009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6010%
6011% RegisterPNGImage() adds properties for the PNG image format to
6012% the list of supported formats. The properties include the image format
6013% tag, a method to read and/or write the format, whether the format
6014% supports the saving of more than one frame to the same file or blob,
6015% whether the format supports native in-memory I/O, and a brief
6016% description of the format.
6017%
6018% The format of the RegisterPNGImage method is:
6019%
cristybb503372010-05-27 20:51:26 +00006020% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006021%
6022*/
cristybb503372010-05-27 20:51:26 +00006023ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006024{
6025 char
6026 version[MaxTextExtent];
6027
6028 MagickInfo
6029 *entry;
6030
6031 static const char
6032 *PNGNote=
6033 {
6034 "See http://www.libpng.org/ for details about the PNG format."
6035 },
6036 *JNGNote=
6037 {
6038 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6039 "format."
6040 },
6041 *MNGNote=
6042 {
6043 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6044 "format."
6045 };
6046
6047 *version='\0';
6048#if defined(PNG_LIBPNG_VER_STRING)
6049 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6050 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00006051 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6052 {
6053 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6054 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6055 MaxTextExtent);
6056 }
6057#endif
cristy3ed852e2009-09-05 21:47:34 +00006058 entry=SetMagickInfo("MNG");
6059 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6060#if defined(MAGICKCORE_PNG_DELEGATE)
6061 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6062 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6063#endif
6064 entry->magick=(IsImageFormatHandler *) IsMNG;
6065 entry->description=ConstantString("Multiple-image Network Graphics");
6066 if (*version != '\0')
6067 entry->version=ConstantString(version);
6068 entry->module=ConstantString("PNG");
6069 entry->note=ConstantString(MNGNote);
6070 (void) RegisterMagickInfo(entry);
6071
6072 entry=SetMagickInfo("PNG");
6073#if defined(MAGICKCORE_PNG_DELEGATE)
6074 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6075 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6076#endif
6077 entry->magick=(IsImageFormatHandler *) IsPNG;
6078 entry->adjoin=MagickFalse;
6079 entry->description=ConstantString("Portable Network Graphics");
6080 entry->module=ConstantString("PNG");
6081 if (*version != '\0')
6082 entry->version=ConstantString(version);
6083 entry->note=ConstantString(PNGNote);
6084 (void) RegisterMagickInfo(entry);
6085
6086 entry=SetMagickInfo("PNG8");
6087#if defined(MAGICKCORE_PNG_DELEGATE)
6088 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6089 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6090#endif
6091 entry->magick=(IsImageFormatHandler *) IsPNG;
6092 entry->adjoin=MagickFalse;
6093 entry->description=ConstantString(
6094 "8-bit indexed with optional binary transparency");
6095 entry->module=ConstantString("PNG");
6096 (void) RegisterMagickInfo(entry);
6097
6098 entry=SetMagickInfo("PNG24");
6099 *version='\0';
6100#if defined(ZLIB_VERSION)
6101 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6102 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6103 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6104 {
6105 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6106 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6107 }
6108#endif
6109 if (*version != '\0')
6110 entry->version=ConstantString(version);
6111#if defined(MAGICKCORE_PNG_DELEGATE)
6112 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6113 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6114#endif
6115 entry->magick=(IsImageFormatHandler *) IsPNG;
6116 entry->adjoin=MagickFalse;
6117 entry->description=ConstantString("opaque 24-bit RGB");
6118 entry->module=ConstantString("PNG");
6119 (void) RegisterMagickInfo(entry);
6120
6121 entry=SetMagickInfo("PNG32");
6122#if defined(MAGICKCORE_PNG_DELEGATE)
6123 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6124 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6125#endif
6126 entry->magick=(IsImageFormatHandler *) IsPNG;
6127 entry->adjoin=MagickFalse;
6128 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6129 entry->module=ConstantString("PNG");
6130 (void) RegisterMagickInfo(entry);
6131
6132 entry=SetMagickInfo("JNG");
6133#if defined(JNG_SUPPORTED)
6134#if defined(MAGICKCORE_PNG_DELEGATE)
6135 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6136 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6137#endif
6138#endif
6139 entry->magick=(IsImageFormatHandler *) IsJNG;
6140 entry->adjoin=MagickFalse;
6141 entry->description=ConstantString("JPEG Network Graphics");
6142 entry->module=ConstantString("PNG");
6143 entry->note=ConstantString(JNGNote);
6144 (void) RegisterMagickInfo(entry);
cristy18b17442009-10-25 18:36:48 +00006145#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6146 png_semaphore=AllocateSemaphoreInfo();
6147#endif
cristy3ed852e2009-09-05 21:47:34 +00006148 return(MagickImageCoderSignature);
6149}
6150
6151/*
6152%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6153% %
6154% %
6155% %
6156% U n r e g i s t e r P N G I m a g e %
6157% %
6158% %
6159% %
6160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6161%
6162% UnregisterPNGImage() removes format registrations made by the
6163% PNG module from the list of supported formats.
6164%
6165% The format of the UnregisterPNGImage method is:
6166%
6167% UnregisterPNGImage(void)
6168%
6169*/
6170ModuleExport void UnregisterPNGImage(void)
6171{
6172 (void) UnregisterMagickInfo("MNG");
6173 (void) UnregisterMagickInfo("PNG");
6174 (void) UnregisterMagickInfo("PNG8");
6175 (void) UnregisterMagickInfo("PNG24");
6176 (void) UnregisterMagickInfo("PNG32");
6177 (void) UnregisterMagickInfo("JNG");
6178#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristy514e9e72009-11-20 02:12:08 +00006179 if (png_semaphore != (SemaphoreInfo *) NULL)
6180 DestroySemaphoreInfo(&png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006181#endif
6182}
6183
6184#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00006185#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00006186/*
6187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6188% %
6189% %
6190% %
6191% W r i t e M N G I m a g e %
6192% %
6193% %
6194% %
6195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6196%
6197% WriteMNGImage() writes an image in the Portable Network Graphics
6198% Group's "Multiple-image Network Graphics" encoded image format.
6199%
6200% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6201%
6202% The format of the WriteMNGImage method is:
6203%
6204% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6205%
6206% A description of each parameter follows.
6207%
6208% o image_info: the image info.
6209%
6210% o image: The image.
6211%
6212%
6213% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6214% "To do" under ReadPNGImage):
6215%
6216% Fix problem with palette sorting (when PNG_SORT_PALETTE is enabled,
6217% some GIF animations don't convert properly)
6218%
6219% Preserve all unknown and not-yet-handled known chunks found in input
6220% PNG file and copy them into output PNG files according to the PNG
6221% copying rules.
6222%
6223% Write the iCCP chunk at MNG level when (icc profile length > 0)
6224%
6225% Improve selection of color type (use indexed-colour or indexed-colour
6226% with tRNS when 256 or fewer unique RGBA values are present).
6227%
6228% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6229% This will be complicated if we limit ourselves to generating MNG-LC
6230% files. For now we ignore disposal method 3 and simply overlay the next
6231% image on it.
6232%
6233% Check for identical PLTE's or PLTE/tRNS combinations and use a
6234% global MNG PLTE or PLTE/tRNS combination when appropriate.
6235% [mostly done 15 June 1999 but still need to take care of tRNS]
6236%
6237% Check for identical sRGB and replace with a global sRGB (and remove
6238% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6239% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6240% local gAMA/cHRM with local sRGB if appropriate).
6241%
6242% Check for identical sBIT chunks and write global ones.
6243%
6244% Provide option to skip writing the signature tEXt chunks.
6245%
6246% Use signatures to detect identical objects and reuse the first
6247% instance of such objects instead of writing duplicate objects.
6248%
6249% Use a smaller-than-32k value of compression window size when
6250% appropriate.
6251%
6252% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6253% ancillary text chunks and save profiles.
6254%
6255% Provide an option to force LC files (to ensure exact framing rate)
6256% instead of VLC.
6257%
6258% Provide an option to force VLC files instead of LC, even when offsets
6259% are present. This will involve expanding the embedded images with a
6260% transparent region at the top and/or left.
6261*/
6262
cristy3ed852e2009-09-05 21:47:34 +00006263static void
6264png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6265 png_info *ping_info, unsigned char *profile_type, unsigned char
6266 *profile_description, unsigned char *profile_data, png_uint_32 length)
6267{
cristy3ed852e2009-09-05 21:47:34 +00006268 png_textp
6269 text;
6270
cristybb503372010-05-27 20:51:26 +00006271 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006272 i;
6273
6274 unsigned char
6275 *sp;
6276
6277 png_charp
6278 dp;
6279
6280 png_uint_32
6281 allocated_length,
6282 description_length;
6283
6284 unsigned char
6285 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00006286
cristy3ed852e2009-09-05 21:47:34 +00006287 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6288 return;
6289
6290 if (image_info->verbose)
6291 {
cristye8c25f92010-06-03 00:53:06 +00006292 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6293 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00006294 }
cristy3ed852e2009-09-05 21:47:34 +00006295 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6296 description_length=(png_uint_32) strlen((const char *) profile_description);
6297 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6298 + description_length);
6299 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6300 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6301 text[0].key[0]='\0';
6302 (void) ConcatenateMagickString(text[0].key,
6303 "Raw profile type ",MaxTextExtent);
6304 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6305 sp=profile_data;
6306 dp=text[0].text;
6307 *dp++='\n';
6308 (void) CopyMagickString(dp,(const char *) profile_description,
6309 allocated_length);
6310 dp+=description_length;
6311 *dp++='\n';
6312 (void) FormatMagickString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00006313 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00006314 dp+=8;
cristybb503372010-05-27 20:51:26 +00006315 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00006316 {
6317 if (i%36 == 0)
6318 *dp++='\n';
6319 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6320 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6321 }
6322 *dp++='\n';
6323 *dp='\0';
6324 text[0].text_length=(png_size_t) (dp-text[0].text);
6325 text[0].compression=image_info->compression == NoCompression ||
6326 (image_info->compression == UndefinedCompression &&
6327 text[0].text_length < 128) ? -1 : 0;
6328 if (text[0].text_length <= allocated_length)
6329 png_set_text(ping,ping_info,text,1);
6330 png_free(ping,text[0].text);
6331 png_free(ping,text[0].key);
6332 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00006333}
6334
6335static MagickBooleanType png_write_chunk_from_profile(Image *image,
6336 const char *string, int logging)
6337{
6338 char
6339 *name;
6340
6341 const StringInfo
6342 *profile;
6343
6344 unsigned char
6345 *data;
6346
6347 png_uint_32 length;
6348
6349 ResetImageProfileIterator(image);
6350 for (name=GetNextImageProfile(image); name != (const char *) NULL; ){
6351 profile=GetImageProfile(image,name);
6352 if (profile != (const StringInfo *) NULL)
6353 {
6354 StringInfo
6355 *png_profile;
6356
6357 if (LocaleNCompare(name,string,11) == 0) {
6358 if (logging != MagickFalse)
6359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6360 " Found %s profile",name);
6361
6362 png_profile=CloneStringInfo(profile);
6363 data=GetStringInfoDatum(png_profile),
6364 length=(png_uint_32) GetStringInfoLength(png_profile);
6365 data[4]=data[3];
6366 data[3]=data[2];
6367 data[2]=data[1];
6368 data[1]=data[0];
6369 (void) WriteBlobMSBULong(image,length-5); /* data length */
6370 (void) WriteBlob(image,length-1,data+1);
6371 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6372 png_profile=DestroyStringInfo(png_profile);
6373 }
6374 }
6375 name=GetNextImageProfile(image);
6376 }
6377 return(MagickTrue);
6378}
6379
6380static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6381 const ImageInfo *image_info,Image *image)
6382{
6383/* Write one PNG image */
6384 char
6385 s[2];
6386
6387 const char
6388 *name,
6389 *property,
6390 *value;
6391
6392 const StringInfo
6393 *profile;
6394
6395
6396 int
6397 image_matte,
6398 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00006399 pass;
6400
glennrpe9c26dc2010-05-30 01:56:35 +00006401 png_byte
6402 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00006403
cristy3ed852e2009-09-05 21:47:34 +00006404 png_colorp
6405 palette;
6406
glennrp5af765f2010-03-30 11:12:18 +00006407 png_color_16
6408 ping_background,
6409 ping_trans_color;
6410
cristy3ed852e2009-09-05 21:47:34 +00006411 png_info
6412 *ping_info;
6413
6414 png_struct
6415 *ping;
6416
glennrp5af765f2010-03-30 11:12:18 +00006417 png_uint_32
6418 ping_height,
6419 ping_width;
6420
cristybb503372010-05-27 20:51:26 +00006421 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006422 y;
6423
6424 MagickBooleanType
6425 status;
6426
6427 QuantumInfo
6428 *quantum_info;
6429
6430 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00006431 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00006432
cristybb503372010-05-27 20:51:26 +00006433 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006434 i,
6435 x;
6436
6437 unsigned char
6438 *png_pixels;
6439
6440 unsigned int
6441 logging,
6442 matte;
6443
glennrp5af765f2010-03-30 11:12:18 +00006444 volatile int
6445 ping_bit_depth,
6446 ping_color_type,
6447 ping_interlace_method,
6448 ping_compression_method,
6449 ping_filter_method,
6450 ping_num_trans;
6451
cristybb503372010-05-27 20:51:26 +00006452 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00006453 image_colors,
glennrp5af765f2010-03-30 11:12:18 +00006454 image_depth,
6455 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006456
cristybb503372010-05-27 20:51:26 +00006457 size_t
cristy3ed852e2009-09-05 21:47:34 +00006458 quality,
6459 rowbytes,
6460 save_image_depth;
6461
6462 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6463 " enter WriteOnePNGImage()");
6464
6465#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00006466 LockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006467#endif
6468
glennrp5af765f2010-03-30 11:12:18 +00006469 /* Initialize some stuff */
6470 ping_bit_depth=0,
6471 ping_color_type=0,
6472 ping_interlace_method=0,
6473 ping_compression_method=0,
6474 ping_filter_method=0,
6475 ping_num_trans = 0;
6476
6477 ping_background.red = 0;
6478 ping_background.green = 0;
6479 ping_background.blue = 0;
6480 ping_background.gray = 0;
6481 ping_background.index = 0;
6482
6483 ping_trans_color.red=0;
6484 ping_trans_color.green=0;
6485 ping_trans_color.blue=0;
6486 ping_trans_color.gray=0;
6487
cristyed552522009-10-16 14:04:35 +00006488 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00006489 image_colors=image->colors;
6490 image_depth=image->depth;
6491 image_matte=image->matte;
6492
6493 if (image->colorspace != RGBColorspace)
6494 (void) TransformImageColorspace(image,RGBColorspace);
6495 mng_info->IsPalette=image->storage_class == PseudoClass &&
cristy057310d2010-01-06 15:53:33 +00006496 image_colors <= 256 && !IsOpaqueImage(image,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +00006497 mng_info->optimize=image_info->type == OptimizeType;
6498
6499 /*
6500 Allocate the PNG structures
6501 */
6502#ifdef PNG_USER_MEM_SUPPORTED
6503 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
6504 PNGErrorHandler,PNGWarningHandler,(void *) NULL,
6505 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
6506#else
6507 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
6508 PNGErrorHandler,PNGWarningHandler);
6509#endif
6510 if (ping == (png_struct *) NULL)
6511 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6512 ping_info=png_create_info_struct(ping);
6513 if (ping_info == (png_info *) NULL)
6514 {
6515 png_destroy_write_struct(&ping,(png_info **) NULL);
6516 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6517 }
6518 png_set_write_fn(ping,image,png_put_data,png_flush_data);
6519 png_pixels=(unsigned char *) NULL;
6520
glennrp5af765f2010-03-30 11:12:18 +00006521 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00006522 {
6523 /*
6524 PNG write failed.
6525 */
6526#ifdef PNG_DEBUG
6527 if (image_info->verbose)
6528 (void) printf("PNG write has failed.\n");
6529#endif
6530 png_destroy_write_struct(&ping,&ping_info);
6531#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00006532 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006533#endif
6534 return(MagickFalse);
6535 }
6536 /*
6537 Prepare PNG for writing.
6538 */
6539#if defined(PNG_MNG_FEATURES_SUPPORTED)
6540 if (mng_info->write_mng)
6541 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
6542#else
6543# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
6544 if (mng_info->write_mng)
6545 png_permit_empty_plte(ping,MagickTrue);
6546# endif
6547#endif
6548 x=0;
glennrp5af765f2010-03-30 11:12:18 +00006549 ping_width=image->columns;
6550 ping_height=image->rows;
cristy3ed852e2009-09-05 21:47:34 +00006551 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
6552 image_depth=8;
6553 if (mng_info->write_png_depth != 0)
6554 image_depth=mng_info->write_png_depth;
6555 /* Adjust requested depth to next higher valid depth if necessary */
6556 if (image_depth > 8)
6557 image_depth=16;
6558 if ((image_depth > 4) && (image_depth < 8))
6559 image_depth=8;
6560 if (image_depth == 3)
6561 image_depth=4;
6562 if (logging != MagickFalse)
6563 {
6564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006565 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00006566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006567 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00006568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006569 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00006570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006571 " image_depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00006572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006573 " requested PNG image_depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00006574 }
6575 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00006576 ping_bit_depth=(png_byte) save_image_depth;
cristy3ed852e2009-09-05 21:47:34 +00006577#if defined(PNG_pHYs_SUPPORTED)
6578 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
6579 (!mng_info->write_mng || !mng_info->equal_physs))
6580 {
6581 int
6582 unit_type;
6583
6584 png_uint_32
6585 x_resolution,
6586 y_resolution;
6587
6588 if (image->units == PixelsPerInchResolution)
6589 {
6590 unit_type=PNG_RESOLUTION_METER;
6591 x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
6592 y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
6593 }
6594 else if (image->units == PixelsPerCentimeterResolution)
6595 {
6596 unit_type=PNG_RESOLUTION_METER;
6597 x_resolution=(png_uint_32) (100.0*image->x_resolution);
6598 y_resolution=(png_uint_32) (100.0*image->y_resolution);
6599 }
6600 else
6601 {
6602 unit_type=PNG_RESOLUTION_UNKNOWN;
6603 x_resolution=(png_uint_32) image->x_resolution;
6604 y_resolution=(png_uint_32) image->y_resolution;
6605 }
6606 png_set_pHYs(ping,ping_info,x_resolution,y_resolution,unit_type);
6607 if (logging != MagickFalse)
6608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6609 " Setting up pHYs chunk");
6610 }
6611#endif
6612#if defined(PNG_oFFs_SUPPORTED)
6613 if (image->page.x || image->page.y)
6614 {
6615 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
6616 (png_int_32) image->page.y, 0);
6617 if (logging != MagickFalse)
6618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6619 " Setting up oFFs chunk");
6620 }
6621#endif
6622 if (image_matte && (!mng_info->adjoin || !mng_info->equal_backgrounds))
6623 {
6624 png_color_16
6625 background;
6626
6627 if (image_depth < MAGICKCORE_QUANTUM_DEPTH)
6628 {
cristybb503372010-05-27 20:51:26 +00006629 size_t
cristy3ed852e2009-09-05 21:47:34 +00006630 maxval;
6631
6632 maxval=(1UL << image_depth)-1;
6633 background.red=(png_uint_16)
6634 (QuantumScale*(maxval*image->background_color.red));
6635 background.green=(png_uint_16)
6636 (QuantumScale*(maxval*image->background_color.green));
6637 background.blue=(png_uint_16)
6638 (QuantumScale*(maxval*image->background_color.blue));
6639 background.gray=(png_uint_16)
6640 (QuantumScale*(maxval*PixelIntensity(&image->background_color)));
6641 }
6642 else
6643 {
6644 background.red=image->background_color.red;
6645 background.green=image->background_color.green;
6646 background.blue=image->background_color.blue;
6647 background.gray=
6648 (png_uint_16) PixelIntensity(&image->background_color);
6649 }
6650 background.index=(png_byte) background.gray;
6651 if (logging != MagickFalse)
6652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6653 " Setting up bKGd chunk");
6654 png_set_bKGD(ping,ping_info,&background);
6655 }
6656 /*
6657 Select the color type.
6658 */
6659 matte=image_matte;
6660 old_bit_depth=0;
6661 if (mng_info->write_png8)
6662 {
glennrp5af765f2010-03-30 11:12:18 +00006663 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6664 ping_bit_depth=8;
6665 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006666 {
6667 /* TO DO: make this a function cause it's used twice, except
6668 for reducing the sample depth from 8. */
6669
6670 QuantizeInfo
6671 quantize_info;
6672
cristybb503372010-05-27 20:51:26 +00006673 size_t
cristy3ed852e2009-09-05 21:47:34 +00006674 number_colors,
6675 save_number_colors;
6676
6677 number_colors=image_colors;
6678 if ((image->storage_class == DirectClass) || (number_colors > 256))
6679 {
6680 GetQuantizeInfo(&quantize_info);
6681 quantize_info.dither=IsPaletteImage(image,&image->exception) ==
6682 MagickFalse ? MagickTrue : MagickFalse;
6683 quantize_info.number_colors= (matte != MagickFalse ? 255UL :
6684 256UL);
6685 (void) QuantizeImage(&quantize_info,image);
6686 number_colors=image_colors;
6687 (void) SyncImage(image);
6688 if (logging != MagickFalse)
6689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006690 " Colors quantized to %.20g",(double) number_colors);
cristy3ed852e2009-09-05 21:47:34 +00006691 }
6692 if (matte)
glennrp5af765f2010-03-30 11:12:18 +00006693 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00006694 /*
6695 Set image palette.
6696 */
glennrp5af765f2010-03-30 11:12:18 +00006697 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
cristy3ed852e2009-09-05 21:47:34 +00006698#if defined(PNG_SORT_PALETTE)
6699 save_number_colors=image_colors;
6700 if (CompressColormapTransFirst(image) == MagickFalse)
6701 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp98156a32009-12-09 15:32:44 +00006702 number_colors=image->colors;
cristy3ed852e2009-09-05 21:47:34 +00006703 image_colors=save_number_colors;
6704#endif
6705 palette=(png_color *) AcquireQuantumMemory(257,
6706 sizeof(*palette));
6707 if (palette == (png_color *) NULL)
6708 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6709 if (logging != MagickFalse)
6710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00006711 " Setting up PLTE chunk with %d colors",
6712 (int) number_colors);
cristybb503372010-05-27 20:51:26 +00006713 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00006714 {
6715 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6716 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6717 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6718 if (logging != MagickFalse)
6719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6720#if MAGICKCORE_QUANTUM_DEPTH == 8
6721 " %3ld (%3d,%3d,%3d)",
6722#else
6723 " %5ld (%5d,%5d,%5d)",
6724#endif
cristyf2faecf2010-05-28 19:19:36 +00006725 (long) i,palette[i].red,palette[i].green,palette[i].blue);
cristy3ed852e2009-09-05 21:47:34 +00006726
6727 }
6728 if (matte)
6729 {
6730 number_colors++;
6731 palette[i].red=ScaleQuantumToChar((Quantum) QuantumRange);
6732 palette[i].green=ScaleQuantumToChar((Quantum) QuantumRange);
6733 palette[i].blue=ScaleQuantumToChar((Quantum) QuantumRange);
6734 }
6735 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
cristy3ed852e2009-09-05 21:47:34 +00006736 palette=(png_colorp) RelinquishMagickMemory(palette);
glennrp5af765f2010-03-30 11:12:18 +00006737 image_depth=ping_bit_depth;
6738 ping_num_trans=0;
cristy3ed852e2009-09-05 21:47:34 +00006739 if (matte)
6740 {
6741 ExceptionInfo
6742 *exception;
6743
glennrp5af765f2010-03-30 11:12:18 +00006744 int
6745 trans_alpha[256];
6746
cristy3ed852e2009-09-05 21:47:34 +00006747 /*
6748 Identify which colormap entry is transparent.
6749 */
cristy3ed852e2009-09-05 21:47:34 +00006750 assert(number_colors <= 256);
cristybb503372010-05-27 20:51:26 +00006751 for (i=0; i < (ssize_t) number_colors; i++)
glennrp5af765f2010-03-30 11:12:18 +00006752 trans_alpha[i]=255;
cristy3ed852e2009-09-05 21:47:34 +00006753 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +00006754 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006755 {
6756 register const PixelPacket
6757 *p;
6758
6759 p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6760 if (p == (PixelPacket *) NULL)
6761 break;
cristy5c6f7892010-05-05 22:53:29 +00006762 indexes=GetAuthenticIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00006763 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006764 {
6765 if (p->opacity != OpaqueOpacity)
6766 {
cristy5c6f7892010-05-05 22:53:29 +00006767 indexes[x]=(IndexPacket) (number_colors-1);
cristybb503372010-05-27 20:51:26 +00006768 trans_alpha[(ssize_t) indexes[x]]=(png_byte) (255-
cristyce70c172010-01-07 17:15:30 +00006769 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00006770 }
6771 p++;
6772 }
6773 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6774 break;
6775 }
cristybb503372010-05-27 20:51:26 +00006776 for (i=0; i < (ssize_t) number_colors; i++)
glennrp5af765f2010-03-30 11:12:18 +00006777 if (trans_alpha[i] != 255)
6778 ping_num_trans=(unsigned short) (i+1);
6779
6780 if (ping_num_trans == 0)
6781 png_set_invalid(ping, ping_info, PNG_INFO_tRNS);
6782 if (!png_get_valid(ping, ping_info, PNG_INFO_tRNS))
6783 ping_num_trans=0;
6784 if (ping_num_trans != 0)
6785 {
6786 for (i=0; i<256; i++)
6787 ping_trans_alpha[i]=(png_byte) trans_alpha[i];
6788 }
6789
6790 (void) png_set_tRNS(ping, ping_info,
6791 ping_trans_alpha,
6792 ping_num_trans,
6793 &ping_trans_color);
cristy3ed852e2009-09-05 21:47:34 +00006794 }
6795 /*
6796 Identify which colormap entry is the background color.
6797 */
cristybb503372010-05-27 20:51:26 +00006798 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
glennrp5af765f2010-03-30 11:12:18 +00006799 if (IsPNGColorEqual(ping_background,image->colormap[i]))
cristy3ed852e2009-09-05 21:47:34 +00006800 break;
glennrp5af765f2010-03-30 11:12:18 +00006801 ping_background.index=(png_byte) i;
cristy3ed852e2009-09-05 21:47:34 +00006802 }
6803 if (image_matte != MagickFalse)
6804 {
6805 /* TO DO: reduce to binary transparency */
6806 }
6807 } /* end of write_png8 */
6808 else if (mng_info->write_png24)
6809 {
6810 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00006811 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00006812 }
6813 else if (mng_info->write_png32)
6814 {
6815 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00006816 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006817 }
6818 else
6819 {
glennrp5af765f2010-03-30 11:12:18 +00006820 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006821 if (mng_info->write_png_colortype)
6822 {
glennrp5af765f2010-03-30 11:12:18 +00006823 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
6824 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6825 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00006826 image_matte=MagickTrue;
6827 }
6828 else
6829 {
6830 if (logging != MagickFalse)
6831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6832 "Selecting PNG colortype");
glennrp5af765f2010-03-30 11:12:18 +00006833 ping_color_type=(png_byte) ((matte == MagickTrue)?
cristy3ed852e2009-09-05 21:47:34 +00006834 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6835 if(image_info->type == TrueColorType)
6836 {
glennrp5af765f2010-03-30 11:12:18 +00006837 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00006838 image_matte=MagickFalse;
6839 }
6840 if(image_info->type == TrueColorMatteType)
6841 {
glennrp5af765f2010-03-30 11:12:18 +00006842 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006843 image_matte=MagickTrue;
6844 }
6845 if ((image_info->type == UndefinedType ||
6846 image_info->type == OptimizeType ||
6847 image_info->type == GrayscaleType) &&
6848 image_matte == MagickFalse && ImageIsGray(image))
6849 {
glennrp5af765f2010-03-30 11:12:18 +00006850 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
cristy3ed852e2009-09-05 21:47:34 +00006851 image_matte=MagickFalse;
6852 }
6853 if ((image_info->type == UndefinedType ||
6854 image_info->type == OptimizeType ||
6855 image_info->type == GrayscaleMatteType) &&
6856 image_matte == MagickTrue && ImageIsGray(image))
6857 {
glennrp5af765f2010-03-30 11:12:18 +00006858 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006859 image_matte=MagickTrue;
6860 }
6861 }
6862 if (logging != MagickFalse)
6863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00006864 "Selected PNG colortype=%d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00006865
glennrp5af765f2010-03-30 11:12:18 +00006866 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00006867 {
glennrp5af765f2010-03-30 11:12:18 +00006868 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6869 ping_color_type == PNG_COLOR_TYPE_RGB ||
6870 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6871 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00006872 }
6873
glennrp5af765f2010-03-30 11:12:18 +00006874 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00006875 {
6876 if (image->matte == MagickFalse && image->colors < 256)
6877 {
6878 if (ImageIsMonochrome(image))
6879 {
glennrp5af765f2010-03-30 11:12:18 +00006880 ping_bit_depth=1;
6881 if (ping_bit_depth < (int)mng_info->write_png_depth)
6882 ping_bit_depth = mng_info->write_png_depth;
cristy3ed852e2009-09-05 21:47:34 +00006883 }
6884 }
6885 }
glennrp5af765f2010-03-30 11:12:18 +00006886 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00006887 {
cristy35ef8242010-06-03 16:24:13 +00006888 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00006889 ping_bit_depth=1;
cristy35ef8242010-06-03 16:24:13 +00006890 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00006891 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00006892
6893 if (logging != MagickFalse)
6894 {
6895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006896 " Number of colors: %.20g",(double) image_colors);
cristy3ed852e2009-09-05 21:47:34 +00006897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00006898 " Tentative PNG bit depth: %d",ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00006899 }
6900 if (mng_info->write_png_depth)
6901 {
glennrp5af765f2010-03-30 11:12:18 +00006902 old_bit_depth=ping_bit_depth;
6903 if (ping_bit_depth < (int)mng_info->write_png_depth)
cristy3ed852e2009-09-05 21:47:34 +00006904 {
glennrp5af765f2010-03-30 11:12:18 +00006905 ping_bit_depth = mng_info->write_png_depth;
6906 if (ping_bit_depth > 8)
6907 ping_bit_depth = 8;
6908 if (ping_bit_depth != (int) old_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00006909 {
6910 if (logging != MagickFalse)
6911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006912 " Colors increased to %.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00006913 image_colors);
cristy3ed852e2009-09-05 21:47:34 +00006914 }
6915 }
6916 }
6917 }
6918 }
glennrp5af765f2010-03-30 11:12:18 +00006919 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006920 if (logging != MagickFalse)
6921 {
6922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006923 " Tentative PNG color type: %.20g",(double) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00006924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006925 " image_info->type: %.20g",(double) image_info->type);
cristy3ed852e2009-09-05 21:47:34 +00006926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006927 " image_depth: %.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00006928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006929 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00006930 }
6931
6932 if (matte && (mng_info->optimize || mng_info->IsPalette))
6933 {
6934 register const PixelPacket
6935 *p;
6936
6937 p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
glennrp5af765f2010-03-30 11:12:18 +00006938 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
cristybb503372010-05-27 20:51:26 +00006939 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006940 {
6941 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6942 if (p == (const PixelPacket *) NULL)
6943 break;
cristybb503372010-05-27 20:51:26 +00006944 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006945 {
6946 if (IsGray(p) == MagickFalse)
6947 {
glennrp5af765f2010-03-30 11:12:18 +00006948 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006949 break;
6950 }
6951 p++;
6952 }
6953 }
6954 /*
6955 Determine if there is any transparent color.
6956 */
cristybb503372010-05-27 20:51:26 +00006957 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006958 {
6959 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6960 if (p == (const PixelPacket *) NULL)
6961 break;
cristybb503372010-05-27 20:51:26 +00006962 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006963 {
6964 if (p->opacity != OpaqueOpacity)
6965 break;
6966 p++;
6967 }
6968 if (x != 0)
6969 break;
6970 }
cristybb503372010-05-27 20:51:26 +00006971 if ((y == (ssize_t) image->rows) && (x == (ssize_t) image->columns))
cristy3ed852e2009-09-05 21:47:34 +00006972 {
6973 /*
glennrp5af765f2010-03-30 11:12:18 +00006974 No transparent pixels are present. Change 4 or 6 to 0 or 2.
cristy3ed852e2009-09-05 21:47:34 +00006975 */
6976 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00006977 ping_color_type&=0x03;
cristy3ed852e2009-09-05 21:47:34 +00006978 }
6979 else
6980 {
6981 unsigned int
6982 mask;
6983
6984 mask=0xffff;
glennrp5af765f2010-03-30 11:12:18 +00006985 if (ping_bit_depth == 8)
cristy3ed852e2009-09-05 21:47:34 +00006986 mask=0x00ff;
glennrp5af765f2010-03-30 11:12:18 +00006987 if (ping_bit_depth == 4)
cristy3ed852e2009-09-05 21:47:34 +00006988 mask=0x000f;
glennrp5af765f2010-03-30 11:12:18 +00006989 if (ping_bit_depth == 2)
cristy3ed852e2009-09-05 21:47:34 +00006990 mask=0x0003;
glennrp5af765f2010-03-30 11:12:18 +00006991 if (ping_bit_depth == 1)
cristy3ed852e2009-09-05 21:47:34 +00006992 mask=0x0001;
glennrp5af765f2010-03-30 11:12:18 +00006993 ping_trans_color.red=(png_uint_16)
cristyce70c172010-01-07 17:15:30 +00006994 (ScaleQuantumToShort(GetRedPixelComponent(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00006995 ping_trans_color.green=(png_uint_16)
cristyce70c172010-01-07 17:15:30 +00006996 (ScaleQuantumToShort(GetGreenPixelComponent(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00006997 ping_trans_color.blue=(png_uint_16)
cristyce70c172010-01-07 17:15:30 +00006998 (ScaleQuantumToShort(GetBluePixelComponent(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00006999 ping_trans_color.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00007000 (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00007001 ping_trans_color.index=(png_byte)
cristy46f08202010-01-10 04:04:21 +00007002 (ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p))));
glennrp5af765f2010-03-30 11:12:18 +00007003 (void) png_set_tRNS(ping, ping_info, NULL, 0,
7004 &ping_trans_color);
cristy3ed852e2009-09-05 21:47:34 +00007005 }
glennrp5af765f2010-03-30 11:12:18 +00007006 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00007007 {
7008 /*
7009 Determine if there is one and only one transparent color
7010 and if so if it is fully transparent.
7011 */
cristybb503372010-05-27 20:51:26 +00007012 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007013 {
7014 p=GetVirtualPixels(image,0,y,image->columns,1,
7015 &image->exception);
7016 x=0;
7017 if (p == (const PixelPacket *) NULL)
7018 break;
cristybb503372010-05-27 20:51:26 +00007019 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00007020 {
7021 if (p->opacity != OpaqueOpacity)
7022 {
glennrp5af765f2010-03-30 11:12:18 +00007023 if (IsPNGColorEqual(ping_trans_color,*p) == 0)
cristy3ed852e2009-09-05 21:47:34 +00007024 {
7025 break; /* Can't use RGB + tRNS for multiple
7026 transparent colors. */
7027 }
7028 if (p->opacity != (Quantum) TransparentOpacity)
7029 {
7030 break; /* Can't use RGB + tRNS for
7031 semitransparency. */
7032 }
7033 }
7034 else
7035 {
glennrp5af765f2010-03-30 11:12:18 +00007036 if (IsPNGColorEqual(ping_trans_color,*p))
cristy3ed852e2009-09-05 21:47:34 +00007037 break; /* Can't use RGB + tRNS when another pixel
7038 having the same RGB samples is
7039 transparent. */
7040 }
7041 p++;
7042 }
7043 if (x != 0)
7044 break;
7045 }
7046 if (x != 0)
glennrp5af765f2010-03-30 11:12:18 +00007047 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00007048 }
glennrp5af765f2010-03-30 11:12:18 +00007049 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00007050 {
glennrp5af765f2010-03-30 11:12:18 +00007051 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
cristy3ed852e2009-09-05 21:47:34 +00007052 if (image_depth == 8)
7053 {
glennrp5af765f2010-03-30 11:12:18 +00007054 ping_trans_color.red&=0xff;
7055 ping_trans_color.green&=0xff;
7056 ping_trans_color.blue&=0xff;
7057 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00007058 }
7059 }
7060 }
7061 matte=image_matte;
glennrp5af765f2010-03-30 11:12:18 +00007062 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00007063 image_matte=MagickFalse;
7064 if ((mng_info->optimize || mng_info->IsPalette) &&
7065 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
7066 ImageIsGray(image) && (!image_matte || image_depth >= 8))
7067 {
cristy35ef8242010-06-03 16:24:13 +00007068 size_t one=1;
cristy3ed852e2009-09-05 21:47:34 +00007069 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00007070 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00007071 else
7072 {
glennrp5af765f2010-03-30 11:12:18 +00007073 ping_color_type=PNG_COLOR_TYPE_GRAY;
cristy3ed852e2009-09-05 21:47:34 +00007074 if (save_image_depth == 16 && image_depth == 8)
glennrp5af765f2010-03-30 11:12:18 +00007075 ping_trans_color.gray*=0x0101;
cristy3ed852e2009-09-05 21:47:34 +00007076 }
7077 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
7078 image_depth=MAGICKCORE_QUANTUM_DEPTH;
7079 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
cristy35ef8242010-06-03 16:24:13 +00007080 image_colors=one << image_depth;
cristy3ed852e2009-09-05 21:47:34 +00007081 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00007082 ping_bit_depth=16;
cristy3ed852e2009-09-05 21:47:34 +00007083 else
7084 {
glennrp5af765f2010-03-30 11:12:18 +00007085 ping_bit_depth=8;
7086 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00007087 {
7088 if(!mng_info->write_png_depth)
7089 {
glennrp5af765f2010-03-30 11:12:18 +00007090 ping_bit_depth=1;
cristy35ef8242010-06-03 16:24:13 +00007091 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00007092 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00007093 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00007094 }
7095 }
glennrp5af765f2010-03-30 11:12:18 +00007096 else if (mng_info->optimize && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00007097 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
7098 mng_info->IsPalette)
7099 {
7100
7101 /* Check if grayscale is reducible */
7102 int
7103 depth_4_ok=MagickTrue,
7104 depth_2_ok=MagickTrue,
7105 depth_1_ok=MagickTrue;
7106
cristybb503372010-05-27 20:51:26 +00007107 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007108 {
7109 unsigned char
7110 intensity;
7111
7112 intensity=ScaleQuantumToChar(image->colormap[i].red);
7113
7114 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
7115 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
7116 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
7117 depth_2_ok=depth_1_ok=MagickFalse;
7118 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
7119 depth_1_ok=MagickFalse;
7120 }
7121 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00007122 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00007123 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00007124 ping_bit_depth=2;
cristy3ed852e2009-09-05 21:47:34 +00007125 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00007126 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00007127 }
7128 }
glennrp5af765f2010-03-30 11:12:18 +00007129 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007130 }
7131 else
7132 if (mng_info->IsPalette)
7133 {
cristybb503372010-05-27 20:51:26 +00007134 size_t
glennrp17a14852010-05-10 03:01:59 +00007135 number_colors;
7136
7137 number_colors=image_colors;
7138
cristy3ed852e2009-09-05 21:47:34 +00007139 if (image_depth <= 8)
7140 {
cristy3ed852e2009-09-05 21:47:34 +00007141 /*
7142 Set image palette.
7143 */
glennrp5af765f2010-03-30 11:12:18 +00007144 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
cristy3ed852e2009-09-05 21:47:34 +00007145 if (mng_info->have_write_global_plte && !matte)
7146 {
glennrp9c1eb072010-06-06 22:19:15 +00007147 png_set_PLTE(ping,ping_info,NULL,0);
7148 if (logging)
7149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7150 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00007151 }
7152 else
7153 {
7154#if defined(PNG_SORT_PALETTE)
cristybb503372010-05-27 20:51:26 +00007155 size_t
cristy3ed852e2009-09-05 21:47:34 +00007156 save_number_colors;
7157
7158 if (mng_info->optimize)
7159 {
7160 save_number_colors=image_colors;
7161 if (CompressColormapTransFirst(image) == MagickFalse)
7162 ThrowWriterException(ResourceLimitError,
7163 "MemoryAllocationFailed");
glennrp98156a32009-12-09 15:32:44 +00007164 number_colors=image->colors;
cristy3ed852e2009-09-05 21:47:34 +00007165 image_colors=save_number_colors;
7166 }
7167#endif
7168 palette=(png_color *) AcquireQuantumMemory(257,
7169 sizeof(*palette));
7170 if (palette == (png_color *) NULL)
7171 ThrowWriterException(ResourceLimitError,
7172 "MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00007173 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007174 {
7175 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7176 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7177 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7178 }
7179 if (logging)
7180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00007181 " Setting up PLTE chunk with %d colors",
7182 (int) number_colors);
cristy3ed852e2009-09-05 21:47:34 +00007183 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
cristy3ed852e2009-09-05 21:47:34 +00007184 palette=(png_colorp) RelinquishMagickMemory(palette);
cristy3ed852e2009-09-05 21:47:34 +00007185 }
7186 /* color_type is PNG_COLOR_TYPE_PALETTE */
7187 if (!mng_info->write_png_depth)
7188 {
cristybefe4d22010-06-07 01:18:58 +00007189 size_t
7190 one;
7191
glennrp5af765f2010-03-30 11:12:18 +00007192 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00007193 one=1;
7194 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00007195 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00007196 }
glennrp5af765f2010-03-30 11:12:18 +00007197 ping_num_trans=0;
cristy3ed852e2009-09-05 21:47:34 +00007198 if (matte)
7199 {
7200 ExceptionInfo
7201 *exception;
7202
7203 register const PixelPacket
7204 *p;
7205
7206 int
7207 trans[256];
7208
7209 register const IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00007210 *packet_indexes;
cristy3ed852e2009-09-05 21:47:34 +00007211
7212 /*
7213 Identify which colormap entry is transparent.
7214 */
7215 assert(number_colors <= 256);
cristybb503372010-05-27 20:51:26 +00007216 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007217 trans[i]=256;
7218 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +00007219 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007220 {
7221 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7222 if (p == (const PixelPacket *) NULL)
7223 break;
cristy5c6f7892010-05-05 22:53:29 +00007224 packet_indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00007225 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00007226 {
7227 if (p->opacity != OpaqueOpacity)
7228 {
7229 IndexPacket
7230 packet_index;
7231
cristy5c6f7892010-05-05 22:53:29 +00007232 packet_index=packet_indexes[x];
cristybb503372010-05-27 20:51:26 +00007233 assert((size_t) packet_index < number_colors);
7234 if (trans[(ssize_t) packet_index] != 256)
cristy3ed852e2009-09-05 21:47:34 +00007235 {
cristybb503372010-05-27 20:51:26 +00007236 if (trans[(ssize_t) packet_index] != (png_byte) (255-
cristyce70c172010-01-07 17:15:30 +00007237 ScaleQuantumToChar(GetOpacityPixelComponent(p))))
cristy3ed852e2009-09-05 21:47:34 +00007238 {
glennrp5af765f2010-03-30 11:12:18 +00007239 ping_color_type=(png_byte)
cristy3ed852e2009-09-05 21:47:34 +00007240 PNG_COLOR_TYPE_RGB_ALPHA;
7241 break;
7242 }
7243 }
cristybb503372010-05-27 20:51:26 +00007244 trans[(ssize_t) packet_index]=(png_byte) (255-
cristyce70c172010-01-07 17:15:30 +00007245 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00007246 }
7247 p++;
7248 }
glennrp5af765f2010-03-30 11:12:18 +00007249 if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00007250 {
glennrp5af765f2010-03-30 11:12:18 +00007251 ping_num_trans=0;
7252 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7253 png_set_invalid(ping,ping_info,PNG_INFO_PLTE);
cristy3ed852e2009-09-05 21:47:34 +00007254 mng_info->IsPalette=MagickFalse;
7255 (void) SyncImage(image);
7256 if (logging)
7257 (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7258 " Cannot write image as indexed PNG, writing RGBA.");
7259 break;
7260 }
7261 }
glennrp5af765f2010-03-30 11:12:18 +00007262 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00007263 {
cristybb503372010-05-27 20:51:26 +00007264 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007265 {
7266 if (trans[i] == 256)
7267 trans[i]=255;
7268 if (trans[i] != 255)
glennrp5af765f2010-03-30 11:12:18 +00007269 ping_num_trans=(unsigned short) (i+1);
cristy3ed852e2009-09-05 21:47:34 +00007270 }
7271 }
glennrp5af765f2010-03-30 11:12:18 +00007272 if (ping_num_trans == 0)
7273 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7274 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7275 ping_num_trans=0;
7276 if (ping_num_trans != 0)
glennrp9c1eb072010-06-06 22:19:15 +00007277 {
7278 for (i=0; i < (ssize_t) number_colors; i++)
7279 ping_trans_alpha[i]=(png_byte) trans[i];
7280 }
cristy3ed852e2009-09-05 21:47:34 +00007281 }
7282
cristy3ed852e2009-09-05 21:47:34 +00007283 }
7284 }
7285 else
7286 {
7287 if (image_depth < 8)
7288 image_depth=8;
7289 if ((save_image_depth == 16) && (image_depth == 8))
7290 {
glennrp5af765f2010-03-30 11:12:18 +00007291 ping_trans_color.red*=0x0101;
7292 ping_trans_color.green*=0x0101;
7293 ping_trans_color.blue*=0x0101;
7294 ping_trans_color.gray*=0x0101;
cristy3ed852e2009-09-05 21:47:34 +00007295 }
7296 }
7297
7298 /*
7299 Adjust background and transparency samples in sub-8-bit grayscale files.
7300 */
glennrp5af765f2010-03-30 11:12:18 +00007301 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00007302 PNG_COLOR_TYPE_GRAY)
7303 {
7304 png_uint_16
7305 maxval;
7306
7307 png_color_16
7308 background;
7309
cristy35ef8242010-06-03 16:24:13 +00007310 size_t
7311 one=1;
7312
cristy22ffd972010-06-03 16:51:47 +00007313 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00007314
7315
7316 background.gray=(png_uint_16)
7317 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7318
7319 if (logging != MagickFalse)
7320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7321 " Setting up bKGD chunk");
7322 png_set_bKGD(ping,ping_info,&background);
7323
glennrp5af765f2010-03-30 11:12:18 +00007324 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7325 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00007326 }
glennrp17a14852010-05-10 03:01:59 +00007327
7328 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7329 {
7330 /*
7331 Identify which colormap entry is the background color.
7332 */
7333
cristybb503372010-05-27 20:51:26 +00007334 size_t
glennrp17a14852010-05-10 03:01:59 +00007335 number_colors;
7336
7337 number_colors=image_colors;
7338
cristybb503372010-05-27 20:51:26 +00007339 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
glennrp17a14852010-05-10 03:01:59 +00007340 if (IsPNGColorEqual(ping_background,image->colormap[i]))
7341 break;
7342
7343 ping_background.index=(png_byte) i;
7344
7345 if (logging)
7346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7347 " Setting up bKGD chunk with index=%d",(int) i);
7348
7349 png_set_bKGD(ping,ping_info,&ping_background);
7350 }
7351
cristy3ed852e2009-09-05 21:47:34 +00007352 if (logging != MagickFalse)
7353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007354 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00007355 /*
7356 Initialize compression level and filtering.
7357 */
7358 if (logging != MagickFalse)
7359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7360 " Setting up deflate compression");
cristy3ed852e2009-09-05 21:47:34 +00007361 if (logging != MagickFalse)
7362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7363 " Compression buffer size: 32768");
7364 png_set_compression_buffer_size(ping,32768L);
cristy3ed852e2009-09-05 21:47:34 +00007365 if (logging != MagickFalse)
7366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7367 " Compression mem level: 9");
7368 png_set_compression_mem_level(ping, 9);
7369 quality=image->quality == UndefinedCompressionQuality ? 75UL :
7370 image->quality;
7371 if (quality > 9)
7372 {
7373 int
7374 level;
7375
cristybb503372010-05-27 20:51:26 +00007376 level=(int) MagickMin((ssize_t) quality/10,9);
cristy3ed852e2009-09-05 21:47:34 +00007377 if (logging != MagickFalse)
7378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7379 " Compression level: %d",level);
7380 png_set_compression_level(ping,level);
7381 }
7382 else
7383 {
7384 if (logging != MagickFalse)
7385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7386 " Compression strategy: Z_HUFFMAN_ONLY");
7387 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7388 }
7389 if (logging != MagickFalse)
7390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7391 " Setting up filtering");
7392#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7393
7394 /* This became available in libpng-1.0.9. Output must be a MNG. */
7395 if (mng_info->write_mng && ((quality % 10) == 7))
7396 {
7397 if (logging != MagickFalse)
7398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7399 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp5af765f2010-03-30 11:12:18 +00007400 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00007401 }
7402 else
7403 if (logging != MagickFalse)
7404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7405 " Filter_type: 0");
7406#endif
7407 {
7408 int
7409 base_filter;
7410
7411 if ((quality % 10) > 5)
7412 base_filter=PNG_ALL_FILTERS;
7413 else
7414 if ((quality % 10) != 5)
7415 base_filter=(int) quality % 10;
7416 else
glennrp5af765f2010-03-30 11:12:18 +00007417 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
7418 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
cristy3ed852e2009-09-05 21:47:34 +00007419 (quality < 50))
7420 base_filter=PNG_NO_FILTERS;
7421 else
7422 base_filter=PNG_ALL_FILTERS;
7423 if (logging != MagickFalse)
7424 {
7425 if (base_filter == PNG_ALL_FILTERS)
7426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7427 " Base filter method: ADAPTIVE");
7428 else
7429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7430 " Base filter method: NONE");
7431 }
7432 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7433 }
7434
7435 ResetImageProfileIterator(image);
7436 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7437 {
7438 profile=GetImageProfile(image,name);
7439 if (profile != (StringInfo *) NULL)
7440 {
glennrp5af765f2010-03-30 11:12:18 +00007441#ifdef PNG_WRITE_iCCP_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00007442 if ((LocaleCompare(name,"ICC") == 0) ||
7443 (LocaleCompare(name,"ICM") == 0))
7444 png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7445 GetStringInfoDatum(profile),
7446 (png_uint_32) GetStringInfoLength(profile));
7447 else
7448#endif
7449 png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7450 name,(unsigned char *) name,GetStringInfoDatum(profile),
7451 (png_uint_32) GetStringInfoLength(profile));
7452 }
7453 if (logging != MagickFalse)
7454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7455 " Setting up text chunk with %s profile",name);
7456 name=GetNextImageProfile(image);
7457 }
7458
7459#if defined(PNG_WRITE_sRGB_SUPPORTED)
7460 if ((mng_info->have_write_global_srgb == 0) &&
7461 ((image->rendering_intent != UndefinedIntent) ||
7462 (image->colorspace == sRGBColorspace)))
7463 {
7464 /*
7465 Note image rendering intent.
7466 */
7467 if (logging != MagickFalse)
7468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7469 " Setting up sRGB chunk");
7470 (void) png_set_sRGB(ping,ping_info,(int) (image->rendering_intent-1));
7471 png_set_gAMA(ping,ping_info,0.45455);
7472 }
glennrp5af765f2010-03-30 11:12:18 +00007473 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00007474#endif
7475 {
7476 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7477 {
7478 /*
7479 Note image gamma.
7480 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7481 */
7482 if (logging != MagickFalse)
7483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7484 " Setting up gAMA chunk");
7485 png_set_gAMA(ping,ping_info,image->gamma);
7486 }
7487 if ((mng_info->have_write_global_chrm == 0) &&
7488 (image->chromaticity.red_primary.x != 0.0))
7489 {
7490 /*
7491 Note image chromaticity.
7492 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7493 */
7494 PrimaryInfo
7495 bp,
7496 gp,
7497 rp,
7498 wp;
7499
7500 wp=image->chromaticity.white_point;
7501 rp=image->chromaticity.red_primary;
7502 gp=image->chromaticity.green_primary;
7503 bp=image->chromaticity.blue_primary;
7504
7505 if (logging != MagickFalse)
7506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7507 " Setting up cHRM chunk");
7508 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7509 bp.x,bp.y);
7510 }
7511 }
glennrp5af765f2010-03-30 11:12:18 +00007512 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00007513
7514 if (mng_info->write_mng)
7515 png_set_sig_bytes(ping,8);
7516
7517 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7518
7519 if (mng_info->write_png_colortype)
7520 {
7521 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7522 if (ImageIsGray(image) == MagickFalse)
7523 {
glennrp5af765f2010-03-30 11:12:18 +00007524 ping_color_type = PNG_COLOR_TYPE_RGB;
7525 if (ping_bit_depth < 8)
7526 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00007527 }
7528
7529 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7530 if (ImageIsGray(image) == MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00007531 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00007532 }
7533
7534 if ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00007535 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00007536 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00007537 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00007538 mng_info->write_png_colortype != 7 &&
glennrp5af765f2010-03-30 11:12:18 +00007539 !(mng_info->write_png_colortype == 5 && ping_color_type == 0))))
cristy3ed852e2009-09-05 21:47:34 +00007540 {
7541 if (logging != MagickFalse)
7542 {
7543 if (mng_info->write_png_depth)
7544 {
7545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7546 " Defined PNG:bit-depth=%u, Computed depth=%u",
7547 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00007548 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00007549 }
7550 if (mng_info->write_png_colortype)
7551 {
7552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7553 " Defined PNG:color-type=%u, Computed color type=%u",
7554 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00007555 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00007556 }
7557 }
7558 png_error(ping,
7559 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7560 }
7561
7562 if (image_matte && !image->matte)
7563 {
7564 /* Add an opaque matte channel */
7565 image->matte = MagickTrue;
7566 (void) SetImageOpacity(image,0);
glennrpb4a13412010-05-05 12:47:19 +00007567 if (logging != MagickFalse)
7568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7569 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00007570 }
7571
glennrpe9c26dc2010-05-30 01:56:35 +00007572 if (image->matte == MagickTrue && ping_color_type < 4)
7573 {
7574 if (ping_color_type == 3 && ping_num_trans == 0)
7575 {
7576 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7577 if (logging != MagickFalse)
7578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7579 " Ignoring request to write tRNS chunk with num_trans==0");
7580 }
7581 else
7582 (void) png_set_tRNS(ping, ping_info,
7583 ping_trans_alpha,
7584 ping_num_trans,
7585 &ping_trans_color);
7586 }
7587
cristy3ed852e2009-09-05 21:47:34 +00007588 if (logging != MagickFalse)
7589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7590 " Writing PNG header chunks");
7591
glennrp5af765f2010-03-30 11:12:18 +00007592 png_set_IHDR(ping,ping_info,ping_width,ping_height,
7593 ping_bit_depth,ping_color_type,
7594 ping_interlace_method,ping_compression_method,
7595 ping_filter_method);
7596
cristy3ed852e2009-09-05 21:47:34 +00007597 png_write_info_before_PLTE(ping, ping_info);
7598 /* write any png-chunk-b profiles */
7599 (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7600 png_write_info(ping,ping_info);
7601 /* write any PNG-chunk-m profiles */
7602 (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7603
7604 if (image->page.width || image->page.height)
7605 {
glennrp9c1eb072010-06-06 22:19:15 +00007606 unsigned char
7607 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00007608
glennrp9c1eb072010-06-06 22:19:15 +00007609 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
7610 PNGType(chunk,mng_vpAg);
7611 LogPNGChunk((int) logging,mng_vpAg,9L);
7612 PNGLong(chunk+4,(png_uint_32) image->page.width);
7613 PNGLong(chunk+8,(png_uint_32) image->page.height);
7614 chunk[12]=0; /* unit = pixels */
7615 (void) WriteBlob(image,13,chunk);
7616 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
cristy3ed852e2009-09-05 21:47:34 +00007617 }
7618
7619#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00007620 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00007621#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00007622 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00007623#undef PNG_HAVE_IDAT
7624#endif
7625
7626 png_set_packing(ping);
7627 /*
7628 Allocate memory.
7629 */
7630 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00007631 if (image_depth > 8)
7632 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00007633 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00007634 {
glennrpb4a13412010-05-05 12:47:19 +00007635 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00007636 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00007637 break;
7638 case PNG_COLOR_TYPE_GRAY_ALPHA:
7639 rowbytes*=2;
7640 break;
7641 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00007642 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00007643 break;
7644 default:
7645 break;
cristy3ed852e2009-09-05 21:47:34 +00007646 }
7647 if (logging)
glennrpb4a13412010-05-05 12:47:19 +00007648 {
7649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7650 " Writing PNG image data");
7651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007652 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00007653 }
cristy3ed852e2009-09-05 21:47:34 +00007654 png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7655 sizeof(*png_pixels));
7656 if (png_pixels == (unsigned char *) NULL)
7657 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7658 /*
7659 Initialize image scanlines.
7660 */
glennrp5af765f2010-03-30 11:12:18 +00007661 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00007662 {
7663 /*
7664 PNG write failed.
7665 */
7666#ifdef PNG_DEBUG
7667 if (image_info->verbose)
7668 (void) printf("PNG write has failed.\n");
7669#endif
7670 png_destroy_write_struct(&ping,&ping_info);
7671 if (quantum_info != (QuantumInfo *) NULL)
7672 quantum_info=DestroyQuantumInfo(quantum_info);
7673 if (png_pixels != (unsigned char *) NULL)
7674 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7675#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00007676 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007677#endif
7678 return(MagickFalse);
7679 }
cristyed552522009-10-16 14:04:35 +00007680 quantum_info=AcquireQuantumInfo(image_info,image);
7681 if (quantum_info == (QuantumInfo *) NULL)
7682 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00007683 quantum_info->format=UndefinedQuantumFormat;
7684 quantum_info->depth=image_depth;
7685 num_passes=png_set_interlace_handling(ping);
7686 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7687 !mng_info->write_png32) &&
7688 (mng_info->optimize || mng_info->IsPalette ||
7689 (image_info->type == BilevelType)) &&
7690 !image_matte && ImageIsMonochrome(image))
7691 {
7692 register const PixelPacket
7693 *p;
7694
7695 quantum_info->depth=8;
7696 for (pass=0; pass < num_passes; pass++)
7697 {
7698 /*
7699 Convert PseudoClass image to a PNG monochrome image.
7700 */
cristybb503372010-05-27 20:51:26 +00007701 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007702 {
7703 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7704 if (p == (const PixelPacket *) NULL)
7705 break;
7706 if (mng_info->IsPalette)
7707 {
7708 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7709 quantum_info,GrayQuantum,png_pixels,&image->exception);
7710 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7711 mng_info->write_png_depth &&
7712 mng_info->write_png_depth != old_bit_depth)
7713 {
7714 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00007715 for (i=0; i < (ssize_t) image->columns; i++)
cristy3ed852e2009-09-05 21:47:34 +00007716 *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7717 >> (8-old_bit_depth));
7718 }
7719 }
7720 else
7721 {
7722 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7723 quantum_info,RedQuantum,png_pixels,&image->exception);
7724 }
7725 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00007726 for (i=0; i < (ssize_t) image->columns; i++)
cristy3ed852e2009-09-05 21:47:34 +00007727 *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7728 255 : 0);
glennrpb4a13412010-05-05 12:47:19 +00007729 if (logging && y == 0)
7730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7731 " Writing row of pixels (1)");
cristy3ed852e2009-09-05 21:47:34 +00007732 png_write_row(ping,png_pixels);
7733 }
7734 if (image->previous == (Image *) NULL)
7735 {
7736 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7737 if (status == MagickFalse)
7738 break;
7739 }
7740 }
7741 }
7742 else
7743 for (pass=0; pass < num_passes; pass++)
7744 {
7745 register const PixelPacket
7746 *p;
7747
7748 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7749 !mng_info->write_png32) &&
7750 (image_matte ||
glennrp5af765f2010-03-30 11:12:18 +00007751 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
cristy3ed852e2009-09-05 21:47:34 +00007752 (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7753 {
cristybb503372010-05-27 20:51:26 +00007754 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007755 {
7756 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7757 if (p == (const PixelPacket *) NULL)
7758 break;
glennrp5af765f2010-03-30 11:12:18 +00007759 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00007760 {
7761 if (mng_info->IsPalette)
7762 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7763 quantum_info,GrayQuantum,png_pixels,&image->exception);
7764 else
7765 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7766 quantum_info,RedQuantum,png_pixels,&image->exception);
glennrpb4a13412010-05-05 12:47:19 +00007767 if (logging && y == 0)
7768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7769 " Writing GRAY PNG pixels (2)");
cristy3ed852e2009-09-05 21:47:34 +00007770 }
7771 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7772 {
glennrpb4a13412010-05-05 12:47:19 +00007773 if (logging && y == 0)
7774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7775 " Writing GRAY_ALPHA PNG pixels (2)");
cristy3ed852e2009-09-05 21:47:34 +00007776 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7777 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7778 }
glennrpb4a13412010-05-05 12:47:19 +00007779 if (logging && y == 0)
7780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7781 " Writing row of pixels (2)");
cristy3ed852e2009-09-05 21:47:34 +00007782 png_write_row(ping,png_pixels);
7783 }
7784 if (image->previous == (Image *) NULL)
7785 {
7786 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7787 if (status == MagickFalse)
7788 break;
7789 }
7790 }
7791 else
7792 for (pass=0; pass < num_passes; pass++)
7793 {
7794 if ((image_depth > 8) || (mng_info->write_png24 ||
7795 mng_info->write_png32 ||
7796 (!mng_info->write_png8 && !mng_info->IsPalette)))
cristybb503372010-05-27 20:51:26 +00007797 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007798 {
7799 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7800 if (p == (const PixelPacket *) NULL)
7801 break;
glennrp5af765f2010-03-30 11:12:18 +00007802 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00007803 {
7804 if (image->storage_class == DirectClass)
7805 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7806 quantum_info,RedQuantum,png_pixels,&image->exception);
7807 else
7808 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7809 quantum_info,GrayQuantum,png_pixels,&image->exception);
7810 }
glennrp5af765f2010-03-30 11:12:18 +00007811 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrpb4a13412010-05-05 12:47:19 +00007812 {
7813 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7814 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7815 if (logging && y == 0)
7816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7817 " Writing GRAY_ALPHA PNG pixels (3)");
7818 }
cristy3ed852e2009-09-05 21:47:34 +00007819 else if (image_matte != MagickFalse)
7820 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7821 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7822 else
7823 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7824 quantum_info,RGBQuantum,png_pixels,&image->exception);
glennrpb4a13412010-05-05 12:47:19 +00007825 if (logging && y == 0)
7826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7827 " Writing row of pixels (3)");
cristy3ed852e2009-09-05 21:47:34 +00007828 png_write_row(ping,png_pixels);
7829 }
7830 else
7831 /* not ((image_depth > 8) || (mng_info->write_png24 ||
7832 mng_info->write_png32 ||
7833 (!mng_info->write_png8 && !mng_info->IsPalette))) */
7834 {
glennrp5af765f2010-03-30 11:12:18 +00007835 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
7836 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
cristy3ed852e2009-09-05 21:47:34 +00007837 {
7838 if (logging)
7839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7840 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7841 quantum_info->depth=8;
7842 image_depth=8;
7843 }
cristybb503372010-05-27 20:51:26 +00007844 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007845 {
glennrpe9c26dc2010-05-30 01:56:35 +00007846 if (logging && y == 0)
cristy3ed852e2009-09-05 21:47:34 +00007847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7848 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7849 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7850 if (p == (const PixelPacket *) NULL)
7851 break;
glennrp5af765f2010-03-30 11:12:18 +00007852 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00007853 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7854 quantum_info,GrayQuantum,png_pixels,&image->exception);
glennrp5af765f2010-03-30 11:12:18 +00007855 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrpb4a13412010-05-05 12:47:19 +00007856 {
glennrp07cd77a2010-05-05 12:49:15 +00007857 if (logging && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00007858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7859 " Writing GRAY_ALPHA PNG pixels (4)");
7860 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7861 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7862 }
cristy3ed852e2009-09-05 21:47:34 +00007863 else
7864 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7865 quantum_info,IndexQuantum,png_pixels,&image->exception);
glennrp07cd77a2010-05-05 12:49:15 +00007866 if (logging && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00007867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7868 " Writing row of pixels (4)");
cristy3ed852e2009-09-05 21:47:34 +00007869 png_write_row(ping,png_pixels);
7870 }
7871 }
7872 if (image->previous == (Image *) NULL)
7873 {
7874 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7875 if (status == MagickFalse)
7876 break;
7877 }
7878 }
7879 }
cristyb32b90a2009-09-07 21:45:48 +00007880 if (quantum_info != (QuantumInfo *) NULL)
7881 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00007882
7883 if (logging != MagickFalse)
7884 {
7885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00007886 " Wrote PNG image data");
cristy3ed852e2009-09-05 21:47:34 +00007887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007888 " Width: %.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00007889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007890 " Height: %.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00007891 if (mng_info->write_png_depth)
7892 {
7893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7894 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7895 }
7896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007897 " PNG bit-depth written: %d",ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00007898 if (mng_info->write_png_colortype)
7899 {
7900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7901 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7902 }
7903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007904 " PNG color-type written: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00007905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007906 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00007907 }
7908 /*
7909 Generate text chunks.
7910 */
cristy3ed852e2009-09-05 21:47:34 +00007911 ResetImagePropertyIterator(image);
7912 property=GetNextImageProperty(image);
7913 while (property != (const char *) NULL)
7914 {
cristy3ed852e2009-09-05 21:47:34 +00007915 png_textp
7916 text;
cristy3ed852e2009-09-05 21:47:34 +00007917
7918 value=GetImageProperty(image,property);
7919 if (value != (const char *) NULL)
7920 {
cristy3ed852e2009-09-05 21:47:34 +00007921 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7922 text[0].key=(char *) property;
7923 text[0].text=(char *) value;
7924 text[0].text_length=strlen(value);
7925 text[0].compression=image_info->compression == NoCompression ||
7926 (image_info->compression == UndefinedCompression &&
7927 text[0].text_length < 128) ? -1 : 0;
7928 if (logging != MagickFalse)
7929 {
7930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7931 " Setting up text chunk");
7932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7933 " keyword: %s",text[0].key);
7934 }
7935 png_set_text(ping,ping_info,text,1);
7936 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007937 }
7938 property=GetNextImageProperty(image);
7939 }
7940
7941 /* write any PNG-chunk-e profiles */
7942 (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
7943
7944 if (logging != MagickFalse)
7945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7946 " Writing PNG end info");
7947 png_write_end(ping,ping_info);
7948 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
7949 {
7950 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00007951 (ping_width != mng_info->page.width) ||
7952 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00007953 {
7954 unsigned char
7955 chunk[32];
7956
7957 /*
7958 Write FRAM 4 with clipping boundaries followed by FRAM 1.
7959 */
7960 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
7961 PNGType(chunk,mng_FRAM);
7962 LogPNGChunk((int) logging,mng_FRAM,27L);
7963 chunk[4]=4;
7964 chunk[5]=0; /* frame name separator (no name) */
7965 chunk[6]=1; /* flag for changing delay, for next frame only */
7966 chunk[7]=0; /* flag for changing frame timeout */
7967 chunk[8]=1; /* flag for changing frame clipping for next frame */
7968 chunk[9]=0; /* flag for changing frame sync_id */
7969 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
7970 chunk[14]=0; /* clipping boundaries delta type */
7971 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
7972 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00007973 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00007974 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
7975 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00007976 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00007977 (void) WriteBlob(image,31,chunk);
7978 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
7979 mng_info->old_framing_mode=4;
7980 mng_info->framing_mode=1;
7981 }
7982 else
7983 mng_info->framing_mode=3;
7984 }
7985 if (mng_info->write_mng && !mng_info->need_fram &&
7986 ((int) image->dispose == 3))
7987 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7988 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
7989 "`%s'",image->filename);
7990 image_depth=save_image_depth;
7991
7992 /* Save depth actually written */
7993
glennrp5af765f2010-03-30 11:12:18 +00007994 s[0]=(char) ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007995 s[1]='\0';
7996
7997 (void) SetImageProperty(image,"png:bit-depth-written",s);
7998
7999 /*
8000 Free PNG resources.
8001 */
glennrp5af765f2010-03-30 11:12:18 +00008002
cristy3ed852e2009-09-05 21:47:34 +00008003 png_destroy_write_struct(&ping,&ping_info);
8004
8005 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
8006
8007#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00008008 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008009#endif
8010
8011 if (logging != MagickFalse)
8012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8013 " exit WriteOnePNGImage()");
8014 return(MagickTrue);
8015/* End write one PNG image */
8016}
8017
8018/*
8019%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8020% %
8021% %
8022% %
8023% W r i t e P N G I m a g e %
8024% %
8025% %
8026% %
8027%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8028%
8029% WritePNGImage() writes a Portable Network Graphics (PNG) or
8030% Multiple-image Network Graphics (MNG) image file.
8031%
8032% MNG support written by Glenn Randers-Pehrson, glennrp@image...
8033%
8034% The format of the WritePNGImage method is:
8035%
8036% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
8037%
8038% A description of each parameter follows:
8039%
8040% o image_info: the image info.
8041%
8042% o image: The image.
8043%
8044% Returns MagickTrue on success, MagickFalse on failure.
8045%
8046% Communicating with the PNG encoder:
8047%
8048% While the datastream written is always in PNG format and normally would
8049% be given the "png" file extension, this method also writes the following
8050% pseudo-formats which are subsets of PNG:
8051%
8052% o PNG8: An 8-bit indexed PNG datastream is written. If transparency
8053% is present, the tRNS chunk must only have values 0 and 255
8054% (i.e., transparency is binary: fully opaque or fully
8055% transparent). The pixels contain 8-bit indices even if
8056% they could be represented with 1, 2, or 4 bits. Note: grayscale
8057% images will be written as indexed PNG files even though the
8058% PNG grayscale type might be slightly more efficient.
8059%
8060% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
8061% chunk can be present to convey binary transparency by naming
8062% one of the colors as transparent.
8063%
8064% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
8065% transparency is permitted, i.e., the alpha sample for
8066% each pixel can have any value from 0 to 255. The alpha
8067% channel is present even if the image is fully opaque.
8068%
8069% o -define: For more precise control of the PNG output, you can use the
8070% Image options "png:bit-depth" and "png:color-type". These
8071% can be set from the commandline with "-define" and also
8072% from the application programming interfaces.
8073%
8074% png:color-type can be 0, 2, 3, 4, or 6.
8075%
8076% When png:color-type is 0 (Grayscale), png:bit-depth can
8077% be 1, 2, 4, 8, or 16.
8078%
8079% When png:color-type is 2 (RGB), png:bit-depth can
8080% be 8 or 16.
8081%
8082% When png:color-type is 3 (Indexed), png:bit-depth can
8083% be 1, 2, 4, or 8. This refers to the number of bits
8084% used to store the index. The color samples always have
8085% bit-depth 8 in indexed PNG files.
8086%
8087% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
8088% png:bit-depth can be 8 or 16.
8089%
8090% If the image cannot be written without loss in the requested PNG8, PNG24,
8091% or PNG32 format or with the requested bit-depth and color-type without loss,
8092% a PNG file will not be written, and the encoder will return MagickFalse.
8093% Since image encoders should not be responsible for the "heavy lifting",
8094% the user should make sure that ImageMagick has already reduced the
8095% image depth and number of colors and limit transparency to binary
8096% transparency prior to attempting to write the image in a format that
8097% is subject to depth, color, or transparency limitations.
8098%
8099% TODO: Enforce the previous paragraph.
8100%
8101% TODO: Allow all other PNG subformats to be requested via new
8102% "-define png:bit-depth -define png:color-type" options.
8103%
8104% Note that another definition, "png:bit-depth-written" exists, but it
8105% is not intended for external use. It is only used internally by the
8106% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
8107%
8108% It is possible to request that the PNG encoder write previously-formatted
8109% ancillary chunks in the output PNG file, using the "-profile" commandline
8110% option as shown below or by setting the profile via a programming
8111% interface:
8112%
8113% -profile PNG-chunk-x:<file>
8114%
8115% where x is a location flag and <file> is a file containing the chunk
8116% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
8117%
8118% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
8119% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
8120% of the same type, then add a short unique string after the "x" to prevent
8121% subsequent profiles from overwriting the preceding ones:
8122%
8123% -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
8124%
8125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8126*/
8127static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
8128 Image *image)
8129{
8130 MagickBooleanType
8131 status;
8132
8133 MngInfo
8134 *mng_info;
8135
8136 const char
8137 *value;
8138
8139 int
8140 have_mng_structure;
8141
8142 unsigned int
8143 logging;
8144
8145 /*
8146 Open image file.
8147 */
8148 assert(image_info != (const ImageInfo *) NULL);
8149 assert(image_info->signature == MagickSignature);
8150 assert(image != (Image *) NULL);
8151 assert(image->signature == MagickSignature);
8152 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8153 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
8154 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8155 if (status == MagickFalse)
8156 return(MagickFalse);
8157 /*
8158 Allocate a MngInfo structure.
8159 */
8160 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00008161 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00008162 if (mng_info == (MngInfo *) NULL)
8163 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8164 /*
8165 Initialize members of the MngInfo structure.
8166 */
8167 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8168 mng_info->image=image;
8169 have_mng_structure=MagickTrue;
8170
8171 /* See if user has requested a specific PNG subformat */
8172
8173 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8174 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8175 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8176
8177 if (mng_info->write_png8)
8178 {
glennrp9c1eb072010-06-06 22:19:15 +00008179 mng_info->write_png_colortype = /* 3 */ 4;
8180 mng_info->write_png_depth = 8;
8181 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00008182#if 0 /* this does not work */
glennrp9c1eb072010-06-06 22:19:15 +00008183 if (image->matte == MagickTrue)
8184 (void) SetImageType(image,PaletteMatteType);
8185 else
8186 (void) SetImageType(image,PaletteType);
8187 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008188#endif
8189 }
8190
8191 if (mng_info->write_png24)
8192 {
glennrp9c1eb072010-06-06 22:19:15 +00008193 mng_info->write_png_colortype = /* 2 */ 3;
8194 mng_info->write_png_depth = 8;
8195 image->depth = 8;
8196 if (image->matte == MagickTrue)
8197 (void) SetImageType(image,TrueColorMatteType);
8198 else
8199 (void) SetImageType(image,TrueColorType);
8200 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008201 }
8202
8203 if (mng_info->write_png32)
8204 {
glennrp9c1eb072010-06-06 22:19:15 +00008205 mng_info->write_png_colortype = /* 6 */ 7;
8206 mng_info->write_png_depth = 8;
8207 image->depth = 8;
8208 if (image->matte == MagickTrue)
8209 (void) SetImageType(image,TrueColorMatteType);
8210 else
8211 (void) SetImageType(image,TrueColorType);
8212 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008213 }
8214
8215 value=GetImageOption(image_info,"png:bit-depth");
8216 if (value != (char *) NULL)
8217 {
8218 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008219 mng_info->write_png_depth = 1;
cristy3ed852e2009-09-05 21:47:34 +00008220 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008221 mng_info->write_png_depth = 2;
cristy3ed852e2009-09-05 21:47:34 +00008222 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008223 mng_info->write_png_depth = 4;
cristy3ed852e2009-09-05 21:47:34 +00008224 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008225 mng_info->write_png_depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00008226 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008227 mng_info->write_png_depth = 16;
cristy3ed852e2009-09-05 21:47:34 +00008228 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8230 "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +00008231 }
8232 value=GetImageOption(image_info,"png:color-type");
8233 if (value != (char *) NULL)
8234 {
8235 /* We must store colortype+1 because 0 is a valid colortype */
8236 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008237 mng_info->write_png_colortype = 1;
cristy3ed852e2009-09-05 21:47:34 +00008238 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008239 mng_info->write_png_colortype = 3;
cristy3ed852e2009-09-05 21:47:34 +00008240 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008241 mng_info->write_png_colortype = 4;
cristy3ed852e2009-09-05 21:47:34 +00008242 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008243 mng_info->write_png_colortype = 5;
cristy3ed852e2009-09-05 21:47:34 +00008244 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008245 mng_info->write_png_colortype = 7;
cristy3ed852e2009-09-05 21:47:34 +00008246 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8248 "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +00008249 }
8250
8251 status=WriteOnePNGImage(mng_info,image_info,image);
8252
8253 (void) CloseBlob(image);
8254
8255 MngInfoFreeStruct(mng_info,&have_mng_structure);
8256 if (logging != MagickFalse)
8257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8258 return(status);
8259}
8260
8261#if defined(JNG_SUPPORTED)
8262
8263/* Write one JNG image */
8264static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8265 const ImageInfo *image_info,Image *image)
8266{
8267 Image
8268 *jpeg_image;
8269
8270 ImageInfo
8271 *jpeg_image_info;
8272
8273 MagickBooleanType
8274 status;
8275
8276 size_t
8277 length;
8278
8279 unsigned char
8280 *blob,
8281 chunk[80],
8282 *p;
8283
8284 unsigned int
8285 jng_alpha_compression_method,
8286 jng_alpha_sample_depth,
8287 jng_color_type,
8288 logging,
8289 transparent;
8290
cristybb503372010-05-27 20:51:26 +00008291 size_t
cristy3ed852e2009-09-05 21:47:34 +00008292 jng_quality;
8293
8294 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8295 " enter WriteOneJNGImage()");
8296
8297 blob=(unsigned char *) NULL;
8298 jpeg_image=(Image *) NULL;
8299 jpeg_image_info=(ImageInfo *) NULL;
8300
8301 status=MagickTrue;
8302 transparent=image_info->type==GrayscaleMatteType ||
8303 image_info->type==TrueColorMatteType;
8304 jng_color_type=10;
8305 jng_alpha_sample_depth=0;
8306 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8307 jng_alpha_compression_method=0;
8308
8309 if (image->matte != MagickFalse)
8310 {
8311 /* if any pixels are transparent */
8312 transparent=MagickTrue;
8313 if (image_info->compression==JPEGCompression)
8314 jng_alpha_compression_method=8;
8315 }
8316
8317 if (transparent)
8318 {
8319 jng_color_type=14;
8320 /* Create JPEG blob, image, and image_info */
8321 if (logging != MagickFalse)
8322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8323 " Creating jpeg_image_info for opacity.");
8324 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8325 if (jpeg_image_info == (ImageInfo *) NULL)
8326 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8327 if (logging != MagickFalse)
8328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8329 " Creating jpeg_image.");
8330 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8331 if (jpeg_image == (Image *) NULL)
8332 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8333 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8334 status=SeparateImageChannel(jpeg_image,OpacityChannel);
8335 status=NegateImage(jpeg_image,MagickFalse);
8336 jpeg_image->matte=MagickFalse;
8337 if (jng_quality >= 1000)
8338 jpeg_image_info->quality=jng_quality/1000;
8339 else
8340 jpeg_image_info->quality=jng_quality;
8341 jpeg_image_info->type=GrayscaleType;
8342 (void) SetImageType(jpeg_image,GrayscaleType);
8343 (void) AcquireUniqueFilename(jpeg_image->filename);
8344 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8345 "%s",jpeg_image->filename);
8346 }
8347
8348 /* To do: check bit depth of PNG alpha channel */
8349
8350 /* Check if image is grayscale. */
8351 if (image_info->type != TrueColorMatteType && image_info->type !=
8352 TrueColorType && ImageIsGray(image))
8353 jng_color_type-=2;
8354
8355 if (transparent)
8356 {
8357 if (jng_alpha_compression_method==0)
8358 {
8359 const char
8360 *value;
8361
8362 /* Encode opacity as a grayscale PNG blob */
8363 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8364 &image->exception);
8365 if (logging != MagickFalse)
8366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8367 " Creating PNG blob.");
8368 length=0;
8369
8370 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8371 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8372 jpeg_image_info->interlace=NoInterlace;
8373
8374 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8375 &image->exception);
8376
8377 /* Retrieve sample depth used */
8378 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8379 if (value != (char *) NULL)
8380 jng_alpha_sample_depth= (unsigned int) value[0];
8381 }
8382 else
8383 {
8384 /* Encode opacity as a grayscale JPEG blob */
8385
8386 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8387 &image->exception);
8388
8389 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8390 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8391 jpeg_image_info->interlace=NoInterlace;
8392 if (logging != MagickFalse)
8393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8394 " Creating blob.");
8395 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +00008396 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00008397 jng_alpha_sample_depth=8;
8398 if (logging != MagickFalse)
8399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008400 " Successfully read jpeg_image into a blob, length=%.20g.",
8401 (double) length);
cristy3ed852e2009-09-05 21:47:34 +00008402
8403 }
8404 /* Destroy JPEG image and image_info */
8405 jpeg_image=DestroyImage(jpeg_image);
8406 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8407 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8408 }
8409
8410 /* Write JHDR chunk */
8411 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
8412 PNGType(chunk,mng_JHDR);
8413 LogPNGChunk((int) logging,mng_JHDR,16L);
8414 PNGLong(chunk+4,image->columns);
8415 PNGLong(chunk+8,image->rows);
8416 chunk[12]=jng_color_type;
8417 chunk[13]=8; /* sample depth */
8418 chunk[14]=8; /*jng_image_compression_method */
8419 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8420 chunk[16]=jng_alpha_sample_depth;
8421 chunk[17]=jng_alpha_compression_method;
8422 chunk[18]=0; /*jng_alpha_filter_method */
8423 chunk[19]=0; /*jng_alpha_interlace_method */
8424 (void) WriteBlob(image,20,chunk);
8425 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8426 if (logging != MagickFalse)
8427 {
8428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00008429 " JNG width:%15lu",(unsigned long) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00008430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00008431 " JNG height:%14lu",(unsigned long) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00008432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8433 " JNG color type:%10d",jng_color_type);
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " JNG sample depth:%8d",8);
8436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8437 " JNG compression:%9d",8);
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " JNG interlace:%11d",0);
8440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8441 " JNG alpha depth:%9d",jng_alpha_sample_depth);
8442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8443 " JNG alpha compression:%3d",jng_alpha_compression_method);
8444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8445 " JNG alpha filter:%8d",0);
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " JNG alpha interlace:%5d",0);
8448 }
8449
8450 /* Write any JNG-chunk-b profiles */
8451 (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8452
8453 /*
8454 Write leading ancillary chunks
8455 */
8456
8457 if (transparent)
8458 {
8459 /*
8460 Write JNG bKGD chunk
8461 */
8462
8463 unsigned char
8464 blue,
8465 green,
8466 red;
8467
cristybb503372010-05-27 20:51:26 +00008468 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008469 num_bytes;
8470
8471 if (jng_color_type == 8 || jng_color_type == 12)
8472 num_bytes=6L;
8473 else
8474 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +00008475 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +00008476 PNGType(chunk,mng_bKGD);
cristybb503372010-05-27 20:51:26 +00008477 LogPNGChunk((int) logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +00008478 red=ScaleQuantumToChar(image->background_color.red);
8479 green=ScaleQuantumToChar(image->background_color.green);
8480 blue=ScaleQuantumToChar(image->background_color.blue);
8481 *(chunk+4)=0;
8482 *(chunk+5)=red;
8483 *(chunk+6)=0;
8484 *(chunk+7)=green;
8485 *(chunk+8)=0;
8486 *(chunk+9)=blue;
8487 (void) WriteBlob(image,(size_t) num_bytes,chunk);
8488 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8489 }
8490
8491 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8492 {
8493 /*
8494 Write JNG sRGB chunk
8495 */
8496 (void) WriteBlobMSBULong(image,1L);
8497 PNGType(chunk,mng_sRGB);
8498 LogPNGChunk((int) logging,mng_sRGB,1L);
8499 if (image->rendering_intent != UndefinedIntent)
8500 chunk[4]=(unsigned char) (image->rendering_intent-1);
8501 else
8502 chunk[4]=(unsigned char) (PerceptualIntent-1);
8503 (void) WriteBlob(image,5,chunk);
8504 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8505 }
8506 else
8507 {
8508 if (image->gamma != 0.0)
8509 {
8510 /*
8511 Write JNG gAMA chunk
8512 */
8513 (void) WriteBlobMSBULong(image,4L);
8514 PNGType(chunk,mng_gAMA);
8515 LogPNGChunk((int) logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +00008516 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008517 (void) WriteBlob(image,8,chunk);
8518 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8519 }
8520 if ((mng_info->equal_chrms == MagickFalse) &&
8521 (image->chromaticity.red_primary.x != 0.0))
8522 {
8523 PrimaryInfo
8524 primary;
8525
8526 /*
8527 Write JNG cHRM chunk
8528 */
8529 (void) WriteBlobMSBULong(image,32L);
8530 PNGType(chunk,mng_cHRM);
8531 LogPNGChunk((int) logging,mng_cHRM,32L);
8532 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +00008533 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
8534 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008535 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +00008536 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
8537 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008538 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +00008539 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
8540 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008541 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +00008542 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
8543 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008544 (void) WriteBlob(image,36,chunk);
8545 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8546 }
8547 }
8548 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8549 {
8550 /*
8551 Write JNG pHYs chunk
8552 */
8553 (void) WriteBlobMSBULong(image,9L);
8554 PNGType(chunk,mng_pHYs);
8555 LogPNGChunk((int) logging,mng_pHYs,9L);
8556 if (image->units == PixelsPerInchResolution)
8557 {
cristy35ef8242010-06-03 16:24:13 +00008558 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00008559 (image->x_resolution*100.0/2.54+0.5));
cristy35ef8242010-06-03 16:24:13 +00008560 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00008561 (image->y_resolution*100.0/2.54+0.5));
8562 chunk[12]=1;
8563 }
8564 else
8565 {
8566 if (image->units == PixelsPerCentimeterResolution)
8567 {
cristy35ef8242010-06-03 16:24:13 +00008568 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00008569 (image->x_resolution*100.0+0.5));
cristy35ef8242010-06-03 16:24:13 +00008570 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00008571 (image->y_resolution*100.0+0.5));
8572 chunk[12]=1;
8573 }
8574 else
8575 {
cristy35ef8242010-06-03 16:24:13 +00008576 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
8577 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008578 chunk[12]=0;
8579 }
8580 }
8581 (void) WriteBlob(image,13,chunk);
8582 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8583 }
8584
8585 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8586 {
8587 /*
8588 Write JNG oFFs chunk
8589 */
8590 (void) WriteBlobMSBULong(image,9L);
8591 PNGType(chunk,mng_oFFs);
8592 LogPNGChunk((int) logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +00008593 PNGsLong(chunk+4,(ssize_t) (image->page.x));
8594 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +00008595 chunk[12]=0;
8596 (void) WriteBlob(image,13,chunk);
8597 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8598 }
8599 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8600 {
8601 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8602 PNGType(chunk,mng_vpAg);
8603 LogPNGChunk((int) logging,mng_vpAg,9L);
8604 PNGLong(chunk+4,(png_uint_32) image->page.width);
8605 PNGLong(chunk+8,(png_uint_32) image->page.height);
8606 chunk[12]=0; /* unit = pixels */
8607 (void) WriteBlob(image,13,chunk);
8608 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8609 }
8610
8611
8612 if (transparent)
8613 {
8614 if (jng_alpha_compression_method==0)
8615 {
cristybb503372010-05-27 20:51:26 +00008616 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008617 i;
8618
cristybb503372010-05-27 20:51:26 +00008619 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008620 len;
8621
8622 /* Write IDAT chunk header */
8623 if (logging != MagickFalse)
8624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008625 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +00008626 length);
cristy3ed852e2009-09-05 21:47:34 +00008627
8628 /* Copy IDAT chunks */
8629 len=0;
8630 p=blob+8;
cristybb503372010-05-27 20:51:26 +00008631 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +00008632 {
8633 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8634 p+=4;
8635 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8636 {
8637 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +00008638 (void) WriteBlobMSBULong(image,(size_t) len);
8639 LogPNGChunk((int) logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +00008640 (void) WriteBlob(image,(size_t) len+4,p);
8641 (void) WriteBlobMSBULong(image,
8642 crc32(0,p,(uInt) len+4));
8643 }
8644 else
8645 {
8646 if (logging != MagickFalse)
8647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008648 " Skipping %c%c%c%c chunk, length=%.20g.",
8649 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +00008650 }
8651 p+=(8+len);
8652 }
8653 }
8654 else
8655 {
8656 /* Write JDAA chunk header */
8657 if (logging != MagickFalse)
8658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008659 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +00008660 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00008661 PNGType(chunk,mng_JDAA);
8662 LogPNGChunk((int) logging,mng_JDAA,length);
8663 /* Write JDAT chunk(s) data */
8664 (void) WriteBlob(image,4,chunk);
8665 (void) WriteBlob(image,length,blob);
8666 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8667 (uInt) length));
8668 }
8669 blob=(unsigned char *) RelinquishMagickMemory(blob);
8670 }
8671
8672 /* Encode image as a JPEG blob */
8673 if (logging != MagickFalse)
8674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8675 " Creating jpeg_image_info.");
8676 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8677 if (jpeg_image_info == (ImageInfo *) NULL)
8678 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8679
8680 if (logging != MagickFalse)
8681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8682 " Creating jpeg_image.");
8683
8684 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8685 if (jpeg_image == (Image *) NULL)
8686 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8687 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8688
8689 (void) AcquireUniqueFilename(jpeg_image->filename);
8690 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8691 jpeg_image->filename);
8692
8693 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8694 &image->exception);
8695
8696 if (logging != MagickFalse)
8697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008698 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
8699 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00008700
8701 if (jng_color_type == 8 || jng_color_type == 12)
8702 jpeg_image_info->type=GrayscaleType;
8703 jpeg_image_info->quality=jng_quality % 1000;
8704 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8705 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8706 if (logging != MagickFalse)
8707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8708 " Creating blob.");
8709 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8710 if (logging != MagickFalse)
8711 {
8712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008713 " Successfully read jpeg_image into a blob, length=%.20g.",
8714 (double) length);
cristy3ed852e2009-09-05 21:47:34 +00008715
8716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008717 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +00008718 }
8719 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +00008720 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00008721 PNGType(chunk,mng_JDAT);
8722 LogPNGChunk((int) logging,mng_JDAT,length);
8723 (void) WriteBlob(image,4,chunk);
8724 (void) WriteBlob(image,length,blob);
8725 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8726
8727 jpeg_image=DestroyImage(jpeg_image);
8728 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8729 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8730 blob=(unsigned char *) RelinquishMagickMemory(blob);
8731
8732 /* Write any JNG-chunk-e profiles */
8733 (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8734
8735 /* Write IEND chunk */
8736 (void) WriteBlobMSBULong(image,0L);
8737 PNGType(chunk,mng_IEND);
8738 LogPNGChunk((int) logging,mng_IEND,0);
8739 (void) WriteBlob(image,4,chunk);
8740 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8741
8742 if (logging != MagickFalse)
8743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8744 " exit WriteOneJNGImage()");
8745 return(status);
8746}
8747
8748
8749/*
8750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8751% %
8752% %
8753% %
8754% W r i t e J N G I m a g e %
8755% %
8756% %
8757% %
8758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8759%
8760% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8761%
8762% JNG support written by Glenn Randers-Pehrson, glennrp@image...
8763%
8764% The format of the WriteJNGImage method is:
8765%
8766% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8767%
8768% A description of each parameter follows:
8769%
8770% o image_info: the image info.
8771%
8772% o image: The image.
8773%
8774%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8775*/
8776static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8777{
8778 MagickBooleanType
8779 status;
8780
8781 MngInfo
8782 *mng_info;
8783
8784 int
8785 have_mng_structure;
8786
8787 unsigned int
8788 logging;
8789
8790 /*
8791 Open image file.
8792 */
8793 assert(image_info != (const ImageInfo *) NULL);
8794 assert(image_info->signature == MagickSignature);
8795 assert(image != (Image *) NULL);
8796 assert(image->signature == MagickSignature);
8797 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8798 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8799 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8800 if (status == MagickFalse)
8801 return(status);
8802
8803 /*
8804 Allocate a MngInfo structure.
8805 */
8806 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00008807 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00008808 if (mng_info == (MngInfo *) NULL)
8809 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8810 /*
8811 Initialize members of the MngInfo structure.
8812 */
8813 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8814 mng_info->image=image;
8815 have_mng_structure=MagickTrue;
8816
8817 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8818
8819 status=WriteOneJNGImage(mng_info,image_info,image);
8820 (void) CloseBlob(image);
8821
8822 (void) CatchImageException(image);
8823 MngInfoFreeStruct(mng_info,&have_mng_structure);
8824 if (logging != MagickFalse)
8825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8826 return(status);
8827}
8828#endif
8829
8830
8831
8832static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8833{
8834 const char
8835 *option;
8836
8837 Image
8838 *next_image;
8839
8840 MagickBooleanType
8841 status;
8842
8843 MngInfo
8844 *mng_info;
8845
8846 int
8847 have_mng_structure,
8848 image_count,
8849 need_iterations,
8850 need_matte;
8851
8852 volatile int
8853#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8854 defined(PNG_MNG_FEATURES_SUPPORTED)
8855 need_local_plte,
8856#endif
8857 all_images_are_gray,
8858 logging,
8859 need_defi,
8860 optimize,
8861 use_global_plte;
8862
cristybb503372010-05-27 20:51:26 +00008863 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008864 i;
8865
8866 unsigned char
8867 chunk[800];
8868
8869 volatile unsigned int
8870 write_jng,
8871 write_mng;
8872
cristybb503372010-05-27 20:51:26 +00008873 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00008874 scene;
8875
cristybb503372010-05-27 20:51:26 +00008876 size_t
cristy3ed852e2009-09-05 21:47:34 +00008877 final_delay=0,
8878 initial_delay;
8879
glennrpd5045b42010-03-24 12:40:35 +00008880#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00008881 if (image_info->verbose)
8882 printf("Your PNG library (libpng-%s) is rather old.\n",
8883 PNG_LIBPNG_VER_STRING);
8884#endif
8885
8886 /*
8887 Open image file.
8888 */
8889 assert(image_info != (const ImageInfo *) NULL);
8890 assert(image_info->signature == MagickSignature);
8891 assert(image != (Image *) NULL);
8892 assert(image->signature == MagickSignature);
8893 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8894 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8895 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8896 if (status == MagickFalse)
8897 return(status);
8898
8899 /*
8900 Allocate a MngInfo structure.
8901 */
8902 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00008903 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00008904 if (mng_info == (MngInfo *) NULL)
8905 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8906 /*
8907 Initialize members of the MngInfo structure.
8908 */
8909 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8910 mng_info->image=image;
8911 have_mng_structure=MagickTrue;
8912 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
8913
8914 /*
8915 * See if user has requested a specific PNG subformat to be used
8916 * for all of the PNGs in the MNG being written, e.g.,
8917 *
8918 * convert *.png png8:animation.mng
8919 *
8920 * To do: check -define png:bit_depth and png:color_type as well,
8921 * or perhaps use mng:bit_depth and mng:color_type instead for
8922 * global settings.
8923 */
8924
8925 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8926 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8927 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8928
8929 write_jng=MagickFalse;
8930 if (image_info->compression == JPEGCompression)
8931 write_jng=MagickTrue;
8932
8933 mng_info->adjoin=image_info->adjoin &&
8934 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
8935
8936 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8937 optimize=MagickFalse;
8938 else
8939 optimize=(image_info->type == OptimizeType || image_info->type ==
8940 UndefinedType);
8941
8942 if (logging != MagickFalse)
8943 {
8944 /* Log some info about the input */
8945 Image
8946 *p;
8947
8948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8949 " Checking input image(s)");
8950 if (optimize)
8951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8952 " Optimize: TRUE");
8953 else
8954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8955 " Optimize: FALSE");
8956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008957 " Image_info depth: %.20g",(double) image_info->depth);
cristy3ed852e2009-09-05 21:47:34 +00008958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8959 " Type: %d",image_info->type);
8960
8961 scene=0;
8962 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8963 {
8964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008965 " Scene: %.20g",(double) scene++);
cristy3ed852e2009-09-05 21:47:34 +00008966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008967 " Image depth: %.20g",(double) p->depth);
cristy3ed852e2009-09-05 21:47:34 +00008968 if (p->matte)
8969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8970 " Matte: True");
8971 else
8972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8973 " Matte: False");
8974 if (p->storage_class == PseudoClass)
8975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8976 " Storage class: PseudoClass");
8977 else
8978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8979 " Storage class: DirectClass");
8980 if (p->colors)
8981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008982 " Number of colors: %.20g",(double) p->colors);
cristy3ed852e2009-09-05 21:47:34 +00008983 else
8984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8985 " Number of colors: unspecified");
8986 if (mng_info->adjoin == MagickFalse)
8987 break;
8988 }
8989 }
8990
8991 /*
8992 Sometimes we get PseudoClass images whose RGB values don't match
8993 the colors in the colormap. This code syncs the RGB values.
8994 */
8995 {
8996 Image
8997 *p;
8998
8999 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9000 {
9001 if (p->taint && p->storage_class == PseudoClass)
9002 (void) SyncImage(p);
9003 if (mng_info->adjoin == MagickFalse)
9004 break;
9005 }
9006 }
9007
9008#ifdef PNG_BUILD_PALETTE
9009 if (optimize)
9010 {
9011 /*
9012 Sometimes we get DirectClass images that have 256 colors or fewer.
9013 This code will convert them to PseudoClass and build a colormap.
9014 */
9015 Image
9016 *p;
9017
9018 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9019 {
9020 if (p->storage_class != PseudoClass)
9021 {
9022 p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
9023 if (p->colors <= 256)
9024 {
9025 p->colors=0;
9026 if (p->matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009027 (void) SetImageType(p,PaletteMatteType);
cristy3ed852e2009-09-05 21:47:34 +00009028 else
glennrp9c1eb072010-06-06 22:19:15 +00009029 (void) SetImageType(p,PaletteType);
cristy3ed852e2009-09-05 21:47:34 +00009030 }
9031 }
9032 if (mng_info->adjoin == MagickFalse)
9033 break;
9034 }
9035 }
9036#endif
9037
9038 use_global_plte=MagickFalse;
9039 all_images_are_gray=MagickFalse;
9040#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9041 need_local_plte=MagickTrue;
9042#endif
9043 need_defi=MagickFalse;
9044 need_matte=MagickFalse;
9045 mng_info->framing_mode=1;
9046 mng_info->old_framing_mode=1;
9047
9048 if (write_mng)
9049 if (image_info->page != (char *) NULL)
9050 {
9051 /*
9052 Determine image bounding box.
9053 */
9054 SetGeometry(image,&mng_info->page);
9055 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
9056 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
9057 }
9058 if (write_mng)
9059 {
9060 unsigned int
9061 need_geom;
9062
9063 unsigned short
9064 red,
9065 green,
9066 blue;
9067
9068 mng_info->page=image->page;
9069 need_geom=MagickTrue;
9070 if (mng_info->page.width || mng_info->page.height)
9071 need_geom=MagickFalse;
9072 /*
9073 Check all the scenes.
9074 */
9075 initial_delay=image->delay;
9076 need_iterations=MagickFalse;
9077 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
9078 mng_info->equal_physs=MagickTrue,
9079 mng_info->equal_gammas=MagickTrue;
9080 mng_info->equal_srgbs=MagickTrue;
9081 mng_info->equal_backgrounds=MagickTrue;
9082 image_count=0;
9083#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9084 defined(PNG_MNG_FEATURES_SUPPORTED)
9085 all_images_are_gray=MagickTrue;
9086 mng_info->equal_palettes=MagickFalse;
9087 need_local_plte=MagickFalse;
9088#endif
9089 for (next_image=image; next_image != (Image *) NULL; )
9090 {
9091 if (need_geom)
9092 {
9093 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
9094 mng_info->page.width=next_image->columns+next_image->page.x;
9095 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
9096 mng_info->page.height=next_image->rows+next_image->page.y;
9097 }
9098 if (next_image->page.x || next_image->page.y)
9099 need_defi=MagickTrue;
9100 if (next_image->matte)
9101 need_matte=MagickTrue;
9102 if ((int) next_image->dispose >= BackgroundDispose)
9103 if (next_image->matte || next_image->page.x || next_image->page.y ||
9104 ((next_image->columns < mng_info->page.width) &&
9105 (next_image->rows < mng_info->page.height)))
9106 mng_info->need_fram=MagickTrue;
9107 if (next_image->iterations)
9108 need_iterations=MagickTrue;
9109 final_delay=next_image->delay;
9110 if (final_delay != initial_delay || final_delay > 1UL*
9111 next_image->ticks_per_second)
9112 mng_info->need_fram=1;
9113#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9114 defined(PNG_MNG_FEATURES_SUPPORTED)
9115 /*
9116 check for global palette possibility.
9117 */
9118 if (image->matte != MagickFalse)
9119 need_local_plte=MagickTrue;
9120 if (need_local_plte == 0)
9121 {
9122 if (ImageIsGray(image) == MagickFalse)
9123 all_images_are_gray=MagickFalse;
9124 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
9125 if (use_global_plte == 0)
9126 use_global_plte=mng_info->equal_palettes;
9127 need_local_plte=!mng_info->equal_palettes;
9128 }
9129#endif
9130 if (GetNextImageInList(next_image) != (Image *) NULL)
9131 {
9132 if (next_image->background_color.red !=
9133 next_image->next->background_color.red ||
9134 next_image->background_color.green !=
9135 next_image->next->background_color.green ||
9136 next_image->background_color.blue !=
9137 next_image->next->background_color.blue)
9138 mng_info->equal_backgrounds=MagickFalse;
9139 if (next_image->gamma != next_image->next->gamma)
9140 mng_info->equal_gammas=MagickFalse;
9141 if (next_image->rendering_intent !=
9142 next_image->next->rendering_intent)
9143 mng_info->equal_srgbs=MagickFalse;
9144 if ((next_image->units != next_image->next->units) ||
9145 (next_image->x_resolution != next_image->next->x_resolution) ||
9146 (next_image->y_resolution != next_image->next->y_resolution))
9147 mng_info->equal_physs=MagickFalse;
9148 if (mng_info->equal_chrms)
9149 {
9150 if (next_image->chromaticity.red_primary.x !=
9151 next_image->next->chromaticity.red_primary.x ||
9152 next_image->chromaticity.red_primary.y !=
9153 next_image->next->chromaticity.red_primary.y ||
9154 next_image->chromaticity.green_primary.x !=
9155 next_image->next->chromaticity.green_primary.x ||
9156 next_image->chromaticity.green_primary.y !=
9157 next_image->next->chromaticity.green_primary.y ||
9158 next_image->chromaticity.blue_primary.x !=
9159 next_image->next->chromaticity.blue_primary.x ||
9160 next_image->chromaticity.blue_primary.y !=
9161 next_image->next->chromaticity.blue_primary.y ||
9162 next_image->chromaticity.white_point.x !=
9163 next_image->next->chromaticity.white_point.x ||
9164 next_image->chromaticity.white_point.y !=
9165 next_image->next->chromaticity.white_point.y)
9166 mng_info->equal_chrms=MagickFalse;
9167 }
9168 }
9169 image_count++;
9170 next_image=GetNextImageInList(next_image);
9171 }
9172 if (image_count < 2)
9173 {
9174 mng_info->equal_backgrounds=MagickFalse;
9175 mng_info->equal_chrms=MagickFalse;
9176 mng_info->equal_gammas=MagickFalse;
9177 mng_info->equal_srgbs=MagickFalse;
9178 mng_info->equal_physs=MagickFalse;
9179 use_global_plte=MagickFalse;
9180#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9181 need_local_plte=MagickTrue;
9182#endif
9183 need_iterations=MagickFalse;
9184 }
9185 if (mng_info->need_fram == MagickFalse)
9186 {
9187 /*
9188 Only certain framing rates 100/n are exactly representable without
9189 the FRAM chunk but we'll allow some slop in VLC files
9190 */
9191 if (final_delay == 0)
9192 {
9193 if (need_iterations != MagickFalse)
9194 {
9195 /*
9196 It's probably a GIF with loop; don't run it *too* fast.
9197 */
9198 final_delay=10;
9199 (void) ThrowMagickException(&image->exception,
9200 GetMagickModule(),CoderError,
9201 "input has zero delay between all frames; assuming 10 cs",
9202 "`%s'","");
9203 }
9204 else
9205 mng_info->ticks_per_second=0;
9206 }
9207 if (final_delay != 0)
9208 mng_info->ticks_per_second=1UL*image->ticks_per_second/final_delay;
9209 if (final_delay > 50)
9210 mng_info->ticks_per_second=2;
9211 if (final_delay > 75)
9212 mng_info->ticks_per_second=1;
9213 if (final_delay > 125)
9214 mng_info->need_fram=MagickTrue;
9215 if (need_defi && final_delay > 2 && (final_delay != 4) &&
9216 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
9217 (final_delay != 25) && (final_delay != 50) && (final_delay !=
9218 1UL*image->ticks_per_second))
9219 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
9220 }
9221 if (mng_info->need_fram != MagickFalse)
9222 mng_info->ticks_per_second=1UL*image->ticks_per_second;
9223 /*
9224 If pseudocolor, we should also check to see if all the
9225 palettes are identical and write a global PLTE if they are.
9226 ../glennrp Feb 99.
9227 */
9228 /*
9229 Write the MNG version 1.0 signature and MHDR chunk.
9230 */
9231 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9232 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
9233 PNGType(chunk,mng_MHDR);
9234 LogPNGChunk((int) logging,mng_MHDR,28L);
9235 PNGLong(chunk+4,mng_info->page.width);
9236 PNGLong(chunk+8,mng_info->page.height);
9237 PNGLong(chunk+12,mng_info->ticks_per_second);
9238 PNGLong(chunk+16,0L); /* layer count=unknown */
9239 PNGLong(chunk+20,0L); /* frame count=unknown */
9240 PNGLong(chunk+24,0L); /* play time=unknown */
9241 if (write_jng)
9242 {
9243 if (need_matte)
9244 {
9245 if (need_defi || mng_info->need_fram || use_global_plte)
9246 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
9247 else
9248 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
9249 }
9250 else
9251 {
9252 if (need_defi || mng_info->need_fram || use_global_plte)
9253 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
9254 else
9255 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
9256 }
9257 }
9258 else
9259 {
9260 if (need_matte)
9261 {
9262 if (need_defi || mng_info->need_fram || use_global_plte)
9263 PNGLong(chunk+28,11L); /* simplicity=LC */
9264 else
9265 PNGLong(chunk+28,9L); /* simplicity=VLC */
9266 }
9267 else
9268 {
9269 if (need_defi || mng_info->need_fram || use_global_plte)
9270 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
9271 else
9272 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
9273 }
9274 }
9275 (void) WriteBlob(image,32,chunk);
9276 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9277 option=GetImageOption(image_info,"mng:need-cacheoff");
9278 if (option != (const char *) NULL)
9279 {
9280 size_t
9281 length;
9282
9283 /*
9284 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9285 */
9286 PNGType(chunk,mng_nEED);
9287 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +00009288 (void) WriteBlobMSBULong(image,(size_t) length);
9289 LogPNGChunk((int) logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00009290 length+=4;
9291 (void) WriteBlob(image,length,chunk);
9292 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9293 }
9294 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9295 (GetNextImageInList(image) != (Image *) NULL) &&
9296 (image->iterations != 1))
9297 {
9298 /*
9299 Write MNG TERM chunk
9300 */
9301 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9302 PNGType(chunk,mng_TERM);
9303 LogPNGChunk((int) logging,mng_TERM,10L);
9304 chunk[4]=3; /* repeat animation */
9305 chunk[5]=0; /* show last frame when done */
9306 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9307 final_delay/MagickMax(image->ticks_per_second,1)));
9308 if (image->iterations == 0)
9309 PNGLong(chunk+10,PNG_UINT_31_MAX);
9310 else
9311 PNGLong(chunk+10,(png_uint_32) image->iterations);
9312 if (logging != MagickFalse)
9313 {
9314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009315 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
9316 final_delay/MagickMax(image->ticks_per_second,1)));
cristy3ed852e2009-09-05 21:47:34 +00009317 if (image->iterations == 0)
9318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009319 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
cristy3ed852e2009-09-05 21:47:34 +00009320 else
9321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009322 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00009323 }
9324 (void) WriteBlob(image,14,chunk);
9325 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9326 }
9327 /*
9328 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9329 */
9330 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9331 mng_info->equal_srgbs)
9332 {
9333 /*
9334 Write MNG sRGB chunk
9335 */
9336 (void) WriteBlobMSBULong(image,1L);
9337 PNGType(chunk,mng_sRGB);
9338 LogPNGChunk((int) logging,mng_sRGB,1L);
9339 if (image->rendering_intent != UndefinedIntent)
9340 chunk[4]=(unsigned char) (image->rendering_intent-1);
9341 else
9342 chunk[4]=(unsigned char) (PerceptualIntent-1);
9343 (void) WriteBlob(image,5,chunk);
9344 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9345 mng_info->have_write_global_srgb=MagickTrue;
9346 }
9347 else
9348 {
9349 if (image->gamma && mng_info->equal_gammas)
9350 {
9351 /*
9352 Write MNG gAMA chunk
9353 */
9354 (void) WriteBlobMSBULong(image,4L);
9355 PNGType(chunk,mng_gAMA);
9356 LogPNGChunk((int) logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +00009357 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009358 (void) WriteBlob(image,8,chunk);
9359 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9360 mng_info->have_write_global_gama=MagickTrue;
9361 }
9362 if (mng_info->equal_chrms)
9363 {
9364 PrimaryInfo
9365 primary;
9366
9367 /*
9368 Write MNG cHRM chunk
9369 */
9370 (void) WriteBlobMSBULong(image,32L);
9371 PNGType(chunk,mng_cHRM);
9372 LogPNGChunk((int) logging,mng_cHRM,32L);
9373 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +00009374 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
9375 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009376 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +00009377 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
9378 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009379 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +00009380 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
9381 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009382 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +00009383 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
9384 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009385 (void) WriteBlob(image,36,chunk);
9386 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9387 mng_info->have_write_global_chrm=MagickTrue;
9388 }
9389 }
9390 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9391 {
9392 /*
9393 Write MNG pHYs chunk
9394 */
9395 (void) WriteBlobMSBULong(image,9L);
9396 PNGType(chunk,mng_pHYs);
9397 LogPNGChunk((int) logging,mng_pHYs,9L);
9398 if (image->units == PixelsPerInchResolution)
9399 {
cristy35ef8242010-06-03 16:24:13 +00009400 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00009401 (image->x_resolution*100.0/2.54+0.5));
cristy35ef8242010-06-03 16:24:13 +00009402 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00009403 (image->y_resolution*100.0/2.54+0.5));
9404 chunk[12]=1;
9405 }
9406 else
9407 {
9408 if (image->units == PixelsPerCentimeterResolution)
9409 {
cristy35ef8242010-06-03 16:24:13 +00009410 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00009411 (image->x_resolution*100.0+0.5));
cristy35ef8242010-06-03 16:24:13 +00009412 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00009413 (image->y_resolution*100.0+0.5));
9414 chunk[12]=1;
9415 }
9416 else
9417 {
cristy35ef8242010-06-03 16:24:13 +00009418 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
9419 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009420 chunk[12]=0;
9421 }
9422 }
9423 (void) WriteBlob(image,13,chunk);
9424 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9425 }
9426 /*
9427 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9428 or does not cover the entire frame.
9429 */
9430 if (write_mng && (image->matte || image->page.x > 0 ||
9431 image->page.y > 0 || (image->page.width &&
9432 (image->page.width+image->page.x < mng_info->page.width))
9433 || (image->page.height && (image->page.height+image->page.y
9434 < mng_info->page.height))))
9435 {
9436 (void) WriteBlobMSBULong(image,6L);
9437 PNGType(chunk,mng_BACK);
9438 LogPNGChunk((int) logging,mng_BACK,6L);
9439 red=ScaleQuantumToShort(image->background_color.red);
9440 green=ScaleQuantumToShort(image->background_color.green);
9441 blue=ScaleQuantumToShort(image->background_color.blue);
9442 PNGShort(chunk+4,red);
9443 PNGShort(chunk+6,green);
9444 PNGShort(chunk+8,blue);
9445 (void) WriteBlob(image,10,chunk);
9446 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9447 if (mng_info->equal_backgrounds)
9448 {
9449 (void) WriteBlobMSBULong(image,6L);
9450 PNGType(chunk,mng_bKGD);
9451 LogPNGChunk((int) logging,mng_bKGD,6L);
9452 (void) WriteBlob(image,10,chunk);
9453 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9454 }
9455 }
9456
9457#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9458 if ((need_local_plte == MagickFalse) &&
9459 (image->storage_class == PseudoClass) &&
9460 (all_images_are_gray == MagickFalse))
9461 {
cristybb503372010-05-27 20:51:26 +00009462 size_t
cristy3ed852e2009-09-05 21:47:34 +00009463 data_length;
9464
9465 /*
9466 Write MNG PLTE chunk
9467 */
9468 data_length=3*image->colors;
9469 (void) WriteBlobMSBULong(image,data_length);
9470 PNGType(chunk,mng_PLTE);
9471 LogPNGChunk((int) logging,mng_PLTE,data_length);
cristybb503372010-05-27 20:51:26 +00009472 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009473 {
9474 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9475 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9476 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9477 }
9478 (void) WriteBlob(image,data_length+4,chunk);
9479 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9480 mng_info->have_write_global_plte=MagickTrue;
9481 }
9482#endif
9483 }
9484 scene=0;
9485 mng_info->delay=0;
9486#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9487 defined(PNG_MNG_FEATURES_SUPPORTED)
9488 mng_info->equal_palettes=MagickFalse;
9489#endif
9490 do
9491 {
9492 if (mng_info->adjoin)
9493 {
9494#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9495 defined(PNG_MNG_FEATURES_SUPPORTED)
9496 /*
9497 If we aren't using a global palette for the entire MNG, check to
9498 see if we can use one for two or more consecutive images.
9499 */
9500 if (need_local_plte && use_global_plte && !all_images_are_gray)
9501 {
9502 if (mng_info->IsPalette)
9503 {
9504 /*
9505 When equal_palettes is true, this image has the same palette
9506 as the previous PseudoClass image
9507 */
9508 mng_info->have_write_global_plte=mng_info->equal_palettes;
9509 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9510 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9511 {
9512 /*
9513 Write MNG PLTE chunk
9514 */
cristybb503372010-05-27 20:51:26 +00009515 size_t
cristy3ed852e2009-09-05 21:47:34 +00009516 data_length;
9517
9518 data_length=3*image->colors;
9519 (void) WriteBlobMSBULong(image,data_length);
9520 PNGType(chunk,mng_PLTE);
9521 LogPNGChunk((int) logging,mng_PLTE,data_length);
cristybb503372010-05-27 20:51:26 +00009522 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009523 {
9524 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9525 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9526 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9527 }
9528 (void) WriteBlob(image,data_length+4,chunk);
9529 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9530 (uInt) (data_length+4)));
9531 mng_info->have_write_global_plte=MagickTrue;
9532 }
9533 }
9534 else
9535 mng_info->have_write_global_plte=MagickFalse;
9536 }
9537#endif
9538 if (need_defi)
9539 {
cristybb503372010-05-27 20:51:26 +00009540 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00009541 previous_x,
9542 previous_y;
9543
9544 if (scene)
9545 {
9546 previous_x=mng_info->page.x;
9547 previous_y=mng_info->page.y;
9548 }
9549 else
9550 {
9551 previous_x=0;
9552 previous_y=0;
9553 }
9554 mng_info->page=image->page;
9555 if ((mng_info->page.x != previous_x) ||
9556 (mng_info->page.y != previous_y))
9557 {
9558 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
9559 PNGType(chunk,mng_DEFI);
9560 LogPNGChunk((int) logging,mng_DEFI,12L);
9561 chunk[4]=0; /* object 0 MSB */
9562 chunk[5]=0; /* object 0 LSB */
9563 chunk[6]=0; /* visible */
9564 chunk[7]=0; /* abstract */
9565 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9566 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9567 (void) WriteBlob(image,16,chunk);
9568 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9569 }
9570 }
9571 }
9572
9573 mng_info->write_mng=write_mng;
9574
9575 if ((int) image->dispose >= 3)
9576 mng_info->framing_mode=3;
9577
9578 if (mng_info->need_fram && mng_info->adjoin &&
9579 ((image->delay != mng_info->delay) ||
9580 (mng_info->framing_mode != mng_info->old_framing_mode)))
9581 {
9582 if (image->delay == mng_info->delay)
9583 {
9584 /*
9585 Write a MNG FRAM chunk with the new framing mode.
9586 */
9587 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
9588 PNGType(chunk,mng_FRAM);
9589 LogPNGChunk((int) logging,mng_FRAM,1L);
9590 chunk[4]=(unsigned char) mng_info->framing_mode;
9591 (void) WriteBlob(image,5,chunk);
9592 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9593 }
9594 else
9595 {
9596 /*
9597 Write a MNG FRAM chunk with the delay.
9598 */
9599 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9600 PNGType(chunk,mng_FRAM);
9601 LogPNGChunk((int) logging,mng_FRAM,10L);
9602 chunk[4]=(unsigned char) mng_info->framing_mode;
9603 chunk[5]=0; /* frame name separator (no name) */
9604 chunk[6]=2; /* flag for changing default delay */
9605 chunk[7]=0; /* flag for changing frame timeout */
9606 chunk[8]=0; /* flag for changing frame clipping */
9607 chunk[9]=0; /* flag for changing frame sync_id */
9608 PNGLong(chunk+10,(png_uint_32)
9609 ((mng_info->ticks_per_second*
9610 image->delay)/MagickMax(image->ticks_per_second,1)));
9611 (void) WriteBlob(image,14,chunk);
9612 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9613 mng_info->delay=image->delay;
9614 }
9615 mng_info->old_framing_mode=mng_info->framing_mode;
9616 }
9617
9618#if defined(JNG_SUPPORTED)
9619 if (image_info->compression == JPEGCompression)
9620 {
9621 ImageInfo
9622 *write_info;
9623
9624 if (logging != MagickFalse)
9625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9626 " Writing JNG object.");
9627 /* To do: specify the desired alpha compression method. */
9628 write_info=CloneImageInfo(image_info);
9629 write_info->compression=UndefinedCompression;
9630 status=WriteOneJNGImage(mng_info,write_info,image);
9631 write_info=DestroyImageInfo(write_info);
9632 }
9633 else
9634#endif
9635 {
9636 if (logging != MagickFalse)
9637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9638 " Writing PNG object.");
9639 status=WriteOnePNGImage(mng_info,image_info,image);
9640 }
9641
9642 if (status == MagickFalse)
9643 {
9644 MngInfoFreeStruct(mng_info,&have_mng_structure);
9645 (void) CloseBlob(image);
9646 return(MagickFalse);
9647 }
9648 (void) CatchImageException(image);
9649 if (GetNextImageInList(image) == (Image *) NULL)
9650 break;
9651 image=SyncNextImageInList(image);
9652 status=SetImageProgress(image,SaveImagesTag,scene++,
9653 GetImageListLength(image));
9654 if (status == MagickFalse)
9655 break;
9656 } while (mng_info->adjoin);
9657 if (write_mng)
9658 {
9659 while (GetPreviousImageInList(image) != (Image *) NULL)
9660 image=GetPreviousImageInList(image);
9661 /*
9662 Write the MEND chunk.
9663 */
9664 (void) WriteBlobMSBULong(image,0x00000000L);
9665 PNGType(chunk,mng_MEND);
9666 LogPNGChunk((int) logging,mng_MEND,0L);
9667 (void) WriteBlob(image,4,chunk);
9668 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9669 }
9670 /*
9671 Relinquish resources.
9672 */
9673 (void) CloseBlob(image);
9674 MngInfoFreeStruct(mng_info,&have_mng_structure);
9675 if (logging != MagickFalse)
9676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9677 return(MagickTrue);
9678}
glennrpd5045b42010-03-24 12:40:35 +00009679#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00009680static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9681{
9682 image=image;
9683 printf("Your PNG library is too old: You have libpng-%s\n",
9684 PNG_LIBPNG_VER_STRING);
9685 ThrowBinaryException(CoderError,"PNG library is too old",
9686 image_info->filename);
9687}
9688static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9689{
9690 return(WritePNGImage(image_info,image));
9691}
glennrpd5045b42010-03-24 12:40:35 +00009692#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00009693#endif