blob: c45587b4e1a811459a3ee8ed00452e5e6a6ac79f [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% %
21% Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization %
22% 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"
51#include "magick/colorspace.h"
52#include "magick/constitute.h"
53#include "magick/enhance.h"
54#include "magick/exception.h"
55#include "magick/exception-private.h"
56#include "magick/geometry.h"
cristyf2e11662009-10-14 01:24:43 +000057#include "magick/histogram.h"
cristy3ed852e2009-09-05 21:47:34 +000058#include "magick/image.h"
59#include "magick/image-private.h"
60#include "magick/layer.h"
61#include "magick/list.h"
62#include "magick/log.h"
63#include "magick/magick.h"
64#include "magick/memory_.h"
65#include "magick/module.h"
66#include "magick/monitor.h"
67#include "magick/monitor-private.h"
68#include "magick/option.h"
69#include "magick/quantum-private.h"
70#include "magick/profile.h"
71#include "magick/property.h"
72#include "magick/quantize.h"
73#include "magick/resource_.h"
74#include "magick/semaphore.h"
75#include "magick/quantum-private.h"
76#include "magick/static.h"
77#include "magick/statistic.h"
78#include "magick/string_.h"
79#include "magick/transform.h"
80#include "magick/utility.h"
81#if defined(MAGICKCORE_PNG_DELEGATE)
82#include "png.h"
83#include "zlib.h"
84
85/* ImageMagick differences */
86#define first_scene scene
87
88#if PNG_LIBPNG_VER < 10400
89#define trans_color trans_values /* Changed at libpng-1.4.0beta35 */
90#define trans_alpha trans /* Changed at libpng-1.4.0beta74 */
91#endif
92
93#if PNG_LIBPNG_VER > 95
94/*
95 Optional declarations. Define or undefine them as you like.
96*/
97/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
98
99/*
100 Features under construction. Define these to work on them.
101*/
102#undef MNG_OBJECT_BUFFERS
103#undef MNG_BASI_SUPPORTED
104#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
105#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
106#define PNG_BUILD_PALETTE /* This works as of 5.4.3. */
107#define PNG_SORT_PALETTE /* This works as of 5.4.0. */
108#if defined(MAGICKCORE_JPEG_DELEGATE)
109# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
110#endif
111#if !defined(RGBColorMatchExact)
112#define IsPNGColorEqual(color,target) \
113 (((color).red == (target).red) && \
114 ((color).green == (target).green) && \
115 ((color).blue == (target).blue))
116#endif
117
118/*
119 Establish thread safety.
120 setjmp/longjmp is claimed to be safe on these platforms:
121 setjmp/longjmp is alleged to be unsafe on these platforms:
122*/
123#ifndef SETJMP_IS_THREAD_SAFE
124#define PNG_SETJMP_NOT_THREAD_SAFE
125#endif
126
127#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
128static SemaphoreInfo
129 *png_semaphore = (SemaphoreInfo *) NULL;
130#endif
131
132/*
133 This temporary until I set up malloc'ed object attributes array.
134 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
135 waste more memory.
136*/
137#define MNG_MAX_OBJECTS 256
138
139/*
140 If this not defined, spec is interpreted strictly. If it is
141 defined, an attempt will be made to recover from some errors,
142 including
143 o global PLTE too short
144*/
145#undef MNG_LOOSE
146
147/*
148 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
149 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
150 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
151 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
152 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
153 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
154 will be enabled by default in libpng-1.2.0.
155*/
156#if (PNG_LIBPNG_VER == 10009) /* work around libpng-1.0.9 bug */
157# undef PNG_READ_EMPTY_PLTE_SUPPORTED
158# undef PNG_WRITE_EMPTY_PLTE_SUPPORTED
159#endif
160#ifdef PNG_MNG_FEATURES_SUPPORTED
161# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
162# define PNG_READ_EMPTY_PLTE_SUPPORTED
163# endif
164# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
165# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
166# endif
167#endif
168
169/*
170 Maximum valid unsigned long in PNG/MNG chunks is (2^31)-1
171 This macro is only defined in libpng-1.0.3 and later.
172 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
173*/
174#ifndef PNG_UINT_31_MAX
175#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
176#endif
177
178/*
179 Constant strings for known chunk types. If you need to add a chunk,
180 add a string holding the name here. To make the code more
181 portable, we use ASCII numbers like this, not characters.
182*/
183
184static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
185static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
186static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
187static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
188static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
189static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
190static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
191static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
192static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
193static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
194static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
195static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
196static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
197static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
198static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
199static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
200static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
201static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
202static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
203static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
204static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
205static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
206static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
207static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
208static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
209static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
210static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
211static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
212static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
213static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
214static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
215static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
216static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
217static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
218
219#if defined(JNG_SUPPORTED)
220static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
221static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
222static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
223static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
224static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
225static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
226#endif
227
228/*
229Other known chunks that are not yet supported by ImageMagick:
230static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
231static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
232static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
233static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
234static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
235static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
236static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
237static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
238*/
239
240typedef struct _MngBox
241{
242 long
243 left,
244 right,
245 top,
246 bottom;
247} MngBox;
248
249typedef struct _MngPair
250{
251 volatile long
252 a,
253 b;
254} MngPair;
255
256#ifdef MNG_OBJECT_BUFFERS
257typedef struct _MngBuffer
258{
259
260 unsigned long
261 height,
262 width;
263
264 Image
265 *image;
266
267 png_color
268 plte[256];
269
270 int
271 reference_count;
272
273 unsigned char
274 alpha_sample_depth,
275 compression_method,
276 color_type,
277 concrete,
278 filter_method,
279 frozen,
280 image_type,
281 interlace_method,
282 pixel_sample_depth,
283 plte_length,
284 sample_depth,
285 viewable;
286} MngBuffer;
287#endif
288
289typedef struct _MngInfo
290{
291
292#ifdef MNG_OBJECT_BUFFERS
293 MngBuffer
294 *ob[MNG_MAX_OBJECTS];
295#endif
296
297 Image *
298 image;
299
300 RectangleInfo
301 page;
302
303 int
304 adjoin,
305#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
306 bytes_in_read_buffer,
307 found_empty_plte,
308#endif
309 equal_backgrounds,
310 equal_chrms,
311 equal_gammas,
312#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
313 defined(PNG_MNG_FEATURES_SUPPORTED)
314 equal_palettes,
315#endif
316 equal_physs,
317 equal_srgbs,
318 framing_mode,
319 have_global_bkgd,
320 have_global_chrm,
321 have_global_gama,
322 have_global_phys,
323 have_global_sbit,
324 have_global_srgb,
325 have_saved_bkgd_index,
326 have_write_global_chrm,
327 have_write_global_gama,
328 have_write_global_plte,
329 have_write_global_srgb,
330 need_fram,
331 object_id,
332 old_framing_mode,
333 optimize,
334 saved_bkgd_index;
335
336 int
337 new_number_colors;
338
339 long
340 image_found,
341 loop_count[256],
342 loop_iteration[256],
343 scenes_found,
344 x_off[MNG_MAX_OBJECTS],
345 y_off[MNG_MAX_OBJECTS];
346
347 MngBox
348 clip,
349 frame,
350 image_box,
351 object_clip[MNG_MAX_OBJECTS];
352
353 unsigned char
354 /* These flags could be combined into one byte */
355 exists[MNG_MAX_OBJECTS],
356 frozen[MNG_MAX_OBJECTS],
357 loop_active[256],
358 invisible[MNG_MAX_OBJECTS],
359 viewable[MNG_MAX_OBJECTS];
360
361 MagickOffsetType
362 loop_jump[256];
363
364 png_colorp
365 global_plte;
366
367 png_color_8
368 global_sbit;
369
370 png_byte
371#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
372 read_buffer[8],
373#endif
374 global_trns[256];
375
376 float
377 global_gamma;
378
379 ChromaticityInfo
380 global_chrm;
381
382 RenderingIntent
383 global_srgb_intent;
384
385 unsigned long
386 delay,
387 global_plte_length,
388 global_trns_length,
389 global_x_pixels_per_unit,
390 global_y_pixels_per_unit,
391 mng_width,
392 mng_height,
393 ticks_per_second;
394
395 unsigned int
396 IsPalette,
397 global_phys_unit_type,
398 basi_warning,
399 clon_warning,
400 dhdr_warning,
401 jhdr_warning,
402 magn_warning,
403 past_warning,
404 phyg_warning,
405 phys_warning,
406 sbit_warning,
407 show_warning,
408 mng_type,
409 write_mng,
410 write_png_colortype,
411 write_png_depth,
412 write_png8,
413 write_png24,
414 write_png32;
415
416#ifdef MNG_BASI_SUPPORTED
417 unsigned long
418 basi_width,
419 basi_height;
420
421 unsigned int
422 basi_depth,
423 basi_color_type,
424 basi_compression_method,
425 basi_filter_type,
426 basi_interlace_method,
427 basi_red,
428 basi_green,
429 basi_blue,
430 basi_alpha,
431 basi_viewable;
432#endif
433
434 png_uint_16
435 magn_first,
436 magn_last,
437 magn_mb,
438 magn_ml,
439 magn_mr,
440 magn_mt,
441 magn_mx,
442 magn_my,
443 magn_methx,
444 magn_methy;
445
446 PixelPacket
447 mng_global_bkgd;
448
449} MngInfo;
450#endif /* VER */
451
452/*
453 Forward declarations.
454*/
455static MagickBooleanType
456 WritePNGImage(const ImageInfo *,Image *);
457static MagickBooleanType
458 WriteMNGImage(const ImageInfo *,Image *);
459#if defined(JNG_SUPPORTED)
460static MagickBooleanType
461 WriteJNGImage(const ImageInfo *,Image *);
462#endif
463
464static inline long MagickMax(const long x,const long y)
465{
466 if (x > y)
467 return(x);
468 return(y);
469}
470static inline long MagickMin(const long x,const long y)
471{
472 if (x < y)
473 return(x);
474 return(y);
475}
476
477#if PNG_LIBPNG_VER > 95
478#if defined(PNG_SORT_PALETTE)
479/*
480%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
481% %
482% %
483% %
484% 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 %
485% %
486% %
487% %
488%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
489%
490% CompressColormapTransFirst compresses an image colormap removing
491% any duplicate and unused color entries and putting the transparent colors
492% first. Returns MagickTrue on success, MagickFalse on error.
493%
494% The format of the CompressColormapTransFirst method is:
495%
496% unsigned int CompressColormapTransFirst(Image *image)
497%
498% A description of each parameter follows:
499%
500% o image: the address of a structure of type Image.
501%
502*/
503static MagickBooleanType CompressColormapTransFirst(Image *image)
504{
505 int
506 remap_needed,
507 k;
508
509 long
510 j,
511 new_number_colors,
512 number_colors,
513 y;
514
515 PixelPacket
516 *colormap;
517
518 register const IndexPacket
519 *indices;
520
521 register const PixelPacket
522 *p;
523
524 IndexPacket
525 top_used;
526
527 register long
528 i,
529 x;
530
531 IndexPacket
532 *map,
533 *opacity;
534
535 unsigned char
536 *marker,
537 have_transparency;
538
539 /*
540 Determine if colormap can be compressed.
541 */
542 assert(image != (Image *) NULL);
543 assert(image->signature == MagickSignature);
544 if (image->debug != MagickFalse)
545 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
546 if (image->storage_class != PseudoClass || image->colors > 256 ||
547 image->colors < 2)
548 return(MagickFalse);
549 marker=(unsigned char *) AcquireQuantumMemory(image->colors,sizeof(*marker));
550 if (marker == (unsigned char *) NULL)
551 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
552 image->filename);
553 opacity=(IndexPacket *) AcquireQuantumMemory(image->colors,sizeof(*opacity));
554 if (opacity == (IndexPacket *) NULL)
555 {
556 marker=(unsigned char *) RelinquishMagickMemory(marker);
557 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
558 image->filename);
559 }
560 /*
561 Mark colors that are present.
562 */
563 number_colors=(long) image->colors;
564 for (i=0; i < number_colors; i++)
565 {
566 marker[i]=MagickFalse;
567 opacity[i]=OpaqueOpacity;
568 }
569 top_used=0;
570 for (y=0; y < (long) image->rows; y++)
571 {
572 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
573 if (p == (const PixelPacket *) NULL)
574 break;
575 indices=GetVirtualIndexQueue(image);
576 if (image->matte != MagickFalse)
577 for (x=0; x < (long) image->columns; x++)
578 {
579 marker[(int) indices[x]]=MagickTrue;
580 opacity[(int) indices[x]]=p->opacity;
581 if (indices[x] > top_used)
582 top_used=indices[x];
583 p++;
584 }
585 else
586 for (x=0; x < (long) image->columns; x++)
587 {
588 marker[(int) indices[x]]=MagickTrue;
589 if (indices[x] > top_used)
590 top_used=indices[x];
591 }
592 }
593
594 if (image->matte != MagickFalse)
595 {
596 /*
597 Mark background color, topmost occurrence if more than one.
598 */
599 for (i=number_colors-1; i; i--)
600 {
601 if (IsColorEqual(image->colormap+i,&image->background_color))
602 {
603 marker[i]=MagickTrue;
604 break;
605 }
606 }
607 }
608 /*
609 Unmark duplicates.
610 */
611 for (i=0; i < number_colors-1; i++)
612 if (marker[i])
613 {
614 for (j=i+1; j < number_colors; j++)
615 if ((opacity[i] == opacity[j]) &&
616 (IsColorEqual(image->colormap+i,image->colormap+j)))
617 marker[j]=MagickFalse;
618 }
619 /*
620 Count colors that still remain.
621 */
622 have_transparency=MagickFalse;
623 new_number_colors=0;
624 for (i=0; i < number_colors; i++)
625 if (marker[i])
626 {
627 new_number_colors++;
628 if (opacity[i] != OpaqueOpacity)
629 have_transparency=MagickTrue;
630 }
631 if ((!have_transparency || (marker[0] &&
632 (opacity[0] == (Quantum) TransparentOpacity)))
633 && (new_number_colors == number_colors))
634 {
635 /*
636 No duplicate or unused entries, and transparency-swap not needed.
637 */
638 marker=(unsigned char *) RelinquishMagickMemory(marker);
639 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
640 return(MagickTrue);
641 }
642
643 remap_needed=MagickFalse;
644 if ((long) top_used >= new_number_colors)
645 remap_needed=MagickTrue;
646
647 /*
648 Compress colormap.
649 */
650
651 colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
652 sizeof(*colormap));
653 if (colormap == (PixelPacket *) NULL)
654 {
655 marker=(unsigned char *) RelinquishMagickMemory(marker);
656 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
657 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
658 image->filename);
659 }
660 /*
661 Eliminate unused colormap entries.
662 */
663 map=(IndexPacket *) AcquireQuantumMemory((size_t) number_colors,
664 sizeof(*map));
665 if (map == (IndexPacket *) NULL)
666 {
667 marker=(unsigned char *) RelinquishMagickMemory(marker);
668 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
669 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
670 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
671 image->filename);
672 }
673 k=0;
674 for (i=0; i < number_colors; i++)
675 {
676 map[i]=(IndexPacket) k;
677 if (marker[i])
678 {
679 for (j=i+1; j < number_colors; j++)
680 {
681 if ((opacity[i] == opacity[j]) &&
682 (IsColorEqual(image->colormap+i,image->colormap+j)))
683 {
684 map[j]=(IndexPacket) k;
685 marker[j]=MagickFalse;
686 }
687 }
688 k++;
689 }
690 }
691 j=0;
692 for (i=0; i < number_colors; i++)
693 {
694 if (marker[i])
695 {
696 colormap[j]=image->colormap[i];
697 j++;
698 }
699 }
700 if (have_transparency && (opacity[0] != (Quantum) TransparentOpacity))
701 {
702 /*
703 Move the first transparent color to palette entry 0.
704 */
705 for (i=1; i < number_colors; i++)
706 {
707 if (marker[i] && opacity[i] == (Quantum) TransparentOpacity)
708 {
709 PixelPacket
710 temp_colormap;
711
712 temp_colormap=colormap[0];
713 colormap[0]=colormap[(int) map[i]];
714 colormap[(long) map[i]]=temp_colormap;
715 for (j=0; j < number_colors; j++)
716 {
717 if (map[j] == 0)
718 map[j]=map[i];
719 else if (map[j] == map[i])
720 map[j]=0;
721 }
722 remap_needed=MagickTrue;
723 break;
724 }
725 }
726 }
727
728 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
729 marker=(unsigned char *) RelinquishMagickMemory(marker);
730
731 if (remap_needed)
732 {
733 ExceptionInfo
734 *exception;
735
736 register IndexPacket
737 *pixels;
738
739 register PixelPacket
740 *q;
741
742 /*
743 Remap pixels.
744 */
745 exception=(&image->exception);
746 for (y=0; y < (long) image->rows; y++)
747 {
748 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
749 if (q == (PixelPacket *) NULL)
750 break;
751 pixels=GetAuthenticIndexQueue(image);
752 for (x=0; x < (long) image->columns; x++)
753 {
754 j=(int) pixels[x];
755 pixels[x]=map[j];
756 }
757 if (SyncAuthenticPixels(image,exception) == MagickFalse)
758 break;
759 }
760 for (i=0; i < new_number_colors; i++)
761 image->colormap[i]=colormap[i];
762 }
763 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
764 image->colors=(unsigned long) new_number_colors;
765 map=(IndexPacket *) RelinquishMagickMemory(map);
766 return(MagickTrue);
767}
768#endif
769
770/*
771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
772% %
773% %
774% %
775% I m a g e I s G r a y %
776% %
777% %
778% %
779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780% %
781% Like IsGrayImage except does not change DirectClass to PseudoClass %
782% %
783%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784*/
785static MagickBooleanType ImageIsGray(Image *image)
786{
787 register const PixelPacket
788 *p;
789
790 register long
791 i,
792 x,
793 y;
794
795 assert(image != (Image *) NULL);
796 assert(image->signature == MagickSignature);
797 if (image->debug != MagickFalse)
798 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
799
800 if (image->storage_class == PseudoClass)
801 {
802 for (i=0; i < (long) image->colors; i++)
803 if (IsGray(image->colormap+i) == MagickFalse)
804 return(MagickFalse);
805 return(MagickTrue);
806 }
807 for (y=0; y < (long) image->rows; y++)
808 {
809 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
810 if (p == (const PixelPacket *) NULL)
811 return(MagickFalse);
812 for (x=(long) image->columns-1; x >= 0; x--)
813 {
814 if (IsGray(p) == MagickFalse)
815 return(MagickFalse);
816 p++;
817 }
818 }
819 return(MagickTrue);
820}
821
822/*
823%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
824% %
825% %
826% %
827% I m a g e I s M o n o c h r o m e %
828% %
829% %
830% %
831%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
832% %
833% Like IsMonochromeImage except does not change DirectClass to PseudoClass %
834% and is more accurate. %
835% %
836%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
837*/
838static MagickBooleanType ImageIsMonochrome(Image *image)
839{
840 register const PixelPacket
841 *p;
842
843 register long
844 i,
845 x,
846 y;
847
848 assert(image != (Image *) NULL);
849 assert(image->signature == MagickSignature);
850 if (image->debug != MagickFalse)
851 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
852 if (image->storage_class == PseudoClass)
853 {
854 for (i=0; i < (long) image->colors; i++)
855 {
856 if ((IsGray(image->colormap+i) == MagickFalse) ||
857 ((image->colormap[i].red != 0) &&
858 (image->colormap[i].red != (Quantum) QuantumRange)))
859 return(MagickFalse);
860 }
861 return(MagickTrue);
862 }
863 for (y=0; y < (long) image->rows; y++)
864 {
865 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
866 if (p == (const PixelPacket *) NULL)
867 return(MagickFalse);
868 for (x=(long) image->columns-1; x >= 0; x--)
869 {
870 if ((p->red != 0) && (p->red != (Quantum) QuantumRange))
871 return(MagickFalse);
872 if (IsGray(p) == MagickFalse)
873 return(MagickFalse);
874 if ((p->opacity != OpaqueOpacity) &&
875 (p->opacity != (Quantum) TransparentOpacity))
876 return(MagickFalse);
877 p++;
878 }
879 }
880 return(MagickTrue);
881}
882#endif /* PNG_LIBPNG_VER > 95 */
883#endif /* MAGICKCORE_PNG_DELEGATE */
884
885/*
886%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
887% %
888% %
889% %
890% I s M N G %
891% %
892% %
893% %
894%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
895%
896% IsMNG() returns MagickTrue if the image format type, identified by the
897% magick string, is MNG.
898%
899% The format of the IsMNG method is:
900%
901% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
902%
903% A description of each parameter follows:
904%
905% o magick: compare image format pattern against these bytes.
906%
907% o length: Specifies the length of the magick string.
908%
909%
910*/
911static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
912{
913 if (length < 8)
914 return(MagickFalse);
915 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
916 return(MagickTrue);
917 return(MagickFalse);
918}
919
920/*
921%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
922% %
923% %
924% %
925% I s J N G %
926% %
927% %
928% %
929%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
930%
931% IsJNG() returns MagickTrue if the image format type, identified by the
932% magick string, is JNG.
933%
934% The format of the IsJNG method is:
935%
936% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
937%
938% A description of each parameter follows:
939%
940% o magick: compare image format pattern against these bytes.
941%
942% o length: Specifies the length of the magick string.
943%
944%
945*/
946static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
947{
948 if (length < 8)
949 return(MagickFalse);
950 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
951 return(MagickTrue);
952 return(MagickFalse);
953}
954
955/*
956%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
957% %
958% %
959% %
960% I s P N G %
961% %
962% %
963% %
964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
965%
966% IsPNG() returns MagickTrue if the image format type, identified by the
967% magick string, is PNG.
968%
969% The format of the IsPNG method is:
970%
971% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
972%
973% A description of each parameter follows:
974%
975% o magick: compare image format pattern against these bytes.
976%
977% o length: Specifies the length of the magick string.
978%
979*/
980static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
981{
982 if (length < 8)
983 return(MagickFalse);
984 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
985 return(MagickTrue);
986 return(MagickFalse);
987}
988
989#if defined(MAGICKCORE_PNG_DELEGATE)
990#if defined(__cplusplus) || defined(c_plusplus)
991extern "C" {
992#endif
993
994#if (PNG_LIBPNG_VER > 95)
995static size_t WriteBlobMSBULong(Image *image,const unsigned long value)
996{
997 unsigned char
998 buffer[4];
999
1000 assert(image != (Image *) NULL);
1001 assert(image->signature == MagickSignature);
1002 buffer[0]=(unsigned char) (value >> 24);
1003 buffer[1]=(unsigned char) (value >> 16);
1004 buffer[2]=(unsigned char) (value >> 8);
1005 buffer[3]=(unsigned char) value;
1006 return((size_t) WriteBlob(image,4,buffer));
1007}
1008
1009static void PNGLong(png_bytep p,png_uint_32 value)
1010{
1011 *p++=(png_byte) ((value >> 24) & 0xff);
1012 *p++=(png_byte) ((value >> 16) & 0xff);
1013 *p++=(png_byte) ((value >> 8) & 0xff);
1014 *p++=(png_byte) (value & 0xff);
1015}
1016
1017static void PNGsLong(png_bytep p,png_int_32 value)
1018{
1019 *p++=(png_byte) ((value >> 24) & 0xff);
1020 *p++=(png_byte) ((value >> 16) & 0xff);
1021 *p++=(png_byte) ((value >> 8) & 0xff);
1022 *p++=(png_byte) (value & 0xff);
1023}
1024
1025static void PNGShort(png_bytep p,png_uint_16 value)
1026{
1027 *p++=(png_byte) ((value >> 8) & 0xff);
1028 *p++=(png_byte) (value & 0xff);
1029}
1030
1031static void PNGType(png_bytep p,png_bytep type)
1032{
1033 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1034}
1035
1036static void LogPNGChunk(int logging, png_bytep type, size_t length)
1037{
1038 if (logging != MagickFalse)
1039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1040 " Writing %c%c%c%c chunk, length: %lu",
1041 type[0],type[1],type[2],type[3],(unsigned long) length);
1042}
1043#endif /* PNG_LIBPNG_VER > 95 */
1044
1045#if defined(__cplusplus) || defined(c_plusplus)
1046}
1047#endif
1048
1049#if PNG_LIBPNG_VER > 95
1050/*
1051%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1052% %
1053% %
1054% %
1055% R e a d P N G I m a g e %
1056% %
1057% %
1058% %
1059%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1060%
1061% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1062% Multiple-image Network Graphics (MNG) image file and returns it. It
1063% allocates the memory necessary for the new Image structure and returns a
1064% pointer to the new image or set of images.
1065%
1066% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1067%
1068% The format of the ReadPNGImage method is:
1069%
1070% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1071%
1072% A description of each parameter follows:
1073%
1074% o image_info: the image info.
1075%
1076% o exception: return any errors or warnings in this structure.
1077%
1078% To do, more or less in chronological order (as of version 5.5.2,
1079% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1080%
1081% Get 16-bit cheap transparency working.
1082%
1083% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1084%
1085% Preserve all unknown and not-yet-handled known chunks found in input
1086% PNG file and copy them into output PNG files according to the PNG
1087% copying rules.
1088%
1089% (At this point, PNG encoding should be in full MNG compliance)
1090%
1091% Provide options for choice of background to use when the MNG BACK
1092% chunk is not present or is not mandatory (i.e., leave transparent,
1093% user specified, MNG BACK, PNG bKGD)
1094%
1095% Implement LOOP/ENDL [done, but could do discretionary loops more
1096% efficiently by linking in the duplicate frames.].
1097%
1098% Decode and act on the MHDR simplicity profile (offer option to reject
1099% files or attempt to process them anyway when the profile isn't LC or VLC).
1100%
1101% Upgrade to full MNG without Delta-PNG.
1102%
1103% o BACK [done a while ago except for background image ID]
1104% o MOVE [done 15 May 1999]
1105% o CLIP [done 15 May 1999]
1106% o DISC [done 19 May 1999]
1107% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1108% o SEEK [partially done 19 May 1999 (discard function only)]
1109% o SHOW
1110% o PAST
1111% o BASI
1112% o MNG-level tEXt/iTXt/zTXt
1113% o pHYg
1114% o pHYs
1115% o sBIT
1116% o bKGD
1117% o iTXt (wait for libpng implementation).
1118%
1119% Use the scene signature to discover when an identical scene is
1120% being reused, and just point to the original image->exception instead
1121% of storing another set of pixels. This not specific to MNG
1122% but could be applied generally.
1123%
1124% Upgrade to full MNG with Delta-PNG.
1125%
1126% JNG tEXt/iTXt/zTXt
1127%
1128% We will not attempt to read files containing the CgBI chunk.
1129% They are really Xcode files meant for display on the iPhone.
1130% These are not valid PNG files and it is impossible to recover
1131% the orginal PNG from files that have been converted to Xcode-PNG,
1132% since irretrievable loss of color data has occurred due to the
1133% use of premultiplied alpha.
1134*/
1135
1136#if defined(__cplusplus) || defined(c_plusplus)
1137extern "C" {
1138#endif
1139
1140/*
1141 This the function that does the actual reading of data. It is
1142 the same as the one supplied in libpng, except that it receives the
1143 datastream from the ReadBlob() function instead of standard input.
1144*/
1145static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1146{
1147 Image
1148 *image;
1149
1150 image=(Image *) png_get_io_ptr(png_ptr);
1151 if (length)
1152 {
1153 png_size_t
1154 check;
1155
1156 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1157 if (check != length)
1158 {
1159 char
1160 msg[MaxTextExtent];
1161
1162 (void) FormatMagickString(msg,MaxTextExtent,
1163 "Expected %lu bytes; found %lu bytes",(unsigned long) length,
1164 (unsigned long) check);
1165 png_warning(png_ptr,msg);
1166 png_error(png_ptr,"Read Exception");
1167 }
1168 }
1169}
1170
1171#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1172 !defined(PNG_MNG_FEATURES_SUPPORTED)
1173/* We use mng_get_data() instead of png_get_data() if we have a libpng
1174 * older than libpng-1.0.3a, which was the first to allow the empty
1175 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1176 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1177 * encountered after an empty PLTE, so we have to look ahead for bKGD
1178 * chunks and remove them from the datastream that is passed to libpng,
1179 * and store their contents for later use.
1180 */
1181static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1182{
1183 MngInfo
1184 *mng_info;
1185
1186 Image
1187 *image;
1188
1189 png_size_t
1190 check;
1191
1192 register long
1193 i;
1194
1195 i=0;
1196 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1197 image=(Image *) mng_info->image;
1198 while (mng_info->bytes_in_read_buffer && length)
1199 {
1200 data[i]=mng_info->read_buffer[i];
1201 mng_info->bytes_in_read_buffer--;
1202 length--;
1203 i++;
1204 }
1205 if (length)
1206 {
1207 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1208 if (check != length)
1209 png_error(png_ptr,"Read Exception");
1210 if (length == 4)
1211 {
1212 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1213 (data[3] == 0))
1214 {
1215 check=(png_size_t) ReadBlob(image,(size_t) length,
1216 (char *) mng_info->read_buffer);
1217 mng_info->read_buffer[4]=0;
1218 mng_info->bytes_in_read_buffer=4;
1219 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1220 mng_info->found_empty_plte=MagickTrue;
1221 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1222 {
1223 mng_info->found_empty_plte=MagickFalse;
1224 mng_info->have_saved_bkgd_index=MagickFalse;
1225 }
1226 }
1227 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1228 (data[3] == 1))
1229 {
1230 check=(png_size_t) ReadBlob(image,(size_t) length,
1231 (char *) mng_info->read_buffer);
1232 mng_info->read_buffer[4]=0;
1233 mng_info->bytes_in_read_buffer=4;
1234 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1235 if (mng_info->found_empty_plte)
1236 {
1237 /*
1238 Skip the bKGD data byte and CRC.
1239 */
1240 check=(png_size_t)
1241 ReadBlob(image,5,(char *) mng_info->read_buffer);
1242 check=(png_size_t) ReadBlob(image,(size_t) length,
1243 (char *) mng_info->read_buffer);
1244 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1245 mng_info->have_saved_bkgd_index=MagickTrue;
1246 mng_info->bytes_in_read_buffer=0;
1247 }
1248 }
1249 }
1250 }
1251}
1252#endif
1253
1254static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1255{
1256 Image
1257 *image;
1258
1259 image=(Image *) png_get_io_ptr(png_ptr);
1260 if (length)
1261 {
1262 png_size_t
1263 check;
1264
1265 check=(png_size_t) WriteBlob(image,(unsigned long) length,data);
1266 if (check != length)
1267 png_error(png_ptr,"WriteBlob Failed");
1268 }
1269}
1270
1271static void png_flush_data(png_structp png_ptr)
1272{
1273 (void) png_ptr;
1274}
1275
1276#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1277static int PalettesAreEqual(Image *a,Image *b)
1278{
1279 long
1280 i;
1281
1282 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1283 return((int) MagickFalse);
1284 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1285 return((int) MagickFalse);
1286 if (a->colors != b->colors)
1287 return((int) MagickFalse);
1288 for (i=0; i < (long) a->colors; i++)
1289 {
1290 if ((a->colormap[i].red != b->colormap[i].red) ||
1291 (a->colormap[i].green != b->colormap[i].green) ||
1292 (a->colormap[i].blue != b->colormap[i].blue))
1293 return((int) MagickFalse);
1294 }
1295 return((int) MagickTrue);
1296}
1297#endif
1298
1299static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1300{
1301 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1302 mng_info->exists[i] && !mng_info->frozen[i])
1303 {
1304#ifdef MNG_OBJECT_BUFFERS
1305 if (mng_info->ob[i] != (MngBuffer *) NULL)
1306 {
1307 if (mng_info->ob[i]->reference_count > 0)
1308 mng_info->ob[i]->reference_count--;
1309 if (mng_info->ob[i]->reference_count == 0)
1310 {
1311 if (mng_info->ob[i]->image != (Image *) NULL)
1312 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1313 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1314 }
1315 }
1316 mng_info->ob[i]=(MngBuffer *) NULL;
1317#endif
1318 mng_info->exists[i]=MagickFalse;
1319 mng_info->invisible[i]=MagickFalse;
1320 mng_info->viewable[i]=MagickFalse;
1321 mng_info->frozen[i]=MagickFalse;
1322 mng_info->x_off[i]=0;
1323 mng_info->y_off[i]=0;
1324 mng_info->object_clip[i].left=0;
1325 mng_info->object_clip[i].right=(long) PNG_UINT_31_MAX;
1326 mng_info->object_clip[i].top=0;
1327 mng_info->object_clip[i].bottom=(long) PNG_UINT_31_MAX;
1328 }
1329}
1330
1331static void MngInfoFreeStruct(MngInfo *mng_info,int *have_mng_structure)
1332{
1333 if (*have_mng_structure && (mng_info != (MngInfo *) NULL))
1334 {
1335 register long
1336 i;
1337
1338 for (i=1; i < MNG_MAX_OBJECTS; i++)
1339 MngInfoDiscardObject(mng_info,i);
1340 if (mng_info->global_plte != (png_colorp) NULL)
1341 mng_info->global_plte=(png_colorp)
1342 RelinquishMagickMemory(mng_info->global_plte);
1343 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1344 *have_mng_structure=MagickFalse;
1345 }
1346}
1347
1348static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1349{
1350 MngBox
1351 box;
1352
1353 box=box1;
1354 if (box.left < box2.left)
1355 box.left=box2.left;
1356 if (box.top < box2.top)
1357 box.top=box2.top;
1358 if (box.right > box2.right)
1359 box.right=box2.right;
1360 if (box.bottom > box2.bottom)
1361 box.bottom=box2.bottom;
1362 return box;
1363}
1364
1365static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1366{
1367 MngBox
1368 box;
1369
1370 /*
1371 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1372 */
1373 box.left=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1374 box.right=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1375 box.top=(long) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1376 box.bottom=(long) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1377 if (delta_type != 0)
1378 {
1379 box.left+=previous_box.left;
1380 box.right+=previous_box.right;
1381 box.top+=previous_box.top;
1382 box.bottom+=previous_box.bottom;
1383 }
1384 return(box);
1385}
1386
1387static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1388 unsigned char *p)
1389{
1390 MngPair
1391 pair;
1392 /*
1393 Read two longs from CLON, MOVE or PAST chunk
1394 */
1395 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1396 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1397 if (delta_type != 0)
1398 {
1399 pair.a+=previous_pair.a;
1400 pair.b+=previous_pair.b;
1401 }
1402 return(pair);
1403}
1404
1405static long mng_get_long(unsigned char *p)
1406{
1407 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1408}
1409
1410static void PNGErrorHandler(png_struct *ping,png_const_charp message)
1411{
1412 Image
1413 *image;
1414
1415 image=(Image *) png_get_error_ptr(ping);
1416 if (image->debug != MagickFalse)
1417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1418 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1419 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1420 message,"`%s'",image->filename);
1421 longjmp(ping->jmpbuf,1);
1422}
1423
1424static void PNGWarningHandler(png_struct *ping,png_const_charp message)
1425{
1426 Image
1427 *image;
1428
1429 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1430 png_error(ping, message);
1431 image=(Image *) png_get_error_ptr(ping);
1432 if (image->debug != MagickFalse)
1433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1434 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,
1435 message);
1436 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1437 message,"`%s'",image->filename);
1438}
1439
1440#ifdef PNG_USER_MEM_SUPPORTED
1441static png_voidp png_IM_malloc(png_structp png_ptr,png_uint_32 size)
1442{
1443#if (PNG_LIBPNG_VER < 10011)
1444 png_voidp
1445 ret;
1446
1447 png_ptr=png_ptr;
1448 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1449 if (ret == NULL)
1450 png_error("Insufficient memory.");
1451 return(ret);
1452#else
1453 png_ptr=png_ptr;
1454 return((png_voidp) AcquireMagickMemory((size_t) size));
1455#endif
1456}
1457
1458/*
1459 Free a pointer. It is removed from the list at the same time.
1460*/
1461static png_free_ptr png_IM_free(png_structp png_ptr,png_voidp ptr)
1462{
1463 png_ptr=png_ptr;
1464 ptr=RelinquishMagickMemory(ptr);
1465 return((png_free_ptr) NULL);
1466}
1467#endif
1468
1469#if defined(__cplusplus) || defined(c_plusplus)
1470}
1471#endif
1472
1473static int
1474png_read_raw_profile(Image *image, const ImageInfo *image_info,
1475 png_textp text,int ii)
1476{
1477 register long
1478 i;
1479
1480 register unsigned char
1481 *dp;
1482
1483 register png_charp
1484 sp;
1485
1486 png_uint_32
1487 length,
1488 nibbles;
1489
1490 StringInfo
1491 *profile;
1492
1493 unsigned char
1494 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1495 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1496 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1497 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1498 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1499 13,14,15};
1500
1501 sp=text[ii].text+1;
1502 /* look for newline */
1503 while (*sp != '\n')
1504 sp++;
1505 /* look for length */
1506 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1507 sp++;
1508 length=(png_uint_32) atol(sp);
1509 while (*sp != ' ' && *sp != '\n')
1510 sp++;
1511 /* allocate space */
1512 if (length == 0)
1513 {
1514 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1515 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1516 return(MagickFalse);
1517 }
1518 profile=AcquireStringInfo(length);
1519 if (profile == (StringInfo *) NULL)
1520 {
1521 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1522 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1523 "unable to copy profile");
1524 return(MagickFalse);
1525 }
1526 /* copy profile, skipping white space and column 1 "=" signs */
1527 dp=GetStringInfoDatum(profile);
1528 nibbles=length*2;
1529 for (i=0; i < (long) nibbles; i++)
1530 {
1531 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1532 {
1533 if (*sp == '\0')
1534 {
1535 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1536 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1537 profile=DestroyStringInfo(profile);
1538 return(MagickFalse);
1539 }
1540 sp++;
1541 }
1542 if (i%2 == 0)
1543 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1544 else
1545 (*dp++)+=unhex[(int) *sp++];
1546 }
1547 /*
1548 We have already read "Raw profile type.
1549 */
1550 (void) SetImageProfile(image,&text[ii].key[17],profile);
1551 profile=DestroyStringInfo(profile);
1552 if (image_info->verbose)
1553 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1554 return MagickTrue;
1555}
1556
1557#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1558static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1559{
1560 Image
1561 *image;
1562
1563
1564 /* The unknown chunk structure contains the chunk data:
1565 png_byte name[5];
1566 png_byte *data;
1567 png_size_t size;
1568
1569 Note that libpng has already taken care of the CRC handling.
1570 */
1571
1572
1573 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1574 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1575 return(0); /* Did not recognize */
1576
1577 /* recognized vpAg */
1578
1579 if (chunk->size != 9)
1580 return(-1); /* Error return */
1581
1582 if (chunk->data[8] != 0)
1583 return(0); /* ImageMagick requires pixel units */
1584
1585 image=(Image *) png_get_user_chunk_ptr(ping);
1586
1587 image->page.width=(unsigned long) ((chunk->data[0] << 24) |
1588 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1589 image->page.height=(unsigned long) ((chunk->data[4] << 24) |
1590 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1591
1592 /* Return one of the following: */
1593 /* return(-n); chunk had an error */
1594 /* return(0); did not recognize */
1595 /* return(n); success */
1596
1597 return(1);
1598
1599}
1600#endif
1601
1602/*
1603%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1604% %
1605% %
1606% %
1607% R e a d O n e P N G I m a g e %
1608% %
1609% %
1610% %
1611%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1612%
1613% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1614% (minus the 8-byte signature) and returns it. It allocates the memory
1615% necessary for the new Image structure and returns a pointer to the new
1616% image.
1617%
1618% The format of the ReadOnePNGImage method is:
1619%
1620% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1621% ExceptionInfo *exception)
1622%
1623% A description of each parameter follows:
1624%
1625% o mng_info: Specifies a pointer to a MngInfo structure.
1626%
1627% o image_info: the image info.
1628%
1629% o exception: return any errors or warnings in this structure.
1630%
1631*/
1632static Image *ReadOnePNGImage(MngInfo *mng_info,
1633 const ImageInfo *image_info, ExceptionInfo *exception)
1634{
1635 /* Read one PNG image */
1636
1637 Image
1638 *image;
1639
1640 int
1641 logging,
1642 num_text,
1643 num_passes,
1644 pass;
1645
1646 MagickBooleanType
1647 status;
1648
1649 PixelPacket
1650 transparent_color;
1651
1652 png_info
1653 *end_info,
1654 *ping_info;
1655
1656 png_struct
1657 *ping;
1658
1659 png_textp
1660 text;
1661
1662 QuantumInfo
1663 *quantum_info;
1664
1665 unsigned char
1666 *png_pixels;
1667
1668 long
1669 y;
1670
1671 register unsigned char
1672 *p;
1673
1674 register IndexPacket
1675 *indices;
1676
1677 register long
1678 i,
1679 x;
1680
1681 register PixelPacket
1682 *q;
1683
1684 size_t
1685 length;
1686
1687 unsigned long
1688 row_offset;
1689
1690#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1691 png_byte unused_chunks[]=
1692 {
1693 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1694 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1695 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1696 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1697 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1698 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1699 };
1700#endif
1701
1702 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1703 " enter ReadOnePNGImage()");
1704
1705#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1706 AcquireSemaphoreInfo(&png_semaphore);
1707#endif
1708
1709#if (PNG_LIBPNG_VER < 10007)
1710 if (image_info->verbose)
1711 printf("Your PNG library (libpng-%s) is rather old.\n",
1712 PNG_LIBPNG_VER_STRING);
1713#endif
1714
cristyed552522009-10-16 14:04:35 +00001715 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00001716 image=mng_info->image;
1717
1718 /*
1719 Allocate the PNG structures
1720 */
1721#ifdef PNG_USER_MEM_SUPPORTED
1722 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1723 PNGErrorHandler,PNGWarningHandler, NULL,
1724 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
1725#else
1726 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1727 PNGErrorHandler,PNGWarningHandler);
1728#endif
1729 if (ping == (png_struct *) NULL)
1730 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1731 ping_info=png_create_info_struct(ping);
1732 if (ping_info == (png_info *) NULL)
1733 {
1734 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1735 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1736 }
1737 end_info=png_create_info_struct(ping);
1738 if (end_info == (png_info *) NULL)
1739 {
1740 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1741 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1742 }
1743 png_pixels=(unsigned char *) NULL;
1744 if (setjmp(ping->jmpbuf))
1745 {
1746 /*
1747 PNG image is corrupt.
1748 */
1749 png_destroy_read_struct(&ping,&ping_info,&end_info);
1750#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1751 RelinquishSemaphoreInfo(png_semaphore);
1752#endif
1753 if (logging != MagickFalse)
1754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1755 " exit ReadOnePNGImage() with error.");
1756 if (image != (Image *) NULL)
1757 {
1758 if (image->exception.severity > exception->severity)
1759 InheritException(exception,&image->exception);
1760 image->columns=0;
1761 }
1762 return(GetFirstImageInList(image));
1763 }
1764 /*
1765 Prepare PNG for reading.
1766 */
1767 mng_info->image_found++;
1768 png_set_sig_bytes(ping,8);
1769 if (LocaleCompare(image_info->magick,"MNG") == 0)
1770 {
1771#if defined(PNG_MNG_FEATURES_SUPPORTED)
1772 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1773 png_set_read_fn(ping,image,png_get_data);
1774#else
1775#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1776 png_permit_empty_plte(ping,MagickTrue);
1777 png_set_read_fn(ping,image,png_get_data);
1778#else
1779 mng_info->image=image;
1780 mng_info->bytes_in_read_buffer=0;
1781 mng_info->found_empty_plte=MagickFalse;
1782 mng_info->have_saved_bkgd_index=MagickFalse;
1783 png_set_read_fn(ping,mng_info,mng_get_data);
1784#endif
1785#endif
1786 }
1787 else
1788 png_set_read_fn(ping,image,png_get_data);
1789
1790#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1791 /* Ignore unused chunks and all unknown chunks except for vpAg */
1792 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1793 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1794 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1795 (int)sizeof(unused_chunks)/5);
1796 /* Callback for other unknown chunks */
1797 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1798#endif
1799
1800#if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1801 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1802 /* Disable thread-unsafe features of pnggccrd */
1803 if (png_access_version_number() >= 10200)
1804 {
1805 png_uint_32 mmx_disable_mask=0;
1806 png_uint_32 asm_flags;
1807
1808 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1809 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1810 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1811 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1812 asm_flags=png_get_asm_flags(ping);
1813 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1814 }
1815#endif
1816
1817 png_read_info(ping,ping_info);
1818 if (ping_info->bit_depth < 8)
1819 {
1820 if (((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE))
1821 png_set_packing(ping);
1822 }
1823 image->depth=ping_info->bit_depth;
1824 image->depth=GetImageQuantumDepth(image,MagickFalse);
1825 image->interlace=ping_info->interlace_type != 0 ? PNGInterlace : NoInterlace;
1826 if (logging != MagickFalse)
1827 {
1828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1829 " PNG width: %lu, height: %lu",
1830 ping_info->width, ping_info->height);
1831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1832 " PNG color_type: %d, bit_depth: %d",
1833 ping_info->color_type, ping_info->bit_depth);
1834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1835 " PNG compression_method: %d",
1836 ping_info->compression_type);
1837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1838 " PNG interlace_method: %d, filter_method: %d",
1839 ping_info->interlace_type,ping_info->filter_type);
1840 }
1841
1842#if (PNG_LIBPNG_VER > 10008) && defined(PNG_READ_iCCP_SUPPORTED)
1843 if (ping_info->valid & PNG_INFO_iCCP)
1844 {
1845 int
1846 compression;
1847
1848 png_charp
1849 info,
1850 name;
1851
1852 png_uint_32
1853 profile_length;
1854
1855 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1856 &profile_length);
1857 if (profile_length != 0)
1858 {
1859 StringInfo
1860 *profile;
1861
1862 if (logging != MagickFalse)
1863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1864 " Reading PNG iCCP chunk.");
1865 profile=AcquireStringInfo(profile_length);
1866 SetStringInfoDatum(profile,(const unsigned char *) info);
1867 (void) SetImageProfile(image,"icc",profile);
1868 profile=DestroyStringInfo(profile);
1869 }
1870 }
1871#endif
1872#if defined(PNG_READ_sRGB_SUPPORTED)
1873 {
1874 int
1875 intent;
1876
1877 if (mng_info->have_global_srgb)
1878 image->rendering_intent=(RenderingIntent)
1879 (mng_info->global_srgb_intent+1);
1880 if (png_get_sRGB(ping,ping_info,&intent))
1881 {
1882 image->rendering_intent=(RenderingIntent) (intent+1);
1883 if (logging != MagickFalse)
1884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1885 " Reading PNG sRGB chunk: rendering_intent: %d",intent+1);
1886 }
1887 }
1888#endif
1889 {
1890 double
1891 file_gamma;
1892
1893 if (mng_info->have_global_gama)
1894 image->gamma=mng_info->global_gamma;
1895 if (png_get_gAMA(ping,ping_info,&file_gamma))
1896 {
1897 image->gamma=(float) file_gamma;
1898 if (logging != MagickFalse)
1899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1900 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1901 }
1902 }
1903 if (mng_info->have_global_chrm != MagickFalse)
1904 image->chromaticity=mng_info->global_chrm;
1905 if (ping_info->valid & PNG_INFO_cHRM)
1906 {
1907 (void) png_get_cHRM(ping,ping_info,
1908 &image->chromaticity.white_point.x,
1909 &image->chromaticity.white_point.y,
1910 &image->chromaticity.red_primary.x,
1911 &image->chromaticity.red_primary.y,
1912 &image->chromaticity.green_primary.x,
1913 &image->chromaticity.green_primary.y,
1914 &image->chromaticity.blue_primary.x,
1915 &image->chromaticity.blue_primary.y);
1916 if (logging != MagickFalse)
1917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1918 " Reading PNG cHRM chunk.");
1919 }
1920 if (image->rendering_intent)
1921 {
1922 image->gamma=0.45455f;
1923 image->chromaticity.red_primary.x=0.6400f;
1924 image->chromaticity.red_primary.y=0.3300f;
1925 image->chromaticity.green_primary.x=0.3000f;
1926 image->chromaticity.green_primary.y=0.6000f;
1927 image->chromaticity.blue_primary.x=0.1500f;
1928 image->chromaticity.blue_primary.y=0.0600f;
1929 image->chromaticity.white_point.x=0.3127f;
1930 image->chromaticity.white_point.y=0.3290f;
1931 }
1932 if ((mng_info->have_global_gama != MagickFalse) || image->rendering_intent)
1933 ping_info->valid|=PNG_INFO_gAMA;
1934 if ((mng_info->have_global_chrm != MagickFalse) || image->rendering_intent)
1935 ping_info->valid|=PNG_INFO_cHRM;
1936#if defined(PNG_oFFs_SUPPORTED)
1937 if (ping_info->valid & PNG_INFO_oFFs)
1938 {
1939 image->page.x=png_get_x_offset_pixels(ping, ping_info);
1940 image->page.y=png_get_y_offset_pixels(ping, ping_info);
1941 if (logging != MagickFalse)
1942 if (image->page.x || image->page.y)
1943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1944 " Reading PNG oFFs chunk: x: %ld, y: %ld.",image->page.x,
1945 image->page.y);
1946 }
1947#endif
1948#if defined(PNG_pHYs_SUPPORTED)
1949 if (ping_info->valid & PNG_INFO_pHYs)
1950 {
1951 int
1952 unit_type;
1953
1954 png_uint_32
1955 x_resolution,
1956 y_resolution;
1957
1958 /*
1959 Set image resolution.
1960 */
1961 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
1962 &unit_type);
1963 image->x_resolution=(float) x_resolution;
1964 image->y_resolution=(float) y_resolution;
1965 if (unit_type == PNG_RESOLUTION_METER)
1966 {
1967 image->units=PixelsPerCentimeterResolution;
1968 image->x_resolution=(double) x_resolution/100.0;
1969 image->y_resolution=(double) y_resolution/100.0;
1970 }
1971 if (logging != MagickFalse)
1972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1973 " Reading PNG pHYs chunk: xres: %lu, yres: %lu, units: %d.",
1974 x_resolution, y_resolution, unit_type);
1975 }
1976 else
1977 {
1978 if (mng_info->have_global_phys)
1979 {
1980 image->x_resolution=(float) mng_info->global_x_pixels_per_unit;
1981 image->y_resolution=(float) mng_info->global_y_pixels_per_unit;
1982 if (mng_info->global_phys_unit_type == PNG_RESOLUTION_METER)
1983 {
1984 image->units=PixelsPerCentimeterResolution;
1985 image->x_resolution=(double)
1986 mng_info->global_x_pixels_per_unit/100.0;
1987 image->y_resolution=(double)
1988 mng_info->global_y_pixels_per_unit/100.0;
1989 }
1990 ping_info->valid|=PNG_INFO_pHYs;
1991 }
1992 }
1993#endif
1994 if (ping_info->valid & PNG_INFO_PLTE)
1995 {
1996 int
1997 number_colors;
1998
1999 png_colorp
2000 palette;
2001
2002 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2003 if ((number_colors == 0) &&
2004 ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE))
2005 {
2006 if (mng_info->global_plte_length)
2007 {
2008 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2009 (int) mng_info->global_plte_length);
2010 if ((ping_info->valid & PNG_INFO_tRNS) == 0)
2011 if (mng_info->global_trns_length)
2012 {
2013 if (mng_info->global_trns_length >
2014 mng_info->global_plte_length)
2015 (void) ThrowMagickException(&image->exception,
2016 GetMagickModule(),CoderError,
2017 "global tRNS has more entries than global PLTE",
2018 "`%s'",image_info->filename);
2019 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2020 (int) mng_info->global_trns_length,NULL);
2021 }
2022#if defined(PNG_READ_bKGD_SUPPORTED)
2023 if (
2024#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2025 mng_info->have_saved_bkgd_index ||
2026#endif
2027 ping_info->valid & PNG_INFO_bKGD)
2028 {
2029 png_color_16
2030 background;
2031
2032#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2033 if (mng_info->have_saved_bkgd_index)
2034 background.index=mng_info->saved_bkgd_index;
2035 else
2036#endif
2037 background.index=ping_info->background.index;
2038 background.red=(png_uint_16)
2039 mng_info->global_plte[background.index].red;
2040 background.green=(png_uint_16)
2041 mng_info->global_plte[background.index].green;
2042 background.blue=(png_uint_16)
2043 mng_info->global_plte[background.index].blue;
2044 png_set_bKGD(ping,ping_info,&background);
2045 }
2046#endif
2047 }
2048 else
2049 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2050 CoderError,"No global PLTE in file","`%s'",
2051 image_info->filename);
2052 }
2053 }
2054
2055#if defined(PNG_READ_bKGD_SUPPORTED)
2056 if (mng_info->have_global_bkgd && !(ping_info->valid & PNG_INFO_bKGD))
2057 image->background_color=mng_info->mng_global_bkgd;
2058 if (ping_info->valid & PNG_INFO_bKGD)
2059 {
2060 /*
2061 Set image background color.
2062 */
2063 if (logging != MagickFalse)
2064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2065 " Reading PNG bKGD chunk.");
2066 if (ping_info->bit_depth <= MAGICKCORE_QUANTUM_DEPTH)
2067 {
2068 image->background_color.red=ping_info->background.red;
2069 image->background_color.green=ping_info->background.green;
2070 image->background_color.blue=ping_info->background.blue;
2071 }
2072 else
2073 {
2074 image->background_color.red=
2075 ScaleShortToQuantum(ping_info->background.red);
2076 image->background_color.green=
2077 ScaleShortToQuantum(ping_info->background.green);
2078 image->background_color.blue=
2079 ScaleShortToQuantum(ping_info->background.blue);
2080 }
2081 }
2082#endif
2083 transparent_color.red=0;
2084 transparent_color.green=0;
2085 transparent_color.blue=0;
2086 transparent_color.opacity=0;
2087 if (ping_info->valid & PNG_INFO_tRNS)
2088 {
2089 /*
2090 Image has a transparent background.
2091 */
2092 int
2093 max_sample;
2094
2095 if (logging != MagickFalse)
2096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2097 " Reading PNG tRNS chunk.");
2098
2099 max_sample = (1 << ping_info->bit_depth) - 1;
2100
2101 if ((ping_info->color_type == PNG_COLOR_TYPE_GRAY &&
2102 (int)ping_info->trans_color.gray > max_sample) ||
2103 (ping_info->color_type == PNG_COLOR_TYPE_RGB &&
2104 ((int)ping_info->trans_color.red > max_sample ||
2105 (int)ping_info->trans_color.green > max_sample ||
2106 (int)ping_info->trans_color.blue > max_sample)))
2107 {
2108 if (logging != MagickFalse)
2109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2110 " Ignoring PNG tRNS chunk with out-of-range sample.");
2111#if (PNG_LIBPNG_VER < 10007)
2112 ping_info->trans_alpha=(unsigned char *) RelinquishMagickMemory(
2113 ping_info->trans_alpha);
2114 ping_info->valid&=(~PNG_INFO_tRNS);
2115#else
2116 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2117#endif
2118 image->matte=MagickFalse;
2119 }
2120 else
2121 {
2122 transparent_color.red= (Quantum)(ping_info->trans_color.red);
2123 transparent_color.green= (Quantum) (ping_info->trans_color.green);
2124 transparent_color.blue= (Quantum) (ping_info->trans_color.blue);
2125 transparent_color.opacity= (Quantum) (ping_info->trans_color.gray);
2126 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
2127 {
2128 if (ping_info->bit_depth < 8)
2129 {
2130 transparent_color.opacity=(Quantum) (((
2131 ping_info->trans_color.gray)*255)/max_sample);
2132 }
2133 transparent_color.red=transparent_color.opacity;
2134 transparent_color.green=transparent_color.opacity;
2135 transparent_color.blue=transparent_color.opacity;
2136 }
2137 }
2138 }
2139#if defined(PNG_READ_sBIT_SUPPORTED)
2140 if (mng_info->have_global_sbit)
2141 {
2142 if (!(ping_info->valid & PNG_INFO_sBIT))
2143 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2144 }
2145#endif
2146 num_passes=png_set_interlace_handling(ping);
2147 png_read_update_info(ping,ping_info);
2148 /*
2149 Initialize image structure.
2150 */
2151 mng_info->image_box.left=0;
2152 mng_info->image_box.right=(long) ping_info->width;
2153 mng_info->image_box.top=0;
2154 mng_info->image_box.bottom=(long) ping_info->height;
2155 if (mng_info->mng_type == 0)
2156 {
2157 mng_info->mng_width=ping_info->width;
2158 mng_info->mng_height=ping_info->height;
2159 mng_info->frame=mng_info->image_box;
2160 mng_info->clip=mng_info->image_box;
2161 }
2162 else
2163 {
2164 image->page.y=mng_info->y_off[mng_info->object_id];
2165 }
2166 image->compression=ZipCompression;
2167 image->columns=ping_info->width;
2168 image->rows=ping_info->height;
2169 if (((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE) ||
2170 ((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2171 ((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY))
2172 {
2173 image->storage_class=PseudoClass;
2174 image->colors=1UL << ping_info->bit_depth;
2175#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2176 if (image->colors > 256)
2177 image->colors=256;
2178#else
2179 if (image->colors > 65536L)
2180 image->colors=65536L;
2181#endif
2182 if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
2183 {
2184 int
2185 number_colors;
2186
2187 png_colorp
2188 palette;
2189
2190 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2191 image->colors=(unsigned long) number_colors;
2192 if (logging != MagickFalse)
2193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2194 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2195 }
2196 }
2197
2198 if (image->storage_class == PseudoClass)
2199 {
2200 /*
2201 Initialize image colormap.
2202 */
2203 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2204 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2205 if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
2206 {
2207 int
2208 number_colors;
2209
2210 png_colorp
2211 palette;
2212
2213 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2214 for (i=0; i < (long) image->colors; i++)
2215 {
2216 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2217 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2218 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2219 }
2220 }
2221 else
2222 {
2223 unsigned long
2224 scale;
2225
2226 scale=(QuantumRange/((1UL << ping_info->bit_depth)-1));
2227 if (scale < 1)
2228 scale=1;
2229 for (i=0; i < (long) image->colors; i++)
2230 {
2231 image->colormap[i].red=(Quantum) (i*scale);
2232 image->colormap[i].green=(Quantum) (i*scale);
2233 image->colormap[i].blue=(Quantum) (i*scale);
2234 }
2235 }
2236 }
2237 /*
2238 Read image scanlines.
2239 */
2240 if (image->delay != 0)
2241 mng_info->scenes_found++;
2242 if ((image_info->number_scenes != 0) && (mng_info->scenes_found > (long)
2243 (image_info->first_scene+image_info->number_scenes)))
2244 {
2245 if (logging != MagickFalse)
2246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2247 " Skipping PNG image data for scene %ld",
2248 mng_info->scenes_found-1);
2249 png_destroy_read_struct(&ping,&ping_info,&end_info);
2250#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2251 RelinquishSemaphoreInfo(png_semaphore);
2252#endif
2253 if (logging != MagickFalse)
2254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2255 " exit ReadOnePNGImage().");
2256 return(image);
2257 }
2258 if (logging != MagickFalse)
2259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2260 " Reading PNG IDAT chunk(s)");
2261 if (num_passes > 1)
2262 png_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2263 ping_info->rowbytes*sizeof(*png_pixels));
2264 else
2265 png_pixels=(unsigned char *) AcquireQuantumMemory(ping_info->rowbytes,
2266 sizeof(*png_pixels));
2267 if (png_pixels == (unsigned char *) NULL)
2268 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2269 if (logging != MagickFalse)
2270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2271 " Converting PNG pixels to pixel packets");
2272 /*
2273 Convert PNG pixels to pixel packets.
2274 */
cristy3ed852e2009-09-05 21:47:34 +00002275 if (setjmp(ping->jmpbuf))
2276 {
2277 /*
2278 PNG image is corrupt.
2279 */
2280 png_destroy_read_struct(&ping,&ping_info,&end_info);
2281#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2282 RelinquishSemaphoreInfo(png_semaphore);
2283#endif
2284 if (quantum_info != (QuantumInfo *) NULL)
2285 quantum_info = DestroyQuantumInfo(quantum_info);
2286 if (png_pixels != (unsigned char *) NULL)
2287 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2288 if (logging != MagickFalse)
2289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2290 " exit ReadOnePNGImage() with error.");
2291 if (image != (Image *) NULL)
2292 {
2293 if (image->exception.severity > exception->severity)
2294 InheritException(exception,&image->exception);
2295 image->columns=0;
2296 }
2297 return(GetFirstImageInList(image));
2298 }
cristyed552522009-10-16 14:04:35 +00002299 quantum_info=AcquireQuantumInfo(image_info,image);
2300 if (quantum_info == (QuantumInfo *) NULL)
2301 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00002302 if (image->storage_class == DirectClass)
2303 for (pass=0; pass < num_passes; pass++)
2304 {
2305 /*
2306 Convert image to DirectClass pixel packets.
2307 */
2308#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2309 int
2310 depth;
2311
2312 depth=(long) ping_info->bit_depth;
2313#endif
2314 image->matte=(((int) ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2315 ((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2316 (ping_info->valid & PNG_INFO_tRNS)) ? MagickTrue : MagickFalse;
2317
2318 for (y=0; y < (long) image->rows; y++)
2319 {
2320 if (num_passes > 1)
2321 row_offset=ping_info->rowbytes*y;
2322 else
2323 row_offset=0;
2324 png_read_row(ping,png_pixels+row_offset,NULL);
cristyabc8f402009-09-19 01:45:58 +00002325 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00002326 if (q == (PixelPacket *) NULL)
2327 break;
2328#if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2329 if (depth == 16)
2330 {
2331 register Quantum
2332 *p,
2333 *r;
2334
2335 r=png_pixels+row_offset;
2336 p=r;
2337 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
2338 {
2339 for (x=(long) image->columns-1; x >= 0; x--)
2340 {
2341 *r++=*p++;
2342 p++;
2343 if ((ping_info->valid & PNG_INFO_tRNS) &&
2344 (((*(p-2) << 8)|*(p-1)) == transparent_color.opacity))
2345 {
2346 /* Cheap transparency */
2347 *r++=TransparentOpacity;
2348 }
2349 else
2350 *r++=OpaqueOpacity;
2351 }
2352 }
2353 else if (ping_info->color_type == PNG_COLOR_TYPE_RGB)
2354 {
2355 if (ping_info->valid & PNG_INFO_tRNS)
2356 for (x=(long) image->columns-1; x >= 0; x--)
2357 {
2358 *r++=*p++;
2359 p++;
2360 *r++=*p++;
2361 p++;
2362 *r++=*p++;
2363 p++;
2364 if ((((*(p-6) << 8)|*(p-5)) == transparent_color.red) &&
2365 (((*(p-4) << 8)|*(p-3)) == transparent_color.green) &&
2366 (((*(p-2) << 8)|*(p-1)) == transparent_color.blue))
2367 {
2368 /* Cheap transparency */
2369 *r++=TransparentOpacity;
2370 }
2371 else
2372 *r++=OpaqueOpacity;
2373 }
2374 else
2375 for (x=(long) image->columns-1; x >= 0; x--)
2376 {
2377 *r++=*p++;
2378 p++;
2379 *r++=*p++;
2380 p++;
2381 *r++=*p++;
2382 p++;
2383 *r++=OpaqueOpacity;
2384 }
2385 }
2386 else if (ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2387 for (x=(long) (4*image->columns); x != 0; x--)
2388 {
2389 *r++=*p++;
2390 p++;
2391 }
2392 else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2393 for (x=(long) (2*image->columns); x != 0; x--)
2394 {
2395 *r++=*p++;
2396 p++;
2397 }
2398 }
2399 if (depth == 8 && ping_info->color_type == PNG_COLOR_TYPE_GRAY)
2400 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2401 GrayQuantum,png_pixels+row_offset);
2402 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY ||
2403 ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2404 {
2405 quantum_info->depth=8;
2406 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2407 GrayAlphaQuantum,png_pixels+row_offset);
2408 }
2409 else if (depth == 8 && ping_info->color_type == PNG_COLOR_TYPE_RGB)
2410 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2411 RGBQuantum,png_pixels+row_offset);
2412 else if (ping_info->color_type == PNG_COLOR_TYPE_RGB ||
2413 ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2414 {
2415 quantum_info->depth=8;
2416 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2417 RGBAQuantum,png_pixels+row_offset);
2418 }
2419 else if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
2420 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2421 IndexQuantum,png_pixels+row_offset);
2422#else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2423 if ((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY)
2424 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2425 GrayQuantum,png_pixels+row_offset,exception);
2426 else if ((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2427 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2428 GrayAlphaQuantum,png_pixels+row_offset,exception);
2429 else if ((int) ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2430 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2431 RGBAQuantum,png_pixels+row_offset,exception);
2432 else if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
2433 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2434 IndexQuantum,png_pixels+row_offset,exception);
2435 else
2436 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2437 RGBQuantum,png_pixels+row_offset,exception);
2438#endif
2439 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2440 break;
2441 }
2442 if (image->previous == (Image *) NULL)
2443 {
2444 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2445 if (status == MagickFalse)
2446 break;
2447 }
2448 }
2449 else /* image->storage_class != DirectClass */
2450 for (pass=0; pass < num_passes; pass++)
2451 {
2452 Quantum
2453 *quantum_scanline;
2454
2455 register Quantum
2456 *r;
2457
2458 /*
2459 Convert grayscale image to PseudoClass pixel packets.
2460 */
2461 image->matte=ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2462 MagickTrue : MagickFalse;
2463 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2464 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2465 if (quantum_scanline == (Quantum *) NULL)
2466 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2467 for (y=0; y < (long) image->rows; y++)
2468 {
2469 if (num_passes > 1)
2470 row_offset=ping_info->rowbytes*y;
2471 else
2472 row_offset=0;
2473 png_read_row(ping,png_pixels+row_offset,NULL);
2474 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2475 if (q == (PixelPacket *) NULL)
2476 break;
2477 indices=GetAuthenticIndexQueue(image);
2478 p=png_pixels+row_offset;
2479 r=quantum_scanline;
2480 switch (ping_info->bit_depth)
2481 {
2482 case 1:
2483 {
2484 register long
2485 bit;
2486
2487 for (x=(long) image->columns-7; x > 0; x-=8)
2488 {
2489 for (bit=7; bit >= 0; bit--)
2490 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2491 p++;
2492 }
2493 if ((image->columns % 8) != 0)
2494 {
2495 for (bit=7; bit >= (long) (8-(image->columns % 8)); bit--)
2496 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2497 }
2498 break;
2499 }
2500 case 2:
2501 {
2502 for (x=(long) image->columns-3; x > 0; x-=4)
2503 {
2504 *r++=(*p >> 6) & 0x03;
2505 *r++=(*p >> 4) & 0x03;
2506 *r++=(*p >> 2) & 0x03;
2507 *r++=(*p++) & 0x03;
2508 }
2509 if ((image->columns % 4) != 0)
2510 {
2511 for (i=3; i >= (long) (4-(image->columns % 4)); i--)
2512 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2513 }
2514 break;
2515 }
2516 case 4:
2517 {
2518 for (x=(long) image->columns-1; x > 0; x-=2)
2519 {
2520 *r++=(*p >> 4) & 0x0f;
2521 *r++=(*p++) & 0x0f;
2522 }
2523 if ((image->columns % 2) != 0)
2524 *r++=(*p++ >> 4) & 0x0f;
2525 break;
2526 }
2527 case 8:
2528 {
2529 if (ping_info->color_type == 4)
2530 for (x=(long) image->columns-1; x >= 0; x--)
2531 {
2532 *r++=*p++;
2533 /* In image.h, OpaqueOpacity is 0
2534 * TransparentOpacity is QuantumRange
2535 * In a PNG datastream, Opaque is QuantumRange
2536 * and Transparent is 0.
2537 */
2538 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2539 q++;
2540 }
2541 else
2542 for (x=(long) image->columns-1; x >= 0; x--)
2543 *r++=*p++;
2544 break;
2545 }
2546 case 16:
2547 {
2548 for (x=(long) image->columns-1; x >= 0; x--)
2549 {
2550#if (MAGICKCORE_QUANTUM_DEPTH == 16)
2551 unsigned long
2552 quantum;
2553
2554 if (image->colors > 256)
2555 *r=((*p++) << 8);
2556 else
2557 *r=0;
2558 quantum=(*r);
2559 quantum|=(*p++);
2560 *r=(Quantum) quantum;
2561 r++;
2562 if (ping_info->color_type == 4)
2563 {
2564 quantum=((*p++) << 8);
2565 quantum|=(*p++);
2566 q->opacity=(Quantum) (QuantumRange-quantum);
2567 q++;
2568 }
2569#else
2570#if (MAGICKCORE_QUANTUM_DEPTH == 32)
2571 unsigned long
2572 quantum;
2573
2574 if (image->colors > 256)
2575 *r=((*p++) << 8);
2576 else
2577 *r=0;
2578 quantum=(*r);
2579 quantum|=(*p++);
2580 *r=quantum;
2581 r++;
2582 if (ping_info->color_type == 4)
2583 {
2584 q->opacity=(*p << 8) | *(p+1);
2585 q->opacity*=65537L;
2586 q->opacity=(Quantum) (QuantumRange-q->opacity);
2587 p+=2;
2588 q++;
2589 }
2590#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2591 *r++=(*p++);
2592 p++; /* strip low byte */
2593 if (ping_info->color_type == 4)
2594 {
2595 q->opacity=(Quantum) (QuantumRange-(*p++));
2596 p++;
2597 q++;
2598 }
2599#endif
2600#endif
2601 }
2602 break;
2603 }
2604 default:
2605 break;
2606 }
2607 /*
2608 Transfer image scanline.
2609 */
2610 r=quantum_scanline;
2611 for (x=0; x < (long) image->columns; x++)
2612 indices[x]=(*r++);
2613 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2614 break;
2615 }
2616 if (image->previous == (Image *) NULL)
2617 {
2618 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2619 if (status == MagickFalse)
2620 break;
2621 }
2622 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2623 }
cristyb32b90a2009-09-07 21:45:48 +00002624 if (quantum_info != (QuantumInfo *) NULL)
2625 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00002626 if (image->storage_class == PseudoClass)
2627 (void) SyncImage(image);
2628 png_read_end(ping,ping_info);
2629
2630 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2631 (long) image_info->first_scene && image->delay != 0)
2632 {
2633 png_destroy_read_struct(&ping,&ping_info,&end_info);
2634 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2635 image->colors=2;
2636 (void) SetImageBackgroundColor(image);
2637#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2638 RelinquishSemaphoreInfo(png_semaphore);
2639#endif
2640 if (logging != MagickFalse)
2641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2642 " exit ReadOnePNGImage() early.");
2643 return(image);
2644 }
2645 if (ping_info->valid & PNG_INFO_tRNS)
2646 {
2647 ClassType
2648 storage_class;
2649
2650 /*
2651 Image has a transparent background.
2652 */
2653 storage_class=image->storage_class;
2654 image->matte=MagickTrue;
2655 for (y=0; y < (long) image->rows; y++)
2656 {
2657 image->storage_class=storage_class;
2658 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2659 if (q == (PixelPacket *) NULL)
2660 break;
2661 indices=GetAuthenticIndexQueue(image);
2662
2663 if (storage_class == PseudoClass)
2664 {
2665 IndexPacket
2666 indexpacket;
2667
2668 if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
2669 for (x=0; x < (long) image->columns; x++)
2670 {
2671 indexpacket=indices[x];
2672 if (indexpacket < ping_info->num_trans)
2673 q->opacity=ScaleCharToQuantum((unsigned char)
2674 (255-ping_info->trans_alpha[(long) indexpacket]));
2675 else
2676 q->opacity=OpaqueOpacity;
2677 q++;
2678 }
2679 else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
2680 for (x=0; x < (long) image->columns; x++)
2681 {
2682 indexpacket=indices[x];
2683 q->red=image->colormap[(long) indexpacket].red;
2684 q->green=q->red;
2685 q->blue=q->red;
2686 if (q->red == transparent_color.opacity)
2687 q->opacity=(Quantum) TransparentOpacity;
2688 else
2689 q->opacity=OpaqueOpacity;
2690 q++;
2691 }
2692 }
2693 else
2694 for (x=(long) image->columns-1; x >= 0; x--)
2695 {
2696 if (ScaleQuantumToChar(q->red) == transparent_color.red &&
2697 ScaleQuantumToChar(q->green) == transparent_color.green &&
2698 ScaleQuantumToChar(q->blue) == transparent_color.blue)
2699 q->opacity=(Quantum) TransparentOpacity;
2700 else
2701 q->opacity=OpaqueOpacity;
2702 q++;
2703 }
2704 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2705 break;
2706 }
2707 image->storage_class=DirectClass;
2708 }
2709#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2710 if (image->depth > 8)
2711 image->depth=8;
2712#endif
2713 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2714 for (i=0; i < (long) num_text; i++)
2715 {
2716 /* Check for a profile */
2717
2718 if (logging != MagickFalse)
2719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2720 " Reading PNG text chunk");
2721 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2722 (void) png_read_raw_profile(image,image_info,text,(int) i);
2723 else
2724 {
2725 char
2726 *value;
2727
2728 length=text[i].text_length;
2729 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2730 sizeof(*value));
2731 if (value == (char *) NULL)
2732 {
2733 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2734 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2735 image->filename);
2736 break;
2737 }
2738 *value='\0';
2739 (void) ConcatenateMagickString(value,text[i].text,length+2);
2740 (void) SetImageProperty(image,text[i].key,value);
2741 if (logging != MagickFalse)
2742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2743 " Keyword: %s",text[i].key);
2744 value=DestroyString(value);
2745 }
2746 }
2747#ifdef MNG_OBJECT_BUFFERS
2748 /*
2749 Store the object if necessary.
2750 */
2751 if (object_id && !mng_info->frozen[object_id])
2752 {
2753 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2754 {
2755 /*
2756 create a new object buffer.
2757 */
2758 mng_info->ob[object_id]=(MngBuffer *)
2759 AcquireMagickMemory(sizeof(MngBuffer));
2760 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2761 {
2762 mng_info->ob[object_id]->image=(Image *) NULL;
2763 mng_info->ob[object_id]->reference_count=1;
2764 }
2765 }
2766 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2767 mng_info->ob[object_id]->frozen)
2768 {
2769 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2770 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2771 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2772 image->filename);
2773 if (mng_info->ob[object_id]->frozen)
2774 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2775 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2776 "`%s'",image->filename);
2777 }
2778 else
2779 {
2780 png_uint_32
2781 width,
2782 height;
2783
2784 int
2785 bit_depth,
2786 color_type,
2787 interlace_method,
2788 compression_method,
2789 filter_method;
2790
2791 if (mng_info->ob[object_id]->image != (Image *) NULL)
2792 mng_info->ob[object_id]->image=DestroyImage
2793 (mng_info->ob[object_id]->image);
2794 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2795 &image->exception);
2796 if (mng_info->ob[object_id]->image != (Image *) NULL)
2797 mng_info->ob[object_id]->image->file=(FILE *) NULL;
2798 else
2799 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2800 ResourceLimitError,"Cloning image for object buffer failed",
2801 "`%s'",image->filename);
2802 png_get_IHDR(ping,ping_info,&width,&height,&bit_depth,&color_type,
2803 &interlace_method,&compression_method,&filter_method);
2804 if (width > 250000L || height > 250000L)
2805 png_error(ping,"PNG Image dimensions are too large.");
2806 mng_info->ob[object_id]->width=width;
2807 mng_info->ob[object_id]->height=height;
2808 mng_info->ob[object_id]->color_type=color_type;
2809 mng_info->ob[object_id]->sample_depth=bit_depth;
2810 mng_info->ob[object_id]->interlace_method=interlace_method;
2811 mng_info->ob[object_id]->compression_method=compression_method;
2812 mng_info->ob[object_id]->filter_method=filter_method;
2813 if (ping_info->valid & PNG_INFO_PLTE)
2814 {
2815 int
2816 number_colors;
2817
2818 png_colorp
2819 plte;
2820
2821 /*
2822 Copy the PLTE to the object buffer.
2823 */
2824 png_get_PLTE(ping,ping_info,&plte,&number_colors);
2825 mng_info->ob[object_id]->plte_length=number_colors;
2826 for (i=0; i < number_colors; i++)
2827 {
2828 mng_info->ob[object_id]->plte[i]=plte[i];
2829 }
2830 }
2831 else
2832 mng_info->ob[object_id]->plte_length=0;
2833 }
2834 }
2835#endif
2836 /*
2837 Relinquish resources.
2838 */
2839 png_destroy_read_struct(&ping,&ping_info,&end_info);
2840
2841 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2842#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2843 RelinquishSemaphoreInfo(png_semaphore);
2844#endif
2845
2846 if (logging != MagickFalse)
2847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2848 " exit ReadOnePNGImage()");
2849 return(image);
2850
2851/* end of reading one PNG image */
2852}
2853
2854static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2855{
2856 Image
2857 *image,
2858 *previous;
2859
2860 MagickBooleanType
2861 status;
2862
2863 MngInfo
2864 *mng_info;
2865
2866 char
2867 magic_number[MaxTextExtent];
2868
2869 int
2870 have_mng_structure,
2871 logging;
2872
2873 ssize_t
2874 count;
2875
2876 /*
2877 Open image file.
2878 */
2879 assert(image_info != (const ImageInfo *) NULL);
2880 assert(image_info->signature == MagickSignature);
2881 if (image_info->debug != MagickFalse)
2882 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2883 image_info->filename);
2884 assert(exception != (ExceptionInfo *) NULL);
2885 assert(exception->signature == MagickSignature);
2886 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()");
2887 image=AcquireImage(image_info);
2888 mng_info=(MngInfo *) NULL;
2889 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
2890 if (status == MagickFalse)
2891 ThrowReaderException(FileOpenError,"UnableToOpenFile");
2892 /*
2893 Verify PNG signature.
2894 */
2895 count=ReadBlob(image,8,(unsigned char *) magic_number);
2896 if (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
2897 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
2898 /*
2899 Allocate a MngInfo structure.
2900 */
2901 have_mng_structure=MagickFalse;
2902 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
2903 if (mng_info == (MngInfo *) NULL)
2904 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2905 /*
2906 Initialize members of the MngInfo structure.
2907 */
2908 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
2909 mng_info->image=image;
2910 have_mng_structure=MagickTrue;
2911
2912 previous=image;
2913 image=ReadOnePNGImage(mng_info,image_info,exception);
2914 MngInfoFreeStruct(mng_info,&have_mng_structure);
2915 if (image == (Image *) NULL)
2916 {
2917 if (previous != (Image *) NULL)
2918 {
2919 if (previous->signature != MagickSignature)
2920 ThrowReaderException(CorruptImageError,"CorruptImage");
2921 (void) CloseBlob(previous);
2922 (void) DestroyImageList(previous);
2923 }
2924 if (logging != MagickFalse)
2925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2926 "exit ReadPNGImage() with error");
2927 return((Image *) NULL);
2928 }
2929 (void) CloseBlob(image);
2930 if ((image->columns == 0) || (image->rows == 0))
2931 {
2932 if (logging != MagickFalse)
2933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2934 "exit ReadPNGImage() with error.");
2935 ThrowReaderException(CorruptImageError,"CorruptImage");
2936 }
2937 if (LocaleCompare(image_info->magick,"PNG8") == 0)
2938 {
2939 (void) SetImageType(image,PaletteType);
2940 if (image->matte != MagickFalse)
2941 {
2942 /* To do: Reduce to binary transparency */
2943 }
2944 }
2945 if (LocaleCompare(image_info->magick,"PNG24") == 0)
2946 {
2947 (void) SetImageType(image,TrueColorType);
2948 image->matte=MagickFalse;
2949 }
2950 if (LocaleCompare(image_info->magick,"PNG32") == 0)
2951 (void) SetImageType(image,TrueColorMatteType);
2952 if (logging != MagickFalse)
2953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
2954 return(image);
2955}
2956
2957
2958
2959#if defined(JNG_SUPPORTED)
2960/*
2961%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2962% %
2963% %
2964% %
2965% R e a d O n e J N G I m a g e %
2966% %
2967% %
2968% %
2969%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2970%
2971% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
2972% (minus the 8-byte signature) and returns it. It allocates the memory
2973% necessary for the new Image structure and returns a pointer to the new
2974% image.
2975%
2976% JNG support written by Glenn Randers-Pehrson, glennrp@image...
2977%
2978% The format of the ReadOneJNGImage method is:
2979%
2980% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2981% ExceptionInfo *exception)
2982%
2983% A description of each parameter follows:
2984%
2985% o mng_info: Specifies a pointer to a MngInfo structure.
2986%
2987% o image_info: the image info.
2988%
2989% o exception: return any errors or warnings in this structure.
2990%
2991*/
2992static Image *ReadOneJNGImage(MngInfo *mng_info,
2993 const ImageInfo *image_info, ExceptionInfo *exception)
2994{
2995 Image
2996 *alpha_image,
2997 *color_image,
2998 *image,
2999 *jng_image;
3000
3001 ImageInfo
3002 *alpha_image_info,
3003 *color_image_info;
3004
3005 long
3006 y;
3007
3008 MagickBooleanType
3009 status;
3010
3011 png_uint_32
3012 jng_height,
3013 jng_width;
3014
3015 png_byte
3016 jng_color_type,
3017 jng_image_sample_depth,
3018 jng_image_compression_method,
3019 jng_image_interlace_method,
3020 jng_alpha_sample_depth,
3021 jng_alpha_compression_method,
3022 jng_alpha_filter_method,
3023 jng_alpha_interlace_method;
3024
3025 register const PixelPacket
3026 *s;
3027
3028 register long
3029 i,
3030 x;
3031
3032 register PixelPacket
3033 *q;
3034
3035 register unsigned char
3036 *p;
3037
3038 unsigned int
3039 logging,
3040 read_JSEP,
3041 reading_idat,
3042 skip_to_iend;
3043
3044 unsigned long
3045 length;
3046
3047 jng_alpha_compression_method=0;
3048 jng_alpha_sample_depth=8;
3049 jng_color_type=0;
3050 jng_height=0;
3051 jng_width=0;
3052 alpha_image=(Image *) NULL;
3053 color_image=(Image *) NULL;
3054 alpha_image_info=(ImageInfo *) NULL;
3055 color_image_info=(ImageInfo *) NULL;
3056
3057 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3058 " enter ReadOneJNGImage()");
3059
3060 image=mng_info->image;
3061 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3062 {
3063 /*
3064 Allocate next image structure.
3065 */
3066 if (logging != MagickFalse)
3067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3068 " AcquireNextImage()");
3069 AcquireNextImage(image_info,image);
3070 if (GetNextImageInList(image) == (Image *) NULL)
3071 return((Image *) NULL);
3072 image=SyncNextImageInList(image);
3073 }
3074 mng_info->image=image;
3075
3076 /*
3077 Signature bytes have already been read.
3078 */
3079
3080 read_JSEP=MagickFalse;
3081 reading_idat=MagickFalse;
3082 skip_to_iend=MagickFalse;
3083 for (;;)
3084 {
3085 char
3086 type[MaxTextExtent];
3087
3088 unsigned char
3089 *chunk;
3090
3091 unsigned int
3092 count;
3093
3094 /*
3095 Read a new JNG chunk.
3096 */
3097 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3098 2*GetBlobSize(image));
3099 if (status == MagickFalse)
3100 break;
3101 type[0]='\0';
3102 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3103 length=ReadBlobMSBLong(image);
3104 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3105
3106 if (logging != MagickFalse)
3107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3108 " Reading JNG chunk type %c%c%c%c, length: %lu",
3109 type[0],type[1],type[2],type[3],length);
3110
3111 if (length > PNG_UINT_31_MAX || count == 0)
3112 ThrowReaderException(CorruptImageError,"CorruptImage");
3113 p=NULL;
3114 chunk=(unsigned char *) NULL;
3115 if (length)
3116 {
3117 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3118 if (chunk == (unsigned char *) NULL)
3119 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3120 for (i=0; i < (long) length; i++)
3121 chunk[i]=(unsigned char) ReadBlobByte(image);
3122 p=chunk;
3123 }
3124 (void) ReadBlobMSBLong(image); /* read crc word */
3125
3126 if (skip_to_iend)
3127 {
3128 if (length)
3129 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3130 continue;
3131 }
3132
3133 if (memcmp(type,mng_JHDR,4) == 0)
3134 {
3135 if (length == 16)
3136 {
3137 jng_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) |
3138 (p[2] << 8) | p[3]);
3139 jng_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) |
3140 (p[6] << 8) | p[7]);
3141 jng_color_type=p[8];
3142 jng_image_sample_depth=p[9];
3143 jng_image_compression_method=p[10];
3144 jng_image_interlace_method=p[11];
3145 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3146 NoInterlace;
3147 jng_alpha_sample_depth=p[12];
3148 jng_alpha_compression_method=p[13];
3149 jng_alpha_filter_method=p[14];
3150 jng_alpha_interlace_method=p[15];
3151 if (logging != MagickFalse)
3152 {
3153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3154 " jng_width: %16lu",jng_width);
3155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3156 " jng_width: %16lu",jng_height);
3157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3158 " jng_color_type: %16d",jng_color_type);
3159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3160 " jng_image_sample_depth: %3d",
3161 jng_image_sample_depth);
3162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3163 " jng_image_compression_method:%3d",
3164 jng_image_compression_method);
3165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3166 " jng_image_interlace_method: %3d",
3167 jng_image_interlace_method);
3168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3169 " jng_alpha_sample_depth: %3d",
3170 jng_alpha_sample_depth);
3171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3172 " jng_alpha_compression_method:%3d",
3173 jng_alpha_compression_method);
3174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3175 " jng_alpha_filter_method: %3d",
3176 jng_alpha_filter_method);
3177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3178 " jng_alpha_interlace_method: %3d",
3179 jng_alpha_interlace_method);
3180 }
3181 }
3182 if (length)
3183 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3184 continue;
3185 }
3186
3187
3188 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3189 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3190 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3191 {
3192 /*
3193 o create color_image
3194 o open color_blob, attached to color_image
3195 o if (color type has alpha)
3196 open alpha_blob, attached to alpha_image
3197 */
3198
3199 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3200 if (color_image_info == (ImageInfo *) NULL)
3201 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3202 GetImageInfo(color_image_info);
3203 color_image=AcquireImage(color_image_info);
3204 if (color_image == (Image *) NULL)
3205 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3206
3207 if (logging != MagickFalse)
3208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3209 " Creating color_blob.");
3210 (void) AcquireUniqueFilename(color_image->filename);
3211 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3212 exception);
3213 if (status == MagickFalse)
3214 return((Image *) NULL);
3215
3216 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3217 {
3218 alpha_image_info=(ImageInfo *)
3219 AcquireMagickMemory(sizeof(ImageInfo));
3220 if (alpha_image_info == (ImageInfo *) NULL)
3221 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3222 GetImageInfo(alpha_image_info);
3223 alpha_image=AcquireImage(alpha_image_info);
3224 if (alpha_image == (Image *) NULL)
3225 {
3226 alpha_image=DestroyImage(alpha_image);
3227 ThrowReaderException(ResourceLimitError,
3228 "MemoryAllocationFailed");
3229 }
3230 if (logging != MagickFalse)
3231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3232 " Creating alpha_blob.");
3233 (void) AcquireUniqueFilename(alpha_image->filename);
3234 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3235 exception);
3236 if (status == MagickFalse)
3237 return((Image *) NULL);
3238 if (jng_alpha_compression_method == 0)
3239 {
3240 unsigned char
3241 data[18];
3242
3243 if (logging != MagickFalse)
3244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3245 " Writing IHDR chunk to alpha_blob.");
3246 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3247 "\211PNG\r\n\032\n");
3248 (void) WriteBlobMSBULong(alpha_image,13L);
3249 PNGType(data,mng_IHDR);
3250 LogPNGChunk((int) logging,mng_IHDR,13L);
3251 PNGLong(data+4,jng_width);
3252 PNGLong(data+8,jng_height);
3253 data[12]=jng_alpha_sample_depth;
3254 data[13]=0; /* color_type gray */
3255 data[14]=0; /* compression method 0 */
3256 data[15]=0; /* filter_method 0 */
3257 data[16]=0; /* interlace_method 0 */
3258 (void) WriteBlob(alpha_image,17,data);
3259 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3260 }
3261 }
3262 reading_idat=MagickTrue;
3263 }
3264
3265 if (memcmp(type,mng_JDAT,4) == 0)
3266 {
3267 /*
3268 Copy chunk to color_image->blob
3269 */
3270
3271 if (logging != MagickFalse)
3272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3273 " Copying JDAT chunk data to color_blob.");
3274
3275 (void) WriteBlob(color_image,length,chunk);
3276 if (length)
3277 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3278 continue;
3279 }
3280
3281 if (memcmp(type,mng_IDAT,4) == 0)
3282 {
3283 png_byte
3284 data[5];
3285
3286 /*
3287 Copy IDAT header and chunk data to alpha_image->blob
3288 */
3289
3290 if (image_info->ping == MagickFalse)
3291 {
3292 if (logging != MagickFalse)
3293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3294 " Copying IDAT chunk data to alpha_blob.");
3295
3296 (void) WriteBlobMSBULong(alpha_image,(unsigned long) length);
3297 PNGType(data,mng_IDAT);
3298 LogPNGChunk((int) logging,mng_IDAT,length);
3299 (void) WriteBlob(alpha_image,4,data);
3300 (void) WriteBlob(alpha_image,length,chunk);
3301 (void) WriteBlobMSBULong(alpha_image,
3302 crc32(crc32(0,data,4),chunk,(uInt) length));
3303 }
3304 if (length)
3305 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3306 continue;
3307 }
3308
3309 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3310 {
3311 /*
3312 Copy chunk data to alpha_image->blob
3313 */
3314
3315 if (image_info->ping == MagickFalse)
3316 {
3317 if (logging != MagickFalse)
3318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3319 " Copying JDAA chunk data to alpha_blob.");
3320
3321 (void) WriteBlob(alpha_image,length,chunk);
3322 }
3323 if (length)
3324 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3325 continue;
3326 }
3327
3328 if (memcmp(type,mng_JSEP,4) == 0)
3329 {
3330 read_JSEP=MagickTrue;
3331 if (length)
3332 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3333 continue;
3334 }
3335
3336 if (memcmp(type,mng_bKGD,4) == 0)
3337 {
3338 if (length == 2)
3339 {
3340 image->background_color.red=ScaleCharToQuantum(p[1]);
3341 image->background_color.green=image->background_color.red;
3342 image->background_color.blue=image->background_color.red;
3343 }
3344 if (length == 6)
3345 {
3346 image->background_color.red=ScaleCharToQuantum(p[1]);
3347 image->background_color.green=ScaleCharToQuantum(p[3]);
3348 image->background_color.blue=ScaleCharToQuantum(p[5]);
3349 }
3350 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3351 continue;
3352 }
3353
3354 if (memcmp(type,mng_gAMA,4) == 0)
3355 {
3356 if (length == 4)
3357 image->gamma=((float) mng_get_long(p))*0.00001;
3358 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3359 continue;
3360 }
3361
3362 if (memcmp(type,mng_cHRM,4) == 0)
3363 {
3364 if (length == 32)
3365 {
3366 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3367 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3368 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3369 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3370 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3371 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3372 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3373 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3374 }
3375 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3376 continue;
3377 }
3378
3379 if (memcmp(type,mng_sRGB,4) == 0)
3380 {
3381 if (length == 1)
3382 {
3383 image->rendering_intent=(RenderingIntent) (p[0]+1);
3384 image->gamma=0.45455f;
3385 image->chromaticity.red_primary.x=0.6400f;
3386 image->chromaticity.red_primary.y=0.3300f;
3387 image->chromaticity.green_primary.x=0.3000f;
3388 image->chromaticity.green_primary.y=0.6000f;
3389 image->chromaticity.blue_primary.x=0.1500f;
3390 image->chromaticity.blue_primary.y=0.0600f;
3391 image->chromaticity.white_point.x=0.3127f;
3392 image->chromaticity.white_point.y=0.3290f;
3393 }
3394 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3395 continue;
3396 }
3397
3398 if (memcmp(type,mng_oFFs,4) == 0)
3399 {
3400 if (length > 8)
3401 {
3402 image->page.x=mng_get_long(p);
3403 image->page.y=mng_get_long(&p[4]);
3404 if ((int) p[8] != 0)
3405 {
3406 image->page.x/=10000;
3407 image->page.y/=10000;
3408 }
3409 }
3410 if (length)
3411 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3412 continue;
3413 }
3414
3415 if (memcmp(type,mng_pHYs,4) == 0)
3416 {
3417 if (length > 8)
3418 {
3419 image->x_resolution=(double) mng_get_long(p);
3420 image->y_resolution=(double) mng_get_long(&p[4]);
3421 if ((int) p[8] == PNG_RESOLUTION_METER)
3422 {
3423 image->units=PixelsPerCentimeterResolution;
3424 image->x_resolution=image->x_resolution/100.0f;
3425 image->y_resolution=image->y_resolution/100.0f;
3426 }
3427 }
3428 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3429 continue;
3430 }
3431
3432#if 0
3433 if (memcmp(type,mng_iCCP,4) == 0)
3434 {
3435 /* To do. */
3436 if (length)
3437 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3438 continue;
3439 }
3440#endif
3441
3442 if (length)
3443 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3444
3445 if (memcmp(type,mng_IEND,4))
3446 continue;
3447 break;
3448 }
3449
3450
3451 /* IEND found */
3452
3453 /*
3454 Finish up reading image data:
3455
3456 o read main image from color_blob.
3457
3458 o close color_blob.
3459
3460 o if (color_type has alpha)
3461 if alpha_encoding is PNG
3462 read secondary image from alpha_blob via ReadPNG
3463 if alpha_encoding is JPEG
3464 read secondary image from alpha_blob via ReadJPEG
3465
3466 o close alpha_blob.
3467
3468 o copy intensity of secondary image into
3469 opacity samples of main image.
3470
3471 o destroy the secondary image.
3472 */
3473
3474 (void) CloseBlob(color_image);
3475 if (logging != MagickFalse)
3476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3477 " Reading jng_image from color_blob.");
3478 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3479 color_image->filename);
3480 color_image_info->ping=MagickFalse; /* To do: avoid this */
3481 jng_image=ReadImage(color_image_info,exception);
3482 if (jng_image == (Image *) NULL)
3483 return((Image *) NULL);
3484
3485 (void) RelinquishUniqueFileResource(color_image->filename);
3486 color_image=DestroyImage(color_image);
3487 color_image_info=DestroyImageInfo(color_image_info);
3488
3489 if (jng_image == (Image *) NULL)
3490 return((Image *) NULL);
3491
3492 if (logging != MagickFalse)
3493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3494 " Copying jng_image pixels to main image.");
3495 image->rows=jng_height;
3496 image->columns=jng_width;
3497 length=image->columns*sizeof(PixelPacket);
3498 for (y=0; y < (long) image->rows; y++)
3499 {
3500 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3501 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3502 (void) CopyMagickMemory(q,s,length);
3503 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3504 break;
3505 }
3506 jng_image=DestroyImage(jng_image);
3507 if (image_info->ping == MagickFalse)
3508 {
3509 if (jng_color_type >= 12)
3510 {
3511 if (jng_alpha_compression_method == 0)
3512 {
3513 png_byte
3514 data[5];
3515 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3516 PNGType(data,mng_IEND);
3517 LogPNGChunk((int) logging,mng_IEND,0L);
3518 (void) WriteBlob(alpha_image,4,data);
3519 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3520 }
3521 (void) CloseBlob(alpha_image);
3522 if (logging != MagickFalse)
3523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3524 " Reading opacity from alpha_blob.");
3525
3526 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3527 "%s",alpha_image->filename);
3528
3529 jng_image=ReadImage(alpha_image_info,exception);
3530 if (jng_image != (Image *) NULL)
3531 for (y=0; y < (long) image->rows; y++)
3532 {
3533 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3534 &image->exception);
3535 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3536 if (image->matte != MagickFalse)
3537 for (x=(long) image->columns; x != 0; x--,q++,s++)
3538 q->opacity=(Quantum) QuantumRange-s->red;
3539 else
3540 for (x=(long) image->columns; x != 0; x--,q++,s++)
3541 {
3542 q->opacity=(Quantum) QuantumRange-s->red;
3543 if (q->opacity != OpaqueOpacity)
3544 image->matte=MagickTrue;
3545 }
3546 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3547 break;
3548 }
3549 (void) RelinquishUniqueFileResource(alpha_image->filename);
3550 alpha_image=DestroyImage(alpha_image);
3551 alpha_image_info=DestroyImageInfo(alpha_image_info);
3552 if (jng_image != (Image *) NULL)
3553 jng_image=DestroyImage(jng_image);
3554 }
3555 }
3556
3557 /*
3558 Read the JNG image.
3559 */
3560 if (mng_info->mng_type == 0)
3561 {
3562 mng_info->mng_width=jng_width;
3563 mng_info->mng_height=jng_height;
3564 }
3565 if (image->page.width == 0 && image->page.height == 0)
3566 {
3567 image->page.width=jng_width;
3568 image->page.height=jng_height;
3569 }
3570 if (image->page.x == 0 && image->page.y == 0)
3571 {
3572 image->page.x=mng_info->x_off[mng_info->object_id];
3573 image->page.y=mng_info->y_off[mng_info->object_id];
3574 }
3575 else
3576 {
3577 image->page.y=mng_info->y_off[mng_info->object_id];
3578 }
3579 mng_info->image_found++;
3580 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3581 2*GetBlobSize(image));
3582 if (logging != MagickFalse)
3583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3584 " exit ReadOneJNGImage()");
3585 return(image);
3586}
3587
3588/*
3589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3590% %
3591% %
3592% %
3593% R e a d J N G I m a g e %
3594% %
3595% %
3596% %
3597%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3598%
3599% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3600% (including the 8-byte signature) and returns it. It allocates the memory
3601% necessary for the new Image structure and returns a pointer to the new
3602% image.
3603%
3604% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3605%
3606% The format of the ReadJNGImage method is:
3607%
3608% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3609% *exception)
3610%
3611% A description of each parameter follows:
3612%
3613% o image_info: the image info.
3614%
3615% o exception: return any errors or warnings in this structure.
3616%
3617*/
3618
3619static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3620{
3621 Image
3622 *image,
3623 *previous;
3624
3625 MagickBooleanType
3626 status;
3627
3628 MngInfo
3629 *mng_info;
3630
3631 char
3632 magic_number[MaxTextExtent];
3633
3634 int
3635 have_mng_structure,
3636 logging;
3637
3638 size_t
3639 count;
3640
3641 /*
3642 Open image file.
3643 */
3644 assert(image_info != (const ImageInfo *) NULL);
3645 assert(image_info->signature == MagickSignature);
3646 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3647 assert(exception != (ExceptionInfo *) NULL);
3648 assert(exception->signature == MagickSignature);
3649 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()");
3650 image=AcquireImage(image_info);
3651 mng_info=(MngInfo *) NULL;
3652 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3653 if (status == MagickFalse)
3654 return((Image *) NULL);
3655 if (LocaleCompare(image_info->magick,"JNG") != 0)
3656 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3657 /*
3658 Verify JNG signature.
3659 */
3660 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3661 if (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3662 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3663 /*
3664 Allocate a MngInfo structure.
3665 */
3666 have_mng_structure=MagickFalse;
3667 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
3668 if (mng_info == (MngInfo *) NULL)
3669 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3670 /*
3671 Initialize members of the MngInfo structure.
3672 */
3673 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3674 have_mng_structure=MagickTrue;
3675
3676 mng_info->image=image;
3677 previous=image;
3678 image=ReadOneJNGImage(mng_info,image_info,exception);
3679 MngInfoFreeStruct(mng_info,&have_mng_structure);
3680 if (image == (Image *) NULL)
3681 {
3682 if (IsImageObject(previous) != MagickFalse)
3683 {
3684 (void) CloseBlob(previous);
3685 (void) DestroyImageList(previous);
3686 }
3687 if (logging != MagickFalse)
3688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3689 "exit ReadJNGImage() with error");
3690 return((Image *) NULL);
3691 }
3692 (void) CloseBlob(image);
3693 if (image->columns == 0 || image->rows == 0)
3694 {
3695 if (logging != MagickFalse)
3696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3697 "exit ReadJNGImage() with error");
3698 ThrowReaderException(CorruptImageError,"CorruptImage");
3699 }
3700 if (logging != MagickFalse)
3701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3702 return(image);
3703}
3704#endif
3705
3706static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3707{
3708 char
3709 page_geometry[MaxTextExtent];
3710
3711 Image
3712 *image,
3713 *previous;
3714
3715 int
3716 have_mng_structure;
3717
3718 volatile int
3719 first_mng_object,
3720 logging,
3721 object_id,
3722 term_chunk_found,
3723 skip_to_iend;
3724
3725 volatile long
3726 image_count=0;
3727
3728 MagickBooleanType
3729 status;
3730
3731 MagickOffsetType
3732 offset;
3733
3734 MngInfo
3735 *mng_info;
3736
3737 MngBox
3738 default_fb,
3739 fb,
3740 previous_fb;
3741
3742#if defined(MNG_INSERT_LAYERS)
3743 PixelPacket
3744 mng_background_color;
3745#endif
3746
3747 register unsigned char
3748 *p;
3749
3750 register long
3751 i;
3752
3753 size_t
3754 count;
3755
3756 long
3757 loop_level;
3758
3759 volatile short
3760 skipping_loop;
3761
3762#if defined(MNG_INSERT_LAYERS)
3763 unsigned int
3764 mandatory_back=0;
3765#endif
3766
3767 volatile unsigned int
3768#ifdef MNG_OBJECT_BUFFERS
3769 mng_background_object=0,
3770#endif
3771 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
3772
3773 unsigned long
3774 default_frame_timeout,
3775 frame_timeout,
3776#if defined(MNG_INSERT_LAYERS)
3777 image_height,
3778 image_width,
3779#endif
3780 length;
3781
3782 volatile unsigned long
3783 default_frame_delay,
3784 final_delay,
3785 final_image_delay,
3786 frame_delay,
3787#if defined(MNG_INSERT_LAYERS)
3788 insert_layers,
3789#endif
3790 mng_iterations=1,
3791 simplicity=0,
3792 subframe_height=0,
3793 subframe_width=0;
3794
3795 previous_fb.top=0;
3796 previous_fb.bottom=0;
3797 previous_fb.left=0;
3798 previous_fb.right=0;
3799 default_fb.top=0;
3800 default_fb.bottom=0;
3801 default_fb.left=0;
3802 default_fb.right=0;
3803
3804 /*
3805 Set image_info->type=OptimizeType (new in version 5.4.0) to get the
3806 following optimizations:
3807
3808 o 16-bit depth is reduced to 8 if all pixels contain samples whose
3809 high byte and low byte are identical.
3810 o Opaque matte channel is removed.
3811 o If matte channel is present but only one transparent color is
3812 present, RGB+tRNS is written instead of RGBA
3813 o Grayscale images are reduced to 1, 2, or 4 bit depth if
3814 this can be done without loss.
3815 o Palette is sorted to remove unused entries and to put a
3816 transparent color first, if PNG_SORT_PALETTE is also defined.
3817 */
3818
3819 /*
3820 Open image file.
3821 */
3822 assert(image_info != (const ImageInfo *) NULL);
3823 assert(image_info->signature == MagickSignature);
3824 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3825 assert(exception != (ExceptionInfo *) NULL);
3826 assert(exception->signature == MagickSignature);
3827 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()");
3828 image=AcquireImage(image_info);
3829 mng_info=(MngInfo *) NULL;
3830 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3831 if (status == MagickFalse)
3832 return((Image *) NULL);
3833 first_mng_object=MagickFalse;
3834 skipping_loop=(-1);
3835 have_mng_structure=MagickFalse;
3836 /*
3837 Allocate a MngInfo structure.
3838 */
3839 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3840 if (mng_info == (MngInfo *) NULL)
3841 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3842 /*
3843 Initialize members of the MngInfo structure.
3844 */
3845 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3846 mng_info->image=image;
3847 have_mng_structure=MagickTrue;
3848#if (MAGICKCORE_QUANTUM_DEPTH == 16)
3849 mng_info->optimize=image_info->type == OptimizeType;
3850#endif
3851
3852 if (LocaleCompare(image_info->magick,"MNG") == 0)
3853 {
3854 char
3855 magic_number[MaxTextExtent];
3856
3857 /*
3858 Verify MNG signature.
3859 */
3860 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3861 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
3862 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3863 /*
3864 Initialize some nonzero members of the MngInfo structure.
3865 */
3866 for (i=0; i < MNG_MAX_OBJECTS; i++)
3867 {
3868 mng_info->object_clip[i].right=(long) PNG_UINT_31_MAX;
3869 mng_info->object_clip[i].bottom=(long) PNG_UINT_31_MAX;
3870 }
3871 mng_info->exists[0]=MagickTrue;
3872 }
3873 first_mng_object=MagickTrue;
3874 mng_type=0;
3875#if defined(MNG_INSERT_LAYERS)
3876 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
3877#endif
3878 default_frame_delay=0;
3879 default_frame_timeout=0;
3880 frame_delay=0;
3881 final_delay=1;
3882 mng_info->ticks_per_second=1UL*image->ticks_per_second;
3883 object_id=0;
3884 skip_to_iend=MagickFalse;
3885 term_chunk_found=MagickFalse;
3886 mng_info->framing_mode=1;
3887#if defined(MNG_INSERT_LAYERS)
3888 mandatory_back=MagickFalse;
3889#endif
3890#if defined(MNG_INSERT_LAYERS)
3891 mng_background_color=image->background_color;
3892#endif
3893 default_fb=mng_info->frame;
3894 previous_fb=mng_info->frame;
3895 do
3896 {
3897 char
3898 type[MaxTextExtent];
3899
3900 if (LocaleCompare(image_info->magick,"MNG") == 0)
3901 {
3902 unsigned char
3903 *chunk;
3904
3905 /*
3906 Read a new chunk.
3907 */
3908 type[0]='\0';
3909 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3910 length=ReadBlobMSBLong(image);
3911 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
3912
3913 if (logging != MagickFalse)
3914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3915 " Reading MNG chunk type %c%c%c%c, length: %lu",
3916 type[0],type[1],type[2],type[3],length);
3917
3918 if (length > PNG_UINT_31_MAX)
3919 status=MagickFalse;
3920 if (count == 0)
3921 ThrowReaderException(CorruptImageError,"CorruptImage");
3922 p=NULL;
3923 chunk=(unsigned char *) NULL;
3924 if (length)
3925 {
3926 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3927 if (chunk == (unsigned char *) NULL)
3928 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3929 for (i=0; i < (long) length; i++)
3930 chunk[i]=(unsigned char) ReadBlobByte(image);
3931 p=chunk;
3932 }
3933 (void) ReadBlobMSBLong(image); /* read crc word */
3934
3935#if !defined(JNG_SUPPORTED)
3936 if (memcmp(type,mng_JHDR,4) == 0)
3937 {
3938 skip_to_iend=MagickTrue;
3939 if (mng_info->jhdr_warning == 0)
3940 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3941 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
3942 mng_info->jhdr_warning++;
3943 }
3944#endif
3945 if (memcmp(type,mng_DHDR,4) == 0)
3946 {
3947 skip_to_iend=MagickTrue;
3948 if (mng_info->dhdr_warning == 0)
3949 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3950 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
3951 mng_info->dhdr_warning++;
3952 }
3953 if (memcmp(type,mng_MEND,4) == 0)
3954 break;
3955 if (skip_to_iend)
3956 {
3957 if (memcmp(type,mng_IEND,4) == 0)
3958 skip_to_iend=MagickFalse;
3959 if (length)
3960 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3961 if (logging != MagickFalse)
3962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3963 " Skip to IEND.");
3964 continue;
3965 }
3966 if (memcmp(type,mng_MHDR,4) == 0)
3967 {
3968 mng_info->mng_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) |
3969 (p[2] << 8) | p[3]);
3970 mng_info->mng_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) |
3971 (p[6] << 8) | p[7]);
3972 if (logging != MagickFalse)
3973 {
3974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3975 " MNG width: %lu",mng_info->mng_width);
3976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3977 " MNG height: %lu",mng_info->mng_height);
3978 }
3979 p+=8;
3980 mng_info->ticks_per_second=(unsigned long) mng_get_long(p);
3981 if (mng_info->ticks_per_second == 0)
3982 default_frame_delay=0;
3983 else
3984 default_frame_delay=1UL*image->ticks_per_second/
3985 mng_info->ticks_per_second;
3986 frame_delay=default_frame_delay;
3987 simplicity=0;
3988 if (length > 16)
3989 {
3990 p+=16;
3991 simplicity=(unsigned long) mng_get_long(p);
3992 }
3993 mng_type=1; /* Full MNG */
3994 if ((simplicity != 0) && ((simplicity | 11) == 11))
3995 mng_type=2; /* LC */
3996 if ((simplicity != 0) && ((simplicity | 9) == 9))
3997 mng_type=3; /* VLC */
3998#if defined(MNG_INSERT_LAYERS)
3999 if (mng_type != 3)
4000 insert_layers=MagickTrue;
4001#endif
4002 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4003 {
4004 /*
4005 Allocate next image structure.
4006 */
4007 AcquireNextImage(image_info,image);
4008 if (GetNextImageInList(image) == (Image *) NULL)
4009 return((Image *) NULL);
4010 image=SyncNextImageInList(image);
4011 mng_info->image=image;
4012 }
4013
4014 if ((mng_info->mng_width > 65535L) ||
4015 (mng_info->mng_height > 65535L))
4016 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4017 (void) FormatMagickString(page_geometry,MaxTextExtent,"%lux%lu+0+0",
4018 mng_info->mng_width,mng_info->mng_height);
4019 mng_info->frame.left=0;
4020 mng_info->frame.right=(long) mng_info->mng_width;
4021 mng_info->frame.top=0;
4022 mng_info->frame.bottom=(long) mng_info->mng_height;
4023 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4024 for (i=0; i < MNG_MAX_OBJECTS; i++)
4025 mng_info->object_clip[i]=mng_info->frame;
4026 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4027 continue;
4028 }
4029
4030 if (memcmp(type,mng_TERM,4) == 0)
4031 {
4032 int
4033 repeat=0;
4034
4035
4036 if (length)
4037 repeat=p[0];
4038 if (repeat == 3)
4039 {
4040 final_delay=(png_uint_32) mng_get_long(&p[2]);
4041 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4042 if (mng_iterations == PNG_UINT_31_MAX)
4043 mng_iterations=0;
4044 image->iterations=mng_iterations;
4045 term_chunk_found=MagickTrue;
4046 }
4047 if (logging != MagickFalse)
4048 {
4049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " repeat=%d",repeat);
4051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4052 " final_delay=%ld",final_delay);
4053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4054 " image->iterations=%ld",image->iterations);
4055 }
4056 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4057 continue;
4058 }
4059 if (memcmp(type,mng_DEFI,4) == 0)
4060 {
4061 if (mng_type == 3)
4062 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4063 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4064 image->filename);
4065 object_id=(p[0] << 8) | p[1];
4066 if (mng_type == 2 && object_id != 0)
4067 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4068 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4069 image->filename);
4070 if (object_id > MNG_MAX_OBJECTS)
4071 {
4072 /*
4073 Instead ofsuing a warning we should allocate a larger
4074 MngInfo structure and continue.
4075 */
4076 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4077 CoderError,"object id too large","`%s'",image->filename);
4078 object_id=MNG_MAX_OBJECTS;
4079 }
4080 if (mng_info->exists[object_id])
4081 if (mng_info->frozen[object_id])
4082 {
4083 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4084 (void) ThrowMagickException(&image->exception,
4085 GetMagickModule(),CoderError,
4086 "DEFI cannot redefine a frozen MNG object","`%s'",
4087 image->filename);
4088 continue;
4089 }
4090 mng_info->exists[object_id]=MagickTrue;
4091 if (length > 2)
4092 mng_info->invisible[object_id]=p[2];
4093 /*
4094 Extract object offset info.
4095 */
4096 if (length > 11)
4097 {
4098 mng_info->x_off[object_id]=(long) ((p[4] << 24) | (p[5] << 16) |
4099 (p[6] << 8) | p[7]);
4100 mng_info->y_off[object_id]=(long) ((p[8] << 24) | (p[9] << 16) |
4101 (p[10] << 8) | p[11]);
4102 if (logging != MagickFalse)
4103 {
4104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4105 " x_off[%d]: %lu",object_id,mng_info->x_off[object_id]);
4106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4107 " y_off[%d]: %lu",object_id,mng_info->y_off[object_id]);
4108 }
4109 }
4110 /*
4111 Extract object clipping info.
4112 */
4113 if (length > 27)
4114 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4115 &p[12]);
4116 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4117 continue;
4118 }
4119 if (memcmp(type,mng_bKGD,4) == 0)
4120 {
4121 mng_info->have_global_bkgd=MagickFalse;
4122 if (length > 5)
4123 {
4124 mng_info->mng_global_bkgd.red=
4125 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4126 mng_info->mng_global_bkgd.green=
4127 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4128 mng_info->mng_global_bkgd.blue=
4129 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4130 mng_info->have_global_bkgd=MagickTrue;
4131 }
4132 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4133 continue;
4134 }
4135 if (memcmp(type,mng_BACK,4) == 0)
4136 {
4137#if defined(MNG_INSERT_LAYERS)
4138 if (length > 6)
4139 mandatory_back=p[6];
4140 else
4141 mandatory_back=0;
4142 if (mandatory_back && length > 5)
4143 {
4144 mng_background_color.red=
4145 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4146 mng_background_color.green=
4147 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4148 mng_background_color.blue=
4149 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4150 mng_background_color.opacity=OpaqueOpacity;
4151 }
4152#ifdef MNG_OBJECT_BUFFERS
4153 if (length > 8)
4154 mng_background_object=(p[7] << 8) | p[8];
4155#endif
4156#endif
4157 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4158 continue;
4159 }
4160 if (memcmp(type,mng_PLTE,4) == 0)
4161 {
4162 /*
4163 Read global PLTE.
4164 */
4165 if (length && (length < 769))
4166 {
4167 if (mng_info->global_plte == (png_colorp) NULL)
4168 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4169 sizeof(*mng_info->global_plte));
4170 for (i=0; i < (long) (length/3); i++)
4171 {
4172 mng_info->global_plte[i].red=p[3*i];
4173 mng_info->global_plte[i].green=p[3*i+1];
4174 mng_info->global_plte[i].blue=p[3*i+2];
4175 }
4176 mng_info->global_plte_length=length/3;
4177 }
4178#ifdef MNG_LOOSE
4179 for ( ; i < 256; i++)
4180 {
4181 mng_info->global_plte[i].red=i;
4182 mng_info->global_plte[i].green=i;
4183 mng_info->global_plte[i].blue=i;
4184 }
4185 if (length)
4186 mng_info->global_plte_length=256;
4187#endif
4188 else
4189 mng_info->global_plte_length=0;
4190 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4191 continue;
4192 }
4193 if (memcmp(type,mng_tRNS,4) == 0)
4194 {
4195 /* read global tRNS */
4196
4197 if (length < 257)
4198 for (i=0; i < (long) length; i++)
4199 mng_info->global_trns[i]=p[i];
4200
4201#ifdef MNG_LOOSE
4202 for ( ; i < 256; i++)
4203 mng_info->global_trns[i]=255;
4204#endif
4205 mng_info->global_trns_length=length;
4206 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4207 continue;
4208 }
4209 if (memcmp(type,mng_gAMA,4) == 0)
4210 {
4211 if (length == 4)
4212 {
4213 long
4214 igamma;
4215
4216 igamma=mng_get_long(p);
4217 mng_info->global_gamma=((float) igamma)*0.00001;
4218 mng_info->have_global_gama=MagickTrue;
4219 }
4220 else
4221 mng_info->have_global_gama=MagickFalse;
4222 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4223 continue;
4224 }
4225
4226 if (memcmp(type,mng_cHRM,4) == 0)
4227 {
4228 /*
4229 Read global cHRM
4230 */
4231 if (length == 32)
4232 {
4233 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4234 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4235 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4236 mng_info->global_chrm.red_primary.y=0.00001*
4237 mng_get_long(&p[12]);
4238 mng_info->global_chrm.green_primary.x=0.00001*
4239 mng_get_long(&p[16]);
4240 mng_info->global_chrm.green_primary.y=0.00001*
4241 mng_get_long(&p[20]);
4242 mng_info->global_chrm.blue_primary.x=0.00001*
4243 mng_get_long(&p[24]);
4244 mng_info->global_chrm.blue_primary.y=0.00001*
4245 mng_get_long(&p[28]);
4246 mng_info->have_global_chrm=MagickTrue;
4247 }
4248 else
4249 mng_info->have_global_chrm=MagickFalse;
4250 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4251 continue;
4252 }
4253 if (memcmp(type,mng_sRGB,4) == 0)
4254 {
4255 /*
4256 Read global sRGB.
4257 */
4258 if (length)
4259 {
4260 mng_info->global_srgb_intent=(RenderingIntent) (p[0]+1);
4261 mng_info->have_global_srgb=MagickTrue;
4262 }
4263 else
4264 mng_info->have_global_srgb=MagickFalse;
4265 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4266 continue;
4267 }
4268 if (memcmp(type,mng_iCCP,4) == 0)
4269 {
4270 /* To do. */
4271
4272 /*
4273 Read global iCCP.
4274 */
4275 if (length)
4276 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4277 continue;
4278 }
4279 if (memcmp(type,mng_FRAM,4) == 0)
4280 {
4281 if (mng_type == 3)
4282 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4283 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4284 image->filename);
4285 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4286 image->delay=frame_delay;
4287 frame_delay=default_frame_delay;
4288 frame_timeout=default_frame_timeout;
4289 fb=default_fb;
4290 if (length)
4291 if (p[0])
4292 mng_info->framing_mode=p[0];
4293 if (logging != MagickFalse)
4294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4295 " Framing_mode=%d",mng_info->framing_mode);
4296 if (length > 6)
4297 {
4298 /*
4299 Note the delay and frame clipping boundaries.
4300 */
4301 p++; /* framing mode */
4302 while (*p && ((p-chunk) < (long) length))
4303 p++; /* frame name */
4304 p++; /* frame name terminator */
4305 if ((p-chunk) < (long) (length-4))
4306 {
4307 int
4308 change_delay,
4309 change_timeout,
4310 change_clipping;
4311
4312 change_delay=(*p++);
4313 change_timeout=(*p++);
4314 change_clipping=(*p++);
4315 p++; /* change_sync */
4316 if (change_delay)
4317 {
4318 frame_delay=(1UL*image->ticks_per_second*
4319 (mng_get_long(p))/mng_info->ticks_per_second);
4320 if (change_delay == 2)
4321 default_frame_delay=frame_delay;
4322 p+=4;
4323 if (logging != MagickFalse)
4324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4325 " Framing_delay=%ld",frame_delay);
4326 }
4327 if (change_timeout)
4328 {
4329 frame_timeout=(1UL*image->ticks_per_second*
4330 (mng_get_long(p))/mng_info->ticks_per_second);
4331 if (change_delay == 2)
4332 default_frame_timeout=frame_timeout;
4333 p+=4;
4334 if (logging != MagickFalse)
4335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4336 " Framing_timeout=%ld",frame_timeout);
4337 }
4338 if (change_clipping)
4339 {
4340 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4341 p+=17;
4342 previous_fb=fb;
4343 if (logging != MagickFalse)
4344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4345 " Frame_clipping: L=%ld R=%ld T=%ld B=%ld",
4346 fb.left, fb.right,fb.top,fb.bottom);
4347 if (change_clipping == 2)
4348 default_fb=fb;
4349 }
4350 }
4351 }
4352 mng_info->clip=fb;
4353 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4354 subframe_width=(unsigned long) (mng_info->clip.right
4355 -mng_info->clip.left);
4356 subframe_height=(unsigned long) (mng_info->clip.bottom
4357 -mng_info->clip.top);
4358 /*
4359 Insert a background layer behind the frame if framing_mode is 4.
4360 */
4361#if defined(MNG_INSERT_LAYERS)
4362 if (logging != MagickFalse)
4363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4364 " subframe_width=%lu, subframe_height=%lu",
4365 subframe_width, subframe_height);
4366 if (insert_layers && (mng_info->framing_mode == 4) &&
4367 (subframe_width) && (subframe_height))
4368 {
4369 /*
4370 Allocate next image structure.
4371 */
4372 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4373 {
4374 AcquireNextImage(image_info,image);
4375 if (GetNextImageInList(image) == (Image *) NULL)
4376 {
4377 image=DestroyImageList(image);
4378 MngInfoFreeStruct(mng_info,&have_mng_structure);
4379 return((Image *) NULL);
4380 }
4381 image=SyncNextImageInList(image);
4382 }
4383 mng_info->image=image;
4384 if (term_chunk_found)
4385 {
4386 image->start_loop=MagickTrue;
4387 image->iterations=mng_iterations;
4388 term_chunk_found=MagickFalse;
4389 }
4390 else
4391 image->start_loop=MagickFalse;
4392 image->columns=subframe_width;
4393 image->rows=subframe_height;
4394 image->page.width=subframe_width;
4395 image->page.height=subframe_height;
4396 image->page.x=mng_info->clip.left;
4397 image->page.y=mng_info->clip.top;
4398 image->background_color=mng_background_color;
4399 image->matte=MagickFalse;
4400 image->delay=0;
4401 (void) SetImageBackgroundColor(image);
4402 if (logging != MagickFalse)
4403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404 " Inserted background layer, L=%ld, R=%ld, T=%ld, B=%ld",
4405 mng_info->clip.left,mng_info->clip.right,
4406 mng_info->clip.top,mng_info->clip.bottom);
4407 }
4408#endif
4409 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4410 continue;
4411 }
4412 if (memcmp(type,mng_CLIP,4) == 0)
4413 {
4414 unsigned int
4415 first_object,
4416 last_object;
4417
4418 /*
4419 Read CLIP.
4420 */
4421 first_object=(p[0] << 8) | p[1];
4422 last_object=(p[2] << 8) | p[3];
4423 for (i=(int) first_object; i <= (int) last_object; i++)
4424 {
4425 if (mng_info->exists[i] && !mng_info->frozen[i])
4426 {
4427 MngBox
4428 box;
4429
4430 box=mng_info->object_clip[i];
4431 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4432 }
4433 }
4434 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4435 continue;
4436 }
4437 if (memcmp(type,mng_SAVE,4) == 0)
4438 {
4439 for (i=1; i < MNG_MAX_OBJECTS; i++)
4440 if (mng_info->exists[i])
4441 {
4442 mng_info->frozen[i]=MagickTrue;
4443#ifdef MNG_OBJECT_BUFFERS
4444 if (mng_info->ob[i] != (MngBuffer *) NULL)
4445 mng_info->ob[i]->frozen=MagickTrue;
4446#endif
4447 }
4448 if (length)
4449 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4450 continue;
4451 }
4452
4453 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4454 {
4455 /*
4456 Read DISC or SEEK.
4457 */
4458 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4459 {
4460 for (i=1; i < MNG_MAX_OBJECTS; i++)
4461 MngInfoDiscardObject(mng_info,i);
4462 }
4463 else
4464 {
4465 register long
4466 j;
4467
4468 for (j=0; j < (long) length; j+=2)
4469 {
4470 i=p[j] << 8 | p[j+1];
4471 MngInfoDiscardObject(mng_info,i);
4472 }
4473 }
4474 if (length)
4475 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4476 continue;
4477 }
4478 if (memcmp(type,mng_MOVE,4) == 0)
4479 {
4480 unsigned long
4481 first_object,
4482 last_object;
4483
4484 /*
4485 read MOVE
4486 */
4487 first_object=(p[0] << 8) | p[1];
4488 last_object=(p[2] << 8) | p[3];
4489 for (i=(long) first_object; i <= (long) last_object; i++)
4490 {
4491 if (mng_info->exists[i] && !mng_info->frozen[i])
4492 {
4493 MngPair
4494 new_pair;
4495
4496 MngPair
4497 old_pair;
4498
4499 old_pair.a=mng_info->x_off[i];
4500 old_pair.b=mng_info->y_off[i];
4501 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4502 mng_info->x_off[i]=new_pair.a;
4503 mng_info->y_off[i]=new_pair.b;
4504 }
4505 }
4506 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4507 continue;
4508 }
4509
4510 if (memcmp(type,mng_LOOP,4) == 0)
4511 {
4512 long loop_iters=1;
4513 loop_level=chunk[0];
4514 mng_info->loop_active[loop_level]=1; /* mark loop active */
4515 /*
4516 Record starting point.
4517 */
4518 loop_iters=mng_get_long(&chunk[1]);
4519 if (logging != MagickFalse)
4520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4521 " LOOP level %ld has %ld iterations ",loop_level,loop_iters);
4522 if (loop_iters == 0)
4523 skipping_loop=loop_level;
4524 else
4525 {
4526 mng_info->loop_jump[loop_level]=TellBlob(image);
4527 mng_info->loop_count[loop_level]=loop_iters;
4528 }
4529 mng_info->loop_iteration[loop_level]=0;
4530 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4531 continue;
4532 }
4533 if (memcmp(type,mng_ENDL,4) == 0)
4534 {
4535 loop_level=chunk[0];
4536 if (skipping_loop > 0)
4537 {
4538 if (skipping_loop == loop_level)
4539 {
4540 /*
4541 Found end of zero-iteration loop.
4542 */
4543 skipping_loop=(-1);
4544 mng_info->loop_active[loop_level]=0;
4545 }
4546 }
4547 else
4548 {
4549 if (mng_info->loop_active[loop_level] == 1)
4550 {
4551 mng_info->loop_count[loop_level]--;
4552 mng_info->loop_iteration[loop_level]++;
4553 if (logging != MagickFalse)
4554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4555 " ENDL: LOOP level %ld has %ld remaining iterations ",
4556 loop_level,mng_info->loop_count[loop_level]);
4557 if (mng_info->loop_count[loop_level] != 0)
4558 {
4559 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4560 SEEK_SET);
4561 if (offset < 0)
4562 ThrowReaderException(CorruptImageError,
4563 "ImproperImageHeader");
4564 }
4565 else
4566 {
4567 short
4568 last_level;
4569
4570 /*
4571 Finished loop.
4572 */
4573 mng_info->loop_active[loop_level]=0;
4574 last_level=(-1);
4575 for (i=0; i < loop_level; i++)
4576 if (mng_info->loop_active[i] == 1)
4577 last_level=(short) i;
4578 loop_level=last_level;
4579 }
4580 }
4581 }
4582 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4583 continue;
4584 }
4585 if (memcmp(type,mng_CLON,4) == 0)
4586 {
4587 if (mng_info->clon_warning == 0)
4588 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4589 CoderError,"CLON is not implemented yet","`%s'",
4590 image->filename);
4591 mng_info->clon_warning++;
4592 }
4593 if (memcmp(type,mng_MAGN,4) == 0)
4594 {
4595 png_uint_16
4596 magn_first,
4597 magn_last,
4598 magn_mb,
4599 magn_ml,
4600 magn_mr,
4601 magn_mt,
4602 magn_mx,
4603 magn_my,
4604 magn_methx,
4605 magn_methy;
4606
4607 if (length > 1)
4608 magn_first=(p[0] << 8) | p[1];
4609 else
4610 magn_first=0;
4611 if (length > 3)
4612 magn_last=(p[2] << 8) | p[3];
4613 else
4614 magn_last=magn_first;
4615#ifndef MNG_OBJECT_BUFFERS
4616 if (magn_first || magn_last)
4617 if (mng_info->magn_warning == 0)
4618 {
4619 (void) ThrowMagickException(&image->exception,
4620 GetMagickModule(),CoderError,
4621 "MAGN is not implemented yet for nonzero objects",
4622 "`%s'",image->filename);
4623 mng_info->magn_warning++;
4624 }
4625#endif
4626 if (length > 4)
4627 magn_methx=p[4];
4628 else
4629 magn_methx=0;
4630
4631 if (length > 6)
4632 magn_mx=(p[5] << 8) | p[6];
4633 else
4634 magn_mx=1;
4635 if (magn_mx == 0)
4636 magn_mx=1;
4637
4638 if (length > 8)
4639 magn_my=(p[7] << 8) | p[8];
4640 else
4641 magn_my=magn_mx;
4642 if (magn_my == 0)
4643 magn_my=1;
4644
4645 if (length > 10)
4646 magn_ml=(p[9] << 8) | p[10];
4647 else
4648 magn_ml=magn_mx;
4649 if (magn_ml == 0)
4650 magn_ml=1;
4651
4652 if (length > 12)
4653 magn_mr=(p[11] << 8) | p[12];
4654 else
4655 magn_mr=magn_mx;
4656 if (magn_mr == 0)
4657 magn_mr=1;
4658
4659 if (length > 14)
4660 magn_mt=(p[13] << 8) | p[14];
4661 else
4662 magn_mt=magn_my;
4663 if (magn_mt == 0)
4664 magn_mt=1;
4665
4666 if (length > 16)
4667 magn_mb=(p[15] << 8) | p[16];
4668 else
4669 magn_mb=magn_my;
4670 if (magn_mb == 0)
4671 magn_mb=1;
4672
4673 if (length > 17)
4674 magn_methy=p[17];
4675 else
4676 magn_methy=magn_methx;
4677
4678 if (magn_methx > 5 || magn_methy > 5)
4679 if (mng_info->magn_warning == 0)
4680 {
4681 (void) ThrowMagickException(&image->exception,
4682 GetMagickModule(),CoderError,
4683 "Unknown MAGN method in MNG datastream","`%s'",
4684 image->filename);
4685 mng_info->magn_warning++;
4686 }
4687#ifdef MNG_OBJECT_BUFFERS
4688 /* Magnify existing objects in the range magn_first to magn_last */
4689#endif
4690 if (magn_first == 0 || magn_last == 0)
4691 {
4692 /* Save the magnification factors for object 0 */
4693 mng_info->magn_mb=magn_mb;
4694 mng_info->magn_ml=magn_ml;
4695 mng_info->magn_mr=magn_mr;
4696 mng_info->magn_mt=magn_mt;
4697 mng_info->magn_mx=magn_mx;
4698 mng_info->magn_my=magn_my;
4699 mng_info->magn_methx=magn_methx;
4700 mng_info->magn_methy=magn_methy;
4701 }
4702 }
4703 if (memcmp(type,mng_PAST,4) == 0)
4704 {
4705 if (mng_info->past_warning == 0)
4706 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4707 CoderError,"PAST is not implemented yet","`%s'",
4708 image->filename);
4709 mng_info->past_warning++;
4710 }
4711 if (memcmp(type,mng_SHOW,4) == 0)
4712 {
4713 if (mng_info->show_warning == 0)
4714 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4715 CoderError,"SHOW is not implemented yet","`%s'",
4716 image->filename);
4717 mng_info->show_warning++;
4718 }
4719 if (memcmp(type,mng_sBIT,4) == 0)
4720 {
4721 if (length < 4)
4722 mng_info->have_global_sbit=MagickFalse;
4723 else
4724 {
4725 mng_info->global_sbit.gray=p[0];
4726 mng_info->global_sbit.red=p[0];
4727 mng_info->global_sbit.green=p[1];
4728 mng_info->global_sbit.blue=p[2];
4729 mng_info->global_sbit.alpha=p[3];
4730 mng_info->have_global_sbit=MagickTrue;
4731 }
4732 }
4733 if (memcmp(type,mng_pHYs,4) == 0)
4734 {
4735 if (length > 8)
4736 {
4737 mng_info->global_x_pixels_per_unit=
4738 (unsigned long) mng_get_long(p);
4739 mng_info->global_y_pixels_per_unit=
4740 (unsigned long) mng_get_long(&p[4]);
4741 mng_info->global_phys_unit_type=p[8];
4742 mng_info->have_global_phys=MagickTrue;
4743 }
4744 else
4745 mng_info->have_global_phys=MagickFalse;
4746 }
4747 if (memcmp(type,mng_pHYg,4) == 0)
4748 {
4749 if (mng_info->phyg_warning == 0)
4750 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4751 CoderError,"pHYg is not implemented.","`%s'",image->filename);
4752 mng_info->phyg_warning++;
4753 }
4754 if (memcmp(type,mng_BASI,4) == 0)
4755 {
4756 skip_to_iend=MagickTrue;
4757 if (mng_info->basi_warning == 0)
4758 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4759 CoderError,"BASI is not implemented yet","`%s'",
4760 image->filename);
4761 mng_info->basi_warning++;
4762#ifdef MNG_BASI_SUPPORTED
4763 basi_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) |
4764 (p[2] << 8) | p[3]);
4765 basi_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) |
4766 (p[6] << 8) | p[7]);
4767 basi_color_type=p[8];
4768 basi_compression_method=p[9];
4769 basi_filter_type=p[10];
4770 basi_interlace_method=p[11];
4771 if (length > 11)
4772 basi_red=(p[12] << 8) & p[13];
4773 else
4774 basi_red=0;
4775 if (length > 13)
4776 basi_green=(p[14] << 8) & p[15];
4777 else
4778 basi_green=0;
4779 if (length > 15)
4780 basi_blue=(p[16] << 8) & p[17];
4781 else
4782 basi_blue=0;
4783 if (length > 17)
4784 basi_alpha=(p[18] << 8) & p[19];
4785 else
4786 {
4787 if (basi_sample_depth == 16)
4788 basi_alpha=65535L;
4789 else
4790 basi_alpha=255;
4791 }
4792 if (length > 19)
4793 basi_viewable=p[20];
4794 else
4795 basi_viewable=0;
4796#endif
4797 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4798 continue;
4799 }
4800 if (memcmp(type,mng_IHDR,4)
4801#if defined(JNG_SUPPORTED)
4802 && memcmp(type,mng_JHDR,4)
4803#endif
4804 )
4805 {
4806 /* Not an IHDR or JHDR chunk */
4807 if (length)
4808 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4809 continue;
4810 }
4811/* Process IHDR */
4812 if (logging != MagickFalse)
4813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4814 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
4815 mng_info->exists[object_id]=MagickTrue;
4816 mng_info->viewable[object_id]=MagickTrue;
4817 if (mng_info->invisible[object_id])
4818 {
4819 if (logging != MagickFalse)
4820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4821 " Skipping invisible object");
4822 skip_to_iend=MagickTrue;
4823 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4824 continue;
4825 }
4826#if defined(MNG_INSERT_LAYERS)
4827 if (length < 8)
4828 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4829 image_width=(unsigned long) mng_get_long(p);
4830 image_height=(unsigned long) mng_get_long(&p[4]);
4831#endif
4832 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4833
4834 /*
4835 Insert a transparent background layer behind the entire animation
4836 if it is not full screen.
4837 */
4838#if defined(MNG_INSERT_LAYERS)
4839 if (insert_layers && mng_type && first_mng_object)
4840 {
4841 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
4842 (image_width < mng_info->mng_width) ||
4843 (mng_info->clip.right < (long) mng_info->mng_width) ||
4844 (image_height < mng_info->mng_height) ||
4845 (mng_info->clip.bottom < (long) mng_info->mng_height))
4846 {
4847 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4848 {
4849 /*
4850 Allocate next image structure.
4851 */
4852 AcquireNextImage(image_info,image);
4853 if (GetNextImageInList(image) == (Image *) NULL)
4854 {
4855 image=DestroyImageList(image);
4856 MngInfoFreeStruct(mng_info,&have_mng_structure);
4857 return((Image *) NULL);
4858 }
4859 image=SyncNextImageInList(image);
4860 }
4861 mng_info->image=image;
4862 if (term_chunk_found)
4863 {
4864 image->start_loop=MagickTrue;
4865 image->iterations=mng_iterations;
4866 term_chunk_found=MagickFalse;
4867 }
4868 else
4869 image->start_loop=MagickFalse;
4870 /*
4871 Make a background rectangle.
4872 */
4873 image->delay=0;
4874 image->columns=mng_info->mng_width;
4875 image->rows=mng_info->mng_height;
4876 image->page.width=mng_info->mng_width;
4877 image->page.height=mng_info->mng_height;
4878 image->page.x=0;
4879 image->page.y=0;
4880 image->background_color=mng_background_color;
4881 (void) SetImageBackgroundColor(image);
4882 if (logging != MagickFalse)
4883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4884 " Inserted transparent background layer, W=%lud, H=%lud",
4885 mng_info->mng_width,mng_info->mng_height);
4886 }
4887 }
4888 /*
4889 Insert a background layer behind the upcoming image if
4890 framing_mode is 3, and we haven't already inserted one.
4891 */
4892 if (insert_layers && (mng_info->framing_mode == 3) &&
4893 (subframe_width) && (subframe_height) && (simplicity == 0 ||
4894 (simplicity & 0x08)))
4895 {
4896 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4897 {
4898 /*
4899 Allocate next image structure.
4900 */
4901 AcquireNextImage(image_info,image);
4902 if (GetNextImageInList(image) == (Image *) NULL)
4903 {
4904 image=DestroyImageList(image);
4905 MngInfoFreeStruct(mng_info,&have_mng_structure);
4906 return((Image *) NULL);
4907 }
4908 image=SyncNextImageInList(image);
4909 }
4910 mng_info->image=image;
4911 if (term_chunk_found)
4912 {
4913 image->start_loop=MagickTrue;
4914 image->iterations=mng_iterations;
4915 term_chunk_found=MagickFalse;
4916 }
4917 else
4918 image->start_loop=MagickFalse;
4919 image->delay=0;
4920 image->columns=subframe_width;
4921 image->rows=subframe_height;
4922 image->page.width=subframe_width;
4923 image->page.height=subframe_height;
4924 image->page.x=mng_info->clip.left;
4925 image->page.y=mng_info->clip.top;
4926 image->background_color=mng_background_color;
4927 image->matte=MagickFalse;
4928 (void) SetImageBackgroundColor(image);
4929 if (logging != MagickFalse)
4930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4931 " Inserted background layer, L=%ld, R=%ld, T=%ld, B=%ld",
4932 mng_info->clip.left,mng_info->clip.right,
4933 mng_info->clip.top,mng_info->clip.bottom);
4934 }
4935#endif /* MNG_INSERT_LAYERS */
4936 first_mng_object=MagickFalse;
4937 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4938 {
4939 /*
4940 Allocate next image structure.
4941 */
4942 AcquireNextImage(image_info,image);
4943 if (GetNextImageInList(image) == (Image *) NULL)
4944 {
4945 image=DestroyImageList(image);
4946 MngInfoFreeStruct(mng_info,&have_mng_structure);
4947 return((Image *) NULL);
4948 }
4949 image=SyncNextImageInList(image);
4950 }
4951 mng_info->image=image;
4952 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4953 GetBlobSize(image));
4954 if (status == MagickFalse)
4955 break;
4956 if (term_chunk_found)
4957 {
4958 image->start_loop=MagickTrue;
4959 term_chunk_found=MagickFalse;
4960 }
4961 else
4962 image->start_loop=MagickFalse;
4963 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
4964 {
4965 image->delay=frame_delay;
4966 frame_delay=default_frame_delay;
4967 }
4968 else
4969 image->delay=0;
4970 image->page.width=mng_info->mng_width;
4971 image->page.height=mng_info->mng_height;
4972 image->page.x=mng_info->x_off[object_id];
4973 image->page.y=mng_info->y_off[object_id];
4974 image->iterations=mng_iterations;
4975 /*
4976 Seek back to the beginning of the IHDR or JHDR chunk's length field.
4977 */
4978 if (logging != MagickFalse)
4979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4980 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
4981 type[2],type[3]);
4982 offset=SeekBlob(image,-((long) length+12),SEEK_CUR);
4983 if (offset < 0)
4984 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4985 }
4986
4987 previous=image;
4988 mng_info->image=image;
4989 mng_info->mng_type=mng_type;
4990 mng_info->object_id=object_id;
4991
4992 if (memcmp(type,mng_IHDR,4) == 0)
4993 image=ReadOnePNGImage(mng_info,image_info,exception);
4994#if defined(JNG_SUPPORTED)
4995 else
4996 image=ReadOneJNGImage(mng_info,image_info,exception);
4997#endif
4998
4999 if (image == (Image *) NULL)
5000 {
5001 if (IsImageObject(previous) != MagickFalse)
5002 {
5003 (void) DestroyImageList(previous);
5004 (void) CloseBlob(previous);
5005 }
5006 MngInfoFreeStruct(mng_info,&have_mng_structure);
5007 return((Image *) NULL);
5008 }
5009 if (image->columns == 0 || image->rows == 0)
5010 {
5011 (void) CloseBlob(image);
5012 image=DestroyImageList(image);
5013 MngInfoFreeStruct(mng_info,&have_mng_structure);
5014 return((Image *) NULL);
5015 }
5016 mng_info->image=image;
5017
5018 if (mng_type)
5019 {
5020 MngBox
5021 crop_box;
5022
5023 if (mng_info->magn_methx || mng_info->magn_methy)
5024 {
5025 png_uint_32
5026 magnified_height,
5027 magnified_width;
5028
5029 if (logging != MagickFalse)
5030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5031 " Processing MNG MAGN chunk");
5032
5033 if (mng_info->magn_methx == 1)
5034 {
5035 magnified_width=mng_info->magn_ml;
5036 if (image->columns > 1)
5037 magnified_width += mng_info->magn_mr;
5038 if (image->columns > 2)
5039 magnified_width += (image->columns-2)*(mng_info->magn_mx);
5040 }
5041 else
5042 {
5043 magnified_width=image->columns;
5044 if (image->columns > 1)
5045 magnified_width += mng_info->magn_ml-1;
5046 if (image->columns > 2)
5047 magnified_width += mng_info->magn_mr-1;
5048 if (image->columns > 3)
5049 magnified_width += (image->columns-3)*(mng_info->magn_mx-1);
5050 }
5051 if (mng_info->magn_methy == 1)
5052 {
5053 magnified_height=mng_info->magn_mt;
5054 if (image->rows > 1)
5055 magnified_height += mng_info->magn_mb;
5056 if (image->rows > 2)
5057 magnified_height += (image->rows-2)*(mng_info->magn_my);
5058 }
5059 else
5060 {
5061 magnified_height=image->rows;
5062 if (image->rows > 1)
5063 magnified_height += mng_info->magn_mt-1;
5064 if (image->rows > 2)
5065 magnified_height += mng_info->magn_mb-1;
5066 if (image->rows > 3)
5067 magnified_height += (image->rows-3)*(mng_info->magn_my-1);
5068 }
5069 if (magnified_height > image->rows ||
5070 magnified_width > image->columns)
5071 {
5072 Image
5073 *large_image;
5074
5075 int
5076 yy;
5077
5078 long
5079 m,
5080 y;
5081
5082 register long
5083 x;
5084
5085 register PixelPacket
5086 *n,
5087 *q;
5088
5089 PixelPacket
5090 *next,
5091 *prev;
5092
5093 png_uint_16
5094 magn_methx,
5095 magn_methy;
5096
5097 /*
5098 Allocate next image structure.
5099 */
5100 if (logging != MagickFalse)
5101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5102 " Allocate magnified image");
5103 AcquireNextImage(image_info,image);
5104 if (GetNextImageInList(image) == (Image *) NULL)
5105 {
5106 image=DestroyImageList(image);
5107 MngInfoFreeStruct(mng_info,&have_mng_structure);
5108 return((Image *) NULL);
5109 }
5110
5111 large_image=SyncNextImageInList(image);
5112
5113 large_image->columns=magnified_width;
5114 large_image->rows=magnified_height;
5115
5116 magn_methx=mng_info->magn_methx;
5117 magn_methy=mng_info->magn_methy;
5118
5119#if (MAGICKCORE_QUANTUM_DEPTH == 32)
5120#define QM unsigned short
5121 if (magn_methx != 1 || magn_methy != 1)
5122 {
5123 /*
5124 Scale pixels to unsigned shorts to prevent
5125 overflow of intermediate values of interpolations
5126 */
5127 for (y=0; y < (long) image->rows; y++)
5128 {
5129 q=GetAuthenticPixels(image,0,y,image->columns,1,
5130 exception);
5131 for (x=(long) image->columns-1; x >= 0; x--)
5132 {
5133 q->red=ScaleQuantumToShort(q->red);
5134 q->green=ScaleQuantumToShort(q->green);
5135 q->blue=ScaleQuantumToShort(q->blue);
5136 q->opacity=ScaleQuantumToShort(q->opacity);
5137 q++;
5138 }
5139 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5140 break;
5141 }
5142 }
5143#else
5144#define QM Quantum
5145#endif
5146
5147 if (image->matte != MagickFalse)
5148 (void) SetImageBackgroundColor(large_image);
5149 else
5150 {
5151 large_image->background_color.opacity=OpaqueOpacity;
5152 (void) SetImageBackgroundColor(large_image);
5153 if (magn_methx == 4)
5154 magn_methx=2;
5155 if (magn_methx == 5)
5156 magn_methx=3;
5157 if (magn_methy == 4)
5158 magn_methy=2;
5159 if (magn_methy == 5)
5160 magn_methy=3;
5161 }
5162
5163 /* magnify the rows into the right side of the large image */
5164
5165 if (logging != MagickFalse)
5166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5167 " Magnify the rows to %lu",large_image->rows);
5168 m=(long) mng_info->magn_mt;
5169 yy=0;
5170 length=(size_t) image->columns;
5171 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5172 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5173 if ((prev == (PixelPacket *) NULL) ||
5174 (next == (PixelPacket *) NULL))
5175 {
5176 image=DestroyImageList(image);
5177 MngInfoFreeStruct(mng_info,&have_mng_structure);
5178 ThrowReaderException(ResourceLimitError,
5179 "MemoryAllocationFailed");
5180 }
5181 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5182 (void) CopyMagickMemory(next,n,length);
5183 for (y=0; y < (long) image->rows; y++)
5184 {
5185 if (y == 0)
5186 m=(long) mng_info->magn_mt;
5187 else if (magn_methy > 1 && y == (long) image->rows-2)
5188 m=(long) mng_info->magn_mb;
5189 else if (magn_methy <= 1 && y == (long) image->rows-1)
5190 m=(long) mng_info->magn_mb;
5191 else if (magn_methy > 1 && y == (long) image->rows-1)
5192 m=1;
5193 else
5194 m=(long) mng_info->magn_my;
5195 n=prev;
5196 prev=next;
5197 next=n;
5198 if (y < (long) image->rows-1)
5199 {
5200 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5201 exception);
5202 (void) CopyMagickMemory(next,n,length);
5203 }
5204 for (i=0; i < m; i++, yy++)
5205 {
5206 register PixelPacket
5207 *pixels;
5208
5209 assert(yy < (long) large_image->rows);
5210 pixels=prev;
5211 n=next;
5212 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5213 1,exception);
5214 q+=(large_image->columns-image->columns);
5215 for (x=(long) image->columns-1; x >= 0; x--)
5216 {
5217 /* TO DO: get color as function of indices[x] */
5218 /*
5219 if (image->storage_class == PseudoClass)
5220 {
5221 }
5222 */
5223
5224 if (magn_methy <= 1)
5225 {
5226 *q=(*pixels); /* replicate previous */
5227 }
5228 else if (magn_methy == 2 || magn_methy == 4)
5229 {
5230 if (i == 0)
5231 *q=(*pixels);
5232 else
5233 {
5234 /* Interpolate */
5235 (*q).red=(QM) (((long) (2*i*((*n).red
5236 -(*pixels).red)+m))/((long) (m*2))
5237 +(*pixels).red);
5238 (*q).green=(QM) (((long) (2*i*((*n).green
5239 -(*pixels).green)+m))/((long) (m*2))
5240 +(*pixels).green);
5241 (*q).blue=(QM) (((long) (2*i*((*n).blue
5242 -(*pixels).blue)+m))/((long) (m*2))
5243 +(*pixels).blue);
5244 if (image->matte != MagickFalse)
5245 (*q).opacity=(QM) (((long)
5246 (2*i*((*n).opacity
5247 -(*pixels).opacity)+m))
5248 /((long) (m*2))+(*pixels).opacity);
5249 }
5250 if (magn_methy == 4)
5251 {
5252 /* Replicate nearest */
5253 if (i <= ((m+1) << 1))
5254 (*q).opacity=(*pixels).opacity+0;
5255 else
5256 (*q).opacity=(*n).opacity+0;
5257 }
5258 }
5259 else /* if (magn_methy == 3 || magn_methy == 5) */
5260 {
5261 /* Replicate nearest */
5262 if (i <= ((m+1) << 1))
5263 *q=(*pixels);
5264 else
5265 *q=(*n);
5266 if (magn_methy == 5)
5267 {
5268 (*q).opacity=(QM) (((long) (2*i*((*n).opacity
5269 -(*pixels).opacity)+m))/((long) (m*2))
5270 +(*pixels).opacity);
5271 }
5272 }
5273 n++;
5274 q++;
5275 pixels++;
5276 } /* x */
5277 if (SyncAuthenticPixels(large_image,exception) == 0)
5278 break;
5279 } /* i */
5280 } /* y */
5281 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5282 next=(PixelPacket *) RelinquishMagickMemory(next);
5283
5284 length=image->columns;
5285
5286 if (logging != MagickFalse)
5287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5288 " Delete original image");
5289
5290 DeleteImageFromList(&image);
5291
5292 image=large_image;
5293
5294 mng_info->image=image;
5295
5296 /* magnify the columns */
5297 if (logging != MagickFalse)
5298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5299 " Magnify the columns to %lu",image->columns);
5300
5301 for (y=0; y < (long) image->rows; y++)
5302 {
5303 register PixelPacket
5304 *pixels;
5305
5306 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5307 pixels=q+(image->columns-length);
5308 n=pixels+1;
5309 for (x=(long) (image->columns-length);
5310 x < (long) image->columns; x++)
5311 {
5312 if (x == (long) (image->columns-length))
5313 m=(long) mng_info->magn_ml;
5314 else if (magn_methx > 1 && x == (long) image->columns-2)
5315 m=(long) mng_info->magn_mr;
5316 else if (magn_methx <= 1 && x == (long) image->columns-1)
5317 m=(long) mng_info->magn_mr;
5318 else if (magn_methx > 1 && x == (long) image->columns-1)
5319 m=1;
5320 else
5321 m=(long) mng_info->magn_mx;
5322 for (i=0; i < m; i++)
5323 {
5324 if (magn_methx <= 1)
5325 {
5326 /* replicate previous */
5327 *q=(*pixels);
5328 }
5329 else if (magn_methx == 2 || magn_methx == 4)
5330 {
5331 if (i == 0)
5332 *q=(*pixels);
5333 else
5334 {
5335 /* Interpolate */
5336 (*q).red=(QM) ((2*i*((*n).red
5337 -(*pixels).red)+m)
5338 /((long) (m*2))+(*pixels).red);
5339 (*q).green=(QM) ((2*i*((*n).green
5340 -(*pixels).green)
5341 +m)/((long) (m*2))+(*pixels).green);
5342 (*q).blue=(QM) ((2*i*((*n).blue
5343 -(*pixels).blue)+m)
5344 /((long) (m*2))+(*pixels).blue);
5345 if (image->matte != MagickFalse)
5346 (*q).opacity=(QM) ((2*i*((*n).opacity
5347 -(*pixels).opacity)+m)/((long) (m*2))
5348 +(*pixels).opacity);
5349 }
5350 if (magn_methx == 4)
5351 {
5352 /* Replicate nearest */
5353 if (i <= ((m+1) << 1))
5354 (*q).opacity=(*pixels).opacity+0;
5355 else
5356 (*q).opacity=(*n).opacity+0;
5357 }
5358 }
5359 else /* if (magn_methx == 3 || magn_methx == 5) */
5360 {
5361 /* Replicate nearest */
5362 if (i <= ((m+1) << 1))
5363 *q=(*pixels);
5364 else
5365 *q=(*n);
5366 if (magn_methx == 5)
5367 {
5368 /* Interpolate */
5369 (*q).opacity=(QM) ((2*i*((*n).opacity
5370 -(*pixels).opacity)+m) /((long) (m*2))
5371 +(*pixels).opacity);
5372 }
5373 }
5374 q++;
5375 }
5376 n++;
5377 p++;
5378 }
5379 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5380 break;
5381 }
5382#if (MAGICKCORE_QUANTUM_DEPTH == 32)
5383 if (magn_methx != 1 || magn_methy != 1)
5384 {
5385 /*
5386 Rescale pixels to Quantum
5387 */
5388 for (y=0; y < (long) image->rows; y++)
5389 {
5390 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5391 for (x=(long) image->columns-1; x >= 0; x--)
5392 {
5393 q->red=ScaleShortToQuantum(q->red);
5394 q->green=ScaleShortToQuantum(q->green);
5395 q->blue=ScaleShortToQuantum(q->blue);
5396 q->opacity=ScaleShortToQuantum(q->opacity);
5397 q++;
5398 }
5399 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5400 break;
5401 }
5402 }
5403#endif
5404 if (logging != MagickFalse)
5405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5406 " Finished MAGN processing");
5407 }
5408 }
5409
5410 /*
5411 Crop_box is with respect to the upper left corner of the MNG.
5412 */
5413 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5414 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5415 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5416 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5417 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5418 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5419 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5420 if ((crop_box.left != (mng_info->image_box.left
5421 +mng_info->x_off[object_id])) ||
5422 (crop_box.right != (mng_info->image_box.right
5423 +mng_info->x_off[object_id])) ||
5424 (crop_box.top != (mng_info->image_box.top
5425 +mng_info->y_off[object_id])) ||
5426 (crop_box.bottom != (mng_info->image_box.bottom
5427 +mng_info->y_off[object_id])))
5428 {
5429 if (logging != MagickFalse)
5430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5431 " Crop the PNG image");
5432 if ((crop_box.left < crop_box.right) &&
5433 (crop_box.top < crop_box.bottom))
5434 {
5435 Image
5436 *im;
5437
5438 RectangleInfo
5439 crop_info;
5440
5441 /*
5442 Crop_info is with respect to the upper left corner of
5443 the image.
5444 */
5445 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5446 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5447 crop_info.width=(unsigned long) (crop_box.right-crop_box.left);
5448 crop_info.height=(unsigned long) (crop_box.bottom-crop_box.top);
5449 image->page.width=image->columns;
5450 image->page.height=image->rows;
5451 image->page.x=0;
5452 image->page.y=0;
5453 im=CropImage(image,&crop_info,exception);
5454 if (im != (Image *) NULL)
5455 {
5456 image->columns=im->columns;
5457 image->rows=im->rows;
5458 im=DestroyImage(im);
5459 image->page.width=image->columns;
5460 image->page.height=image->rows;
5461 image->page.x=crop_box.left;
5462 image->page.y=crop_box.top;
5463 }
5464 }
5465 else
5466 {
5467 /*
5468 No pixels in crop area. The MNG spec still requires
5469 a layer, though, so make a single transparent pixel in
5470 the top left corner.
5471 */
5472 image->columns=1;
5473 image->rows=1;
5474 image->colors=2;
5475 (void) SetImageBackgroundColor(image);
5476 image->page.width=1;
5477 image->page.height=1;
5478 image->page.x=0;
5479 image->page.y=0;
5480 }
5481 }
5482#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
5483 image=mng_info->image;
5484#endif
5485 }
5486
5487#if (MAGICKCORE_QUANTUM_DEPTH == 16) /* TO DO: treat Q:32 */
5488 /* Determine if bit depth can be reduced from 16 to 8.
5489 * Note that the method GetImageDepth doesn't check background
5490 * and doesn't handle PseudoClass specially. Also it uses
5491 * multiplication and division by 257 instead of shifting, so
5492 * might be slower.
5493 */
5494 if (mng_info->optimize && image->depth == 16)
5495 {
5496 int
5497 ok_to_reduce;
5498
5499 const PixelPacket
5500 *p;
5501
5502 ok_to_reduce=(((((unsigned long) image->background_color.red >> 8) &
5503 0xff)
5504 == ((unsigned long) image->background_color.red & 0xff)) &&
5505 ((((unsigned long) image->background_color.green >> 8) & 0xff)
5506 == ((unsigned long) image->background_color.green & 0xff)) &&
5507 ((((unsigned long) image->background_color.blue >> 8) & 0xff)
5508 == ((unsigned long) image->background_color.blue & 0xff)));
5509 if (ok_to_reduce && image->storage_class == PseudoClass)
5510 {
5511 int indx;
5512
5513 for (indx=0; indx < (long) image->colors; indx++)
5514 {
5515 ok_to_reduce=(((((unsigned long) image->colormap[indx].red >>
5516 8) & 0xff)
5517 == ((unsigned long) image->colormap[indx].red & 0xff)) &&
5518 ((((unsigned long) image->colormap[indx].green >> 8) & 0xff)
5519 == ((unsigned long) image->colormap[indx].green & 0xff)) &&
5520 ((((unsigned long) image->colormap[indx].blue >> 8) & 0xff)
5521 == ((unsigned long) image->colormap[indx].blue & 0xff)));
5522 if (ok_to_reduce == MagickFalse)
5523 break;
5524 }
5525 }
5526 if ((ok_to_reduce != MagickFalse) &&
5527 (image->storage_class != PseudoClass))
5528 {
5529 long
5530 y;
5531
5532 register long
5533 x;
5534
5535 for (y=0; y < (long) image->rows; y++)
5536 {
5537 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
5538 if (p == (const PixelPacket *) NULL)
5539 break;
5540 for (x=(long) image->columns-1; x >= 0; x--)
5541 {
5542 ok_to_reduce=((
5543 (((unsigned long) p->red >> 8) & 0xff) ==
5544 ((unsigned long) p->red & 0xff)) &&
5545 ((((unsigned long) p->green >> 8) & 0xff) ==
5546 ((unsigned long) p->green & 0xff)) &&
5547 ((((unsigned long) p->blue >> 8) & 0xff) ==
5548 ((unsigned long) p->blue & 0xff)) &&
5549 (((!image->matte ||
5550 (((unsigned long) p->opacity >> 8) & 0xff) ==
5551 ((unsigned long) p->opacity & 0xff)))));
5552 if (ok_to_reduce == 0)
5553 break;
5554 p++;
5555 }
5556 if (x != 0)
5557 break;
5558 }
5559 }
5560 if (ok_to_reduce)
5561 {
5562 image->depth=8;
5563 if (logging != MagickFalse)
5564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5565 " Reducing PNG bit depth to 8 without loss of info");
5566 }
5567 }
5568#endif
5569 GetImageException(image,exception);
5570 if (image_info->number_scenes != 0)
5571 {
5572 if (mng_info->scenes_found >
5573 (long) (image_info->first_scene+image_info->number_scenes))
5574 break;
5575 }
5576 if (logging != MagickFalse)
5577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5578 " Finished reading image datastream.");
5579 } while (LocaleCompare(image_info->magick,"MNG") == 0);
5580 (void) CloseBlob(image);
5581 if (logging != MagickFalse)
5582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5583 " Finished reading all image datastreams.");
5584#if defined(MNG_INSERT_LAYERS)
5585 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
5586 (mng_info->mng_height))
5587 {
5588 /*
5589 Insert a background layer if nothing else was found.
5590 */
5591 if (logging != MagickFalse)
5592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5593 " No images found. Inserting a background layer.");
5594 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5595 {
5596 /*
5597 Allocate next image structure.
5598 */
5599 AcquireNextImage(image_info,image);
5600 if (GetNextImageInList(image) == (Image *) NULL)
5601 {
5602 image=DestroyImageList(image);
5603 MngInfoFreeStruct(mng_info,&have_mng_structure);
5604 if (logging != MagickFalse)
5605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5606 " Allocation failed, returning NULL.");
5607 return((Image *) NULL);
5608 }
5609 image=SyncNextImageInList(image);
5610 }
5611 image->columns=mng_info->mng_width;
5612 image->rows=mng_info->mng_height;
5613 image->page.width=mng_info->mng_width;
5614 image->page.height=mng_info->mng_height;
5615 image->page.x=0;
5616 image->page.y=0;
5617 image->background_color=mng_background_color;
5618 image->matte=MagickFalse;
5619 if (image_info->ping == MagickFalse)
5620 (void) SetImageBackgroundColor(image);
5621 mng_info->image_found++;
5622 }
5623#endif
5624 image->iterations=mng_iterations;
5625 if (mng_iterations == 1)
5626 image->start_loop=MagickTrue;
5627 while (GetPreviousImageInList(image) != (Image *) NULL)
5628 {
5629 image_count++;
5630 if (image_count > 10*mng_info->image_found)
5631 {
5632 if (logging != MagickFalse)
5633 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
5634 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5635 CoderError,"Linked list is corrupted, beginning of list not found",
5636 "`%s'",image_info->filename);
5637 return((Image *) NULL);
5638 }
5639 image=GetPreviousImageInList(image);
5640 if (GetNextImageInList(image) == (Image *) NULL)
5641 {
5642 if (logging != MagickFalse)
5643 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
5644 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5645 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
5646 image_info->filename);
5647 }
5648 }
5649 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
5650 GetNextImageInList(image) ==
5651 (Image *) NULL)
5652 {
5653 if (logging != MagickFalse)
5654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5655 " First image null");
5656 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5657 CoderError,"image->next for first image is NULL but shouldn't be.",
5658 "`%s'",image_info->filename);
5659 }
5660 if (mng_info->image_found == 0)
5661 {
5662 if (logging != MagickFalse)
5663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5664 " No visible images found.");
5665 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5666 CoderError,"No visible images in file","`%s'",image_info->filename);
5667 if (image != (Image *) NULL)
5668 image=DestroyImageList(image);
5669 MngInfoFreeStruct(mng_info,&have_mng_structure);
5670 return((Image *) NULL);
5671 }
5672
5673 if (mng_info->ticks_per_second)
5674 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
5675 final_delay/mng_info->ticks_per_second;
5676 else
5677 image->start_loop=MagickTrue;
5678 /* Find final nonzero image delay */
5679 final_image_delay=0;
5680 while (GetNextImageInList(image) != (Image *) NULL)
5681 {
5682 if (image->delay)
5683 final_image_delay=image->delay;
5684 image=GetNextImageInList(image);
5685 }
5686 if (final_delay < final_image_delay)
5687 final_delay=final_image_delay;
5688 image->delay=final_delay;
5689 if (logging != MagickFalse)
5690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5691 " image->delay=%lu, final_delay=%lu",image->delay,final_delay);
5692 if (logging != MagickFalse)
5693 {
5694 int
5695 scene;
5696
5697 scene=0;
5698 image=GetFirstImageInList(image);
5699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5700 " Before coalesce:");
5701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5702 " scene 0 delay=%lu",image->delay);
5703 while (GetNextImageInList(image) != (Image *) NULL)
5704 {
5705 image=GetNextImageInList(image);
5706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5707 " scene %d delay=%lu",++scene,image->delay);
5708 }
5709 }
5710
5711 image=GetFirstImageInList(image);
5712#ifdef MNG_COALESCE_LAYERS
5713 if (insert_layers)
5714 {
5715 Image
5716 *next_image,
5717 *next;
5718
5719 unsigned long
5720 scene;
5721
5722 if (logging != MagickFalse)
5723 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
5724 scene=image->scene;
5725 next_image=CoalesceImages(image,&image->exception);
5726 if (next_image == (Image *) NULL)
5727 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5728 image=DestroyImageList(image);
5729 image=next_image;
5730 for (next=image; next != (Image *) NULL; next=next_image)
5731 {
5732 next->page.width=mng_info->mng_width;
5733 next->page.height=mng_info->mng_height;
5734 next->page.x=0;
5735 next->page.y=0;
5736 next->scene=scene++;
5737 next_image=GetNextImageInList(next);
5738 if (next_image == (Image *) NULL)
5739 break;
5740 if (next->delay == 0)
5741 {
5742 scene--;
5743 next_image->previous=GetPreviousImageInList(next);
5744 if (GetPreviousImageInList(next) == (Image *) NULL)
5745 image=next_image;
5746 else
5747 next->previous->next=next_image;
5748 next=DestroyImage(next);
5749 }
5750 }
5751 }
5752#endif
5753
5754 while (GetNextImageInList(image) != (Image *) NULL)
5755 image=GetNextImageInList(image);
5756 image->dispose=BackgroundDispose;
5757
5758 if (logging != MagickFalse)
5759 {
5760 int
5761 scene;
5762
5763 scene=0;
5764 image=GetFirstImageInList(image);
5765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5766 " After coalesce:");
5767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5768 " scene 0 delay=%lu dispose=%d",image->delay,(int) image->dispose);
5769 while (GetNextImageInList(image) != (Image *) NULL)
5770 {
5771 image=GetNextImageInList(image);
5772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5773 " scene %d delay=%lu dispose=%d",++scene,
5774 image->delay,(int) image->dispose);
5775 }
5776 }
5777 image=GetFirstImageInList(image);
5778 MngInfoFreeStruct(mng_info,&have_mng_structure);
5779 have_mng_structure=MagickFalse;
5780 if (logging != MagickFalse)
5781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
5782 return(GetFirstImageInList(image));
5783}
5784#else /* PNG_LIBPNG_VER > 95 */
5785static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5786{
5787 printf("Your PNG library is too old: You have libpng-%s\n",
5788 PNG_LIBPNG_VER_STRING);
5789 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
5790 "PNG library is too old","`%s'",image_info->filename);
5791 return(Image *) NULL;
5792}
5793static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5794{
5795 return(ReadPNGImage(image_info,exception));
5796}
5797#endif /* PNG_LIBPNG_VER > 95 */
5798#endif
5799
5800/*
5801%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5802% %
5803% %
5804% %
5805% R e g i s t e r P N G I m a g e %
5806% %
5807% %
5808% %
5809%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5810%
5811% RegisterPNGImage() adds properties for the PNG image format to
5812% the list of supported formats. The properties include the image format
5813% tag, a method to read and/or write the format, whether the format
5814% supports the saving of more than one frame to the same file or blob,
5815% whether the format supports native in-memory I/O, and a brief
5816% description of the format.
5817%
5818% The format of the RegisterPNGImage method is:
5819%
5820% unsigned long RegisterPNGImage(void)
5821%
5822*/
5823ModuleExport unsigned long RegisterPNGImage(void)
5824{
5825 char
5826 version[MaxTextExtent];
5827
5828 MagickInfo
5829 *entry;
5830
5831 static const char
5832 *PNGNote=
5833 {
5834 "See http://www.libpng.org/ for details about the PNG format."
5835 },
5836 *JNGNote=
5837 {
5838 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
5839 "format."
5840 },
5841 *MNGNote=
5842 {
5843 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
5844 "format."
5845 };
5846
5847 *version='\0';
5848#if defined(PNG_LIBPNG_VER_STRING)
5849 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
5850 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
5851#if (PNG_LIBPNG_VER > 10005)
5852 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
5853 {
5854 (void) ConcatenateMagickString(version,",",MaxTextExtent);
5855 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
5856 MaxTextExtent);
5857 }
5858#endif
5859#endif
5860 entry=SetMagickInfo("MNG");
5861 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
5862#if defined(MAGICKCORE_PNG_DELEGATE)
5863 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
5864 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
5865#endif
5866 entry->magick=(IsImageFormatHandler *) IsMNG;
5867 entry->description=ConstantString("Multiple-image Network Graphics");
5868 if (*version != '\0')
5869 entry->version=ConstantString(version);
5870 entry->module=ConstantString("PNG");
5871 entry->note=ConstantString(MNGNote);
5872 (void) RegisterMagickInfo(entry);
5873
5874 entry=SetMagickInfo("PNG");
5875#if defined(MAGICKCORE_PNG_DELEGATE)
5876 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
5877 entry->encoder=(EncodeImageHandler *) WritePNGImage;
5878#endif
5879 entry->magick=(IsImageFormatHandler *) IsPNG;
5880 entry->adjoin=MagickFalse;
5881 entry->description=ConstantString("Portable Network Graphics");
5882 entry->module=ConstantString("PNG");
5883 if (*version != '\0')
5884 entry->version=ConstantString(version);
5885 entry->note=ConstantString(PNGNote);
5886 (void) RegisterMagickInfo(entry);
5887
5888 entry=SetMagickInfo("PNG8");
5889#if defined(MAGICKCORE_PNG_DELEGATE)
5890 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
5891 entry->encoder=(EncodeImageHandler *) WritePNGImage;
5892#endif
5893 entry->magick=(IsImageFormatHandler *) IsPNG;
5894 entry->adjoin=MagickFalse;
5895 entry->description=ConstantString(
5896 "8-bit indexed with optional binary transparency");
5897 entry->module=ConstantString("PNG");
5898 (void) RegisterMagickInfo(entry);
5899
5900 entry=SetMagickInfo("PNG24");
5901 *version='\0';
5902#if defined(ZLIB_VERSION)
5903 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
5904 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
5905 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
5906 {
5907 (void) ConcatenateMagickString(version,",",MaxTextExtent);
5908 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
5909 }
5910#endif
5911 if (*version != '\0')
5912 entry->version=ConstantString(version);
5913#if defined(MAGICKCORE_PNG_DELEGATE)
5914 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
5915 entry->encoder=(EncodeImageHandler *) WritePNGImage;
5916#endif
5917 entry->magick=(IsImageFormatHandler *) IsPNG;
5918 entry->adjoin=MagickFalse;
5919 entry->description=ConstantString("opaque 24-bit RGB");
5920 entry->module=ConstantString("PNG");
5921 (void) RegisterMagickInfo(entry);
5922
5923 entry=SetMagickInfo("PNG32");
5924#if defined(MAGICKCORE_PNG_DELEGATE)
5925 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
5926 entry->encoder=(EncodeImageHandler *) WritePNGImage;
5927#endif
5928 entry->magick=(IsImageFormatHandler *) IsPNG;
5929 entry->adjoin=MagickFalse;
5930 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
5931 entry->module=ConstantString("PNG");
5932 (void) RegisterMagickInfo(entry);
5933
5934 entry=SetMagickInfo("JNG");
5935#if defined(JNG_SUPPORTED)
5936#if defined(MAGICKCORE_PNG_DELEGATE)
5937 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
5938 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
5939#endif
5940#endif
5941 entry->magick=(IsImageFormatHandler *) IsJNG;
5942 entry->adjoin=MagickFalse;
5943 entry->description=ConstantString("JPEG Network Graphics");
5944 entry->module=ConstantString("PNG");
5945 entry->note=ConstantString(JNGNote);
5946 (void) RegisterMagickInfo(entry);
5947 return(MagickImageCoderSignature);
5948}
5949
5950/*
5951%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5952% %
5953% %
5954% %
5955% U n r e g i s t e r P N G I m a g e %
5956% %
5957% %
5958% %
5959%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5960%
5961% UnregisterPNGImage() removes format registrations made by the
5962% PNG module from the list of supported formats.
5963%
5964% The format of the UnregisterPNGImage method is:
5965%
5966% UnregisterPNGImage(void)
5967%
5968*/
5969ModuleExport void UnregisterPNGImage(void)
5970{
5971 (void) UnregisterMagickInfo("MNG");
5972 (void) UnregisterMagickInfo("PNG");
5973 (void) UnregisterMagickInfo("PNG8");
5974 (void) UnregisterMagickInfo("PNG24");
5975 (void) UnregisterMagickInfo("PNG32");
5976 (void) UnregisterMagickInfo("JNG");
5977#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
5978 AcquireSemaphoreInfo(&png_semaphore);
5979 RelinquishSemaphoreInfo(png_semaphore);
5980 DestroySemaphoreInfo(&png_semaphore);
5981#endif
5982}
5983
5984#if defined(MAGICKCORE_PNG_DELEGATE)
5985#if PNG_LIBPNG_VER > 95
5986/*
5987%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5988% %
5989% %
5990% %
5991% W r i t e M N G I m a g e %
5992% %
5993% %
5994% %
5995%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5996%
5997% WriteMNGImage() writes an image in the Portable Network Graphics
5998% Group's "Multiple-image Network Graphics" encoded image format.
5999%
6000% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6001%
6002% The format of the WriteMNGImage method is:
6003%
6004% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6005%
6006% A description of each parameter follows.
6007%
6008% o image_info: the image info.
6009%
6010% o image: The image.
6011%
6012%
6013% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6014% "To do" under ReadPNGImage):
6015%
6016% Fix problem with palette sorting (when PNG_SORT_PALETTE is enabled,
6017% some GIF animations don't convert properly)
6018%
6019% Preserve all unknown and not-yet-handled known chunks found in input
6020% PNG file and copy them into output PNG files according to the PNG
6021% copying rules.
6022%
6023% Write the iCCP chunk at MNG level when (icc profile length > 0)
6024%
6025% Improve selection of color type (use indexed-colour or indexed-colour
6026% with tRNS when 256 or fewer unique RGBA values are present).
6027%
6028% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6029% This will be complicated if we limit ourselves to generating MNG-LC
6030% files. For now we ignore disposal method 3 and simply overlay the next
6031% image on it.
6032%
6033% Check for identical PLTE's or PLTE/tRNS combinations and use a
6034% global MNG PLTE or PLTE/tRNS combination when appropriate.
6035% [mostly done 15 June 1999 but still need to take care of tRNS]
6036%
6037% Check for identical sRGB and replace with a global sRGB (and remove
6038% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6039% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6040% local gAMA/cHRM with local sRGB if appropriate).
6041%
6042% Check for identical sBIT chunks and write global ones.
6043%
6044% Provide option to skip writing the signature tEXt chunks.
6045%
6046% Use signatures to detect identical objects and reuse the first
6047% instance of such objects instead of writing duplicate objects.
6048%
6049% Use a smaller-than-32k value of compression window size when
6050% appropriate.
6051%
6052% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6053% ancillary text chunks and save profiles.
6054%
6055% Provide an option to force LC files (to ensure exact framing rate)
6056% instead of VLC.
6057%
6058% Provide an option to force VLC files instead of LC, even when offsets
6059% are present. This will involve expanding the embedded images with a
6060% transparent region at the top and/or left.
6061*/
6062
6063#if (PNG_LIBPNG_VER > 99 && PNG_LIBPNG_VER < 10007)
6064/* This function became available in libpng version 1.0.6g. */
6065static void
6066png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size)
6067{
6068 if (png_ptr->zbuf)
6069 png_free(png_ptr, png_ptr->zbuf); png_ptr->zbuf=NULL;
6070 png_ptr->zbuf_size=(png_size_t) size;
6071 png_ptr->zbuf=(png_bytep) png_malloc(png_ptr, size);
6072 if (png_ptr->zbuf == 0)
6073 png_error(png_ptr,"Unable to allocate zbuf");
6074}
6075#endif
6076
6077static void
6078png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6079 png_info *ping_info, unsigned char *profile_type, unsigned char
6080 *profile_description, unsigned char *profile_data, png_uint_32 length)
6081{
6082#if (PNG_LIBPNG_VER > 10005)
6083 png_textp
6084 text;
6085
6086 register long
6087 i;
6088
6089 unsigned char
6090 *sp;
6091
6092 png_charp
6093 dp;
6094
6095 png_uint_32
6096 allocated_length,
6097 description_length;
6098
6099 unsigned char
6100 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6101#endif
6102
6103#if (PNG_LIBPNG_VER <= 10005)
6104 if (image_info->verbose)
6105 (void) printf("Not ");
6106 image_info=image_info;
6107 ping=ping;
6108 ping_info=ping_info;
6109 profile_type=profile_type;
6110 profile_description=profile_description;
6111 profile_data=profile_data;
6112 length=length;
6113#endif
6114 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6115 return;
6116
6117 if (image_info->verbose)
6118 {
6119 (void) printf("writing raw profile: type=%s, length=%lu\n",
6120 (char *) profile_type, length);
6121 }
6122#if (PNG_LIBPNG_VER > 10005)
6123 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6124 description_length=(png_uint_32) strlen((const char *) profile_description);
6125 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6126 + description_length);
6127 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6128 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6129 text[0].key[0]='\0';
6130 (void) ConcatenateMagickString(text[0].key,
6131 "Raw profile type ",MaxTextExtent);
6132 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6133 sp=profile_data;
6134 dp=text[0].text;
6135 *dp++='\n';
6136 (void) CopyMagickString(dp,(const char *) profile_description,
6137 allocated_length);
6138 dp+=description_length;
6139 *dp++='\n';
6140 (void) FormatMagickString(dp,allocated_length-
6141 (png_size_t) (dp-text[0].text),"%8lu ",length);
6142 dp+=8;
6143 for (i=0; i < (long) length; i++)
6144 {
6145 if (i%36 == 0)
6146 *dp++='\n';
6147 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6148 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6149 }
6150 *dp++='\n';
6151 *dp='\0';
6152 text[0].text_length=(png_size_t) (dp-text[0].text);
6153 text[0].compression=image_info->compression == NoCompression ||
6154 (image_info->compression == UndefinedCompression &&
6155 text[0].text_length < 128) ? -1 : 0;
6156 if (text[0].text_length <= allocated_length)
6157 png_set_text(ping,ping_info,text,1);
6158 png_free(ping,text[0].text);
6159 png_free(ping,text[0].key);
6160 png_free(ping,text);
6161#endif
6162}
6163
6164static MagickBooleanType png_write_chunk_from_profile(Image *image,
6165 const char *string, int logging)
6166{
6167 char
6168 *name;
6169
6170 const StringInfo
6171 *profile;
6172
6173 unsigned char
6174 *data;
6175
6176 png_uint_32 length;
6177
6178 ResetImageProfileIterator(image);
6179 for (name=GetNextImageProfile(image); name != (const char *) NULL; ){
6180 profile=GetImageProfile(image,name);
6181 if (profile != (const StringInfo *) NULL)
6182 {
6183 StringInfo
6184 *png_profile;
6185
6186 if (LocaleNCompare(name,string,11) == 0) {
6187 if (logging != MagickFalse)
6188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6189 " Found %s profile",name);
6190
6191 png_profile=CloneStringInfo(profile);
6192 data=GetStringInfoDatum(png_profile),
6193 length=(png_uint_32) GetStringInfoLength(png_profile);
6194 data[4]=data[3];
6195 data[3]=data[2];
6196 data[2]=data[1];
6197 data[1]=data[0];
6198 (void) WriteBlobMSBULong(image,length-5); /* data length */
6199 (void) WriteBlob(image,length-1,data+1);
6200 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6201 png_profile=DestroyStringInfo(png_profile);
6202 }
6203 }
6204 name=GetNextImageProfile(image);
6205 }
6206 return(MagickTrue);
6207}
6208
6209static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6210 const ImageInfo *image_info,Image *image)
6211{
6212/* Write one PNG image */
6213 char
6214 s[2];
6215
6216 const char
6217 *name,
6218 *property,
6219 *value;
6220
6221 const StringInfo
6222 *profile;
6223
6224
6225 int
6226 image_matte,
6227 num_passes,
6228 pass;
6229
6230 png_colorp
6231 palette;
6232
6233 png_info
6234 *ping_info;
6235
6236 png_struct
6237 *ping;
6238
6239 long
6240 y;
6241
6242 MagickBooleanType
6243 status;
6244
6245 QuantumInfo
6246 *quantum_info;
6247
6248 register IndexPacket
6249 *indices;
6250
6251 register long
6252 i,
6253 x;
6254
6255 unsigned char
6256 *png_pixels;
6257
6258 unsigned int
6259 logging,
6260 matte;
6261
6262 volatile unsigned long
6263 image_colors,
6264 image_depth;
6265
6266 unsigned long
6267 old_bit_depth,
6268 quality,
6269 rowbytes,
6270 save_image_depth;
6271
6272 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6273 " enter WriteOnePNGImage()");
6274
6275#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6276 AcquireSemaphoreInfo(&png_semaphore);
6277#endif
6278
cristyed552522009-10-16 14:04:35 +00006279 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00006280 image_colors=image->colors;
6281 image_depth=image->depth;
6282 image_matte=image->matte;
6283
6284 if (image->colorspace != RGBColorspace)
6285 (void) TransformImageColorspace(image,RGBColorspace);
6286 mng_info->IsPalette=image->storage_class == PseudoClass &&
6287 image_colors <= 256;
6288 mng_info->optimize=image_info->type == OptimizeType;
6289
6290 /*
6291 Allocate the PNG structures
6292 */
6293#ifdef PNG_USER_MEM_SUPPORTED
6294 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
6295 PNGErrorHandler,PNGWarningHandler,(void *) NULL,
6296 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
6297#else
6298 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
6299 PNGErrorHandler,PNGWarningHandler);
6300#endif
6301 if (ping == (png_struct *) NULL)
6302 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6303 ping_info=png_create_info_struct(ping);
6304 if (ping_info == (png_info *) NULL)
6305 {
6306 png_destroy_write_struct(&ping,(png_info **) NULL);
6307 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6308 }
6309 png_set_write_fn(ping,image,png_put_data,png_flush_data);
6310 png_pixels=(unsigned char *) NULL;
6311
6312 if (setjmp(ping->jmpbuf))
6313 {
6314 /*
6315 PNG write failed.
6316 */
6317#ifdef PNG_DEBUG
6318 if (image_info->verbose)
6319 (void) printf("PNG write has failed.\n");
6320#endif
6321 png_destroy_write_struct(&ping,&ping_info);
6322#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6323 RelinquishSemaphoreInfo(png_semaphore);
6324#endif
6325 return(MagickFalse);
6326 }
6327 /*
6328 Prepare PNG for writing.
6329 */
6330#if defined(PNG_MNG_FEATURES_SUPPORTED)
6331 if (mng_info->write_mng)
6332 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
6333#else
6334# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
6335 if (mng_info->write_mng)
6336 png_permit_empty_plte(ping,MagickTrue);
6337# endif
6338#endif
6339 x=0;
6340 ping_info->width=image->columns;
6341 ping_info->height=image->rows;
6342 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
6343 image_depth=8;
6344 if (mng_info->write_png_depth != 0)
6345 image_depth=mng_info->write_png_depth;
6346 /* Adjust requested depth to next higher valid depth if necessary */
6347 if (image_depth > 8)
6348 image_depth=16;
6349 if ((image_depth > 4) && (image_depth < 8))
6350 image_depth=8;
6351 if (image_depth == 3)
6352 image_depth=4;
6353 if (logging != MagickFalse)
6354 {
6355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6356 " width=%lu",ping_info->width);
6357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6358 " height=%lu",ping_info->height);
6359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6360 " image_matte=%u",image->matte);
6361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6362 " image_depth=%lu",image->depth);
6363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6364 " requested PNG image_depth=%lu",image->depth);
6365 }
6366 save_image_depth=image_depth;
6367 ping_info->bit_depth=(png_byte) save_image_depth;
6368#if defined(PNG_pHYs_SUPPORTED)
6369 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
6370 (!mng_info->write_mng || !mng_info->equal_physs))
6371 {
6372 int
6373 unit_type;
6374
6375 png_uint_32
6376 x_resolution,
6377 y_resolution;
6378
6379 if (image->units == PixelsPerInchResolution)
6380 {
6381 unit_type=PNG_RESOLUTION_METER;
6382 x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
6383 y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
6384 }
6385 else if (image->units == PixelsPerCentimeterResolution)
6386 {
6387 unit_type=PNG_RESOLUTION_METER;
6388 x_resolution=(png_uint_32) (100.0*image->x_resolution);
6389 y_resolution=(png_uint_32) (100.0*image->y_resolution);
6390 }
6391 else
6392 {
6393 unit_type=PNG_RESOLUTION_UNKNOWN;
6394 x_resolution=(png_uint_32) image->x_resolution;
6395 y_resolution=(png_uint_32) image->y_resolution;
6396 }
6397 png_set_pHYs(ping,ping_info,x_resolution,y_resolution,unit_type);
6398 if (logging != MagickFalse)
6399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6400 " Setting up pHYs chunk");
6401 }
6402#endif
6403#if defined(PNG_oFFs_SUPPORTED)
6404 if (image->page.x || image->page.y)
6405 {
6406 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
6407 (png_int_32) image->page.y, 0);
6408 if (logging != MagickFalse)
6409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6410 " Setting up oFFs chunk");
6411 }
6412#endif
6413 if (image_matte && (!mng_info->adjoin || !mng_info->equal_backgrounds))
6414 {
6415 png_color_16
6416 background;
6417
6418 if (image_depth < MAGICKCORE_QUANTUM_DEPTH)
6419 {
6420 unsigned long
6421 maxval;
6422
6423 maxval=(1UL << image_depth)-1;
6424 background.red=(png_uint_16)
6425 (QuantumScale*(maxval*image->background_color.red));
6426 background.green=(png_uint_16)
6427 (QuantumScale*(maxval*image->background_color.green));
6428 background.blue=(png_uint_16)
6429 (QuantumScale*(maxval*image->background_color.blue));
6430 background.gray=(png_uint_16)
6431 (QuantumScale*(maxval*PixelIntensity(&image->background_color)));
6432 }
6433 else
6434 {
6435 background.red=image->background_color.red;
6436 background.green=image->background_color.green;
6437 background.blue=image->background_color.blue;
6438 background.gray=
6439 (png_uint_16) PixelIntensity(&image->background_color);
6440 }
6441 background.index=(png_byte) background.gray;
6442 if (logging != MagickFalse)
6443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6444 " Setting up bKGd chunk");
6445 png_set_bKGD(ping,ping_info,&background);
6446 }
6447 /*
6448 Select the color type.
6449 */
6450 matte=image_matte;
6451 old_bit_depth=0;
6452 if (mng_info->write_png8)
6453 {
6454 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6455 ping_info->bit_depth=8;
6456 image_depth=ping_info->bit_depth;
6457 {
6458 /* TO DO: make this a function cause it's used twice, except
6459 for reducing the sample depth from 8. */
6460
6461 QuantizeInfo
6462 quantize_info;
6463
6464 unsigned long
6465 number_colors,
6466 save_number_colors;
6467
6468 number_colors=image_colors;
6469 if ((image->storage_class == DirectClass) || (number_colors > 256))
6470 {
6471 GetQuantizeInfo(&quantize_info);
6472 quantize_info.dither=IsPaletteImage(image,&image->exception) ==
6473 MagickFalse ? MagickTrue : MagickFalse;
6474 quantize_info.number_colors= (matte != MagickFalse ? 255UL :
6475 256UL);
6476 (void) QuantizeImage(&quantize_info,image);
6477 number_colors=image_colors;
6478 (void) SyncImage(image);
6479 if (logging != MagickFalse)
6480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6481 " Colors quantized to %ld",number_colors);
6482 }
6483 if (matte)
6484 ping_info->valid|=PNG_INFO_tRNS;
6485 /*
6486 Set image palette.
6487 */
6488 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6489 ping_info->valid|=PNG_INFO_PLTE;
6490#if defined(PNG_SORT_PALETTE)
6491 save_number_colors=image_colors;
6492 if (CompressColormapTransFirst(image) == MagickFalse)
6493 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6494 number_colors=image_colors;
6495 image_colors=save_number_colors;
6496#endif
6497 palette=(png_color *) AcquireQuantumMemory(257,
6498 sizeof(*palette));
6499 if (palette == (png_color *) NULL)
6500 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6501 if (logging != MagickFalse)
6502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6503 " Setting up PLTE chunk");
6504 for (i=0; i < (long) number_colors; i++)
6505 {
6506 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6507 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6508 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6509 if (logging != MagickFalse)
6510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6511#if MAGICKCORE_QUANTUM_DEPTH == 8
6512 " %3ld (%3d,%3d,%3d)",
6513#else
6514 " %5ld (%5d,%5d,%5d)",
6515#endif
6516 i,palette[i].red,palette[i].green,palette[i].blue);
6517
6518 }
6519 if (matte)
6520 {
6521 number_colors++;
6522 palette[i].red=ScaleQuantumToChar((Quantum) QuantumRange);
6523 palette[i].green=ScaleQuantumToChar((Quantum) QuantumRange);
6524 palette[i].blue=ScaleQuantumToChar((Quantum) QuantumRange);
6525 }
6526 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6527#if (PNG_LIBPNG_VER > 10008)
6528 palette=(png_colorp) RelinquishMagickMemory(palette);
6529#endif
6530 image_depth=ping_info->bit_depth;
6531 ping_info->num_trans=0;
6532 if (matte)
6533 {
6534 ExceptionInfo
6535 *exception;
6536
6537 /*
6538 Identify which colormap entry is transparent.
6539 */
6540 ping_info->trans_alpha=(unsigned char *) AcquireQuantumMemory(
6541 number_colors,sizeof(*ping_info->trans_alpha));
6542 if (ping_info->trans == (unsigned char *) NULL)
6543 ThrowWriterException(ResourceLimitError,
6544 "MemoryAllocationFailed");
6545 assert(number_colors <= 256);
6546 for (i=0; i < (long) number_colors; i++)
6547 ping_info->trans_alpha[i]=255;
6548 exception=(&image->exception);
6549 for (y=0; y < (long) image->rows; y++)
6550 {
6551 register const PixelPacket
6552 *p;
6553
6554 p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6555 if (p == (PixelPacket *) NULL)
6556 break;
6557 indices=GetAuthenticIndexQueue(image);
6558 for (x=0; x < (long) image->columns; x++)
6559 {
6560 if (p->opacity != OpaqueOpacity)
6561 {
6562 indices[x]=(IndexPacket) (number_colors-1);
6563 ping_info->trans_alpha[(long) indices[x]]=(png_byte) (255-
6564 ScaleQuantumToChar(p->opacity));
6565 }
6566 p++;
6567 }
6568 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6569 break;
6570 }
6571 for (i=0; i < (long) number_colors; i++)
6572 if (ping_info->trans_alpha[i] != 255)
6573 ping_info->num_trans=(unsigned short) (i+1);
6574 if (ping_info->num_trans == 0)
6575 ping_info->valid&=(~PNG_INFO_tRNS);
6576 if (!(ping_info->valid & PNG_INFO_tRNS))
6577 ping_info->num_trans=0;
6578 if (ping_info->num_trans == 0)
6579 ping_info->trans_alpha=(unsigned char *)
6580 RelinquishMagickMemory(ping_info->trans_alpha);
6581 }
6582 /*
6583 Identify which colormap entry is the background color.
6584 */
6585 for (i=0; i < (long) MagickMax(1L*number_colors-1L,1L); i++)
6586 if (IsPNGColorEqual(ping_info->background,image->colormap[i]))
6587 break;
6588 ping_info->background.index=(png_byte) i;
6589 }
6590 if (image_matte != MagickFalse)
6591 {
6592 /* TO DO: reduce to binary transparency */
6593 }
6594 } /* end of write_png8 */
6595 else if (mng_info->write_png24)
6596 {
6597 image_matte=MagickFalse;
6598 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6599 }
6600 else if (mng_info->write_png32)
6601 {
6602 image_matte=MagickTrue;
6603 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6604 }
6605 else
6606 {
6607 image_depth=ping_info->bit_depth;
6608 if (mng_info->write_png_colortype)
6609 {
6610 ping_info->color_type=(png_byte) mng_info->write_png_colortype-1;
6611 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6612 ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6613 image_matte=MagickTrue;
6614 }
6615 else
6616 {
6617 if (logging != MagickFalse)
6618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6619 "Selecting PNG colortype");
6620 ping_info->color_type=(png_byte) ((matte == MagickTrue)?
6621 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6622 if(image_info->type == TrueColorType)
6623 {
6624 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6625 image_matte=MagickFalse;
6626 }
6627 if(image_info->type == TrueColorMatteType)
6628 {
6629 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6630 image_matte=MagickTrue;
6631 }
6632 if ((image_info->type == UndefinedType ||
6633 image_info->type == OptimizeType ||
6634 image_info->type == GrayscaleType) &&
6635 image_matte == MagickFalse && ImageIsGray(image))
6636 {
6637 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
6638 image_matte=MagickFalse;
6639 }
6640 if ((image_info->type == UndefinedType ||
6641 image_info->type == OptimizeType ||
6642 image_info->type == GrayscaleMatteType) &&
6643 image_matte == MagickTrue && ImageIsGray(image))
6644 {
6645 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
6646 image_matte=MagickTrue;
6647 }
6648 }
6649 if (logging != MagickFalse)
6650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6651 "Selected PNG colortype=%d",ping_info->color_type);
6652
6653 if (ping_info->bit_depth < 8)
6654 {
6655 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6656 ping_info->color_type == PNG_COLOR_TYPE_RGB ||
6657 ping_info->color_type == PNG_COLOR_TYPE_RGBA)
6658 ping_info->bit_depth=8;
6659 }
6660
6661 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
6662 {
6663 if (image->matte == MagickFalse && image->colors < 256)
6664 {
6665 if (ImageIsMonochrome(image))
6666 {
6667 ping_info->bit_depth=1;
6668 if (ping_info->bit_depth < mng_info->write_png_depth)
6669 ping_info->bit_depth = mng_info->write_png_depth;
6670 }
6671 }
6672 }
6673 if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
6674 {
6675 ping_info->bit_depth=1;
6676 while ((int) (1 << ping_info->bit_depth) < (long) image_colors)
6677 ping_info->bit_depth <<= 1;
6678
6679 if (logging != MagickFalse)
6680 {
6681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6682 " Number of colors: %lu",image_colors);
6683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6684 " Tentative PNG bit depth: %d",ping_info->bit_depth);
6685 }
6686 if (mng_info->write_png_depth)
6687 {
6688 old_bit_depth=ping_info->bit_depth;
6689 if (ping_info->bit_depth < mng_info->write_png_depth)
6690 {
6691 ping_info->bit_depth = mng_info->write_png_depth;
6692 if (ping_info->bit_depth > 8)
6693 ping_info->bit_depth = 8;
6694 if (ping_info->bit_depth != old_bit_depth)
6695 {
6696 if (logging != MagickFalse)
6697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6698 " Colors increased to %ld",image_colors);
6699 }
6700 }
6701 }
6702 }
6703 }
6704 image_depth=ping_info->bit_depth;
6705 if (logging != MagickFalse)
6706 {
6707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6708 " Tentative PNG color type: %d",ping_info->color_type);
6709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6710 " image_info->type: %d",image_info->type);
6711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6712 " image_depth: %lu",image_depth);
6713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6714 " ping_info->bit_depth: %d",ping_info->bit_depth);
6715 }
6716
6717 if (matte && (mng_info->optimize || mng_info->IsPalette))
6718 {
6719 register const PixelPacket
6720 *p;
6721
6722 p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
6723 ping_info->color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
6724 for (y=0; y < (long) image->rows; y++)
6725 {
6726 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6727 if (p == (const PixelPacket *) NULL)
6728 break;
6729 for (x=(long) image->columns-1; x >= 0; x--)
6730 {
6731 if (IsGray(p) == MagickFalse)
6732 {
6733 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6734 break;
6735 }
6736 p++;
6737 }
6738 }
6739 /*
6740 Determine if there is any transparent color.
6741 */
6742 for (y=0; y < (long) image->rows; y++)
6743 {
6744 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6745 if (p == (const PixelPacket *) NULL)
6746 break;
6747 for (x=(long) image->columns-1; x >= 0; x--)
6748 {
6749 if (p->opacity != OpaqueOpacity)
6750 break;
6751 p++;
6752 }
6753 if (x != 0)
6754 break;
6755 }
6756 if ((y == (long) image->rows) && (x == (long) image->columns))
6757 {
6758 /*
6759 No transparent pixels are present. Change 4 or 6 to 0 or 2,
6760 and do not set the PNG_INFO_tRNS flag in ping_info->valid.
6761 */
6762 image_matte=MagickFalse;
6763 ping_info->color_type&=0x03;
6764 }
6765 else
6766 {
6767 unsigned int
6768 mask;
6769
6770 mask=0xffff;
6771 if (ping_info->bit_depth == 8)
6772 mask=0x00ff;
6773 if (ping_info->bit_depth == 4)
6774 mask=0x000f;
6775 if (ping_info->bit_depth == 2)
6776 mask=0x0003;
6777 if (ping_info->bit_depth == 1)
6778 mask=0x0001;
6779 ping_info->valid|=PNG_INFO_tRNS;
6780 ping_info->trans_color.red=(png_uint_16)
6781 (ScaleQuantumToShort(p->red) & mask);
6782 ping_info->trans_color.green=(png_uint_16)
6783 (ScaleQuantumToShort(p->green) & mask);
6784 ping_info->trans_color.blue=(png_uint_16)
6785 (ScaleQuantumToShort(p->blue) & mask);
6786 ping_info->trans_color.gray=(png_uint_16)
6787 (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
6788 ping_info->trans_color.index=(png_byte)
6789 (ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity)));
6790 }
6791 if (ping_info->valid & PNG_INFO_tRNS)
6792 {
6793 /*
6794 Determine if there is one and only one transparent color
6795 and if so if it is fully transparent.
6796 */
6797 for (y=0; y < (long) image->rows; y++)
6798 {
6799 p=GetVirtualPixels(image,0,y,image->columns,1,
6800 &image->exception);
6801 x=0;
6802 if (p == (const PixelPacket *) NULL)
6803 break;
6804 for (x=(long) image->columns-1; x >= 0; x--)
6805 {
6806 if (p->opacity != OpaqueOpacity)
6807 {
6808 if (IsPNGColorEqual(ping_info->trans_color,*p) == 0)
6809 {
6810 break; /* Can't use RGB + tRNS for multiple
6811 transparent colors. */
6812 }
6813 if (p->opacity != (Quantum) TransparentOpacity)
6814 {
6815 break; /* Can't use RGB + tRNS for
6816 semitransparency. */
6817 }
6818 }
6819 else
6820 {
6821 if (IsPNGColorEqual(ping_info->trans_color,*p))
6822 break; /* Can't use RGB + tRNS when another pixel
6823 having the same RGB samples is
6824 transparent. */
6825 }
6826 p++;
6827 }
6828 if (x != 0)
6829 break;
6830 }
6831 if (x != 0)
6832 ping_info->valid&=(~PNG_INFO_tRNS);
6833 }
6834 if (ping_info->valid & PNG_INFO_tRNS)
6835 {
6836 ping_info->color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
6837 if (image_depth == 8)
6838 {
6839 ping_info->trans_color.red&=0xff;
6840 ping_info->trans_color.green&=0xff;
6841 ping_info->trans_color.blue&=0xff;
6842 ping_info->trans_color.gray&=0xff;
6843 }
6844 }
6845 }
6846 matte=image_matte;
6847 if (ping_info->valid & PNG_INFO_tRNS)
6848 image_matte=MagickFalse;
6849 if ((mng_info->optimize || mng_info->IsPalette) &&
6850 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
6851 ImageIsGray(image) && (!image_matte || image_depth >= 8))
6852 {
6853 if (image_matte != MagickFalse)
6854 ping_info->color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
6855 else
6856 {
6857 ping_info->color_type=PNG_COLOR_TYPE_GRAY;
6858 if (save_image_depth == 16 && image_depth == 8)
6859 ping_info->trans_color.gray*=0x0101;
6860 }
6861 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
6862 image_depth=MAGICKCORE_QUANTUM_DEPTH;
6863 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
6864 image_colors=1 << image_depth;
6865 if (image_depth > 8)
6866 ping_info->bit_depth=16;
6867 else
6868 {
6869 ping_info->bit_depth=8;
6870 if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
6871 {
6872 if(!mng_info->write_png_depth)
6873 {
6874 ping_info->bit_depth=1;
6875 while ((int) (1 << ping_info->bit_depth)
6876 < (long) image_colors)
6877 ping_info->bit_depth <<= 1;
6878 }
6879 }
6880 else if (mng_info->optimize && ping_info->color_type ==
6881 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
6882 mng_info->IsPalette)
6883 {
6884
6885 /* Check if grayscale is reducible */
6886 int
6887 depth_4_ok=MagickTrue,
6888 depth_2_ok=MagickTrue,
6889 depth_1_ok=MagickTrue;
6890
6891 for (i=0; i < (long) image_colors; i++)
6892 {
6893 unsigned char
6894 intensity;
6895
6896 intensity=ScaleQuantumToChar(image->colormap[i].red);
6897
6898 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
6899 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
6900 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
6901 depth_2_ok=depth_1_ok=MagickFalse;
6902 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
6903 depth_1_ok=MagickFalse;
6904 }
6905 if (depth_1_ok && mng_info->write_png_depth <= 1)
6906 ping_info->bit_depth=1;
6907 else if (depth_2_ok && mng_info->write_png_depth <= 2)
6908 ping_info->bit_depth=2;
6909 else if (depth_4_ok && mng_info->write_png_depth <= 4)
6910 ping_info->bit_depth=4;
6911 }
6912 }
6913 image_depth=ping_info->bit_depth;
6914 }
6915 else
6916 if (mng_info->IsPalette)
6917 {
6918 if (image_depth <= 8)
6919 {
6920 unsigned long
6921 number_colors;
6922
6923 number_colors=image_colors;
6924 if (matte)
6925 ping_info->valid|=PNG_INFO_tRNS;
6926 /*
6927 Set image palette.
6928 */
6929 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6930 ping_info->valid|=PNG_INFO_PLTE;
6931 if (mng_info->have_write_global_plte && !matte)
6932 {
6933 png_set_PLTE(ping,ping_info,NULL,0);
6934 if (logging)
6935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6936 " Setting up empty PLTE chunk");
6937 }
6938 else
6939 {
6940#if defined(PNG_SORT_PALETTE)
6941 unsigned long
6942 save_number_colors;
6943
6944 if (mng_info->optimize)
6945 {
6946 save_number_colors=image_colors;
6947 if (CompressColormapTransFirst(image) == MagickFalse)
6948 ThrowWriterException(ResourceLimitError,
6949 "MemoryAllocationFailed");
6950 number_colors=image_colors;
6951 image_colors=save_number_colors;
6952 }
6953#endif
6954 palette=(png_color *) AcquireQuantumMemory(257,
6955 sizeof(*palette));
6956 if (palette == (png_color *) NULL)
6957 ThrowWriterException(ResourceLimitError,
6958 "MemoryAllocationFailed");
6959 for (i=0; i < (long) number_colors; i++)
6960 {
6961 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6962 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6963 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6964 }
6965 if (logging)
6966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6967 " Setting up PLTE chunk");
6968 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6969#if (PNG_LIBPNG_VER > 10008)
6970 palette=(png_colorp) RelinquishMagickMemory(palette);
6971#endif
6972 }
6973 /* color_type is PNG_COLOR_TYPE_PALETTE */
6974 if (!mng_info->write_png_depth)
6975 {
6976 ping_info->bit_depth=1;
6977 while ((1UL << ping_info->bit_depth) < number_colors)
6978 ping_info->bit_depth <<= 1;
6979 }
6980 ping_info->num_trans=0;
6981 if (matte)
6982 {
6983 ExceptionInfo
6984 *exception;
6985
6986 register const PixelPacket
6987 *p;
6988
6989 int
6990 trans[256];
6991
6992 register const IndexPacket
6993 *packet_indices;
6994
6995 /*
6996 Identify which colormap entry is transparent.
6997 */
6998 assert(number_colors <= 256);
6999 for (i=0; i < (long) number_colors; i++)
7000 trans[i]=256;
7001 exception=(&image->exception);
7002 for (y=0; y < (long) image->rows; y++)
7003 {
7004 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7005 if (p == (const PixelPacket *) NULL)
7006 break;
7007 packet_indices=GetVirtualIndexQueue(image);
7008 for (x=0; x < (long) image->columns; x++)
7009 {
7010 if (p->opacity != OpaqueOpacity)
7011 {
7012 IndexPacket
7013 packet_index;
7014
7015 packet_index=packet_indices[x];
7016 assert((unsigned long) packet_index < number_colors);
7017 if (trans[(long) packet_index] != 256)
7018 {
7019 if (trans[(long) packet_index] != (png_byte) (255-
7020 ScaleQuantumToChar(p->opacity)))
7021 {
7022 ping_info->color_type=(png_byte)
7023 PNG_COLOR_TYPE_RGB_ALPHA;
7024 break;
7025 }
7026 }
7027 trans[(long) packet_index]=(png_byte) (255-
7028 ScaleQuantumToChar(p->opacity));
7029 }
7030 p++;
7031 }
7032 if ((int) ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
7033 {
7034 ping_info->num_trans=0;
7035 ping_info->valid&=(~PNG_INFO_tRNS);
7036 ping_info->valid&=(~PNG_INFO_PLTE);
7037 mng_info->IsPalette=MagickFalse;
7038 (void) SyncImage(image);
7039 if (logging)
7040 (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7041 " Cannot write image as indexed PNG, writing RGBA.");
7042 break;
7043 }
7044 }
7045 if ((ping_info->valid & PNG_INFO_tRNS))
7046 {
7047 for (i=0; i < (long) number_colors; i++)
7048 {
7049 if (trans[i] == 256)
7050 trans[i]=255;
7051 if (trans[i] != 255)
7052 ping_info->num_trans=(unsigned short) (i+1);
7053 }
7054 }
7055 if (ping_info->num_trans == 0)
7056 ping_info->valid&=(~PNG_INFO_tRNS);
7057 if (!(ping_info->valid & PNG_INFO_tRNS))
7058 ping_info->num_trans=0;
7059 if (ping_info->num_trans != 0)
7060 {
7061 ping_info->trans_alpha=(unsigned char *) AcquireQuantumMemory(
7062 number_colors,sizeof(*ping_info->trans_alpha));
7063 if (ping_info->trans_alpha == (unsigned char *) NULL)
7064 ThrowWriterException(ResourceLimitError,
7065 "MemoryAllocationFailed");
7066 for (i=0; i < (long) number_colors; i++)
7067 ping_info->trans_alpha[i]=(png_byte) trans[i];
7068 }
7069 }
7070
7071 /*
7072 Identify which colormap entry is the background color.
7073 */
7074 for (i=0; i < (long) MagickMax(1L*number_colors-1L,1L); i++)
7075 if (IsPNGColorEqual(ping_info->background,image->colormap[i]))
7076 break;
7077 ping_info->background.index=(png_byte) i;
7078 }
7079 }
7080 else
7081 {
7082 if (image_depth < 8)
7083 image_depth=8;
7084 if ((save_image_depth == 16) && (image_depth == 8))
7085 {
7086 ping_info->trans_color.red*=0x0101;
7087 ping_info->trans_color.green*=0x0101;
7088 ping_info->trans_color.blue*=0x0101;
7089 ping_info->trans_color.gray*=0x0101;
7090 }
7091 }
7092
7093 /*
7094 Adjust background and transparency samples in sub-8-bit grayscale files.
7095 */
7096 if (ping_info->bit_depth < 8 && ping_info->color_type ==
7097 PNG_COLOR_TYPE_GRAY)
7098 {
7099 png_uint_16
7100 maxval;
7101
7102 png_color_16
7103 background;
7104
7105 maxval=(png_uint_16) ((1 << ping_info->bit_depth)-1);
7106
7107
7108 background.gray=(png_uint_16)
7109 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7110
7111 if (logging != MagickFalse)
7112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7113 " Setting up bKGD chunk");
7114 png_set_bKGD(ping,ping_info,&background);
7115
7116 ping_info->trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7117 ping_info->trans_color.gray));
7118 }
7119 if (logging != MagickFalse)
7120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7121 " PNG color type: %d",ping_info->color_type);
7122 /*
7123 Initialize compression level and filtering.
7124 */
7125 if (logging != MagickFalse)
7126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7127 " Setting up deflate compression");
7128#if (PNG_LIBPNG_VER > 99)
7129 if (logging != MagickFalse)
7130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7131 " Compression buffer size: 32768");
7132 png_set_compression_buffer_size(ping,32768L);
7133#endif
7134 if (logging != MagickFalse)
7135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7136 " Compression mem level: 9");
7137 png_set_compression_mem_level(ping, 9);
7138 quality=image->quality == UndefinedCompressionQuality ? 75UL :
7139 image->quality;
7140 if (quality > 9)
7141 {
7142 int
7143 level;
7144
7145 level=(int) MagickMin((long) quality/10,9);
7146 if (logging != MagickFalse)
7147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7148 " Compression level: %d",level);
7149 png_set_compression_level(ping,level);
7150 }
7151 else
7152 {
7153 if (logging != MagickFalse)
7154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7155 " Compression strategy: Z_HUFFMAN_ONLY");
7156 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7157 }
7158 if (logging != MagickFalse)
7159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7160 " Setting up filtering");
7161#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7162
7163 /* This became available in libpng-1.0.9. Output must be a MNG. */
7164 if (mng_info->write_mng && ((quality % 10) == 7))
7165 {
7166 if (logging != MagickFalse)
7167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7168 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
7169 ping_info->filter_type=PNG_INTRAPIXEL_DIFFERENCING;
7170 }
7171 else
7172 if (logging != MagickFalse)
7173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7174 " Filter_type: 0");
7175#endif
7176 {
7177 int
7178 base_filter;
7179
7180 if ((quality % 10) > 5)
7181 base_filter=PNG_ALL_FILTERS;
7182 else
7183 if ((quality % 10) != 5)
7184 base_filter=(int) quality % 10;
7185 else
7186 if (((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY) ||
7187 ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE) ||
7188 (quality < 50))
7189 base_filter=PNG_NO_FILTERS;
7190 else
7191 base_filter=PNG_ALL_FILTERS;
7192 if (logging != MagickFalse)
7193 {
7194 if (base_filter == PNG_ALL_FILTERS)
7195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7196 " Base filter method: ADAPTIVE");
7197 else
7198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7199 " Base filter method: NONE");
7200 }
7201 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7202 }
7203
7204 ResetImageProfileIterator(image);
7205 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7206 {
7207 profile=GetImageProfile(image,name);
7208 if (profile != (StringInfo *) NULL)
7209 {
7210#if (PNG_LIBPNG_VER > 10008) && defined(PNG_WRITE_iCCP_SUPPORTED)
7211 if ((LocaleCompare(name,"ICC") == 0) ||
7212 (LocaleCompare(name,"ICM") == 0))
7213 png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7214 GetStringInfoDatum(profile),
7215 (png_uint_32) GetStringInfoLength(profile));
7216 else
7217#endif
7218 png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7219 name,(unsigned char *) name,GetStringInfoDatum(profile),
7220 (png_uint_32) GetStringInfoLength(profile));
7221 }
7222 if (logging != MagickFalse)
7223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7224 " Setting up text chunk with %s profile",name);
7225 name=GetNextImageProfile(image);
7226 }
7227
7228#if defined(PNG_WRITE_sRGB_SUPPORTED)
7229 if ((mng_info->have_write_global_srgb == 0) &&
7230 ((image->rendering_intent != UndefinedIntent) ||
7231 (image->colorspace == sRGBColorspace)))
7232 {
7233 /*
7234 Note image rendering intent.
7235 */
7236 if (logging != MagickFalse)
7237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7238 " Setting up sRGB chunk");
7239 (void) png_set_sRGB(ping,ping_info,(int) (image->rendering_intent-1));
7240 png_set_gAMA(ping,ping_info,0.45455);
7241 }
7242 if ((!mng_info->write_mng) || !(ping_info->valid & PNG_INFO_sRGB))
7243#endif
7244 {
7245 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7246 {
7247 /*
7248 Note image gamma.
7249 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7250 */
7251 if (logging != MagickFalse)
7252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7253 " Setting up gAMA chunk");
7254 png_set_gAMA(ping,ping_info,image->gamma);
7255 }
7256 if ((mng_info->have_write_global_chrm == 0) &&
7257 (image->chromaticity.red_primary.x != 0.0))
7258 {
7259 /*
7260 Note image chromaticity.
7261 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7262 */
7263 PrimaryInfo
7264 bp,
7265 gp,
7266 rp,
7267 wp;
7268
7269 wp=image->chromaticity.white_point;
7270 rp=image->chromaticity.red_primary;
7271 gp=image->chromaticity.green_primary;
7272 bp=image->chromaticity.blue_primary;
7273
7274 if (logging != MagickFalse)
7275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7276 " Setting up cHRM chunk");
7277 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7278 bp.x,bp.y);
7279 }
7280 }
7281 ping_info->interlace_type=image_info->interlace != NoInterlace;
7282
7283 if (mng_info->write_mng)
7284 png_set_sig_bytes(ping,8);
7285
7286 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7287
7288 if (mng_info->write_png_colortype)
7289 {
7290 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7291 if (ImageIsGray(image) == MagickFalse)
7292 {
7293 ping_info->color_type = PNG_COLOR_TYPE_RGB;
7294 if (ping_info->bit_depth < 8)
7295 ping_info->bit_depth=8;
7296 }
7297
7298 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7299 if (ImageIsGray(image) == MagickFalse)
7300 ping_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
7301 }
7302
7303 if ((mng_info->write_png_depth &&
7304 mng_info->write_png_depth != ping_info->bit_depth) ||
7305 (mng_info->write_png_colortype &&
7306 mng_info->write_png_colortype-1 != ping_info->color_type))
7307 {
7308 if (logging != MagickFalse)
7309 {
7310 if (mng_info->write_png_depth)
7311 {
7312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7313 " Defined PNG:bit-depth=%u, Computed depth=%u",
7314 mng_info->write_png_depth,
7315 ping_info->bit_depth);
7316 }
7317 if (mng_info->write_png_colortype)
7318 {
7319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7320 " Defined PNG:color-type=%u, Computed color type=%u",
7321 mng_info->write_png_colortype-1,
7322 ping_info->color_type);
7323 }
7324 }
7325 png_error(ping,
7326 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7327 }
7328
7329 if (image_matte && !image->matte)
7330 {
7331 /* Add an opaque matte channel */
7332 image->matte = MagickTrue;
7333 (void) SetImageOpacity(image,0);
7334 }
7335
7336 if (logging != MagickFalse)
7337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7338 " Writing PNG header chunks");
7339
7340 png_write_info_before_PLTE(ping, ping_info);
7341 /* write any png-chunk-b profiles */
7342 (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7343 png_write_info(ping,ping_info);
7344 /* write any PNG-chunk-m profiles */
7345 (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7346
7347 if (image->page.width || image->page.height)
7348 {
7349 unsigned char
7350 chunk[14];
7351
7352 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
7353 PNGType(chunk,mng_vpAg);
7354 LogPNGChunk((int) logging,mng_vpAg,9L);
7355 PNGLong(chunk+4,(png_uint_32) image->page.width);
7356 PNGLong(chunk+8,(png_uint_32) image->page.height);
7357 chunk[12]=0; /* unit = pixels */
7358 (void) WriteBlob(image,13,chunk);
7359 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
7360 }
7361
7362#if (PNG_LIBPNG_VER == 10206)
7363 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
7364#define PNG_HAVE_IDAT 0x04
7365 ping->mode |= PNG_HAVE_IDAT;
7366#undef PNG_HAVE_IDAT
7367#endif
7368
7369 png_set_packing(ping);
7370 /*
7371 Allocate memory.
7372 */
7373 rowbytes=image->columns;
7374 if (image_depth <= 8)
7375 {
7376 if (mng_info->write_png24 || (mng_info->write_png_depth == 8 &&
7377 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_RGB))
7378 rowbytes*=3;
7379 else if (mng_info->write_png32 || (mng_info->write_png_depth == 8 &&
7380 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_RGB_ALPHA))
7381 rowbytes*=4;
7382 else if ((!mng_info->write_png8 ||
7383 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY ||
7384 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA )&&
7385 ((mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image)))
7386 rowbytes*=(image_matte ? 2 : 1);
7387 else
7388 {
7389 if (!mng_info->IsPalette)
7390 rowbytes*=(image_matte ? 4 : 3);
7391 }
7392 }
7393 else
7394 {
7395 if ((mng_info->optimize || mng_info->IsPalette) &&
7396 ImageIsGray(image))
7397 rowbytes*=(image_matte ? 4 : 2);
7398 else
7399 rowbytes*=(image_matte ? 8 : 6);
7400 }
7401 if (logging)
7402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7403 " Allocating %lu bytes of memory for pixels",rowbytes);
7404 png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7405 sizeof(*png_pixels));
7406 if (png_pixels == (unsigned char *) NULL)
7407 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7408 /*
7409 Initialize image scanlines.
7410 */
cristy3ed852e2009-09-05 21:47:34 +00007411 if (setjmp(ping->jmpbuf))
7412 {
7413 /*
7414 PNG write failed.
7415 */
7416#ifdef PNG_DEBUG
7417 if (image_info->verbose)
7418 (void) printf("PNG write has failed.\n");
7419#endif
7420 png_destroy_write_struct(&ping,&ping_info);
7421 if (quantum_info != (QuantumInfo *) NULL)
7422 quantum_info=DestroyQuantumInfo(quantum_info);
7423 if (png_pixels != (unsigned char *) NULL)
7424 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7425#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7426 RelinquishSemaphoreInfo(png_semaphore);
7427#endif
7428 return(MagickFalse);
7429 }
cristyed552522009-10-16 14:04:35 +00007430 quantum_info=AcquireQuantumInfo(image_info,image);
7431 if (quantum_info == (QuantumInfo *) NULL)
7432 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00007433 quantum_info->format=UndefinedQuantumFormat;
7434 quantum_info->depth=image_depth;
7435 num_passes=png_set_interlace_handling(ping);
7436 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7437 !mng_info->write_png32) &&
7438 (mng_info->optimize || mng_info->IsPalette ||
7439 (image_info->type == BilevelType)) &&
7440 !image_matte && ImageIsMonochrome(image))
7441 {
7442 register const PixelPacket
7443 *p;
7444
7445 quantum_info->depth=8;
7446 for (pass=0; pass < num_passes; pass++)
7447 {
7448 /*
7449 Convert PseudoClass image to a PNG monochrome image.
7450 */
7451 for (y=0; y < (long) image->rows; y++)
7452 {
7453 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7454 if (p == (const PixelPacket *) NULL)
7455 break;
7456 if (mng_info->IsPalette)
7457 {
7458 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7459 quantum_info,GrayQuantum,png_pixels,&image->exception);
7460 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7461 mng_info->write_png_depth &&
7462 mng_info->write_png_depth != old_bit_depth)
7463 {
7464 /* Undo pixel scaling */
7465 for (i=0; i < (long) image->columns; i++)
7466 *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7467 >> (8-old_bit_depth));
7468 }
7469 }
7470 else
7471 {
7472 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7473 quantum_info,RedQuantum,png_pixels,&image->exception);
7474 }
7475 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
7476 for (i=0; i < (long) image->columns; i++)
7477 *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7478 255 : 0);
7479 png_write_row(ping,png_pixels);
7480 }
7481 if (image->previous == (Image *) NULL)
7482 {
7483 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7484 if (status == MagickFalse)
7485 break;
7486 }
7487 }
7488 }
7489 else
7490 for (pass=0; pass < num_passes; pass++)
7491 {
7492 register const PixelPacket
7493 *p;
7494
7495 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7496 !mng_info->write_png32) &&
7497 (image_matte ||
7498 (ping_info->bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
7499 (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7500 {
7501 for (y=0; y < (long) image->rows; y++)
7502 {
7503 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7504 if (p == (const PixelPacket *) NULL)
7505 break;
7506 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
7507 {
7508 if (mng_info->IsPalette)
7509 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7510 quantum_info,GrayQuantum,png_pixels,&image->exception);
7511 else
7512 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7513 quantum_info,RedQuantum,png_pixels,&image->exception);
7514 }
7515 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7516 {
7517 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7518 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7519 }
7520 png_write_row(ping,png_pixels);
7521 }
7522 if (image->previous == (Image *) NULL)
7523 {
7524 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7525 if (status == MagickFalse)
7526 break;
7527 }
7528 }
7529 else
7530 for (pass=0; pass < num_passes; pass++)
7531 {
7532 if ((image_depth > 8) || (mng_info->write_png24 ||
7533 mng_info->write_png32 ||
7534 (!mng_info->write_png8 && !mng_info->IsPalette)))
7535 for (y=0; y < (long) image->rows; y++)
7536 {
7537 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7538 if (p == (const PixelPacket *) NULL)
7539 break;
7540 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
7541 {
7542 if (image->storage_class == DirectClass)
7543 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7544 quantum_info,RedQuantum,png_pixels,&image->exception);
7545 else
7546 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7547 quantum_info,GrayQuantum,png_pixels,&image->exception);
7548 }
7549 else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7550 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7551 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7552 else if (image_matte != MagickFalse)
7553 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7554 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7555 else
7556 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7557 quantum_info,RGBQuantum,png_pixels,&image->exception);
7558 png_write_row(ping,png_pixels);
7559 }
7560 else
7561 /* not ((image_depth > 8) || (mng_info->write_png24 ||
7562 mng_info->write_png32 ||
7563 (!mng_info->write_png8 && !mng_info->IsPalette))) */
7564 {
7565 if ((ping_info->color_type != PNG_COLOR_TYPE_GRAY) &&
7566 (ping_info->color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
7567 {
7568 if (logging)
7569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7570 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7571 quantum_info->depth=8;
7572 image_depth=8;
7573 }
7574 for (y=0; y < (long) image->rows; y++)
7575 {
7576 if (logging)
7577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7578 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7579 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7580 if (p == (const PixelPacket *) NULL)
7581 break;
7582 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
7583 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7584 quantum_info,GrayQuantum,png_pixels,&image->exception);
7585 else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7586 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7587 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7588 else
7589 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7590 quantum_info,IndexQuantum,png_pixels,&image->exception);
7591 png_write_row(ping,png_pixels);
7592 }
7593 }
7594 if (image->previous == (Image *) NULL)
7595 {
7596 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7597 if (status == MagickFalse)
7598 break;
7599 }
7600 }
7601 }
cristyb32b90a2009-09-07 21:45:48 +00007602 if (quantum_info != (QuantumInfo *) NULL)
7603 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00007604
7605 if (logging != MagickFalse)
7606 {
7607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7608 " Writing PNG image data");
7609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7610 " Width: %lu",ping_info->width);
7611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7612 " Height: %lu",ping_info->height);
7613 if (mng_info->write_png_depth)
7614 {
7615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7616 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7617 }
7618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7619 " PNG bit-depth written: %d",ping_info->bit_depth);
7620 if (mng_info->write_png_colortype)
7621 {
7622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7623 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7624 }
7625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7626 " PNG color-type written: %d",ping_info->color_type);
7627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7628 " PNG Interlace method: %d",ping_info->interlace_type);
7629 }
7630 /*
7631 Generate text chunks.
7632 */
7633#if (PNG_LIBPNG_VER <= 10005)
7634 ping_info->num_text=0;
7635#endif
7636 ResetImagePropertyIterator(image);
7637 property=GetNextImageProperty(image);
7638 while (property != (const char *) NULL)
7639 {
7640#if (PNG_LIBPNG_VER > 10005)
7641 png_textp
7642 text;
7643#endif
7644
7645 value=GetImageProperty(image,property);
7646 if (value != (const char *) NULL)
7647 {
7648#if (PNG_LIBPNG_VER > 10005)
7649 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7650 text[0].key=(char *) property;
7651 text[0].text=(char *) value;
7652 text[0].text_length=strlen(value);
7653 text[0].compression=image_info->compression == NoCompression ||
7654 (image_info->compression == UndefinedCompression &&
7655 text[0].text_length < 128) ? -1 : 0;
7656 if (logging != MagickFalse)
7657 {
7658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7659 " Setting up text chunk");
7660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7661 " keyword: %s",text[0].key);
7662 }
7663 png_set_text(ping,ping_info,text,1);
7664 png_free(ping,text);
7665#else
7666/* Work directly with ping_info struct; png_set_text before libpng version
7667 * 1.0.5a is leaky */
7668 if (ping_info->num_text == 0)
7669 {
7670 ping_info->text=(png_text *) AcquireQuantumMemory(256,
7671 sizeof(*ping_info->text));
7672 if (ping_info->text == (png_text *) NULL)
7673 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7674 ResourceLimitError,"MemoryAllocationFailed","`%s'",
7675 image->filename);
7676 }
7677 i=ping_info->num_text++;
7678 if (i > 255)
7679 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7680 ResourceLimitError,"Cannot write more than 256 PNG text chunks",
7681 "`%s'",image->filename);
7682 ping_info->text[i].key=(char *) property;
7683 ping_info->text[i].text=(char *) value;
7684 ping_info->text[i].text_length=strlen(value);
7685 ping_info->text[i].compression=
7686 image_info->compression == NoCompression ||
7687 (image_info->compression == UndefinedCompression &&
7688 ping_info->text[i].text_length < 128) ? -1 : 0;
7689 if (logging != MagickFalse)
7690 {
7691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7692 " Setting up text chunk");
7693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7694 " keyword: %s",ping_info->text[i].key);
7695 }
7696#endif
7697 }
7698 property=GetNextImageProperty(image);
7699 }
7700
7701 /* write any PNG-chunk-e profiles */
7702 (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
7703
7704 if (logging != MagickFalse)
7705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7706 " Writing PNG end info");
7707 png_write_end(ping,ping_info);
7708 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
7709 {
7710 if (mng_info->page.x || mng_info->page.y ||
7711 (ping_info->width != mng_info->page.width) ||
7712 (ping_info->height != mng_info->page.height))
7713 {
7714 unsigned char
7715 chunk[32];
7716
7717 /*
7718 Write FRAM 4 with clipping boundaries followed by FRAM 1.
7719 */
7720 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
7721 PNGType(chunk,mng_FRAM);
7722 LogPNGChunk((int) logging,mng_FRAM,27L);
7723 chunk[4]=4;
7724 chunk[5]=0; /* frame name separator (no name) */
7725 chunk[6]=1; /* flag for changing delay, for next frame only */
7726 chunk[7]=0; /* flag for changing frame timeout */
7727 chunk[8]=1; /* flag for changing frame clipping for next frame */
7728 chunk[9]=0; /* flag for changing frame sync_id */
7729 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
7730 chunk[14]=0; /* clipping boundaries delta type */
7731 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
7732 PNGLong(chunk+19,
7733 (png_uint_32) (mng_info->page.x + ping_info->width));
7734 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
7735 PNGLong(chunk+27,
7736 (png_uint_32) (mng_info->page.y + ping_info->height));
7737 (void) WriteBlob(image,31,chunk);
7738 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
7739 mng_info->old_framing_mode=4;
7740 mng_info->framing_mode=1;
7741 }
7742 else
7743 mng_info->framing_mode=3;
7744 }
7745 if (mng_info->write_mng && !mng_info->need_fram &&
7746 ((int) image->dispose == 3))
7747 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7748 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
7749 "`%s'",image->filename);
7750 image_depth=save_image_depth;
7751
7752 /* Save depth actually written */
7753
7754 s[0]=(char) ping_info->bit_depth;
7755 s[1]='\0';
7756
7757 (void) SetImageProperty(image,"png:bit-depth-written",s);
7758
7759 /*
7760 Free PNG resources.
7761 */
7762#if (PNG_LIBPNG_VER < 10007)
7763 if (ping_info->valid & PNG_INFO_PLTE)
7764 {
7765 ping_info->palette=(png_colorp)
7766 RelinquishMagickMemory(ping_info->palette);
7767 ping_info->valid&=(~PNG_INFO_PLTE);
7768 }
7769 if (ping_info->valid & PNG_INFO_tRNS)
7770 {
7771 ping_info->trans_alpha=(unsigned char *) RelinquishMagickMemory(
7772 ping_info->trans_alpha);
7773 ping_info->valid&=(~PNG_INFO_tRNS);
7774 }
7775#endif
7776
7777 png_destroy_write_struct(&ping,&ping_info);
7778
7779 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7780
7781#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7782 RelinquishSemaphoreInfo(png_semaphore);
7783#endif
7784
7785 if (logging != MagickFalse)
7786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7787 " exit WriteOnePNGImage()");
7788 return(MagickTrue);
7789/* End write one PNG image */
7790}
7791
7792/*
7793%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7794% %
7795% %
7796% %
7797% W r i t e P N G I m a g e %
7798% %
7799% %
7800% %
7801%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7802%
7803% WritePNGImage() writes a Portable Network Graphics (PNG) or
7804% Multiple-image Network Graphics (MNG) image file.
7805%
7806% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7807%
7808% The format of the WritePNGImage method is:
7809%
7810% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
7811%
7812% A description of each parameter follows:
7813%
7814% o image_info: the image info.
7815%
7816% o image: The image.
7817%
7818% Returns MagickTrue on success, MagickFalse on failure.
7819%
7820% Communicating with the PNG encoder:
7821%
7822% While the datastream written is always in PNG format and normally would
7823% be given the "png" file extension, this method also writes the following
7824% pseudo-formats which are subsets of PNG:
7825%
7826% o PNG8: An 8-bit indexed PNG datastream is written. If transparency
7827% is present, the tRNS chunk must only have values 0 and 255
7828% (i.e., transparency is binary: fully opaque or fully
7829% transparent). The pixels contain 8-bit indices even if
7830% they could be represented with 1, 2, or 4 bits. Note: grayscale
7831% images will be written as indexed PNG files even though the
7832% PNG grayscale type might be slightly more efficient.
7833%
7834% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
7835% chunk can be present to convey binary transparency by naming
7836% one of the colors as transparent.
7837%
7838% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
7839% transparency is permitted, i.e., the alpha sample for
7840% each pixel can have any value from 0 to 255. The alpha
7841% channel is present even if the image is fully opaque.
7842%
7843% o -define: For more precise control of the PNG output, you can use the
7844% Image options "png:bit-depth" and "png:color-type". These
7845% can be set from the commandline with "-define" and also
7846% from the application programming interfaces.
7847%
7848% png:color-type can be 0, 2, 3, 4, or 6.
7849%
7850% When png:color-type is 0 (Grayscale), png:bit-depth can
7851% be 1, 2, 4, 8, or 16.
7852%
7853% When png:color-type is 2 (RGB), png:bit-depth can
7854% be 8 or 16.
7855%
7856% When png:color-type is 3 (Indexed), png:bit-depth can
7857% be 1, 2, 4, or 8. This refers to the number of bits
7858% used to store the index. The color samples always have
7859% bit-depth 8 in indexed PNG files.
7860%
7861% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
7862% png:bit-depth can be 8 or 16.
7863%
7864% If the image cannot be written without loss in the requested PNG8, PNG24,
7865% or PNG32 format or with the requested bit-depth and color-type without loss,
7866% a PNG file will not be written, and the encoder will return MagickFalse.
7867% Since image encoders should not be responsible for the "heavy lifting",
7868% the user should make sure that ImageMagick has already reduced the
7869% image depth and number of colors and limit transparency to binary
7870% transparency prior to attempting to write the image in a format that
7871% is subject to depth, color, or transparency limitations.
7872%
7873% TODO: Enforce the previous paragraph.
7874%
7875% TODO: Allow all other PNG subformats to be requested via new
7876% "-define png:bit-depth -define png:color-type" options.
7877%
7878% Note that another definition, "png:bit-depth-written" exists, but it
7879% is not intended for external use. It is only used internally by the
7880% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
7881%
7882% It is possible to request that the PNG encoder write previously-formatted
7883% ancillary chunks in the output PNG file, using the "-profile" commandline
7884% option as shown below or by setting the profile via a programming
7885% interface:
7886%
7887% -profile PNG-chunk-x:<file>
7888%
7889% where x is a location flag and <file> is a file containing the chunk
7890% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
7891%
7892% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
7893% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
7894% of the same type, then add a short unique string after the "x" to prevent
7895% subsequent profiles from overwriting the preceding ones:
7896%
7897% -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
7898%
7899%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7900*/
7901static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
7902 Image *image)
7903{
7904 MagickBooleanType
7905 status;
7906
7907 MngInfo
7908 *mng_info;
7909
7910 const char
7911 *value;
7912
7913 int
7914 have_mng_structure;
7915
7916 unsigned int
7917 logging;
7918
7919 /*
7920 Open image file.
7921 */
7922 assert(image_info != (const ImageInfo *) NULL);
7923 assert(image_info->signature == MagickSignature);
7924 assert(image != (Image *) NULL);
7925 assert(image->signature == MagickSignature);
7926 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
7927 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
7928 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
7929 if (status == MagickFalse)
7930 return(MagickFalse);
7931 /*
7932 Allocate a MngInfo structure.
7933 */
7934 have_mng_structure=MagickFalse;
7935 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7936 if (mng_info == (MngInfo *) NULL)
7937 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7938 /*
7939 Initialize members of the MngInfo structure.
7940 */
7941 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
7942 mng_info->image=image;
7943 have_mng_structure=MagickTrue;
7944
7945 /* See if user has requested a specific PNG subformat */
7946
7947 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
7948 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
7949 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
7950
7951 if (mng_info->write_png8)
7952 {
7953 mng_info->write_png_colortype = /* 3 */ 4;
7954 mng_info->write_png_depth = 8;
7955 image->depth = 8;
7956#if 0 /* this does not work */
7957 if (image->matte == MagickTrue)
7958 (void) SetImageType(image,PaletteMatteType);
7959 else
7960 (void) SetImageType(image,PaletteType);
7961 (void) SyncImage(image);
7962#endif
7963 }
7964
7965 if (mng_info->write_png24)
7966 {
7967 mng_info->write_png_colortype = /* 2 */ 3;
7968 mng_info->write_png_depth = 8;
7969 image->depth = 8;
7970 if (image->matte == MagickTrue)
7971 (void) SetImageType(image,TrueColorMatteType);
7972 else
7973 (void) SetImageType(image,TrueColorType);
7974 (void) SyncImage(image);
7975 }
7976
7977 if (mng_info->write_png32)
7978 {
7979 mng_info->write_png_colortype = /* 6 */ 7;
7980 mng_info->write_png_depth = 8;
7981 image->depth = 8;
7982 if (image->matte == MagickTrue)
7983 (void) SetImageType(image,TrueColorMatteType);
7984 else
7985 (void) SetImageType(image,TrueColorType);
7986 (void) SyncImage(image);
7987 }
7988
7989 value=GetImageOption(image_info,"png:bit-depth");
7990 if (value != (char *) NULL)
7991 {
7992 if (LocaleCompare(value,"1") == 0)
7993 mng_info->write_png_depth = 1;
7994 else if (LocaleCompare(value,"2") == 0)
7995 mng_info->write_png_depth = 2;
7996 else if (LocaleCompare(value,"4") == 0)
7997 mng_info->write_png_depth = 4;
7998 else if (LocaleCompare(value,"8") == 0)
7999 mng_info->write_png_depth = 8;
8000 else if (LocaleCompare(value,"16") == 0)
8001 mng_info->write_png_depth = 16;
8002 if (logging != MagickFalse)
8003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8004 "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
8005 }
8006 value=GetImageOption(image_info,"png:color-type");
8007 if (value != (char *) NULL)
8008 {
8009 /* We must store colortype+1 because 0 is a valid colortype */
8010 if (LocaleCompare(value,"0") == 0)
8011 mng_info->write_png_colortype = 1;
8012 else if (LocaleCompare(value,"2") == 0)
8013 mng_info->write_png_colortype = 3;
8014 else if (LocaleCompare(value,"3") == 0)
8015 mng_info->write_png_colortype = 4;
8016 else if (LocaleCompare(value,"4") == 0)
8017 mng_info->write_png_colortype = 5;
8018 else if (LocaleCompare(value,"6") == 0)
8019 mng_info->write_png_colortype = 7;
8020 if (logging != MagickFalse)
8021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8022 "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
8023 }
8024
8025 status=WriteOnePNGImage(mng_info,image_info,image);
8026
8027 (void) CloseBlob(image);
8028
8029 MngInfoFreeStruct(mng_info,&have_mng_structure);
8030 if (logging != MagickFalse)
8031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8032 return(status);
8033}
8034
8035#if defined(JNG_SUPPORTED)
8036
8037/* Write one JNG image */
8038static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8039 const ImageInfo *image_info,Image *image)
8040{
8041 Image
8042 *jpeg_image;
8043
8044 ImageInfo
8045 *jpeg_image_info;
8046
8047 MagickBooleanType
8048 status;
8049
8050 size_t
8051 length;
8052
8053 unsigned char
8054 *blob,
8055 chunk[80],
8056 *p;
8057
8058 unsigned int
8059 jng_alpha_compression_method,
8060 jng_alpha_sample_depth,
8061 jng_color_type,
8062 logging,
8063 transparent;
8064
8065 unsigned long
8066 jng_quality;
8067
8068 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8069 " enter WriteOneJNGImage()");
8070
8071 blob=(unsigned char *) NULL;
8072 jpeg_image=(Image *) NULL;
8073 jpeg_image_info=(ImageInfo *) NULL;
8074
8075 status=MagickTrue;
8076 transparent=image_info->type==GrayscaleMatteType ||
8077 image_info->type==TrueColorMatteType;
8078 jng_color_type=10;
8079 jng_alpha_sample_depth=0;
8080 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8081 jng_alpha_compression_method=0;
8082
8083 if (image->matte != MagickFalse)
8084 {
8085 /* if any pixels are transparent */
8086 transparent=MagickTrue;
8087 if (image_info->compression==JPEGCompression)
8088 jng_alpha_compression_method=8;
8089 }
8090
8091 if (transparent)
8092 {
8093 jng_color_type=14;
8094 /* Create JPEG blob, image, and image_info */
8095 if (logging != MagickFalse)
8096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8097 " Creating jpeg_image_info for opacity.");
8098 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8099 if (jpeg_image_info == (ImageInfo *) NULL)
8100 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8101 if (logging != MagickFalse)
8102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8103 " Creating jpeg_image.");
8104 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8105 if (jpeg_image == (Image *) NULL)
8106 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8107 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8108 status=SeparateImageChannel(jpeg_image,OpacityChannel);
8109 status=NegateImage(jpeg_image,MagickFalse);
8110 jpeg_image->matte=MagickFalse;
8111 if (jng_quality >= 1000)
8112 jpeg_image_info->quality=jng_quality/1000;
8113 else
8114 jpeg_image_info->quality=jng_quality;
8115 jpeg_image_info->type=GrayscaleType;
8116 (void) SetImageType(jpeg_image,GrayscaleType);
8117 (void) AcquireUniqueFilename(jpeg_image->filename);
8118 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8119 "%s",jpeg_image->filename);
8120 }
8121
8122 /* To do: check bit depth of PNG alpha channel */
8123
8124 /* Check if image is grayscale. */
8125 if (image_info->type != TrueColorMatteType && image_info->type !=
8126 TrueColorType && ImageIsGray(image))
8127 jng_color_type-=2;
8128
8129 if (transparent)
8130 {
8131 if (jng_alpha_compression_method==0)
8132 {
8133 const char
8134 *value;
8135
8136 /* Encode opacity as a grayscale PNG blob */
8137 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8138 &image->exception);
8139 if (logging != MagickFalse)
8140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8141 " Creating PNG blob.");
8142 length=0;
8143
8144 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8145 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8146 jpeg_image_info->interlace=NoInterlace;
8147
8148 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8149 &image->exception);
8150
8151 /* Retrieve sample depth used */
8152 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8153 if (value != (char *) NULL)
8154 jng_alpha_sample_depth= (unsigned int) value[0];
8155 }
8156 else
8157 {
8158 /* Encode opacity as a grayscale JPEG blob */
8159
8160 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8161 &image->exception);
8162
8163 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8164 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8165 jpeg_image_info->interlace=NoInterlace;
8166 if (logging != MagickFalse)
8167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8168 " Creating blob.");
8169 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8170 &image->exception);
8171 jng_alpha_sample_depth=8;
8172 if (logging != MagickFalse)
8173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8174 " Successfully read jpeg_image into a blob, length=%lu.",
8175 (unsigned long) length);
8176
8177 }
8178 /* Destroy JPEG image and image_info */
8179 jpeg_image=DestroyImage(jpeg_image);
8180 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8181 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8182 }
8183
8184 /* Write JHDR chunk */
8185 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
8186 PNGType(chunk,mng_JHDR);
8187 LogPNGChunk((int) logging,mng_JHDR,16L);
8188 PNGLong(chunk+4,image->columns);
8189 PNGLong(chunk+8,image->rows);
8190 chunk[12]=jng_color_type;
8191 chunk[13]=8; /* sample depth */
8192 chunk[14]=8; /*jng_image_compression_method */
8193 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8194 chunk[16]=jng_alpha_sample_depth;
8195 chunk[17]=jng_alpha_compression_method;
8196 chunk[18]=0; /*jng_alpha_filter_method */
8197 chunk[19]=0; /*jng_alpha_interlace_method */
8198 (void) WriteBlob(image,20,chunk);
8199 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8200 if (logging != MagickFalse)
8201 {
8202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8203 " JNG width:%15lu",image->columns);
8204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8205 " JNG height:%14lu",image->rows);
8206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8207 " JNG color type:%10d",jng_color_type);
8208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8209 " JNG sample depth:%8d",8);
8210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8211 " JNG compression:%9d",8);
8212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8213 " JNG interlace:%11d",0);
8214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8215 " JNG alpha depth:%9d",jng_alpha_sample_depth);
8216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8217 " JNG alpha compression:%3d",jng_alpha_compression_method);
8218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8219 " JNG alpha filter:%8d",0);
8220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8221 " JNG alpha interlace:%5d",0);
8222 }
8223
8224 /* Write any JNG-chunk-b profiles */
8225 (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8226
8227 /*
8228 Write leading ancillary chunks
8229 */
8230
8231 if (transparent)
8232 {
8233 /*
8234 Write JNG bKGD chunk
8235 */
8236
8237 unsigned char
8238 blue,
8239 green,
8240 red;
8241
8242 long
8243 num_bytes;
8244
8245 if (jng_color_type == 8 || jng_color_type == 12)
8246 num_bytes=6L;
8247 else
8248 num_bytes=10L;
8249 (void) WriteBlobMSBULong(image,(unsigned long) (num_bytes-4L));
8250 PNGType(chunk,mng_bKGD);
8251 LogPNGChunk((int) logging,mng_bKGD,(unsigned long) (num_bytes-4L));
8252 red=ScaleQuantumToChar(image->background_color.red);
8253 green=ScaleQuantumToChar(image->background_color.green);
8254 blue=ScaleQuantumToChar(image->background_color.blue);
8255 *(chunk+4)=0;
8256 *(chunk+5)=red;
8257 *(chunk+6)=0;
8258 *(chunk+7)=green;
8259 *(chunk+8)=0;
8260 *(chunk+9)=blue;
8261 (void) WriteBlob(image,(size_t) num_bytes,chunk);
8262 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8263 }
8264
8265 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8266 {
8267 /*
8268 Write JNG sRGB chunk
8269 */
8270 (void) WriteBlobMSBULong(image,1L);
8271 PNGType(chunk,mng_sRGB);
8272 LogPNGChunk((int) logging,mng_sRGB,1L);
8273 if (image->rendering_intent != UndefinedIntent)
8274 chunk[4]=(unsigned char) (image->rendering_intent-1);
8275 else
8276 chunk[4]=(unsigned char) (PerceptualIntent-1);
8277 (void) WriteBlob(image,5,chunk);
8278 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8279 }
8280 else
8281 {
8282 if (image->gamma != 0.0)
8283 {
8284 /*
8285 Write JNG gAMA chunk
8286 */
8287 (void) WriteBlobMSBULong(image,4L);
8288 PNGType(chunk,mng_gAMA);
8289 LogPNGChunk((int) logging,mng_gAMA,4L);
8290 PNGLong(chunk+4,(unsigned long) (100000*image->gamma+0.5));
8291 (void) WriteBlob(image,8,chunk);
8292 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8293 }
8294 if ((mng_info->equal_chrms == MagickFalse) &&
8295 (image->chromaticity.red_primary.x != 0.0))
8296 {
8297 PrimaryInfo
8298 primary;
8299
8300 /*
8301 Write JNG cHRM chunk
8302 */
8303 (void) WriteBlobMSBULong(image,32L);
8304 PNGType(chunk,mng_cHRM);
8305 LogPNGChunk((int) logging,mng_cHRM,32L);
8306 primary=image->chromaticity.white_point;
8307 PNGLong(chunk+4,(unsigned long) (100000*primary.x+0.5));
8308 PNGLong(chunk+8,(unsigned long) (100000*primary.y+0.5));
8309 primary=image->chromaticity.red_primary;
8310 PNGLong(chunk+12,(unsigned long) (100000*primary.x+0.5));
8311 PNGLong(chunk+16,(unsigned long) (100000*primary.y+0.5));
8312 primary=image->chromaticity.green_primary;
8313 PNGLong(chunk+20,(unsigned long) (100000*primary.x+0.5));
8314 PNGLong(chunk+24,(unsigned long) (100000*primary.y+0.5));
8315 primary=image->chromaticity.blue_primary;
8316 PNGLong(chunk+28,(unsigned long) (100000*primary.x+0.5));
8317 PNGLong(chunk+32,(unsigned long) (100000*primary.y+0.5));
8318 (void) WriteBlob(image,36,chunk);
8319 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8320 }
8321 }
8322 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8323 {
8324 /*
8325 Write JNG pHYs chunk
8326 */
8327 (void) WriteBlobMSBULong(image,9L);
8328 PNGType(chunk,mng_pHYs);
8329 LogPNGChunk((int) logging,mng_pHYs,9L);
8330 if (image->units == PixelsPerInchResolution)
8331 {
8332 PNGLong(chunk+4,(unsigned long)
8333 (image->x_resolution*100.0/2.54+0.5));
8334 PNGLong(chunk+8,(unsigned long)
8335 (image->y_resolution*100.0/2.54+0.5));
8336 chunk[12]=1;
8337 }
8338 else
8339 {
8340 if (image->units == PixelsPerCentimeterResolution)
8341 {
8342 PNGLong(chunk+4,(unsigned long)
8343 (image->x_resolution*100.0+0.5));
8344 PNGLong(chunk+8,(unsigned long)
8345 (image->y_resolution*100.0+0.5));
8346 chunk[12]=1;
8347 }
8348 else
8349 {
8350 PNGLong(chunk+4,(unsigned long) (image->x_resolution+0.5));
8351 PNGLong(chunk+8,(unsigned long) (image->y_resolution+0.5));
8352 chunk[12]=0;
8353 }
8354 }
8355 (void) WriteBlob(image,13,chunk);
8356 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8357 }
8358
8359 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8360 {
8361 /*
8362 Write JNG oFFs chunk
8363 */
8364 (void) WriteBlobMSBULong(image,9L);
8365 PNGType(chunk,mng_oFFs);
8366 LogPNGChunk((int) logging,mng_oFFs,9L);
8367 PNGsLong(chunk+4,(long) (image->page.x));
8368 PNGsLong(chunk+8,(long) (image->page.y));
8369 chunk[12]=0;
8370 (void) WriteBlob(image,13,chunk);
8371 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8372 }
8373 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8374 {
8375 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8376 PNGType(chunk,mng_vpAg);
8377 LogPNGChunk((int) logging,mng_vpAg,9L);
8378 PNGLong(chunk+4,(png_uint_32) image->page.width);
8379 PNGLong(chunk+8,(png_uint_32) image->page.height);
8380 chunk[12]=0; /* unit = pixels */
8381 (void) WriteBlob(image,13,chunk);
8382 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8383 }
8384
8385
8386 if (transparent)
8387 {
8388 if (jng_alpha_compression_method==0)
8389 {
8390 register long
8391 i;
8392
8393 long
8394 len;
8395
8396 /* Write IDAT chunk header */
8397 if (logging != MagickFalse)
8398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8399 " Write IDAT chunks from blob, length=%lu.",
8400 (unsigned long) length);
8401
8402 /* Copy IDAT chunks */
8403 len=0;
8404 p=blob+8;
8405 for (i=8; i<(long) length; i+=len+12)
8406 {
8407 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8408 p+=4;
8409 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8410 {
8411 /* Found an IDAT chunk. */
8412 (void) WriteBlobMSBULong(image,(unsigned long) len);
8413 LogPNGChunk((int) logging,mng_IDAT,(unsigned long) len);
8414 (void) WriteBlob(image,(size_t) len+4,p);
8415 (void) WriteBlobMSBULong(image,
8416 crc32(0,p,(uInt) len+4));
8417 }
8418 else
8419 {
8420 if (logging != MagickFalse)
8421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8422 " Skipping %c%c%c%c chunk, length=%lu.",
8423 *(p),*(p+1),*(p+2),*(p+3),len);
8424 }
8425 p+=(8+len);
8426 }
8427 }
8428 else
8429 {
8430 /* Write JDAA chunk header */
8431 if (logging != MagickFalse)
8432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8433 " Write JDAA chunk, length=%lu.",
8434 (unsigned long) length);
8435 (void) WriteBlobMSBULong(image,(unsigned long) length);
8436 PNGType(chunk,mng_JDAA);
8437 LogPNGChunk((int) logging,mng_JDAA,length);
8438 /* Write JDAT chunk(s) data */
8439 (void) WriteBlob(image,4,chunk);
8440 (void) WriteBlob(image,length,blob);
8441 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8442 (uInt) length));
8443 }
8444 blob=(unsigned char *) RelinquishMagickMemory(blob);
8445 }
8446
8447 /* Encode image as a JPEG blob */
8448 if (logging != MagickFalse)
8449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8450 " Creating jpeg_image_info.");
8451 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8452 if (jpeg_image_info == (ImageInfo *) NULL)
8453 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8454
8455 if (logging != MagickFalse)
8456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8457 " Creating jpeg_image.");
8458
8459 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8460 if (jpeg_image == (Image *) NULL)
8461 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8462 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8463
8464 (void) AcquireUniqueFilename(jpeg_image->filename);
8465 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8466 jpeg_image->filename);
8467
8468 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8469 &image->exception);
8470
8471 if (logging != MagickFalse)
8472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8473 " Created jpeg_image, %lu x %lu.",jpeg_image->columns,
8474 jpeg_image->rows);
8475
8476 if (jng_color_type == 8 || jng_color_type == 12)
8477 jpeg_image_info->type=GrayscaleType;
8478 jpeg_image_info->quality=jng_quality % 1000;
8479 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8480 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8481 if (logging != MagickFalse)
8482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8483 " Creating blob.");
8484 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8485 if (logging != MagickFalse)
8486 {
8487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8488 " Successfully read jpeg_image into a blob, length=%lu.",
8489 (unsigned long) length);
8490
8491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8492 " Write JDAT chunk, length=%lu.",
8493 (unsigned long) length);
8494 }
8495 /* Write JDAT chunk(s) */
8496 (void) WriteBlobMSBULong(image,(unsigned long) length);
8497 PNGType(chunk,mng_JDAT);
8498 LogPNGChunk((int) logging,mng_JDAT,length);
8499 (void) WriteBlob(image,4,chunk);
8500 (void) WriteBlob(image,length,blob);
8501 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8502
8503 jpeg_image=DestroyImage(jpeg_image);
8504 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8505 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8506 blob=(unsigned char *) RelinquishMagickMemory(blob);
8507
8508 /* Write any JNG-chunk-e profiles */
8509 (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8510
8511 /* Write IEND chunk */
8512 (void) WriteBlobMSBULong(image,0L);
8513 PNGType(chunk,mng_IEND);
8514 LogPNGChunk((int) logging,mng_IEND,0);
8515 (void) WriteBlob(image,4,chunk);
8516 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8517
8518 if (logging != MagickFalse)
8519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8520 " exit WriteOneJNGImage()");
8521 return(status);
8522}
8523
8524
8525/*
8526%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8527% %
8528% %
8529% %
8530% W r i t e J N G I m a g e %
8531% %
8532% %
8533% %
8534%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8535%
8536% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8537%
8538% JNG support written by Glenn Randers-Pehrson, glennrp@image...
8539%
8540% The format of the WriteJNGImage method is:
8541%
8542% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8543%
8544% A description of each parameter follows:
8545%
8546% o image_info: the image info.
8547%
8548% o image: The image.
8549%
8550%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8551*/
8552static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8553{
8554 MagickBooleanType
8555 status;
8556
8557 MngInfo
8558 *mng_info;
8559
8560 int
8561 have_mng_structure;
8562
8563 unsigned int
8564 logging;
8565
8566 /*
8567 Open image file.
8568 */
8569 assert(image_info != (const ImageInfo *) NULL);
8570 assert(image_info->signature == MagickSignature);
8571 assert(image != (Image *) NULL);
8572 assert(image->signature == MagickSignature);
8573 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8574 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8575 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8576 if (status == MagickFalse)
8577 return(status);
8578
8579 /*
8580 Allocate a MngInfo structure.
8581 */
8582 have_mng_structure=MagickFalse;
8583 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
8584 if (mng_info == (MngInfo *) NULL)
8585 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8586 /*
8587 Initialize members of the MngInfo structure.
8588 */
8589 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8590 mng_info->image=image;
8591 have_mng_structure=MagickTrue;
8592
8593 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8594
8595 status=WriteOneJNGImage(mng_info,image_info,image);
8596 (void) CloseBlob(image);
8597
8598 (void) CatchImageException(image);
8599 MngInfoFreeStruct(mng_info,&have_mng_structure);
8600 if (logging != MagickFalse)
8601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8602 return(status);
8603}
8604#endif
8605
8606
8607
8608static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8609{
8610 const char
8611 *option;
8612
8613 Image
8614 *next_image;
8615
8616 MagickBooleanType
8617 status;
8618
8619 MngInfo
8620 *mng_info;
8621
8622 int
8623 have_mng_structure,
8624 image_count,
8625 need_iterations,
8626 need_matte;
8627
8628 volatile int
8629#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8630 defined(PNG_MNG_FEATURES_SUPPORTED)
8631 need_local_plte,
8632#endif
8633 all_images_are_gray,
8634 logging,
8635 need_defi,
8636 optimize,
8637 use_global_plte;
8638
8639 register long
8640 i;
8641
8642 unsigned char
8643 chunk[800];
8644
8645 volatile unsigned int
8646 write_jng,
8647 write_mng;
8648
8649 volatile unsigned long
8650 scene;
8651
8652 unsigned long
8653 final_delay=0,
8654 initial_delay;
8655
8656#if (PNG_LIBPNG_VER < 10007)
8657 if (image_info->verbose)
8658 printf("Your PNG library (libpng-%s) is rather old.\n",
8659 PNG_LIBPNG_VER_STRING);
8660#endif
8661
8662 /*
8663 Open image file.
8664 */
8665 assert(image_info != (const ImageInfo *) NULL);
8666 assert(image_info->signature == MagickSignature);
8667 assert(image != (Image *) NULL);
8668 assert(image->signature == MagickSignature);
8669 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8670 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8671 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8672 if (status == MagickFalse)
8673 return(status);
8674
8675 /*
8676 Allocate a MngInfo structure.
8677 */
8678 have_mng_structure=MagickFalse;
8679 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
8680 if (mng_info == (MngInfo *) NULL)
8681 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8682 /*
8683 Initialize members of the MngInfo structure.
8684 */
8685 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8686 mng_info->image=image;
8687 have_mng_structure=MagickTrue;
8688 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
8689
8690 /*
8691 * See if user has requested a specific PNG subformat to be used
8692 * for all of the PNGs in the MNG being written, e.g.,
8693 *
8694 * convert *.png png8:animation.mng
8695 *
8696 * To do: check -define png:bit_depth and png:color_type as well,
8697 * or perhaps use mng:bit_depth and mng:color_type instead for
8698 * global settings.
8699 */
8700
8701 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8702 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8703 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8704
8705 write_jng=MagickFalse;
8706 if (image_info->compression == JPEGCompression)
8707 write_jng=MagickTrue;
8708
8709 mng_info->adjoin=image_info->adjoin &&
8710 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
8711
8712 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8713 optimize=MagickFalse;
8714 else
8715 optimize=(image_info->type == OptimizeType || image_info->type ==
8716 UndefinedType);
8717
8718 if (logging != MagickFalse)
8719 {
8720 /* Log some info about the input */
8721 Image
8722 *p;
8723
8724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8725 " Checking input image(s)");
8726 if (optimize)
8727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8728 " Optimize: TRUE");
8729 else
8730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8731 " Optimize: FALSE");
8732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8733 " Image_info depth: %ld",image_info->depth);
8734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8735 " Type: %d",image_info->type);
8736
8737 scene=0;
8738 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8739 {
8740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8741 " Scene: %ld",scene++);
8742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8743 " Image depth: %lu",p->depth);
8744 if (p->matte)
8745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8746 " Matte: True");
8747 else
8748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8749 " Matte: False");
8750 if (p->storage_class == PseudoClass)
8751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8752 " Storage class: PseudoClass");
8753 else
8754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8755 " Storage class: DirectClass");
8756 if (p->colors)
8757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8758 " Number of colors: %lu",p->colors);
8759 else
8760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8761 " Number of colors: unspecified");
8762 if (mng_info->adjoin == MagickFalse)
8763 break;
8764 }
8765 }
8766
8767 /*
8768 Sometimes we get PseudoClass images whose RGB values don't match
8769 the colors in the colormap. This code syncs the RGB values.
8770 */
8771 {
8772 Image
8773 *p;
8774
8775 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8776 {
8777 if (p->taint && p->storage_class == PseudoClass)
8778 (void) SyncImage(p);
8779 if (mng_info->adjoin == MagickFalse)
8780 break;
8781 }
8782 }
8783
8784#ifdef PNG_BUILD_PALETTE
8785 if (optimize)
8786 {
8787 /*
8788 Sometimes we get DirectClass images that have 256 colors or fewer.
8789 This code will convert them to PseudoClass and build a colormap.
8790 */
8791 Image
8792 *p;
8793
8794 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8795 {
8796 if (p->storage_class != PseudoClass)
8797 {
8798 p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
8799 if (p->colors <= 256)
8800 {
8801 p->colors=0;
8802 if (p->matte != MagickFalse)
8803 (void) SetImageType(p,PaletteMatteType);
8804 else
8805 (void) SetImageType(p,PaletteType);
8806 }
8807 }
8808 if (mng_info->adjoin == MagickFalse)
8809 break;
8810 }
8811 }
8812#endif
8813
8814 use_global_plte=MagickFalse;
8815 all_images_are_gray=MagickFalse;
8816#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8817 need_local_plte=MagickTrue;
8818#endif
8819 need_defi=MagickFalse;
8820 need_matte=MagickFalse;
8821 mng_info->framing_mode=1;
8822 mng_info->old_framing_mode=1;
8823
8824 if (write_mng)
8825 if (image_info->page != (char *) NULL)
8826 {
8827 /*
8828 Determine image bounding box.
8829 */
8830 SetGeometry(image,&mng_info->page);
8831 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
8832 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
8833 }
8834 if (write_mng)
8835 {
8836 unsigned int
8837 need_geom;
8838
8839 unsigned short
8840 red,
8841 green,
8842 blue;
8843
8844 mng_info->page=image->page;
8845 need_geom=MagickTrue;
8846 if (mng_info->page.width || mng_info->page.height)
8847 need_geom=MagickFalse;
8848 /*
8849 Check all the scenes.
8850 */
8851 initial_delay=image->delay;
8852 need_iterations=MagickFalse;
8853 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
8854 mng_info->equal_physs=MagickTrue,
8855 mng_info->equal_gammas=MagickTrue;
8856 mng_info->equal_srgbs=MagickTrue;
8857 mng_info->equal_backgrounds=MagickTrue;
8858 image_count=0;
8859#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8860 defined(PNG_MNG_FEATURES_SUPPORTED)
8861 all_images_are_gray=MagickTrue;
8862 mng_info->equal_palettes=MagickFalse;
8863 need_local_plte=MagickFalse;
8864#endif
8865 for (next_image=image; next_image != (Image *) NULL; )
8866 {
8867 if (need_geom)
8868 {
8869 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
8870 mng_info->page.width=next_image->columns+next_image->page.x;
8871 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
8872 mng_info->page.height=next_image->rows+next_image->page.y;
8873 }
8874 if (next_image->page.x || next_image->page.y)
8875 need_defi=MagickTrue;
8876 if (next_image->matte)
8877 need_matte=MagickTrue;
8878 if ((int) next_image->dispose >= BackgroundDispose)
8879 if (next_image->matte || next_image->page.x || next_image->page.y ||
8880 ((next_image->columns < mng_info->page.width) &&
8881 (next_image->rows < mng_info->page.height)))
8882 mng_info->need_fram=MagickTrue;
8883 if (next_image->iterations)
8884 need_iterations=MagickTrue;
8885 final_delay=next_image->delay;
8886 if (final_delay != initial_delay || final_delay > 1UL*
8887 next_image->ticks_per_second)
8888 mng_info->need_fram=1;
8889#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8890 defined(PNG_MNG_FEATURES_SUPPORTED)
8891 /*
8892 check for global palette possibility.
8893 */
8894 if (image->matte != MagickFalse)
8895 need_local_plte=MagickTrue;
8896 if (need_local_plte == 0)
8897 {
8898 if (ImageIsGray(image) == MagickFalse)
8899 all_images_are_gray=MagickFalse;
8900 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
8901 if (use_global_plte == 0)
8902 use_global_plte=mng_info->equal_palettes;
8903 need_local_plte=!mng_info->equal_palettes;
8904 }
8905#endif
8906 if (GetNextImageInList(next_image) != (Image *) NULL)
8907 {
8908 if (next_image->background_color.red !=
8909 next_image->next->background_color.red ||
8910 next_image->background_color.green !=
8911 next_image->next->background_color.green ||
8912 next_image->background_color.blue !=
8913 next_image->next->background_color.blue)
8914 mng_info->equal_backgrounds=MagickFalse;
8915 if (next_image->gamma != next_image->next->gamma)
8916 mng_info->equal_gammas=MagickFalse;
8917 if (next_image->rendering_intent !=
8918 next_image->next->rendering_intent)
8919 mng_info->equal_srgbs=MagickFalse;
8920 if ((next_image->units != next_image->next->units) ||
8921 (next_image->x_resolution != next_image->next->x_resolution) ||
8922 (next_image->y_resolution != next_image->next->y_resolution))
8923 mng_info->equal_physs=MagickFalse;
8924 if (mng_info->equal_chrms)
8925 {
8926 if (next_image->chromaticity.red_primary.x !=
8927 next_image->next->chromaticity.red_primary.x ||
8928 next_image->chromaticity.red_primary.y !=
8929 next_image->next->chromaticity.red_primary.y ||
8930 next_image->chromaticity.green_primary.x !=
8931 next_image->next->chromaticity.green_primary.x ||
8932 next_image->chromaticity.green_primary.y !=
8933 next_image->next->chromaticity.green_primary.y ||
8934 next_image->chromaticity.blue_primary.x !=
8935 next_image->next->chromaticity.blue_primary.x ||
8936 next_image->chromaticity.blue_primary.y !=
8937 next_image->next->chromaticity.blue_primary.y ||
8938 next_image->chromaticity.white_point.x !=
8939 next_image->next->chromaticity.white_point.x ||
8940 next_image->chromaticity.white_point.y !=
8941 next_image->next->chromaticity.white_point.y)
8942 mng_info->equal_chrms=MagickFalse;
8943 }
8944 }
8945 image_count++;
8946 next_image=GetNextImageInList(next_image);
8947 }
8948 if (image_count < 2)
8949 {
8950 mng_info->equal_backgrounds=MagickFalse;
8951 mng_info->equal_chrms=MagickFalse;
8952 mng_info->equal_gammas=MagickFalse;
8953 mng_info->equal_srgbs=MagickFalse;
8954 mng_info->equal_physs=MagickFalse;
8955 use_global_plte=MagickFalse;
8956#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8957 need_local_plte=MagickTrue;
8958#endif
8959 need_iterations=MagickFalse;
8960 }
8961 if (mng_info->need_fram == MagickFalse)
8962 {
8963 /*
8964 Only certain framing rates 100/n are exactly representable without
8965 the FRAM chunk but we'll allow some slop in VLC files
8966 */
8967 if (final_delay == 0)
8968 {
8969 if (need_iterations != MagickFalse)
8970 {
8971 /*
8972 It's probably a GIF with loop; don't run it *too* fast.
8973 */
8974 final_delay=10;
8975 (void) ThrowMagickException(&image->exception,
8976 GetMagickModule(),CoderError,
8977 "input has zero delay between all frames; assuming 10 cs",
8978 "`%s'","");
8979 }
8980 else
8981 mng_info->ticks_per_second=0;
8982 }
8983 if (final_delay != 0)
8984 mng_info->ticks_per_second=1UL*image->ticks_per_second/final_delay;
8985 if (final_delay > 50)
8986 mng_info->ticks_per_second=2;
8987 if (final_delay > 75)
8988 mng_info->ticks_per_second=1;
8989 if (final_delay > 125)
8990 mng_info->need_fram=MagickTrue;
8991 if (need_defi && final_delay > 2 && (final_delay != 4) &&
8992 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
8993 (final_delay != 25) && (final_delay != 50) && (final_delay !=
8994 1UL*image->ticks_per_second))
8995 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
8996 }
8997 if (mng_info->need_fram != MagickFalse)
8998 mng_info->ticks_per_second=1UL*image->ticks_per_second;
8999 /*
9000 If pseudocolor, we should also check to see if all the
9001 palettes are identical and write a global PLTE if they are.
9002 ../glennrp Feb 99.
9003 */
9004 /*
9005 Write the MNG version 1.0 signature and MHDR chunk.
9006 */
9007 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9008 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
9009 PNGType(chunk,mng_MHDR);
9010 LogPNGChunk((int) logging,mng_MHDR,28L);
9011 PNGLong(chunk+4,mng_info->page.width);
9012 PNGLong(chunk+8,mng_info->page.height);
9013 PNGLong(chunk+12,mng_info->ticks_per_second);
9014 PNGLong(chunk+16,0L); /* layer count=unknown */
9015 PNGLong(chunk+20,0L); /* frame count=unknown */
9016 PNGLong(chunk+24,0L); /* play time=unknown */
9017 if (write_jng)
9018 {
9019 if (need_matte)
9020 {
9021 if (need_defi || mng_info->need_fram || use_global_plte)
9022 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
9023 else
9024 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
9025 }
9026 else
9027 {
9028 if (need_defi || mng_info->need_fram || use_global_plte)
9029 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
9030 else
9031 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
9032 }
9033 }
9034 else
9035 {
9036 if (need_matte)
9037 {
9038 if (need_defi || mng_info->need_fram || use_global_plte)
9039 PNGLong(chunk+28,11L); /* simplicity=LC */
9040 else
9041 PNGLong(chunk+28,9L); /* simplicity=VLC */
9042 }
9043 else
9044 {
9045 if (need_defi || mng_info->need_fram || use_global_plte)
9046 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
9047 else
9048 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
9049 }
9050 }
9051 (void) WriteBlob(image,32,chunk);
9052 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9053 option=GetImageOption(image_info,"mng:need-cacheoff");
9054 if (option != (const char *) NULL)
9055 {
9056 size_t
9057 length;
9058
9059 /*
9060 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9061 */
9062 PNGType(chunk,mng_nEED);
9063 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
9064 (void) WriteBlobMSBULong(image,(unsigned long) length);
9065 LogPNGChunk((int) logging,mng_nEED,(unsigned long) length);
9066 length+=4;
9067 (void) WriteBlob(image,length,chunk);
9068 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9069 }
9070 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9071 (GetNextImageInList(image) != (Image *) NULL) &&
9072 (image->iterations != 1))
9073 {
9074 /*
9075 Write MNG TERM chunk
9076 */
9077 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9078 PNGType(chunk,mng_TERM);
9079 LogPNGChunk((int) logging,mng_TERM,10L);
9080 chunk[4]=3; /* repeat animation */
9081 chunk[5]=0; /* show last frame when done */
9082 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9083 final_delay/MagickMax(image->ticks_per_second,1)));
9084 if (image->iterations == 0)
9085 PNGLong(chunk+10,PNG_UINT_31_MAX);
9086 else
9087 PNGLong(chunk+10,(png_uint_32) image->iterations);
9088 if (logging != MagickFalse)
9089 {
9090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9091 " TERM delay: %lu",
9092 (png_uint_32) (mng_info->ticks_per_second*
9093 final_delay/MagickMax(image->ticks_per_second,1)));
9094 if (image->iterations == 0)
9095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9096 " TERM iterations: %lu",PNG_UINT_31_MAX);
9097 else
9098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9099 " Image iterations: %lu",image->iterations);
9100 }
9101 (void) WriteBlob(image,14,chunk);
9102 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9103 }
9104 /*
9105 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9106 */
9107 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9108 mng_info->equal_srgbs)
9109 {
9110 /*
9111 Write MNG sRGB chunk
9112 */
9113 (void) WriteBlobMSBULong(image,1L);
9114 PNGType(chunk,mng_sRGB);
9115 LogPNGChunk((int) logging,mng_sRGB,1L);
9116 if (image->rendering_intent != UndefinedIntent)
9117 chunk[4]=(unsigned char) (image->rendering_intent-1);
9118 else
9119 chunk[4]=(unsigned char) (PerceptualIntent-1);
9120 (void) WriteBlob(image,5,chunk);
9121 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9122 mng_info->have_write_global_srgb=MagickTrue;
9123 }
9124 else
9125 {
9126 if (image->gamma && mng_info->equal_gammas)
9127 {
9128 /*
9129 Write MNG gAMA chunk
9130 */
9131 (void) WriteBlobMSBULong(image,4L);
9132 PNGType(chunk,mng_gAMA);
9133 LogPNGChunk((int) logging,mng_gAMA,4L);
9134 PNGLong(chunk+4,(unsigned long) (100000*image->gamma+0.5));
9135 (void) WriteBlob(image,8,chunk);
9136 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9137 mng_info->have_write_global_gama=MagickTrue;
9138 }
9139 if (mng_info->equal_chrms)
9140 {
9141 PrimaryInfo
9142 primary;
9143
9144 /*
9145 Write MNG cHRM chunk
9146 */
9147 (void) WriteBlobMSBULong(image,32L);
9148 PNGType(chunk,mng_cHRM);
9149 LogPNGChunk((int) logging,mng_cHRM,32L);
9150 primary=image->chromaticity.white_point;
9151 PNGLong(chunk+4,(unsigned long) (100000*primary.x+0.5));
9152 PNGLong(chunk+8,(unsigned long) (100000*primary.y+0.5));
9153 primary=image->chromaticity.red_primary;
9154 PNGLong(chunk+12,(unsigned long) (100000*primary.x+0.5));
9155 PNGLong(chunk+16,(unsigned long) (100000*primary.y+0.5));
9156 primary=image->chromaticity.green_primary;
9157 PNGLong(chunk+20,(unsigned long) (100000*primary.x+0.5));
9158 PNGLong(chunk+24,(unsigned long) (100000*primary.y+0.5));
9159 primary=image->chromaticity.blue_primary;
9160 PNGLong(chunk+28,(unsigned long) (100000*primary.x+0.5));
9161 PNGLong(chunk+32,(unsigned long) (100000*primary.y+0.5));
9162 (void) WriteBlob(image,36,chunk);
9163 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9164 mng_info->have_write_global_chrm=MagickTrue;
9165 }
9166 }
9167 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9168 {
9169 /*
9170 Write MNG pHYs chunk
9171 */
9172 (void) WriteBlobMSBULong(image,9L);
9173 PNGType(chunk,mng_pHYs);
9174 LogPNGChunk((int) logging,mng_pHYs,9L);
9175 if (image->units == PixelsPerInchResolution)
9176 {
9177 PNGLong(chunk+4,(unsigned long)
9178 (image->x_resolution*100.0/2.54+0.5));
9179 PNGLong(chunk+8,(unsigned long)
9180 (image->y_resolution*100.0/2.54+0.5));
9181 chunk[12]=1;
9182 }
9183 else
9184 {
9185 if (image->units == PixelsPerCentimeterResolution)
9186 {
9187 PNGLong(chunk+4,(unsigned long)
9188 (image->x_resolution*100.0+0.5));
9189 PNGLong(chunk+8,(unsigned long)
9190 (image->y_resolution*100.0+0.5));
9191 chunk[12]=1;
9192 }
9193 else
9194 {
9195 PNGLong(chunk+4,(unsigned long) (image->x_resolution+0.5));
9196 PNGLong(chunk+8,(unsigned long) (image->y_resolution+0.5));
9197 chunk[12]=0;
9198 }
9199 }
9200 (void) WriteBlob(image,13,chunk);
9201 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9202 }
9203 /*
9204 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9205 or does not cover the entire frame.
9206 */
9207 if (write_mng && (image->matte || image->page.x > 0 ||
9208 image->page.y > 0 || (image->page.width &&
9209 (image->page.width+image->page.x < mng_info->page.width))
9210 || (image->page.height && (image->page.height+image->page.y
9211 < mng_info->page.height))))
9212 {
9213 (void) WriteBlobMSBULong(image,6L);
9214 PNGType(chunk,mng_BACK);
9215 LogPNGChunk((int) logging,mng_BACK,6L);
9216 red=ScaleQuantumToShort(image->background_color.red);
9217 green=ScaleQuantumToShort(image->background_color.green);
9218 blue=ScaleQuantumToShort(image->background_color.blue);
9219 PNGShort(chunk+4,red);
9220 PNGShort(chunk+6,green);
9221 PNGShort(chunk+8,blue);
9222 (void) WriteBlob(image,10,chunk);
9223 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9224 if (mng_info->equal_backgrounds)
9225 {
9226 (void) WriteBlobMSBULong(image,6L);
9227 PNGType(chunk,mng_bKGD);
9228 LogPNGChunk((int) logging,mng_bKGD,6L);
9229 (void) WriteBlob(image,10,chunk);
9230 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9231 }
9232 }
9233
9234#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9235 if ((need_local_plte == MagickFalse) &&
9236 (image->storage_class == PseudoClass) &&
9237 (all_images_are_gray == MagickFalse))
9238 {
9239 unsigned long
9240 data_length;
9241
9242 /*
9243 Write MNG PLTE chunk
9244 */
9245 data_length=3*image->colors;
9246 (void) WriteBlobMSBULong(image,data_length);
9247 PNGType(chunk,mng_PLTE);
9248 LogPNGChunk((int) logging,mng_PLTE,data_length);
9249 for (i=0; i < (long) image->colors; i++)
9250 {
9251 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9252 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9253 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9254 }
9255 (void) WriteBlob(image,data_length+4,chunk);
9256 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9257 mng_info->have_write_global_plte=MagickTrue;
9258 }
9259#endif
9260 }
9261 scene=0;
9262 mng_info->delay=0;
9263#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9264 defined(PNG_MNG_FEATURES_SUPPORTED)
9265 mng_info->equal_palettes=MagickFalse;
9266#endif
9267 do
9268 {
9269 if (mng_info->adjoin)
9270 {
9271#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9272 defined(PNG_MNG_FEATURES_SUPPORTED)
9273 /*
9274 If we aren't using a global palette for the entire MNG, check to
9275 see if we can use one for two or more consecutive images.
9276 */
9277 if (need_local_plte && use_global_plte && !all_images_are_gray)
9278 {
9279 if (mng_info->IsPalette)
9280 {
9281 /*
9282 When equal_palettes is true, this image has the same palette
9283 as the previous PseudoClass image
9284 */
9285 mng_info->have_write_global_plte=mng_info->equal_palettes;
9286 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9287 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9288 {
9289 /*
9290 Write MNG PLTE chunk
9291 */
9292 unsigned long
9293 data_length;
9294
9295 data_length=3*image->colors;
9296 (void) WriteBlobMSBULong(image,data_length);
9297 PNGType(chunk,mng_PLTE);
9298 LogPNGChunk((int) logging,mng_PLTE,data_length);
9299 for (i=0; i < (long) image->colors; i++)
9300 {
9301 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9302 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9303 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9304 }
9305 (void) WriteBlob(image,data_length+4,chunk);
9306 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9307 (uInt) (data_length+4)));
9308 mng_info->have_write_global_plte=MagickTrue;
9309 }
9310 }
9311 else
9312 mng_info->have_write_global_plte=MagickFalse;
9313 }
9314#endif
9315 if (need_defi)
9316 {
9317 long
9318 previous_x,
9319 previous_y;
9320
9321 if (scene)
9322 {
9323 previous_x=mng_info->page.x;
9324 previous_y=mng_info->page.y;
9325 }
9326 else
9327 {
9328 previous_x=0;
9329 previous_y=0;
9330 }
9331 mng_info->page=image->page;
9332 if ((mng_info->page.x != previous_x) ||
9333 (mng_info->page.y != previous_y))
9334 {
9335 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
9336 PNGType(chunk,mng_DEFI);
9337 LogPNGChunk((int) logging,mng_DEFI,12L);
9338 chunk[4]=0; /* object 0 MSB */
9339 chunk[5]=0; /* object 0 LSB */
9340 chunk[6]=0; /* visible */
9341 chunk[7]=0; /* abstract */
9342 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9343 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9344 (void) WriteBlob(image,16,chunk);
9345 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9346 }
9347 }
9348 }
9349
9350 mng_info->write_mng=write_mng;
9351
9352 if ((int) image->dispose >= 3)
9353 mng_info->framing_mode=3;
9354
9355 if (mng_info->need_fram && mng_info->adjoin &&
9356 ((image->delay != mng_info->delay) ||
9357 (mng_info->framing_mode != mng_info->old_framing_mode)))
9358 {
9359 if (image->delay == mng_info->delay)
9360 {
9361 /*
9362 Write a MNG FRAM chunk with the new framing mode.
9363 */
9364 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
9365 PNGType(chunk,mng_FRAM);
9366 LogPNGChunk((int) logging,mng_FRAM,1L);
9367 chunk[4]=(unsigned char) mng_info->framing_mode;
9368 (void) WriteBlob(image,5,chunk);
9369 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9370 }
9371 else
9372 {
9373 /*
9374 Write a MNG FRAM chunk with the delay.
9375 */
9376 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9377 PNGType(chunk,mng_FRAM);
9378 LogPNGChunk((int) logging,mng_FRAM,10L);
9379 chunk[4]=(unsigned char) mng_info->framing_mode;
9380 chunk[5]=0; /* frame name separator (no name) */
9381 chunk[6]=2; /* flag for changing default delay */
9382 chunk[7]=0; /* flag for changing frame timeout */
9383 chunk[8]=0; /* flag for changing frame clipping */
9384 chunk[9]=0; /* flag for changing frame sync_id */
9385 PNGLong(chunk+10,(png_uint_32)
9386 ((mng_info->ticks_per_second*
9387 image->delay)/MagickMax(image->ticks_per_second,1)));
9388 (void) WriteBlob(image,14,chunk);
9389 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9390 mng_info->delay=image->delay;
9391 }
9392 mng_info->old_framing_mode=mng_info->framing_mode;
9393 }
9394
9395#if defined(JNG_SUPPORTED)
9396 if (image_info->compression == JPEGCompression)
9397 {
9398 ImageInfo
9399 *write_info;
9400
9401 if (logging != MagickFalse)
9402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9403 " Writing JNG object.");
9404 /* To do: specify the desired alpha compression method. */
9405 write_info=CloneImageInfo(image_info);
9406 write_info->compression=UndefinedCompression;
9407 status=WriteOneJNGImage(mng_info,write_info,image);
9408 write_info=DestroyImageInfo(write_info);
9409 }
9410 else
9411#endif
9412 {
9413 if (logging != MagickFalse)
9414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9415 " Writing PNG object.");
9416 status=WriteOnePNGImage(mng_info,image_info,image);
9417 }
9418
9419 if (status == MagickFalse)
9420 {
9421 MngInfoFreeStruct(mng_info,&have_mng_structure);
9422 (void) CloseBlob(image);
9423 return(MagickFalse);
9424 }
9425 (void) CatchImageException(image);
9426 if (GetNextImageInList(image) == (Image *) NULL)
9427 break;
9428 image=SyncNextImageInList(image);
9429 status=SetImageProgress(image,SaveImagesTag,scene++,
9430 GetImageListLength(image));
9431 if (status == MagickFalse)
9432 break;
9433 } while (mng_info->adjoin);
9434 if (write_mng)
9435 {
9436 while (GetPreviousImageInList(image) != (Image *) NULL)
9437 image=GetPreviousImageInList(image);
9438 /*
9439 Write the MEND chunk.
9440 */
9441 (void) WriteBlobMSBULong(image,0x00000000L);
9442 PNGType(chunk,mng_MEND);
9443 LogPNGChunk((int) logging,mng_MEND,0L);
9444 (void) WriteBlob(image,4,chunk);
9445 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9446 }
9447 /*
9448 Relinquish resources.
9449 */
9450 (void) CloseBlob(image);
9451 MngInfoFreeStruct(mng_info,&have_mng_structure);
9452 if (logging != MagickFalse)
9453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9454 return(MagickTrue);
9455}
9456#else /* PNG_LIBPNG_VER > 95 */
9457static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9458{
9459 image=image;
9460 printf("Your PNG library is too old: You have libpng-%s\n",
9461 PNG_LIBPNG_VER_STRING);
9462 ThrowBinaryException(CoderError,"PNG library is too old",
9463 image_info->filename);
9464}
9465static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9466{
9467 return(WritePNGImage(image_info,image));
9468}
9469#endif /* PNG_LIBPNG_VER > 95 */
9470#endif