blob: 58030cc355ce2757cc0ad9ad65ea0685ba334529 [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)
cristy4e5bc842010-06-09 13:56:01 +00005237 magnified_width += (png_uint_32) ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00005238 }
5239 else
5240 {
cristy4e5bc842010-06-09 13:56:01 +00005241 magnified_width=(png_uint_32) image->columns;
cristy3ed852e2009-09-05 21:47:34 +00005242 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)
cristy4e5bc842010-06-09 13:56:01 +00005247 magnified_width += (png_uint_32) ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00005248 }
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 {
cristy4e5bc842010-06-09 13:56:01 +00005259 magnified_height=(png_uint_32) image->rows;
cristy3ed852e2009-09-05 21:47:34 +00005260 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)
cristy4e5bc842010-06-09 13:56:01 +00005265 magnified_height += (png_uint_32) ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00005266 }
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 &&
cristy1449ea22010-06-25 00:59:28 +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;
cristy4e5bc842010-06-09 13:56:01 +00006549 ping_width=(png_uint_32) image->columns;
6550 ping_height=(png_uint_32) 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;
cristyb905e452010-06-24 00:30:31 +00006824 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00006825 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6826 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00006827 image_matte=MagickTrue;
cristy1449ea22010-06-25 00:59:28 +00006828 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
6829 (void) SetImageType(image,PaletteType);
cristy3ed852e2009-09-05 21:47:34 +00006830 }
6831 else
6832 {
6833 if (logging != MagickFalse)
6834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6835 "Selecting PNG colortype");
glennrp5af765f2010-03-30 11:12:18 +00006836 ping_color_type=(png_byte) ((matte == MagickTrue)?
cristy3ed852e2009-09-05 21:47:34 +00006837 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6838 if(image_info->type == TrueColorType)
6839 {
glennrp5af765f2010-03-30 11:12:18 +00006840 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00006841 image_matte=MagickFalse;
6842 }
6843 if(image_info->type == TrueColorMatteType)
6844 {
glennrp5af765f2010-03-30 11:12:18 +00006845 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006846 image_matte=MagickTrue;
6847 }
6848 if ((image_info->type == UndefinedType ||
6849 image_info->type == OptimizeType ||
6850 image_info->type == GrayscaleType) &&
6851 image_matte == MagickFalse && ImageIsGray(image))
6852 {
glennrp5af765f2010-03-30 11:12:18 +00006853 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
cristy3ed852e2009-09-05 21:47:34 +00006854 image_matte=MagickFalse;
6855 }
6856 if ((image_info->type == UndefinedType ||
6857 image_info->type == OptimizeType ||
6858 image_info->type == GrayscaleMatteType) &&
6859 image_matte == MagickTrue && ImageIsGray(image))
6860 {
glennrp5af765f2010-03-30 11:12:18 +00006861 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006862 image_matte=MagickTrue;
6863 }
6864 }
6865 if (logging != MagickFalse)
6866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00006867 "Selected PNG colortype=%d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00006868
glennrp5af765f2010-03-30 11:12:18 +00006869 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00006870 {
glennrp5af765f2010-03-30 11:12:18 +00006871 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6872 ping_color_type == PNG_COLOR_TYPE_RGB ||
6873 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6874 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00006875 }
6876
glennrp5af765f2010-03-30 11:12:18 +00006877 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00006878 {
6879 if (image->matte == MagickFalse && image->colors < 256)
6880 {
6881 if (ImageIsMonochrome(image))
6882 {
glennrp5af765f2010-03-30 11:12:18 +00006883 ping_bit_depth=1;
6884 if (ping_bit_depth < (int)mng_info->write_png_depth)
6885 ping_bit_depth = mng_info->write_png_depth;
cristy3ed852e2009-09-05 21:47:34 +00006886 }
6887 }
6888 }
glennrp5af765f2010-03-30 11:12:18 +00006889 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00006890 {
cristy35ef8242010-06-03 16:24:13 +00006891 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00006892 ping_bit_depth=1;
cristy35ef8242010-06-03 16:24:13 +00006893 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00006894 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00006895
6896 if (logging != MagickFalse)
6897 {
6898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006899 " Number of colors: %.20g",(double) image_colors);
cristy3ed852e2009-09-05 21:47:34 +00006900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00006901 " Tentative PNG bit depth: %d",ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00006902 }
6903 if (mng_info->write_png_depth)
6904 {
glennrp5af765f2010-03-30 11:12:18 +00006905 old_bit_depth=ping_bit_depth;
6906 if (ping_bit_depth < (int)mng_info->write_png_depth)
cristy3ed852e2009-09-05 21:47:34 +00006907 {
glennrp5af765f2010-03-30 11:12:18 +00006908 ping_bit_depth = mng_info->write_png_depth;
6909 if (ping_bit_depth > 8)
6910 ping_bit_depth = 8;
6911 if (ping_bit_depth != (int) old_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00006912 {
6913 if (logging != MagickFalse)
6914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006915 " Colors increased to %.20g",(double)
cristyf2faecf2010-05-28 19:19:36 +00006916 image_colors);
cristy3ed852e2009-09-05 21:47:34 +00006917 }
6918 }
6919 }
6920 }
6921 }
glennrp5af765f2010-03-30 11:12:18 +00006922 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00006923 if (logging != MagickFalse)
6924 {
6925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006926 " Tentative PNG color type: %.20g",(double) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00006927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006928 " image_info->type: %.20g",(double) image_info->type);
cristy3ed852e2009-09-05 21:47:34 +00006929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006930 " image_depth: %.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00006931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006932 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00006933 }
6934
6935 if (matte && (mng_info->optimize || mng_info->IsPalette))
6936 {
6937 register const PixelPacket
6938 *p;
6939
6940 p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
glennrp5af765f2010-03-30 11:12:18 +00006941 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
cristybb503372010-05-27 20:51:26 +00006942 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006943 {
6944 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6945 if (p == (const PixelPacket *) NULL)
6946 break;
cristybb503372010-05-27 20:51:26 +00006947 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006948 {
6949 if (IsGray(p) == MagickFalse)
6950 {
glennrp5af765f2010-03-30 11:12:18 +00006951 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00006952 break;
6953 }
6954 p++;
6955 }
6956 }
6957 /*
6958 Determine if there is any transparent color.
6959 */
cristybb503372010-05-27 20:51:26 +00006960 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006961 {
6962 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6963 if (p == (const PixelPacket *) NULL)
6964 break;
cristybb503372010-05-27 20:51:26 +00006965 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006966 {
6967 if (p->opacity != OpaqueOpacity)
6968 break;
6969 p++;
6970 }
6971 if (x != 0)
6972 break;
6973 }
cristybb503372010-05-27 20:51:26 +00006974 if ((y == (ssize_t) image->rows) && (x == (ssize_t) image->columns))
cristy3ed852e2009-09-05 21:47:34 +00006975 {
6976 /*
glennrp5af765f2010-03-30 11:12:18 +00006977 No transparent pixels are present. Change 4 or 6 to 0 or 2.
cristy3ed852e2009-09-05 21:47:34 +00006978 */
6979 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00006980 ping_color_type&=0x03;
cristy3ed852e2009-09-05 21:47:34 +00006981 }
6982 else
6983 {
6984 unsigned int
6985 mask;
6986
6987 mask=0xffff;
glennrp5af765f2010-03-30 11:12:18 +00006988 if (ping_bit_depth == 8)
cristy3ed852e2009-09-05 21:47:34 +00006989 mask=0x00ff;
glennrp5af765f2010-03-30 11:12:18 +00006990 if (ping_bit_depth == 4)
cristy3ed852e2009-09-05 21:47:34 +00006991 mask=0x000f;
glennrp5af765f2010-03-30 11:12:18 +00006992 if (ping_bit_depth == 2)
cristy3ed852e2009-09-05 21:47:34 +00006993 mask=0x0003;
glennrp5af765f2010-03-30 11:12:18 +00006994 if (ping_bit_depth == 1)
cristy3ed852e2009-09-05 21:47:34 +00006995 mask=0x0001;
glennrp5af765f2010-03-30 11:12:18 +00006996 ping_trans_color.red=(png_uint_16)
cristyce70c172010-01-07 17:15:30 +00006997 (ScaleQuantumToShort(GetRedPixelComponent(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00006998 ping_trans_color.green=(png_uint_16)
cristyce70c172010-01-07 17:15:30 +00006999 (ScaleQuantumToShort(GetGreenPixelComponent(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00007000 ping_trans_color.blue=(png_uint_16)
cristyce70c172010-01-07 17:15:30 +00007001 (ScaleQuantumToShort(GetBluePixelComponent(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00007002 ping_trans_color.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00007003 (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
glennrp5af765f2010-03-30 11:12:18 +00007004 ping_trans_color.index=(png_byte)
cristy46f08202010-01-10 04:04:21 +00007005 (ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p))));
glennrp5af765f2010-03-30 11:12:18 +00007006 (void) png_set_tRNS(ping, ping_info, NULL, 0,
7007 &ping_trans_color);
cristy3ed852e2009-09-05 21:47:34 +00007008 }
glennrp5af765f2010-03-30 11:12:18 +00007009 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00007010 {
7011 /*
7012 Determine if there is one and only one transparent color
7013 and if so if it is fully transparent.
7014 */
cristybb503372010-05-27 20:51:26 +00007015 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007016 {
7017 p=GetVirtualPixels(image,0,y,image->columns,1,
7018 &image->exception);
7019 x=0;
7020 if (p == (const PixelPacket *) NULL)
7021 break;
cristybb503372010-05-27 20:51:26 +00007022 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00007023 {
7024 if (p->opacity != OpaqueOpacity)
7025 {
glennrp5af765f2010-03-30 11:12:18 +00007026 if (IsPNGColorEqual(ping_trans_color,*p) == 0)
cristy3ed852e2009-09-05 21:47:34 +00007027 {
7028 break; /* Can't use RGB + tRNS for multiple
7029 transparent colors. */
7030 }
7031 if (p->opacity != (Quantum) TransparentOpacity)
7032 {
7033 break; /* Can't use RGB + tRNS for
7034 semitransparency. */
7035 }
7036 }
7037 else
7038 {
glennrp5af765f2010-03-30 11:12:18 +00007039 if (IsPNGColorEqual(ping_trans_color,*p))
cristy3ed852e2009-09-05 21:47:34 +00007040 break; /* Can't use RGB + tRNS when another pixel
7041 having the same RGB samples is
7042 transparent. */
7043 }
7044 p++;
7045 }
7046 if (x != 0)
7047 break;
7048 }
7049 if (x != 0)
glennrp5af765f2010-03-30 11:12:18 +00007050 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00007051 }
glennrp5af765f2010-03-30 11:12:18 +00007052 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00007053 {
glennrp5af765f2010-03-30 11:12:18 +00007054 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
cristy3ed852e2009-09-05 21:47:34 +00007055 if (image_depth == 8)
7056 {
glennrp5af765f2010-03-30 11:12:18 +00007057 ping_trans_color.red&=0xff;
7058 ping_trans_color.green&=0xff;
7059 ping_trans_color.blue&=0xff;
7060 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00007061 }
7062 }
7063 }
7064 matte=image_matte;
glennrp5af765f2010-03-30 11:12:18 +00007065 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00007066 image_matte=MagickFalse;
7067 if ((mng_info->optimize || mng_info->IsPalette) &&
7068 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
7069 ImageIsGray(image) && (!image_matte || image_depth >= 8))
7070 {
cristy35ef8242010-06-03 16:24:13 +00007071 size_t one=1;
cristy3ed852e2009-09-05 21:47:34 +00007072 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00007073 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00007074 else
7075 {
glennrp5af765f2010-03-30 11:12:18 +00007076 ping_color_type=PNG_COLOR_TYPE_GRAY;
cristy3ed852e2009-09-05 21:47:34 +00007077 if (save_image_depth == 16 && image_depth == 8)
glennrp5af765f2010-03-30 11:12:18 +00007078 ping_trans_color.gray*=0x0101;
cristy3ed852e2009-09-05 21:47:34 +00007079 }
7080 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
7081 image_depth=MAGICKCORE_QUANTUM_DEPTH;
7082 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
cristy35ef8242010-06-03 16:24:13 +00007083 image_colors=one << image_depth;
cristy3ed852e2009-09-05 21:47:34 +00007084 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00007085 ping_bit_depth=16;
cristy3ed852e2009-09-05 21:47:34 +00007086 else
7087 {
glennrp5af765f2010-03-30 11:12:18 +00007088 ping_bit_depth=8;
7089 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00007090 {
7091 if(!mng_info->write_png_depth)
7092 {
glennrp5af765f2010-03-30 11:12:18 +00007093 ping_bit_depth=1;
cristy35ef8242010-06-03 16:24:13 +00007094 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00007095 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00007096 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00007097 }
7098 }
glennrp5af765f2010-03-30 11:12:18 +00007099 else if (mng_info->optimize && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00007100 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
7101 mng_info->IsPalette)
7102 {
7103
7104 /* Check if grayscale is reducible */
7105 int
7106 depth_4_ok=MagickTrue,
7107 depth_2_ok=MagickTrue,
7108 depth_1_ok=MagickTrue;
7109
cristybb503372010-05-27 20:51:26 +00007110 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007111 {
7112 unsigned char
7113 intensity;
7114
7115 intensity=ScaleQuantumToChar(image->colormap[i].red);
7116
7117 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
7118 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
7119 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
7120 depth_2_ok=depth_1_ok=MagickFalse;
7121 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
7122 depth_1_ok=MagickFalse;
7123 }
7124 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00007125 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00007126 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00007127 ping_bit_depth=2;
cristy3ed852e2009-09-05 21:47:34 +00007128 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00007129 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00007130 }
7131 }
glennrp5af765f2010-03-30 11:12:18 +00007132 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007133 }
7134 else
7135 if (mng_info->IsPalette)
7136 {
cristybb503372010-05-27 20:51:26 +00007137 size_t
glennrp17a14852010-05-10 03:01:59 +00007138 number_colors;
7139
7140 number_colors=image_colors;
7141
cristy3ed852e2009-09-05 21:47:34 +00007142 if (image_depth <= 8)
7143 {
cristy3ed852e2009-09-05 21:47:34 +00007144 /*
7145 Set image palette.
7146 */
glennrp5af765f2010-03-30 11:12:18 +00007147 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
cristy3ed852e2009-09-05 21:47:34 +00007148 if (mng_info->have_write_global_plte && !matte)
7149 {
glennrp9c1eb072010-06-06 22:19:15 +00007150 png_set_PLTE(ping,ping_info,NULL,0);
7151 if (logging)
7152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7153 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00007154 }
7155 else
7156 {
7157#if defined(PNG_SORT_PALETTE)
cristybb503372010-05-27 20:51:26 +00007158 size_t
cristy3ed852e2009-09-05 21:47:34 +00007159 save_number_colors;
7160
7161 if (mng_info->optimize)
7162 {
7163 save_number_colors=image_colors;
7164 if (CompressColormapTransFirst(image) == MagickFalse)
7165 ThrowWriterException(ResourceLimitError,
7166 "MemoryAllocationFailed");
glennrp98156a32009-12-09 15:32:44 +00007167 number_colors=image->colors;
cristy3ed852e2009-09-05 21:47:34 +00007168 image_colors=save_number_colors;
7169 }
7170#endif
7171 palette=(png_color *) AcquireQuantumMemory(257,
7172 sizeof(*palette));
7173 if (palette == (png_color *) NULL)
7174 ThrowWriterException(ResourceLimitError,
7175 "MemoryAllocationFailed");
cristybb503372010-05-27 20:51:26 +00007176 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007177 {
7178 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7179 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7180 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7181 }
7182 if (logging)
7183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00007184 " Setting up PLTE chunk with %d colors",
7185 (int) number_colors);
cristy3ed852e2009-09-05 21:47:34 +00007186 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
cristy3ed852e2009-09-05 21:47:34 +00007187 palette=(png_colorp) RelinquishMagickMemory(palette);
cristy3ed852e2009-09-05 21:47:34 +00007188 }
7189 /* color_type is PNG_COLOR_TYPE_PALETTE */
7190 if (!mng_info->write_png_depth)
7191 {
cristybefe4d22010-06-07 01:18:58 +00007192 size_t
7193 one;
7194
glennrp5af765f2010-03-30 11:12:18 +00007195 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00007196 one=1;
7197 while ((one << ping_bit_depth) < number_colors)
glennrp5af765f2010-03-30 11:12:18 +00007198 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00007199 }
glennrp5af765f2010-03-30 11:12:18 +00007200 ping_num_trans=0;
cristy3ed852e2009-09-05 21:47:34 +00007201 if (matte)
7202 {
7203 ExceptionInfo
7204 *exception;
7205
7206 register const PixelPacket
7207 *p;
7208
7209 int
7210 trans[256];
7211
7212 register const IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00007213 *packet_indexes;
cristy3ed852e2009-09-05 21:47:34 +00007214
7215 /*
7216 Identify which colormap entry is transparent.
7217 */
7218 assert(number_colors <= 256);
cristybb503372010-05-27 20:51:26 +00007219 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007220 trans[i]=256;
7221 exception=(&image->exception);
cristybb503372010-05-27 20:51:26 +00007222 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007223 {
7224 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7225 if (p == (const PixelPacket *) NULL)
7226 break;
cristy5c6f7892010-05-05 22:53:29 +00007227 packet_indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +00007228 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00007229 {
7230 if (p->opacity != OpaqueOpacity)
7231 {
7232 IndexPacket
7233 packet_index;
7234
cristy5c6f7892010-05-05 22:53:29 +00007235 packet_index=packet_indexes[x];
cristybb503372010-05-27 20:51:26 +00007236 assert((size_t) packet_index < number_colors);
7237 if (trans[(ssize_t) packet_index] != 256)
cristy3ed852e2009-09-05 21:47:34 +00007238 {
cristybb503372010-05-27 20:51:26 +00007239 if (trans[(ssize_t) packet_index] != (png_byte) (255-
cristyce70c172010-01-07 17:15:30 +00007240 ScaleQuantumToChar(GetOpacityPixelComponent(p))))
cristy3ed852e2009-09-05 21:47:34 +00007241 {
glennrp5af765f2010-03-30 11:12:18 +00007242 ping_color_type=(png_byte)
cristy3ed852e2009-09-05 21:47:34 +00007243 PNG_COLOR_TYPE_RGB_ALPHA;
7244 break;
7245 }
7246 }
cristybb503372010-05-27 20:51:26 +00007247 trans[(ssize_t) packet_index]=(png_byte) (255-
cristyce70c172010-01-07 17:15:30 +00007248 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00007249 }
7250 p++;
7251 }
glennrp5af765f2010-03-30 11:12:18 +00007252 if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00007253 {
glennrp5af765f2010-03-30 11:12:18 +00007254 ping_num_trans=0;
7255 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7256 png_set_invalid(ping,ping_info,PNG_INFO_PLTE);
cristy3ed852e2009-09-05 21:47:34 +00007257 mng_info->IsPalette=MagickFalse;
7258 (void) SyncImage(image);
7259 if (logging)
7260 (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7261 " Cannot write image as indexed PNG, writing RGBA.");
7262 break;
7263 }
7264 }
glennrp5af765f2010-03-30 11:12:18 +00007265 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00007266 {
cristybb503372010-05-27 20:51:26 +00007267 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00007268 {
7269 if (trans[i] == 256)
7270 trans[i]=255;
7271 if (trans[i] != 255)
glennrp5af765f2010-03-30 11:12:18 +00007272 ping_num_trans=(unsigned short) (i+1);
cristy3ed852e2009-09-05 21:47:34 +00007273 }
7274 }
glennrp5af765f2010-03-30 11:12:18 +00007275 if (ping_num_trans == 0)
7276 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7277 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7278 ping_num_trans=0;
7279 if (ping_num_trans != 0)
glennrp9c1eb072010-06-06 22:19:15 +00007280 {
7281 for (i=0; i < (ssize_t) number_colors; i++)
7282 ping_trans_alpha[i]=(png_byte) trans[i];
7283 }
cristy3ed852e2009-09-05 21:47:34 +00007284 }
7285
cristy3ed852e2009-09-05 21:47:34 +00007286 }
7287 }
7288 else
7289 {
7290 if (image_depth < 8)
7291 image_depth=8;
7292 if ((save_image_depth == 16) && (image_depth == 8))
7293 {
glennrp5af765f2010-03-30 11:12:18 +00007294 ping_trans_color.red*=0x0101;
7295 ping_trans_color.green*=0x0101;
7296 ping_trans_color.blue*=0x0101;
7297 ping_trans_color.gray*=0x0101;
cristy3ed852e2009-09-05 21:47:34 +00007298 }
7299 }
7300
7301 /*
7302 Adjust background and transparency samples in sub-8-bit grayscale files.
7303 */
glennrp5af765f2010-03-30 11:12:18 +00007304 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00007305 PNG_COLOR_TYPE_GRAY)
7306 {
7307 png_uint_16
7308 maxval;
7309
7310 png_color_16
7311 background;
7312
cristy35ef8242010-06-03 16:24:13 +00007313 size_t
7314 one=1;
7315
cristy22ffd972010-06-03 16:51:47 +00007316 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00007317
7318
7319 background.gray=(png_uint_16)
7320 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7321
7322 if (logging != MagickFalse)
7323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7324 " Setting up bKGD chunk");
7325 png_set_bKGD(ping,ping_info,&background);
7326
glennrp5af765f2010-03-30 11:12:18 +00007327 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7328 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00007329 }
glennrp17a14852010-05-10 03:01:59 +00007330
7331 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7332 {
7333 /*
7334 Identify which colormap entry is the background color.
7335 */
7336
cristybb503372010-05-27 20:51:26 +00007337 size_t
glennrp17a14852010-05-10 03:01:59 +00007338 number_colors;
7339
7340 number_colors=image_colors;
7341
cristybb503372010-05-27 20:51:26 +00007342 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
glennrp17a14852010-05-10 03:01:59 +00007343 if (IsPNGColorEqual(ping_background,image->colormap[i]))
7344 break;
7345
7346 ping_background.index=(png_byte) i;
7347
7348 if (logging)
7349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7350 " Setting up bKGD chunk with index=%d",(int) i);
7351
7352 png_set_bKGD(ping,ping_info,&ping_background);
7353 }
7354
cristy3ed852e2009-09-05 21:47:34 +00007355 if (logging != MagickFalse)
7356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007357 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00007358 /*
7359 Initialize compression level and filtering.
7360 */
7361 if (logging != MagickFalse)
7362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7363 " Setting up deflate compression");
cristy3ed852e2009-09-05 21:47:34 +00007364 if (logging != MagickFalse)
7365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7366 " Compression buffer size: 32768");
7367 png_set_compression_buffer_size(ping,32768L);
cristy3ed852e2009-09-05 21:47:34 +00007368 if (logging != MagickFalse)
7369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7370 " Compression mem level: 9");
7371 png_set_compression_mem_level(ping, 9);
7372 quality=image->quality == UndefinedCompressionQuality ? 75UL :
7373 image->quality;
7374 if (quality > 9)
7375 {
7376 int
7377 level;
7378
cristybb503372010-05-27 20:51:26 +00007379 level=(int) MagickMin((ssize_t) quality/10,9);
cristy3ed852e2009-09-05 21:47:34 +00007380 if (logging != MagickFalse)
7381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7382 " Compression level: %d",level);
7383 png_set_compression_level(ping,level);
7384 }
7385 else
7386 {
7387 if (logging != MagickFalse)
7388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7389 " Compression strategy: Z_HUFFMAN_ONLY");
7390 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7391 }
7392 if (logging != MagickFalse)
7393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7394 " Setting up filtering");
7395#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7396
7397 /* This became available in libpng-1.0.9. Output must be a MNG. */
7398 if (mng_info->write_mng && ((quality % 10) == 7))
7399 {
7400 if (logging != MagickFalse)
7401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7402 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp5af765f2010-03-30 11:12:18 +00007403 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00007404 }
7405 else
7406 if (logging != MagickFalse)
7407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7408 " Filter_type: 0");
7409#endif
7410 {
7411 int
7412 base_filter;
7413
7414 if ((quality % 10) > 5)
7415 base_filter=PNG_ALL_FILTERS;
7416 else
7417 if ((quality % 10) != 5)
7418 base_filter=(int) quality % 10;
7419 else
glennrp5af765f2010-03-30 11:12:18 +00007420 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
7421 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
cristy3ed852e2009-09-05 21:47:34 +00007422 (quality < 50))
7423 base_filter=PNG_NO_FILTERS;
7424 else
7425 base_filter=PNG_ALL_FILTERS;
7426 if (logging != MagickFalse)
7427 {
7428 if (base_filter == PNG_ALL_FILTERS)
7429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7430 " Base filter method: ADAPTIVE");
7431 else
7432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7433 " Base filter method: NONE");
7434 }
7435 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7436 }
7437
7438 ResetImageProfileIterator(image);
7439 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7440 {
7441 profile=GetImageProfile(image,name);
7442 if (profile != (StringInfo *) NULL)
7443 {
glennrp5af765f2010-03-30 11:12:18 +00007444#ifdef PNG_WRITE_iCCP_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00007445 if ((LocaleCompare(name,"ICC") == 0) ||
7446 (LocaleCompare(name,"ICM") == 0))
7447 png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7448 GetStringInfoDatum(profile),
7449 (png_uint_32) GetStringInfoLength(profile));
7450 else
7451#endif
7452 png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7453 name,(unsigned char *) name,GetStringInfoDatum(profile),
7454 (png_uint_32) GetStringInfoLength(profile));
7455 }
7456 if (logging != MagickFalse)
7457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7458 " Setting up text chunk with %s profile",name);
7459 name=GetNextImageProfile(image);
7460 }
7461
7462#if defined(PNG_WRITE_sRGB_SUPPORTED)
7463 if ((mng_info->have_write_global_srgb == 0) &&
7464 ((image->rendering_intent != UndefinedIntent) ||
7465 (image->colorspace == sRGBColorspace)))
7466 {
7467 /*
7468 Note image rendering intent.
7469 */
7470 if (logging != MagickFalse)
7471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7472 " Setting up sRGB chunk");
7473 (void) png_set_sRGB(ping,ping_info,(int) (image->rendering_intent-1));
7474 png_set_gAMA(ping,ping_info,0.45455);
7475 }
glennrp5af765f2010-03-30 11:12:18 +00007476 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00007477#endif
7478 {
7479 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7480 {
7481 /*
7482 Note image gamma.
7483 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7484 */
7485 if (logging != MagickFalse)
7486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7487 " Setting up gAMA chunk");
7488 png_set_gAMA(ping,ping_info,image->gamma);
7489 }
7490 if ((mng_info->have_write_global_chrm == 0) &&
7491 (image->chromaticity.red_primary.x != 0.0))
7492 {
7493 /*
7494 Note image chromaticity.
7495 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7496 */
7497 PrimaryInfo
7498 bp,
7499 gp,
7500 rp,
7501 wp;
7502
7503 wp=image->chromaticity.white_point;
7504 rp=image->chromaticity.red_primary;
7505 gp=image->chromaticity.green_primary;
7506 bp=image->chromaticity.blue_primary;
7507
7508 if (logging != MagickFalse)
7509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7510 " Setting up cHRM chunk");
7511 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7512 bp.x,bp.y);
7513 }
7514 }
glennrp5af765f2010-03-30 11:12:18 +00007515 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00007516
7517 if (mng_info->write_mng)
7518 png_set_sig_bytes(ping,8);
7519
7520 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7521
7522 if (mng_info->write_png_colortype)
7523 {
7524 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7525 if (ImageIsGray(image) == MagickFalse)
7526 {
glennrp5af765f2010-03-30 11:12:18 +00007527 ping_color_type = PNG_COLOR_TYPE_RGB;
7528 if (ping_bit_depth < 8)
7529 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00007530 }
7531
7532 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7533 if (ImageIsGray(image) == MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00007534 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00007535 }
7536
7537 if ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00007538 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00007539 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00007540 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00007541 mng_info->write_png_colortype != 7 &&
glennrp5af765f2010-03-30 11:12:18 +00007542 !(mng_info->write_png_colortype == 5 && ping_color_type == 0))))
cristy3ed852e2009-09-05 21:47:34 +00007543 {
7544 if (logging != MagickFalse)
7545 {
7546 if (mng_info->write_png_depth)
7547 {
7548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7549 " Defined PNG:bit-depth=%u, Computed depth=%u",
7550 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00007551 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00007552 }
7553 if (mng_info->write_png_colortype)
7554 {
7555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7556 " Defined PNG:color-type=%u, Computed color type=%u",
7557 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00007558 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00007559 }
7560 }
7561 png_error(ping,
7562 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7563 }
7564
7565 if (image_matte && !image->matte)
7566 {
7567 /* Add an opaque matte channel */
7568 image->matte = MagickTrue;
7569 (void) SetImageOpacity(image,0);
glennrpb4a13412010-05-05 12:47:19 +00007570 if (logging != MagickFalse)
7571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7572 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00007573 }
7574
glennrpe9c26dc2010-05-30 01:56:35 +00007575 if (image->matte == MagickTrue && ping_color_type < 4)
7576 {
7577 if (ping_color_type == 3 && ping_num_trans == 0)
7578 {
7579 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7580 if (logging != MagickFalse)
7581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7582 " Ignoring request to write tRNS chunk with num_trans==0");
7583 }
7584 else
7585 (void) png_set_tRNS(ping, ping_info,
7586 ping_trans_alpha,
7587 ping_num_trans,
7588 &ping_trans_color);
7589 }
7590
cristy3ed852e2009-09-05 21:47:34 +00007591 if (logging != MagickFalse)
7592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7593 " Writing PNG header chunks");
7594
glennrp5af765f2010-03-30 11:12:18 +00007595 png_set_IHDR(ping,ping_info,ping_width,ping_height,
7596 ping_bit_depth,ping_color_type,
7597 ping_interlace_method,ping_compression_method,
7598 ping_filter_method);
7599
cristy3ed852e2009-09-05 21:47:34 +00007600 png_write_info_before_PLTE(ping, ping_info);
7601 /* write any png-chunk-b profiles */
7602 (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7603 png_write_info(ping,ping_info);
7604 /* write any PNG-chunk-m profiles */
7605 (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7606
7607 if (image->page.width || image->page.height)
7608 {
glennrp9c1eb072010-06-06 22:19:15 +00007609 unsigned char
7610 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +00007611
glennrp9c1eb072010-06-06 22:19:15 +00007612 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
7613 PNGType(chunk,mng_vpAg);
7614 LogPNGChunk((int) logging,mng_vpAg,9L);
7615 PNGLong(chunk+4,(png_uint_32) image->page.width);
7616 PNGLong(chunk+8,(png_uint_32) image->page.height);
7617 chunk[12]=0; /* unit = pixels */
7618 (void) WriteBlob(image,13,chunk);
7619 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
cristy3ed852e2009-09-05 21:47:34 +00007620 }
7621
7622#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +00007623 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +00007624#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +00007625 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +00007626#undef PNG_HAVE_IDAT
7627#endif
7628
7629 png_set_packing(ping);
7630 /*
7631 Allocate memory.
7632 */
7633 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +00007634 if (image_depth > 8)
7635 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +00007636 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +00007637 {
glennrpb4a13412010-05-05 12:47:19 +00007638 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +00007639 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +00007640 break;
7641 case PNG_COLOR_TYPE_GRAY_ALPHA:
7642 rowbytes*=2;
7643 break;
7644 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +00007645 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +00007646 break;
7647 default:
7648 break;
cristy3ed852e2009-09-05 21:47:34 +00007649 }
7650 if (logging)
glennrpb4a13412010-05-05 12:47:19 +00007651 {
7652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7653 " Writing PNG image data");
7654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007655 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +00007656 }
cristy3ed852e2009-09-05 21:47:34 +00007657 png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7658 sizeof(*png_pixels));
7659 if (png_pixels == (unsigned char *) NULL)
7660 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7661 /*
7662 Initialize image scanlines.
7663 */
glennrp5af765f2010-03-30 11:12:18 +00007664 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00007665 {
7666 /*
7667 PNG write failed.
7668 */
7669#ifdef PNG_DEBUG
7670 if (image_info->verbose)
7671 (void) printf("PNG write has failed.\n");
7672#endif
7673 png_destroy_write_struct(&ping,&ping_info);
7674 if (quantum_info != (QuantumInfo *) NULL)
7675 quantum_info=DestroyQuantumInfo(quantum_info);
7676 if (png_pixels != (unsigned char *) NULL)
7677 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7678#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00007679 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007680#endif
7681 return(MagickFalse);
7682 }
cristyed552522009-10-16 14:04:35 +00007683 quantum_info=AcquireQuantumInfo(image_info,image);
7684 if (quantum_info == (QuantumInfo *) NULL)
7685 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00007686 quantum_info->format=UndefinedQuantumFormat;
7687 quantum_info->depth=image_depth;
7688 num_passes=png_set_interlace_handling(ping);
7689 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7690 !mng_info->write_png32) &&
7691 (mng_info->optimize || mng_info->IsPalette ||
7692 (image_info->type == BilevelType)) &&
7693 !image_matte && ImageIsMonochrome(image))
7694 {
7695 register const PixelPacket
7696 *p;
7697
7698 quantum_info->depth=8;
7699 for (pass=0; pass < num_passes; pass++)
7700 {
7701 /*
7702 Convert PseudoClass image to a PNG monochrome image.
7703 */
cristybb503372010-05-27 20:51:26 +00007704 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007705 {
7706 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7707 if (p == (const PixelPacket *) NULL)
7708 break;
7709 if (mng_info->IsPalette)
7710 {
7711 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7712 quantum_info,GrayQuantum,png_pixels,&image->exception);
7713 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7714 mng_info->write_png_depth &&
7715 mng_info->write_png_depth != old_bit_depth)
7716 {
7717 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +00007718 for (i=0; i < (ssize_t) image->columns; i++)
cristy3ed852e2009-09-05 21:47:34 +00007719 *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7720 >> (8-old_bit_depth));
7721 }
7722 }
7723 else
7724 {
7725 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7726 quantum_info,RedQuantum,png_pixels,&image->exception);
7727 }
7728 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +00007729 for (i=0; i < (ssize_t) image->columns; i++)
cristy3ed852e2009-09-05 21:47:34 +00007730 *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7731 255 : 0);
glennrpb4a13412010-05-05 12:47:19 +00007732 if (logging && y == 0)
7733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7734 " Writing row of pixels (1)");
cristy3ed852e2009-09-05 21:47:34 +00007735 png_write_row(ping,png_pixels);
7736 }
7737 if (image->previous == (Image *) NULL)
7738 {
7739 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7740 if (status == MagickFalse)
7741 break;
7742 }
7743 }
7744 }
7745 else
7746 for (pass=0; pass < num_passes; pass++)
7747 {
7748 register const PixelPacket
7749 *p;
7750
7751 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7752 !mng_info->write_png32) &&
7753 (image_matte ||
glennrp5af765f2010-03-30 11:12:18 +00007754 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
cristy3ed852e2009-09-05 21:47:34 +00007755 (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7756 {
cristybb503372010-05-27 20:51:26 +00007757 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007758 {
7759 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7760 if (p == (const PixelPacket *) NULL)
7761 break;
glennrp5af765f2010-03-30 11:12:18 +00007762 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00007763 {
7764 if (mng_info->IsPalette)
7765 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7766 quantum_info,GrayQuantum,png_pixels,&image->exception);
7767 else
7768 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7769 quantum_info,RedQuantum,png_pixels,&image->exception);
glennrpb4a13412010-05-05 12:47:19 +00007770 if (logging && y == 0)
7771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7772 " Writing GRAY PNG pixels (2)");
cristy3ed852e2009-09-05 21:47:34 +00007773 }
7774 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7775 {
glennrpb4a13412010-05-05 12:47:19 +00007776 if (logging && y == 0)
7777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7778 " Writing GRAY_ALPHA PNG pixels (2)");
cristy3ed852e2009-09-05 21:47:34 +00007779 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7780 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7781 }
glennrpb4a13412010-05-05 12:47:19 +00007782 if (logging && y == 0)
7783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7784 " Writing row of pixels (2)");
cristy3ed852e2009-09-05 21:47:34 +00007785 png_write_row(ping,png_pixels);
7786 }
7787 if (image->previous == (Image *) NULL)
7788 {
7789 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7790 if (status == MagickFalse)
7791 break;
7792 }
7793 }
7794 else
7795 for (pass=0; pass < num_passes; pass++)
7796 {
7797 if ((image_depth > 8) || (mng_info->write_png24 ||
7798 mng_info->write_png32 ||
7799 (!mng_info->write_png8 && !mng_info->IsPalette)))
cristybb503372010-05-27 20:51:26 +00007800 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007801 {
7802 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7803 if (p == (const PixelPacket *) NULL)
7804 break;
glennrp5af765f2010-03-30 11:12:18 +00007805 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00007806 {
7807 if (image->storage_class == DirectClass)
7808 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7809 quantum_info,RedQuantum,png_pixels,&image->exception);
7810 else
7811 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7812 quantum_info,GrayQuantum,png_pixels,&image->exception);
7813 }
glennrp5af765f2010-03-30 11:12:18 +00007814 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrpb4a13412010-05-05 12:47:19 +00007815 {
7816 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7817 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7818 if (logging && y == 0)
7819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7820 " Writing GRAY_ALPHA PNG pixels (3)");
7821 }
cristy3ed852e2009-09-05 21:47:34 +00007822 else if (image_matte != MagickFalse)
7823 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7824 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7825 else
7826 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7827 quantum_info,RGBQuantum,png_pixels,&image->exception);
glennrpb4a13412010-05-05 12:47:19 +00007828 if (logging && y == 0)
7829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7830 " Writing row of pixels (3)");
cristy3ed852e2009-09-05 21:47:34 +00007831 png_write_row(ping,png_pixels);
7832 }
7833 else
7834 /* not ((image_depth > 8) || (mng_info->write_png24 ||
7835 mng_info->write_png32 ||
7836 (!mng_info->write_png8 && !mng_info->IsPalette))) */
7837 {
glennrp5af765f2010-03-30 11:12:18 +00007838 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
7839 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
cristy3ed852e2009-09-05 21:47:34 +00007840 {
7841 if (logging)
7842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7843 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7844 quantum_info->depth=8;
7845 image_depth=8;
7846 }
cristybb503372010-05-27 20:51:26 +00007847 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007848 {
glennrpe9c26dc2010-05-30 01:56:35 +00007849 if (logging && y == 0)
cristy3ed852e2009-09-05 21:47:34 +00007850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7851 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7852 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7853 if (p == (const PixelPacket *) NULL)
7854 break;
glennrp5af765f2010-03-30 11:12:18 +00007855 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00007856 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7857 quantum_info,GrayQuantum,png_pixels,&image->exception);
glennrp5af765f2010-03-30 11:12:18 +00007858 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrpb4a13412010-05-05 12:47:19 +00007859 {
glennrp07cd77a2010-05-05 12:49:15 +00007860 if (logging && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00007861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7862 " Writing GRAY_ALPHA PNG pixels (4)");
7863 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7864 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7865 }
cristy3ed852e2009-09-05 21:47:34 +00007866 else
7867 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7868 quantum_info,IndexQuantum,png_pixels,&image->exception);
glennrp07cd77a2010-05-05 12:49:15 +00007869 if (logging && y == 0)
glennrpb4a13412010-05-05 12:47:19 +00007870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7871 " Writing row of pixels (4)");
cristy3ed852e2009-09-05 21:47:34 +00007872 png_write_row(ping,png_pixels);
7873 }
7874 }
7875 if (image->previous == (Image *) NULL)
7876 {
7877 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7878 if (status == MagickFalse)
7879 break;
7880 }
7881 }
7882 }
cristyb32b90a2009-09-07 21:45:48 +00007883 if (quantum_info != (QuantumInfo *) NULL)
7884 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00007885
7886 if (logging != MagickFalse)
7887 {
7888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +00007889 " Wrote PNG image data");
cristy3ed852e2009-09-05 21:47:34 +00007890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007891 " Width: %.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00007892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007893 " Height: %.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00007894 if (mng_info->write_png_depth)
7895 {
7896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7897 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7898 }
7899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007900 " PNG bit-depth written: %d",ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00007901 if (mng_info->write_png_colortype)
7902 {
7903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7904 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7905 }
7906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007907 " PNG color-type written: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00007908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00007909 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +00007910 }
7911 /*
7912 Generate text chunks.
7913 */
cristy3ed852e2009-09-05 21:47:34 +00007914 ResetImagePropertyIterator(image);
7915 property=GetNextImageProperty(image);
7916 while (property != (const char *) NULL)
7917 {
cristy3ed852e2009-09-05 21:47:34 +00007918 png_textp
7919 text;
cristy3ed852e2009-09-05 21:47:34 +00007920
7921 value=GetImageProperty(image,property);
7922 if (value != (const char *) NULL)
7923 {
cristy3ed852e2009-09-05 21:47:34 +00007924 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7925 text[0].key=(char *) property;
7926 text[0].text=(char *) value;
7927 text[0].text_length=strlen(value);
7928 text[0].compression=image_info->compression == NoCompression ||
7929 (image_info->compression == UndefinedCompression &&
7930 text[0].text_length < 128) ? -1 : 0;
7931 if (logging != MagickFalse)
7932 {
7933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7934 " Setting up text chunk");
7935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7936 " keyword: %s",text[0].key);
7937 }
7938 png_set_text(ping,ping_info,text,1);
7939 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007940 }
7941 property=GetNextImageProperty(image);
7942 }
7943
7944 /* write any PNG-chunk-e profiles */
7945 (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
7946
7947 if (logging != MagickFalse)
7948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7949 " Writing PNG end info");
7950 png_write_end(ping,ping_info);
7951 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
7952 {
7953 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +00007954 (ping_width != mng_info->page.width) ||
7955 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +00007956 {
7957 unsigned char
7958 chunk[32];
7959
7960 /*
7961 Write FRAM 4 with clipping boundaries followed by FRAM 1.
7962 */
7963 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
7964 PNGType(chunk,mng_FRAM);
7965 LogPNGChunk((int) logging,mng_FRAM,27L);
7966 chunk[4]=4;
7967 chunk[5]=0; /* frame name separator (no name) */
7968 chunk[6]=1; /* flag for changing delay, for next frame only */
7969 chunk[7]=0; /* flag for changing frame timeout */
7970 chunk[8]=1; /* flag for changing frame clipping for next frame */
7971 chunk[9]=0; /* flag for changing frame sync_id */
7972 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
7973 chunk[14]=0; /* clipping boundaries delta type */
7974 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
7975 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +00007976 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +00007977 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
7978 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +00007979 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +00007980 (void) WriteBlob(image,31,chunk);
7981 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
7982 mng_info->old_framing_mode=4;
7983 mng_info->framing_mode=1;
7984 }
7985 else
7986 mng_info->framing_mode=3;
7987 }
7988 if (mng_info->write_mng && !mng_info->need_fram &&
7989 ((int) image->dispose == 3))
7990 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7991 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
7992 "`%s'",image->filename);
7993 image_depth=save_image_depth;
7994
7995 /* Save depth actually written */
7996
glennrp5af765f2010-03-30 11:12:18 +00007997 s[0]=(char) ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007998 s[1]='\0';
7999
8000 (void) SetImageProperty(image,"png:bit-depth-written",s);
8001
8002 /*
8003 Free PNG resources.
8004 */
glennrp5af765f2010-03-30 11:12:18 +00008005
cristy3ed852e2009-09-05 21:47:34 +00008006 png_destroy_write_struct(&ping,&ping_info);
8007
8008 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
8009
8010#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristyf84a1932010-01-03 18:00:18 +00008011 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008012#endif
8013
8014 if (logging != MagickFalse)
8015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8016 " exit WriteOnePNGImage()");
8017 return(MagickTrue);
8018/* End write one PNG image */
8019}
8020
8021/*
8022%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8023% %
8024% %
8025% %
8026% W r i t e P N G I m a g e %
8027% %
8028% %
8029% %
8030%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8031%
8032% WritePNGImage() writes a Portable Network Graphics (PNG) or
8033% Multiple-image Network Graphics (MNG) image file.
8034%
8035% MNG support written by Glenn Randers-Pehrson, glennrp@image...
8036%
8037% The format of the WritePNGImage method is:
8038%
8039% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
8040%
8041% A description of each parameter follows:
8042%
8043% o image_info: the image info.
8044%
8045% o image: The image.
8046%
8047% Returns MagickTrue on success, MagickFalse on failure.
8048%
8049% Communicating with the PNG encoder:
8050%
8051% While the datastream written is always in PNG format and normally would
8052% be given the "png" file extension, this method also writes the following
8053% pseudo-formats which are subsets of PNG:
8054%
8055% o PNG8: An 8-bit indexed PNG datastream is written. If transparency
8056% is present, the tRNS chunk must only have values 0 and 255
8057% (i.e., transparency is binary: fully opaque or fully
8058% transparent). The pixels contain 8-bit indices even if
8059% they could be represented with 1, 2, or 4 bits. Note: grayscale
8060% images will be written as indexed PNG files even though the
8061% PNG grayscale type might be slightly more efficient.
8062%
8063% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
8064% chunk can be present to convey binary transparency by naming
8065% one of the colors as transparent.
8066%
8067% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
8068% transparency is permitted, i.e., the alpha sample for
8069% each pixel can have any value from 0 to 255. The alpha
8070% channel is present even if the image is fully opaque.
8071%
8072% o -define: For more precise control of the PNG output, you can use the
8073% Image options "png:bit-depth" and "png:color-type". These
8074% can be set from the commandline with "-define" and also
8075% from the application programming interfaces.
8076%
8077% png:color-type can be 0, 2, 3, 4, or 6.
8078%
8079% When png:color-type is 0 (Grayscale), png:bit-depth can
8080% be 1, 2, 4, 8, or 16.
8081%
8082% When png:color-type is 2 (RGB), png:bit-depth can
8083% be 8 or 16.
8084%
8085% When png:color-type is 3 (Indexed), png:bit-depth can
8086% be 1, 2, 4, or 8. This refers to the number of bits
8087% used to store the index. The color samples always have
8088% bit-depth 8 in indexed PNG files.
8089%
8090% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
8091% png:bit-depth can be 8 or 16.
8092%
8093% If the image cannot be written without loss in the requested PNG8, PNG24,
8094% or PNG32 format or with the requested bit-depth and color-type without loss,
8095% a PNG file will not be written, and the encoder will return MagickFalse.
8096% Since image encoders should not be responsible for the "heavy lifting",
8097% the user should make sure that ImageMagick has already reduced the
8098% image depth and number of colors and limit transparency to binary
8099% transparency prior to attempting to write the image in a format that
8100% is subject to depth, color, or transparency limitations.
8101%
8102% TODO: Enforce the previous paragraph.
8103%
8104% TODO: Allow all other PNG subformats to be requested via new
8105% "-define png:bit-depth -define png:color-type" options.
8106%
8107% Note that another definition, "png:bit-depth-written" exists, but it
8108% is not intended for external use. It is only used internally by the
8109% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
8110%
8111% It is possible to request that the PNG encoder write previously-formatted
8112% ancillary chunks in the output PNG file, using the "-profile" commandline
8113% option as shown below or by setting the profile via a programming
8114% interface:
8115%
8116% -profile PNG-chunk-x:<file>
8117%
8118% where x is a location flag and <file> is a file containing the chunk
8119% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
8120%
8121% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
8122% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
8123% of the same type, then add a short unique string after the "x" to prevent
8124% subsequent profiles from overwriting the preceding ones:
8125%
8126% -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
8127%
8128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8129*/
8130static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
8131 Image *image)
8132{
8133 MagickBooleanType
8134 status;
8135
8136 MngInfo
8137 *mng_info;
8138
8139 const char
8140 *value;
8141
8142 int
8143 have_mng_structure;
8144
8145 unsigned int
8146 logging;
8147
8148 /*
8149 Open image file.
8150 */
8151 assert(image_info != (const ImageInfo *) NULL);
8152 assert(image_info->signature == MagickSignature);
8153 assert(image != (Image *) NULL);
8154 assert(image->signature == MagickSignature);
8155 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8156 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
8157 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8158 if (status == MagickFalse)
8159 return(MagickFalse);
8160 /*
8161 Allocate a MngInfo structure.
8162 */
8163 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00008164 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00008165 if (mng_info == (MngInfo *) NULL)
8166 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8167 /*
8168 Initialize members of the MngInfo structure.
8169 */
8170 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8171 mng_info->image=image;
8172 have_mng_structure=MagickTrue;
8173
8174 /* See if user has requested a specific PNG subformat */
8175
8176 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8177 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8178 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8179
8180 if (mng_info->write_png8)
8181 {
glennrp9c1eb072010-06-06 22:19:15 +00008182 mng_info->write_png_colortype = /* 3 */ 4;
8183 mng_info->write_png_depth = 8;
8184 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00008185#if 0 /* this does not work */
glennrp9c1eb072010-06-06 22:19:15 +00008186 if (image->matte == MagickTrue)
8187 (void) SetImageType(image,PaletteMatteType);
8188 else
8189 (void) SetImageType(image,PaletteType);
8190 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008191#endif
8192 }
8193
8194 if (mng_info->write_png24)
8195 {
glennrp9c1eb072010-06-06 22:19:15 +00008196 mng_info->write_png_colortype = /* 2 */ 3;
8197 mng_info->write_png_depth = 8;
8198 image->depth = 8;
8199 if (image->matte == MagickTrue)
8200 (void) SetImageType(image,TrueColorMatteType);
8201 else
8202 (void) SetImageType(image,TrueColorType);
8203 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008204 }
8205
8206 if (mng_info->write_png32)
8207 {
glennrp9c1eb072010-06-06 22:19:15 +00008208 mng_info->write_png_colortype = /* 6 */ 7;
8209 mng_info->write_png_depth = 8;
8210 image->depth = 8;
8211 if (image->matte == MagickTrue)
8212 (void) SetImageType(image,TrueColorMatteType);
8213 else
8214 (void) SetImageType(image,TrueColorType);
8215 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008216 }
8217
8218 value=GetImageOption(image_info,"png:bit-depth");
8219 if (value != (char *) NULL)
8220 {
8221 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008222 mng_info->write_png_depth = 1;
cristy3ed852e2009-09-05 21:47:34 +00008223 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008224 mng_info->write_png_depth = 2;
cristy3ed852e2009-09-05 21:47:34 +00008225 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008226 mng_info->write_png_depth = 4;
cristy3ed852e2009-09-05 21:47:34 +00008227 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008228 mng_info->write_png_depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00008229 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008230 mng_info->write_png_depth = 16;
cristy3ed852e2009-09-05 21:47:34 +00008231 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233 "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +00008234 }
8235 value=GetImageOption(image_info,"png:color-type");
8236 if (value != (char *) NULL)
8237 {
8238 /* We must store colortype+1 because 0 is a valid colortype */
8239 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008240 mng_info->write_png_colortype = 1;
cristy3ed852e2009-09-05 21:47:34 +00008241 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008242 mng_info->write_png_colortype = 3;
cristy3ed852e2009-09-05 21:47:34 +00008243 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008244 mng_info->write_png_colortype = 4;
cristy3ed852e2009-09-05 21:47:34 +00008245 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008246 mng_info->write_png_colortype = 5;
cristy3ed852e2009-09-05 21:47:34 +00008247 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +00008248 mng_info->write_png_colortype = 7;
cristy3ed852e2009-09-05 21:47:34 +00008249 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00008250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8251 "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +00008252 }
8253
8254 status=WriteOnePNGImage(mng_info,image_info,image);
8255
8256 (void) CloseBlob(image);
8257
8258 MngInfoFreeStruct(mng_info,&have_mng_structure);
8259 if (logging != MagickFalse)
8260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8261 return(status);
8262}
8263
8264#if defined(JNG_SUPPORTED)
8265
8266/* Write one JNG image */
8267static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8268 const ImageInfo *image_info,Image *image)
8269{
8270 Image
8271 *jpeg_image;
8272
8273 ImageInfo
8274 *jpeg_image_info;
8275
8276 MagickBooleanType
8277 status;
8278
8279 size_t
8280 length;
8281
8282 unsigned char
8283 *blob,
8284 chunk[80],
8285 *p;
8286
8287 unsigned int
8288 jng_alpha_compression_method,
8289 jng_alpha_sample_depth,
8290 jng_color_type,
8291 logging,
8292 transparent;
8293
cristybb503372010-05-27 20:51:26 +00008294 size_t
cristy3ed852e2009-09-05 21:47:34 +00008295 jng_quality;
8296
8297 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8298 " enter WriteOneJNGImage()");
8299
8300 blob=(unsigned char *) NULL;
8301 jpeg_image=(Image *) NULL;
8302 jpeg_image_info=(ImageInfo *) NULL;
8303
8304 status=MagickTrue;
8305 transparent=image_info->type==GrayscaleMatteType ||
8306 image_info->type==TrueColorMatteType;
8307 jng_color_type=10;
8308 jng_alpha_sample_depth=0;
8309 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8310 jng_alpha_compression_method=0;
8311
8312 if (image->matte != MagickFalse)
8313 {
8314 /* if any pixels are transparent */
8315 transparent=MagickTrue;
8316 if (image_info->compression==JPEGCompression)
8317 jng_alpha_compression_method=8;
8318 }
8319
8320 if (transparent)
8321 {
8322 jng_color_type=14;
8323 /* Create JPEG blob, image, and image_info */
8324 if (logging != MagickFalse)
8325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8326 " Creating jpeg_image_info for opacity.");
8327 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8328 if (jpeg_image_info == (ImageInfo *) NULL)
8329 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8330 if (logging != MagickFalse)
8331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8332 " Creating jpeg_image.");
8333 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8334 if (jpeg_image == (Image *) NULL)
8335 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8336 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8337 status=SeparateImageChannel(jpeg_image,OpacityChannel);
8338 status=NegateImage(jpeg_image,MagickFalse);
8339 jpeg_image->matte=MagickFalse;
8340 if (jng_quality >= 1000)
8341 jpeg_image_info->quality=jng_quality/1000;
8342 else
8343 jpeg_image_info->quality=jng_quality;
8344 jpeg_image_info->type=GrayscaleType;
8345 (void) SetImageType(jpeg_image,GrayscaleType);
8346 (void) AcquireUniqueFilename(jpeg_image->filename);
8347 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8348 "%s",jpeg_image->filename);
8349 }
8350
8351 /* To do: check bit depth of PNG alpha channel */
8352
8353 /* Check if image is grayscale. */
8354 if (image_info->type != TrueColorMatteType && image_info->type !=
8355 TrueColorType && ImageIsGray(image))
8356 jng_color_type-=2;
8357
8358 if (transparent)
8359 {
8360 if (jng_alpha_compression_method==0)
8361 {
8362 const char
8363 *value;
8364
8365 /* Encode opacity as a grayscale PNG blob */
8366 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8367 &image->exception);
8368 if (logging != MagickFalse)
8369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8370 " Creating PNG blob.");
8371 length=0;
8372
8373 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8374 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8375 jpeg_image_info->interlace=NoInterlace;
8376
8377 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8378 &image->exception);
8379
8380 /* Retrieve sample depth used */
8381 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8382 if (value != (char *) NULL)
8383 jng_alpha_sample_depth= (unsigned int) value[0];
8384 }
8385 else
8386 {
8387 /* Encode opacity as a grayscale JPEG blob */
8388
8389 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8390 &image->exception);
8391
8392 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8393 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8394 jpeg_image_info->interlace=NoInterlace;
8395 if (logging != MagickFalse)
8396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8397 " Creating blob.");
8398 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +00008399 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00008400 jng_alpha_sample_depth=8;
8401 if (logging != MagickFalse)
8402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008403 " Successfully read jpeg_image into a blob, length=%.20g.",
8404 (double) length);
cristy3ed852e2009-09-05 21:47:34 +00008405
8406 }
8407 /* Destroy JPEG image and image_info */
8408 jpeg_image=DestroyImage(jpeg_image);
8409 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8410 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8411 }
8412
8413 /* Write JHDR chunk */
8414 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
8415 PNGType(chunk,mng_JHDR);
8416 LogPNGChunk((int) logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +00008417 PNGLong(chunk+4,(png_uint_32) image->columns);
8418 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00008419 chunk[12]=jng_color_type;
8420 chunk[13]=8; /* sample depth */
8421 chunk[14]=8; /*jng_image_compression_method */
8422 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8423 chunk[16]=jng_alpha_sample_depth;
8424 chunk[17]=jng_alpha_compression_method;
8425 chunk[18]=0; /*jng_alpha_filter_method */
8426 chunk[19]=0; /*jng_alpha_interlace_method */
8427 (void) WriteBlob(image,20,chunk);
8428 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8429 if (logging != MagickFalse)
8430 {
8431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00008432 " JNG width:%15lu",(unsigned long) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00008433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00008434 " JNG height:%14lu",(unsigned long) image->rows);
cristy3ed852e2009-09-05 21:47:34 +00008435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8436 " JNG color type:%10d",jng_color_type);
8437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8438 " JNG sample depth:%8d",8);
8439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8440 " JNG compression:%9d",8);
8441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442 " JNG interlace:%11d",0);
8443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8444 " JNG alpha depth:%9d",jng_alpha_sample_depth);
8445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8446 " JNG alpha compression:%3d",jng_alpha_compression_method);
8447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8448 " JNG alpha filter:%8d",0);
8449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8450 " JNG alpha interlace:%5d",0);
8451 }
8452
8453 /* Write any JNG-chunk-b profiles */
8454 (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8455
8456 /*
8457 Write leading ancillary chunks
8458 */
8459
8460 if (transparent)
8461 {
8462 /*
8463 Write JNG bKGD chunk
8464 */
8465
8466 unsigned char
8467 blue,
8468 green,
8469 red;
8470
cristybb503372010-05-27 20:51:26 +00008471 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008472 num_bytes;
8473
8474 if (jng_color_type == 8 || jng_color_type == 12)
8475 num_bytes=6L;
8476 else
8477 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +00008478 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +00008479 PNGType(chunk,mng_bKGD);
cristybb503372010-05-27 20:51:26 +00008480 LogPNGChunk((int) logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +00008481 red=ScaleQuantumToChar(image->background_color.red);
8482 green=ScaleQuantumToChar(image->background_color.green);
8483 blue=ScaleQuantumToChar(image->background_color.blue);
8484 *(chunk+4)=0;
8485 *(chunk+5)=red;
8486 *(chunk+6)=0;
8487 *(chunk+7)=green;
8488 *(chunk+8)=0;
8489 *(chunk+9)=blue;
8490 (void) WriteBlob(image,(size_t) num_bytes,chunk);
8491 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8492 }
8493
8494 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8495 {
8496 /*
8497 Write JNG sRGB chunk
8498 */
8499 (void) WriteBlobMSBULong(image,1L);
8500 PNGType(chunk,mng_sRGB);
8501 LogPNGChunk((int) logging,mng_sRGB,1L);
8502 if (image->rendering_intent != UndefinedIntent)
8503 chunk[4]=(unsigned char) (image->rendering_intent-1);
8504 else
8505 chunk[4]=(unsigned char) (PerceptualIntent-1);
8506 (void) WriteBlob(image,5,chunk);
8507 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8508 }
8509 else
8510 {
8511 if (image->gamma != 0.0)
8512 {
8513 /*
8514 Write JNG gAMA chunk
8515 */
8516 (void) WriteBlobMSBULong(image,4L);
8517 PNGType(chunk,mng_gAMA);
8518 LogPNGChunk((int) logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +00008519 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008520 (void) WriteBlob(image,8,chunk);
8521 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8522 }
8523 if ((mng_info->equal_chrms == MagickFalse) &&
8524 (image->chromaticity.red_primary.x != 0.0))
8525 {
8526 PrimaryInfo
8527 primary;
8528
8529 /*
8530 Write JNG cHRM chunk
8531 */
8532 (void) WriteBlobMSBULong(image,32L);
8533 PNGType(chunk,mng_cHRM);
8534 LogPNGChunk((int) logging,mng_cHRM,32L);
8535 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +00008536 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
8537 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008538 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +00008539 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
8540 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008541 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +00008542 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
8543 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008544 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +00008545 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
8546 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008547 (void) WriteBlob(image,36,chunk);
8548 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8549 }
8550 }
8551 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8552 {
8553 /*
8554 Write JNG pHYs chunk
8555 */
8556 (void) WriteBlobMSBULong(image,9L);
8557 PNGType(chunk,mng_pHYs);
8558 LogPNGChunk((int) logging,mng_pHYs,9L);
8559 if (image->units == PixelsPerInchResolution)
8560 {
cristy35ef8242010-06-03 16:24:13 +00008561 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00008562 (image->x_resolution*100.0/2.54+0.5));
cristy35ef8242010-06-03 16:24:13 +00008563 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00008564 (image->y_resolution*100.0/2.54+0.5));
8565 chunk[12]=1;
8566 }
8567 else
8568 {
8569 if (image->units == PixelsPerCentimeterResolution)
8570 {
cristy35ef8242010-06-03 16:24:13 +00008571 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00008572 (image->x_resolution*100.0+0.5));
cristy35ef8242010-06-03 16:24:13 +00008573 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00008574 (image->y_resolution*100.0+0.5));
8575 chunk[12]=1;
8576 }
8577 else
8578 {
cristy35ef8242010-06-03 16:24:13 +00008579 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
8580 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00008581 chunk[12]=0;
8582 }
8583 }
8584 (void) WriteBlob(image,13,chunk);
8585 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8586 }
8587
8588 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8589 {
8590 /*
8591 Write JNG oFFs chunk
8592 */
8593 (void) WriteBlobMSBULong(image,9L);
8594 PNGType(chunk,mng_oFFs);
8595 LogPNGChunk((int) logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +00008596 PNGsLong(chunk+4,(ssize_t) (image->page.x));
8597 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +00008598 chunk[12]=0;
8599 (void) WriteBlob(image,13,chunk);
8600 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8601 }
8602 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8603 {
8604 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8605 PNGType(chunk,mng_vpAg);
8606 LogPNGChunk((int) logging,mng_vpAg,9L);
8607 PNGLong(chunk+4,(png_uint_32) image->page.width);
8608 PNGLong(chunk+8,(png_uint_32) image->page.height);
8609 chunk[12]=0; /* unit = pixels */
8610 (void) WriteBlob(image,13,chunk);
8611 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8612 }
8613
8614
8615 if (transparent)
8616 {
8617 if (jng_alpha_compression_method==0)
8618 {
cristybb503372010-05-27 20:51:26 +00008619 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008620 i;
8621
cristybb503372010-05-27 20:51:26 +00008622 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008623 len;
8624
8625 /* Write IDAT chunk header */
8626 if (logging != MagickFalse)
8627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008628 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +00008629 length);
cristy3ed852e2009-09-05 21:47:34 +00008630
8631 /* Copy IDAT chunks */
8632 len=0;
8633 p=blob+8;
cristybb503372010-05-27 20:51:26 +00008634 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +00008635 {
8636 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8637 p+=4;
8638 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8639 {
8640 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +00008641 (void) WriteBlobMSBULong(image,(size_t) len);
8642 LogPNGChunk((int) logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +00008643 (void) WriteBlob(image,(size_t) len+4,p);
8644 (void) WriteBlobMSBULong(image,
8645 crc32(0,p,(uInt) len+4));
8646 }
8647 else
8648 {
8649 if (logging != MagickFalse)
8650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008651 " Skipping %c%c%c%c chunk, length=%.20g.",
8652 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +00008653 }
8654 p+=(8+len);
8655 }
8656 }
8657 else
8658 {
8659 /* Write JDAA chunk header */
8660 if (logging != MagickFalse)
8661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008662 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +00008663 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00008664 PNGType(chunk,mng_JDAA);
8665 LogPNGChunk((int) logging,mng_JDAA,length);
8666 /* Write JDAT chunk(s) data */
8667 (void) WriteBlob(image,4,chunk);
8668 (void) WriteBlob(image,length,blob);
8669 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8670 (uInt) length));
8671 }
8672 blob=(unsigned char *) RelinquishMagickMemory(blob);
8673 }
8674
8675 /* Encode image as a JPEG blob */
8676 if (logging != MagickFalse)
8677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8678 " Creating jpeg_image_info.");
8679 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8680 if (jpeg_image_info == (ImageInfo *) NULL)
8681 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8682
8683 if (logging != MagickFalse)
8684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8685 " Creating jpeg_image.");
8686
8687 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8688 if (jpeg_image == (Image *) NULL)
8689 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8690 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8691
8692 (void) AcquireUniqueFilename(jpeg_image->filename);
8693 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8694 jpeg_image->filename);
8695
8696 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8697 &image->exception);
8698
8699 if (logging != MagickFalse)
8700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008701 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
8702 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00008703
8704 if (jng_color_type == 8 || jng_color_type == 12)
8705 jpeg_image_info->type=GrayscaleType;
8706 jpeg_image_info->quality=jng_quality % 1000;
8707 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8708 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8709 if (logging != MagickFalse)
8710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8711 " Creating blob.");
8712 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8713 if (logging != MagickFalse)
8714 {
8715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008716 " Successfully read jpeg_image into a blob, length=%.20g.",
8717 (double) length);
cristy3ed852e2009-09-05 21:47:34 +00008718
8719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008720 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +00008721 }
8722 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +00008723 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00008724 PNGType(chunk,mng_JDAT);
8725 LogPNGChunk((int) logging,mng_JDAT,length);
8726 (void) WriteBlob(image,4,chunk);
8727 (void) WriteBlob(image,length,blob);
8728 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8729
8730 jpeg_image=DestroyImage(jpeg_image);
8731 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8732 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8733 blob=(unsigned char *) RelinquishMagickMemory(blob);
8734
8735 /* Write any JNG-chunk-e profiles */
8736 (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8737
8738 /* Write IEND chunk */
8739 (void) WriteBlobMSBULong(image,0L);
8740 PNGType(chunk,mng_IEND);
8741 LogPNGChunk((int) logging,mng_IEND,0);
8742 (void) WriteBlob(image,4,chunk);
8743 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8744
8745 if (logging != MagickFalse)
8746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8747 " exit WriteOneJNGImage()");
8748 return(status);
8749}
8750
8751
8752/*
8753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8754% %
8755% %
8756% %
8757% W r i t e J N G I m a g e %
8758% %
8759% %
8760% %
8761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8762%
8763% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8764%
8765% JNG support written by Glenn Randers-Pehrson, glennrp@image...
8766%
8767% The format of the WriteJNGImage method is:
8768%
8769% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8770%
8771% A description of each parameter follows:
8772%
8773% o image_info: the image info.
8774%
8775% o image: The image.
8776%
8777%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8778*/
8779static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8780{
8781 MagickBooleanType
8782 status;
8783
8784 MngInfo
8785 *mng_info;
8786
8787 int
8788 have_mng_structure;
8789
8790 unsigned int
8791 logging;
8792
8793 /*
8794 Open image file.
8795 */
8796 assert(image_info != (const ImageInfo *) NULL);
8797 assert(image_info->signature == MagickSignature);
8798 assert(image != (Image *) NULL);
8799 assert(image->signature == MagickSignature);
8800 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8801 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8802 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8803 if (status == MagickFalse)
8804 return(status);
8805
8806 /*
8807 Allocate a MngInfo structure.
8808 */
8809 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00008810 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00008811 if (mng_info == (MngInfo *) NULL)
8812 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8813 /*
8814 Initialize members of the MngInfo structure.
8815 */
8816 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8817 mng_info->image=image;
8818 have_mng_structure=MagickTrue;
8819
8820 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8821
8822 status=WriteOneJNGImage(mng_info,image_info,image);
8823 (void) CloseBlob(image);
8824
8825 (void) CatchImageException(image);
8826 MngInfoFreeStruct(mng_info,&have_mng_structure);
8827 if (logging != MagickFalse)
8828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8829 return(status);
8830}
8831#endif
8832
8833
8834
8835static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8836{
8837 const char
8838 *option;
8839
8840 Image
8841 *next_image;
8842
8843 MagickBooleanType
8844 status;
8845
8846 MngInfo
8847 *mng_info;
8848
8849 int
8850 have_mng_structure,
8851 image_count,
8852 need_iterations,
8853 need_matte;
8854
8855 volatile int
8856#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8857 defined(PNG_MNG_FEATURES_SUPPORTED)
8858 need_local_plte,
8859#endif
8860 all_images_are_gray,
8861 logging,
8862 need_defi,
8863 optimize,
8864 use_global_plte;
8865
cristybb503372010-05-27 20:51:26 +00008866 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008867 i;
8868
8869 unsigned char
8870 chunk[800];
8871
8872 volatile unsigned int
8873 write_jng,
8874 write_mng;
8875
cristybb503372010-05-27 20:51:26 +00008876 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00008877 scene;
8878
cristybb503372010-05-27 20:51:26 +00008879 size_t
cristy3ed852e2009-09-05 21:47:34 +00008880 final_delay=0,
8881 initial_delay;
8882
glennrpd5045b42010-03-24 12:40:35 +00008883#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00008884 if (image_info->verbose)
8885 printf("Your PNG library (libpng-%s) is rather old.\n",
8886 PNG_LIBPNG_VER_STRING);
8887#endif
8888
8889 /*
8890 Open image file.
8891 */
8892 assert(image_info != (const ImageInfo *) NULL);
8893 assert(image_info->signature == MagickSignature);
8894 assert(image != (Image *) NULL);
8895 assert(image->signature == MagickSignature);
8896 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8897 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8898 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8899 if (status == MagickFalse)
8900 return(status);
8901
8902 /*
8903 Allocate a MngInfo structure.
8904 */
8905 have_mng_structure=MagickFalse;
cristy90823212009-12-12 20:48:33 +00008906 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +00008907 if (mng_info == (MngInfo *) NULL)
8908 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8909 /*
8910 Initialize members of the MngInfo structure.
8911 */
8912 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8913 mng_info->image=image;
8914 have_mng_structure=MagickTrue;
8915 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
8916
8917 /*
8918 * See if user has requested a specific PNG subformat to be used
8919 * for all of the PNGs in the MNG being written, e.g.,
8920 *
8921 * convert *.png png8:animation.mng
8922 *
8923 * To do: check -define png:bit_depth and png:color_type as well,
8924 * or perhaps use mng:bit_depth and mng:color_type instead for
8925 * global settings.
8926 */
8927
8928 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8929 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8930 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8931
8932 write_jng=MagickFalse;
8933 if (image_info->compression == JPEGCompression)
8934 write_jng=MagickTrue;
8935
8936 mng_info->adjoin=image_info->adjoin &&
8937 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
8938
8939 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8940 optimize=MagickFalse;
8941 else
8942 optimize=(image_info->type == OptimizeType || image_info->type ==
8943 UndefinedType);
8944
8945 if (logging != MagickFalse)
8946 {
8947 /* Log some info about the input */
8948 Image
8949 *p;
8950
8951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8952 " Checking input image(s)");
8953 if (optimize)
8954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8955 " Optimize: TRUE");
8956 else
8957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8958 " Optimize: FALSE");
8959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008960 " Image_info depth: %.20g",(double) image_info->depth);
cristy3ed852e2009-09-05 21:47:34 +00008961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8962 " Type: %d",image_info->type);
8963
8964 scene=0;
8965 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8966 {
8967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008968 " Scene: %.20g",(double) scene++);
cristy3ed852e2009-09-05 21:47:34 +00008969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008970 " Image depth: %.20g",(double) p->depth);
cristy3ed852e2009-09-05 21:47:34 +00008971 if (p->matte)
8972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8973 " Matte: True");
8974 else
8975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8976 " Matte: False");
8977 if (p->storage_class == PseudoClass)
8978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8979 " Storage class: PseudoClass");
8980 else
8981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8982 " Storage class: DirectClass");
8983 if (p->colors)
8984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008985 " Number of colors: %.20g",(double) p->colors);
cristy3ed852e2009-09-05 21:47:34 +00008986 else
8987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8988 " Number of colors: unspecified");
8989 if (mng_info->adjoin == MagickFalse)
8990 break;
8991 }
8992 }
8993
8994 /*
8995 Sometimes we get PseudoClass images whose RGB values don't match
8996 the colors in the colormap. This code syncs the RGB values.
8997 */
8998 {
8999 Image
9000 *p;
9001
9002 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9003 {
9004 if (p->taint && p->storage_class == PseudoClass)
9005 (void) SyncImage(p);
9006 if (mng_info->adjoin == MagickFalse)
9007 break;
9008 }
9009 }
9010
9011#ifdef PNG_BUILD_PALETTE
9012 if (optimize)
9013 {
9014 /*
9015 Sometimes we get DirectClass images that have 256 colors or fewer.
9016 This code will convert them to PseudoClass and build a colormap.
9017 */
9018 Image
9019 *p;
9020
9021 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9022 {
9023 if (p->storage_class != PseudoClass)
9024 {
9025 p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
9026 if (p->colors <= 256)
9027 {
9028 p->colors=0;
9029 if (p->matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009030 (void) SetImageType(p,PaletteMatteType);
cristy3ed852e2009-09-05 21:47:34 +00009031 else
glennrp9c1eb072010-06-06 22:19:15 +00009032 (void) SetImageType(p,PaletteType);
cristy3ed852e2009-09-05 21:47:34 +00009033 }
9034 }
9035 if (mng_info->adjoin == MagickFalse)
9036 break;
9037 }
9038 }
9039#endif
9040
9041 use_global_plte=MagickFalse;
9042 all_images_are_gray=MagickFalse;
9043#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9044 need_local_plte=MagickTrue;
9045#endif
9046 need_defi=MagickFalse;
9047 need_matte=MagickFalse;
9048 mng_info->framing_mode=1;
9049 mng_info->old_framing_mode=1;
9050
9051 if (write_mng)
9052 if (image_info->page != (char *) NULL)
9053 {
9054 /*
9055 Determine image bounding box.
9056 */
9057 SetGeometry(image,&mng_info->page);
9058 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
9059 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
9060 }
9061 if (write_mng)
9062 {
9063 unsigned int
9064 need_geom;
9065
9066 unsigned short
9067 red,
9068 green,
9069 blue;
9070
9071 mng_info->page=image->page;
9072 need_geom=MagickTrue;
9073 if (mng_info->page.width || mng_info->page.height)
9074 need_geom=MagickFalse;
9075 /*
9076 Check all the scenes.
9077 */
9078 initial_delay=image->delay;
9079 need_iterations=MagickFalse;
9080 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
9081 mng_info->equal_physs=MagickTrue,
9082 mng_info->equal_gammas=MagickTrue;
9083 mng_info->equal_srgbs=MagickTrue;
9084 mng_info->equal_backgrounds=MagickTrue;
9085 image_count=0;
9086#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9087 defined(PNG_MNG_FEATURES_SUPPORTED)
9088 all_images_are_gray=MagickTrue;
9089 mng_info->equal_palettes=MagickFalse;
9090 need_local_plte=MagickFalse;
9091#endif
9092 for (next_image=image; next_image != (Image *) NULL; )
9093 {
9094 if (need_geom)
9095 {
9096 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
9097 mng_info->page.width=next_image->columns+next_image->page.x;
9098 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
9099 mng_info->page.height=next_image->rows+next_image->page.y;
9100 }
9101 if (next_image->page.x || next_image->page.y)
9102 need_defi=MagickTrue;
9103 if (next_image->matte)
9104 need_matte=MagickTrue;
9105 if ((int) next_image->dispose >= BackgroundDispose)
9106 if (next_image->matte || next_image->page.x || next_image->page.y ||
9107 ((next_image->columns < mng_info->page.width) &&
9108 (next_image->rows < mng_info->page.height)))
9109 mng_info->need_fram=MagickTrue;
9110 if (next_image->iterations)
9111 need_iterations=MagickTrue;
9112 final_delay=next_image->delay;
9113 if (final_delay != initial_delay || final_delay > 1UL*
9114 next_image->ticks_per_second)
9115 mng_info->need_fram=1;
9116#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9117 defined(PNG_MNG_FEATURES_SUPPORTED)
9118 /*
9119 check for global palette possibility.
9120 */
9121 if (image->matte != MagickFalse)
9122 need_local_plte=MagickTrue;
9123 if (need_local_plte == 0)
9124 {
9125 if (ImageIsGray(image) == MagickFalse)
9126 all_images_are_gray=MagickFalse;
9127 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
9128 if (use_global_plte == 0)
9129 use_global_plte=mng_info->equal_palettes;
9130 need_local_plte=!mng_info->equal_palettes;
9131 }
9132#endif
9133 if (GetNextImageInList(next_image) != (Image *) NULL)
9134 {
9135 if (next_image->background_color.red !=
9136 next_image->next->background_color.red ||
9137 next_image->background_color.green !=
9138 next_image->next->background_color.green ||
9139 next_image->background_color.blue !=
9140 next_image->next->background_color.blue)
9141 mng_info->equal_backgrounds=MagickFalse;
9142 if (next_image->gamma != next_image->next->gamma)
9143 mng_info->equal_gammas=MagickFalse;
9144 if (next_image->rendering_intent !=
9145 next_image->next->rendering_intent)
9146 mng_info->equal_srgbs=MagickFalse;
9147 if ((next_image->units != next_image->next->units) ||
9148 (next_image->x_resolution != next_image->next->x_resolution) ||
9149 (next_image->y_resolution != next_image->next->y_resolution))
9150 mng_info->equal_physs=MagickFalse;
9151 if (mng_info->equal_chrms)
9152 {
9153 if (next_image->chromaticity.red_primary.x !=
9154 next_image->next->chromaticity.red_primary.x ||
9155 next_image->chromaticity.red_primary.y !=
9156 next_image->next->chromaticity.red_primary.y ||
9157 next_image->chromaticity.green_primary.x !=
9158 next_image->next->chromaticity.green_primary.x ||
9159 next_image->chromaticity.green_primary.y !=
9160 next_image->next->chromaticity.green_primary.y ||
9161 next_image->chromaticity.blue_primary.x !=
9162 next_image->next->chromaticity.blue_primary.x ||
9163 next_image->chromaticity.blue_primary.y !=
9164 next_image->next->chromaticity.blue_primary.y ||
9165 next_image->chromaticity.white_point.x !=
9166 next_image->next->chromaticity.white_point.x ||
9167 next_image->chromaticity.white_point.y !=
9168 next_image->next->chromaticity.white_point.y)
9169 mng_info->equal_chrms=MagickFalse;
9170 }
9171 }
9172 image_count++;
9173 next_image=GetNextImageInList(next_image);
9174 }
9175 if (image_count < 2)
9176 {
9177 mng_info->equal_backgrounds=MagickFalse;
9178 mng_info->equal_chrms=MagickFalse;
9179 mng_info->equal_gammas=MagickFalse;
9180 mng_info->equal_srgbs=MagickFalse;
9181 mng_info->equal_physs=MagickFalse;
9182 use_global_plte=MagickFalse;
9183#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9184 need_local_plte=MagickTrue;
9185#endif
9186 need_iterations=MagickFalse;
9187 }
9188 if (mng_info->need_fram == MagickFalse)
9189 {
9190 /*
9191 Only certain framing rates 100/n are exactly representable without
9192 the FRAM chunk but we'll allow some slop in VLC files
9193 */
9194 if (final_delay == 0)
9195 {
9196 if (need_iterations != MagickFalse)
9197 {
9198 /*
9199 It's probably a GIF with loop; don't run it *too* fast.
9200 */
9201 final_delay=10;
9202 (void) ThrowMagickException(&image->exception,
9203 GetMagickModule(),CoderError,
9204 "input has zero delay between all frames; assuming 10 cs",
9205 "`%s'","");
9206 }
9207 else
9208 mng_info->ticks_per_second=0;
9209 }
9210 if (final_delay != 0)
cristy4e5bc842010-06-09 13:56:01 +00009211 mng_info->ticks_per_second=(png_uint_32) (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +00009212 if (final_delay > 50)
9213 mng_info->ticks_per_second=2;
9214 if (final_delay > 75)
9215 mng_info->ticks_per_second=1;
9216 if (final_delay > 125)
9217 mng_info->need_fram=MagickTrue;
9218 if (need_defi && final_delay > 2 && (final_delay != 4) &&
9219 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
9220 (final_delay != 25) && (final_delay != 50) && (final_delay !=
9221 1UL*image->ticks_per_second))
9222 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
9223 }
9224 if (mng_info->need_fram != MagickFalse)
9225 mng_info->ticks_per_second=1UL*image->ticks_per_second;
9226 /*
9227 If pseudocolor, we should also check to see if all the
9228 palettes are identical and write a global PLTE if they are.
9229 ../glennrp Feb 99.
9230 */
9231 /*
9232 Write the MNG version 1.0 signature and MHDR chunk.
9233 */
9234 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9235 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
9236 PNGType(chunk,mng_MHDR);
9237 LogPNGChunk((int) logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +00009238 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
9239 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +00009240 PNGLong(chunk+12,mng_info->ticks_per_second);
9241 PNGLong(chunk+16,0L); /* layer count=unknown */
9242 PNGLong(chunk+20,0L); /* frame count=unknown */
9243 PNGLong(chunk+24,0L); /* play time=unknown */
9244 if (write_jng)
9245 {
9246 if (need_matte)
9247 {
9248 if (need_defi || mng_info->need_fram || use_global_plte)
9249 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
9250 else
9251 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
9252 }
9253 else
9254 {
9255 if (need_defi || mng_info->need_fram || use_global_plte)
9256 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
9257 else
9258 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
9259 }
9260 }
9261 else
9262 {
9263 if (need_matte)
9264 {
9265 if (need_defi || mng_info->need_fram || use_global_plte)
9266 PNGLong(chunk+28,11L); /* simplicity=LC */
9267 else
9268 PNGLong(chunk+28,9L); /* simplicity=VLC */
9269 }
9270 else
9271 {
9272 if (need_defi || mng_info->need_fram || use_global_plte)
9273 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
9274 else
9275 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
9276 }
9277 }
9278 (void) WriteBlob(image,32,chunk);
9279 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9280 option=GetImageOption(image_info,"mng:need-cacheoff");
9281 if (option != (const char *) NULL)
9282 {
9283 size_t
9284 length;
9285
9286 /*
9287 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9288 */
9289 PNGType(chunk,mng_nEED);
9290 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +00009291 (void) WriteBlobMSBULong(image,(size_t) length);
9292 LogPNGChunk((int) logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00009293 length+=4;
9294 (void) WriteBlob(image,length,chunk);
9295 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9296 }
9297 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9298 (GetNextImageInList(image) != (Image *) NULL) &&
9299 (image->iterations != 1))
9300 {
9301 /*
9302 Write MNG TERM chunk
9303 */
9304 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9305 PNGType(chunk,mng_TERM);
9306 LogPNGChunk((int) logging,mng_TERM,10L);
9307 chunk[4]=3; /* repeat animation */
9308 chunk[5]=0; /* show last frame when done */
9309 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9310 final_delay/MagickMax(image->ticks_per_second,1)));
9311 if (image->iterations == 0)
9312 PNGLong(chunk+10,PNG_UINT_31_MAX);
9313 else
9314 PNGLong(chunk+10,(png_uint_32) image->iterations);
9315 if (logging != MagickFalse)
9316 {
9317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009318 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
9319 final_delay/MagickMax(image->ticks_per_second,1)));
cristy3ed852e2009-09-05 21:47:34 +00009320 if (image->iterations == 0)
9321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009322 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
cristy3ed852e2009-09-05 21:47:34 +00009323 else
9324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009325 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00009326 }
9327 (void) WriteBlob(image,14,chunk);
9328 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9329 }
9330 /*
9331 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9332 */
9333 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9334 mng_info->equal_srgbs)
9335 {
9336 /*
9337 Write MNG sRGB chunk
9338 */
9339 (void) WriteBlobMSBULong(image,1L);
9340 PNGType(chunk,mng_sRGB);
9341 LogPNGChunk((int) logging,mng_sRGB,1L);
9342 if (image->rendering_intent != UndefinedIntent)
9343 chunk[4]=(unsigned char) (image->rendering_intent-1);
9344 else
9345 chunk[4]=(unsigned char) (PerceptualIntent-1);
9346 (void) WriteBlob(image,5,chunk);
9347 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9348 mng_info->have_write_global_srgb=MagickTrue;
9349 }
9350 else
9351 {
9352 if (image->gamma && mng_info->equal_gammas)
9353 {
9354 /*
9355 Write MNG gAMA chunk
9356 */
9357 (void) WriteBlobMSBULong(image,4L);
9358 PNGType(chunk,mng_gAMA);
9359 LogPNGChunk((int) logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +00009360 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009361 (void) WriteBlob(image,8,chunk);
9362 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9363 mng_info->have_write_global_gama=MagickTrue;
9364 }
9365 if (mng_info->equal_chrms)
9366 {
9367 PrimaryInfo
9368 primary;
9369
9370 /*
9371 Write MNG cHRM chunk
9372 */
9373 (void) WriteBlobMSBULong(image,32L);
9374 PNGType(chunk,mng_cHRM);
9375 LogPNGChunk((int) logging,mng_cHRM,32L);
9376 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +00009377 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
9378 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009379 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +00009380 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
9381 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009382 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +00009383 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
9384 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009385 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +00009386 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
9387 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009388 (void) WriteBlob(image,36,chunk);
9389 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9390 mng_info->have_write_global_chrm=MagickTrue;
9391 }
9392 }
9393 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9394 {
9395 /*
9396 Write MNG pHYs chunk
9397 */
9398 (void) WriteBlobMSBULong(image,9L);
9399 PNGType(chunk,mng_pHYs);
9400 LogPNGChunk((int) logging,mng_pHYs,9L);
9401 if (image->units == PixelsPerInchResolution)
9402 {
cristy35ef8242010-06-03 16:24:13 +00009403 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00009404 (image->x_resolution*100.0/2.54+0.5));
cristy35ef8242010-06-03 16:24:13 +00009405 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00009406 (image->y_resolution*100.0/2.54+0.5));
9407 chunk[12]=1;
9408 }
9409 else
9410 {
9411 if (image->units == PixelsPerCentimeterResolution)
9412 {
cristy35ef8242010-06-03 16:24:13 +00009413 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00009414 (image->x_resolution*100.0+0.5));
cristy35ef8242010-06-03 16:24:13 +00009415 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +00009416 (image->y_resolution*100.0+0.5));
9417 chunk[12]=1;
9418 }
9419 else
9420 {
cristy35ef8242010-06-03 16:24:13 +00009421 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
9422 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +00009423 chunk[12]=0;
9424 }
9425 }
9426 (void) WriteBlob(image,13,chunk);
9427 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9428 }
9429 /*
9430 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9431 or does not cover the entire frame.
9432 */
9433 if (write_mng && (image->matte || image->page.x > 0 ||
9434 image->page.y > 0 || (image->page.width &&
9435 (image->page.width+image->page.x < mng_info->page.width))
9436 || (image->page.height && (image->page.height+image->page.y
9437 < mng_info->page.height))))
9438 {
9439 (void) WriteBlobMSBULong(image,6L);
9440 PNGType(chunk,mng_BACK);
9441 LogPNGChunk((int) logging,mng_BACK,6L);
9442 red=ScaleQuantumToShort(image->background_color.red);
9443 green=ScaleQuantumToShort(image->background_color.green);
9444 blue=ScaleQuantumToShort(image->background_color.blue);
9445 PNGShort(chunk+4,red);
9446 PNGShort(chunk+6,green);
9447 PNGShort(chunk+8,blue);
9448 (void) WriteBlob(image,10,chunk);
9449 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9450 if (mng_info->equal_backgrounds)
9451 {
9452 (void) WriteBlobMSBULong(image,6L);
9453 PNGType(chunk,mng_bKGD);
9454 LogPNGChunk((int) logging,mng_bKGD,6L);
9455 (void) WriteBlob(image,10,chunk);
9456 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9457 }
9458 }
9459
9460#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9461 if ((need_local_plte == MagickFalse) &&
9462 (image->storage_class == PseudoClass) &&
9463 (all_images_are_gray == MagickFalse))
9464 {
cristybb503372010-05-27 20:51:26 +00009465 size_t
cristy3ed852e2009-09-05 21:47:34 +00009466 data_length;
9467
9468 /*
9469 Write MNG PLTE chunk
9470 */
9471 data_length=3*image->colors;
9472 (void) WriteBlobMSBULong(image,data_length);
9473 PNGType(chunk,mng_PLTE);
9474 LogPNGChunk((int) logging,mng_PLTE,data_length);
cristybb503372010-05-27 20:51:26 +00009475 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009476 {
9477 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9478 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9479 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9480 }
9481 (void) WriteBlob(image,data_length+4,chunk);
9482 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9483 mng_info->have_write_global_plte=MagickTrue;
9484 }
9485#endif
9486 }
9487 scene=0;
9488 mng_info->delay=0;
9489#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9490 defined(PNG_MNG_FEATURES_SUPPORTED)
9491 mng_info->equal_palettes=MagickFalse;
9492#endif
9493 do
9494 {
9495 if (mng_info->adjoin)
9496 {
9497#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9498 defined(PNG_MNG_FEATURES_SUPPORTED)
9499 /*
9500 If we aren't using a global palette for the entire MNG, check to
9501 see if we can use one for two or more consecutive images.
9502 */
9503 if (need_local_plte && use_global_plte && !all_images_are_gray)
9504 {
9505 if (mng_info->IsPalette)
9506 {
9507 /*
9508 When equal_palettes is true, this image has the same palette
9509 as the previous PseudoClass image
9510 */
9511 mng_info->have_write_global_plte=mng_info->equal_palettes;
9512 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9513 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9514 {
9515 /*
9516 Write MNG PLTE chunk
9517 */
cristybb503372010-05-27 20:51:26 +00009518 size_t
cristy3ed852e2009-09-05 21:47:34 +00009519 data_length;
9520
9521 data_length=3*image->colors;
9522 (void) WriteBlobMSBULong(image,data_length);
9523 PNGType(chunk,mng_PLTE);
9524 LogPNGChunk((int) logging,mng_PLTE,data_length);
cristybb503372010-05-27 20:51:26 +00009525 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009526 {
9527 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9528 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9529 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9530 }
9531 (void) WriteBlob(image,data_length+4,chunk);
9532 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9533 (uInt) (data_length+4)));
9534 mng_info->have_write_global_plte=MagickTrue;
9535 }
9536 }
9537 else
9538 mng_info->have_write_global_plte=MagickFalse;
9539 }
9540#endif
9541 if (need_defi)
9542 {
cristybb503372010-05-27 20:51:26 +00009543 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00009544 previous_x,
9545 previous_y;
9546
9547 if (scene)
9548 {
9549 previous_x=mng_info->page.x;
9550 previous_y=mng_info->page.y;
9551 }
9552 else
9553 {
9554 previous_x=0;
9555 previous_y=0;
9556 }
9557 mng_info->page=image->page;
9558 if ((mng_info->page.x != previous_x) ||
9559 (mng_info->page.y != previous_y))
9560 {
9561 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
9562 PNGType(chunk,mng_DEFI);
9563 LogPNGChunk((int) logging,mng_DEFI,12L);
9564 chunk[4]=0; /* object 0 MSB */
9565 chunk[5]=0; /* object 0 LSB */
9566 chunk[6]=0; /* visible */
9567 chunk[7]=0; /* abstract */
9568 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9569 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9570 (void) WriteBlob(image,16,chunk);
9571 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9572 }
9573 }
9574 }
9575
9576 mng_info->write_mng=write_mng;
9577
9578 if ((int) image->dispose >= 3)
9579 mng_info->framing_mode=3;
9580
9581 if (mng_info->need_fram && mng_info->adjoin &&
9582 ((image->delay != mng_info->delay) ||
9583 (mng_info->framing_mode != mng_info->old_framing_mode)))
9584 {
9585 if (image->delay == mng_info->delay)
9586 {
9587 /*
9588 Write a MNG FRAM chunk with the new framing mode.
9589 */
9590 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
9591 PNGType(chunk,mng_FRAM);
9592 LogPNGChunk((int) logging,mng_FRAM,1L);
9593 chunk[4]=(unsigned char) mng_info->framing_mode;
9594 (void) WriteBlob(image,5,chunk);
9595 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9596 }
9597 else
9598 {
9599 /*
9600 Write a MNG FRAM chunk with the delay.
9601 */
9602 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9603 PNGType(chunk,mng_FRAM);
9604 LogPNGChunk((int) logging,mng_FRAM,10L);
9605 chunk[4]=(unsigned char) mng_info->framing_mode;
9606 chunk[5]=0; /* frame name separator (no name) */
9607 chunk[6]=2; /* flag for changing default delay */
9608 chunk[7]=0; /* flag for changing frame timeout */
9609 chunk[8]=0; /* flag for changing frame clipping */
9610 chunk[9]=0; /* flag for changing frame sync_id */
9611 PNGLong(chunk+10,(png_uint_32)
9612 ((mng_info->ticks_per_second*
9613 image->delay)/MagickMax(image->ticks_per_second,1)));
9614 (void) WriteBlob(image,14,chunk);
9615 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +00009616 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +00009617 }
9618 mng_info->old_framing_mode=mng_info->framing_mode;
9619 }
9620
9621#if defined(JNG_SUPPORTED)
9622 if (image_info->compression == JPEGCompression)
9623 {
9624 ImageInfo
9625 *write_info;
9626
9627 if (logging != MagickFalse)
9628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9629 " Writing JNG object.");
9630 /* To do: specify the desired alpha compression method. */
9631 write_info=CloneImageInfo(image_info);
9632 write_info->compression=UndefinedCompression;
9633 status=WriteOneJNGImage(mng_info,write_info,image);
9634 write_info=DestroyImageInfo(write_info);
9635 }
9636 else
9637#endif
9638 {
9639 if (logging != MagickFalse)
9640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9641 " Writing PNG object.");
9642 status=WriteOnePNGImage(mng_info,image_info,image);
9643 }
9644
9645 if (status == MagickFalse)
9646 {
9647 MngInfoFreeStruct(mng_info,&have_mng_structure);
9648 (void) CloseBlob(image);
9649 return(MagickFalse);
9650 }
9651 (void) CatchImageException(image);
9652 if (GetNextImageInList(image) == (Image *) NULL)
9653 break;
9654 image=SyncNextImageInList(image);
9655 status=SetImageProgress(image,SaveImagesTag,scene++,
9656 GetImageListLength(image));
9657 if (status == MagickFalse)
9658 break;
9659 } while (mng_info->adjoin);
9660 if (write_mng)
9661 {
9662 while (GetPreviousImageInList(image) != (Image *) NULL)
9663 image=GetPreviousImageInList(image);
9664 /*
9665 Write the MEND chunk.
9666 */
9667 (void) WriteBlobMSBULong(image,0x00000000L);
9668 PNGType(chunk,mng_MEND);
9669 LogPNGChunk((int) logging,mng_MEND,0L);
9670 (void) WriteBlob(image,4,chunk);
9671 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9672 }
9673 /*
9674 Relinquish resources.
9675 */
9676 (void) CloseBlob(image);
9677 MngInfoFreeStruct(mng_info,&have_mng_structure);
9678 if (logging != MagickFalse)
9679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9680 return(MagickTrue);
9681}
glennrpd5045b42010-03-24 12:40:35 +00009682#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00009683static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9684{
9685 image=image;
9686 printf("Your PNG library is too old: You have libpng-%s\n",
9687 PNG_LIBPNG_VER_STRING);
9688 ThrowBinaryException(CoderError,"PNG library is too old",
9689 image_info->filename);
9690}
9691static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9692{
9693 return(WritePNGImage(image_info,image));
9694}
glennrpd5045b42010-03-24 12:40:35 +00009695#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00009696#endif