blob: 515676e5db05bb6f5f7feae2fd1aacf875563a87 [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)
cristy18b17442009-10-25 18:36:48 +00001706 LockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001707#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)
cristy18b17442009-10-25 18:36:48 +00001751 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00001752#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)
cristy18b17442009-10-25 18:36:48 +00002251 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002252#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)
cristy18b17442009-10-25 18:36:48 +00002282 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002283#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)
cristy18b17442009-10-25 18:36:48 +00002638 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002639#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)
cristy18b17442009-10-25 18:36:48 +00002843 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002844#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);
cristy18b17442009-10-25 18:36:48 +00005947#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
5948 png_semaphore=AllocateSemaphoreInfo();
5949#endif
cristy3ed852e2009-09-05 21:47:34 +00005950 return(MagickImageCoderSignature);
5951}
5952
5953/*
5954%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5955% %
5956% %
5957% %
5958% U n r e g i s t e r P N G I m a g e %
5959% %
5960% %
5961% %
5962%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5963%
5964% UnregisterPNGImage() removes format registrations made by the
5965% PNG module from the list of supported formats.
5966%
5967% The format of the UnregisterPNGImage method is:
5968%
5969% UnregisterPNGImage(void)
5970%
5971*/
5972ModuleExport void UnregisterPNGImage(void)
5973{
5974 (void) UnregisterMagickInfo("MNG");
5975 (void) UnregisterMagickInfo("PNG");
5976 (void) UnregisterMagickInfo("PNG8");
5977 (void) UnregisterMagickInfo("PNG24");
5978 (void) UnregisterMagickInfo("PNG32");
5979 (void) UnregisterMagickInfo("JNG");
5980#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristy3ed852e2009-09-05 21:47:34 +00005981 DestroySemaphoreInfo(&png_semaphore);
5982#endif
5983}
5984
5985#if defined(MAGICKCORE_PNG_DELEGATE)
5986#if PNG_LIBPNG_VER > 95
5987/*
5988%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5989% %
5990% %
5991% %
5992% W r i t e M N G I m a g e %
5993% %
5994% %
5995% %
5996%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5997%
5998% WriteMNGImage() writes an image in the Portable Network Graphics
5999% Group's "Multiple-image Network Graphics" encoded image format.
6000%
6001% MNG support written by Glenn Randers-Pehrson, glennrp@image...
6002%
6003% The format of the WriteMNGImage method is:
6004%
6005% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6006%
6007% A description of each parameter follows.
6008%
6009% o image_info: the image info.
6010%
6011% o image: The image.
6012%
6013%
6014% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6015% "To do" under ReadPNGImage):
6016%
6017% Fix problem with palette sorting (when PNG_SORT_PALETTE is enabled,
6018% some GIF animations don't convert properly)
6019%
6020% Preserve all unknown and not-yet-handled known chunks found in input
6021% PNG file and copy them into output PNG files according to the PNG
6022% copying rules.
6023%
6024% Write the iCCP chunk at MNG level when (icc profile length > 0)
6025%
6026% Improve selection of color type (use indexed-colour or indexed-colour
6027% with tRNS when 256 or fewer unique RGBA values are present).
6028%
6029% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6030% This will be complicated if we limit ourselves to generating MNG-LC
6031% files. For now we ignore disposal method 3 and simply overlay the next
6032% image on it.
6033%
6034% Check for identical PLTE's or PLTE/tRNS combinations and use a
6035% global MNG PLTE or PLTE/tRNS combination when appropriate.
6036% [mostly done 15 June 1999 but still need to take care of tRNS]
6037%
6038% Check for identical sRGB and replace with a global sRGB (and remove
6039% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6040% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6041% local gAMA/cHRM with local sRGB if appropriate).
6042%
6043% Check for identical sBIT chunks and write global ones.
6044%
6045% Provide option to skip writing the signature tEXt chunks.
6046%
6047% Use signatures to detect identical objects and reuse the first
6048% instance of such objects instead of writing duplicate objects.
6049%
6050% Use a smaller-than-32k value of compression window size when
6051% appropriate.
6052%
6053% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6054% ancillary text chunks and save profiles.
6055%
6056% Provide an option to force LC files (to ensure exact framing rate)
6057% instead of VLC.
6058%
6059% Provide an option to force VLC files instead of LC, even when offsets
6060% are present. This will involve expanding the embedded images with a
6061% transparent region at the top and/or left.
6062*/
6063
6064#if (PNG_LIBPNG_VER > 99 && PNG_LIBPNG_VER < 10007)
6065/* This function became available in libpng version 1.0.6g. */
6066static void
6067png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size)
6068{
6069 if (png_ptr->zbuf)
6070 png_free(png_ptr, png_ptr->zbuf); png_ptr->zbuf=NULL;
6071 png_ptr->zbuf_size=(png_size_t) size;
6072 png_ptr->zbuf=(png_bytep) png_malloc(png_ptr, size);
6073 if (png_ptr->zbuf == 0)
6074 png_error(png_ptr,"Unable to allocate zbuf");
6075}
6076#endif
6077
6078static void
6079png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6080 png_info *ping_info, unsigned char *profile_type, unsigned char
6081 *profile_description, unsigned char *profile_data, png_uint_32 length)
6082{
6083#if (PNG_LIBPNG_VER > 10005)
6084 png_textp
6085 text;
6086
6087 register long
6088 i;
6089
6090 unsigned char
6091 *sp;
6092
6093 png_charp
6094 dp;
6095
6096 png_uint_32
6097 allocated_length,
6098 description_length;
6099
6100 unsigned char
6101 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6102#endif
6103
6104#if (PNG_LIBPNG_VER <= 10005)
6105 if (image_info->verbose)
6106 (void) printf("Not ");
6107 image_info=image_info;
6108 ping=ping;
6109 ping_info=ping_info;
6110 profile_type=profile_type;
6111 profile_description=profile_description;
6112 profile_data=profile_data;
6113 length=length;
6114#endif
6115 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6116 return;
6117
6118 if (image_info->verbose)
6119 {
6120 (void) printf("writing raw profile: type=%s, length=%lu\n",
6121 (char *) profile_type, length);
6122 }
6123#if (PNG_LIBPNG_VER > 10005)
6124 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6125 description_length=(png_uint_32) strlen((const char *) profile_description);
6126 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6127 + description_length);
6128 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6129 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6130 text[0].key[0]='\0';
6131 (void) ConcatenateMagickString(text[0].key,
6132 "Raw profile type ",MaxTextExtent);
6133 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6134 sp=profile_data;
6135 dp=text[0].text;
6136 *dp++='\n';
6137 (void) CopyMagickString(dp,(const char *) profile_description,
6138 allocated_length);
6139 dp+=description_length;
6140 *dp++='\n';
6141 (void) FormatMagickString(dp,allocated_length-
6142 (png_size_t) (dp-text[0].text),"%8lu ",length);
6143 dp+=8;
6144 for (i=0; i < (long) length; i++)
6145 {
6146 if (i%36 == 0)
6147 *dp++='\n';
6148 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6149 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6150 }
6151 *dp++='\n';
6152 *dp='\0';
6153 text[0].text_length=(png_size_t) (dp-text[0].text);
6154 text[0].compression=image_info->compression == NoCompression ||
6155 (image_info->compression == UndefinedCompression &&
6156 text[0].text_length < 128) ? -1 : 0;
6157 if (text[0].text_length <= allocated_length)
6158 png_set_text(ping,ping_info,text,1);
6159 png_free(ping,text[0].text);
6160 png_free(ping,text[0].key);
6161 png_free(ping,text);
6162#endif
6163}
6164
6165static MagickBooleanType png_write_chunk_from_profile(Image *image,
6166 const char *string, int logging)
6167{
6168 char
6169 *name;
6170
6171 const StringInfo
6172 *profile;
6173
6174 unsigned char
6175 *data;
6176
6177 png_uint_32 length;
6178
6179 ResetImageProfileIterator(image);
6180 for (name=GetNextImageProfile(image); name != (const char *) NULL; ){
6181 profile=GetImageProfile(image,name);
6182 if (profile != (const StringInfo *) NULL)
6183 {
6184 StringInfo
6185 *png_profile;
6186
6187 if (LocaleNCompare(name,string,11) == 0) {
6188 if (logging != MagickFalse)
6189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6190 " Found %s profile",name);
6191
6192 png_profile=CloneStringInfo(profile);
6193 data=GetStringInfoDatum(png_profile),
6194 length=(png_uint_32) GetStringInfoLength(png_profile);
6195 data[4]=data[3];
6196 data[3]=data[2];
6197 data[2]=data[1];
6198 data[1]=data[0];
6199 (void) WriteBlobMSBULong(image,length-5); /* data length */
6200 (void) WriteBlob(image,length-1,data+1);
6201 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6202 png_profile=DestroyStringInfo(png_profile);
6203 }
6204 }
6205 name=GetNextImageProfile(image);
6206 }
6207 return(MagickTrue);
6208}
6209
6210static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6211 const ImageInfo *image_info,Image *image)
6212{
6213/* Write one PNG image */
6214 char
6215 s[2];
6216
6217 const char
6218 *name,
6219 *property,
6220 *value;
6221
6222 const StringInfo
6223 *profile;
6224
6225
6226 int
6227 image_matte,
6228 num_passes,
6229 pass;
6230
6231 png_colorp
6232 palette;
6233
6234 png_info
6235 *ping_info;
6236
6237 png_struct
6238 *ping;
6239
6240 long
6241 y;
6242
6243 MagickBooleanType
6244 status;
6245
6246 QuantumInfo
6247 *quantum_info;
6248
6249 register IndexPacket
6250 *indices;
6251
6252 register long
6253 i,
6254 x;
6255
6256 unsigned char
6257 *png_pixels;
6258
6259 unsigned int
6260 logging,
6261 matte;
6262
6263 volatile unsigned long
6264 image_colors,
6265 image_depth;
6266
6267 unsigned long
6268 old_bit_depth,
6269 quality,
6270 rowbytes,
6271 save_image_depth;
6272
6273 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6274 " enter WriteOnePNGImage()");
6275
6276#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristy18b17442009-10-25 18:36:48 +00006277 LockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006278#endif
6279
cristyed552522009-10-16 14:04:35 +00006280 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00006281 image_colors=image->colors;
6282 image_depth=image->depth;
6283 image_matte=image->matte;
6284
6285 if (image->colorspace != RGBColorspace)
6286 (void) TransformImageColorspace(image,RGBColorspace);
6287 mng_info->IsPalette=image->storage_class == PseudoClass &&
6288 image_colors <= 256;
6289 mng_info->optimize=image_info->type == OptimizeType;
6290
6291 /*
6292 Allocate the PNG structures
6293 */
6294#ifdef PNG_USER_MEM_SUPPORTED
6295 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
6296 PNGErrorHandler,PNGWarningHandler,(void *) NULL,
6297 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
6298#else
6299 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
6300 PNGErrorHandler,PNGWarningHandler);
6301#endif
6302 if (ping == (png_struct *) NULL)
6303 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6304 ping_info=png_create_info_struct(ping);
6305 if (ping_info == (png_info *) NULL)
6306 {
6307 png_destroy_write_struct(&ping,(png_info **) NULL);
6308 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6309 }
6310 png_set_write_fn(ping,image,png_put_data,png_flush_data);
6311 png_pixels=(unsigned char *) NULL;
6312
6313 if (setjmp(ping->jmpbuf))
6314 {
6315 /*
6316 PNG write failed.
6317 */
6318#ifdef PNG_DEBUG
6319 if (image_info->verbose)
6320 (void) printf("PNG write has failed.\n");
6321#endif
6322 png_destroy_write_struct(&ping,&ping_info);
6323#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristy18b17442009-10-25 18:36:48 +00006324 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00006325#endif
6326 return(MagickFalse);
6327 }
6328 /*
6329 Prepare PNG for writing.
6330 */
6331#if defined(PNG_MNG_FEATURES_SUPPORTED)
6332 if (mng_info->write_mng)
6333 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
6334#else
6335# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
6336 if (mng_info->write_mng)
6337 png_permit_empty_plte(ping,MagickTrue);
6338# endif
6339#endif
6340 x=0;
6341 ping_info->width=image->columns;
6342 ping_info->height=image->rows;
6343 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
6344 image_depth=8;
6345 if (mng_info->write_png_depth != 0)
6346 image_depth=mng_info->write_png_depth;
6347 /* Adjust requested depth to next higher valid depth if necessary */
6348 if (image_depth > 8)
6349 image_depth=16;
6350 if ((image_depth > 4) && (image_depth < 8))
6351 image_depth=8;
6352 if (image_depth == 3)
6353 image_depth=4;
6354 if (logging != MagickFalse)
6355 {
6356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6357 " width=%lu",ping_info->width);
6358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6359 " height=%lu",ping_info->height);
6360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6361 " image_matte=%u",image->matte);
6362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6363 " image_depth=%lu",image->depth);
6364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6365 " requested PNG image_depth=%lu",image->depth);
6366 }
6367 save_image_depth=image_depth;
6368 ping_info->bit_depth=(png_byte) save_image_depth;
6369#if defined(PNG_pHYs_SUPPORTED)
6370 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
6371 (!mng_info->write_mng || !mng_info->equal_physs))
6372 {
6373 int
6374 unit_type;
6375
6376 png_uint_32
6377 x_resolution,
6378 y_resolution;
6379
6380 if (image->units == PixelsPerInchResolution)
6381 {
6382 unit_type=PNG_RESOLUTION_METER;
6383 x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
6384 y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
6385 }
6386 else if (image->units == PixelsPerCentimeterResolution)
6387 {
6388 unit_type=PNG_RESOLUTION_METER;
6389 x_resolution=(png_uint_32) (100.0*image->x_resolution);
6390 y_resolution=(png_uint_32) (100.0*image->y_resolution);
6391 }
6392 else
6393 {
6394 unit_type=PNG_RESOLUTION_UNKNOWN;
6395 x_resolution=(png_uint_32) image->x_resolution;
6396 y_resolution=(png_uint_32) image->y_resolution;
6397 }
6398 png_set_pHYs(ping,ping_info,x_resolution,y_resolution,unit_type);
6399 if (logging != MagickFalse)
6400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6401 " Setting up pHYs chunk");
6402 }
6403#endif
6404#if defined(PNG_oFFs_SUPPORTED)
6405 if (image->page.x || image->page.y)
6406 {
6407 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
6408 (png_int_32) image->page.y, 0);
6409 if (logging != MagickFalse)
6410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6411 " Setting up oFFs chunk");
6412 }
6413#endif
6414 if (image_matte && (!mng_info->adjoin || !mng_info->equal_backgrounds))
6415 {
6416 png_color_16
6417 background;
6418
6419 if (image_depth < MAGICKCORE_QUANTUM_DEPTH)
6420 {
6421 unsigned long
6422 maxval;
6423
6424 maxval=(1UL << image_depth)-1;
6425 background.red=(png_uint_16)
6426 (QuantumScale*(maxval*image->background_color.red));
6427 background.green=(png_uint_16)
6428 (QuantumScale*(maxval*image->background_color.green));
6429 background.blue=(png_uint_16)
6430 (QuantumScale*(maxval*image->background_color.blue));
6431 background.gray=(png_uint_16)
6432 (QuantumScale*(maxval*PixelIntensity(&image->background_color)));
6433 }
6434 else
6435 {
6436 background.red=image->background_color.red;
6437 background.green=image->background_color.green;
6438 background.blue=image->background_color.blue;
6439 background.gray=
6440 (png_uint_16) PixelIntensity(&image->background_color);
6441 }
6442 background.index=(png_byte) background.gray;
6443 if (logging != MagickFalse)
6444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6445 " Setting up bKGd chunk");
6446 png_set_bKGD(ping,ping_info,&background);
6447 }
6448 /*
6449 Select the color type.
6450 */
6451 matte=image_matte;
6452 old_bit_depth=0;
6453 if (mng_info->write_png8)
6454 {
6455 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6456 ping_info->bit_depth=8;
6457 image_depth=ping_info->bit_depth;
6458 {
6459 /* TO DO: make this a function cause it's used twice, except
6460 for reducing the sample depth from 8. */
6461
6462 QuantizeInfo
6463 quantize_info;
6464
6465 unsigned long
6466 number_colors,
6467 save_number_colors;
6468
6469 number_colors=image_colors;
6470 if ((image->storage_class == DirectClass) || (number_colors > 256))
6471 {
6472 GetQuantizeInfo(&quantize_info);
6473 quantize_info.dither=IsPaletteImage(image,&image->exception) ==
6474 MagickFalse ? MagickTrue : MagickFalse;
6475 quantize_info.number_colors= (matte != MagickFalse ? 255UL :
6476 256UL);
6477 (void) QuantizeImage(&quantize_info,image);
6478 number_colors=image_colors;
6479 (void) SyncImage(image);
6480 if (logging != MagickFalse)
6481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6482 " Colors quantized to %ld",number_colors);
6483 }
6484 if (matte)
6485 ping_info->valid|=PNG_INFO_tRNS;
6486 /*
6487 Set image palette.
6488 */
6489 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6490 ping_info->valid|=PNG_INFO_PLTE;
6491#if defined(PNG_SORT_PALETTE)
6492 save_number_colors=image_colors;
6493 if (CompressColormapTransFirst(image) == MagickFalse)
6494 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6495 number_colors=image_colors;
6496 image_colors=save_number_colors;
6497#endif
6498 palette=(png_color *) AcquireQuantumMemory(257,
6499 sizeof(*palette));
6500 if (palette == (png_color *) NULL)
6501 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6502 if (logging != MagickFalse)
6503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6504 " Setting up PLTE chunk");
6505 for (i=0; i < (long) number_colors; i++)
6506 {
6507 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6508 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6509 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6510 if (logging != MagickFalse)
6511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6512#if MAGICKCORE_QUANTUM_DEPTH == 8
6513 " %3ld (%3d,%3d,%3d)",
6514#else
6515 " %5ld (%5d,%5d,%5d)",
6516#endif
6517 i,palette[i].red,palette[i].green,palette[i].blue);
6518
6519 }
6520 if (matte)
6521 {
6522 number_colors++;
6523 palette[i].red=ScaleQuantumToChar((Quantum) QuantumRange);
6524 palette[i].green=ScaleQuantumToChar((Quantum) QuantumRange);
6525 palette[i].blue=ScaleQuantumToChar((Quantum) QuantumRange);
6526 }
6527 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6528#if (PNG_LIBPNG_VER > 10008)
6529 palette=(png_colorp) RelinquishMagickMemory(palette);
6530#endif
6531 image_depth=ping_info->bit_depth;
6532 ping_info->num_trans=0;
6533 if (matte)
6534 {
6535 ExceptionInfo
6536 *exception;
6537
6538 /*
6539 Identify which colormap entry is transparent.
6540 */
6541 ping_info->trans_alpha=(unsigned char *) AcquireQuantumMemory(
6542 number_colors,sizeof(*ping_info->trans_alpha));
6543 if (ping_info->trans == (unsigned char *) NULL)
6544 ThrowWriterException(ResourceLimitError,
6545 "MemoryAllocationFailed");
6546 assert(number_colors <= 256);
6547 for (i=0; i < (long) number_colors; i++)
6548 ping_info->trans_alpha[i]=255;
6549 exception=(&image->exception);
6550 for (y=0; y < (long) image->rows; y++)
6551 {
6552 register const PixelPacket
6553 *p;
6554
6555 p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6556 if (p == (PixelPacket *) NULL)
6557 break;
6558 indices=GetAuthenticIndexQueue(image);
6559 for (x=0; x < (long) image->columns; x++)
6560 {
6561 if (p->opacity != OpaqueOpacity)
6562 {
6563 indices[x]=(IndexPacket) (number_colors-1);
6564 ping_info->trans_alpha[(long) indices[x]]=(png_byte) (255-
6565 ScaleQuantumToChar(p->opacity));
6566 }
6567 p++;
6568 }
6569 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6570 break;
6571 }
6572 for (i=0; i < (long) number_colors; i++)
6573 if (ping_info->trans_alpha[i] != 255)
6574 ping_info->num_trans=(unsigned short) (i+1);
6575 if (ping_info->num_trans == 0)
6576 ping_info->valid&=(~PNG_INFO_tRNS);
6577 if (!(ping_info->valid & PNG_INFO_tRNS))
6578 ping_info->num_trans=0;
6579 if (ping_info->num_trans == 0)
6580 ping_info->trans_alpha=(unsigned char *)
6581 RelinquishMagickMemory(ping_info->trans_alpha);
6582 }
6583 /*
6584 Identify which colormap entry is the background color.
6585 */
6586 for (i=0; i < (long) MagickMax(1L*number_colors-1L,1L); i++)
6587 if (IsPNGColorEqual(ping_info->background,image->colormap[i]))
6588 break;
6589 ping_info->background.index=(png_byte) i;
6590 }
6591 if (image_matte != MagickFalse)
6592 {
6593 /* TO DO: reduce to binary transparency */
6594 }
6595 } /* end of write_png8 */
6596 else if (mng_info->write_png24)
6597 {
6598 image_matte=MagickFalse;
6599 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6600 }
6601 else if (mng_info->write_png32)
6602 {
6603 image_matte=MagickTrue;
6604 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6605 }
6606 else
6607 {
6608 image_depth=ping_info->bit_depth;
6609 if (mng_info->write_png_colortype)
6610 {
6611 ping_info->color_type=(png_byte) mng_info->write_png_colortype-1;
6612 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6613 ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6614 image_matte=MagickTrue;
6615 }
6616 else
6617 {
6618 if (logging != MagickFalse)
6619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6620 "Selecting PNG colortype");
6621 ping_info->color_type=(png_byte) ((matte == MagickTrue)?
6622 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6623 if(image_info->type == TrueColorType)
6624 {
6625 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6626 image_matte=MagickFalse;
6627 }
6628 if(image_info->type == TrueColorMatteType)
6629 {
6630 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6631 image_matte=MagickTrue;
6632 }
6633 if ((image_info->type == UndefinedType ||
6634 image_info->type == OptimizeType ||
6635 image_info->type == GrayscaleType) &&
6636 image_matte == MagickFalse && ImageIsGray(image))
6637 {
6638 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
6639 image_matte=MagickFalse;
6640 }
6641 if ((image_info->type == UndefinedType ||
6642 image_info->type == OptimizeType ||
6643 image_info->type == GrayscaleMatteType) &&
6644 image_matte == MagickTrue && ImageIsGray(image))
6645 {
6646 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
6647 image_matte=MagickTrue;
6648 }
6649 }
6650 if (logging != MagickFalse)
6651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6652 "Selected PNG colortype=%d",ping_info->color_type);
6653
6654 if (ping_info->bit_depth < 8)
6655 {
6656 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6657 ping_info->color_type == PNG_COLOR_TYPE_RGB ||
6658 ping_info->color_type == PNG_COLOR_TYPE_RGBA)
6659 ping_info->bit_depth=8;
6660 }
6661
6662 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
6663 {
6664 if (image->matte == MagickFalse && image->colors < 256)
6665 {
6666 if (ImageIsMonochrome(image))
6667 {
6668 ping_info->bit_depth=1;
6669 if (ping_info->bit_depth < mng_info->write_png_depth)
6670 ping_info->bit_depth = mng_info->write_png_depth;
6671 }
6672 }
6673 }
6674 if (ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
6675 {
6676 ping_info->bit_depth=1;
6677 while ((int) (1 << ping_info->bit_depth) < (long) image_colors)
6678 ping_info->bit_depth <<= 1;
6679
6680 if (logging != MagickFalse)
6681 {
6682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6683 " Number of colors: %lu",image_colors);
6684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6685 " Tentative PNG bit depth: %d",ping_info->bit_depth);
6686 }
6687 if (mng_info->write_png_depth)
6688 {
6689 old_bit_depth=ping_info->bit_depth;
6690 if (ping_info->bit_depth < mng_info->write_png_depth)
6691 {
6692 ping_info->bit_depth = mng_info->write_png_depth;
6693 if (ping_info->bit_depth > 8)
6694 ping_info->bit_depth = 8;
6695 if (ping_info->bit_depth != old_bit_depth)
6696 {
6697 if (logging != MagickFalse)
6698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6699 " Colors increased to %ld",image_colors);
6700 }
6701 }
6702 }
6703 }
6704 }
6705 image_depth=ping_info->bit_depth;
6706 if (logging != MagickFalse)
6707 {
6708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6709 " Tentative PNG color type: %d",ping_info->color_type);
6710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6711 " image_info->type: %d",image_info->type);
6712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6713 " image_depth: %lu",image_depth);
6714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6715 " ping_info->bit_depth: %d",ping_info->bit_depth);
6716 }
6717
6718 if (matte && (mng_info->optimize || mng_info->IsPalette))
6719 {
6720 register const PixelPacket
6721 *p;
6722
6723 p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
6724 ping_info->color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
6725 for (y=0; y < (long) image->rows; y++)
6726 {
6727 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6728 if (p == (const PixelPacket *) NULL)
6729 break;
6730 for (x=(long) image->columns-1; x >= 0; x--)
6731 {
6732 if (IsGray(p) == MagickFalse)
6733 {
6734 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6735 break;
6736 }
6737 p++;
6738 }
6739 }
6740 /*
6741 Determine if there is any transparent color.
6742 */
6743 for (y=0; y < (long) image->rows; y++)
6744 {
6745 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6746 if (p == (const PixelPacket *) NULL)
6747 break;
6748 for (x=(long) image->columns-1; x >= 0; x--)
6749 {
6750 if (p->opacity != OpaqueOpacity)
6751 break;
6752 p++;
6753 }
6754 if (x != 0)
6755 break;
6756 }
6757 if ((y == (long) image->rows) && (x == (long) image->columns))
6758 {
6759 /*
6760 No transparent pixels are present. Change 4 or 6 to 0 or 2,
6761 and do not set the PNG_INFO_tRNS flag in ping_info->valid.
6762 */
6763 image_matte=MagickFalse;
6764 ping_info->color_type&=0x03;
6765 }
6766 else
6767 {
6768 unsigned int
6769 mask;
6770
6771 mask=0xffff;
6772 if (ping_info->bit_depth == 8)
6773 mask=0x00ff;
6774 if (ping_info->bit_depth == 4)
6775 mask=0x000f;
6776 if (ping_info->bit_depth == 2)
6777 mask=0x0003;
6778 if (ping_info->bit_depth == 1)
6779 mask=0x0001;
6780 ping_info->valid|=PNG_INFO_tRNS;
6781 ping_info->trans_color.red=(png_uint_16)
6782 (ScaleQuantumToShort(p->red) & mask);
6783 ping_info->trans_color.green=(png_uint_16)
6784 (ScaleQuantumToShort(p->green) & mask);
6785 ping_info->trans_color.blue=(png_uint_16)
6786 (ScaleQuantumToShort(p->blue) & mask);
6787 ping_info->trans_color.gray=(png_uint_16)
6788 (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
6789 ping_info->trans_color.index=(png_byte)
6790 (ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity)));
6791 }
6792 if (ping_info->valid & PNG_INFO_tRNS)
6793 {
6794 /*
6795 Determine if there is one and only one transparent color
6796 and if so if it is fully transparent.
6797 */
6798 for (y=0; y < (long) image->rows; y++)
6799 {
6800 p=GetVirtualPixels(image,0,y,image->columns,1,
6801 &image->exception);
6802 x=0;
6803 if (p == (const PixelPacket *) NULL)
6804 break;
6805 for (x=(long) image->columns-1; x >= 0; x--)
6806 {
6807 if (p->opacity != OpaqueOpacity)
6808 {
6809 if (IsPNGColorEqual(ping_info->trans_color,*p) == 0)
6810 {
6811 break; /* Can't use RGB + tRNS for multiple
6812 transparent colors. */
6813 }
6814 if (p->opacity != (Quantum) TransparentOpacity)
6815 {
6816 break; /* Can't use RGB + tRNS for
6817 semitransparency. */
6818 }
6819 }
6820 else
6821 {
6822 if (IsPNGColorEqual(ping_info->trans_color,*p))
6823 break; /* Can't use RGB + tRNS when another pixel
6824 having the same RGB samples is
6825 transparent. */
6826 }
6827 p++;
6828 }
6829 if (x != 0)
6830 break;
6831 }
6832 if (x != 0)
6833 ping_info->valid&=(~PNG_INFO_tRNS);
6834 }
6835 if (ping_info->valid & PNG_INFO_tRNS)
6836 {
6837 ping_info->color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
6838 if (image_depth == 8)
6839 {
6840 ping_info->trans_color.red&=0xff;
6841 ping_info->trans_color.green&=0xff;
6842 ping_info->trans_color.blue&=0xff;
6843 ping_info->trans_color.gray&=0xff;
6844 }
6845 }
6846 }
6847 matte=image_matte;
6848 if (ping_info->valid & PNG_INFO_tRNS)
6849 image_matte=MagickFalse;
6850 if ((mng_info->optimize || mng_info->IsPalette) &&
6851 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
6852 ImageIsGray(image) && (!image_matte || image_depth >= 8))
6853 {
6854 if (image_matte != MagickFalse)
6855 ping_info->color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
6856 else
6857 {
6858 ping_info->color_type=PNG_COLOR_TYPE_GRAY;
6859 if (save_image_depth == 16 && image_depth == 8)
6860 ping_info->trans_color.gray*=0x0101;
6861 }
6862 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
6863 image_depth=MAGICKCORE_QUANTUM_DEPTH;
6864 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
6865 image_colors=1 << image_depth;
6866 if (image_depth > 8)
6867 ping_info->bit_depth=16;
6868 else
6869 {
6870 ping_info->bit_depth=8;
6871 if ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE)
6872 {
6873 if(!mng_info->write_png_depth)
6874 {
6875 ping_info->bit_depth=1;
6876 while ((int) (1 << ping_info->bit_depth)
6877 < (long) image_colors)
6878 ping_info->bit_depth <<= 1;
6879 }
6880 }
6881 else if (mng_info->optimize && ping_info->color_type ==
6882 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
6883 mng_info->IsPalette)
6884 {
6885
6886 /* Check if grayscale is reducible */
6887 int
6888 depth_4_ok=MagickTrue,
6889 depth_2_ok=MagickTrue,
6890 depth_1_ok=MagickTrue;
6891
6892 for (i=0; i < (long) image_colors; i++)
6893 {
6894 unsigned char
6895 intensity;
6896
6897 intensity=ScaleQuantumToChar(image->colormap[i].red);
6898
6899 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
6900 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
6901 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
6902 depth_2_ok=depth_1_ok=MagickFalse;
6903 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
6904 depth_1_ok=MagickFalse;
6905 }
6906 if (depth_1_ok && mng_info->write_png_depth <= 1)
6907 ping_info->bit_depth=1;
6908 else if (depth_2_ok && mng_info->write_png_depth <= 2)
6909 ping_info->bit_depth=2;
6910 else if (depth_4_ok && mng_info->write_png_depth <= 4)
6911 ping_info->bit_depth=4;
6912 }
6913 }
6914 image_depth=ping_info->bit_depth;
6915 }
6916 else
6917 if (mng_info->IsPalette)
6918 {
6919 if (image_depth <= 8)
6920 {
6921 unsigned long
6922 number_colors;
6923
6924 number_colors=image_colors;
6925 if (matte)
6926 ping_info->valid|=PNG_INFO_tRNS;
6927 /*
6928 Set image palette.
6929 */
6930 ping_info->color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6931 ping_info->valid|=PNG_INFO_PLTE;
6932 if (mng_info->have_write_global_plte && !matte)
6933 {
6934 png_set_PLTE(ping,ping_info,NULL,0);
6935 if (logging)
6936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6937 " Setting up empty PLTE chunk");
6938 }
6939 else
6940 {
6941#if defined(PNG_SORT_PALETTE)
6942 unsigned long
6943 save_number_colors;
6944
6945 if (mng_info->optimize)
6946 {
6947 save_number_colors=image_colors;
6948 if (CompressColormapTransFirst(image) == MagickFalse)
6949 ThrowWriterException(ResourceLimitError,
6950 "MemoryAllocationFailed");
6951 number_colors=image_colors;
6952 image_colors=save_number_colors;
6953 }
6954#endif
6955 palette=(png_color *) AcquireQuantumMemory(257,
6956 sizeof(*palette));
6957 if (palette == (png_color *) NULL)
6958 ThrowWriterException(ResourceLimitError,
6959 "MemoryAllocationFailed");
6960 for (i=0; i < (long) number_colors; i++)
6961 {
6962 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6963 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6964 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6965 }
6966 if (logging)
6967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6968 " Setting up PLTE chunk");
6969 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6970#if (PNG_LIBPNG_VER > 10008)
6971 palette=(png_colorp) RelinquishMagickMemory(palette);
6972#endif
6973 }
6974 /* color_type is PNG_COLOR_TYPE_PALETTE */
6975 if (!mng_info->write_png_depth)
6976 {
6977 ping_info->bit_depth=1;
6978 while ((1UL << ping_info->bit_depth) < number_colors)
6979 ping_info->bit_depth <<= 1;
6980 }
6981 ping_info->num_trans=0;
6982 if (matte)
6983 {
6984 ExceptionInfo
6985 *exception;
6986
6987 register const PixelPacket
6988 *p;
6989
6990 int
6991 trans[256];
6992
6993 register const IndexPacket
6994 *packet_indices;
6995
6996 /*
6997 Identify which colormap entry is transparent.
6998 */
6999 assert(number_colors <= 256);
7000 for (i=0; i < (long) number_colors; i++)
7001 trans[i]=256;
7002 exception=(&image->exception);
7003 for (y=0; y < (long) image->rows; y++)
7004 {
7005 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7006 if (p == (const PixelPacket *) NULL)
7007 break;
7008 packet_indices=GetVirtualIndexQueue(image);
7009 for (x=0; x < (long) image->columns; x++)
7010 {
7011 if (p->opacity != OpaqueOpacity)
7012 {
7013 IndexPacket
7014 packet_index;
7015
7016 packet_index=packet_indices[x];
7017 assert((unsigned long) packet_index < number_colors);
7018 if (trans[(long) packet_index] != 256)
7019 {
7020 if (trans[(long) packet_index] != (png_byte) (255-
7021 ScaleQuantumToChar(p->opacity)))
7022 {
7023 ping_info->color_type=(png_byte)
7024 PNG_COLOR_TYPE_RGB_ALPHA;
7025 break;
7026 }
7027 }
7028 trans[(long) packet_index]=(png_byte) (255-
7029 ScaleQuantumToChar(p->opacity));
7030 }
7031 p++;
7032 }
7033 if ((int) ping_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
7034 {
7035 ping_info->num_trans=0;
7036 ping_info->valid&=(~PNG_INFO_tRNS);
7037 ping_info->valid&=(~PNG_INFO_PLTE);
7038 mng_info->IsPalette=MagickFalse;
7039 (void) SyncImage(image);
7040 if (logging)
7041 (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7042 " Cannot write image as indexed PNG, writing RGBA.");
7043 break;
7044 }
7045 }
7046 if ((ping_info->valid & PNG_INFO_tRNS))
7047 {
7048 for (i=0; i < (long) number_colors; i++)
7049 {
7050 if (trans[i] == 256)
7051 trans[i]=255;
7052 if (trans[i] != 255)
7053 ping_info->num_trans=(unsigned short) (i+1);
7054 }
7055 }
7056 if (ping_info->num_trans == 0)
7057 ping_info->valid&=(~PNG_INFO_tRNS);
7058 if (!(ping_info->valid & PNG_INFO_tRNS))
7059 ping_info->num_trans=0;
7060 if (ping_info->num_trans != 0)
7061 {
7062 ping_info->trans_alpha=(unsigned char *) AcquireQuantumMemory(
7063 number_colors,sizeof(*ping_info->trans_alpha));
7064 if (ping_info->trans_alpha == (unsigned char *) NULL)
7065 ThrowWriterException(ResourceLimitError,
7066 "MemoryAllocationFailed");
7067 for (i=0; i < (long) number_colors; i++)
7068 ping_info->trans_alpha[i]=(png_byte) trans[i];
7069 }
7070 }
7071
7072 /*
7073 Identify which colormap entry is the background color.
7074 */
7075 for (i=0; i < (long) MagickMax(1L*number_colors-1L,1L); i++)
7076 if (IsPNGColorEqual(ping_info->background,image->colormap[i]))
7077 break;
7078 ping_info->background.index=(png_byte) i;
7079 }
7080 }
7081 else
7082 {
7083 if (image_depth < 8)
7084 image_depth=8;
7085 if ((save_image_depth == 16) && (image_depth == 8))
7086 {
7087 ping_info->trans_color.red*=0x0101;
7088 ping_info->trans_color.green*=0x0101;
7089 ping_info->trans_color.blue*=0x0101;
7090 ping_info->trans_color.gray*=0x0101;
7091 }
7092 }
7093
7094 /*
7095 Adjust background and transparency samples in sub-8-bit grayscale files.
7096 */
7097 if (ping_info->bit_depth < 8 && ping_info->color_type ==
7098 PNG_COLOR_TYPE_GRAY)
7099 {
7100 png_uint_16
7101 maxval;
7102
7103 png_color_16
7104 background;
7105
7106 maxval=(png_uint_16) ((1 << ping_info->bit_depth)-1);
7107
7108
7109 background.gray=(png_uint_16)
7110 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7111
7112 if (logging != MagickFalse)
7113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7114 " Setting up bKGD chunk");
7115 png_set_bKGD(ping,ping_info,&background);
7116
7117 ping_info->trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7118 ping_info->trans_color.gray));
7119 }
7120 if (logging != MagickFalse)
7121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7122 " PNG color type: %d",ping_info->color_type);
7123 /*
7124 Initialize compression level and filtering.
7125 */
7126 if (logging != MagickFalse)
7127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7128 " Setting up deflate compression");
7129#if (PNG_LIBPNG_VER > 99)
7130 if (logging != MagickFalse)
7131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7132 " Compression buffer size: 32768");
7133 png_set_compression_buffer_size(ping,32768L);
7134#endif
7135 if (logging != MagickFalse)
7136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7137 " Compression mem level: 9");
7138 png_set_compression_mem_level(ping, 9);
7139 quality=image->quality == UndefinedCompressionQuality ? 75UL :
7140 image->quality;
7141 if (quality > 9)
7142 {
7143 int
7144 level;
7145
7146 level=(int) MagickMin((long) quality/10,9);
7147 if (logging != MagickFalse)
7148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7149 " Compression level: %d",level);
7150 png_set_compression_level(ping,level);
7151 }
7152 else
7153 {
7154 if (logging != MagickFalse)
7155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7156 " Compression strategy: Z_HUFFMAN_ONLY");
7157 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7158 }
7159 if (logging != MagickFalse)
7160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7161 " Setting up filtering");
7162#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7163
7164 /* This became available in libpng-1.0.9. Output must be a MNG. */
7165 if (mng_info->write_mng && ((quality % 10) == 7))
7166 {
7167 if (logging != MagickFalse)
7168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7169 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
7170 ping_info->filter_type=PNG_INTRAPIXEL_DIFFERENCING;
7171 }
7172 else
7173 if (logging != MagickFalse)
7174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7175 " Filter_type: 0");
7176#endif
7177 {
7178 int
7179 base_filter;
7180
7181 if ((quality % 10) > 5)
7182 base_filter=PNG_ALL_FILTERS;
7183 else
7184 if ((quality % 10) != 5)
7185 base_filter=(int) quality % 10;
7186 else
7187 if (((int) ping_info->color_type == PNG_COLOR_TYPE_GRAY) ||
7188 ((int) ping_info->color_type == PNG_COLOR_TYPE_PALETTE) ||
7189 (quality < 50))
7190 base_filter=PNG_NO_FILTERS;
7191 else
7192 base_filter=PNG_ALL_FILTERS;
7193 if (logging != MagickFalse)
7194 {
7195 if (base_filter == PNG_ALL_FILTERS)
7196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7197 " Base filter method: ADAPTIVE");
7198 else
7199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7200 " Base filter method: NONE");
7201 }
7202 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7203 }
7204
7205 ResetImageProfileIterator(image);
7206 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7207 {
7208 profile=GetImageProfile(image,name);
7209 if (profile != (StringInfo *) NULL)
7210 {
7211#if (PNG_LIBPNG_VER > 10008) && defined(PNG_WRITE_iCCP_SUPPORTED)
7212 if ((LocaleCompare(name,"ICC") == 0) ||
7213 (LocaleCompare(name,"ICM") == 0))
7214 png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7215 GetStringInfoDatum(profile),
7216 (png_uint_32) GetStringInfoLength(profile));
7217 else
7218#endif
7219 png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7220 name,(unsigned char *) name,GetStringInfoDatum(profile),
7221 (png_uint_32) GetStringInfoLength(profile));
7222 }
7223 if (logging != MagickFalse)
7224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7225 " Setting up text chunk with %s profile",name);
7226 name=GetNextImageProfile(image);
7227 }
7228
7229#if defined(PNG_WRITE_sRGB_SUPPORTED)
7230 if ((mng_info->have_write_global_srgb == 0) &&
7231 ((image->rendering_intent != UndefinedIntent) ||
7232 (image->colorspace == sRGBColorspace)))
7233 {
7234 /*
7235 Note image rendering intent.
7236 */
7237 if (logging != MagickFalse)
7238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7239 " Setting up sRGB chunk");
7240 (void) png_set_sRGB(ping,ping_info,(int) (image->rendering_intent-1));
7241 png_set_gAMA(ping,ping_info,0.45455);
7242 }
7243 if ((!mng_info->write_mng) || !(ping_info->valid & PNG_INFO_sRGB))
7244#endif
7245 {
7246 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7247 {
7248 /*
7249 Note image gamma.
7250 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7251 */
7252 if (logging != MagickFalse)
7253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7254 " Setting up gAMA chunk");
7255 png_set_gAMA(ping,ping_info,image->gamma);
7256 }
7257 if ((mng_info->have_write_global_chrm == 0) &&
7258 (image->chromaticity.red_primary.x != 0.0))
7259 {
7260 /*
7261 Note image chromaticity.
7262 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7263 */
7264 PrimaryInfo
7265 bp,
7266 gp,
7267 rp,
7268 wp;
7269
7270 wp=image->chromaticity.white_point;
7271 rp=image->chromaticity.red_primary;
7272 gp=image->chromaticity.green_primary;
7273 bp=image->chromaticity.blue_primary;
7274
7275 if (logging != MagickFalse)
7276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7277 " Setting up cHRM chunk");
7278 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7279 bp.x,bp.y);
7280 }
7281 }
7282 ping_info->interlace_type=image_info->interlace != NoInterlace;
7283
7284 if (mng_info->write_mng)
7285 png_set_sig_bytes(ping,8);
7286
7287 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7288
7289 if (mng_info->write_png_colortype)
7290 {
7291 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7292 if (ImageIsGray(image) == MagickFalse)
7293 {
7294 ping_info->color_type = PNG_COLOR_TYPE_RGB;
7295 if (ping_info->bit_depth < 8)
7296 ping_info->bit_depth=8;
7297 }
7298
7299 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7300 if (ImageIsGray(image) == MagickFalse)
7301 ping_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
7302 }
7303
7304 if ((mng_info->write_png_depth &&
7305 mng_info->write_png_depth != ping_info->bit_depth) ||
7306 (mng_info->write_png_colortype &&
7307 mng_info->write_png_colortype-1 != ping_info->color_type))
7308 {
7309 if (logging != MagickFalse)
7310 {
7311 if (mng_info->write_png_depth)
7312 {
7313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7314 " Defined PNG:bit-depth=%u, Computed depth=%u",
7315 mng_info->write_png_depth,
7316 ping_info->bit_depth);
7317 }
7318 if (mng_info->write_png_colortype)
7319 {
7320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7321 " Defined PNG:color-type=%u, Computed color type=%u",
7322 mng_info->write_png_colortype-1,
7323 ping_info->color_type);
7324 }
7325 }
7326 png_error(ping,
7327 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7328 }
7329
7330 if (image_matte && !image->matte)
7331 {
7332 /* Add an opaque matte channel */
7333 image->matte = MagickTrue;
7334 (void) SetImageOpacity(image,0);
7335 }
7336
7337 if (logging != MagickFalse)
7338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7339 " Writing PNG header chunks");
7340
7341 png_write_info_before_PLTE(ping, ping_info);
7342 /* write any png-chunk-b profiles */
7343 (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7344 png_write_info(ping,ping_info);
7345 /* write any PNG-chunk-m profiles */
7346 (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7347
7348 if (image->page.width || image->page.height)
7349 {
7350 unsigned char
7351 chunk[14];
7352
7353 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
7354 PNGType(chunk,mng_vpAg);
7355 LogPNGChunk((int) logging,mng_vpAg,9L);
7356 PNGLong(chunk+4,(png_uint_32) image->page.width);
7357 PNGLong(chunk+8,(png_uint_32) image->page.height);
7358 chunk[12]=0; /* unit = pixels */
7359 (void) WriteBlob(image,13,chunk);
7360 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
7361 }
7362
7363#if (PNG_LIBPNG_VER == 10206)
7364 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
7365#define PNG_HAVE_IDAT 0x04
7366 ping->mode |= PNG_HAVE_IDAT;
7367#undef PNG_HAVE_IDAT
7368#endif
7369
7370 png_set_packing(ping);
7371 /*
7372 Allocate memory.
7373 */
7374 rowbytes=image->columns;
7375 if (image_depth <= 8)
7376 {
7377 if (mng_info->write_png24 || (mng_info->write_png_depth == 8 &&
7378 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_RGB))
7379 rowbytes*=3;
7380 else if (mng_info->write_png32 || (mng_info->write_png_depth == 8 &&
7381 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_RGB_ALPHA))
7382 rowbytes*=4;
7383 else if ((!mng_info->write_png8 ||
7384 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY ||
7385 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA )&&
7386 ((mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image)))
7387 rowbytes*=(image_matte ? 2 : 1);
7388 else
7389 {
7390 if (!mng_info->IsPalette)
7391 rowbytes*=(image_matte ? 4 : 3);
7392 }
7393 }
7394 else
7395 {
7396 if ((mng_info->optimize || mng_info->IsPalette) &&
7397 ImageIsGray(image))
7398 rowbytes*=(image_matte ? 4 : 2);
7399 else
7400 rowbytes*=(image_matte ? 8 : 6);
7401 }
7402 if (logging)
7403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7404 " Allocating %lu bytes of memory for pixels",rowbytes);
7405 png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7406 sizeof(*png_pixels));
7407 if (png_pixels == (unsigned char *) NULL)
7408 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7409 /*
7410 Initialize image scanlines.
7411 */
cristy3ed852e2009-09-05 21:47:34 +00007412 if (setjmp(ping->jmpbuf))
7413 {
7414 /*
7415 PNG write failed.
7416 */
7417#ifdef PNG_DEBUG
7418 if (image_info->verbose)
7419 (void) printf("PNG write has failed.\n");
7420#endif
7421 png_destroy_write_struct(&ping,&ping_info);
7422 if (quantum_info != (QuantumInfo *) NULL)
7423 quantum_info=DestroyQuantumInfo(quantum_info);
7424 if (png_pixels != (unsigned char *) NULL)
7425 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7426#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristy18b17442009-10-25 18:36:48 +00007427 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007428#endif
7429 return(MagickFalse);
7430 }
cristyed552522009-10-16 14:04:35 +00007431 quantum_info=AcquireQuantumInfo(image_info,image);
7432 if (quantum_info == (QuantumInfo *) NULL)
7433 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +00007434 quantum_info->format=UndefinedQuantumFormat;
7435 quantum_info->depth=image_depth;
7436 num_passes=png_set_interlace_handling(ping);
7437 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7438 !mng_info->write_png32) &&
7439 (mng_info->optimize || mng_info->IsPalette ||
7440 (image_info->type == BilevelType)) &&
7441 !image_matte && ImageIsMonochrome(image))
7442 {
7443 register const PixelPacket
7444 *p;
7445
7446 quantum_info->depth=8;
7447 for (pass=0; pass < num_passes; pass++)
7448 {
7449 /*
7450 Convert PseudoClass image to a PNG monochrome image.
7451 */
7452 for (y=0; y < (long) image->rows; y++)
7453 {
7454 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7455 if (p == (const PixelPacket *) NULL)
7456 break;
7457 if (mng_info->IsPalette)
7458 {
7459 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7460 quantum_info,GrayQuantum,png_pixels,&image->exception);
7461 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7462 mng_info->write_png_depth &&
7463 mng_info->write_png_depth != old_bit_depth)
7464 {
7465 /* Undo pixel scaling */
7466 for (i=0; i < (long) image->columns; i++)
7467 *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7468 >> (8-old_bit_depth));
7469 }
7470 }
7471 else
7472 {
7473 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7474 quantum_info,RedQuantum,png_pixels,&image->exception);
7475 }
7476 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
7477 for (i=0; i < (long) image->columns; i++)
7478 *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7479 255 : 0);
7480 png_write_row(ping,png_pixels);
7481 }
7482 if (image->previous == (Image *) NULL)
7483 {
7484 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7485 if (status == MagickFalse)
7486 break;
7487 }
7488 }
7489 }
7490 else
7491 for (pass=0; pass < num_passes; pass++)
7492 {
7493 register const PixelPacket
7494 *p;
7495
7496 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7497 !mng_info->write_png32) &&
7498 (image_matte ||
7499 (ping_info->bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
7500 (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7501 {
7502 for (y=0; y < (long) image->rows; y++)
7503 {
7504 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7505 if (p == (const PixelPacket *) NULL)
7506 break;
7507 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
7508 {
7509 if (mng_info->IsPalette)
7510 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7511 quantum_info,GrayQuantum,png_pixels,&image->exception);
7512 else
7513 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7514 quantum_info,RedQuantum,png_pixels,&image->exception);
7515 }
7516 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7517 {
7518 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7519 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7520 }
7521 png_write_row(ping,png_pixels);
7522 }
7523 if (image->previous == (Image *) NULL)
7524 {
7525 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7526 if (status == MagickFalse)
7527 break;
7528 }
7529 }
7530 else
7531 for (pass=0; pass < num_passes; pass++)
7532 {
7533 if ((image_depth > 8) || (mng_info->write_png24 ||
7534 mng_info->write_png32 ||
7535 (!mng_info->write_png8 && !mng_info->IsPalette)))
7536 for (y=0; y < (long) image->rows; y++)
7537 {
7538 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7539 if (p == (const PixelPacket *) NULL)
7540 break;
7541 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
7542 {
7543 if (image->storage_class == DirectClass)
7544 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7545 quantum_info,RedQuantum,png_pixels,&image->exception);
7546 else
7547 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7548 quantum_info,GrayQuantum,png_pixels,&image->exception);
7549 }
7550 else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7551 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7552 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7553 else if (image_matte != MagickFalse)
7554 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7555 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7556 else
7557 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7558 quantum_info,RGBQuantum,png_pixels,&image->exception);
7559 png_write_row(ping,png_pixels);
7560 }
7561 else
7562 /* not ((image_depth > 8) || (mng_info->write_png24 ||
7563 mng_info->write_png32 ||
7564 (!mng_info->write_png8 && !mng_info->IsPalette))) */
7565 {
7566 if ((ping_info->color_type != PNG_COLOR_TYPE_GRAY) &&
7567 (ping_info->color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
7568 {
7569 if (logging)
7570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7571 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7572 quantum_info->depth=8;
7573 image_depth=8;
7574 }
7575 for (y=0; y < (long) image->rows; y++)
7576 {
7577 if (logging)
7578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7579 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7580 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7581 if (p == (const PixelPacket *) NULL)
7582 break;
7583 if (ping_info->color_type == PNG_COLOR_TYPE_GRAY)
7584 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7585 quantum_info,GrayQuantum,png_pixels,&image->exception);
7586 else if (ping_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7587 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7588 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7589 else
7590 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7591 quantum_info,IndexQuantum,png_pixels,&image->exception);
7592 png_write_row(ping,png_pixels);
7593 }
7594 }
7595 if (image->previous == (Image *) NULL)
7596 {
7597 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7598 if (status == MagickFalse)
7599 break;
7600 }
7601 }
7602 }
cristyb32b90a2009-09-07 21:45:48 +00007603 if (quantum_info != (QuantumInfo *) NULL)
7604 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +00007605
7606 if (logging != MagickFalse)
7607 {
7608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7609 " Writing PNG image data");
7610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7611 " Width: %lu",ping_info->width);
7612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7613 " Height: %lu",ping_info->height);
7614 if (mng_info->write_png_depth)
7615 {
7616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7617 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7618 }
7619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7620 " PNG bit-depth written: %d",ping_info->bit_depth);
7621 if (mng_info->write_png_colortype)
7622 {
7623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7624 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7625 }
7626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7627 " PNG color-type written: %d",ping_info->color_type);
7628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7629 " PNG Interlace method: %d",ping_info->interlace_type);
7630 }
7631 /*
7632 Generate text chunks.
7633 */
7634#if (PNG_LIBPNG_VER <= 10005)
7635 ping_info->num_text=0;
7636#endif
7637 ResetImagePropertyIterator(image);
7638 property=GetNextImageProperty(image);
7639 while (property != (const char *) NULL)
7640 {
7641#if (PNG_LIBPNG_VER > 10005)
7642 png_textp
7643 text;
7644#endif
7645
7646 value=GetImageProperty(image,property);
7647 if (value != (const char *) NULL)
7648 {
7649#if (PNG_LIBPNG_VER > 10005)
7650 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7651 text[0].key=(char *) property;
7652 text[0].text=(char *) value;
7653 text[0].text_length=strlen(value);
7654 text[0].compression=image_info->compression == NoCompression ||
7655 (image_info->compression == UndefinedCompression &&
7656 text[0].text_length < 128) ? -1 : 0;
7657 if (logging != MagickFalse)
7658 {
7659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7660 " Setting up text chunk");
7661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7662 " keyword: %s",text[0].key);
7663 }
7664 png_set_text(ping,ping_info,text,1);
7665 png_free(ping,text);
7666#else
7667/* Work directly with ping_info struct; png_set_text before libpng version
7668 * 1.0.5a is leaky */
7669 if (ping_info->num_text == 0)
7670 {
7671 ping_info->text=(png_text *) AcquireQuantumMemory(256,
7672 sizeof(*ping_info->text));
7673 if (ping_info->text == (png_text *) NULL)
7674 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7675 ResourceLimitError,"MemoryAllocationFailed","`%s'",
7676 image->filename);
7677 }
7678 i=ping_info->num_text++;
7679 if (i > 255)
7680 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7681 ResourceLimitError,"Cannot write more than 256 PNG text chunks",
7682 "`%s'",image->filename);
7683 ping_info->text[i].key=(char *) property;
7684 ping_info->text[i].text=(char *) value;
7685 ping_info->text[i].text_length=strlen(value);
7686 ping_info->text[i].compression=
7687 image_info->compression == NoCompression ||
7688 (image_info->compression == UndefinedCompression &&
7689 ping_info->text[i].text_length < 128) ? -1 : 0;
7690 if (logging != MagickFalse)
7691 {
7692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7693 " Setting up text chunk");
7694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7695 " keyword: %s",ping_info->text[i].key);
7696 }
7697#endif
7698 }
7699 property=GetNextImageProperty(image);
7700 }
7701
7702 /* write any PNG-chunk-e profiles */
7703 (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
7704
7705 if (logging != MagickFalse)
7706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7707 " Writing PNG end info");
7708 png_write_end(ping,ping_info);
7709 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
7710 {
7711 if (mng_info->page.x || mng_info->page.y ||
7712 (ping_info->width != mng_info->page.width) ||
7713 (ping_info->height != mng_info->page.height))
7714 {
7715 unsigned char
7716 chunk[32];
7717
7718 /*
7719 Write FRAM 4 with clipping boundaries followed by FRAM 1.
7720 */
7721 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
7722 PNGType(chunk,mng_FRAM);
7723 LogPNGChunk((int) logging,mng_FRAM,27L);
7724 chunk[4]=4;
7725 chunk[5]=0; /* frame name separator (no name) */
7726 chunk[6]=1; /* flag for changing delay, for next frame only */
7727 chunk[7]=0; /* flag for changing frame timeout */
7728 chunk[8]=1; /* flag for changing frame clipping for next frame */
7729 chunk[9]=0; /* flag for changing frame sync_id */
7730 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
7731 chunk[14]=0; /* clipping boundaries delta type */
7732 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
7733 PNGLong(chunk+19,
7734 (png_uint_32) (mng_info->page.x + ping_info->width));
7735 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
7736 PNGLong(chunk+27,
7737 (png_uint_32) (mng_info->page.y + ping_info->height));
7738 (void) WriteBlob(image,31,chunk);
7739 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
7740 mng_info->old_framing_mode=4;
7741 mng_info->framing_mode=1;
7742 }
7743 else
7744 mng_info->framing_mode=3;
7745 }
7746 if (mng_info->write_mng && !mng_info->need_fram &&
7747 ((int) image->dispose == 3))
7748 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7749 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
7750 "`%s'",image->filename);
7751 image_depth=save_image_depth;
7752
7753 /* Save depth actually written */
7754
7755 s[0]=(char) ping_info->bit_depth;
7756 s[1]='\0';
7757
7758 (void) SetImageProperty(image,"png:bit-depth-written",s);
7759
7760 /*
7761 Free PNG resources.
7762 */
7763#if (PNG_LIBPNG_VER < 10007)
7764 if (ping_info->valid & PNG_INFO_PLTE)
7765 {
7766 ping_info->palette=(png_colorp)
7767 RelinquishMagickMemory(ping_info->palette);
7768 ping_info->valid&=(~PNG_INFO_PLTE);
7769 }
7770 if (ping_info->valid & PNG_INFO_tRNS)
7771 {
7772 ping_info->trans_alpha=(unsigned char *) RelinquishMagickMemory(
7773 ping_info->trans_alpha);
7774 ping_info->valid&=(~PNG_INFO_tRNS);
7775 }
7776#endif
7777
7778 png_destroy_write_struct(&ping,&ping_info);
7779
7780 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7781
7782#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
cristy18b17442009-10-25 18:36:48 +00007783 UnlockSemaphoreInfo(png_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007784#endif
7785
7786 if (logging != MagickFalse)
7787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7788 " exit WriteOnePNGImage()");
7789 return(MagickTrue);
7790/* End write one PNG image */
7791}
7792
7793/*
7794%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7795% %
7796% %
7797% %
7798% W r i t e P N G I m a g e %
7799% %
7800% %
7801% %
7802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7803%
7804% WritePNGImage() writes a Portable Network Graphics (PNG) or
7805% Multiple-image Network Graphics (MNG) image file.
7806%
7807% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7808%
7809% The format of the WritePNGImage method is:
7810%
7811% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
7812%
7813% A description of each parameter follows:
7814%
7815% o image_info: the image info.
7816%
7817% o image: The image.
7818%
7819% Returns MagickTrue on success, MagickFalse on failure.
7820%
7821% Communicating with the PNG encoder:
7822%
7823% While the datastream written is always in PNG format and normally would
7824% be given the "png" file extension, this method also writes the following
7825% pseudo-formats which are subsets of PNG:
7826%
7827% o PNG8: An 8-bit indexed PNG datastream is written. If transparency
7828% is present, the tRNS chunk must only have values 0 and 255
7829% (i.e., transparency is binary: fully opaque or fully
7830% transparent). The pixels contain 8-bit indices even if
7831% they could be represented with 1, 2, or 4 bits. Note: grayscale
7832% images will be written as indexed PNG files even though the
7833% PNG grayscale type might be slightly more efficient.
7834%
7835% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
7836% chunk can be present to convey binary transparency by naming
7837% one of the colors as transparent.
7838%
7839% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
7840% transparency is permitted, i.e., the alpha sample for
7841% each pixel can have any value from 0 to 255. The alpha
7842% channel is present even if the image is fully opaque.
7843%
7844% o -define: For more precise control of the PNG output, you can use the
7845% Image options "png:bit-depth" and "png:color-type". These
7846% can be set from the commandline with "-define" and also
7847% from the application programming interfaces.
7848%
7849% png:color-type can be 0, 2, 3, 4, or 6.
7850%
7851% When png:color-type is 0 (Grayscale), png:bit-depth can
7852% be 1, 2, 4, 8, or 16.
7853%
7854% When png:color-type is 2 (RGB), png:bit-depth can
7855% be 8 or 16.
7856%
7857% When png:color-type is 3 (Indexed), png:bit-depth can
7858% be 1, 2, 4, or 8. This refers to the number of bits
7859% used to store the index. The color samples always have
7860% bit-depth 8 in indexed PNG files.
7861%
7862% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
7863% png:bit-depth can be 8 or 16.
7864%
7865% If the image cannot be written without loss in the requested PNG8, PNG24,
7866% or PNG32 format or with the requested bit-depth and color-type without loss,
7867% a PNG file will not be written, and the encoder will return MagickFalse.
7868% Since image encoders should not be responsible for the "heavy lifting",
7869% the user should make sure that ImageMagick has already reduced the
7870% image depth and number of colors and limit transparency to binary
7871% transparency prior to attempting to write the image in a format that
7872% is subject to depth, color, or transparency limitations.
7873%
7874% TODO: Enforce the previous paragraph.
7875%
7876% TODO: Allow all other PNG subformats to be requested via new
7877% "-define png:bit-depth -define png:color-type" options.
7878%
7879% Note that another definition, "png:bit-depth-written" exists, but it
7880% is not intended for external use. It is only used internally by the
7881% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
7882%
7883% It is possible to request that the PNG encoder write previously-formatted
7884% ancillary chunks in the output PNG file, using the "-profile" commandline
7885% option as shown below or by setting the profile via a programming
7886% interface:
7887%
7888% -profile PNG-chunk-x:<file>
7889%
7890% where x is a location flag and <file> is a file containing the chunk
7891% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
7892%
7893% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
7894% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
7895% of the same type, then add a short unique string after the "x" to prevent
7896% subsequent profiles from overwriting the preceding ones:
7897%
7898% -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
7899%
7900%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7901*/
7902static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
7903 Image *image)
7904{
7905 MagickBooleanType
7906 status;
7907
7908 MngInfo
7909 *mng_info;
7910
7911 const char
7912 *value;
7913
7914 int
7915 have_mng_structure;
7916
7917 unsigned int
7918 logging;
7919
7920 /*
7921 Open image file.
7922 */
7923 assert(image_info != (const ImageInfo *) NULL);
7924 assert(image_info->signature == MagickSignature);
7925 assert(image != (Image *) NULL);
7926 assert(image->signature == MagickSignature);
7927 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
7928 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
7929 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
7930 if (status == MagickFalse)
7931 return(MagickFalse);
7932 /*
7933 Allocate a MngInfo structure.
7934 */
7935 have_mng_structure=MagickFalse;
7936 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7937 if (mng_info == (MngInfo *) NULL)
7938 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7939 /*
7940 Initialize members of the MngInfo structure.
7941 */
7942 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
7943 mng_info->image=image;
7944 have_mng_structure=MagickTrue;
7945
7946 /* See if user has requested a specific PNG subformat */
7947
7948 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
7949 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
7950 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
7951
7952 if (mng_info->write_png8)
7953 {
7954 mng_info->write_png_colortype = /* 3 */ 4;
7955 mng_info->write_png_depth = 8;
7956 image->depth = 8;
7957#if 0 /* this does not work */
7958 if (image->matte == MagickTrue)
7959 (void) SetImageType(image,PaletteMatteType);
7960 else
7961 (void) SetImageType(image,PaletteType);
7962 (void) SyncImage(image);
7963#endif
7964 }
7965
7966 if (mng_info->write_png24)
7967 {
7968 mng_info->write_png_colortype = /* 2 */ 3;
7969 mng_info->write_png_depth = 8;
7970 image->depth = 8;
7971 if (image->matte == MagickTrue)
7972 (void) SetImageType(image,TrueColorMatteType);
7973 else
7974 (void) SetImageType(image,TrueColorType);
7975 (void) SyncImage(image);
7976 }
7977
7978 if (mng_info->write_png32)
7979 {
7980 mng_info->write_png_colortype = /* 6 */ 7;
7981 mng_info->write_png_depth = 8;
7982 image->depth = 8;
7983 if (image->matte == MagickTrue)
7984 (void) SetImageType(image,TrueColorMatteType);
7985 else
7986 (void) SetImageType(image,TrueColorType);
7987 (void) SyncImage(image);
7988 }
7989
7990 value=GetImageOption(image_info,"png:bit-depth");
7991 if (value != (char *) NULL)
7992 {
7993 if (LocaleCompare(value,"1") == 0)
7994 mng_info->write_png_depth = 1;
7995 else if (LocaleCompare(value,"2") == 0)
7996 mng_info->write_png_depth = 2;
7997 else if (LocaleCompare(value,"4") == 0)
7998 mng_info->write_png_depth = 4;
7999 else if (LocaleCompare(value,"8") == 0)
8000 mng_info->write_png_depth = 8;
8001 else if (LocaleCompare(value,"16") == 0)
8002 mng_info->write_png_depth = 16;
8003 if (logging != MagickFalse)
8004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8005 "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
8006 }
8007 value=GetImageOption(image_info,"png:color-type");
8008 if (value != (char *) NULL)
8009 {
8010 /* We must store colortype+1 because 0 is a valid colortype */
8011 if (LocaleCompare(value,"0") == 0)
8012 mng_info->write_png_colortype = 1;
8013 else if (LocaleCompare(value,"2") == 0)
8014 mng_info->write_png_colortype = 3;
8015 else if (LocaleCompare(value,"3") == 0)
8016 mng_info->write_png_colortype = 4;
8017 else if (LocaleCompare(value,"4") == 0)
8018 mng_info->write_png_colortype = 5;
8019 else if (LocaleCompare(value,"6") == 0)
8020 mng_info->write_png_colortype = 7;
8021 if (logging != MagickFalse)
8022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8023 "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
8024 }
8025
8026 status=WriteOnePNGImage(mng_info,image_info,image);
8027
8028 (void) CloseBlob(image);
8029
8030 MngInfoFreeStruct(mng_info,&have_mng_structure);
8031 if (logging != MagickFalse)
8032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8033 return(status);
8034}
8035
8036#if defined(JNG_SUPPORTED)
8037
8038/* Write one JNG image */
8039static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8040 const ImageInfo *image_info,Image *image)
8041{
8042 Image
8043 *jpeg_image;
8044
8045 ImageInfo
8046 *jpeg_image_info;
8047
8048 MagickBooleanType
8049 status;
8050
8051 size_t
8052 length;
8053
8054 unsigned char
8055 *blob,
8056 chunk[80],
8057 *p;
8058
8059 unsigned int
8060 jng_alpha_compression_method,
8061 jng_alpha_sample_depth,
8062 jng_color_type,
8063 logging,
8064 transparent;
8065
8066 unsigned long
8067 jng_quality;
8068
8069 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8070 " enter WriteOneJNGImage()");
8071
8072 blob=(unsigned char *) NULL;
8073 jpeg_image=(Image *) NULL;
8074 jpeg_image_info=(ImageInfo *) NULL;
8075
8076 status=MagickTrue;
8077 transparent=image_info->type==GrayscaleMatteType ||
8078 image_info->type==TrueColorMatteType;
8079 jng_color_type=10;
8080 jng_alpha_sample_depth=0;
8081 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8082 jng_alpha_compression_method=0;
8083
8084 if (image->matte != MagickFalse)
8085 {
8086 /* if any pixels are transparent */
8087 transparent=MagickTrue;
8088 if (image_info->compression==JPEGCompression)
8089 jng_alpha_compression_method=8;
8090 }
8091
8092 if (transparent)
8093 {
8094 jng_color_type=14;
8095 /* Create JPEG blob, image, and image_info */
8096 if (logging != MagickFalse)
8097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8098 " Creating jpeg_image_info for opacity.");
8099 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8100 if (jpeg_image_info == (ImageInfo *) NULL)
8101 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8102 if (logging != MagickFalse)
8103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8104 " Creating jpeg_image.");
8105 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8106 if (jpeg_image == (Image *) NULL)
8107 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8108 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8109 status=SeparateImageChannel(jpeg_image,OpacityChannel);
8110 status=NegateImage(jpeg_image,MagickFalse);
8111 jpeg_image->matte=MagickFalse;
8112 if (jng_quality >= 1000)
8113 jpeg_image_info->quality=jng_quality/1000;
8114 else
8115 jpeg_image_info->quality=jng_quality;
8116 jpeg_image_info->type=GrayscaleType;
8117 (void) SetImageType(jpeg_image,GrayscaleType);
8118 (void) AcquireUniqueFilename(jpeg_image->filename);
8119 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8120 "%s",jpeg_image->filename);
8121 }
8122
8123 /* To do: check bit depth of PNG alpha channel */
8124
8125 /* Check if image is grayscale. */
8126 if (image_info->type != TrueColorMatteType && image_info->type !=
8127 TrueColorType && ImageIsGray(image))
8128 jng_color_type-=2;
8129
8130 if (transparent)
8131 {
8132 if (jng_alpha_compression_method==0)
8133 {
8134 const char
8135 *value;
8136
8137 /* Encode opacity as a grayscale PNG blob */
8138 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8139 &image->exception);
8140 if (logging != MagickFalse)
8141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8142 " Creating PNG blob.");
8143 length=0;
8144
8145 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8146 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8147 jpeg_image_info->interlace=NoInterlace;
8148
8149 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8150 &image->exception);
8151
8152 /* Retrieve sample depth used */
8153 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8154 if (value != (char *) NULL)
8155 jng_alpha_sample_depth= (unsigned int) value[0];
8156 }
8157 else
8158 {
8159 /* Encode opacity as a grayscale JPEG blob */
8160
8161 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8162 &image->exception);
8163
8164 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8165 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8166 jpeg_image_info->interlace=NoInterlace;
8167 if (logging != MagickFalse)
8168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8169 " Creating blob.");
8170 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8171 &image->exception);
8172 jng_alpha_sample_depth=8;
8173 if (logging != MagickFalse)
8174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8175 " Successfully read jpeg_image into a blob, length=%lu.",
8176 (unsigned long) length);
8177
8178 }
8179 /* Destroy JPEG image and image_info */
8180 jpeg_image=DestroyImage(jpeg_image);
8181 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8182 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8183 }
8184
8185 /* Write JHDR chunk */
8186 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
8187 PNGType(chunk,mng_JHDR);
8188 LogPNGChunk((int) logging,mng_JHDR,16L);
8189 PNGLong(chunk+4,image->columns);
8190 PNGLong(chunk+8,image->rows);
8191 chunk[12]=jng_color_type;
8192 chunk[13]=8; /* sample depth */
8193 chunk[14]=8; /*jng_image_compression_method */
8194 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8195 chunk[16]=jng_alpha_sample_depth;
8196 chunk[17]=jng_alpha_compression_method;
8197 chunk[18]=0; /*jng_alpha_filter_method */
8198 chunk[19]=0; /*jng_alpha_interlace_method */
8199 (void) WriteBlob(image,20,chunk);
8200 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8201 if (logging != MagickFalse)
8202 {
8203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8204 " JNG width:%15lu",image->columns);
8205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8206 " JNG height:%14lu",image->rows);
8207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8208 " JNG color type:%10d",jng_color_type);
8209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8210 " JNG sample depth:%8d",8);
8211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8212 " JNG compression:%9d",8);
8213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8214 " JNG interlace:%11d",0);
8215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8216 " JNG alpha depth:%9d",jng_alpha_sample_depth);
8217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8218 " JNG alpha compression:%3d",jng_alpha_compression_method);
8219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8220 " JNG alpha filter:%8d",0);
8221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8222 " JNG alpha interlace:%5d",0);
8223 }
8224
8225 /* Write any JNG-chunk-b profiles */
8226 (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8227
8228 /*
8229 Write leading ancillary chunks
8230 */
8231
8232 if (transparent)
8233 {
8234 /*
8235 Write JNG bKGD chunk
8236 */
8237
8238 unsigned char
8239 blue,
8240 green,
8241 red;
8242
8243 long
8244 num_bytes;
8245
8246 if (jng_color_type == 8 || jng_color_type == 12)
8247 num_bytes=6L;
8248 else
8249 num_bytes=10L;
8250 (void) WriteBlobMSBULong(image,(unsigned long) (num_bytes-4L));
8251 PNGType(chunk,mng_bKGD);
8252 LogPNGChunk((int) logging,mng_bKGD,(unsigned long) (num_bytes-4L));
8253 red=ScaleQuantumToChar(image->background_color.red);
8254 green=ScaleQuantumToChar(image->background_color.green);
8255 blue=ScaleQuantumToChar(image->background_color.blue);
8256 *(chunk+4)=0;
8257 *(chunk+5)=red;
8258 *(chunk+6)=0;
8259 *(chunk+7)=green;
8260 *(chunk+8)=0;
8261 *(chunk+9)=blue;
8262 (void) WriteBlob(image,(size_t) num_bytes,chunk);
8263 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8264 }
8265
8266 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8267 {
8268 /*
8269 Write JNG sRGB chunk
8270 */
8271 (void) WriteBlobMSBULong(image,1L);
8272 PNGType(chunk,mng_sRGB);
8273 LogPNGChunk((int) logging,mng_sRGB,1L);
8274 if (image->rendering_intent != UndefinedIntent)
8275 chunk[4]=(unsigned char) (image->rendering_intent-1);
8276 else
8277 chunk[4]=(unsigned char) (PerceptualIntent-1);
8278 (void) WriteBlob(image,5,chunk);
8279 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8280 }
8281 else
8282 {
8283 if (image->gamma != 0.0)
8284 {
8285 /*
8286 Write JNG gAMA chunk
8287 */
8288 (void) WriteBlobMSBULong(image,4L);
8289 PNGType(chunk,mng_gAMA);
8290 LogPNGChunk((int) logging,mng_gAMA,4L);
8291 PNGLong(chunk+4,(unsigned long) (100000*image->gamma+0.5));
8292 (void) WriteBlob(image,8,chunk);
8293 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8294 }
8295 if ((mng_info->equal_chrms == MagickFalse) &&
8296 (image->chromaticity.red_primary.x != 0.0))
8297 {
8298 PrimaryInfo
8299 primary;
8300
8301 /*
8302 Write JNG cHRM chunk
8303 */
8304 (void) WriteBlobMSBULong(image,32L);
8305 PNGType(chunk,mng_cHRM);
8306 LogPNGChunk((int) logging,mng_cHRM,32L);
8307 primary=image->chromaticity.white_point;
8308 PNGLong(chunk+4,(unsigned long) (100000*primary.x+0.5));
8309 PNGLong(chunk+8,(unsigned long) (100000*primary.y+0.5));
8310 primary=image->chromaticity.red_primary;
8311 PNGLong(chunk+12,(unsigned long) (100000*primary.x+0.5));
8312 PNGLong(chunk+16,(unsigned long) (100000*primary.y+0.5));
8313 primary=image->chromaticity.green_primary;
8314 PNGLong(chunk+20,(unsigned long) (100000*primary.x+0.5));
8315 PNGLong(chunk+24,(unsigned long) (100000*primary.y+0.5));
8316 primary=image->chromaticity.blue_primary;
8317 PNGLong(chunk+28,(unsigned long) (100000*primary.x+0.5));
8318 PNGLong(chunk+32,(unsigned long) (100000*primary.y+0.5));
8319 (void) WriteBlob(image,36,chunk);
8320 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8321 }
8322 }
8323 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8324 {
8325 /*
8326 Write JNG pHYs chunk
8327 */
8328 (void) WriteBlobMSBULong(image,9L);
8329 PNGType(chunk,mng_pHYs);
8330 LogPNGChunk((int) logging,mng_pHYs,9L);
8331 if (image->units == PixelsPerInchResolution)
8332 {
8333 PNGLong(chunk+4,(unsigned long)
8334 (image->x_resolution*100.0/2.54+0.5));
8335 PNGLong(chunk+8,(unsigned long)
8336 (image->y_resolution*100.0/2.54+0.5));
8337 chunk[12]=1;
8338 }
8339 else
8340 {
8341 if (image->units == PixelsPerCentimeterResolution)
8342 {
8343 PNGLong(chunk+4,(unsigned long)
8344 (image->x_resolution*100.0+0.5));
8345 PNGLong(chunk+8,(unsigned long)
8346 (image->y_resolution*100.0+0.5));
8347 chunk[12]=1;
8348 }
8349 else
8350 {
8351 PNGLong(chunk+4,(unsigned long) (image->x_resolution+0.5));
8352 PNGLong(chunk+8,(unsigned long) (image->y_resolution+0.5));
8353 chunk[12]=0;
8354 }
8355 }
8356 (void) WriteBlob(image,13,chunk);
8357 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8358 }
8359
8360 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8361 {
8362 /*
8363 Write JNG oFFs chunk
8364 */
8365 (void) WriteBlobMSBULong(image,9L);
8366 PNGType(chunk,mng_oFFs);
8367 LogPNGChunk((int) logging,mng_oFFs,9L);
8368 PNGsLong(chunk+4,(long) (image->page.x));
8369 PNGsLong(chunk+8,(long) (image->page.y));
8370 chunk[12]=0;
8371 (void) WriteBlob(image,13,chunk);
8372 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8373 }
8374 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8375 {
8376 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8377 PNGType(chunk,mng_vpAg);
8378 LogPNGChunk((int) logging,mng_vpAg,9L);
8379 PNGLong(chunk+4,(png_uint_32) image->page.width);
8380 PNGLong(chunk+8,(png_uint_32) image->page.height);
8381 chunk[12]=0; /* unit = pixels */
8382 (void) WriteBlob(image,13,chunk);
8383 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8384 }
8385
8386
8387 if (transparent)
8388 {
8389 if (jng_alpha_compression_method==0)
8390 {
8391 register long
8392 i;
8393
8394 long
8395 len;
8396
8397 /* Write IDAT chunk header */
8398 if (logging != MagickFalse)
8399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8400 " Write IDAT chunks from blob, length=%lu.",
8401 (unsigned long) length);
8402
8403 /* Copy IDAT chunks */
8404 len=0;
8405 p=blob+8;
8406 for (i=8; i<(long) length; i+=len+12)
8407 {
8408 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8409 p+=4;
8410 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8411 {
8412 /* Found an IDAT chunk. */
8413 (void) WriteBlobMSBULong(image,(unsigned long) len);
8414 LogPNGChunk((int) logging,mng_IDAT,(unsigned long) len);
8415 (void) WriteBlob(image,(size_t) len+4,p);
8416 (void) WriteBlobMSBULong(image,
8417 crc32(0,p,(uInt) len+4));
8418 }
8419 else
8420 {
8421 if (logging != MagickFalse)
8422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8423 " Skipping %c%c%c%c chunk, length=%lu.",
8424 *(p),*(p+1),*(p+2),*(p+3),len);
8425 }
8426 p+=(8+len);
8427 }
8428 }
8429 else
8430 {
8431 /* Write JDAA chunk header */
8432 if (logging != MagickFalse)
8433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8434 " Write JDAA chunk, length=%lu.",
8435 (unsigned long) length);
8436 (void) WriteBlobMSBULong(image,(unsigned long) length);
8437 PNGType(chunk,mng_JDAA);
8438 LogPNGChunk((int) logging,mng_JDAA,length);
8439 /* Write JDAT chunk(s) data */
8440 (void) WriteBlob(image,4,chunk);
8441 (void) WriteBlob(image,length,blob);
8442 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8443 (uInt) length));
8444 }
8445 blob=(unsigned char *) RelinquishMagickMemory(blob);
8446 }
8447
8448 /* Encode image as a JPEG blob */
8449 if (logging != MagickFalse)
8450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451 " Creating jpeg_image_info.");
8452 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8453 if (jpeg_image_info == (ImageInfo *) NULL)
8454 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8455
8456 if (logging != MagickFalse)
8457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8458 " Creating jpeg_image.");
8459
8460 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8461 if (jpeg_image == (Image *) NULL)
8462 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8463 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8464
8465 (void) AcquireUniqueFilename(jpeg_image->filename);
8466 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8467 jpeg_image->filename);
8468
8469 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8470 &image->exception);
8471
8472 if (logging != MagickFalse)
8473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8474 " Created jpeg_image, %lu x %lu.",jpeg_image->columns,
8475 jpeg_image->rows);
8476
8477 if (jng_color_type == 8 || jng_color_type == 12)
8478 jpeg_image_info->type=GrayscaleType;
8479 jpeg_image_info->quality=jng_quality % 1000;
8480 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8481 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8482 if (logging != MagickFalse)
8483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8484 " Creating blob.");
8485 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8486 if (logging != MagickFalse)
8487 {
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " Successfully read jpeg_image into a blob, length=%lu.",
8490 (unsigned long) length);
8491
8492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8493 " Write JDAT chunk, length=%lu.",
8494 (unsigned long) length);
8495 }
8496 /* Write JDAT chunk(s) */
8497 (void) WriteBlobMSBULong(image,(unsigned long) length);
8498 PNGType(chunk,mng_JDAT);
8499 LogPNGChunk((int) logging,mng_JDAT,length);
8500 (void) WriteBlob(image,4,chunk);
8501 (void) WriteBlob(image,length,blob);
8502 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8503
8504 jpeg_image=DestroyImage(jpeg_image);
8505 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8506 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8507 blob=(unsigned char *) RelinquishMagickMemory(blob);
8508
8509 /* Write any JNG-chunk-e profiles */
8510 (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8511
8512 /* Write IEND chunk */
8513 (void) WriteBlobMSBULong(image,0L);
8514 PNGType(chunk,mng_IEND);
8515 LogPNGChunk((int) logging,mng_IEND,0);
8516 (void) WriteBlob(image,4,chunk);
8517 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8518
8519 if (logging != MagickFalse)
8520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8521 " exit WriteOneJNGImage()");
8522 return(status);
8523}
8524
8525
8526/*
8527%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8528% %
8529% %
8530% %
8531% W r i t e J N G I m a g e %
8532% %
8533% %
8534% %
8535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8536%
8537% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8538%
8539% JNG support written by Glenn Randers-Pehrson, glennrp@image...
8540%
8541% The format of the WriteJNGImage method is:
8542%
8543% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8544%
8545% A description of each parameter follows:
8546%
8547% o image_info: the image info.
8548%
8549% o image: The image.
8550%
8551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8552*/
8553static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8554{
8555 MagickBooleanType
8556 status;
8557
8558 MngInfo
8559 *mng_info;
8560
8561 int
8562 have_mng_structure;
8563
8564 unsigned int
8565 logging;
8566
8567 /*
8568 Open image file.
8569 */
8570 assert(image_info != (const ImageInfo *) NULL);
8571 assert(image_info->signature == MagickSignature);
8572 assert(image != (Image *) NULL);
8573 assert(image->signature == MagickSignature);
8574 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8575 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8576 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8577 if (status == MagickFalse)
8578 return(status);
8579
8580 /*
8581 Allocate a MngInfo structure.
8582 */
8583 have_mng_structure=MagickFalse;
8584 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
8585 if (mng_info == (MngInfo *) NULL)
8586 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8587 /*
8588 Initialize members of the MngInfo structure.
8589 */
8590 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8591 mng_info->image=image;
8592 have_mng_structure=MagickTrue;
8593
8594 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8595
8596 status=WriteOneJNGImage(mng_info,image_info,image);
8597 (void) CloseBlob(image);
8598
8599 (void) CatchImageException(image);
8600 MngInfoFreeStruct(mng_info,&have_mng_structure);
8601 if (logging != MagickFalse)
8602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8603 return(status);
8604}
8605#endif
8606
8607
8608
8609static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8610{
8611 const char
8612 *option;
8613
8614 Image
8615 *next_image;
8616
8617 MagickBooleanType
8618 status;
8619
8620 MngInfo
8621 *mng_info;
8622
8623 int
8624 have_mng_structure,
8625 image_count,
8626 need_iterations,
8627 need_matte;
8628
8629 volatile int
8630#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8631 defined(PNG_MNG_FEATURES_SUPPORTED)
8632 need_local_plte,
8633#endif
8634 all_images_are_gray,
8635 logging,
8636 need_defi,
8637 optimize,
8638 use_global_plte;
8639
8640 register long
8641 i;
8642
8643 unsigned char
8644 chunk[800];
8645
8646 volatile unsigned int
8647 write_jng,
8648 write_mng;
8649
8650 volatile unsigned long
8651 scene;
8652
8653 unsigned long
8654 final_delay=0,
8655 initial_delay;
8656
8657#if (PNG_LIBPNG_VER < 10007)
8658 if (image_info->verbose)
8659 printf("Your PNG library (libpng-%s) is rather old.\n",
8660 PNG_LIBPNG_VER_STRING);
8661#endif
8662
8663 /*
8664 Open image file.
8665 */
8666 assert(image_info != (const ImageInfo *) NULL);
8667 assert(image_info->signature == MagickSignature);
8668 assert(image != (Image *) NULL);
8669 assert(image->signature == MagickSignature);
8670 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8671 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8672 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8673 if (status == MagickFalse)
8674 return(status);
8675
8676 /*
8677 Allocate a MngInfo structure.
8678 */
8679 have_mng_structure=MagickFalse;
8680 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
8681 if (mng_info == (MngInfo *) NULL)
8682 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8683 /*
8684 Initialize members of the MngInfo structure.
8685 */
8686 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8687 mng_info->image=image;
8688 have_mng_structure=MagickTrue;
8689 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
8690
8691 /*
8692 * See if user has requested a specific PNG subformat to be used
8693 * for all of the PNGs in the MNG being written, e.g.,
8694 *
8695 * convert *.png png8:animation.mng
8696 *
8697 * To do: check -define png:bit_depth and png:color_type as well,
8698 * or perhaps use mng:bit_depth and mng:color_type instead for
8699 * global settings.
8700 */
8701
8702 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8703 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8704 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8705
8706 write_jng=MagickFalse;
8707 if (image_info->compression == JPEGCompression)
8708 write_jng=MagickTrue;
8709
8710 mng_info->adjoin=image_info->adjoin &&
8711 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
8712
8713 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8714 optimize=MagickFalse;
8715 else
8716 optimize=(image_info->type == OptimizeType || image_info->type ==
8717 UndefinedType);
8718
8719 if (logging != MagickFalse)
8720 {
8721 /* Log some info about the input */
8722 Image
8723 *p;
8724
8725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8726 " Checking input image(s)");
8727 if (optimize)
8728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8729 " Optimize: TRUE");
8730 else
8731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8732 " Optimize: FALSE");
8733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8734 " Image_info depth: %ld",image_info->depth);
8735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8736 " Type: %d",image_info->type);
8737
8738 scene=0;
8739 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8740 {
8741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8742 " Scene: %ld",scene++);
8743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8744 " Image depth: %lu",p->depth);
8745 if (p->matte)
8746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8747 " Matte: True");
8748 else
8749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8750 " Matte: False");
8751 if (p->storage_class == PseudoClass)
8752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8753 " Storage class: PseudoClass");
8754 else
8755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8756 " Storage class: DirectClass");
8757 if (p->colors)
8758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8759 " Number of colors: %lu",p->colors);
8760 else
8761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8762 " Number of colors: unspecified");
8763 if (mng_info->adjoin == MagickFalse)
8764 break;
8765 }
8766 }
8767
8768 /*
8769 Sometimes we get PseudoClass images whose RGB values don't match
8770 the colors in the colormap. This code syncs the RGB values.
8771 */
8772 {
8773 Image
8774 *p;
8775
8776 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8777 {
8778 if (p->taint && p->storage_class == PseudoClass)
8779 (void) SyncImage(p);
8780 if (mng_info->adjoin == MagickFalse)
8781 break;
8782 }
8783 }
8784
8785#ifdef PNG_BUILD_PALETTE
8786 if (optimize)
8787 {
8788 /*
8789 Sometimes we get DirectClass images that have 256 colors or fewer.
8790 This code will convert them to PseudoClass and build a colormap.
8791 */
8792 Image
8793 *p;
8794
8795 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8796 {
8797 if (p->storage_class != PseudoClass)
8798 {
8799 p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
8800 if (p->colors <= 256)
8801 {
8802 p->colors=0;
8803 if (p->matte != MagickFalse)
8804 (void) SetImageType(p,PaletteMatteType);
8805 else
8806 (void) SetImageType(p,PaletteType);
8807 }
8808 }
8809 if (mng_info->adjoin == MagickFalse)
8810 break;
8811 }
8812 }
8813#endif
8814
8815 use_global_plte=MagickFalse;
8816 all_images_are_gray=MagickFalse;
8817#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8818 need_local_plte=MagickTrue;
8819#endif
8820 need_defi=MagickFalse;
8821 need_matte=MagickFalse;
8822 mng_info->framing_mode=1;
8823 mng_info->old_framing_mode=1;
8824
8825 if (write_mng)
8826 if (image_info->page != (char *) NULL)
8827 {
8828 /*
8829 Determine image bounding box.
8830 */
8831 SetGeometry(image,&mng_info->page);
8832 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
8833 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
8834 }
8835 if (write_mng)
8836 {
8837 unsigned int
8838 need_geom;
8839
8840 unsigned short
8841 red,
8842 green,
8843 blue;
8844
8845 mng_info->page=image->page;
8846 need_geom=MagickTrue;
8847 if (mng_info->page.width || mng_info->page.height)
8848 need_geom=MagickFalse;
8849 /*
8850 Check all the scenes.
8851 */
8852 initial_delay=image->delay;
8853 need_iterations=MagickFalse;
8854 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
8855 mng_info->equal_physs=MagickTrue,
8856 mng_info->equal_gammas=MagickTrue;
8857 mng_info->equal_srgbs=MagickTrue;
8858 mng_info->equal_backgrounds=MagickTrue;
8859 image_count=0;
8860#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8861 defined(PNG_MNG_FEATURES_SUPPORTED)
8862 all_images_are_gray=MagickTrue;
8863 mng_info->equal_palettes=MagickFalse;
8864 need_local_plte=MagickFalse;
8865#endif
8866 for (next_image=image; next_image != (Image *) NULL; )
8867 {
8868 if (need_geom)
8869 {
8870 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
8871 mng_info->page.width=next_image->columns+next_image->page.x;
8872 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
8873 mng_info->page.height=next_image->rows+next_image->page.y;
8874 }
8875 if (next_image->page.x || next_image->page.y)
8876 need_defi=MagickTrue;
8877 if (next_image->matte)
8878 need_matte=MagickTrue;
8879 if ((int) next_image->dispose >= BackgroundDispose)
8880 if (next_image->matte || next_image->page.x || next_image->page.y ||
8881 ((next_image->columns < mng_info->page.width) &&
8882 (next_image->rows < mng_info->page.height)))
8883 mng_info->need_fram=MagickTrue;
8884 if (next_image->iterations)
8885 need_iterations=MagickTrue;
8886 final_delay=next_image->delay;
8887 if (final_delay != initial_delay || final_delay > 1UL*
8888 next_image->ticks_per_second)
8889 mng_info->need_fram=1;
8890#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8891 defined(PNG_MNG_FEATURES_SUPPORTED)
8892 /*
8893 check for global palette possibility.
8894 */
8895 if (image->matte != MagickFalse)
8896 need_local_plte=MagickTrue;
8897 if (need_local_plte == 0)
8898 {
8899 if (ImageIsGray(image) == MagickFalse)
8900 all_images_are_gray=MagickFalse;
8901 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
8902 if (use_global_plte == 0)
8903 use_global_plte=mng_info->equal_palettes;
8904 need_local_plte=!mng_info->equal_palettes;
8905 }
8906#endif
8907 if (GetNextImageInList(next_image) != (Image *) NULL)
8908 {
8909 if (next_image->background_color.red !=
8910 next_image->next->background_color.red ||
8911 next_image->background_color.green !=
8912 next_image->next->background_color.green ||
8913 next_image->background_color.blue !=
8914 next_image->next->background_color.blue)
8915 mng_info->equal_backgrounds=MagickFalse;
8916 if (next_image->gamma != next_image->next->gamma)
8917 mng_info->equal_gammas=MagickFalse;
8918 if (next_image->rendering_intent !=
8919 next_image->next->rendering_intent)
8920 mng_info->equal_srgbs=MagickFalse;
8921 if ((next_image->units != next_image->next->units) ||
8922 (next_image->x_resolution != next_image->next->x_resolution) ||
8923 (next_image->y_resolution != next_image->next->y_resolution))
8924 mng_info->equal_physs=MagickFalse;
8925 if (mng_info->equal_chrms)
8926 {
8927 if (next_image->chromaticity.red_primary.x !=
8928 next_image->next->chromaticity.red_primary.x ||
8929 next_image->chromaticity.red_primary.y !=
8930 next_image->next->chromaticity.red_primary.y ||
8931 next_image->chromaticity.green_primary.x !=
8932 next_image->next->chromaticity.green_primary.x ||
8933 next_image->chromaticity.green_primary.y !=
8934 next_image->next->chromaticity.green_primary.y ||
8935 next_image->chromaticity.blue_primary.x !=
8936 next_image->next->chromaticity.blue_primary.x ||
8937 next_image->chromaticity.blue_primary.y !=
8938 next_image->next->chromaticity.blue_primary.y ||
8939 next_image->chromaticity.white_point.x !=
8940 next_image->next->chromaticity.white_point.x ||
8941 next_image->chromaticity.white_point.y !=
8942 next_image->next->chromaticity.white_point.y)
8943 mng_info->equal_chrms=MagickFalse;
8944 }
8945 }
8946 image_count++;
8947 next_image=GetNextImageInList(next_image);
8948 }
8949 if (image_count < 2)
8950 {
8951 mng_info->equal_backgrounds=MagickFalse;
8952 mng_info->equal_chrms=MagickFalse;
8953 mng_info->equal_gammas=MagickFalse;
8954 mng_info->equal_srgbs=MagickFalse;
8955 mng_info->equal_physs=MagickFalse;
8956 use_global_plte=MagickFalse;
8957#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8958 need_local_plte=MagickTrue;
8959#endif
8960 need_iterations=MagickFalse;
8961 }
8962 if (mng_info->need_fram == MagickFalse)
8963 {
8964 /*
8965 Only certain framing rates 100/n are exactly representable without
8966 the FRAM chunk but we'll allow some slop in VLC files
8967 */
8968 if (final_delay == 0)
8969 {
8970 if (need_iterations != MagickFalse)
8971 {
8972 /*
8973 It's probably a GIF with loop; don't run it *too* fast.
8974 */
8975 final_delay=10;
8976 (void) ThrowMagickException(&image->exception,
8977 GetMagickModule(),CoderError,
8978 "input has zero delay between all frames; assuming 10 cs",
8979 "`%s'","");
8980 }
8981 else
8982 mng_info->ticks_per_second=0;
8983 }
8984 if (final_delay != 0)
8985 mng_info->ticks_per_second=1UL*image->ticks_per_second/final_delay;
8986 if (final_delay > 50)
8987 mng_info->ticks_per_second=2;
8988 if (final_delay > 75)
8989 mng_info->ticks_per_second=1;
8990 if (final_delay > 125)
8991 mng_info->need_fram=MagickTrue;
8992 if (need_defi && final_delay > 2 && (final_delay != 4) &&
8993 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
8994 (final_delay != 25) && (final_delay != 50) && (final_delay !=
8995 1UL*image->ticks_per_second))
8996 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
8997 }
8998 if (mng_info->need_fram != MagickFalse)
8999 mng_info->ticks_per_second=1UL*image->ticks_per_second;
9000 /*
9001 If pseudocolor, we should also check to see if all the
9002 palettes are identical and write a global PLTE if they are.
9003 ../glennrp Feb 99.
9004 */
9005 /*
9006 Write the MNG version 1.0 signature and MHDR chunk.
9007 */
9008 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9009 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
9010 PNGType(chunk,mng_MHDR);
9011 LogPNGChunk((int) logging,mng_MHDR,28L);
9012 PNGLong(chunk+4,mng_info->page.width);
9013 PNGLong(chunk+8,mng_info->page.height);
9014 PNGLong(chunk+12,mng_info->ticks_per_second);
9015 PNGLong(chunk+16,0L); /* layer count=unknown */
9016 PNGLong(chunk+20,0L); /* frame count=unknown */
9017 PNGLong(chunk+24,0L); /* play time=unknown */
9018 if (write_jng)
9019 {
9020 if (need_matte)
9021 {
9022 if (need_defi || mng_info->need_fram || use_global_plte)
9023 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
9024 else
9025 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
9026 }
9027 else
9028 {
9029 if (need_defi || mng_info->need_fram || use_global_plte)
9030 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
9031 else
9032 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
9033 }
9034 }
9035 else
9036 {
9037 if (need_matte)
9038 {
9039 if (need_defi || mng_info->need_fram || use_global_plte)
9040 PNGLong(chunk+28,11L); /* simplicity=LC */
9041 else
9042 PNGLong(chunk+28,9L); /* simplicity=VLC */
9043 }
9044 else
9045 {
9046 if (need_defi || mng_info->need_fram || use_global_plte)
9047 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
9048 else
9049 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
9050 }
9051 }
9052 (void) WriteBlob(image,32,chunk);
9053 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9054 option=GetImageOption(image_info,"mng:need-cacheoff");
9055 if (option != (const char *) NULL)
9056 {
9057 size_t
9058 length;
9059
9060 /*
9061 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9062 */
9063 PNGType(chunk,mng_nEED);
9064 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
9065 (void) WriteBlobMSBULong(image,(unsigned long) length);
9066 LogPNGChunk((int) logging,mng_nEED,(unsigned long) length);
9067 length+=4;
9068 (void) WriteBlob(image,length,chunk);
9069 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9070 }
9071 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9072 (GetNextImageInList(image) != (Image *) NULL) &&
9073 (image->iterations != 1))
9074 {
9075 /*
9076 Write MNG TERM chunk
9077 */
9078 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9079 PNGType(chunk,mng_TERM);
9080 LogPNGChunk((int) logging,mng_TERM,10L);
9081 chunk[4]=3; /* repeat animation */
9082 chunk[5]=0; /* show last frame when done */
9083 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9084 final_delay/MagickMax(image->ticks_per_second,1)));
9085 if (image->iterations == 0)
9086 PNGLong(chunk+10,PNG_UINT_31_MAX);
9087 else
9088 PNGLong(chunk+10,(png_uint_32) image->iterations);
9089 if (logging != MagickFalse)
9090 {
9091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9092 " TERM delay: %lu",
9093 (png_uint_32) (mng_info->ticks_per_second*
9094 final_delay/MagickMax(image->ticks_per_second,1)));
9095 if (image->iterations == 0)
9096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9097 " TERM iterations: %lu",PNG_UINT_31_MAX);
9098 else
9099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9100 " Image iterations: %lu",image->iterations);
9101 }
9102 (void) WriteBlob(image,14,chunk);
9103 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9104 }
9105 /*
9106 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9107 */
9108 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9109 mng_info->equal_srgbs)
9110 {
9111 /*
9112 Write MNG sRGB chunk
9113 */
9114 (void) WriteBlobMSBULong(image,1L);
9115 PNGType(chunk,mng_sRGB);
9116 LogPNGChunk((int) logging,mng_sRGB,1L);
9117 if (image->rendering_intent != UndefinedIntent)
9118 chunk[4]=(unsigned char) (image->rendering_intent-1);
9119 else
9120 chunk[4]=(unsigned char) (PerceptualIntent-1);
9121 (void) WriteBlob(image,5,chunk);
9122 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9123 mng_info->have_write_global_srgb=MagickTrue;
9124 }
9125 else
9126 {
9127 if (image->gamma && mng_info->equal_gammas)
9128 {
9129 /*
9130 Write MNG gAMA chunk
9131 */
9132 (void) WriteBlobMSBULong(image,4L);
9133 PNGType(chunk,mng_gAMA);
9134 LogPNGChunk((int) logging,mng_gAMA,4L);
9135 PNGLong(chunk+4,(unsigned long) (100000*image->gamma+0.5));
9136 (void) WriteBlob(image,8,chunk);
9137 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9138 mng_info->have_write_global_gama=MagickTrue;
9139 }
9140 if (mng_info->equal_chrms)
9141 {
9142 PrimaryInfo
9143 primary;
9144
9145 /*
9146 Write MNG cHRM chunk
9147 */
9148 (void) WriteBlobMSBULong(image,32L);
9149 PNGType(chunk,mng_cHRM);
9150 LogPNGChunk((int) logging,mng_cHRM,32L);
9151 primary=image->chromaticity.white_point;
9152 PNGLong(chunk+4,(unsigned long) (100000*primary.x+0.5));
9153 PNGLong(chunk+8,(unsigned long) (100000*primary.y+0.5));
9154 primary=image->chromaticity.red_primary;
9155 PNGLong(chunk+12,(unsigned long) (100000*primary.x+0.5));
9156 PNGLong(chunk+16,(unsigned long) (100000*primary.y+0.5));
9157 primary=image->chromaticity.green_primary;
9158 PNGLong(chunk+20,(unsigned long) (100000*primary.x+0.5));
9159 PNGLong(chunk+24,(unsigned long) (100000*primary.y+0.5));
9160 primary=image->chromaticity.blue_primary;
9161 PNGLong(chunk+28,(unsigned long) (100000*primary.x+0.5));
9162 PNGLong(chunk+32,(unsigned long) (100000*primary.y+0.5));
9163 (void) WriteBlob(image,36,chunk);
9164 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9165 mng_info->have_write_global_chrm=MagickTrue;
9166 }
9167 }
9168 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9169 {
9170 /*
9171 Write MNG pHYs chunk
9172 */
9173 (void) WriteBlobMSBULong(image,9L);
9174 PNGType(chunk,mng_pHYs);
9175 LogPNGChunk((int) logging,mng_pHYs,9L);
9176 if (image->units == PixelsPerInchResolution)
9177 {
9178 PNGLong(chunk+4,(unsigned long)
9179 (image->x_resolution*100.0/2.54+0.5));
9180 PNGLong(chunk+8,(unsigned long)
9181 (image->y_resolution*100.0/2.54+0.5));
9182 chunk[12]=1;
9183 }
9184 else
9185 {
9186 if (image->units == PixelsPerCentimeterResolution)
9187 {
9188 PNGLong(chunk+4,(unsigned long)
9189 (image->x_resolution*100.0+0.5));
9190 PNGLong(chunk+8,(unsigned long)
9191 (image->y_resolution*100.0+0.5));
9192 chunk[12]=1;
9193 }
9194 else
9195 {
9196 PNGLong(chunk+4,(unsigned long) (image->x_resolution+0.5));
9197 PNGLong(chunk+8,(unsigned long) (image->y_resolution+0.5));
9198 chunk[12]=0;
9199 }
9200 }
9201 (void) WriteBlob(image,13,chunk);
9202 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9203 }
9204 /*
9205 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9206 or does not cover the entire frame.
9207 */
9208 if (write_mng && (image->matte || image->page.x > 0 ||
9209 image->page.y > 0 || (image->page.width &&
9210 (image->page.width+image->page.x < mng_info->page.width))
9211 || (image->page.height && (image->page.height+image->page.y
9212 < mng_info->page.height))))
9213 {
9214 (void) WriteBlobMSBULong(image,6L);
9215 PNGType(chunk,mng_BACK);
9216 LogPNGChunk((int) logging,mng_BACK,6L);
9217 red=ScaleQuantumToShort(image->background_color.red);
9218 green=ScaleQuantumToShort(image->background_color.green);
9219 blue=ScaleQuantumToShort(image->background_color.blue);
9220 PNGShort(chunk+4,red);
9221 PNGShort(chunk+6,green);
9222 PNGShort(chunk+8,blue);
9223 (void) WriteBlob(image,10,chunk);
9224 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9225 if (mng_info->equal_backgrounds)
9226 {
9227 (void) WriteBlobMSBULong(image,6L);
9228 PNGType(chunk,mng_bKGD);
9229 LogPNGChunk((int) logging,mng_bKGD,6L);
9230 (void) WriteBlob(image,10,chunk);
9231 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9232 }
9233 }
9234
9235#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9236 if ((need_local_plte == MagickFalse) &&
9237 (image->storage_class == PseudoClass) &&
9238 (all_images_are_gray == MagickFalse))
9239 {
9240 unsigned long
9241 data_length;
9242
9243 /*
9244 Write MNG PLTE chunk
9245 */
9246 data_length=3*image->colors;
9247 (void) WriteBlobMSBULong(image,data_length);
9248 PNGType(chunk,mng_PLTE);
9249 LogPNGChunk((int) logging,mng_PLTE,data_length);
9250 for (i=0; i < (long) image->colors; i++)
9251 {
9252 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9253 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9254 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9255 }
9256 (void) WriteBlob(image,data_length+4,chunk);
9257 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9258 mng_info->have_write_global_plte=MagickTrue;
9259 }
9260#endif
9261 }
9262 scene=0;
9263 mng_info->delay=0;
9264#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9265 defined(PNG_MNG_FEATURES_SUPPORTED)
9266 mng_info->equal_palettes=MagickFalse;
9267#endif
9268 do
9269 {
9270 if (mng_info->adjoin)
9271 {
9272#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9273 defined(PNG_MNG_FEATURES_SUPPORTED)
9274 /*
9275 If we aren't using a global palette for the entire MNG, check to
9276 see if we can use one for two or more consecutive images.
9277 */
9278 if (need_local_plte && use_global_plte && !all_images_are_gray)
9279 {
9280 if (mng_info->IsPalette)
9281 {
9282 /*
9283 When equal_palettes is true, this image has the same palette
9284 as the previous PseudoClass image
9285 */
9286 mng_info->have_write_global_plte=mng_info->equal_palettes;
9287 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9288 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9289 {
9290 /*
9291 Write MNG PLTE chunk
9292 */
9293 unsigned long
9294 data_length;
9295
9296 data_length=3*image->colors;
9297 (void) WriteBlobMSBULong(image,data_length);
9298 PNGType(chunk,mng_PLTE);
9299 LogPNGChunk((int) logging,mng_PLTE,data_length);
9300 for (i=0; i < (long) image->colors; i++)
9301 {
9302 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9303 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9304 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9305 }
9306 (void) WriteBlob(image,data_length+4,chunk);
9307 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9308 (uInt) (data_length+4)));
9309 mng_info->have_write_global_plte=MagickTrue;
9310 }
9311 }
9312 else
9313 mng_info->have_write_global_plte=MagickFalse;
9314 }
9315#endif
9316 if (need_defi)
9317 {
9318 long
9319 previous_x,
9320 previous_y;
9321
9322 if (scene)
9323 {
9324 previous_x=mng_info->page.x;
9325 previous_y=mng_info->page.y;
9326 }
9327 else
9328 {
9329 previous_x=0;
9330 previous_y=0;
9331 }
9332 mng_info->page=image->page;
9333 if ((mng_info->page.x != previous_x) ||
9334 (mng_info->page.y != previous_y))
9335 {
9336 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
9337 PNGType(chunk,mng_DEFI);
9338 LogPNGChunk((int) logging,mng_DEFI,12L);
9339 chunk[4]=0; /* object 0 MSB */
9340 chunk[5]=0; /* object 0 LSB */
9341 chunk[6]=0; /* visible */
9342 chunk[7]=0; /* abstract */
9343 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9344 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9345 (void) WriteBlob(image,16,chunk);
9346 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9347 }
9348 }
9349 }
9350
9351 mng_info->write_mng=write_mng;
9352
9353 if ((int) image->dispose >= 3)
9354 mng_info->framing_mode=3;
9355
9356 if (mng_info->need_fram && mng_info->adjoin &&
9357 ((image->delay != mng_info->delay) ||
9358 (mng_info->framing_mode != mng_info->old_framing_mode)))
9359 {
9360 if (image->delay == mng_info->delay)
9361 {
9362 /*
9363 Write a MNG FRAM chunk with the new framing mode.
9364 */
9365 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
9366 PNGType(chunk,mng_FRAM);
9367 LogPNGChunk((int) logging,mng_FRAM,1L);
9368 chunk[4]=(unsigned char) mng_info->framing_mode;
9369 (void) WriteBlob(image,5,chunk);
9370 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9371 }
9372 else
9373 {
9374 /*
9375 Write a MNG FRAM chunk with the delay.
9376 */
9377 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9378 PNGType(chunk,mng_FRAM);
9379 LogPNGChunk((int) logging,mng_FRAM,10L);
9380 chunk[4]=(unsigned char) mng_info->framing_mode;
9381 chunk[5]=0; /* frame name separator (no name) */
9382 chunk[6]=2; /* flag for changing default delay */
9383 chunk[7]=0; /* flag for changing frame timeout */
9384 chunk[8]=0; /* flag for changing frame clipping */
9385 chunk[9]=0; /* flag for changing frame sync_id */
9386 PNGLong(chunk+10,(png_uint_32)
9387 ((mng_info->ticks_per_second*
9388 image->delay)/MagickMax(image->ticks_per_second,1)));
9389 (void) WriteBlob(image,14,chunk);
9390 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9391 mng_info->delay=image->delay;
9392 }
9393 mng_info->old_framing_mode=mng_info->framing_mode;
9394 }
9395
9396#if defined(JNG_SUPPORTED)
9397 if (image_info->compression == JPEGCompression)
9398 {
9399 ImageInfo
9400 *write_info;
9401
9402 if (logging != MagickFalse)
9403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9404 " Writing JNG object.");
9405 /* To do: specify the desired alpha compression method. */
9406 write_info=CloneImageInfo(image_info);
9407 write_info->compression=UndefinedCompression;
9408 status=WriteOneJNGImage(mng_info,write_info,image);
9409 write_info=DestroyImageInfo(write_info);
9410 }
9411 else
9412#endif
9413 {
9414 if (logging != MagickFalse)
9415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9416 " Writing PNG object.");
9417 status=WriteOnePNGImage(mng_info,image_info,image);
9418 }
9419
9420 if (status == MagickFalse)
9421 {
9422 MngInfoFreeStruct(mng_info,&have_mng_structure);
9423 (void) CloseBlob(image);
9424 return(MagickFalse);
9425 }
9426 (void) CatchImageException(image);
9427 if (GetNextImageInList(image) == (Image *) NULL)
9428 break;
9429 image=SyncNextImageInList(image);
9430 status=SetImageProgress(image,SaveImagesTag,scene++,
9431 GetImageListLength(image));
9432 if (status == MagickFalse)
9433 break;
9434 } while (mng_info->adjoin);
9435 if (write_mng)
9436 {
9437 while (GetPreviousImageInList(image) != (Image *) NULL)
9438 image=GetPreviousImageInList(image);
9439 /*
9440 Write the MEND chunk.
9441 */
9442 (void) WriteBlobMSBULong(image,0x00000000L);
9443 PNGType(chunk,mng_MEND);
9444 LogPNGChunk((int) logging,mng_MEND,0L);
9445 (void) WriteBlob(image,4,chunk);
9446 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9447 }
9448 /*
9449 Relinquish resources.
9450 */
9451 (void) CloseBlob(image);
9452 MngInfoFreeStruct(mng_info,&have_mng_structure);
9453 if (logging != MagickFalse)
9454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9455 return(MagickTrue);
9456}
9457#else /* PNG_LIBPNG_VER > 95 */
9458static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9459{
9460 image=image;
9461 printf("Your PNG library is too old: You have libpng-%s\n",
9462 PNG_LIBPNG_VER_STRING);
9463 ThrowBinaryException(CoderError,"PNG library is too old",
9464 image_info->filename);
9465}
9466static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9467{
9468 return(WritePNGImage(image_info,image));
9469}
9470#endif /* PNG_LIBPNG_VER > 95 */
9471#endif