2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
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. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "magick/studio.h"
45 #include "magick/attribute.h"
46 #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/colormap.h"
52 #include "magick/colorspace.h"
53 #include "magick/constitute.h"
54 #include "magick/enhance.h"
55 #include "magick/exception.h"
56 #include "magick/exception-private.h"
57 #include "magick/geometry.h"
58 #include "magick/histogram.h"
59 #include "magick/image.h"
60 #include "magick/image-private.h"
61 #include "magick/layer.h"
62 #include "magick/list.h"
63 #include "magick/log.h"
64 #include "magick/magick.h"
65 #include "magick/memory_.h"
66 #include "magick/module.h"
67 #include "magick/monitor.h"
68 #include "magick/monitor-private.h"
69 #include "magick/option.h"
70 #include "magick/quantum-private.h"
71 #include "magick/profile.h"
72 #include "magick/property.h"
73 #include "magick/quantize.h"
74 #include "magick/resource_.h"
75 #include "magick/semaphore.h"
76 #include "magick/quantum-private.h"
77 #include "magick/static.h"
78 #include "magick/statistic.h"
79 #include "magick/string_.h"
80 #include "magick/string-private.h"
81 #include "magick/transform.h"
82 #include "magick/utility.h"
83 #if defined(MAGICKCORE_PNG_DELEGATE)
85 /* Suppress libpng pedantic warnings that were added in
86 * libpng-1.2.41 and libpng-1.4.0. If you are working on
87 * migration to libpng-1.5, remove these defines and then
88 * fix any code that generates warnings.
90 /* #define PNG_DEPRECATED Use of this function is deprecated */
91 /* #define PNG_USE_RESULT The result of this function must be checked */
92 /* #define PNG_NORETURN This function does not return */
93 /* #define PNG_ALLOCATED The result of the function is new memory */
94 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
99 /* ImageMagick differences */
100 #define first_scene scene
102 #if PNG_LIBPNG_VER > 10011
104 Optional declarations. Define or undefine them as you like.
106 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
109 Features under construction. Define these to work on them.
111 #undef MNG_OBJECT_BUFFERS
112 #undef MNG_BASI_SUPPORTED
113 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
114 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
115 #define PNG_BUILD_PALETTE /* This works as of 5.4.3. */
116 #define PNG_SORT_PALETTE /* This works as of 5.4.0. */
117 #if defined(MAGICKCORE_JPEG_DELEGATE)
118 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
120 #if !defined(RGBColorMatchExact)
121 #define IsPNGColorEqual(color,target) \
122 (((color).red == (target).red) && \
123 ((color).green == (target).green) && \
124 ((color).blue == (target).blue))
128 Establish thread safety.
129 setjmp/longjmp is claimed to be safe on these platforms:
130 setjmp/longjmp is alleged to be unsafe on these platforms:
132 #ifndef SETJMP_IS_THREAD_SAFE
133 #define PNG_SETJMP_NOT_THREAD_SAFE
136 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
138 *png_semaphore = (SemaphoreInfo *) NULL;
142 This temporary until I set up malloc'ed object attributes array.
143 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
146 #define MNG_MAX_OBJECTS 256
149 If this not defined, spec is interpreted strictly. If it is
150 defined, an attempt will be made to recover from some errors,
152 o global PLTE too short
157 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
158 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
159 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
160 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
161 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
162 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
163 will be enabled by default in libpng-1.2.0.
165 #ifdef PNG_MNG_FEATURES_SUPPORTED
166 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
167 # define PNG_READ_EMPTY_PLTE_SUPPORTED
169 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
170 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
175 Maximum valid unsigned long in PNG/MNG chunks is (2^31)-1
176 This macro is only defined in libpng-1.0.3 and later.
177 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
179 #ifndef PNG_UINT_31_MAX
180 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
184 Constant strings for known chunk types. If you need to add a chunk,
185 add a string holding the name here. To make the code more
186 portable, we use ASCII numbers like this, not characters.
189 static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
190 static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
191 static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
192 static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
193 static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
194 static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
195 static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
196 static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
197 static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
198 static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
199 static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
200 static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
201 static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
202 static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
203 static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
204 static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
205 static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
206 static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
207 static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
208 static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
209 static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
210 static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
211 static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
212 static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
213 static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
214 static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
215 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
216 static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
217 static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
218 static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
219 static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
220 static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
221 static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
222 static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
224 #if defined(JNG_SUPPORTED)
225 static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
226 static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
227 static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
228 static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
229 static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
230 static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
234 Other known chunks that are not yet supported by ImageMagick:
235 static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
236 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
237 static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
238 static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
239 static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
240 static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
241 static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
242 static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
245 typedef struct _MngBox
254 typedef struct _MngPair
261 #ifdef MNG_OBJECT_BUFFERS
262 typedef struct _MngBuffer
294 typedef struct _MngInfo
297 #ifdef MNG_OBJECT_BUFFERS
299 *ob[MNG_MAX_OBJECTS];
310 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
311 bytes_in_read_buffer,
317 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
318 defined(PNG_MNG_FEATURES_SUPPORTED)
330 have_saved_bkgd_index,
331 have_write_global_chrm,
332 have_write_global_gama,
333 have_write_global_plte,
334 have_write_global_srgb,
349 x_off[MNG_MAX_OBJECTS],
350 y_off[MNG_MAX_OBJECTS];
356 object_clip[MNG_MAX_OBJECTS];
359 /* These flags could be combined into one byte */
360 exists[MNG_MAX_OBJECTS],
361 frozen[MNG_MAX_OBJECTS],
363 invisible[MNG_MAX_OBJECTS],
364 viewable[MNG_MAX_OBJECTS];
376 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
394 global_x_pixels_per_unit,
395 global_y_pixels_per_unit,
402 global_phys_unit_type,
421 #ifdef MNG_BASI_SUPPORTED
429 basi_compression_method,
431 basi_interlace_method,
458 Forward declarations.
460 static MagickBooleanType
461 WritePNGImage(const ImageInfo *,Image *);
462 static MagickBooleanType
463 WriteMNGImage(const ImageInfo *,Image *);
464 #if defined(JNG_SUPPORTED)
465 static MagickBooleanType
466 WriteJNGImage(const ImageInfo *,Image *);
469 static inline long MagickMax(const long x,const long y)
475 static inline long MagickMin(const long x,const long y)
482 #if PNG_LIBPNG_VER > 10011
483 #if defined(PNG_SORT_PALETTE)
485 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
489 % C o m p r e s s C o l o r m a p T r a n s F i r s t %
493 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
495 % CompressColormapTransFirst compresses an image colormap removing
496 % any duplicate and unused color entries and putting the transparent colors
497 % first. Returns MagickTrue on success, MagickFalse on error.
499 % The format of the CompressColormapTransFirst method is:
501 % unsigned int CompressColormapTransFirst(Image *image)
503 % A description of each parameter follows:
505 % o image: the address of a structure of type Image.
506 % This function updates image->colors and image->colormap.
509 static MagickBooleanType CompressColormapTransFirst(Image *image)
524 register const IndexPacket
527 register const PixelPacket
546 Determine if colormap can be compressed.
548 assert(image != (Image *) NULL);
549 assert(image->signature == MagickSignature);
550 if (image->debug != MagickFalse)
551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
552 " CompressColorMapTransFirst %s (%ld colors)",
553 image->filename,image->colors);
554 if (image->storage_class != PseudoClass || image->colors > 256 ||
557 if (image->debug != MagickFalse)
559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
560 " Could not compress colormap");
561 if (image->colors > 256 || image->colors == 0)
567 marker=(unsigned char *) AcquireQuantumMemory(image->colors,sizeof(*marker));
568 if (marker == (unsigned char *) NULL)
569 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
571 opacity=(IndexPacket *) AcquireQuantumMemory(image->colors,sizeof(*opacity));
572 if (opacity == (IndexPacket *) NULL)
574 marker=(unsigned char *) RelinquishMagickMemory(marker);
575 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
579 Mark colors that are present.
581 number_colors=(long) image->colors;
582 for (i=0; i < number_colors; i++)
584 marker[i]=MagickFalse;
585 opacity[i]=OpaqueOpacity;
588 for (y=0; y < (long) image->rows; y++)
590 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
591 if (p == (const PixelPacket *) NULL)
593 indices=GetVirtualIndexQueue(image);
594 if (image->matte != MagickFalse)
595 for (x=0; x < (long) image->columns; x++)
597 marker[(int) indices[x]]=MagickTrue;
598 opacity[(int) indices[x]]=GetOpacityPixelComponent(p);
599 if (indices[x] > top_used)
604 for (x=0; x < (long) image->columns; x++)
606 marker[(int) indices[x]]=MagickTrue;
607 if (indices[x] > top_used)
612 if (image->matte != MagickFalse)
615 Mark background color, topmost occurrence if more than one.
617 for (i=number_colors-1; i; i--)
619 if (IsColorEqual(image->colormap+i,&image->background_color))
621 marker[i]=MagickTrue;
629 for (i=0; i < number_colors-1; i++)
632 for (j=i+1; j < number_colors; j++)
633 if ((opacity[i] == opacity[j]) &&
634 (IsColorEqual(image->colormap+i,image->colormap+j)))
635 marker[j]=MagickFalse;
638 Count colors that still remain.
640 have_transparency=MagickFalse;
642 for (i=0; i < number_colors; i++)
646 if (opacity[i] != OpaqueOpacity)
647 have_transparency=MagickTrue;
649 if ((!have_transparency || (marker[0] &&
650 (opacity[0] == (Quantum) TransparentOpacity)))
651 && (new_number_colors == number_colors))
654 No duplicate or unused entries, and transparency-swap not needed.
656 marker=(unsigned char *) RelinquishMagickMemory(marker);
657 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
661 remap_needed=MagickFalse;
662 if ((long) top_used >= new_number_colors)
663 remap_needed=MagickTrue;
669 colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
671 if (colormap == (PixelPacket *) NULL)
673 marker=(unsigned char *) RelinquishMagickMemory(marker);
674 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
675 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
679 Eliminate unused colormap entries.
681 map=(IndexPacket *) AcquireQuantumMemory((size_t) number_colors,
683 if (map == (IndexPacket *) NULL)
685 marker=(unsigned char *) RelinquishMagickMemory(marker);
686 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
687 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
688 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
692 for (i=0; i < number_colors; i++)
694 map[i]=(IndexPacket) k;
697 for (j=i+1; j < number_colors; j++)
699 if ((opacity[i] == opacity[j]) &&
700 (IsColorEqual(image->colormap+i,image->colormap+j)))
702 map[j]=(IndexPacket) k;
703 marker[j]=MagickFalse;
710 for (i=0; i < number_colors; i++)
714 colormap[j]=image->colormap[i];
718 if (have_transparency && (opacity[0] != (Quantum) TransparentOpacity))
721 Move the first transparent color to palette entry 0.
723 for (i=1; i < number_colors; i++)
725 if (marker[i] && opacity[i] == (Quantum) TransparentOpacity)
730 temp_colormap=colormap[0];
731 colormap[0]=colormap[(int) map[i]];
732 colormap[(long) map[i]]=temp_colormap;
733 for (j=0; j < number_colors; j++)
737 else if (map[j] == map[i])
740 remap_needed=MagickTrue;
746 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
747 marker=(unsigned char *) RelinquishMagickMemory(marker);
763 exception=(&image->exception);
764 for (y=0; y < (long) image->rows; y++)
766 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
767 if (q == (PixelPacket *) NULL)
769 pixels=GetAuthenticIndexQueue(image);
770 for (x=0; x < (long) image->columns; x++)
775 if (SyncAuthenticPixels(image,exception) == MagickFalse)
778 for (i=0; i < new_number_colors; i++)
779 image->colormap[i]=colormap[i];
781 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
782 image->colors=(unsigned long) new_number_colors;
783 map=(IndexPacket *) RelinquishMagickMemory(map);
789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
793 % I m a g e I s G r a y %
797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799 % Like IsGrayImage except does not change DirectClass to PseudoClass %
801 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
803 static MagickBooleanType ImageIsGray(Image *image)
805 register const PixelPacket
813 assert(image != (Image *) NULL);
814 assert(image->signature == MagickSignature);
815 if (image->debug != MagickFalse)
816 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
818 if (image->storage_class == PseudoClass)
820 for (i=0; i < (long) image->colors; i++)
821 if (IsGray(image->colormap+i) == MagickFalse)
825 for (y=0; y < (long) image->rows; y++)
827 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
828 if (p == (const PixelPacket *) NULL)
830 for (x=(long) image->columns-1; x >= 0; x--)
832 if (IsGray(p) == MagickFalse)
841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
845 % I m a g e I s M o n o c h r o m e %
849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
851 % Like IsMonochromeImage except does not change DirectClass to PseudoClass %
852 % and is more accurate. %
854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
856 static MagickBooleanType ImageIsMonochrome(Image *image)
858 register const PixelPacket
866 assert(image != (Image *) NULL);
867 assert(image->signature == MagickSignature);
868 if (image->debug != MagickFalse)
869 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
870 if (image->storage_class == PseudoClass)
872 for (i=0; i < (long) image->colors; i++)
874 if ((IsGray(image->colormap+i) == MagickFalse) ||
875 ((image->colormap[i].red != 0) &&
876 (image->colormap[i].red != (Quantum) QuantumRange)))
881 for (y=0; y < (long) image->rows; y++)
883 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
884 if (p == (const PixelPacket *) NULL)
886 for (x=(long) image->columns-1; x >= 0; x--)
888 if ((p->red != 0) && (p->red != (Quantum) QuantumRange))
890 if (IsGray(p) == MagickFalse)
892 if ((p->opacity != OpaqueOpacity) &&
893 (p->opacity != (Quantum) TransparentOpacity))
900 #endif /* PNG_LIBPNG_VER > 10011 */
901 #endif /* MAGICKCORE_PNG_DELEGATE */
904 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
912 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
914 % IsMNG() returns MagickTrue if the image format type, identified by the
915 % magick string, is MNG.
917 % The format of the IsMNG method is:
919 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
921 % A description of each parameter follows:
923 % o magick: compare image format pattern against these bytes.
925 % o length: Specifies the length of the magick string.
929 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
933 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
939 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
947 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
949 % IsJNG() returns MagickTrue if the image format type, identified by the
950 % magick string, is JNG.
952 % The format of the IsJNG method is:
954 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
956 % A description of each parameter follows:
958 % o magick: compare image format pattern against these bytes.
960 % o length: Specifies the length of the magick string.
964 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
968 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
974 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
982 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
984 % IsPNG() returns MagickTrue if the image format type, identified by the
985 % magick string, is PNG.
987 % The format of the IsPNG method is:
989 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
991 % A description of each parameter follows:
993 % o magick: compare image format pattern against these bytes.
995 % o length: Specifies the length of the magick string.
998 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1001 return(MagickFalse);
1002 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1004 return(MagickFalse);
1007 #if defined(MAGICKCORE_PNG_DELEGATE)
1008 #if defined(__cplusplus) || defined(c_plusplus)
1012 #if (PNG_LIBPNG_VER > 10011)
1013 static size_t WriteBlobMSBULong(Image *image,const unsigned long value)
1018 assert(image != (Image *) NULL);
1019 assert(image->signature == MagickSignature);
1020 buffer[0]=(unsigned char) (value >> 24);
1021 buffer[1]=(unsigned char) (value >> 16);
1022 buffer[2]=(unsigned char) (value >> 8);
1023 buffer[3]=(unsigned char) value;
1024 return((size_t) WriteBlob(image,4,buffer));
1027 static void PNGLong(png_bytep p,png_uint_32 value)
1029 *p++=(png_byte) ((value >> 24) & 0xff);
1030 *p++=(png_byte) ((value >> 16) & 0xff);
1031 *p++=(png_byte) ((value >> 8) & 0xff);
1032 *p++=(png_byte) (value & 0xff);
1035 static void PNGsLong(png_bytep p,png_int_32 value)
1037 *p++=(png_byte) ((value >> 24) & 0xff);
1038 *p++=(png_byte) ((value >> 16) & 0xff);
1039 *p++=(png_byte) ((value >> 8) & 0xff);
1040 *p++=(png_byte) (value & 0xff);
1043 static void PNGShort(png_bytep p,png_uint_16 value)
1045 *p++=(png_byte) ((value >> 8) & 0xff);
1046 *p++=(png_byte) (value & 0xff);
1049 static void PNGType(png_bytep p,png_bytep type)
1051 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1054 static void LogPNGChunk(int logging, png_bytep type, size_t length)
1056 if (logging != MagickFalse)
1057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1058 " Writing %c%c%c%c chunk, length: %lu",
1059 type[0],type[1],type[2],type[3],(unsigned long) length);
1061 #endif /* PNG_LIBPNG_VER > 10011 */
1063 #if defined(__cplusplus) || defined(c_plusplus)
1067 #if PNG_LIBPNG_VER > 10011
1069 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1073 % R e a d P N G I m a g e %
1077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1080 % Multiple-image Network Graphics (MNG) image file and returns it. It
1081 % allocates the memory necessary for the new Image structure and returns a
1082 % pointer to the new image or set of images.
1084 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1086 % The format of the ReadPNGImage method is:
1088 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1090 % A description of each parameter follows:
1092 % o image_info: the image info.
1094 % o exception: return any errors or warnings in this structure.
1096 % To do, more or less in chronological order (as of version 5.5.2,
1097 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1099 % Get 16-bit cheap transparency working.
1101 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1103 % Preserve all unknown and not-yet-handled known chunks found in input
1104 % PNG file and copy them into output PNG files according to the PNG
1107 % (At this point, PNG encoding should be in full MNG compliance)
1109 % Provide options for choice of background to use when the MNG BACK
1110 % chunk is not present or is not mandatory (i.e., leave transparent,
1111 % user specified, MNG BACK, PNG bKGD)
1113 % Implement LOOP/ENDL [done, but could do discretionary loops more
1114 % efficiently by linking in the duplicate frames.].
1116 % Decode and act on the MHDR simplicity profile (offer option to reject
1117 % files or attempt to process them anyway when the profile isn't LC or VLC).
1119 % Upgrade to full MNG without Delta-PNG.
1121 % o BACK [done a while ago except for background image ID]
1122 % o MOVE [done 15 May 1999]
1123 % o CLIP [done 15 May 1999]
1124 % o DISC [done 19 May 1999]
1125 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1126 % o SEEK [partially done 19 May 1999 (discard function only)]
1130 % o MNG-level tEXt/iTXt/zTXt
1135 % o iTXt (wait for libpng implementation).
1137 % Use the scene signature to discover when an identical scene is
1138 % being reused, and just point to the original image->exception instead
1139 % of storing another set of pixels. This not specific to MNG
1140 % but could be applied generally.
1142 % Upgrade to full MNG with Delta-PNG.
1144 % JNG tEXt/iTXt/zTXt
1146 % We will not attempt to read files containing the CgBI chunk.
1147 % They are really Xcode files meant for display on the iPhone.
1148 % These are not valid PNG files and it is impossible to recover
1149 % the orginal PNG from files that have been converted to Xcode-PNG,
1150 % since irretrievable loss of color data has occurred due to the
1151 % use of premultiplied alpha.
1154 #if defined(__cplusplus) || defined(c_plusplus)
1159 This the function that does the actual reading of data. It is
1160 the same as the one supplied in libpng, except that it receives the
1161 datastream from the ReadBlob() function instead of standard input.
1163 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1168 image=(Image *) png_get_io_ptr(png_ptr);
1174 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1175 if (check != length)
1180 (void) FormatMagickString(msg,MaxTextExtent,
1181 "Expected %lu bytes; found %lu bytes",(unsigned long) length,
1182 (unsigned long) check);
1183 png_warning(png_ptr,msg);
1184 png_error(png_ptr,"Read Exception");
1189 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1190 !defined(PNG_MNG_FEATURES_SUPPORTED)
1191 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1192 * older than libpng-1.0.3a, which was the first to allow the empty
1193 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1194 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1195 * encountered after an empty PLTE, so we have to look ahead for bKGD
1196 * chunks and remove them from the datastream that is passed to libpng,
1197 * and store their contents for later use.
1199 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1214 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1215 image=(Image *) mng_info->image;
1216 while (mng_info->bytes_in_read_buffer && length)
1218 data[i]=mng_info->read_buffer[i];
1219 mng_info->bytes_in_read_buffer--;
1225 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1226 if (check != length)
1227 png_error(png_ptr,"Read Exception");
1230 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1233 check=(png_size_t) ReadBlob(image,(size_t) length,
1234 (char *) mng_info->read_buffer);
1235 mng_info->read_buffer[4]=0;
1236 mng_info->bytes_in_read_buffer=4;
1237 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1238 mng_info->found_empty_plte=MagickTrue;
1239 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1241 mng_info->found_empty_plte=MagickFalse;
1242 mng_info->have_saved_bkgd_index=MagickFalse;
1245 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1248 check=(png_size_t) ReadBlob(image,(size_t) length,
1249 (char *) mng_info->read_buffer);
1250 mng_info->read_buffer[4]=0;
1251 mng_info->bytes_in_read_buffer=4;
1252 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1253 if (mng_info->found_empty_plte)
1256 Skip the bKGD data byte and CRC.
1259 ReadBlob(image,5,(char *) mng_info->read_buffer);
1260 check=(png_size_t) ReadBlob(image,(size_t) length,
1261 (char *) mng_info->read_buffer);
1262 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1263 mng_info->have_saved_bkgd_index=MagickTrue;
1264 mng_info->bytes_in_read_buffer=0;
1272 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1277 image=(Image *) png_get_io_ptr(png_ptr);
1283 check=(png_size_t) WriteBlob(image,(unsigned long) length,data);
1284 if (check != length)
1285 png_error(png_ptr,"WriteBlob Failed");
1289 static void png_flush_data(png_structp png_ptr)
1294 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1295 static int PalettesAreEqual(Image *a,Image *b)
1300 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1301 return((int) MagickFalse);
1302 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1303 return((int) MagickFalse);
1304 if (a->colors != b->colors)
1305 return((int) MagickFalse);
1306 for (i=0; i < (long) a->colors; i++)
1308 if ((a->colormap[i].red != b->colormap[i].red) ||
1309 (a->colormap[i].green != b->colormap[i].green) ||
1310 (a->colormap[i].blue != b->colormap[i].blue))
1311 return((int) MagickFalse);
1313 return((int) MagickTrue);
1317 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1319 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1320 mng_info->exists[i] && !mng_info->frozen[i])
1322 #ifdef MNG_OBJECT_BUFFERS
1323 if (mng_info->ob[i] != (MngBuffer *) NULL)
1325 if (mng_info->ob[i]->reference_count > 0)
1326 mng_info->ob[i]->reference_count--;
1327 if (mng_info->ob[i]->reference_count == 0)
1329 if (mng_info->ob[i]->image != (Image *) NULL)
1330 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1331 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1334 mng_info->ob[i]=(MngBuffer *) NULL;
1336 mng_info->exists[i]=MagickFalse;
1337 mng_info->invisible[i]=MagickFalse;
1338 mng_info->viewable[i]=MagickFalse;
1339 mng_info->frozen[i]=MagickFalse;
1340 mng_info->x_off[i]=0;
1341 mng_info->y_off[i]=0;
1342 mng_info->object_clip[i].left=0;
1343 mng_info->object_clip[i].right=(long) PNG_UINT_31_MAX;
1344 mng_info->object_clip[i].top=0;
1345 mng_info->object_clip[i].bottom=(long) PNG_UINT_31_MAX;
1349 static void MngInfoFreeStruct(MngInfo *mng_info,int *have_mng_structure)
1351 if (*have_mng_structure && (mng_info != (MngInfo *) NULL))
1356 for (i=1; i < MNG_MAX_OBJECTS; i++)
1357 MngInfoDiscardObject(mng_info,i);
1358 if (mng_info->global_plte != (png_colorp) NULL)
1359 mng_info->global_plte=(png_colorp)
1360 RelinquishMagickMemory(mng_info->global_plte);
1361 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1362 *have_mng_structure=MagickFalse;
1366 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1372 if (box.left < box2.left)
1374 if (box.top < box2.top)
1376 if (box.right > box2.right)
1377 box.right=box2.right;
1378 if (box.bottom > box2.bottom)
1379 box.bottom=box2.bottom;
1383 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1389 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1391 box.left=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1392 box.right=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1393 box.top=(long) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1394 box.bottom=(long) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1395 if (delta_type != 0)
1397 box.left+=previous_box.left;
1398 box.right+=previous_box.right;
1399 box.top+=previous_box.top;
1400 box.bottom+=previous_box.bottom;
1405 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1411 Read two longs from CLON, MOVE or PAST chunk
1413 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1414 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1415 if (delta_type != 0)
1417 pair.a+=previous_pair.a;
1418 pair.b+=previous_pair.b;
1423 static long mng_get_long(unsigned char *p)
1425 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1428 static void PNGErrorHandler(png_struct *ping,png_const_charp message)
1433 image=(Image *) png_get_error_ptr(ping);
1434 if (image->debug != MagickFalse)
1435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1436 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1437 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1438 message,"`%s'",image->filename);
1439 #if PNG_LIBPNG_VER < 10500
1440 longjmp(ping->jmpbuf,1);
1442 png_longjmp(ping,1);
1446 static void PNGWarningHandler(png_struct *ping,png_const_charp message)
1451 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1452 png_error(ping, message);
1453 image=(Image *) png_get_error_ptr(ping);
1454 if (image->debug != MagickFalse)
1455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1456 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,
1458 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1459 message,"`%s'",image->filename);
1462 #ifdef PNG_USER_MEM_SUPPORTED
1463 static png_voidp png_IM_malloc(png_structp png_ptr,png_uint_32 size)
1465 #if (PNG_LIBPNG_VER < 10011)
1470 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1472 png_error("Insufficient memory.");
1476 return((png_voidp) AcquireMagickMemory((size_t) size));
1481 Free a pointer. It is removed from the list at the same time.
1483 static png_free_ptr png_IM_free(png_structp png_ptr,png_voidp ptr)
1486 ptr=RelinquishMagickMemory(ptr);
1487 return((png_free_ptr) NULL);
1491 #if defined(__cplusplus) || defined(c_plusplus)
1496 png_read_raw_profile(Image *image, const ImageInfo *image_info,
1497 png_textp text,int ii)
1502 register unsigned char
1516 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1517 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1518 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1519 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1520 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1524 /* look for newline */
1527 /* look for length */
1528 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1530 length=(png_uint_32) StringToLong(sp);
1531 while (*sp != ' ' && *sp != '\n')
1533 /* allocate space */
1536 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1537 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1538 return(MagickFalse);
1540 profile=AcquireStringInfo(length);
1541 if (profile == (StringInfo *) NULL)
1543 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1544 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1545 "unable to copy profile");
1546 return(MagickFalse);
1548 /* copy profile, skipping white space and column 1 "=" signs */
1549 dp=GetStringInfoDatum(profile);
1551 for (i=0; i < (long) nibbles; i++)
1553 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1557 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1558 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1559 profile=DestroyStringInfo(profile);
1560 return(MagickFalse);
1565 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1567 (*dp++)+=unhex[(int) *sp++];
1570 We have already read "Raw profile type.
1572 (void) SetImageProfile(image,&text[ii].key[17],profile);
1573 profile=DestroyStringInfo(profile);
1574 if (image_info->verbose)
1575 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1579 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1580 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1586 /* The unknown chunk structure contains the chunk data:
1591 Note that libpng has already taken care of the CRC handling.
1595 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1596 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1597 return(0); /* Did not recognize */
1599 /* recognized vpAg */
1601 if (chunk->size != 9)
1602 return(-1); /* Error return */
1604 if (chunk->data[8] != 0)
1605 return(0); /* ImageMagick requires pixel units */
1607 image=(Image *) png_get_user_chunk_ptr(ping);
1609 image->page.width=(unsigned long) ((chunk->data[0] << 24) |
1610 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1611 image->page.height=(unsigned long) ((chunk->data[4] << 24) |
1612 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1614 /* Return one of the following: */
1615 /* return(-n); chunk had an error */
1616 /* return(0); did not recognize */
1617 /* return(n); success */
1625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1629 % R e a d O n e P N G I m a g e %
1633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1635 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1636 % (minus the 8-byte signature) and returns it. It allocates the memory
1637 % necessary for the new Image structure and returns a pointer to the new
1640 % The format of the ReadOnePNGImage method is:
1642 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1643 % ExceptionInfo *exception)
1645 % A description of each parameter follows:
1647 % o mng_info: Specifies a pointer to a MngInfo structure.
1649 % o image_info: the image info.
1651 % o exception: return any errors or warnings in this structure.
1654 static Image *ReadOnePNGImage(MngInfo *mng_info,
1655 const ImageInfo *image_info, ExceptionInfo *exception)
1657 /* Read one PNG image */
1669 ping_interlace_method,
1670 ping_compression_method,
1711 register unsigned char
1714 register IndexPacket
1721 register PixelPacket
1730 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1731 png_byte unused_chunks[]=
1733 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1734 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1735 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1736 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1737 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1738 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1742 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1743 " enter ReadOnePNGImage()");
1745 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1746 LockSemaphoreInfo(png_semaphore);
1749 #if (PNG_LIBPNG_VER < 10200)
1750 if (image_info->verbose)
1751 printf("Your PNG library (libpng-%s) is rather old.\n",
1752 PNG_LIBPNG_VER_STRING);
1755 #if (PNG_LIBPNG_VER >= 10400)
1756 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1757 if (image_info->verbose)
1759 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1760 PNG_LIBPNG_VER_STRING);
1761 printf("Please update it.\n");
1767 quantum_info = (QuantumInfo *) NULL;
1768 image=mng_info->image;
1771 Allocate the PNG structures
1773 #ifdef PNG_USER_MEM_SUPPORTED
1774 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1775 PNGErrorHandler,PNGWarningHandler, NULL,
1776 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
1778 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1779 PNGErrorHandler,PNGWarningHandler);
1781 if (ping == (png_struct *) NULL)
1782 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1783 ping_info=png_create_info_struct(ping);
1784 if (ping_info == (png_info *) NULL)
1786 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1787 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1789 end_info=png_create_info_struct(ping);
1790 if (end_info == (png_info *) NULL)
1792 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1793 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1795 png_pixels=(unsigned char *) NULL;
1796 if (setjmp(png_jmpbuf(ping)))
1799 PNG image is corrupt.
1801 png_destroy_read_struct(&ping,&ping_info,&end_info);
1802 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1803 UnlockSemaphoreInfo(png_semaphore);
1805 if (logging != MagickFalse)
1806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1807 " exit ReadOnePNGImage() with error.");
1808 if (image != (Image *) NULL)
1810 InheritException(exception,&image->exception);
1813 return(GetFirstImageInList(image));
1816 Prepare PNG for reading.
1819 mng_info->image_found++;
1820 png_set_sig_bytes(ping,8);
1821 if (LocaleCompare(image_info->magick,"MNG") == 0)
1823 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1824 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1825 png_set_read_fn(ping,image,png_get_data);
1827 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1828 png_permit_empty_plte(ping,MagickTrue);
1829 png_set_read_fn(ping,image,png_get_data);
1831 mng_info->image=image;
1832 mng_info->bytes_in_read_buffer=0;
1833 mng_info->found_empty_plte=MagickFalse;
1834 mng_info->have_saved_bkgd_index=MagickFalse;
1835 png_set_read_fn(ping,mng_info,mng_get_data);
1840 png_set_read_fn(ping,image,png_get_data);
1842 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1843 /* Ignore unused chunks and all unknown chunks except for vpAg */
1844 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1845 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1846 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1847 (int)sizeof(unused_chunks)/5);
1848 /* Callback for other unknown chunks */
1849 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1852 #if (PNG_LIBPNG_VER < 10400)
1853 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1854 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1855 /* Disable thread-unsafe features of pnggccrd */
1856 if (png_access_version_number() >= 10200)
1858 png_uint_32 mmx_disable_mask=0;
1859 png_uint_32 asm_flags;
1861 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1862 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1863 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1864 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1865 asm_flags=png_get_asm_flags(ping);
1866 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1871 png_read_info(ping,ping_info);
1873 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1874 &ping_bit_depth,&ping_color_type,
1875 &ping_interlace_method,&ping_compression_method,
1876 &ping_filter_method);
1878 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1881 (void) png_get_bKGD(ping, ping_info, &ping_background);
1883 if (ping_bit_depth < 8)
1885 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1887 png_set_packing(ping);
1892 image->depth=ping_bit_depth;
1893 image->depth=GetImageQuantumDepth(image,MagickFalse);
1894 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1895 if (logging != MagickFalse)
1897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1898 " PNG width: %lu, height: %lu",
1899 (unsigned long) ping_width, (unsigned long) ping_height);
1900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1901 " PNG color_type: %d, bit_depth: %d",
1902 ping_color_type, ping_bit_depth);
1903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1904 " PNG compression_method: %d",
1905 ping_compression_method);
1906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1907 " PNG interlace_method: %d, filter_method: %d",
1908 ping_interlace_method,ping_filter_method);
1911 #ifdef PNG_READ_iCCP_SUPPORTED
1912 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1924 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1926 if (profile_length != 0)
1931 if (logging != MagickFalse)
1932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1933 " Reading PNG iCCP chunk.");
1934 profile=AcquireStringInfo(profile_length);
1935 SetStringInfoDatum(profile,(const unsigned char *) info);
1936 (void) SetImageProfile(image,"icc",profile);
1937 profile=DestroyStringInfo(profile);
1941 #if defined(PNG_READ_sRGB_SUPPORTED)
1946 if (mng_info->have_global_srgb)
1947 image->rendering_intent=(RenderingIntent)
1948 (mng_info->global_srgb_intent+1);
1949 if (png_get_sRGB(ping,ping_info,&intent))
1951 image->rendering_intent=(RenderingIntent) (intent+1);
1952 if (logging != MagickFalse)
1953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1954 " Reading PNG sRGB chunk: rendering_intent: %d",intent+1);
1962 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1963 if (mng_info->have_global_gama)
1964 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
1965 if (png_get_gAMA(ping,ping_info,&file_gamma))
1967 image->gamma=(float) file_gamma;
1968 if (logging != MagickFalse)
1969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1970 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1973 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1975 if (mng_info->have_global_chrm != MagickFalse)
1977 (void) png_set_cHRM(ping,ping_info,
1978 mng_info->global_chrm.white_point.x,
1979 mng_info->global_chrm.white_point.y,
1980 mng_info->global_chrm.red_primary.x,
1981 mng_info->global_chrm.red_primary.y,
1982 mng_info->global_chrm.green_primary.x,
1983 mng_info->global_chrm.green_primary.y,
1984 mng_info->global_chrm.blue_primary.x,
1985 mng_info->global_chrm.blue_primary.y);
1988 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1990 (void) png_get_cHRM(ping,ping_info,
1991 &image->chromaticity.white_point.x,
1992 &image->chromaticity.white_point.y,
1993 &image->chromaticity.red_primary.x,
1994 &image->chromaticity.red_primary.y,
1995 &image->chromaticity.green_primary.x,
1996 &image->chromaticity.green_primary.y,
1997 &image->chromaticity.blue_primary.x,
1998 &image->chromaticity.blue_primary.y);
1999 if (logging != MagickFalse)
2000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2001 " Reading PNG cHRM chunk.");
2003 if (image->rendering_intent)
2005 png_set_sRGB(ping,ping_info,image->rendering_intent-1);
2006 png_set_gAMA(ping,ping_info,0.45455f);
2007 png_set_cHRM(ping,ping_info,
2008 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2009 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2011 #if defined(PNG_oFFs_SUPPORTED)
2012 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2014 image->page.x=png_get_x_offset_pixels(ping, ping_info);
2015 image->page.y=png_get_y_offset_pixels(ping, ping_info);
2016 if (logging != MagickFalse)
2017 if (image->page.x || image->page.y)
2018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2019 " Reading PNG oFFs chunk: x: %ld, y: %ld.",image->page.x,
2023 #if defined(PNG_pHYs_SUPPORTED)
2024 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2026 if (mng_info->have_global_phys)
2028 png_set_pHYs(ping,ping_info,
2029 mng_info->global_x_pixels_per_unit,
2030 mng_info->global_y_pixels_per_unit,
2031 mng_info->global_phys_unit_type);
2035 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2045 Set image resolution.
2047 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2049 image->x_resolution=(double) x_resolution;
2050 image->y_resolution=(double) y_resolution;
2051 if (unit_type == PNG_RESOLUTION_METER)
2053 image->units=PixelsPerCentimeterResolution;
2054 image->x_resolution=(double) x_resolution/100.0;
2055 image->y_resolution=(double) y_resolution/100.0;
2057 if (logging != MagickFalse)
2058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2059 " Reading PNG pHYs chunk: xres: %lu, yres: %lu, units: %d.",
2060 (unsigned long) x_resolution,(unsigned long) y_resolution,unit_type);
2063 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2071 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2072 if ((number_colors == 0) &&
2073 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2075 if (mng_info->global_plte_length)
2077 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2078 (int) mng_info->global_plte_length);
2079 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2080 if (mng_info->global_trns_length)
2082 if (mng_info->global_trns_length >
2083 mng_info->global_plte_length)
2084 (void) ThrowMagickException(&image->exception,
2085 GetMagickModule(),CoderError,
2086 "global tRNS has more entries than global PLTE",
2087 "`%s'",image_info->filename);
2088 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2089 (int) mng_info->global_trns_length,NULL);
2091 #if defined(PNG_READ_bKGD_SUPPORTED)
2093 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2094 mng_info->have_saved_bkgd_index ||
2096 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2101 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2102 if (mng_info->have_saved_bkgd_index)
2103 background.index=mng_info->saved_bkgd_index;
2105 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2106 background.index=ping_background->index;
2107 background.red=(png_uint_16)
2108 mng_info->global_plte[background.index].red;
2109 background.green=(png_uint_16)
2110 mng_info->global_plte[background.index].green;
2111 background.blue=(png_uint_16)
2112 mng_info->global_plte[background.index].blue;
2113 png_set_bKGD(ping,ping_info,&background);
2118 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2119 CoderError,"No global PLTE in file","`%s'",
2120 image_info->filename);
2124 #if defined(PNG_READ_bKGD_SUPPORTED)
2125 if (mng_info->have_global_bkgd &&
2126 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2127 image->background_color=mng_info->mng_global_bkgd;
2128 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2131 Set image background color.
2133 if (logging != MagickFalse)
2134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2135 " Reading PNG bKGD chunk.");
2136 if (ping_bit_depth <= MAGICKCORE_QUANTUM_DEPTH)
2138 image->background_color.red=ping_background->red;
2139 image->background_color.green=ping_background->green;
2140 image->background_color.blue=ping_background->blue;
2144 image->background_color.red=
2145 ScaleShortToQuantum(ping_background->red);
2146 image->background_color.green=
2147 ScaleShortToQuantum(ping_background->green);
2148 image->background_color.blue=
2149 ScaleShortToQuantum(ping_background->blue);
2153 transparent_color.red=0;
2154 transparent_color.green=0;
2155 transparent_color.blue=0;
2156 transparent_color.opacity=0;
2157 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2160 Image has a transparent background.
2165 if (logging != MagickFalse)
2166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2167 " Reading PNG tRNS chunk.");
2169 max_sample = (1 << ping_bit_depth) - 1;
2171 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2172 (int)ping_trans_color->gray > max_sample) ||
2173 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2174 ((int)ping_trans_color->red > max_sample ||
2175 (int)ping_trans_color->green > max_sample ||
2176 (int)ping_trans_color->blue > max_sample)))
2178 if (logging != MagickFalse)
2179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2180 " Ignoring PNG tRNS chunk with out-of-range sample.");
2181 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2182 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2183 image->matte=MagickFalse;
2187 transparent_color.red= (Quantum)(ping_trans_color->red);
2188 transparent_color.green= (Quantum) (ping_trans_color->green);
2189 transparent_color.blue= (Quantum) (ping_trans_color->blue);
2190 transparent_color.opacity= (Quantum) (ping_trans_color->gray);
2191 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2193 if (ping_bit_depth < 8)
2195 transparent_color.opacity=(Quantum) (((
2196 ping_trans_color->gray)*255)/max_sample);
2198 transparent_color.red=transparent_color.opacity;
2199 transparent_color.green=transparent_color.opacity;
2200 transparent_color.blue=transparent_color.opacity;
2204 #if defined(PNG_READ_sBIT_SUPPORTED)
2205 if (mng_info->have_global_sbit)
2207 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2208 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2211 num_passes=png_set_interlace_handling(ping);
2213 png_read_update_info(ping,ping_info);
2215 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2218 Initialize image structure.
2220 mng_info->image_box.left=0;
2221 mng_info->image_box.right=(long) ping_width;
2222 mng_info->image_box.top=0;
2223 mng_info->image_box.bottom=(long) ping_height;
2224 if (mng_info->mng_type == 0)
2226 mng_info->mng_width=ping_width;
2227 mng_info->mng_height=ping_height;
2228 mng_info->frame=mng_info->image_box;
2229 mng_info->clip=mng_info->image_box;
2233 image->page.y=mng_info->y_off[mng_info->object_id];
2235 image->compression=ZipCompression;
2236 image->columns=ping_width;
2237 image->rows=ping_height;
2238 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2239 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2240 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2242 image->storage_class=PseudoClass;
2243 image->colors=1UL << ping_bit_depth;
2244 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2245 if (image->colors > 256)
2248 if (image->colors > 65536L)
2249 image->colors=65536L;
2251 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2259 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2260 image->colors=(unsigned long) number_colors;
2261 if (logging != MagickFalse)
2262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2263 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2267 if (image->storage_class == PseudoClass)
2270 Initialize image colormap.
2272 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2273 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2274 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2282 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2283 for (i=0; i < (long) image->colors; i++)
2285 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2286 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2287 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2295 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2298 for (i=0; i < (long) image->colors; i++)
2300 image->colormap[i].red=(Quantum) (i*scale);
2301 image->colormap[i].green=(Quantum) (i*scale);
2302 image->colormap[i].blue=(Quantum) (i*scale);
2307 Read image scanlines.
2309 if (image->delay != 0)
2310 mng_info->scenes_found++;
2311 if ((image_info->number_scenes != 0) && (mng_info->scenes_found > (long)
2312 (image_info->first_scene+image_info->number_scenes)))
2314 if (logging != MagickFalse)
2315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2316 " Skipping PNG image data for scene %ld",
2317 mng_info->scenes_found-1);
2318 png_destroy_read_struct(&ping,&ping_info,&end_info);
2319 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2320 UnlockSemaphoreInfo(png_semaphore);
2322 if (logging != MagickFalse)
2323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2324 " exit ReadOnePNGImage().");
2327 if (logging != MagickFalse)
2328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2329 " Reading PNG IDAT chunk(s)");
2331 png_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2332 ping_rowbytes*sizeof(*png_pixels));
2334 png_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2335 sizeof(*png_pixels));
2336 if (png_pixels == (unsigned char *) NULL)
2337 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2338 if (logging != MagickFalse)
2339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2340 " Converting PNG pixels to pixel packets");
2342 Convert PNG pixels to pixel packets.
2344 if (setjmp(png_jmpbuf(ping)))
2347 PNG image is corrupt.
2349 png_destroy_read_struct(&ping,&ping_info,&end_info);
2350 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2351 UnlockSemaphoreInfo(png_semaphore);
2353 if (quantum_info != (QuantumInfo *) NULL)
2354 quantum_info = DestroyQuantumInfo(quantum_info);
2355 if (png_pixels != (unsigned char *) NULL)
2356 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2357 if (logging != MagickFalse)
2358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2359 " exit ReadOnePNGImage() with error.");
2360 if (image != (Image *) NULL)
2362 InheritException(exception,&image->exception);
2365 return(GetFirstImageInList(image));
2367 quantum_info=AcquireQuantumInfo(image_info,image);
2368 if (quantum_info == (QuantumInfo *) NULL)
2369 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2370 if (image->storage_class == DirectClass)
2371 for (pass=0; pass < num_passes; pass++)
2374 Convert image to DirectClass pixel packets.
2376 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2380 depth=(long) ping_bit_depth;
2382 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2383 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2384 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2385 MagickTrue : MagickFalse;
2387 for (y=0; y < (long) image->rows; y++)
2390 row_offset=ping_rowbytes*y;
2393 png_read_row(ping,png_pixels+row_offset,NULL);
2394 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2395 if (q == (PixelPacket *) NULL)
2397 #if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2404 r=png_pixels+row_offset;
2406 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2408 for (x=(long) image->columns-1; x >= 0; x--)
2412 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS)) &&
2413 (((*(p-2) << 8)|*(p-1)) == transparent_color.opacity))
2415 /* Cheap transparency */
2416 *r++=TransparentOpacity;
2422 else if (ping_color_type == PNG_COLOR_TYPE_RGB)
2424 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2425 for (x=(long) image->columns-1; x >= 0; x--)
2433 if ((((*(p-6) << 8)|*(p-5)) == transparent_color.red) &&
2434 (((*(p-4) << 8)|*(p-3)) == transparent_color.green) &&
2435 (((*(p-2) << 8)|*(p-1)) == transparent_color.blue))
2437 /* Cheap transparency */
2438 *r++=TransparentOpacity;
2444 for (x=(long) image->columns-1; x >= 0; x--)
2455 else if (ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2456 for (x=(long) (4*image->columns); x != 0; x--)
2461 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2462 for (x=(long) (2*image->columns); x != 0; x--)
2468 if (depth == 8 && ping_color_type == PNG_COLOR_TYPE_GRAY)
2469 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2470 GrayQuantum,png_pixels+row_offset);
2471 if (ping_color_type == PNG_COLOR_TYPE_GRAY ||
2472 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2474 quantum_info->depth=8;
2475 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2476 GrayAlphaQuantum,png_pixels+row_offset);
2478 else if (depth == 8 && ping_color_type == PNG_COLOR_TYPE_RGB)
2479 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2480 RGBQuantum,png_pixels+row_offset);
2481 else if (ping_color_type == PNG_COLOR_TYPE_RGB ||
2482 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2484 quantum_info->depth=8;
2485 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2486 RGBAQuantum,png_pixels+row_offset);
2488 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
2489 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2490 IndexQuantum,png_pixels+row_offset);
2491 #else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2492 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2493 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2494 GrayQuantum,png_pixels+row_offset,exception);
2495 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2496 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2497 GrayAlphaQuantum,png_pixels+row_offset,exception);
2498 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2499 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2500 RGBAQuantum,png_pixels+row_offset,exception);
2501 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2502 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2503 IndexQuantum,png_pixels+row_offset,exception);
2505 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2506 RGBQuantum,png_pixels+row_offset,exception);
2508 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2510 status=SetImageProgress(image,LoadImageTag,y,image->rows);
2511 if (status == MagickFalse)
2514 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2517 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2519 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2520 if (status == MagickFalse)
2524 else /* image->storage_class != DirectClass */
2525 for (pass=0; pass < num_passes; pass++)
2534 Convert grayscale image to PseudoClass pixel packets.
2536 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2537 MagickTrue : MagickFalse;
2538 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2539 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2540 if (quantum_scanline == (Quantum *) NULL)
2541 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2542 for (y=0; y < (long) image->rows; y++)
2545 row_offset=ping_rowbytes*y;
2548 png_read_row(ping,png_pixels+row_offset,NULL);
2549 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2550 if (q == (PixelPacket *) NULL)
2552 indices=GetAuthenticIndexQueue(image);
2553 p=png_pixels+row_offset;
2555 switch (ping_bit_depth)
2562 for (x=(long) image->columns-7; x > 0; x-=8)
2564 for (bit=7; bit >= 0; bit--)
2565 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2568 if ((image->columns % 8) != 0)
2570 for (bit=7; bit >= (long) (8-(image->columns % 8)); bit--)
2571 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2577 for (x=(long) image->columns-3; x > 0; x-=4)
2579 *r++=(*p >> 6) & 0x03;
2580 *r++=(*p >> 4) & 0x03;
2581 *r++=(*p >> 2) & 0x03;
2584 if ((image->columns % 4) != 0)
2586 for (i=3; i >= (long) (4-(image->columns % 4)); i--)
2587 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2593 for (x=(long) image->columns-1; x > 0; x-=2)
2595 *r++=(*p >> 4) & 0x0f;
2598 if ((image->columns % 2) != 0)
2599 *r++=(*p++ >> 4) & 0x0f;
2604 if (ping_color_type == 4)
2605 for (x=(long) image->columns-1; x >= 0; x--)
2608 /* In image.h, OpaqueOpacity is 0
2609 * TransparentOpacity is QuantumRange
2610 * In a PNG datastream, Opaque is QuantumRange
2611 * and Transparent is 0.
2613 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2617 for (x=(long) image->columns-1; x >= 0; x--)
2623 for (x=(long) image->columns-1; x >= 0; x--)
2625 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2629 if (image->colors > 256)
2635 *r=(Quantum) quantum;
2637 if (ping_color_type == 4)
2639 quantum=((*p++) << 8);
2641 q->opacity=(Quantum) (QuantumRange-quantum);
2645 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2649 if (image->colors > 256)
2657 if (ping_color_type == 4)
2659 q->opacity=(*p << 8) | *(p+1);
2661 q->opacity=(Quantum) GetAlphaPixelComponent(q);
2665 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2667 p++; /* strip low byte */
2668 if (ping_color_type == 4)
2670 q->opacity=(Quantum) (QuantumRange-(*p++));
2683 Transfer image scanline.
2686 for (x=0; x < (long) image->columns; x++)
2688 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2690 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2692 status=SetImageProgress(image,LoadImageTag,y,image->rows);
2693 if (status == MagickFalse)
2697 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2699 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2700 if (status == MagickFalse)
2703 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2705 if (quantum_info != (QuantumInfo *) NULL)
2706 quantum_info=DestroyQuantumInfo(quantum_info);
2707 if (image->storage_class == PseudoClass)
2708 (void) SyncImage(image);
2709 png_read_end(ping,ping_info);
2711 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2712 (long) image_info->first_scene && image->delay != 0)
2714 png_destroy_read_struct(&ping,&ping_info,&end_info);
2715 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2717 (void) SetImageBackgroundColor(image);
2718 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2719 UnlockSemaphoreInfo(png_semaphore);
2721 if (logging != MagickFalse)
2722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2723 " exit ReadOnePNGImage() early.");
2726 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2732 Image has a transparent background.
2734 storage_class=image->storage_class;
2735 image->matte=MagickTrue;
2737 #if 1 /* balfour fix */
2738 /* From imagemagick discourse server, 5 Feb 2010 */
2740 if (storage_class == PseudoClass)
2742 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2744 for (x=0; x < ping_num_trans; x++)
2746 image->colormap[x].opacity = ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2749 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2751 for (x=0; x < (int) image->colors; x++)
2753 if (image->colormap[x].red == transparent_color.opacity)
2755 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2759 (void) SyncImage(image);
2764 for (y=0; y < (long) image->rows; y++)
2766 image->storage_class=storage_class;
2767 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2768 if (q == (PixelPacket *) NULL)
2770 indices=GetAuthenticIndexQueue(image);
2772 for (x=(long) image->columns-1; x >= 0; x--)
2774 if (ScaleQuantumToChar(q->red) == transparent_color.red &&
2775 ScaleQuantumToChar(q->green) == transparent_color.green &&
2776 ScaleQuantumToChar(q->blue) == transparent_color.blue)
2777 q->opacity=(Quantum) TransparentOpacity;
2779 SetOpacityPixelComponent(q,OpaqueOpacity);
2782 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2787 #else /* not balfour */
2790 for (y=0; y < (long) image->rows; y++)
2792 image->storage_class=storage_class;
2793 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2794 if (q == (PixelPacket *) NULL)
2796 indices=GetAuthenticIndexQueue(image);
2798 if (storage_class == PseudoClass)
2803 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2804 for (x=0; x < (long) image->columns; x++)
2806 indexpacket=indices[x];
2807 if (indexpacket < ping_num_trans)
2808 q->opacity=ScaleCharToQuantum((unsigned char)
2809 (255-ping_trans_alpha[(long) indexpacket]));
2811 SetOpacityPixelComponent(q,OpaqueOpacity);
2814 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2815 for (x=0; x < (long) image->columns; x++)
2817 indexpacket=indices[x];
2818 q->red=image->colormap[(long) indexpacket].red;
2821 if (q->red == transparent_color.opacity)
2822 q->opacity=(Quantum) TransparentOpacity;
2824 SetOpacityPixelComponent(q,OpaqueOpacity);
2829 for (x=(long) image->columns-1; x >= 0; x--)
2831 if (ScaleQuantumToChar(q->red) == transparent_color.red &&
2832 ScaleQuantumToChar(q->green) == transparent_color.green &&
2833 ScaleQuantumToChar(q->blue) == transparent_color.blue)
2834 q->opacity=(Quantum) TransparentOpacity;
2836 SetOpacityPixelComponent(q,OpaqueOpacity);
2839 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2842 #endif /* not balfour */
2843 image->storage_class=DirectClass;
2845 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2846 if (image->depth > 8)
2849 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2850 for (i=0; i < (long) num_text; i++)
2852 /* Check for a profile */
2854 if (logging != MagickFalse)
2855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2856 " Reading PNG text chunk");
2857 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2858 (void) png_read_raw_profile(image,image_info,text,(int) i);
2864 length=text[i].text_length;
2865 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2867 if (value == (char *) NULL)
2869 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2870 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2875 (void) ConcatenateMagickString(value,text[i].text,length+2);
2876 (void) SetImageProperty(image,text[i].key,value);
2877 if (logging != MagickFalse)
2878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2879 " Keyword: %s",text[i].key);
2880 value=DestroyString(value);
2883 #ifdef MNG_OBJECT_BUFFERS
2885 Store the object if necessary.
2887 if (object_id && !mng_info->frozen[object_id])
2889 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2892 create a new object buffer.
2894 mng_info->ob[object_id]=(MngBuffer *)
2895 AcquireAlignedMemory(1,sizeof(MngBuffer));
2896 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2898 mng_info->ob[object_id]->image=(Image *) NULL;
2899 mng_info->ob[object_id]->reference_count=1;
2902 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2903 mng_info->ob[object_id]->frozen)
2905 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2906 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2907 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2909 if (mng_info->ob[object_id]->frozen)
2910 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2911 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2912 "`%s'",image->filename);
2917 if (mng_info->ob[object_id]->image != (Image *) NULL)
2918 mng_info->ob[object_id]->image=DestroyImage
2919 (mng_info->ob[object_id]->image);
2920 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2922 if (mng_info->ob[object_id]->image != (Image *) NULL)
2923 mng_info->ob[object_id]->image->file=(FILE *) NULL;
2925 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2926 ResourceLimitError,"Cloning image for object buffer failed",
2927 "`%s'",image->filename);
2928 if (ping_width > 250000L || ping_height > 250000L)
2929 png_error(ping,"PNG Image dimensions are too large.");
2930 mng_info->ob[object_id]->width=ping_width;
2931 mng_info->ob[object_id]->height=ping_height;
2932 mng_info->ob[object_id]->color_type=ping_color_type;
2933 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
2934 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
2935 mng_info->ob[object_id]->compression_method=
2936 ping_compression_method;
2937 mng_info->ob[object_id]->filter_method=ping_filter_method;
2938 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2947 Copy the PLTE to the object buffer.
2949 png_get_PLTE(ping,ping_info,&plte,&number_colors);
2950 mng_info->ob[object_id]->plte_length=number_colors;
2951 for (i=0; i < number_colors; i++)
2953 mng_info->ob[object_id]->plte[i]=plte[i];
2957 mng_info->ob[object_id]->plte_length=0;
2962 Relinquish resources.
2964 png_destroy_read_struct(&ping,&ping_info,&end_info);
2966 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2967 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2968 UnlockSemaphoreInfo(png_semaphore);
2971 if (logging != MagickFalse)
2972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2973 " exit ReadOnePNGImage()");
2976 /* end of reading one PNG image */
2979 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
2992 magic_number[MaxTextExtent];
3004 assert(image_info != (const ImageInfo *) NULL);
3005 assert(image_info->signature == MagickSignature);
3006 if (image_info->debug != MagickFalse)
3007 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3008 image_info->filename);
3009 assert(exception != (ExceptionInfo *) NULL);
3010 assert(exception->signature == MagickSignature);
3011 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()");
3012 image=AcquireImage(image_info);
3013 mng_info=(MngInfo *) NULL;
3014 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3015 if (status == MagickFalse)
3016 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3018 Verify PNG signature.
3020 count=ReadBlob(image,8,(unsigned char *) magic_number);
3021 if (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3022 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3024 Allocate a MngInfo structure.
3026 have_mng_structure=MagickFalse;
3027 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
3028 if (mng_info == (MngInfo *) NULL)
3029 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3031 Initialize members of the MngInfo structure.
3033 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3034 mng_info->image=image;
3035 have_mng_structure=MagickTrue;
3038 image=ReadOnePNGImage(mng_info,image_info,exception);
3039 MngInfoFreeStruct(mng_info,&have_mng_structure);
3040 if (image == (Image *) NULL)
3042 if (previous != (Image *) NULL)
3044 if (previous->signature != MagickSignature)
3045 ThrowReaderException(CorruptImageError,"CorruptImage");
3046 (void) CloseBlob(previous);
3047 (void) DestroyImageList(previous);
3049 if (logging != MagickFalse)
3050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3051 "exit ReadPNGImage() with error");
3052 return((Image *) NULL);
3054 (void) CloseBlob(image);
3055 if ((image->columns == 0) || (image->rows == 0))
3057 if (logging != MagickFalse)
3058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3059 "exit ReadPNGImage() with error.");
3060 ThrowReaderException(CorruptImageError,"CorruptImage");
3062 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3064 (void) SetImageType(image,PaletteType);
3065 if (image->matte != MagickFalse)
3067 /* To do: Reduce to binary transparency */
3070 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3072 (void) SetImageType(image,TrueColorType);
3073 image->matte=MagickFalse;
3075 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3076 (void) SetImageType(image,TrueColorMatteType);
3077 if (logging != MagickFalse)
3078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3084 #if defined(JNG_SUPPORTED)
3086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3090 % R e a d O n e J N G I m a g e %
3094 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3096 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3097 % (minus the 8-byte signature) and returns it. It allocates the memory
3098 % necessary for the new Image structure and returns a pointer to the new
3101 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3103 % The format of the ReadOneJNGImage method is:
3105 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3106 % ExceptionInfo *exception)
3108 % A description of each parameter follows:
3110 % o mng_info: Specifies a pointer to a MngInfo structure.
3112 % o image_info: the image info.
3114 % o exception: return any errors or warnings in this structure.
3117 static Image *ReadOneJNGImage(MngInfo *mng_info,
3118 const ImageInfo *image_info, ExceptionInfo *exception)
3142 jng_image_sample_depth,
3143 jng_image_compression_method,
3144 jng_image_interlace_method,
3145 jng_alpha_sample_depth,
3146 jng_alpha_compression_method,
3147 jng_alpha_filter_method,
3148 jng_alpha_interlace_method;
3150 register const PixelPacket
3157 register PixelPacket
3160 register unsigned char
3172 jng_alpha_compression_method=0;
3173 jng_alpha_sample_depth=8;
3177 alpha_image=(Image *) NULL;
3178 color_image=(Image *) NULL;
3179 alpha_image_info=(ImageInfo *) NULL;
3180 color_image_info=(ImageInfo *) NULL;
3182 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3183 " enter ReadOneJNGImage()");
3185 image=mng_info->image;
3186 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3189 Allocate next image structure.
3191 if (logging != MagickFalse)
3192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3193 " AcquireNextImage()");
3194 AcquireNextImage(image_info,image);
3195 if (GetNextImageInList(image) == (Image *) NULL)
3196 return((Image *) NULL);
3197 image=SyncNextImageInList(image);
3199 mng_info->image=image;
3202 Signature bytes have already been read.
3205 read_JSEP=MagickFalse;
3206 reading_idat=MagickFalse;
3207 skip_to_iend=MagickFalse;
3211 type[MaxTextExtent];
3220 Read a new JNG chunk.
3222 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3223 2*GetBlobSize(image));
3224 if (status == MagickFalse)
3227 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3228 length=ReadBlobMSBLong(image);
3229 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3231 if (logging != MagickFalse)
3232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3233 " Reading JNG chunk type %c%c%c%c, length: %lu",
3234 type[0],type[1],type[2],type[3],length);
3236 if (length > PNG_UINT_31_MAX || count == 0)
3237 ThrowReaderException(CorruptImageError,"CorruptImage");
3239 chunk=(unsigned char *) NULL;
3242 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3243 if (chunk == (unsigned char *) NULL)
3244 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3245 for (i=0; i < (long) length; i++)
3246 chunk[i]=(unsigned char) ReadBlobByte(image);
3249 (void) ReadBlobMSBLong(image); /* read crc word */
3254 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3258 if (memcmp(type,mng_JHDR,4) == 0)
3262 jng_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) |
3263 (p[2] << 8) | p[3]);
3264 jng_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) |
3265 (p[6] << 8) | p[7]);
3266 jng_color_type=p[8];
3267 jng_image_sample_depth=p[9];
3268 jng_image_compression_method=p[10];
3269 jng_image_interlace_method=p[11];
3270 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3272 jng_alpha_sample_depth=p[12];
3273 jng_alpha_compression_method=p[13];
3274 jng_alpha_filter_method=p[14];
3275 jng_alpha_interlace_method=p[15];
3276 if (logging != MagickFalse)
3278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3279 " jng_width: %16lu",(unsigned long) jng_width);
3280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3281 " jng_width: %16lu",(unsigned long) jng_height);
3282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3283 " jng_color_type: %16d",jng_color_type);
3284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3285 " jng_image_sample_depth: %3d",
3286 jng_image_sample_depth);
3287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3288 " jng_image_compression_method:%3d",
3289 jng_image_compression_method);
3290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3291 " jng_image_interlace_method: %3d",
3292 jng_image_interlace_method);
3293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3294 " jng_alpha_sample_depth: %3d",
3295 jng_alpha_sample_depth);
3296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3297 " jng_alpha_compression_method:%3d",
3298 jng_alpha_compression_method);
3299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3300 " jng_alpha_filter_method: %3d",
3301 jng_alpha_filter_method);
3302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3303 " jng_alpha_interlace_method: %3d",
3304 jng_alpha_interlace_method);
3308 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3313 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3314 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3315 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3318 o create color_image
3319 o open color_blob, attached to color_image
3320 o if (color type has alpha)
3321 open alpha_blob, attached to alpha_image
3324 color_image_info=(ImageInfo *)AcquireAlignedMemory(1,sizeof(ImageInfo));
3325 if (color_image_info == (ImageInfo *) NULL)
3326 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3327 GetImageInfo(color_image_info);
3328 color_image=AcquireImage(color_image_info);
3329 if (color_image == (Image *) NULL)
3330 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3332 if (logging != MagickFalse)
3333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3334 " Creating color_blob.");
3335 (void) AcquireUniqueFilename(color_image->filename);
3336 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3338 if (status == MagickFalse)
3339 return((Image *) NULL);
3341 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3343 alpha_image_info=(ImageInfo *)
3344 AcquireAlignedMemory(1,sizeof(ImageInfo));
3345 if (alpha_image_info == (ImageInfo *) NULL)
3346 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3347 GetImageInfo(alpha_image_info);
3348 alpha_image=AcquireImage(alpha_image_info);
3349 if (alpha_image == (Image *) NULL)
3351 alpha_image=DestroyImage(alpha_image);
3352 ThrowReaderException(ResourceLimitError,
3353 "MemoryAllocationFailed");
3355 if (logging != MagickFalse)
3356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3357 " Creating alpha_blob.");
3358 (void) AcquireUniqueFilename(alpha_image->filename);
3359 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3361 if (status == MagickFalse)
3362 return((Image *) NULL);
3363 if (jng_alpha_compression_method == 0)
3368 if (logging != MagickFalse)
3369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3370 " Writing IHDR chunk to alpha_blob.");
3371 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3372 "\211PNG\r\n\032\n");
3373 (void) WriteBlobMSBULong(alpha_image,13L);
3374 PNGType(data,mng_IHDR);
3375 LogPNGChunk((int) logging,mng_IHDR,13L);
3376 PNGLong(data+4,jng_width);
3377 PNGLong(data+8,jng_height);
3378 data[12]=jng_alpha_sample_depth;
3379 data[13]=0; /* color_type gray */
3380 data[14]=0; /* compression method 0 */
3381 data[15]=0; /* filter_method 0 */
3382 data[16]=0; /* interlace_method 0 */
3383 (void) WriteBlob(alpha_image,17,data);
3384 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3387 reading_idat=MagickTrue;
3390 if (memcmp(type,mng_JDAT,4) == 0)
3393 Copy chunk to color_image->blob
3396 if (logging != MagickFalse)
3397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3398 " Copying JDAT chunk data to color_blob.");
3400 (void) WriteBlob(color_image,length,chunk);
3402 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3406 if (memcmp(type,mng_IDAT,4) == 0)
3412 Copy IDAT header and chunk data to alpha_image->blob
3415 if (image_info->ping == MagickFalse)
3417 if (logging != MagickFalse)
3418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3419 " Copying IDAT chunk data to alpha_blob.");
3421 (void) WriteBlobMSBULong(alpha_image,(unsigned long) length);
3422 PNGType(data,mng_IDAT);
3423 LogPNGChunk((int) logging,mng_IDAT,length);
3424 (void) WriteBlob(alpha_image,4,data);
3425 (void) WriteBlob(alpha_image,length,chunk);
3426 (void) WriteBlobMSBULong(alpha_image,
3427 crc32(crc32(0,data,4),chunk,(uInt) length));
3430 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3434 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3437 Copy chunk data to alpha_image->blob
3440 if (image_info->ping == MagickFalse)
3442 if (logging != MagickFalse)
3443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3444 " Copying JDAA chunk data to alpha_blob.");
3446 (void) WriteBlob(alpha_image,length,chunk);
3449 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3453 if (memcmp(type,mng_JSEP,4) == 0)
3455 read_JSEP=MagickTrue;
3457 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3461 if (memcmp(type,mng_bKGD,4) == 0)
3465 image->background_color.red=ScaleCharToQuantum(p[1]);
3466 image->background_color.green=image->background_color.red;
3467 image->background_color.blue=image->background_color.red;
3471 image->background_color.red=ScaleCharToQuantum(p[1]);
3472 image->background_color.green=ScaleCharToQuantum(p[3]);
3473 image->background_color.blue=ScaleCharToQuantum(p[5]);
3475 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3479 if (memcmp(type,mng_gAMA,4) == 0)
3482 image->gamma=((float) mng_get_long(p))*0.00001;
3483 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3487 if (memcmp(type,mng_cHRM,4) == 0)
3491 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3492 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3493 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3494 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3495 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3496 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3497 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3498 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3500 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3504 if (memcmp(type,mng_sRGB,4) == 0)
3508 image->rendering_intent=(RenderingIntent) (p[0]+1);
3509 image->gamma=0.45455f;
3510 image->chromaticity.red_primary.x=0.6400f;
3511 image->chromaticity.red_primary.y=0.3300f;
3512 image->chromaticity.green_primary.x=0.3000f;
3513 image->chromaticity.green_primary.y=0.6000f;
3514 image->chromaticity.blue_primary.x=0.1500f;
3515 image->chromaticity.blue_primary.y=0.0600f;
3516 image->chromaticity.white_point.x=0.3127f;
3517 image->chromaticity.white_point.y=0.3290f;
3519 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3523 if (memcmp(type,mng_oFFs,4) == 0)
3527 image->page.x=mng_get_long(p);
3528 image->page.y=mng_get_long(&p[4]);
3529 if ((int) p[8] != 0)
3531 image->page.x/=10000;
3532 image->page.y/=10000;
3536 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3540 if (memcmp(type,mng_pHYs,4) == 0)
3544 image->x_resolution=(double) mng_get_long(p);
3545 image->y_resolution=(double) mng_get_long(&p[4]);
3546 if ((int) p[8] == PNG_RESOLUTION_METER)
3548 image->units=PixelsPerCentimeterResolution;
3549 image->x_resolution=image->x_resolution/100.0f;
3550 image->y_resolution=image->y_resolution/100.0f;
3553 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3558 if (memcmp(type,mng_iCCP,4) == 0)
3562 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3568 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3570 if (memcmp(type,mng_IEND,4))
3579 Finish up reading image data:
3581 o read main image from color_blob.
3585 o if (color_type has alpha)
3586 if alpha_encoding is PNG
3587 read secondary image from alpha_blob via ReadPNG
3588 if alpha_encoding is JPEG
3589 read secondary image from alpha_blob via ReadJPEG
3593 o copy intensity of secondary image into
3594 opacity samples of main image.
3596 o destroy the secondary image.
3599 (void) CloseBlob(color_image);
3600 if (logging != MagickFalse)
3601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3602 " Reading jng_image from color_blob.");
3603 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3604 color_image->filename);
3605 color_image_info->ping=MagickFalse; /* To do: avoid this */
3606 jng_image=ReadImage(color_image_info,exception);
3607 if (jng_image == (Image *) NULL)
3608 return((Image *) NULL);
3610 (void) RelinquishUniqueFileResource(color_image->filename);
3611 color_image=DestroyImage(color_image);
3612 color_image_info=DestroyImageInfo(color_image_info);
3614 if (jng_image == (Image *) NULL)
3615 return((Image *) NULL);
3617 if (logging != MagickFalse)
3618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3619 " Copying jng_image pixels to main image.");
3620 image->rows=jng_height;
3621 image->columns=jng_width;
3622 length=image->columns*sizeof(PixelPacket);
3623 for (y=0; y < (long) image->rows; y++)
3625 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3626 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3627 (void) CopyMagickMemory(q,s,length);
3628 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3631 jng_image=DestroyImage(jng_image);
3632 if (image_info->ping == MagickFalse)
3634 if (jng_color_type >= 12)
3636 if (jng_alpha_compression_method == 0)
3640 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3641 PNGType(data,mng_IEND);
3642 LogPNGChunk((int) logging,mng_IEND,0L);
3643 (void) WriteBlob(alpha_image,4,data);
3644 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3646 (void) CloseBlob(alpha_image);
3647 if (logging != MagickFalse)
3648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3649 " Reading opacity from alpha_blob.");
3651 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3652 "%s",alpha_image->filename);
3654 jng_image=ReadImage(alpha_image_info,exception);
3655 if (jng_image != (Image *) NULL)
3656 for (y=0; y < (long) image->rows; y++)
3658 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3660 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3661 if (image->matte != MagickFalse)
3662 for (x=(long) image->columns; x != 0; x--,q++,s++)
3663 q->opacity=(Quantum) QuantumRange-s->red;
3665 for (x=(long) image->columns; x != 0; x--,q++,s++)
3667 q->opacity=(Quantum) QuantumRange-s->red;
3668 if (q->opacity != OpaqueOpacity)
3669 image->matte=MagickTrue;
3671 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3674 (void) RelinquishUniqueFileResource(alpha_image->filename);
3675 alpha_image=DestroyImage(alpha_image);
3676 alpha_image_info=DestroyImageInfo(alpha_image_info);
3677 if (jng_image != (Image *) NULL)
3678 jng_image=DestroyImage(jng_image);
3685 if (mng_info->mng_type == 0)
3687 mng_info->mng_width=jng_width;
3688 mng_info->mng_height=jng_height;
3690 if (image->page.width == 0 && image->page.height == 0)
3692 image->page.width=jng_width;
3693 image->page.height=jng_height;
3695 if (image->page.x == 0 && image->page.y == 0)
3697 image->page.x=mng_info->x_off[mng_info->object_id];
3698 image->page.y=mng_info->y_off[mng_info->object_id];
3702 image->page.y=mng_info->y_off[mng_info->object_id];
3704 mng_info->image_found++;
3705 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3706 2*GetBlobSize(image));
3707 if (logging != MagickFalse)
3708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3709 " exit ReadOneJNGImage()");
3714 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3718 % R e a d J N G I m a g e %
3722 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3724 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3725 % (including the 8-byte signature) and returns it. It allocates the memory
3726 % necessary for the new Image structure and returns a pointer to the new
3729 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3731 % The format of the ReadJNGImage method is:
3733 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3736 % A description of each parameter follows:
3738 % o image_info: the image info.
3740 % o exception: return any errors or warnings in this structure.
3744 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3757 magic_number[MaxTextExtent];
3769 assert(image_info != (const ImageInfo *) NULL);
3770 assert(image_info->signature == MagickSignature);
3771 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3772 assert(exception != (ExceptionInfo *) NULL);
3773 assert(exception->signature == MagickSignature);
3774 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()");
3775 image=AcquireImage(image_info);
3776 mng_info=(MngInfo *) NULL;
3777 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3778 if (status == MagickFalse)
3779 return((Image *) NULL);
3780 if (LocaleCompare(image_info->magick,"JNG") != 0)
3781 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3783 Verify JNG signature.
3785 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3786 if (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3787 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3789 Allocate a MngInfo structure.
3791 have_mng_structure=MagickFalse;
3792 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(*mng_info));
3793 if (mng_info == (MngInfo *) NULL)
3794 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3796 Initialize members of the MngInfo structure.
3798 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3799 have_mng_structure=MagickTrue;
3801 mng_info->image=image;
3803 image=ReadOneJNGImage(mng_info,image_info,exception);
3804 MngInfoFreeStruct(mng_info,&have_mng_structure);
3805 if (image == (Image *) NULL)
3807 if (IsImageObject(previous) != MagickFalse)
3809 (void) CloseBlob(previous);
3810 (void) DestroyImageList(previous);
3812 if (logging != MagickFalse)
3813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3814 "exit ReadJNGImage() with error");
3815 return((Image *) NULL);
3817 (void) CloseBlob(image);
3818 if (image->columns == 0 || image->rows == 0)
3820 if (logging != MagickFalse)
3821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3822 "exit ReadJNGImage() with error");
3823 ThrowReaderException(CorruptImageError,"CorruptImage");
3825 if (logging != MagickFalse)
3826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3831 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3834 page_geometry[MaxTextExtent];
3867 #if defined(MNG_INSERT_LAYERS)
3869 mng_background_color;
3872 register unsigned char
3887 #if defined(MNG_INSERT_LAYERS)
3892 volatile unsigned int
3893 #ifdef MNG_OBJECT_BUFFERS
3894 mng_background_object=0,
3896 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
3899 default_frame_timeout,
3901 #if defined(MNG_INSERT_LAYERS)
3907 volatile unsigned long
3908 default_frame_delay,
3912 #if defined(MNG_INSERT_LAYERS)
3921 previous_fb.bottom=0;
3923 previous_fb.right=0;
3925 default_fb.bottom=0;
3930 Set image_info->type=OptimizeType (new in version 5.4.0) to get the
3931 following optimizations:
3933 o 16-bit depth is reduced to 8 if all pixels contain samples whose
3934 high byte and low byte are identical.
3935 o Opaque matte channel is removed.
3936 o If matte channel is present but only one transparent color is
3937 present, RGB+tRNS is written instead of RGBA
3938 o Grayscale images are reduced to 1, 2, or 4 bit depth if
3939 this can be done without loss.
3940 o Palette is sorted to remove unused entries and to put a
3941 transparent color first, if PNG_SORT_PALETTE is also defined.
3947 assert(image_info != (const ImageInfo *) NULL);
3948 assert(image_info->signature == MagickSignature);
3949 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3950 assert(exception != (ExceptionInfo *) NULL);
3951 assert(exception->signature == MagickSignature);
3952 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()");
3953 image=AcquireImage(image_info);
3954 mng_info=(MngInfo *) NULL;
3955 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3956 if (status == MagickFalse)
3957 return((Image *) NULL);
3958 first_mng_object=MagickFalse;
3960 have_mng_structure=MagickFalse;
3962 Allocate a MngInfo structure.
3964 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
3965 if (mng_info == (MngInfo *) NULL)
3966 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3968 Initialize members of the MngInfo structure.
3970 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3971 mng_info->image=image;
3972 have_mng_structure=MagickTrue;
3973 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
3974 mng_info->optimize=image_info->type == OptimizeType;
3977 if (LocaleCompare(image_info->magick,"MNG") == 0)
3980 magic_number[MaxTextExtent];
3983 Verify MNG signature.
3985 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3986 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
3987 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3989 Initialize some nonzero members of the MngInfo structure.
3991 for (i=0; i < MNG_MAX_OBJECTS; i++)
3993 mng_info->object_clip[i].right=(long) PNG_UINT_31_MAX;
3994 mng_info->object_clip[i].bottom=(long) PNG_UINT_31_MAX;
3996 mng_info->exists[0]=MagickTrue;
3998 first_mng_object=MagickTrue;
4000 #if defined(MNG_INSERT_LAYERS)
4001 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4003 default_frame_delay=0;
4004 default_frame_timeout=0;
4007 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4009 skip_to_iend=MagickFalse;
4010 term_chunk_found=MagickFalse;
4011 mng_info->framing_mode=1;
4012 #if defined(MNG_INSERT_LAYERS)
4013 mandatory_back=MagickFalse;
4015 #if defined(MNG_INSERT_LAYERS)
4016 mng_background_color=image->background_color;
4018 default_fb=mng_info->frame;
4019 previous_fb=mng_info->frame;
4023 type[MaxTextExtent];
4025 if (LocaleCompare(image_info->magick,"MNG") == 0)
4034 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4035 length=ReadBlobMSBLong(image);
4036 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4038 if (logging != MagickFalse)
4039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4040 " Reading MNG chunk type %c%c%c%c, length: %lu",
4041 type[0],type[1],type[2],type[3],length);
4043 if (length > PNG_UINT_31_MAX)
4046 ThrowReaderException(CorruptImageError,"CorruptImage");
4048 chunk=(unsigned char *) NULL;
4051 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4052 if (chunk == (unsigned char *) NULL)
4053 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4054 for (i=0; i < (long) length; i++)
4055 chunk[i]=(unsigned char) ReadBlobByte(image);
4058 (void) ReadBlobMSBLong(image); /* read crc word */
4060 #if !defined(JNG_SUPPORTED)
4061 if (memcmp(type,mng_JHDR,4) == 0)
4063 skip_to_iend=MagickTrue;
4064 if (mng_info->jhdr_warning == 0)
4065 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4066 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4067 mng_info->jhdr_warning++;
4070 if (memcmp(type,mng_DHDR,4) == 0)
4072 skip_to_iend=MagickTrue;
4073 if (mng_info->dhdr_warning == 0)
4074 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4075 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4076 mng_info->dhdr_warning++;
4078 if (memcmp(type,mng_MEND,4) == 0)
4082 if (memcmp(type,mng_IEND,4) == 0)
4083 skip_to_iend=MagickFalse;
4085 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4086 if (logging != MagickFalse)
4087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4091 if (memcmp(type,mng_MHDR,4) == 0)
4093 mng_info->mng_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) |
4094 (p[2] << 8) | p[3]);
4095 mng_info->mng_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) |
4096 (p[6] << 8) | p[7]);
4097 if (logging != MagickFalse)
4099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4100 " MNG width: %lu",mng_info->mng_width);
4101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4102 " MNG height: %lu",mng_info->mng_height);
4105 mng_info->ticks_per_second=(unsigned long) mng_get_long(p);
4106 if (mng_info->ticks_per_second == 0)
4107 default_frame_delay=0;
4109 default_frame_delay=1UL*image->ticks_per_second/
4110 mng_info->ticks_per_second;
4111 frame_delay=default_frame_delay;
4116 simplicity=(unsigned long) mng_get_long(p);
4118 mng_type=1; /* Full MNG */
4119 if ((simplicity != 0) && ((simplicity | 11) == 11))
4120 mng_type=2; /* LC */
4121 if ((simplicity != 0) && ((simplicity | 9) == 9))
4122 mng_type=3; /* VLC */
4123 #if defined(MNG_INSERT_LAYERS)
4125 insert_layers=MagickTrue;
4127 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4130 Allocate next image structure.
4132 AcquireNextImage(image_info,image);
4133 if (GetNextImageInList(image) == (Image *) NULL)
4134 return((Image *) NULL);
4135 image=SyncNextImageInList(image);
4136 mng_info->image=image;
4139 if ((mng_info->mng_width > 65535L) ||
4140 (mng_info->mng_height > 65535L))
4141 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4142 (void) FormatMagickString(page_geometry,MaxTextExtent,"%lux%lu+0+0",
4143 mng_info->mng_width,mng_info->mng_height);
4144 mng_info->frame.left=0;
4145 mng_info->frame.right=(long) mng_info->mng_width;
4146 mng_info->frame.top=0;
4147 mng_info->frame.bottom=(long) mng_info->mng_height;
4148 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4149 for (i=0; i < MNG_MAX_OBJECTS; i++)
4150 mng_info->object_clip[i]=mng_info->frame;
4151 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4155 if (memcmp(type,mng_TERM,4) == 0)
4165 final_delay=(png_uint_32) mng_get_long(&p[2]);
4166 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4167 if (mng_iterations == PNG_UINT_31_MAX)
4169 image->iterations=mng_iterations;
4170 term_chunk_found=MagickTrue;
4172 if (logging != MagickFalse)
4174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4175 " repeat=%d",repeat);
4176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4177 " final_delay=%ld",final_delay);
4178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4179 " image->iterations=%ld",image->iterations);
4181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4184 if (memcmp(type,mng_DEFI,4) == 0)
4187 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4188 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4190 object_id=(p[0] << 8) | p[1];
4191 if (mng_type == 2 && object_id != 0)
4192 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4193 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4195 if (object_id > MNG_MAX_OBJECTS)
4198 Instead ofsuing a warning we should allocate a larger
4199 MngInfo structure and continue.
4201 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4202 CoderError,"object id too large","`%s'",image->filename);
4203 object_id=MNG_MAX_OBJECTS;
4205 if (mng_info->exists[object_id])
4206 if (mng_info->frozen[object_id])
4208 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4209 (void) ThrowMagickException(&image->exception,
4210 GetMagickModule(),CoderError,
4211 "DEFI cannot redefine a frozen MNG object","`%s'",
4215 mng_info->exists[object_id]=MagickTrue;
4217 mng_info->invisible[object_id]=p[2];
4219 Extract object offset info.
4223 mng_info->x_off[object_id]=(long) ((p[4] << 24) | (p[5] << 16) |
4224 (p[6] << 8) | p[7]);
4225 mng_info->y_off[object_id]=(long) ((p[8] << 24) | (p[9] << 16) |
4226 (p[10] << 8) | p[11]);
4227 if (logging != MagickFalse)
4229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4230 " x_off[%d]: %lu",object_id,mng_info->x_off[object_id]);
4231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4232 " y_off[%d]: %lu",object_id,mng_info->y_off[object_id]);
4236 Extract object clipping info.
4239 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4241 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4244 if (memcmp(type,mng_bKGD,4) == 0)
4246 mng_info->have_global_bkgd=MagickFalse;
4249 mng_info->mng_global_bkgd.red=
4250 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4251 mng_info->mng_global_bkgd.green=
4252 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4253 mng_info->mng_global_bkgd.blue=
4254 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4255 mng_info->have_global_bkgd=MagickTrue;
4257 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4260 if (memcmp(type,mng_BACK,4) == 0)
4262 #if defined(MNG_INSERT_LAYERS)
4264 mandatory_back=p[6];
4267 if (mandatory_back && length > 5)
4269 mng_background_color.red=
4270 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4271 mng_background_color.green=
4272 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4273 mng_background_color.blue=
4274 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4275 mng_background_color.opacity=OpaqueOpacity;
4277 #ifdef MNG_OBJECT_BUFFERS
4279 mng_background_object=(p[7] << 8) | p[8];
4282 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4285 if (memcmp(type,mng_PLTE,4) == 0)
4290 if (length && (length < 769))
4292 if (mng_info->global_plte == (png_colorp) NULL)
4293 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4294 sizeof(*mng_info->global_plte));
4295 for (i=0; i < (long) (length/3); i++)
4297 mng_info->global_plte[i].red=p[3*i];
4298 mng_info->global_plte[i].green=p[3*i+1];
4299 mng_info->global_plte[i].blue=p[3*i+2];
4301 mng_info->global_plte_length=length/3;
4304 for ( ; i < 256; i++)
4306 mng_info->global_plte[i].red=i;
4307 mng_info->global_plte[i].green=i;
4308 mng_info->global_plte[i].blue=i;
4311 mng_info->global_plte_length=256;
4314 mng_info->global_plte_length=0;
4315 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4318 if (memcmp(type,mng_tRNS,4) == 0)
4320 /* read global tRNS */
4323 for (i=0; i < (long) length; i++)
4324 mng_info->global_trns[i]=p[i];
4327 for ( ; i < 256; i++)
4328 mng_info->global_trns[i]=255;
4330 mng_info->global_trns_length=length;
4331 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4334 if (memcmp(type,mng_gAMA,4) == 0)
4341 igamma=mng_get_long(p);
4342 mng_info->global_gamma=((float) igamma)*0.00001;
4343 mng_info->have_global_gama=MagickTrue;
4346 mng_info->have_global_gama=MagickFalse;
4347 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4351 if (memcmp(type,mng_cHRM,4) == 0)
4358 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4359 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4360 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4361 mng_info->global_chrm.red_primary.y=0.00001*
4362 mng_get_long(&p[12]);
4363 mng_info->global_chrm.green_primary.x=0.00001*
4364 mng_get_long(&p[16]);
4365 mng_info->global_chrm.green_primary.y=0.00001*
4366 mng_get_long(&p[20]);
4367 mng_info->global_chrm.blue_primary.x=0.00001*
4368 mng_get_long(&p[24]);
4369 mng_info->global_chrm.blue_primary.y=0.00001*
4370 mng_get_long(&p[28]);
4371 mng_info->have_global_chrm=MagickTrue;
4374 mng_info->have_global_chrm=MagickFalse;
4375 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4378 if (memcmp(type,mng_sRGB,4) == 0)
4385 mng_info->global_srgb_intent=(RenderingIntent) (p[0]+1);
4386 mng_info->have_global_srgb=MagickTrue;
4389 mng_info->have_global_srgb=MagickFalse;
4390 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4393 if (memcmp(type,mng_iCCP,4) == 0)
4401 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4404 if (memcmp(type,mng_FRAM,4) == 0)
4407 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4408 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4410 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4411 image->delay=frame_delay;
4412 frame_delay=default_frame_delay;
4413 frame_timeout=default_frame_timeout;
4417 mng_info->framing_mode=p[0];
4418 if (logging != MagickFalse)
4419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4420 " Framing_mode=%d",mng_info->framing_mode);
4424 Note the delay and frame clipping boundaries.
4426 p++; /* framing mode */
4427 while (*p && ((p-chunk) < (long) length))
4428 p++; /* frame name */
4429 p++; /* frame name terminator */
4430 if ((p-chunk) < (long) (length-4))
4437 change_delay=(*p++);
4438 change_timeout=(*p++);
4439 change_clipping=(*p++);
4440 p++; /* change_sync */
4443 frame_delay=(1UL*image->ticks_per_second*
4444 (mng_get_long(p))/mng_info->ticks_per_second);
4445 if (change_delay == 2)
4446 default_frame_delay=frame_delay;
4448 if (logging != MagickFalse)
4449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4450 " Framing_delay=%ld",frame_delay);
4454 frame_timeout=(1UL*image->ticks_per_second*
4455 (mng_get_long(p))/mng_info->ticks_per_second);
4456 if (change_delay == 2)
4457 default_frame_timeout=frame_timeout;
4459 if (logging != MagickFalse)
4460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4461 " Framing_timeout=%ld",frame_timeout);
4463 if (change_clipping)
4465 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4468 if (logging != MagickFalse)
4469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4470 " Frame_clipping: L=%ld R=%ld T=%ld B=%ld",
4471 fb.left, fb.right,fb.top,fb.bottom);
4472 if (change_clipping == 2)
4478 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4479 subframe_width=(unsigned long) (mng_info->clip.right
4480 -mng_info->clip.left);
4481 subframe_height=(unsigned long) (mng_info->clip.bottom
4482 -mng_info->clip.top);
4484 Insert a background layer behind the frame if framing_mode is 4.
4486 #if defined(MNG_INSERT_LAYERS)
4487 if (logging != MagickFalse)
4488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4489 " subframe_width=%lu, subframe_height=%lu",
4490 subframe_width, subframe_height);
4491 if (insert_layers && (mng_info->framing_mode == 4) &&
4492 (subframe_width) && (subframe_height))
4495 Allocate next image structure.
4497 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4499 AcquireNextImage(image_info,image);
4500 if (GetNextImageInList(image) == (Image *) NULL)
4502 image=DestroyImageList(image);
4503 MngInfoFreeStruct(mng_info,&have_mng_structure);
4504 return((Image *) NULL);
4506 image=SyncNextImageInList(image);
4508 mng_info->image=image;
4509 if (term_chunk_found)
4511 image->start_loop=MagickTrue;
4512 image->iterations=mng_iterations;
4513 term_chunk_found=MagickFalse;
4516 image->start_loop=MagickFalse;
4517 image->columns=subframe_width;
4518 image->rows=subframe_height;
4519 image->page.width=subframe_width;
4520 image->page.height=subframe_height;
4521 image->page.x=mng_info->clip.left;
4522 image->page.y=mng_info->clip.top;
4523 image->background_color=mng_background_color;
4524 image->matte=MagickFalse;
4526 (void) SetImageBackgroundColor(image);
4527 if (logging != MagickFalse)
4528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4529 " Inserted background layer, L=%ld, R=%ld, T=%ld, B=%ld",
4530 mng_info->clip.left,mng_info->clip.right,
4531 mng_info->clip.top,mng_info->clip.bottom);
4534 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4537 if (memcmp(type,mng_CLIP,4) == 0)
4546 first_object=(p[0] << 8) | p[1];
4547 last_object=(p[2] << 8) | p[3];
4548 for (i=(int) first_object; i <= (int) last_object; i++)
4550 if (mng_info->exists[i] && !mng_info->frozen[i])
4555 box=mng_info->object_clip[i];
4556 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4559 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4562 if (memcmp(type,mng_SAVE,4) == 0)
4564 for (i=1; i < MNG_MAX_OBJECTS; i++)
4565 if (mng_info->exists[i])
4567 mng_info->frozen[i]=MagickTrue;
4568 #ifdef MNG_OBJECT_BUFFERS
4569 if (mng_info->ob[i] != (MngBuffer *) NULL)
4570 mng_info->ob[i]->frozen=MagickTrue;
4574 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4578 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4583 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4585 for (i=1; i < MNG_MAX_OBJECTS; i++)
4586 MngInfoDiscardObject(mng_info,i);
4593 for (j=0; j < (long) length; j+=2)
4595 i=p[j] << 8 | p[j+1];
4596 MngInfoDiscardObject(mng_info,i);
4600 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4603 if (memcmp(type,mng_MOVE,4) == 0)
4612 first_object=(p[0] << 8) | p[1];
4613 last_object=(p[2] << 8) | p[3];
4614 for (i=(long) first_object; i <= (long) last_object; i++)
4616 if (mng_info->exists[i] && !mng_info->frozen[i])
4624 old_pair.a=mng_info->x_off[i];
4625 old_pair.b=mng_info->y_off[i];
4626 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4627 mng_info->x_off[i]=new_pair.a;
4628 mng_info->y_off[i]=new_pair.b;
4631 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4635 if (memcmp(type,mng_LOOP,4) == 0)
4638 loop_level=chunk[0];
4639 mng_info->loop_active[loop_level]=1; /* mark loop active */
4641 Record starting point.
4643 loop_iters=mng_get_long(&chunk[1]);
4644 if (logging != MagickFalse)
4645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4646 " LOOP level %ld has %ld iterations ",loop_level,loop_iters);
4647 if (loop_iters == 0)
4648 skipping_loop=loop_level;
4651 mng_info->loop_jump[loop_level]=TellBlob(image);
4652 mng_info->loop_count[loop_level]=loop_iters;
4654 mng_info->loop_iteration[loop_level]=0;
4655 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4658 if (memcmp(type,mng_ENDL,4) == 0)
4660 loop_level=chunk[0];
4661 if (skipping_loop > 0)
4663 if (skipping_loop == loop_level)
4666 Found end of zero-iteration loop.
4669 mng_info->loop_active[loop_level]=0;
4674 if (mng_info->loop_active[loop_level] == 1)
4676 mng_info->loop_count[loop_level]--;
4677 mng_info->loop_iteration[loop_level]++;
4678 if (logging != MagickFalse)
4679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4680 " ENDL: LOOP level %ld has %ld remaining iterations ",
4681 loop_level,mng_info->loop_count[loop_level]);
4682 if (mng_info->loop_count[loop_level] != 0)
4684 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4687 ThrowReaderException(CorruptImageError,
4688 "ImproperImageHeader");
4698 mng_info->loop_active[loop_level]=0;
4700 for (i=0; i < loop_level; i++)
4701 if (mng_info->loop_active[i] == 1)
4702 last_level=(short) i;
4703 loop_level=last_level;
4707 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4710 if (memcmp(type,mng_CLON,4) == 0)
4712 if (mng_info->clon_warning == 0)
4713 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4714 CoderError,"CLON is not implemented yet","`%s'",
4716 mng_info->clon_warning++;
4718 if (memcmp(type,mng_MAGN,4) == 0)
4733 magn_first=(p[0] << 8) | p[1];
4737 magn_last=(p[2] << 8) | p[3];
4739 magn_last=magn_first;
4740 #ifndef MNG_OBJECT_BUFFERS
4741 if (magn_first || magn_last)
4742 if (mng_info->magn_warning == 0)
4744 (void) ThrowMagickException(&image->exception,
4745 GetMagickModule(),CoderError,
4746 "MAGN is not implemented yet for nonzero objects",
4747 "`%s'",image->filename);
4748 mng_info->magn_warning++;
4757 magn_mx=(p[5] << 8) | p[6];
4764 magn_my=(p[7] << 8) | p[8];
4771 magn_ml=(p[9] << 8) | p[10];
4778 magn_mr=(p[11] << 8) | p[12];
4785 magn_mt=(p[13] << 8) | p[14];
4792 magn_mb=(p[15] << 8) | p[16];
4801 magn_methy=magn_methx;
4803 if (magn_methx > 5 || magn_methy > 5)
4804 if (mng_info->magn_warning == 0)
4806 (void) ThrowMagickException(&image->exception,
4807 GetMagickModule(),CoderError,
4808 "Unknown MAGN method in MNG datastream","`%s'",
4810 mng_info->magn_warning++;
4812 #ifdef MNG_OBJECT_BUFFERS
4813 /* Magnify existing objects in the range magn_first to magn_last */
4815 if (magn_first == 0 || magn_last == 0)
4817 /* Save the magnification factors for object 0 */
4818 mng_info->magn_mb=magn_mb;
4819 mng_info->magn_ml=magn_ml;
4820 mng_info->magn_mr=magn_mr;
4821 mng_info->magn_mt=magn_mt;
4822 mng_info->magn_mx=magn_mx;
4823 mng_info->magn_my=magn_my;
4824 mng_info->magn_methx=magn_methx;
4825 mng_info->magn_methy=magn_methy;
4828 if (memcmp(type,mng_PAST,4) == 0)
4830 if (mng_info->past_warning == 0)
4831 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4832 CoderError,"PAST is not implemented yet","`%s'",
4834 mng_info->past_warning++;
4836 if (memcmp(type,mng_SHOW,4) == 0)
4838 if (mng_info->show_warning == 0)
4839 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4840 CoderError,"SHOW is not implemented yet","`%s'",
4842 mng_info->show_warning++;
4844 if (memcmp(type,mng_sBIT,4) == 0)
4847 mng_info->have_global_sbit=MagickFalse;
4850 mng_info->global_sbit.gray=p[0];
4851 mng_info->global_sbit.red=p[0];
4852 mng_info->global_sbit.green=p[1];
4853 mng_info->global_sbit.blue=p[2];
4854 mng_info->global_sbit.alpha=p[3];
4855 mng_info->have_global_sbit=MagickTrue;
4858 if (memcmp(type,mng_pHYs,4) == 0)
4862 mng_info->global_x_pixels_per_unit=
4863 (unsigned long) mng_get_long(p);
4864 mng_info->global_y_pixels_per_unit=
4865 (unsigned long) mng_get_long(&p[4]);
4866 mng_info->global_phys_unit_type=p[8];
4867 mng_info->have_global_phys=MagickTrue;
4870 mng_info->have_global_phys=MagickFalse;
4872 if (memcmp(type,mng_pHYg,4) == 0)
4874 if (mng_info->phyg_warning == 0)
4875 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4876 CoderError,"pHYg is not implemented.","`%s'",image->filename);
4877 mng_info->phyg_warning++;
4879 if (memcmp(type,mng_BASI,4) == 0)
4881 skip_to_iend=MagickTrue;
4882 if (mng_info->basi_warning == 0)
4883 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4884 CoderError,"BASI is not implemented yet","`%s'",
4886 mng_info->basi_warning++;
4887 #ifdef MNG_BASI_SUPPORTED
4888 basi_width=(unsigned long) ((p[0] << 24) | (p[1] << 16) |
4889 (p[2] << 8) | p[3]);
4890 basi_height=(unsigned long) ((p[4] << 24) | (p[5] << 16) |
4891 (p[6] << 8) | p[7]);
4892 basi_color_type=p[8];
4893 basi_compression_method=p[9];
4894 basi_filter_type=p[10];
4895 basi_interlace_method=p[11];
4897 basi_red=(p[12] << 8) & p[13];
4901 basi_green=(p[14] << 8) & p[15];
4905 basi_blue=(p[16] << 8) & p[17];
4909 basi_alpha=(p[18] << 8) & p[19];
4912 if (basi_sample_depth == 16)
4918 basi_viewable=p[20];
4922 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4925 if (memcmp(type,mng_IHDR,4)
4926 #if defined(JNG_SUPPORTED)
4927 && memcmp(type,mng_JHDR,4)
4931 /* Not an IHDR or JHDR chunk */
4933 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4937 if (logging != MagickFalse)
4938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4939 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
4940 mng_info->exists[object_id]=MagickTrue;
4941 mng_info->viewable[object_id]=MagickTrue;
4942 if (mng_info->invisible[object_id])
4944 if (logging != MagickFalse)
4945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4946 " Skipping invisible object");
4947 skip_to_iend=MagickTrue;
4948 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4951 #if defined(MNG_INSERT_LAYERS)
4953 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4954 image_width=(unsigned long) mng_get_long(p);
4955 image_height=(unsigned long) mng_get_long(&p[4]);
4957 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4960 Insert a transparent background layer behind the entire animation
4961 if it is not full screen.
4963 #if defined(MNG_INSERT_LAYERS)
4964 if (insert_layers && mng_type && first_mng_object)
4966 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
4967 (image_width < mng_info->mng_width) ||
4968 (mng_info->clip.right < (long) mng_info->mng_width) ||
4969 (image_height < mng_info->mng_height) ||
4970 (mng_info->clip.bottom < (long) mng_info->mng_height))
4972 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4975 Allocate next image structure.
4977 AcquireNextImage(image_info,image);
4978 if (GetNextImageInList(image) == (Image *) NULL)
4980 image=DestroyImageList(image);
4981 MngInfoFreeStruct(mng_info,&have_mng_structure);
4982 return((Image *) NULL);
4984 image=SyncNextImageInList(image);
4986 mng_info->image=image;
4987 if (term_chunk_found)
4989 image->start_loop=MagickTrue;
4990 image->iterations=mng_iterations;
4991 term_chunk_found=MagickFalse;
4994 image->start_loop=MagickFalse;
4996 Make a background rectangle.
4999 image->columns=mng_info->mng_width;
5000 image->rows=mng_info->mng_height;
5001 image->page.width=mng_info->mng_width;
5002 image->page.height=mng_info->mng_height;
5005 image->background_color=mng_background_color;
5006 (void) SetImageBackgroundColor(image);
5007 if (logging != MagickFalse)
5008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5009 " Inserted transparent background layer, W=%lud, H=%lud",
5010 mng_info->mng_width,mng_info->mng_height);
5014 Insert a background layer behind the upcoming image if
5015 framing_mode is 3, and we haven't already inserted one.
5017 if (insert_layers && (mng_info->framing_mode == 3) &&
5018 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5019 (simplicity & 0x08)))
5021 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5024 Allocate next image structure.
5026 AcquireNextImage(image_info,image);
5027 if (GetNextImageInList(image) == (Image *) NULL)
5029 image=DestroyImageList(image);
5030 MngInfoFreeStruct(mng_info,&have_mng_structure);
5031 return((Image *) NULL);
5033 image=SyncNextImageInList(image);
5035 mng_info->image=image;
5036 if (term_chunk_found)
5038 image->start_loop=MagickTrue;
5039 image->iterations=mng_iterations;
5040 term_chunk_found=MagickFalse;
5043 image->start_loop=MagickFalse;
5045 image->columns=subframe_width;
5046 image->rows=subframe_height;
5047 image->page.width=subframe_width;
5048 image->page.height=subframe_height;
5049 image->page.x=mng_info->clip.left;
5050 image->page.y=mng_info->clip.top;
5051 image->background_color=mng_background_color;
5052 image->matte=MagickFalse;
5053 (void) SetImageBackgroundColor(image);
5054 if (logging != MagickFalse)
5055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5056 " Inserted background layer, L=%ld, R=%ld, T=%ld, B=%ld",
5057 mng_info->clip.left,mng_info->clip.right,
5058 mng_info->clip.top,mng_info->clip.bottom);
5060 #endif /* MNG_INSERT_LAYERS */
5061 first_mng_object=MagickFalse;
5062 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5065 Allocate next image structure.
5067 AcquireNextImage(image_info,image);
5068 if (GetNextImageInList(image) == (Image *) NULL)
5070 image=DestroyImageList(image);
5071 MngInfoFreeStruct(mng_info,&have_mng_structure);
5072 return((Image *) NULL);
5074 image=SyncNextImageInList(image);
5076 mng_info->image=image;
5077 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5078 GetBlobSize(image));
5079 if (status == MagickFalse)
5081 if (term_chunk_found)
5083 image->start_loop=MagickTrue;
5084 term_chunk_found=MagickFalse;
5087 image->start_loop=MagickFalse;
5088 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5090 image->delay=frame_delay;
5091 frame_delay=default_frame_delay;
5095 image->page.width=mng_info->mng_width;
5096 image->page.height=mng_info->mng_height;
5097 image->page.x=mng_info->x_off[object_id];
5098 image->page.y=mng_info->y_off[object_id];
5099 image->iterations=mng_iterations;
5101 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5103 if (logging != MagickFalse)
5104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5105 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5107 offset=SeekBlob(image,-((long) length+12),SEEK_CUR);
5109 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5113 mng_info->image=image;
5114 mng_info->mng_type=mng_type;
5115 mng_info->object_id=object_id;
5117 if (memcmp(type,mng_IHDR,4) == 0)
5118 image=ReadOnePNGImage(mng_info,image_info,exception);
5119 #if defined(JNG_SUPPORTED)
5121 image=ReadOneJNGImage(mng_info,image_info,exception);
5124 if (image == (Image *) NULL)
5126 if (IsImageObject(previous) != MagickFalse)
5128 (void) DestroyImageList(previous);
5129 (void) CloseBlob(previous);
5131 MngInfoFreeStruct(mng_info,&have_mng_structure);
5132 return((Image *) NULL);
5134 if (image->columns == 0 || image->rows == 0)
5136 (void) CloseBlob(image);
5137 image=DestroyImageList(image);
5138 MngInfoFreeStruct(mng_info,&have_mng_structure);
5139 return((Image *) NULL);
5141 mng_info->image=image;
5148 if (mng_info->magn_methx || mng_info->magn_methy)
5154 if (logging != MagickFalse)
5155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5156 " Processing MNG MAGN chunk");
5158 if (mng_info->magn_methx == 1)
5160 magnified_width=mng_info->magn_ml;
5161 if (image->columns > 1)
5162 magnified_width += mng_info->magn_mr;
5163 if (image->columns > 2)
5164 magnified_width += (image->columns-2)*(mng_info->magn_mx);
5168 magnified_width=image->columns;
5169 if (image->columns > 1)
5170 magnified_width += mng_info->magn_ml-1;
5171 if (image->columns > 2)
5172 magnified_width += mng_info->magn_mr-1;
5173 if (image->columns > 3)
5174 magnified_width += (image->columns-3)*(mng_info->magn_mx-1);
5176 if (mng_info->magn_methy == 1)
5178 magnified_height=mng_info->magn_mt;
5179 if (image->rows > 1)
5180 magnified_height += mng_info->magn_mb;
5181 if (image->rows > 2)
5182 magnified_height += (image->rows-2)*(mng_info->magn_my);
5186 magnified_height=image->rows;
5187 if (image->rows > 1)
5188 magnified_height += mng_info->magn_mt-1;
5189 if (image->rows > 2)
5190 magnified_height += mng_info->magn_mb-1;
5191 if (image->rows > 3)
5192 magnified_height += (image->rows-3)*(mng_info->magn_my-1);
5194 if (magnified_height > image->rows ||
5195 magnified_width > image->columns)
5210 register PixelPacket
5223 Allocate next image structure.
5225 if (logging != MagickFalse)
5226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5227 " Allocate magnified image");
5228 AcquireNextImage(image_info,image);
5229 if (GetNextImageInList(image) == (Image *) NULL)
5231 image=DestroyImageList(image);
5232 MngInfoFreeStruct(mng_info,&have_mng_structure);
5233 return((Image *) NULL);
5236 large_image=SyncNextImageInList(image);
5238 large_image->columns=magnified_width;
5239 large_image->rows=magnified_height;
5241 magn_methx=mng_info->magn_methx;
5242 magn_methy=mng_info->magn_methy;
5244 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5245 #define QM unsigned short
5246 if (magn_methx != 1 || magn_methy != 1)
5249 Scale pixels to unsigned shorts to prevent
5250 overflow of intermediate values of interpolations
5252 for (y=0; y < (long) image->rows; y++)
5254 q=GetAuthenticPixels(image,0,y,image->columns,1,
5256 for (x=(long) image->columns-1; x >= 0; x--)
5258 q->red=ScaleQuantumToShort(q->red);
5259 q->green=ScaleQuantumToShort(q->green);
5260 q->blue=ScaleQuantumToShort(q->blue);
5261 q->opacity=ScaleQuantumToShort(q->opacity);
5264 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5272 if (image->matte != MagickFalse)
5273 (void) SetImageBackgroundColor(large_image);
5276 large_image->background_color.opacity=OpaqueOpacity;
5277 (void) SetImageBackgroundColor(large_image);
5278 if (magn_methx == 4)
5280 if (magn_methx == 5)
5282 if (magn_methy == 4)
5284 if (magn_methy == 5)
5288 /* magnify the rows into the right side of the large image */
5290 if (logging != MagickFalse)
5291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5292 " Magnify the rows to %lu",large_image->rows);
5293 m=(long) mng_info->magn_mt;
5295 length=(size_t) image->columns;
5296 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5297 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5298 if ((prev == (PixelPacket *) NULL) ||
5299 (next == (PixelPacket *) NULL))
5301 image=DestroyImageList(image);
5302 MngInfoFreeStruct(mng_info,&have_mng_structure);
5303 ThrowReaderException(ResourceLimitError,
5304 "MemoryAllocationFailed");
5306 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5307 (void) CopyMagickMemory(next,n,length);
5308 for (y=0; y < (long) image->rows; y++)
5311 m=(long) mng_info->magn_mt;
5312 else if (magn_methy > 1 && y == (long) image->rows-2)
5313 m=(long) mng_info->magn_mb;
5314 else if (magn_methy <= 1 && y == (long) image->rows-1)
5315 m=(long) mng_info->magn_mb;
5316 else if (magn_methy > 1 && y == (long) image->rows-1)
5319 m=(long) mng_info->magn_my;
5323 if (y < (long) image->rows-1)
5325 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5327 (void) CopyMagickMemory(next,n,length);
5329 for (i=0; i < m; i++, yy++)
5331 register PixelPacket
5334 assert(yy < (long) large_image->rows);
5337 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5339 q+=(large_image->columns-image->columns);
5340 for (x=(long) image->columns-1; x >= 0; x--)
5342 /* TO DO: get color as function of indices[x] */
5344 if (image->storage_class == PseudoClass)
5349 if (magn_methy <= 1)
5351 *q=(*pixels); /* replicate previous */
5353 else if (magn_methy == 2 || magn_methy == 4)
5360 (*q).red=(QM) (((long) (2*i*((*n).red
5361 -(*pixels).red)+m))/((long) (m*2))
5363 (*q).green=(QM) (((long) (2*i*((*n).green
5364 -(*pixels).green)+m))/((long) (m*2))
5366 (*q).blue=(QM) (((long) (2*i*((*n).blue
5367 -(*pixels).blue)+m))/((long) (m*2))
5369 if (image->matte != MagickFalse)
5370 (*q).opacity=(QM) (((long)
5372 -(*pixels).opacity)+m))
5373 /((long) (m*2))+(*pixels).opacity);
5375 if (magn_methy == 4)
5377 /* Replicate nearest */
5378 if (i <= ((m+1) << 1))
5379 (*q).opacity=(*pixels).opacity+0;
5381 (*q).opacity=(*n).opacity+0;
5384 else /* if (magn_methy == 3 || magn_methy == 5) */
5386 /* Replicate nearest */
5387 if (i <= ((m+1) << 1))
5391 if (magn_methy == 5)
5393 (*q).opacity=(QM) (((long) (2*i*((*n).opacity
5394 -(*pixels).opacity)+m))/((long) (m*2))
5395 +(*pixels).opacity);
5402 if (SyncAuthenticPixels(large_image,exception) == 0)
5406 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5407 next=(PixelPacket *) RelinquishMagickMemory(next);
5409 length=image->columns;
5411 if (logging != MagickFalse)
5412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5413 " Delete original image");
5415 DeleteImageFromList(&image);
5419 mng_info->image=image;
5421 /* magnify the columns */
5422 if (logging != MagickFalse)
5423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5424 " Magnify the columns to %lu",image->columns);
5426 for (y=0; y < (long) image->rows; y++)
5428 register PixelPacket
5431 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5432 pixels=q+(image->columns-length);
5434 for (x=(long) (image->columns-length);
5435 x < (long) image->columns; x++)
5437 if (x == (long) (image->columns-length))
5438 m=(long) mng_info->magn_ml;
5439 else if (magn_methx > 1 && x == (long) image->columns-2)
5440 m=(long) mng_info->magn_mr;
5441 else if (magn_methx <= 1 && x == (long) image->columns-1)
5442 m=(long) mng_info->magn_mr;
5443 else if (magn_methx > 1 && x == (long) image->columns-1)
5446 m=(long) mng_info->magn_mx;
5447 for (i=0; i < m; i++)
5449 if (magn_methx <= 1)
5451 /* replicate previous */
5454 else if (magn_methx == 2 || magn_methx == 4)
5461 (*q).red=(QM) ((2*i*((*n).red
5463 /((long) (m*2))+(*pixels).red);
5464 (*q).green=(QM) ((2*i*((*n).green
5466 +m)/((long) (m*2))+(*pixels).green);
5467 (*q).blue=(QM) ((2*i*((*n).blue
5469 /((long) (m*2))+(*pixels).blue);
5470 if (image->matte != MagickFalse)
5471 (*q).opacity=(QM) ((2*i*((*n).opacity
5472 -(*pixels).opacity)+m)/((long) (m*2))
5473 +(*pixels).opacity);
5475 if (magn_methx == 4)
5477 /* Replicate nearest */
5478 if (i <= ((m+1) << 1))
5479 (*q).opacity=(*pixels).opacity+0;
5481 (*q).opacity=(*n).opacity+0;
5484 else /* if (magn_methx == 3 || magn_methx == 5) */
5486 /* Replicate nearest */
5487 if (i <= ((m+1) << 1))
5491 if (magn_methx == 5)
5494 (*q).opacity=(QM) ((2*i*((*n).opacity
5495 -(*pixels).opacity)+m) /((long) (m*2))
5496 +(*pixels).opacity);
5504 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5507 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5508 if (magn_methx != 1 || magn_methy != 1)
5511 Rescale pixels to Quantum
5513 for (y=0; y < (long) image->rows; y++)
5515 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5516 for (x=(long) image->columns-1; x >= 0; x--)
5518 q->red=ScaleShortToQuantum(q->red);
5519 q->green=ScaleShortToQuantum(q->green);
5520 q->blue=ScaleShortToQuantum(q->blue);
5521 q->opacity=ScaleShortToQuantum(q->opacity);
5524 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5529 if (logging != MagickFalse)
5530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5531 " Finished MAGN processing");
5536 Crop_box is with respect to the upper left corner of the MNG.
5538 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5539 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5540 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5541 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5542 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5543 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5544 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5545 if ((crop_box.left != (mng_info->image_box.left
5546 +mng_info->x_off[object_id])) ||
5547 (crop_box.right != (mng_info->image_box.right
5548 +mng_info->x_off[object_id])) ||
5549 (crop_box.top != (mng_info->image_box.top
5550 +mng_info->y_off[object_id])) ||
5551 (crop_box.bottom != (mng_info->image_box.bottom
5552 +mng_info->y_off[object_id])))
5554 if (logging != MagickFalse)
5555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5556 " Crop the PNG image");
5557 if ((crop_box.left < crop_box.right) &&
5558 (crop_box.top < crop_box.bottom))
5567 Crop_info is with respect to the upper left corner of
5570 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5571 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5572 crop_info.width=(unsigned long) (crop_box.right-crop_box.left);
5573 crop_info.height=(unsigned long) (crop_box.bottom-crop_box.top);
5574 image->page.width=image->columns;
5575 image->page.height=image->rows;
5578 im=CropImage(image,&crop_info,exception);
5579 if (im != (Image *) NULL)
5581 image->columns=im->columns;
5582 image->rows=im->rows;
5583 im=DestroyImage(im);
5584 image->page.width=image->columns;
5585 image->page.height=image->rows;
5586 image->page.x=crop_box.left;
5587 image->page.y=crop_box.top;
5593 No pixels in crop area. The MNG spec still requires
5594 a layer, though, so make a single transparent pixel in
5595 the top left corner.
5600 (void) SetImageBackgroundColor(image);
5601 image->page.width=1;
5602 image->page.height=1;
5607 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
5608 image=mng_info->image;
5612 #if (MAGICKCORE_QUANTUM_DEPTH == 16) /* TO DO: treat Q:32 */
5613 /* Determine if bit depth can be reduced from 16 to 8.
5614 * Note that the method GetImageDepth doesn't check background
5615 * and doesn't handle PseudoClass specially. Also it uses
5616 * multiplication and division by 257 instead of shifting, so
5619 if (mng_info->optimize && image->depth == 16)
5627 ok_to_reduce=(((((unsigned long) image->background_color.red >> 8) &
5629 == ((unsigned long) image->background_color.red & 0xff)) &&
5630 ((((unsigned long) image->background_color.green >> 8) & 0xff)
5631 == ((unsigned long) image->background_color.green & 0xff)) &&
5632 ((((unsigned long) image->background_color.blue >> 8) & 0xff)
5633 == ((unsigned long) image->background_color.blue & 0xff)));
5634 if (ok_to_reduce && image->storage_class == PseudoClass)
5638 for (indx=0; indx < (long) image->colors; indx++)
5640 ok_to_reduce=(((((unsigned long) image->colormap[indx].red >>
5642 == ((unsigned long) image->colormap[indx].red & 0xff)) &&
5643 ((((unsigned long) image->colormap[indx].green >> 8) & 0xff)
5644 == ((unsigned long) image->colormap[indx].green & 0xff)) &&
5645 ((((unsigned long) image->colormap[indx].blue >> 8) & 0xff)
5646 == ((unsigned long) image->colormap[indx].blue & 0xff)));
5647 if (ok_to_reduce == MagickFalse)
5651 if ((ok_to_reduce != MagickFalse) &&
5652 (image->storage_class != PseudoClass))
5660 for (y=0; y < (long) image->rows; y++)
5662 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
5663 if (p == (const PixelPacket *) NULL)
5665 for (x=(long) image->columns-1; x >= 0; x--)
5668 (((unsigned long) p->red >> 8) & 0xff) ==
5669 ((unsigned long) p->red & 0xff)) &&
5670 ((((unsigned long) p->green >> 8) & 0xff) ==
5671 ((unsigned long) p->green & 0xff)) &&
5672 ((((unsigned long) p->blue >> 8) & 0xff) ==
5673 ((unsigned long) p->blue & 0xff)) &&
5675 (((unsigned long) p->opacity >> 8) & 0xff) ==
5676 ((unsigned long) p->opacity & 0xff)))));
5677 if (ok_to_reduce == 0)
5688 if (logging != MagickFalse)
5689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5690 " Reducing PNG bit depth to 8 without loss of info");
5694 GetImageException(image,exception);
5695 if (image_info->number_scenes != 0)
5697 if (mng_info->scenes_found >
5698 (long) (image_info->first_scene+image_info->number_scenes))
5701 if (logging != MagickFalse)
5702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5703 " Finished reading image datastream.");
5704 } while (LocaleCompare(image_info->magick,"MNG") == 0);
5705 (void) CloseBlob(image);
5706 if (logging != MagickFalse)
5707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5708 " Finished reading all image datastreams.");
5709 #if defined(MNG_INSERT_LAYERS)
5710 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
5711 (mng_info->mng_height))
5714 Insert a background layer if nothing else was found.
5716 if (logging != MagickFalse)
5717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5718 " No images found. Inserting a background layer.");
5719 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5722 Allocate next image structure.
5724 AcquireNextImage(image_info,image);
5725 if (GetNextImageInList(image) == (Image *) NULL)
5727 image=DestroyImageList(image);
5728 MngInfoFreeStruct(mng_info,&have_mng_structure);
5729 if (logging != MagickFalse)
5730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5731 " Allocation failed, returning NULL.");
5732 return((Image *) NULL);
5734 image=SyncNextImageInList(image);
5736 image->columns=mng_info->mng_width;
5737 image->rows=mng_info->mng_height;
5738 image->page.width=mng_info->mng_width;
5739 image->page.height=mng_info->mng_height;
5742 image->background_color=mng_background_color;
5743 image->matte=MagickFalse;
5744 if (image_info->ping == MagickFalse)
5745 (void) SetImageBackgroundColor(image);
5746 mng_info->image_found++;
5749 image->iterations=mng_iterations;
5750 if (mng_iterations == 1)
5751 image->start_loop=MagickTrue;
5752 while (GetPreviousImageInList(image) != (Image *) NULL)
5755 if (image_count > 10*mng_info->image_found)
5757 if (logging != MagickFalse)
5758 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
5759 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5760 CoderError,"Linked list is corrupted, beginning of list not found",
5761 "`%s'",image_info->filename);
5762 return((Image *) NULL);
5764 image=GetPreviousImageInList(image);
5765 if (GetNextImageInList(image) == (Image *) NULL)
5767 if (logging != MagickFalse)
5768 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
5769 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5770 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
5771 image_info->filename);
5774 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
5775 GetNextImageInList(image) ==
5778 if (logging != MagickFalse)
5779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5780 " First image null");
5781 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5782 CoderError,"image->next for first image is NULL but shouldn't be.",
5783 "`%s'",image_info->filename);
5785 if (mng_info->image_found == 0)
5787 if (logging != MagickFalse)
5788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5789 " No visible images found.");
5790 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5791 CoderError,"No visible images in file","`%s'",image_info->filename);
5792 if (image != (Image *) NULL)
5793 image=DestroyImageList(image);
5794 MngInfoFreeStruct(mng_info,&have_mng_structure);
5795 return((Image *) NULL);
5798 if (mng_info->ticks_per_second)
5799 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
5800 final_delay/mng_info->ticks_per_second;
5802 image->start_loop=MagickTrue;
5803 /* Find final nonzero image delay */
5804 final_image_delay=0;
5805 while (GetNextImageInList(image) != (Image *) NULL)
5808 final_image_delay=image->delay;
5809 image=GetNextImageInList(image);
5811 if (final_delay < final_image_delay)
5812 final_delay=final_image_delay;
5813 image->delay=final_delay;
5814 if (logging != MagickFalse)
5815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5816 " image->delay=%lu, final_delay=%lu",image->delay,final_delay);
5817 if (logging != MagickFalse)
5823 image=GetFirstImageInList(image);
5824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5825 " Before coalesce:");
5826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5827 " scene 0 delay=%lu",image->delay);
5828 while (GetNextImageInList(image) != (Image *) NULL)
5830 image=GetNextImageInList(image);
5831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5832 " scene %d delay=%lu",++scene,image->delay);
5836 image=GetFirstImageInList(image);
5837 #ifdef MNG_COALESCE_LAYERS
5847 if (logging != MagickFalse)
5848 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
5850 next_image=CoalesceImages(image,&image->exception);
5851 if (next_image == (Image *) NULL)
5852 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5853 image=DestroyImageList(image);
5855 for (next=image; next != (Image *) NULL; next=next_image)
5857 next->page.width=mng_info->mng_width;
5858 next->page.height=mng_info->mng_height;
5861 next->scene=scene++;
5862 next_image=GetNextImageInList(next);
5863 if (next_image == (Image *) NULL)
5865 if (next->delay == 0)
5868 next_image->previous=GetPreviousImageInList(next);
5869 if (GetPreviousImageInList(next) == (Image *) NULL)
5872 next->previous->next=next_image;
5873 next=DestroyImage(next);
5879 while (GetNextImageInList(image) != (Image *) NULL)
5880 image=GetNextImageInList(image);
5881 image->dispose=BackgroundDispose;
5883 if (logging != MagickFalse)
5889 image=GetFirstImageInList(image);
5890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5891 " After coalesce:");
5892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5893 " scene 0 delay=%lu dispose=%d",image->delay,(int) image->dispose);
5894 while (GetNextImageInList(image) != (Image *) NULL)
5896 image=GetNextImageInList(image);
5897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5898 " scene %d delay=%lu dispose=%d",++scene,
5899 image->delay,(int) image->dispose);
5902 image=GetFirstImageInList(image);
5903 MngInfoFreeStruct(mng_info,&have_mng_structure);
5904 have_mng_structure=MagickFalse;
5905 if (logging != MagickFalse)
5906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
5907 return(GetFirstImageInList(image));
5909 #else /* PNG_LIBPNG_VER > 10011 */
5910 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5912 printf("Your PNG library is too old: You have libpng-%s\n",
5913 PNG_LIBPNG_VER_STRING);
5914 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
5915 "PNG library is too old","`%s'",image_info->filename);
5916 return(Image *) NULL;
5918 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5920 return(ReadPNGImage(image_info,exception));
5922 #endif /* PNG_LIBPNG_VER > 10011 */
5926 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5930 % R e g i s t e r P N G I m a g e %
5934 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5936 % RegisterPNGImage() adds properties for the PNG image format to
5937 % the list of supported formats. The properties include the image format
5938 % tag, a method to read and/or write the format, whether the format
5939 % supports the saving of more than one frame to the same file or blob,
5940 % whether the format supports native in-memory I/O, and a brief
5941 % description of the format.
5943 % The format of the RegisterPNGImage method is:
5945 % unsigned long RegisterPNGImage(void)
5948 ModuleExport unsigned long RegisterPNGImage(void)
5951 version[MaxTextExtent];
5959 "See http://www.libpng.org/ for details about the PNG format."
5963 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
5968 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
5973 #if defined(PNG_LIBPNG_VER_STRING)
5974 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
5975 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
5976 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
5978 (void) ConcatenateMagickString(version,",",MaxTextExtent);
5979 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
5983 entry=SetMagickInfo("MNG");
5984 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
5985 #if defined(MAGICKCORE_PNG_DELEGATE)
5986 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
5987 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
5989 entry->magick=(IsImageFormatHandler *) IsMNG;
5990 entry->description=ConstantString("Multiple-image Network Graphics");
5991 if (*version != '\0')
5992 entry->version=ConstantString(version);
5993 entry->module=ConstantString("PNG");
5994 entry->note=ConstantString(MNGNote);
5995 (void) RegisterMagickInfo(entry);
5997 entry=SetMagickInfo("PNG");
5998 #if defined(MAGICKCORE_PNG_DELEGATE)
5999 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6000 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6002 entry->magick=(IsImageFormatHandler *) IsPNG;
6003 entry->adjoin=MagickFalse;
6004 entry->description=ConstantString("Portable Network Graphics");
6005 entry->module=ConstantString("PNG");
6006 if (*version != '\0')
6007 entry->version=ConstantString(version);
6008 entry->note=ConstantString(PNGNote);
6009 (void) RegisterMagickInfo(entry);
6011 entry=SetMagickInfo("PNG8");
6012 #if defined(MAGICKCORE_PNG_DELEGATE)
6013 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6014 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6016 entry->magick=(IsImageFormatHandler *) IsPNG;
6017 entry->adjoin=MagickFalse;
6018 entry->description=ConstantString(
6019 "8-bit indexed with optional binary transparency");
6020 entry->module=ConstantString("PNG");
6021 (void) RegisterMagickInfo(entry);
6023 entry=SetMagickInfo("PNG24");
6025 #if defined(ZLIB_VERSION)
6026 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6027 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6028 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6030 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6031 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6034 if (*version != '\0')
6035 entry->version=ConstantString(version);
6036 #if defined(MAGICKCORE_PNG_DELEGATE)
6037 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6038 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6040 entry->magick=(IsImageFormatHandler *) IsPNG;
6041 entry->adjoin=MagickFalse;
6042 entry->description=ConstantString("opaque 24-bit RGB");
6043 entry->module=ConstantString("PNG");
6044 (void) RegisterMagickInfo(entry);
6046 entry=SetMagickInfo("PNG32");
6047 #if defined(MAGICKCORE_PNG_DELEGATE)
6048 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6049 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6051 entry->magick=(IsImageFormatHandler *) IsPNG;
6052 entry->adjoin=MagickFalse;
6053 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6054 entry->module=ConstantString("PNG");
6055 (void) RegisterMagickInfo(entry);
6057 entry=SetMagickInfo("JNG");
6058 #if defined(JNG_SUPPORTED)
6059 #if defined(MAGICKCORE_PNG_DELEGATE)
6060 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6061 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6064 entry->magick=(IsImageFormatHandler *) IsJNG;
6065 entry->adjoin=MagickFalse;
6066 entry->description=ConstantString("JPEG Network Graphics");
6067 entry->module=ConstantString("PNG");
6068 entry->note=ConstantString(JNGNote);
6069 (void) RegisterMagickInfo(entry);
6070 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6071 png_semaphore=AllocateSemaphoreInfo();
6073 return(MagickImageCoderSignature);
6077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6081 % U n r e g i s t e r P N G I m a g e %
6085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6087 % UnregisterPNGImage() removes format registrations made by the
6088 % PNG module from the list of supported formats.
6090 % The format of the UnregisterPNGImage method is:
6092 % UnregisterPNGImage(void)
6095 ModuleExport void UnregisterPNGImage(void)
6097 (void) UnregisterMagickInfo("MNG");
6098 (void) UnregisterMagickInfo("PNG");
6099 (void) UnregisterMagickInfo("PNG8");
6100 (void) UnregisterMagickInfo("PNG24");
6101 (void) UnregisterMagickInfo("PNG32");
6102 (void) UnregisterMagickInfo("JNG");
6103 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6104 if (png_semaphore != (SemaphoreInfo *) NULL)
6105 DestroySemaphoreInfo(&png_semaphore);
6109 #if defined(MAGICKCORE_PNG_DELEGATE)
6110 #if PNG_LIBPNG_VER > 10011
6112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6116 % W r i t e M N G I m a g e %
6120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6122 % WriteMNGImage() writes an image in the Portable Network Graphics
6123 % Group's "Multiple-image Network Graphics" encoded image format.
6125 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6127 % The format of the WriteMNGImage method is:
6129 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6131 % A description of each parameter follows.
6133 % o image_info: the image info.
6135 % o image: The image.
6138 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6139 % "To do" under ReadPNGImage):
6141 % Fix problem with palette sorting (when PNG_SORT_PALETTE is enabled,
6142 % some GIF animations don't convert properly)
6144 % Preserve all unknown and not-yet-handled known chunks found in input
6145 % PNG file and copy them into output PNG files according to the PNG
6148 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6150 % Improve selection of color type (use indexed-colour or indexed-colour
6151 % with tRNS when 256 or fewer unique RGBA values are present).
6153 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6154 % This will be complicated if we limit ourselves to generating MNG-LC
6155 % files. For now we ignore disposal method 3 and simply overlay the next
6158 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6159 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6160 % [mostly done 15 June 1999 but still need to take care of tRNS]
6162 % Check for identical sRGB and replace with a global sRGB (and remove
6163 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6164 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6165 % local gAMA/cHRM with local sRGB if appropriate).
6167 % Check for identical sBIT chunks and write global ones.
6169 % Provide option to skip writing the signature tEXt chunks.
6171 % Use signatures to detect identical objects and reuse the first
6172 % instance of such objects instead of writing duplicate objects.
6174 % Use a smaller-than-32k value of compression window size when
6177 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6178 % ancillary text chunks and save profiles.
6180 % Provide an option to force LC files (to ensure exact framing rate)
6183 % Provide an option to force VLC files instead of LC, even when offsets
6184 % are present. This will involve expanding the embedded images with a
6185 % transparent region at the top and/or left.
6189 png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6190 png_info *ping_info, unsigned char *profile_type, unsigned char
6191 *profile_description, unsigned char *profile_data, png_uint_32 length)
6210 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6212 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6215 if (image_info->verbose)
6217 (void) printf("writing raw profile: type=%s, length=%lu\n",
6218 (char *) profile_type, length);
6220 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6221 description_length=(png_uint_32) strlen((const char *) profile_description);
6222 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6223 + description_length);
6224 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6225 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6226 text[0].key[0]='\0';
6227 (void) ConcatenateMagickString(text[0].key,
6228 "Raw profile type ",MaxTextExtent);
6229 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6233 (void) CopyMagickString(dp,(const char *) profile_description,
6235 dp+=description_length;
6237 (void) FormatMagickString(dp,allocated_length-
6238 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6240 for (i=0; i < (long) length; i++)
6244 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6245 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6249 text[0].text_length=(png_size_t) (dp-text[0].text);
6250 text[0].compression=image_info->compression == NoCompression ||
6251 (image_info->compression == UndefinedCompression &&
6252 text[0].text_length < 128) ? -1 : 0;
6253 if (text[0].text_length <= allocated_length)
6254 png_set_text(ping,ping_info,text,1);
6255 png_free(ping,text[0].text);
6256 png_free(ping,text[0].key);
6257 png_free(ping,text);
6260 static MagickBooleanType png_write_chunk_from_profile(Image *image,
6261 const char *string, int logging)
6274 ResetImageProfileIterator(image);
6275 for (name=GetNextImageProfile(image); name != (const char *) NULL; ){
6276 profile=GetImageProfile(image,name);
6277 if (profile != (const StringInfo *) NULL)
6282 if (LocaleNCompare(name,string,11) == 0) {
6283 if (logging != MagickFalse)
6284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6285 " Found %s profile",name);
6287 png_profile=CloneStringInfo(profile);
6288 data=GetStringInfoDatum(png_profile),
6289 length=(png_uint_32) GetStringInfoLength(png_profile);
6294 (void) WriteBlobMSBULong(image,length-5); /* data length */
6295 (void) WriteBlob(image,length-1,data+1);
6296 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6297 png_profile=DestroyStringInfo(png_profile);
6300 name=GetNextImageProfile(image);
6305 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6306 const ImageInfo *image_info,Image *image)
6308 /* Write one PNG image */
6355 register IndexPacket
6372 ping_interlace_method,
6373 ping_compression_method,
6377 volatile unsigned long
6387 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6388 " enter WriteOnePNGImage()");
6390 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6391 LockSemaphoreInfo(png_semaphore);
6394 /* Initialize some stuff */
6397 ping_interlace_method=0,
6398 ping_compression_method=0,
6399 ping_filter_method=0,
6402 ping_background.red = 0;
6403 ping_background.green = 0;
6404 ping_background.blue = 0;
6405 ping_background.gray = 0;
6406 ping_background.index = 0;
6408 ping_trans_color.red=0;
6409 ping_trans_color.green=0;
6410 ping_trans_color.blue=0;
6411 ping_trans_color.gray=0;
6413 ping_trans_alpha = NULL;
6415 quantum_info = (QuantumInfo *) NULL;
6416 image_colors=image->colors;
6417 image_depth=image->depth;
6418 image_matte=image->matte;
6420 if (image->colorspace != RGBColorspace)
6421 (void) TransformImageColorspace(image,RGBColorspace);
6422 mng_info->IsPalette=image->storage_class == PseudoClass &&
6423 image_colors <= 256 && !IsOpaqueImage(image,&image->exception);
6424 mng_info->optimize=image_info->type == OptimizeType;
6427 Allocate the PNG structures
6429 #ifdef PNG_USER_MEM_SUPPORTED
6430 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
6431 PNGErrorHandler,PNGWarningHandler,(void *) NULL,
6432 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
6434 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
6435 PNGErrorHandler,PNGWarningHandler);
6437 if (ping == (png_struct *) NULL)
6438 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6439 ping_info=png_create_info_struct(ping);
6440 if (ping_info == (png_info *) NULL)
6442 png_destroy_write_struct(&ping,(png_info **) NULL);
6443 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6445 png_set_write_fn(ping,image,png_put_data,png_flush_data);
6446 png_pixels=(unsigned char *) NULL;
6448 if (setjmp(png_jmpbuf(ping)))
6454 if (image_info->verbose)
6455 (void) printf("PNG write has failed.\n");
6457 png_destroy_write_struct(&ping,&ping_info);
6458 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6459 UnlockSemaphoreInfo(png_semaphore);
6461 return(MagickFalse);
6464 Prepare PNG for writing.
6466 #if defined(PNG_MNG_FEATURES_SUPPORTED)
6467 if (mng_info->write_mng)
6468 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
6470 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
6471 if (mng_info->write_mng)
6472 png_permit_empty_plte(ping,MagickTrue);
6476 ping_width=image->columns;
6477 ping_height=image->rows;
6478 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
6480 if (mng_info->write_png_depth != 0)
6481 image_depth=mng_info->write_png_depth;
6482 /* Adjust requested depth to next higher valid depth if necessary */
6483 if (image_depth > 8)
6485 if ((image_depth > 4) && (image_depth < 8))
6487 if (image_depth == 3)
6489 if (logging != MagickFalse)
6491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6492 " width=%lu",(unsigned long) ping_width);
6493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6494 " height=%lu",(unsigned long) ping_height);
6495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6496 " image_matte=%u",image->matte);
6497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6498 " image_depth=%lu",image->depth);
6499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6500 " requested PNG image_depth=%lu",image->depth);
6502 save_image_depth=image_depth;
6503 ping_bit_depth=(png_byte) save_image_depth;
6504 #if defined(PNG_pHYs_SUPPORTED)
6505 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
6506 (!mng_info->write_mng || !mng_info->equal_physs))
6515 if (image->units == PixelsPerInchResolution)
6517 unit_type=PNG_RESOLUTION_METER;
6518 x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
6519 y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
6521 else if (image->units == PixelsPerCentimeterResolution)
6523 unit_type=PNG_RESOLUTION_METER;
6524 x_resolution=(png_uint_32) (100.0*image->x_resolution);
6525 y_resolution=(png_uint_32) (100.0*image->y_resolution);
6529 unit_type=PNG_RESOLUTION_UNKNOWN;
6530 x_resolution=(png_uint_32) image->x_resolution;
6531 y_resolution=(png_uint_32) image->y_resolution;
6533 png_set_pHYs(ping,ping_info,x_resolution,y_resolution,unit_type);
6534 if (logging != MagickFalse)
6535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6536 " Setting up pHYs chunk");
6539 #if defined(PNG_oFFs_SUPPORTED)
6540 if (image->page.x || image->page.y)
6542 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
6543 (png_int_32) image->page.y, 0);
6544 if (logging != MagickFalse)
6545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6546 " Setting up oFFs chunk");
6549 if (image_matte && (!mng_info->adjoin || !mng_info->equal_backgrounds))
6554 if (image_depth < MAGICKCORE_QUANTUM_DEPTH)
6559 maxval=(1UL << image_depth)-1;
6560 background.red=(png_uint_16)
6561 (QuantumScale*(maxval*image->background_color.red));
6562 background.green=(png_uint_16)
6563 (QuantumScale*(maxval*image->background_color.green));
6564 background.blue=(png_uint_16)
6565 (QuantumScale*(maxval*image->background_color.blue));
6566 background.gray=(png_uint_16)
6567 (QuantumScale*(maxval*PixelIntensity(&image->background_color)));
6571 background.red=image->background_color.red;
6572 background.green=image->background_color.green;
6573 background.blue=image->background_color.blue;
6575 (png_uint_16) PixelIntensity(&image->background_color);
6577 background.index=(png_byte) background.gray;
6578 if (logging != MagickFalse)
6579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6580 " Setting up bKGd chunk");
6581 png_set_bKGD(ping,ping_info,&background);
6584 Select the color type.
6588 if (mng_info->write_png8)
6590 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6592 image_depth=ping_bit_depth;
6594 /* TO DO: make this a function cause it's used twice, except
6595 for reducing the sample depth from 8. */
6604 number_colors=image_colors;
6605 if ((image->storage_class == DirectClass) || (number_colors > 256))
6607 GetQuantizeInfo(&quantize_info);
6608 quantize_info.dither=IsPaletteImage(image,&image->exception) ==
6609 MagickFalse ? MagickTrue : MagickFalse;
6610 quantize_info.number_colors= (matte != MagickFalse ? 255UL :
6612 (void) QuantizeImage(&quantize_info,image);
6613 number_colors=image_colors;
6614 (void) SyncImage(image);
6615 if (logging != MagickFalse)
6616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6617 " Colors quantized to %ld",number_colors);
6620 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
6624 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6625 #if defined(PNG_SORT_PALETTE)
6626 save_number_colors=image_colors;
6627 if (CompressColormapTransFirst(image) == MagickFalse)
6628 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6629 number_colors=image->colors;
6630 image_colors=save_number_colors;
6632 palette=(png_color *) AcquireQuantumMemory(257,
6634 if (palette == (png_color *) NULL)
6635 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6636 if (logging != MagickFalse)
6637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6638 " Setting up PLTE chunk with %d colors",
6639 (int) number_colors);
6640 for (i=0; i < (long) number_colors; i++)
6642 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6643 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6644 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6645 if (logging != MagickFalse)
6646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6647 #if MAGICKCORE_QUANTUM_DEPTH == 8
6648 " %3ld (%3d,%3d,%3d)",
6650 " %5ld (%5d,%5d,%5d)",
6652 i,palette[i].red,palette[i].green,palette[i].blue);
6658 palette[i].red=ScaleQuantumToChar((Quantum) QuantumRange);
6659 palette[i].green=ScaleQuantumToChar((Quantum) QuantumRange);
6660 palette[i].blue=ScaleQuantumToChar((Quantum) QuantumRange);
6662 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6663 palette=(png_colorp) RelinquishMagickMemory(palette);
6664 image_depth=ping_bit_depth;
6675 Identify which colormap entry is transparent.
6677 assert(number_colors <= 256);
6678 for (i=0; i < (long) number_colors; i++)
6680 exception=(&image->exception);
6681 for (y=0; y < (long) image->rows; y++)
6683 register const PixelPacket
6686 p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6687 if (p == (PixelPacket *) NULL)
6689 indices=GetAuthenticIndexQueue(image);
6690 for (x=0; x < (long) image->columns; x++)
6692 if (p->opacity != OpaqueOpacity)
6694 indices[x]=(IndexPacket) (number_colors-1);
6695 trans_alpha[(long) indices[x]]=(png_byte) (255-
6696 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
6700 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6703 for (i=0; i < (long) number_colors; i++)
6704 if (trans_alpha[i] != 255)
6705 ping_num_trans=(unsigned short) (i+1);
6707 if (ping_num_trans == 0)
6708 png_set_invalid(ping, ping_info, PNG_INFO_tRNS);
6709 if (!png_get_valid(ping, ping_info, PNG_INFO_tRNS))
6711 if (ping_num_trans != 0)
6713 for (i=0; i<256; i++)
6714 ping_trans_alpha[i]=(png_byte) trans_alpha[i];
6717 (void) png_set_tRNS(ping, ping_info,
6723 Identify which colormap entry is the background color.
6725 for (i=0; i < (long) MagickMax(1L*number_colors-1L,1L); i++)
6726 if (IsPNGColorEqual(ping_background,image->colormap[i]))
6728 ping_background.index=(png_byte) i;
6730 if (image_matte != MagickFalse)
6732 /* TO DO: reduce to binary transparency */
6734 } /* end of write_png8 */
6735 else if (mng_info->write_png24)
6737 image_matte=MagickFalse;
6738 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6740 else if (mng_info->write_png32)
6742 image_matte=MagickTrue;
6743 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6747 image_depth=ping_bit_depth;
6748 if (mng_info->write_png_colortype)
6750 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
6751 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6752 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6753 image_matte=MagickTrue;
6757 if (logging != MagickFalse)
6758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6759 "Selecting PNG colortype");
6760 ping_color_type=(png_byte) ((matte == MagickTrue)?
6761 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6762 if(image_info->type == TrueColorType)
6764 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6765 image_matte=MagickFalse;
6767 if(image_info->type == TrueColorMatteType)
6769 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6770 image_matte=MagickTrue;
6772 if ((image_info->type == UndefinedType ||
6773 image_info->type == OptimizeType ||
6774 image_info->type == GrayscaleType) &&
6775 image_matte == MagickFalse && ImageIsGray(image))
6777 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
6778 image_matte=MagickFalse;
6780 if ((image_info->type == UndefinedType ||
6781 image_info->type == OptimizeType ||
6782 image_info->type == GrayscaleMatteType) &&
6783 image_matte == MagickTrue && ImageIsGray(image))
6785 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
6786 image_matte=MagickTrue;
6789 if (logging != MagickFalse)
6790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6791 "Selected PNG colortype=%d",ping_color_type);
6793 if (ping_bit_depth < 8)
6795 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6796 ping_color_type == PNG_COLOR_TYPE_RGB ||
6797 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6801 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
6803 if (image->matte == MagickFalse && image->colors < 256)
6805 if (ImageIsMonochrome(image))
6808 if (ping_bit_depth < (int)mng_info->write_png_depth)
6809 ping_bit_depth = mng_info->write_png_depth;
6813 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
6816 while ((int) (1 << ping_bit_depth) < (long) image_colors)
6817 ping_bit_depth <<= 1;
6819 if (logging != MagickFalse)
6821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6822 " Number of colors: %lu",image_colors);
6823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6824 " Tentative PNG bit depth: %d",ping_bit_depth);
6826 if (mng_info->write_png_depth)
6828 old_bit_depth=ping_bit_depth;
6829 if (ping_bit_depth < (int)mng_info->write_png_depth)
6831 ping_bit_depth = mng_info->write_png_depth;
6832 if (ping_bit_depth > 8)
6834 if (ping_bit_depth != (int) old_bit_depth)
6836 if (logging != MagickFalse)
6837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6838 " Colors increased to %ld",image_colors);
6844 image_depth=ping_bit_depth;
6845 if (logging != MagickFalse)
6847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6848 " Tentative PNG color type: %d",ping_color_type);
6849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6850 " image_info->type: %d",image_info->type);
6851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6852 " image_depth: %lu",image_depth);
6853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6854 " ping_bit_depth: %d",ping_bit_depth);
6857 if (matte && (mng_info->optimize || mng_info->IsPalette))
6859 register const PixelPacket
6862 p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
6863 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
6864 for (y=0; y < (long) image->rows; y++)
6866 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6867 if (p == (const PixelPacket *) NULL)
6869 for (x=(long) image->columns-1; x >= 0; x--)
6871 if (IsGray(p) == MagickFalse)
6873 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6880 Determine if there is any transparent color.
6882 for (y=0; y < (long) image->rows; y++)
6884 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
6885 if (p == (const PixelPacket *) NULL)
6887 for (x=(long) image->columns-1; x >= 0; x--)
6889 if (p->opacity != OpaqueOpacity)
6896 if ((y == (long) image->rows) && (x == (long) image->columns))
6899 No transparent pixels are present. Change 4 or 6 to 0 or 2.
6901 image_matte=MagickFalse;
6902 ping_color_type&=0x03;
6910 if (ping_bit_depth == 8)
6912 if (ping_bit_depth == 4)
6914 if (ping_bit_depth == 2)
6916 if (ping_bit_depth == 1)
6918 ping_trans_color.red=(png_uint_16)
6919 (ScaleQuantumToShort(GetRedPixelComponent(p)) & mask);
6920 ping_trans_color.green=(png_uint_16)
6921 (ScaleQuantumToShort(GetGreenPixelComponent(p)) & mask);
6922 ping_trans_color.blue=(png_uint_16)
6923 (ScaleQuantumToShort(GetBluePixelComponent(p)) & mask);
6924 ping_trans_color.gray=(png_uint_16)
6925 (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
6926 ping_trans_color.index=(png_byte)
6927 (ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p))));
6928 (void) png_set_tRNS(ping, ping_info, NULL, 0,
6931 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
6934 Determine if there is one and only one transparent color
6935 and if so if it is fully transparent.
6937 for (y=0; y < (long) image->rows; y++)
6939 p=GetVirtualPixels(image,0,y,image->columns,1,
6942 if (p == (const PixelPacket *) NULL)
6944 for (x=(long) image->columns-1; x >= 0; x--)
6946 if (p->opacity != OpaqueOpacity)
6948 if (IsPNGColorEqual(ping_trans_color,*p) == 0)
6950 break; /* Can't use RGB + tRNS for multiple
6951 transparent colors. */
6953 if (p->opacity != (Quantum) TransparentOpacity)
6955 break; /* Can't use RGB + tRNS for
6956 semitransparency. */
6961 if (IsPNGColorEqual(ping_trans_color,*p))
6962 break; /* Can't use RGB + tRNS when another pixel
6963 having the same RGB samples is
6972 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
6974 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
6976 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
6977 if (image_depth == 8)
6979 ping_trans_color.red&=0xff;
6980 ping_trans_color.green&=0xff;
6981 ping_trans_color.blue&=0xff;
6982 ping_trans_color.gray&=0xff;
6987 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
6988 image_matte=MagickFalse;
6989 if ((mng_info->optimize || mng_info->IsPalette) &&
6990 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
6991 ImageIsGray(image) && (!image_matte || image_depth >= 8))
6993 if (image_matte != MagickFalse)
6994 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
6997 ping_color_type=PNG_COLOR_TYPE_GRAY;
6998 if (save_image_depth == 16 && image_depth == 8)
6999 ping_trans_color.gray*=0x0101;
7001 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
7002 image_depth=MAGICKCORE_QUANTUM_DEPTH;
7003 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
7004 image_colors=1 << image_depth;
7005 if (image_depth > 8)
7010 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7012 if(!mng_info->write_png_depth)
7015 while ((int) (1 << ping_bit_depth)
7016 < (long) image_colors)
7017 ping_bit_depth <<= 1;
7020 else if (mng_info->optimize && ping_color_type ==
7021 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
7022 mng_info->IsPalette)
7025 /* Check if grayscale is reducible */
7027 depth_4_ok=MagickTrue,
7028 depth_2_ok=MagickTrue,
7029 depth_1_ok=MagickTrue;
7031 for (i=0; i < (long) image_colors; i++)
7036 intensity=ScaleQuantumToChar(image->colormap[i].red);
7038 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
7039 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
7040 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
7041 depth_2_ok=depth_1_ok=MagickFalse;
7042 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
7043 depth_1_ok=MagickFalse;
7045 if (depth_1_ok && mng_info->write_png_depth <= 1)
7047 else if (depth_2_ok && mng_info->write_png_depth <= 2)
7049 else if (depth_4_ok && mng_info->write_png_depth <= 4)
7053 image_depth=ping_bit_depth;
7056 if (mng_info->IsPalette)
7058 if (image_depth <= 8)
7063 number_colors=image_colors;
7067 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
7068 if (mng_info->have_write_global_plte && !matte)
7070 png_set_PLTE(ping,ping_info,NULL,0);
7072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7073 " Setting up empty PLTE chunk");
7077 #if defined(PNG_SORT_PALETTE)
7081 if (mng_info->optimize)
7083 save_number_colors=image_colors;
7084 if (CompressColormapTransFirst(image) == MagickFalse)
7085 ThrowWriterException(ResourceLimitError,
7086 "MemoryAllocationFailed");
7087 number_colors=image->colors;
7088 image_colors=save_number_colors;
7091 palette=(png_color *) AcquireQuantumMemory(257,
7093 if (palette == (png_color *) NULL)
7094 ThrowWriterException(ResourceLimitError,
7095 "MemoryAllocationFailed");
7096 for (i=0; i < (long) number_colors; i++)
7098 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7099 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7100 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7104 " Setting up PLTE chunk with %d colors",
7105 (int) number_colors);
7106 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
7107 palette=(png_colorp) RelinquishMagickMemory(palette);
7109 /* color_type is PNG_COLOR_TYPE_PALETTE */
7110 if (!mng_info->write_png_depth)
7113 while ((1UL << ping_bit_depth) < number_colors)
7114 ping_bit_depth <<= 1;
7122 register const PixelPacket
7128 register const IndexPacket
7132 Identify which colormap entry is transparent.
7134 assert(number_colors <= 256);
7135 for (i=0; i < (long) number_colors; i++)
7137 exception=(&image->exception);
7138 for (y=0; y < (long) image->rows; y++)
7140 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7141 if (p == (const PixelPacket *) NULL)
7143 packet_indices=GetVirtualIndexQueue(image);
7144 for (x=0; x < (long) image->columns; x++)
7146 if (p->opacity != OpaqueOpacity)
7151 packet_index=packet_indices[x];
7152 assert((unsigned long) packet_index < number_colors);
7153 if (trans[(long) packet_index] != 256)
7155 if (trans[(long) packet_index] != (png_byte) (255-
7156 ScaleQuantumToChar(GetOpacityPixelComponent(p))))
7158 ping_color_type=(png_byte)
7159 PNG_COLOR_TYPE_RGB_ALPHA;
7163 trans[(long) packet_index]=(png_byte) (255-
7164 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
7168 if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
7171 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7172 png_set_invalid(ping,ping_info,PNG_INFO_PLTE);
7173 mng_info->IsPalette=MagickFalse;
7174 (void) SyncImage(image);
7176 (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7177 " Cannot write image as indexed PNG, writing RGBA.");
7181 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7183 for (i=0; i < (long) number_colors; i++)
7185 if (trans[i] == 256)
7187 if (trans[i] != 255)
7188 ping_num_trans=(unsigned short) (i+1);
7191 if (ping_num_trans == 0)
7192 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7193 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7195 if (ping_num_trans != 0)
7197 ping_trans_alpha=(unsigned char *) AcquireQuantumMemory(
7198 number_colors,sizeof(*ping_trans_alpha));
7199 if (ping_trans_alpha == (unsigned char *) NULL)
7200 ThrowWriterException(ResourceLimitError,
7201 "MemoryAllocationFailed");
7202 for (i=0; i < (long) number_colors; i++)
7203 ping_trans_alpha[i]=(png_byte) trans[i];
7208 Identify which colormap entry is the background color.
7210 for (i=0; i < (long) MagickMax(1L*number_colors-1L,1L); i++)
7211 if (IsPNGColorEqual(ping_background,image->colormap[i]))
7213 ping_background.index=(png_byte) i;
7218 if (image_depth < 8)
7220 if ((save_image_depth == 16) && (image_depth == 8))
7222 ping_trans_color.red*=0x0101;
7223 ping_trans_color.green*=0x0101;
7224 ping_trans_color.blue*=0x0101;
7225 ping_trans_color.gray*=0x0101;
7230 Adjust background and transparency samples in sub-8-bit grayscale files.
7232 if (ping_bit_depth < 8 && ping_color_type ==
7233 PNG_COLOR_TYPE_GRAY)
7241 maxval=(png_uint_16) ((1 << ping_bit_depth)-1);
7244 background.gray=(png_uint_16)
7245 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7247 if (logging != MagickFalse)
7248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7249 " Setting up bKGD chunk");
7250 png_set_bKGD(ping,ping_info,&background);
7252 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7253 ping_trans_color.gray));
7255 if (logging != MagickFalse)
7256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7257 " PNG color type: %d",ping_color_type);
7259 Initialize compression level and filtering.
7261 if (logging != MagickFalse)
7262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7263 " Setting up deflate compression");
7264 if (logging != MagickFalse)
7265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7266 " Compression buffer size: 32768");
7267 png_set_compression_buffer_size(ping,32768L);
7268 if (logging != MagickFalse)
7269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7270 " Compression mem level: 9");
7271 png_set_compression_mem_level(ping, 9);
7272 quality=image->quality == UndefinedCompressionQuality ? 75UL :
7279 level=(int) MagickMin((long) quality/10,9);
7280 if (logging != MagickFalse)
7281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7282 " Compression level: %d",level);
7283 png_set_compression_level(ping,level);
7287 if (logging != MagickFalse)
7288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7289 " Compression strategy: Z_HUFFMAN_ONLY");
7290 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7292 if (logging != MagickFalse)
7293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7294 " Setting up filtering");
7295 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7297 /* This became available in libpng-1.0.9. Output must be a MNG. */
7298 if (mng_info->write_mng && ((quality % 10) == 7))
7300 if (logging != MagickFalse)
7301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7302 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
7303 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
7306 if (logging != MagickFalse)
7307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7314 if ((quality % 10) > 5)
7315 base_filter=PNG_ALL_FILTERS;
7317 if ((quality % 10) != 5)
7318 base_filter=(int) quality % 10;
7320 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
7321 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
7323 base_filter=PNG_NO_FILTERS;
7325 base_filter=PNG_ALL_FILTERS;
7326 if (logging != MagickFalse)
7328 if (base_filter == PNG_ALL_FILTERS)
7329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7330 " Base filter method: ADAPTIVE");
7332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7333 " Base filter method: NONE");
7335 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7338 ResetImageProfileIterator(image);
7339 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7341 profile=GetImageProfile(image,name);
7342 if (profile != (StringInfo *) NULL)
7344 #ifdef PNG_WRITE_iCCP_SUPPORTED
7345 if ((LocaleCompare(name,"ICC") == 0) ||
7346 (LocaleCompare(name,"ICM") == 0))
7347 png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7348 GetStringInfoDatum(profile),
7349 (png_uint_32) GetStringInfoLength(profile));
7352 png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7353 name,(unsigned char *) name,GetStringInfoDatum(profile),
7354 (png_uint_32) GetStringInfoLength(profile));
7356 if (logging != MagickFalse)
7357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7358 " Setting up text chunk with %s profile",name);
7359 name=GetNextImageProfile(image);
7362 #if defined(PNG_WRITE_sRGB_SUPPORTED)
7363 if ((mng_info->have_write_global_srgb == 0) &&
7364 ((image->rendering_intent != UndefinedIntent) ||
7365 (image->colorspace == sRGBColorspace)))
7368 Note image rendering intent.
7370 if (logging != MagickFalse)
7371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7372 " Setting up sRGB chunk");
7373 (void) png_set_sRGB(ping,ping_info,(int) (image->rendering_intent-1));
7374 png_set_gAMA(ping,ping_info,0.45455);
7376 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
7379 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7383 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7385 if (logging != MagickFalse)
7386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7387 " Setting up gAMA chunk");
7388 png_set_gAMA(ping,ping_info,image->gamma);
7390 if ((mng_info->have_write_global_chrm == 0) &&
7391 (image->chromaticity.red_primary.x != 0.0))
7394 Note image chromaticity.
7395 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7403 wp=image->chromaticity.white_point;
7404 rp=image->chromaticity.red_primary;
7405 gp=image->chromaticity.green_primary;
7406 bp=image->chromaticity.blue_primary;
7408 if (logging != MagickFalse)
7409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7410 " Setting up cHRM chunk");
7411 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7415 ping_interlace_method=image_info->interlace != NoInterlace;
7417 if (mng_info->write_mng)
7418 png_set_sig_bytes(ping,8);
7420 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7422 if (mng_info->write_png_colortype)
7424 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7425 if (ImageIsGray(image) == MagickFalse)
7427 ping_color_type = PNG_COLOR_TYPE_RGB;
7428 if (ping_bit_depth < 8)
7432 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7433 if (ImageIsGray(image) == MagickFalse)
7434 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
7437 if ((mng_info->write_png_depth &&
7438 (int) mng_info->write_png_depth != ping_bit_depth) ||
7439 (mng_info->write_png_colortype &&
7440 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
7441 mng_info->write_png_colortype != 7 &&
7442 !(mng_info->write_png_colortype == 5 && ping_color_type == 0))))
7444 if (logging != MagickFalse)
7446 if (mng_info->write_png_depth)
7448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7449 " Defined PNG:bit-depth=%u, Computed depth=%u",
7450 mng_info->write_png_depth,
7453 if (mng_info->write_png_colortype)
7455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7456 " Defined PNG:color-type=%u, Computed color type=%u",
7457 mng_info->write_png_colortype-1,
7462 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7465 if (image_matte && !image->matte)
7467 /* Add an opaque matte channel */
7468 image->matte = MagickTrue;
7469 (void) SetImageOpacity(image,0);
7472 if (logging != MagickFalse)
7473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7474 " Writing PNG header chunks");
7476 png_set_IHDR(ping,ping_info,ping_width,ping_height,
7477 ping_bit_depth,ping_color_type,
7478 ping_interlace_method,ping_compression_method,
7479 ping_filter_method);
7481 png_write_info_before_PLTE(ping, ping_info);
7482 /* write any png-chunk-b profiles */
7483 (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7484 png_write_info(ping,ping_info);
7485 /* write any PNG-chunk-m profiles */
7486 (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7488 if (image->page.width || image->page.height)
7493 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
7494 PNGType(chunk,mng_vpAg);
7495 LogPNGChunk((int) logging,mng_vpAg,9L);
7496 PNGLong(chunk+4,(png_uint_32) image->page.width);
7497 PNGLong(chunk+8,(png_uint_32) image->page.height);
7498 chunk[12]=0; /* unit = pixels */
7499 (void) WriteBlob(image,13,chunk);
7500 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
7503 #if (PNG_LIBPNG_VER == 10206)
7504 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
7505 #define PNG_HAVE_IDAT 0x04
7506 ping->mode |= PNG_HAVE_IDAT;
7507 #undef PNG_HAVE_IDAT
7510 png_set_packing(ping);
7514 rowbytes=image->columns;
7515 if (image_depth <= 8)
7517 if (mng_info->write_png24 || (mng_info->write_png_depth == 8 &&
7518 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_RGB))
7520 else if (mng_info->write_png32 || (mng_info->write_png_depth == 8 &&
7521 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_RGB_ALPHA))
7523 else if ((!mng_info->write_png8 ||
7524 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY ||
7525 mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA )&&
7526 ((mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image)))
7527 rowbytes*=(image_matte ? 2 : 1);
7530 if (!mng_info->IsPalette)
7531 rowbytes*=(image_matte ? 4 : 3);
7536 if ((mng_info->optimize || mng_info->IsPalette) &&
7538 rowbytes*=(image_matte ? 4 : 2);
7540 rowbytes*=(image_matte ? 8 : 6);
7543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7544 " Allocating %lu bytes of memory for pixels",rowbytes);
7545 png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7546 sizeof(*png_pixels));
7547 if (png_pixels == (unsigned char *) NULL)
7548 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7550 Initialize image scanlines.
7552 if (setjmp(png_jmpbuf(ping)))
7558 if (image_info->verbose)
7559 (void) printf("PNG write has failed.\n");
7561 png_destroy_write_struct(&ping,&ping_info);
7562 if (quantum_info != (QuantumInfo *) NULL)
7563 quantum_info=DestroyQuantumInfo(quantum_info);
7564 if (png_pixels != (unsigned char *) NULL)
7565 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7566 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7567 UnlockSemaphoreInfo(png_semaphore);
7569 return(MagickFalse);
7571 quantum_info=AcquireQuantumInfo(image_info,image);
7572 if (quantum_info == (QuantumInfo *) NULL)
7573 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7574 quantum_info->format=UndefinedQuantumFormat;
7575 quantum_info->depth=image_depth;
7576 num_passes=png_set_interlace_handling(ping);
7577 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7578 !mng_info->write_png32) &&
7579 (mng_info->optimize || mng_info->IsPalette ||
7580 (image_info->type == BilevelType)) &&
7581 !image_matte && ImageIsMonochrome(image))
7583 register const PixelPacket
7586 quantum_info->depth=8;
7587 for (pass=0; pass < num_passes; pass++)
7590 Convert PseudoClass image to a PNG monochrome image.
7592 for (y=0; y < (long) image->rows; y++)
7594 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7595 if (p == (const PixelPacket *) NULL)
7597 if (mng_info->IsPalette)
7599 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7600 quantum_info,GrayQuantum,png_pixels,&image->exception);
7601 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7602 mng_info->write_png_depth &&
7603 mng_info->write_png_depth != old_bit_depth)
7605 /* Undo pixel scaling */
7606 for (i=0; i < (long) image->columns; i++)
7607 *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7608 >> (8-old_bit_depth));
7613 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7614 quantum_info,RedQuantum,png_pixels,&image->exception);
7616 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
7617 for (i=0; i < (long) image->columns; i++)
7618 *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7620 png_write_row(ping,png_pixels);
7622 if (image->previous == (Image *) NULL)
7624 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7625 if (status == MagickFalse)
7631 for (pass=0; pass < num_passes; pass++)
7633 register const PixelPacket
7636 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7637 !mng_info->write_png32) &&
7639 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
7640 (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7642 for (y=0; y < (long) image->rows; y++)
7644 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7645 if (p == (const PixelPacket *) NULL)
7647 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7649 if (mng_info->IsPalette)
7650 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7651 quantum_info,GrayQuantum,png_pixels,&image->exception);
7653 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7654 quantum_info,RedQuantum,png_pixels,&image->exception);
7656 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7658 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7659 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7661 png_write_row(ping,png_pixels);
7663 if (image->previous == (Image *) NULL)
7665 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7666 if (status == MagickFalse)
7671 for (pass=0; pass < num_passes; pass++)
7673 if ((image_depth > 8) || (mng_info->write_png24 ||
7674 mng_info->write_png32 ||
7675 (!mng_info->write_png8 && !mng_info->IsPalette)))
7676 for (y=0; y < (long) image->rows; y++)
7678 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7679 if (p == (const PixelPacket *) NULL)
7681 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7683 if (image->storage_class == DirectClass)
7684 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7685 quantum_info,RedQuantum,png_pixels,&image->exception);
7687 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7688 quantum_info,GrayQuantum,png_pixels,&image->exception);
7690 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7691 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7692 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7693 else if (image_matte != MagickFalse)
7694 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7695 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7697 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7698 quantum_info,RGBQuantum,png_pixels,&image->exception);
7699 png_write_row(ping,png_pixels);
7702 /* not ((image_depth > 8) || (mng_info->write_png24 ||
7703 mng_info->write_png32 ||
7704 (!mng_info->write_png8 && !mng_info->IsPalette))) */
7706 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
7707 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7712 quantum_info->depth=8;
7715 for (y=0; y < (long) image->rows; y++)
7718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7720 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7721 if (p == (const PixelPacket *) NULL)
7723 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7724 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7725 quantum_info,GrayQuantum,png_pixels,&image->exception);
7726 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7727 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7728 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7730 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7731 quantum_info,IndexQuantum,png_pixels,&image->exception);
7732 png_write_row(ping,png_pixels);
7735 if (image->previous == (Image *) NULL)
7737 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7738 if (status == MagickFalse)
7743 if (quantum_info != (QuantumInfo *) NULL)
7744 quantum_info=DestroyQuantumInfo(quantum_info);
7746 if (logging != MagickFalse)
7748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7749 " Writing PNG image data");
7750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7751 " Width: %lu",(unsigned long) ping_width);
7752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7753 " Height: %lu",(unsigned long) ping_height);
7754 if (mng_info->write_png_depth)
7756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7757 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7760 " PNG bit-depth written: %d",ping_bit_depth);
7761 if (mng_info->write_png_colortype)
7763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7764 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7767 " PNG color-type written: %d",ping_color_type);
7768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7769 " PNG Interlace method: %d",ping_interlace_method);
7772 Generate text chunks.
7774 ResetImagePropertyIterator(image);
7775 property=GetNextImageProperty(image);
7776 while (property != (const char *) NULL)
7781 value=GetImageProperty(image,property);
7782 if (value != (const char *) NULL)
7784 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7785 text[0].key=(char *) property;
7786 text[0].text=(char *) value;
7787 text[0].text_length=strlen(value);
7788 text[0].compression=image_info->compression == NoCompression ||
7789 (image_info->compression == UndefinedCompression &&
7790 text[0].text_length < 128) ? -1 : 0;
7791 if (logging != MagickFalse)
7793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7794 " Setting up text chunk");
7795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7796 " keyword: %s",text[0].key);
7798 png_set_text(ping,ping_info,text,1);
7799 png_free(ping,text);
7801 property=GetNextImageProperty(image);
7804 /* write any PNG-chunk-e profiles */
7805 (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
7807 if (logging != MagickFalse)
7808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7809 " Writing PNG end info");
7810 png_write_end(ping,ping_info);
7811 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
7813 if (mng_info->page.x || mng_info->page.y ||
7814 (ping_width != mng_info->page.width) ||
7815 (ping_height != mng_info->page.height))
7821 Write FRAM 4 with clipping boundaries followed by FRAM 1.
7823 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
7824 PNGType(chunk,mng_FRAM);
7825 LogPNGChunk((int) logging,mng_FRAM,27L);
7827 chunk[5]=0; /* frame name separator (no name) */
7828 chunk[6]=1; /* flag for changing delay, for next frame only */
7829 chunk[7]=0; /* flag for changing frame timeout */
7830 chunk[8]=1; /* flag for changing frame clipping for next frame */
7831 chunk[9]=0; /* flag for changing frame sync_id */
7832 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
7833 chunk[14]=0; /* clipping boundaries delta type */
7834 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
7836 (png_uint_32) (mng_info->page.x + ping_width));
7837 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
7839 (png_uint_32) (mng_info->page.y + ping_height));
7840 (void) WriteBlob(image,31,chunk);
7841 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
7842 mng_info->old_framing_mode=4;
7843 mng_info->framing_mode=1;
7846 mng_info->framing_mode=3;
7848 if (mng_info->write_mng && !mng_info->need_fram &&
7849 ((int) image->dispose == 3))
7850 (void) ThrowMagickException(&image->exception,GetMagickModule(),
7851 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
7852 "`%s'",image->filename);
7853 image_depth=save_image_depth;
7855 /* Save depth actually written */
7857 s[0]=(char) ping_bit_depth;
7860 (void) SetImageProperty(image,"png:bit-depth-written",s);
7866 png_destroy_write_struct(&ping,&ping_info);
7868 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7870 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7871 UnlockSemaphoreInfo(png_semaphore);
7874 if (logging != MagickFalse)
7875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7876 " exit WriteOnePNGImage()");
7878 /* End write one PNG image */
7882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7886 % W r i t e P N G I m a g e %
7890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7892 % WritePNGImage() writes a Portable Network Graphics (PNG) or
7893 % Multiple-image Network Graphics (MNG) image file.
7895 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7897 % The format of the WritePNGImage method is:
7899 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
7901 % A description of each parameter follows:
7903 % o image_info: the image info.
7905 % o image: The image.
7907 % Returns MagickTrue on success, MagickFalse on failure.
7909 % Communicating with the PNG encoder:
7911 % While the datastream written is always in PNG format and normally would
7912 % be given the "png" file extension, this method also writes the following
7913 % pseudo-formats which are subsets of PNG:
7915 % o PNG8: An 8-bit indexed PNG datastream is written. If transparency
7916 % is present, the tRNS chunk must only have values 0 and 255
7917 % (i.e., transparency is binary: fully opaque or fully
7918 % transparent). The pixels contain 8-bit indices even if
7919 % they could be represented with 1, 2, or 4 bits. Note: grayscale
7920 % images will be written as indexed PNG files even though the
7921 % PNG grayscale type might be slightly more efficient.
7923 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
7924 % chunk can be present to convey binary transparency by naming
7925 % one of the colors as transparent.
7927 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
7928 % transparency is permitted, i.e., the alpha sample for
7929 % each pixel can have any value from 0 to 255. The alpha
7930 % channel is present even if the image is fully opaque.
7932 % o -define: For more precise control of the PNG output, you can use the
7933 % Image options "png:bit-depth" and "png:color-type". These
7934 % can be set from the commandline with "-define" and also
7935 % from the application programming interfaces.
7937 % png:color-type can be 0, 2, 3, 4, or 6.
7939 % When png:color-type is 0 (Grayscale), png:bit-depth can
7940 % be 1, 2, 4, 8, or 16.
7942 % When png:color-type is 2 (RGB), png:bit-depth can
7945 % When png:color-type is 3 (Indexed), png:bit-depth can
7946 % be 1, 2, 4, or 8. This refers to the number of bits
7947 % used to store the index. The color samples always have
7948 % bit-depth 8 in indexed PNG files.
7950 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
7951 % png:bit-depth can be 8 or 16.
7953 % If the image cannot be written without loss in the requested PNG8, PNG24,
7954 % or PNG32 format or with the requested bit-depth and color-type without loss,
7955 % a PNG file will not be written, and the encoder will return MagickFalse.
7956 % Since image encoders should not be responsible for the "heavy lifting",
7957 % the user should make sure that ImageMagick has already reduced the
7958 % image depth and number of colors and limit transparency to binary
7959 % transparency prior to attempting to write the image in a format that
7960 % is subject to depth, color, or transparency limitations.
7962 % TODO: Enforce the previous paragraph.
7964 % TODO: Allow all other PNG subformats to be requested via new
7965 % "-define png:bit-depth -define png:color-type" options.
7967 % Note that another definition, "png:bit-depth-written" exists, but it
7968 % is not intended for external use. It is only used internally by the
7969 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
7971 % It is possible to request that the PNG encoder write previously-formatted
7972 % ancillary chunks in the output PNG file, using the "-profile" commandline
7973 % option as shown below or by setting the profile via a programming
7976 % -profile PNG-chunk-x:<file>
7978 % where x is a location flag and <file> is a file containing the chunk
7979 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
7981 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
7982 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
7983 % of the same type, then add a short unique string after the "x" to prevent
7984 % subsequent profiles from overwriting the preceding ones:
7986 % -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
7988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7990 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
8011 assert(image_info != (const ImageInfo *) NULL);
8012 assert(image_info->signature == MagickSignature);
8013 assert(image != (Image *) NULL);
8014 assert(image->signature == MagickSignature);
8015 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8016 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
8017 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8018 if (status == MagickFalse)
8019 return(MagickFalse);
8021 Allocate a MngInfo structure.
8023 have_mng_structure=MagickFalse;
8024 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8025 if (mng_info == (MngInfo *) NULL)
8026 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8028 Initialize members of the MngInfo structure.
8030 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8031 mng_info->image=image;
8032 have_mng_structure=MagickTrue;
8034 /* See if user has requested a specific PNG subformat */
8036 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8037 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8038 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8040 if (mng_info->write_png8)
8042 mng_info->write_png_colortype = /* 3 */ 4;
8043 mng_info->write_png_depth = 8;
8045 #if 0 /* this does not work */
8046 if (image->matte == MagickTrue)
8047 (void) SetImageType(image,PaletteMatteType);
8049 (void) SetImageType(image,PaletteType);
8050 (void) SyncImage(image);
8054 if (mng_info->write_png24)
8056 mng_info->write_png_colortype = /* 2 */ 3;
8057 mng_info->write_png_depth = 8;
8059 if (image->matte == MagickTrue)
8060 (void) SetImageType(image,TrueColorMatteType);
8062 (void) SetImageType(image,TrueColorType);
8063 (void) SyncImage(image);
8066 if (mng_info->write_png32)
8068 mng_info->write_png_colortype = /* 6 */ 7;
8069 mng_info->write_png_depth = 8;
8071 if (image->matte == MagickTrue)
8072 (void) SetImageType(image,TrueColorMatteType);
8074 (void) SetImageType(image,TrueColorType);
8075 (void) SyncImage(image);
8078 value=GetImageOption(image_info,"png:bit-depth");
8079 if (value != (char *) NULL)
8081 if (LocaleCompare(value,"1") == 0)
8082 mng_info->write_png_depth = 1;
8083 else if (LocaleCompare(value,"2") == 0)
8084 mng_info->write_png_depth = 2;
8085 else if (LocaleCompare(value,"4") == 0)
8086 mng_info->write_png_depth = 4;
8087 else if (LocaleCompare(value,"8") == 0)
8088 mng_info->write_png_depth = 8;
8089 else if (LocaleCompare(value,"16") == 0)
8090 mng_info->write_png_depth = 16;
8091 if (logging != MagickFalse)
8092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8093 "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
8095 value=GetImageOption(image_info,"png:color-type");
8096 if (value != (char *) NULL)
8098 /* We must store colortype+1 because 0 is a valid colortype */
8099 if (LocaleCompare(value,"0") == 0)
8100 mng_info->write_png_colortype = 1;
8101 else if (LocaleCompare(value,"2") == 0)
8102 mng_info->write_png_colortype = 3;
8103 else if (LocaleCompare(value,"3") == 0)
8104 mng_info->write_png_colortype = 4;
8105 else if (LocaleCompare(value,"4") == 0)
8106 mng_info->write_png_colortype = 5;
8107 else if (LocaleCompare(value,"6") == 0)
8108 mng_info->write_png_colortype = 7;
8109 if (logging != MagickFalse)
8110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8111 "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
8114 status=WriteOnePNGImage(mng_info,image_info,image);
8116 (void) CloseBlob(image);
8118 MngInfoFreeStruct(mng_info,&have_mng_structure);
8119 if (logging != MagickFalse)
8120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8124 #if defined(JNG_SUPPORTED)
8126 /* Write one JNG image */
8127 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8128 const ImageInfo *image_info,Image *image)
8148 jng_alpha_compression_method,
8149 jng_alpha_sample_depth,
8157 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8158 " enter WriteOneJNGImage()");
8160 blob=(unsigned char *) NULL;
8161 jpeg_image=(Image *) NULL;
8162 jpeg_image_info=(ImageInfo *) NULL;
8165 transparent=image_info->type==GrayscaleMatteType ||
8166 image_info->type==TrueColorMatteType;
8168 jng_alpha_sample_depth=0;
8169 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8170 jng_alpha_compression_method=0;
8172 if (image->matte != MagickFalse)
8174 /* if any pixels are transparent */
8175 transparent=MagickTrue;
8176 if (image_info->compression==JPEGCompression)
8177 jng_alpha_compression_method=8;
8183 /* Create JPEG blob, image, and image_info */
8184 if (logging != MagickFalse)
8185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8186 " Creating jpeg_image_info for opacity.");
8187 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8188 if (jpeg_image_info == (ImageInfo *) NULL)
8189 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8190 if (logging != MagickFalse)
8191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " Creating jpeg_image.");
8193 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8194 if (jpeg_image == (Image *) NULL)
8195 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8196 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8197 status=SeparateImageChannel(jpeg_image,OpacityChannel);
8198 status=NegateImage(jpeg_image,MagickFalse);
8199 jpeg_image->matte=MagickFalse;
8200 if (jng_quality >= 1000)
8201 jpeg_image_info->quality=jng_quality/1000;
8203 jpeg_image_info->quality=jng_quality;
8204 jpeg_image_info->type=GrayscaleType;
8205 (void) SetImageType(jpeg_image,GrayscaleType);
8206 (void) AcquireUniqueFilename(jpeg_image->filename);
8207 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8208 "%s",jpeg_image->filename);
8211 /* To do: check bit depth of PNG alpha channel */
8213 /* Check if image is grayscale. */
8214 if (image_info->type != TrueColorMatteType && image_info->type !=
8215 TrueColorType && ImageIsGray(image))
8220 if (jng_alpha_compression_method==0)
8225 /* Encode opacity as a grayscale PNG blob */
8226 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8228 if (logging != MagickFalse)
8229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8230 " Creating PNG blob.");
8233 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8234 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8235 jpeg_image_info->interlace=NoInterlace;
8237 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8240 /* Retrieve sample depth used */
8241 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8242 if (value != (char *) NULL)
8243 jng_alpha_sample_depth= (unsigned int) value[0];
8247 /* Encode opacity as a grayscale JPEG blob */
8249 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8252 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8253 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8254 jpeg_image_info->interlace=NoInterlace;
8255 if (logging != MagickFalse)
8256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8258 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8260 jng_alpha_sample_depth=8;
8261 if (logging != MagickFalse)
8262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8263 " Successfully read jpeg_image into a blob, length=%lu.",
8264 (unsigned long) length);
8267 /* Destroy JPEG image and image_info */
8268 jpeg_image=DestroyImage(jpeg_image);
8269 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8270 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8273 /* Write JHDR chunk */
8274 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
8275 PNGType(chunk,mng_JHDR);
8276 LogPNGChunk((int) logging,mng_JHDR,16L);
8277 PNGLong(chunk+4,image->columns);
8278 PNGLong(chunk+8,image->rows);
8279 chunk[12]=jng_color_type;
8280 chunk[13]=8; /* sample depth */
8281 chunk[14]=8; /*jng_image_compression_method */
8282 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8283 chunk[16]=jng_alpha_sample_depth;
8284 chunk[17]=jng_alpha_compression_method;
8285 chunk[18]=0; /*jng_alpha_filter_method */
8286 chunk[19]=0; /*jng_alpha_interlace_method */
8287 (void) WriteBlob(image,20,chunk);
8288 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8289 if (logging != MagickFalse)
8291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8292 " JNG width:%15lu",image->columns);
8293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8294 " JNG height:%14lu",image->rows);
8295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8296 " JNG color type:%10d",jng_color_type);
8297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8298 " JNG sample depth:%8d",8);
8299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8300 " JNG compression:%9d",8);
8301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8302 " JNG interlace:%11d",0);
8303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8304 " JNG alpha depth:%9d",jng_alpha_sample_depth);
8305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8306 " JNG alpha compression:%3d",jng_alpha_compression_method);
8307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8308 " JNG alpha filter:%8d",0);
8309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8310 " JNG alpha interlace:%5d",0);
8313 /* Write any JNG-chunk-b profiles */
8314 (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8317 Write leading ancillary chunks
8323 Write JNG bKGD chunk
8334 if (jng_color_type == 8 || jng_color_type == 12)
8338 (void) WriteBlobMSBULong(image,(unsigned long) (num_bytes-4L));
8339 PNGType(chunk,mng_bKGD);
8340 LogPNGChunk((int) logging,mng_bKGD,(unsigned long) (num_bytes-4L));
8341 red=ScaleQuantumToChar(image->background_color.red);
8342 green=ScaleQuantumToChar(image->background_color.green);
8343 blue=ScaleQuantumToChar(image->background_color.blue);
8350 (void) WriteBlob(image,(size_t) num_bytes,chunk);
8351 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8354 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8357 Write JNG sRGB chunk
8359 (void) WriteBlobMSBULong(image,1L);
8360 PNGType(chunk,mng_sRGB);
8361 LogPNGChunk((int) logging,mng_sRGB,1L);
8362 if (image->rendering_intent != UndefinedIntent)
8363 chunk[4]=(unsigned char) (image->rendering_intent-1);
8365 chunk[4]=(unsigned char) (PerceptualIntent-1);
8366 (void) WriteBlob(image,5,chunk);
8367 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8371 if (image->gamma != 0.0)
8374 Write JNG gAMA chunk
8376 (void) WriteBlobMSBULong(image,4L);
8377 PNGType(chunk,mng_gAMA);
8378 LogPNGChunk((int) logging,mng_gAMA,4L);
8379 PNGLong(chunk+4,(unsigned long) (100000*image->gamma+0.5));
8380 (void) WriteBlob(image,8,chunk);
8381 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8383 if ((mng_info->equal_chrms == MagickFalse) &&
8384 (image->chromaticity.red_primary.x != 0.0))
8390 Write JNG cHRM chunk
8392 (void) WriteBlobMSBULong(image,32L);
8393 PNGType(chunk,mng_cHRM);
8394 LogPNGChunk((int) logging,mng_cHRM,32L);
8395 primary=image->chromaticity.white_point;
8396 PNGLong(chunk+4,(unsigned long) (100000*primary.x+0.5));
8397 PNGLong(chunk+8,(unsigned long) (100000*primary.y+0.5));
8398 primary=image->chromaticity.red_primary;
8399 PNGLong(chunk+12,(unsigned long) (100000*primary.x+0.5));
8400 PNGLong(chunk+16,(unsigned long) (100000*primary.y+0.5));
8401 primary=image->chromaticity.green_primary;
8402 PNGLong(chunk+20,(unsigned long) (100000*primary.x+0.5));
8403 PNGLong(chunk+24,(unsigned long) (100000*primary.y+0.5));
8404 primary=image->chromaticity.blue_primary;
8405 PNGLong(chunk+28,(unsigned long) (100000*primary.x+0.5));
8406 PNGLong(chunk+32,(unsigned long) (100000*primary.y+0.5));
8407 (void) WriteBlob(image,36,chunk);
8408 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8411 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8414 Write JNG pHYs chunk
8416 (void) WriteBlobMSBULong(image,9L);
8417 PNGType(chunk,mng_pHYs);
8418 LogPNGChunk((int) logging,mng_pHYs,9L);
8419 if (image->units == PixelsPerInchResolution)
8421 PNGLong(chunk+4,(unsigned long)
8422 (image->x_resolution*100.0/2.54+0.5));
8423 PNGLong(chunk+8,(unsigned long)
8424 (image->y_resolution*100.0/2.54+0.5));
8429 if (image->units == PixelsPerCentimeterResolution)
8431 PNGLong(chunk+4,(unsigned long)
8432 (image->x_resolution*100.0+0.5));
8433 PNGLong(chunk+8,(unsigned long)
8434 (image->y_resolution*100.0+0.5));
8439 PNGLong(chunk+4,(unsigned long) (image->x_resolution+0.5));
8440 PNGLong(chunk+8,(unsigned long) (image->y_resolution+0.5));
8444 (void) WriteBlob(image,13,chunk);
8445 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8448 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8451 Write JNG oFFs chunk
8453 (void) WriteBlobMSBULong(image,9L);
8454 PNGType(chunk,mng_oFFs);
8455 LogPNGChunk((int) logging,mng_oFFs,9L);
8456 PNGsLong(chunk+4,(long) (image->page.x));
8457 PNGsLong(chunk+8,(long) (image->page.y));
8459 (void) WriteBlob(image,13,chunk);
8460 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8462 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8464 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8465 PNGType(chunk,mng_vpAg);
8466 LogPNGChunk((int) logging,mng_vpAg,9L);
8467 PNGLong(chunk+4,(png_uint_32) image->page.width);
8468 PNGLong(chunk+8,(png_uint_32) image->page.height);
8469 chunk[12]=0; /* unit = pixels */
8470 (void) WriteBlob(image,13,chunk);
8471 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8477 if (jng_alpha_compression_method==0)
8485 /* Write IDAT chunk header */
8486 if (logging != MagickFalse)
8487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8488 " Write IDAT chunks from blob, length=%lu.",
8489 (unsigned long) length);
8491 /* Copy IDAT chunks */
8494 for (i=8; i<(long) length; i+=len+12)
8496 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8498 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8500 /* Found an IDAT chunk. */
8501 (void) WriteBlobMSBULong(image,(unsigned long) len);
8502 LogPNGChunk((int) logging,mng_IDAT,(unsigned long) len);
8503 (void) WriteBlob(image,(size_t) len+4,p);
8504 (void) WriteBlobMSBULong(image,
8505 crc32(0,p,(uInt) len+4));
8509 if (logging != MagickFalse)
8510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8511 " Skipping %c%c%c%c chunk, length=%lu.",
8512 *(p),*(p+1),*(p+2),*(p+3),len);
8519 /* Write JDAA chunk header */
8520 if (logging != MagickFalse)
8521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8522 " Write JDAA chunk, length=%lu.",
8523 (unsigned long) length);
8524 (void) WriteBlobMSBULong(image,(unsigned long) length);
8525 PNGType(chunk,mng_JDAA);
8526 LogPNGChunk((int) logging,mng_JDAA,length);
8527 /* Write JDAT chunk(s) data */
8528 (void) WriteBlob(image,4,chunk);
8529 (void) WriteBlob(image,length,blob);
8530 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8533 blob=(unsigned char *) RelinquishMagickMemory(blob);
8536 /* Encode image as a JPEG blob */
8537 if (logging != MagickFalse)
8538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8539 " Creating jpeg_image_info.");
8540 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8541 if (jpeg_image_info == (ImageInfo *) NULL)
8542 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8544 if (logging != MagickFalse)
8545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8546 " Creating jpeg_image.");
8548 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8549 if (jpeg_image == (Image *) NULL)
8550 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8551 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8553 (void) AcquireUniqueFilename(jpeg_image->filename);
8554 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8555 jpeg_image->filename);
8557 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8560 if (logging != MagickFalse)
8561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8562 " Created jpeg_image, %lu x %lu.",jpeg_image->columns,
8565 if (jng_color_type == 8 || jng_color_type == 12)
8566 jpeg_image_info->type=GrayscaleType;
8567 jpeg_image_info->quality=jng_quality % 1000;
8568 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8569 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8570 if (logging != MagickFalse)
8571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8573 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8574 if (logging != MagickFalse)
8576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8577 " Successfully read jpeg_image into a blob, length=%lu.",
8578 (unsigned long) length);
8580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8581 " Write JDAT chunk, length=%lu.",
8582 (unsigned long) length);
8584 /* Write JDAT chunk(s) */
8585 (void) WriteBlobMSBULong(image,(unsigned long) length);
8586 PNGType(chunk,mng_JDAT);
8587 LogPNGChunk((int) logging,mng_JDAT,length);
8588 (void) WriteBlob(image,4,chunk);
8589 (void) WriteBlob(image,length,blob);
8590 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8592 jpeg_image=DestroyImage(jpeg_image);
8593 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8594 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8595 blob=(unsigned char *) RelinquishMagickMemory(blob);
8597 /* Write any JNG-chunk-e profiles */
8598 (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8600 /* Write IEND chunk */
8601 (void) WriteBlobMSBULong(image,0L);
8602 PNGType(chunk,mng_IEND);
8603 LogPNGChunk((int) logging,mng_IEND,0);
8604 (void) WriteBlob(image,4,chunk);
8605 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8607 if (logging != MagickFalse)
8608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8609 " exit WriteOneJNGImage()");
8615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8619 % W r i t e J N G I m a g e %
8623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8625 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8627 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
8629 % The format of the WriteJNGImage method is:
8631 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8633 % A description of each parameter follows:
8635 % o image_info: the image info.
8637 % o image: The image.
8639 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8641 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8658 assert(image_info != (const ImageInfo *) NULL);
8659 assert(image_info->signature == MagickSignature);
8660 assert(image != (Image *) NULL);
8661 assert(image->signature == MagickSignature);
8662 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8663 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8664 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8665 if (status == MagickFalse)
8669 Allocate a MngInfo structure.
8671 have_mng_structure=MagickFalse;
8672 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8673 if (mng_info == (MngInfo *) NULL)
8674 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8676 Initialize members of the MngInfo structure.
8678 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8679 mng_info->image=image;
8680 have_mng_structure=MagickTrue;
8682 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8684 status=WriteOneJNGImage(mng_info,image_info,image);
8685 (void) CloseBlob(image);
8687 (void) CatchImageException(image);
8688 MngInfoFreeStruct(mng_info,&have_mng_structure);
8689 if (logging != MagickFalse)
8690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8697 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8718 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8719 defined(PNG_MNG_FEATURES_SUPPORTED)
8722 all_images_are_gray,
8734 volatile unsigned int
8738 volatile unsigned long
8745 #if (PNG_LIBPNG_VER < 10200)
8746 if (image_info->verbose)
8747 printf("Your PNG library (libpng-%s) is rather old.\n",
8748 PNG_LIBPNG_VER_STRING);
8754 assert(image_info != (const ImageInfo *) NULL);
8755 assert(image_info->signature == MagickSignature);
8756 assert(image != (Image *) NULL);
8757 assert(image->signature == MagickSignature);
8758 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8759 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8760 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8761 if (status == MagickFalse)
8765 Allocate a MngInfo structure.
8767 have_mng_structure=MagickFalse;
8768 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8769 if (mng_info == (MngInfo *) NULL)
8770 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8772 Initialize members of the MngInfo structure.
8774 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8775 mng_info->image=image;
8776 have_mng_structure=MagickTrue;
8777 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
8780 * See if user has requested a specific PNG subformat to be used
8781 * for all of the PNGs in the MNG being written, e.g.,
8783 * convert *.png png8:animation.mng
8785 * To do: check -define png:bit_depth and png:color_type as well,
8786 * or perhaps use mng:bit_depth and mng:color_type instead for
8790 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8791 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8792 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8794 write_jng=MagickFalse;
8795 if (image_info->compression == JPEGCompression)
8796 write_jng=MagickTrue;
8798 mng_info->adjoin=image_info->adjoin &&
8799 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
8801 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8802 optimize=MagickFalse;
8804 optimize=(image_info->type == OptimizeType || image_info->type ==
8807 if (logging != MagickFalse)
8809 /* Log some info about the input */
8813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8814 " Checking input image(s)");
8816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8820 " Optimize: FALSE");
8821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8822 " Image_info depth: %ld",image_info->depth);
8823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8824 " Type: %d",image_info->type);
8827 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8830 " Scene: %ld",scene++);
8831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8832 " Image depth: %lu",p->depth);
8834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8839 if (p->storage_class == PseudoClass)
8840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8841 " Storage class: PseudoClass");
8843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8844 " Storage class: DirectClass");
8846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8847 " Number of colors: %lu",p->colors);
8849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8850 " Number of colors: unspecified");
8851 if (mng_info->adjoin == MagickFalse)
8857 Sometimes we get PseudoClass images whose RGB values don't match
8858 the colors in the colormap. This code syncs the RGB values.
8864 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8866 if (p->taint && p->storage_class == PseudoClass)
8867 (void) SyncImage(p);
8868 if (mng_info->adjoin == MagickFalse)
8873 #ifdef PNG_BUILD_PALETTE
8877 Sometimes we get DirectClass images that have 256 colors or fewer.
8878 This code will convert them to PseudoClass and build a colormap.
8883 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
8885 if (p->storage_class != PseudoClass)
8887 p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
8888 if (p->colors <= 256)
8891 if (p->matte != MagickFalse)
8892 (void) SetImageType(p,PaletteMatteType);
8894 (void) SetImageType(p,PaletteType);
8897 if (mng_info->adjoin == MagickFalse)
8903 use_global_plte=MagickFalse;
8904 all_images_are_gray=MagickFalse;
8905 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8906 need_local_plte=MagickTrue;
8908 need_defi=MagickFalse;
8909 need_matte=MagickFalse;
8910 mng_info->framing_mode=1;
8911 mng_info->old_framing_mode=1;
8914 if (image_info->page != (char *) NULL)
8917 Determine image bounding box.
8919 SetGeometry(image,&mng_info->page);
8920 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
8921 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
8933 mng_info->page=image->page;
8934 need_geom=MagickTrue;
8935 if (mng_info->page.width || mng_info->page.height)
8936 need_geom=MagickFalse;
8938 Check all the scenes.
8940 initial_delay=image->delay;
8941 need_iterations=MagickFalse;
8942 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
8943 mng_info->equal_physs=MagickTrue,
8944 mng_info->equal_gammas=MagickTrue;
8945 mng_info->equal_srgbs=MagickTrue;
8946 mng_info->equal_backgrounds=MagickTrue;
8948 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8949 defined(PNG_MNG_FEATURES_SUPPORTED)
8950 all_images_are_gray=MagickTrue;
8951 mng_info->equal_palettes=MagickFalse;
8952 need_local_plte=MagickFalse;
8954 for (next_image=image; next_image != (Image *) NULL; )
8958 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
8959 mng_info->page.width=next_image->columns+next_image->page.x;
8960 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
8961 mng_info->page.height=next_image->rows+next_image->page.y;
8963 if (next_image->page.x || next_image->page.y)
8964 need_defi=MagickTrue;
8965 if (next_image->matte)
8966 need_matte=MagickTrue;
8967 if ((int) next_image->dispose >= BackgroundDispose)
8968 if (next_image->matte || next_image->page.x || next_image->page.y ||
8969 ((next_image->columns < mng_info->page.width) &&
8970 (next_image->rows < mng_info->page.height)))
8971 mng_info->need_fram=MagickTrue;
8972 if (next_image->iterations)
8973 need_iterations=MagickTrue;
8974 final_delay=next_image->delay;
8975 if (final_delay != initial_delay || final_delay > 1UL*
8976 next_image->ticks_per_second)
8977 mng_info->need_fram=1;
8978 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8979 defined(PNG_MNG_FEATURES_SUPPORTED)
8981 check for global palette possibility.
8983 if (image->matte != MagickFalse)
8984 need_local_plte=MagickTrue;
8985 if (need_local_plte == 0)
8987 if (ImageIsGray(image) == MagickFalse)
8988 all_images_are_gray=MagickFalse;
8989 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
8990 if (use_global_plte == 0)
8991 use_global_plte=mng_info->equal_palettes;
8992 need_local_plte=!mng_info->equal_palettes;
8995 if (GetNextImageInList(next_image) != (Image *) NULL)
8997 if (next_image->background_color.red !=
8998 next_image->next->background_color.red ||
8999 next_image->background_color.green !=
9000 next_image->next->background_color.green ||
9001 next_image->background_color.blue !=
9002 next_image->next->background_color.blue)
9003 mng_info->equal_backgrounds=MagickFalse;
9004 if (next_image->gamma != next_image->next->gamma)
9005 mng_info->equal_gammas=MagickFalse;
9006 if (next_image->rendering_intent !=
9007 next_image->next->rendering_intent)
9008 mng_info->equal_srgbs=MagickFalse;
9009 if ((next_image->units != next_image->next->units) ||
9010 (next_image->x_resolution != next_image->next->x_resolution) ||
9011 (next_image->y_resolution != next_image->next->y_resolution))
9012 mng_info->equal_physs=MagickFalse;
9013 if (mng_info->equal_chrms)
9015 if (next_image->chromaticity.red_primary.x !=
9016 next_image->next->chromaticity.red_primary.x ||
9017 next_image->chromaticity.red_primary.y !=
9018 next_image->next->chromaticity.red_primary.y ||
9019 next_image->chromaticity.green_primary.x !=
9020 next_image->next->chromaticity.green_primary.x ||
9021 next_image->chromaticity.green_primary.y !=
9022 next_image->next->chromaticity.green_primary.y ||
9023 next_image->chromaticity.blue_primary.x !=
9024 next_image->next->chromaticity.blue_primary.x ||
9025 next_image->chromaticity.blue_primary.y !=
9026 next_image->next->chromaticity.blue_primary.y ||
9027 next_image->chromaticity.white_point.x !=
9028 next_image->next->chromaticity.white_point.x ||
9029 next_image->chromaticity.white_point.y !=
9030 next_image->next->chromaticity.white_point.y)
9031 mng_info->equal_chrms=MagickFalse;
9035 next_image=GetNextImageInList(next_image);
9037 if (image_count < 2)
9039 mng_info->equal_backgrounds=MagickFalse;
9040 mng_info->equal_chrms=MagickFalse;
9041 mng_info->equal_gammas=MagickFalse;
9042 mng_info->equal_srgbs=MagickFalse;
9043 mng_info->equal_physs=MagickFalse;
9044 use_global_plte=MagickFalse;
9045 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9046 need_local_plte=MagickTrue;
9048 need_iterations=MagickFalse;
9050 if (mng_info->need_fram == MagickFalse)
9053 Only certain framing rates 100/n are exactly representable without
9054 the FRAM chunk but we'll allow some slop in VLC files
9056 if (final_delay == 0)
9058 if (need_iterations != MagickFalse)
9061 It's probably a GIF with loop; don't run it *too* fast.
9064 (void) ThrowMagickException(&image->exception,
9065 GetMagickModule(),CoderError,
9066 "input has zero delay between all frames; assuming 10 cs",
9070 mng_info->ticks_per_second=0;
9072 if (final_delay != 0)
9073 mng_info->ticks_per_second=1UL*image->ticks_per_second/final_delay;
9074 if (final_delay > 50)
9075 mng_info->ticks_per_second=2;
9076 if (final_delay > 75)
9077 mng_info->ticks_per_second=1;
9078 if (final_delay > 125)
9079 mng_info->need_fram=MagickTrue;
9080 if (need_defi && final_delay > 2 && (final_delay != 4) &&
9081 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
9082 (final_delay != 25) && (final_delay != 50) && (final_delay !=
9083 1UL*image->ticks_per_second))
9084 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
9086 if (mng_info->need_fram != MagickFalse)
9087 mng_info->ticks_per_second=1UL*image->ticks_per_second;
9089 If pseudocolor, we should also check to see if all the
9090 palettes are identical and write a global PLTE if they are.
9094 Write the MNG version 1.0 signature and MHDR chunk.
9096 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9097 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
9098 PNGType(chunk,mng_MHDR);
9099 LogPNGChunk((int) logging,mng_MHDR,28L);
9100 PNGLong(chunk+4,mng_info->page.width);
9101 PNGLong(chunk+8,mng_info->page.height);
9102 PNGLong(chunk+12,mng_info->ticks_per_second);
9103 PNGLong(chunk+16,0L); /* layer count=unknown */
9104 PNGLong(chunk+20,0L); /* frame count=unknown */
9105 PNGLong(chunk+24,0L); /* play time=unknown */
9110 if (need_defi || mng_info->need_fram || use_global_plte)
9111 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
9113 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
9117 if (need_defi || mng_info->need_fram || use_global_plte)
9118 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
9120 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
9127 if (need_defi || mng_info->need_fram || use_global_plte)
9128 PNGLong(chunk+28,11L); /* simplicity=LC */
9130 PNGLong(chunk+28,9L); /* simplicity=VLC */
9134 if (need_defi || mng_info->need_fram || use_global_plte)
9135 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
9137 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
9140 (void) WriteBlob(image,32,chunk);
9141 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9142 option=GetImageOption(image_info,"mng:need-cacheoff");
9143 if (option != (const char *) NULL)
9149 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9151 PNGType(chunk,mng_nEED);
9152 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
9153 (void) WriteBlobMSBULong(image,(unsigned long) length);
9154 LogPNGChunk((int) logging,mng_nEED,(unsigned long) length);
9156 (void) WriteBlob(image,length,chunk);
9157 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9159 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9160 (GetNextImageInList(image) != (Image *) NULL) &&
9161 (image->iterations != 1))
9164 Write MNG TERM chunk
9166 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9167 PNGType(chunk,mng_TERM);
9168 LogPNGChunk((int) logging,mng_TERM,10L);
9169 chunk[4]=3; /* repeat animation */
9170 chunk[5]=0; /* show last frame when done */
9171 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9172 final_delay/MagickMax(image->ticks_per_second,1)));
9173 if (image->iterations == 0)
9174 PNGLong(chunk+10,PNG_UINT_31_MAX);
9176 PNGLong(chunk+10,(png_uint_32) image->iterations);
9177 if (logging != MagickFalse)
9179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9181 (unsigned long) (mng_info->ticks_per_second*
9182 final_delay/MagickMax(image->ticks_per_second,1)));
9183 if (image->iterations == 0)
9184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9185 " TERM iterations: %lu",(unsigned long) PNG_UINT_31_MAX);
9187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9188 " Image iterations: %lu",image->iterations);
9190 (void) WriteBlob(image,14,chunk);
9191 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9194 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9196 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9197 mng_info->equal_srgbs)
9200 Write MNG sRGB chunk
9202 (void) WriteBlobMSBULong(image,1L);
9203 PNGType(chunk,mng_sRGB);
9204 LogPNGChunk((int) logging,mng_sRGB,1L);
9205 if (image->rendering_intent != UndefinedIntent)
9206 chunk[4]=(unsigned char) (image->rendering_intent-1);
9208 chunk[4]=(unsigned char) (PerceptualIntent-1);
9209 (void) WriteBlob(image,5,chunk);
9210 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9211 mng_info->have_write_global_srgb=MagickTrue;
9215 if (image->gamma && mng_info->equal_gammas)
9218 Write MNG gAMA chunk
9220 (void) WriteBlobMSBULong(image,4L);
9221 PNGType(chunk,mng_gAMA);
9222 LogPNGChunk((int) logging,mng_gAMA,4L);
9223 PNGLong(chunk+4,(unsigned long) (100000*image->gamma+0.5));
9224 (void) WriteBlob(image,8,chunk);
9225 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9226 mng_info->have_write_global_gama=MagickTrue;
9228 if (mng_info->equal_chrms)
9234 Write MNG cHRM chunk
9236 (void) WriteBlobMSBULong(image,32L);
9237 PNGType(chunk,mng_cHRM);
9238 LogPNGChunk((int) logging,mng_cHRM,32L);
9239 primary=image->chromaticity.white_point;
9240 PNGLong(chunk+4,(unsigned long) (100000*primary.x+0.5));
9241 PNGLong(chunk+8,(unsigned long) (100000*primary.y+0.5));
9242 primary=image->chromaticity.red_primary;
9243 PNGLong(chunk+12,(unsigned long) (100000*primary.x+0.5));
9244 PNGLong(chunk+16,(unsigned long) (100000*primary.y+0.5));
9245 primary=image->chromaticity.green_primary;
9246 PNGLong(chunk+20,(unsigned long) (100000*primary.x+0.5));
9247 PNGLong(chunk+24,(unsigned long) (100000*primary.y+0.5));
9248 primary=image->chromaticity.blue_primary;
9249 PNGLong(chunk+28,(unsigned long) (100000*primary.x+0.5));
9250 PNGLong(chunk+32,(unsigned long) (100000*primary.y+0.5));
9251 (void) WriteBlob(image,36,chunk);
9252 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9253 mng_info->have_write_global_chrm=MagickTrue;
9256 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9259 Write MNG pHYs chunk
9261 (void) WriteBlobMSBULong(image,9L);
9262 PNGType(chunk,mng_pHYs);
9263 LogPNGChunk((int) logging,mng_pHYs,9L);
9264 if (image->units == PixelsPerInchResolution)
9266 PNGLong(chunk+4,(unsigned long)
9267 (image->x_resolution*100.0/2.54+0.5));
9268 PNGLong(chunk+8,(unsigned long)
9269 (image->y_resolution*100.0/2.54+0.5));
9274 if (image->units == PixelsPerCentimeterResolution)
9276 PNGLong(chunk+4,(unsigned long)
9277 (image->x_resolution*100.0+0.5));
9278 PNGLong(chunk+8,(unsigned long)
9279 (image->y_resolution*100.0+0.5));
9284 PNGLong(chunk+4,(unsigned long) (image->x_resolution+0.5));
9285 PNGLong(chunk+8,(unsigned long) (image->y_resolution+0.5));
9289 (void) WriteBlob(image,13,chunk);
9290 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9293 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9294 or does not cover the entire frame.
9296 if (write_mng && (image->matte || image->page.x > 0 ||
9297 image->page.y > 0 || (image->page.width &&
9298 (image->page.width+image->page.x < mng_info->page.width))
9299 || (image->page.height && (image->page.height+image->page.y
9300 < mng_info->page.height))))
9302 (void) WriteBlobMSBULong(image,6L);
9303 PNGType(chunk,mng_BACK);
9304 LogPNGChunk((int) logging,mng_BACK,6L);
9305 red=ScaleQuantumToShort(image->background_color.red);
9306 green=ScaleQuantumToShort(image->background_color.green);
9307 blue=ScaleQuantumToShort(image->background_color.blue);
9308 PNGShort(chunk+4,red);
9309 PNGShort(chunk+6,green);
9310 PNGShort(chunk+8,blue);
9311 (void) WriteBlob(image,10,chunk);
9312 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9313 if (mng_info->equal_backgrounds)
9315 (void) WriteBlobMSBULong(image,6L);
9316 PNGType(chunk,mng_bKGD);
9317 LogPNGChunk((int) logging,mng_bKGD,6L);
9318 (void) WriteBlob(image,10,chunk);
9319 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9323 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9324 if ((need_local_plte == MagickFalse) &&
9325 (image->storage_class == PseudoClass) &&
9326 (all_images_are_gray == MagickFalse))
9332 Write MNG PLTE chunk
9334 data_length=3*image->colors;
9335 (void) WriteBlobMSBULong(image,data_length);
9336 PNGType(chunk,mng_PLTE);
9337 LogPNGChunk((int) logging,mng_PLTE,data_length);
9338 for (i=0; i < (long) image->colors; i++)
9340 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9341 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9342 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9344 (void) WriteBlob(image,data_length+4,chunk);
9345 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9346 mng_info->have_write_global_plte=MagickTrue;
9352 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9353 defined(PNG_MNG_FEATURES_SUPPORTED)
9354 mng_info->equal_palettes=MagickFalse;
9358 if (mng_info->adjoin)
9360 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9361 defined(PNG_MNG_FEATURES_SUPPORTED)
9363 If we aren't using a global palette for the entire MNG, check to
9364 see if we can use one for two or more consecutive images.
9366 if (need_local_plte && use_global_plte && !all_images_are_gray)
9368 if (mng_info->IsPalette)
9371 When equal_palettes is true, this image has the same palette
9372 as the previous PseudoClass image
9374 mng_info->have_write_global_plte=mng_info->equal_palettes;
9375 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9376 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9379 Write MNG PLTE chunk
9384 data_length=3*image->colors;
9385 (void) WriteBlobMSBULong(image,data_length);
9386 PNGType(chunk,mng_PLTE);
9387 LogPNGChunk((int) logging,mng_PLTE,data_length);
9388 for (i=0; i < (long) image->colors; i++)
9390 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9391 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9392 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9394 (void) WriteBlob(image,data_length+4,chunk);
9395 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9396 (uInt) (data_length+4)));
9397 mng_info->have_write_global_plte=MagickTrue;
9401 mng_info->have_write_global_plte=MagickFalse;
9412 previous_x=mng_info->page.x;
9413 previous_y=mng_info->page.y;
9420 mng_info->page=image->page;
9421 if ((mng_info->page.x != previous_x) ||
9422 (mng_info->page.y != previous_y))
9424 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
9425 PNGType(chunk,mng_DEFI);
9426 LogPNGChunk((int) logging,mng_DEFI,12L);
9427 chunk[4]=0; /* object 0 MSB */
9428 chunk[5]=0; /* object 0 LSB */
9429 chunk[6]=0; /* visible */
9430 chunk[7]=0; /* abstract */
9431 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9432 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9433 (void) WriteBlob(image,16,chunk);
9434 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9439 mng_info->write_mng=write_mng;
9441 if ((int) image->dispose >= 3)
9442 mng_info->framing_mode=3;
9444 if (mng_info->need_fram && mng_info->adjoin &&
9445 ((image->delay != mng_info->delay) ||
9446 (mng_info->framing_mode != mng_info->old_framing_mode)))
9448 if (image->delay == mng_info->delay)
9451 Write a MNG FRAM chunk with the new framing mode.
9453 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
9454 PNGType(chunk,mng_FRAM);
9455 LogPNGChunk((int) logging,mng_FRAM,1L);
9456 chunk[4]=(unsigned char) mng_info->framing_mode;
9457 (void) WriteBlob(image,5,chunk);
9458 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9463 Write a MNG FRAM chunk with the delay.
9465 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9466 PNGType(chunk,mng_FRAM);
9467 LogPNGChunk((int) logging,mng_FRAM,10L);
9468 chunk[4]=(unsigned char) mng_info->framing_mode;
9469 chunk[5]=0; /* frame name separator (no name) */
9470 chunk[6]=2; /* flag for changing default delay */
9471 chunk[7]=0; /* flag for changing frame timeout */
9472 chunk[8]=0; /* flag for changing frame clipping */
9473 chunk[9]=0; /* flag for changing frame sync_id */
9474 PNGLong(chunk+10,(png_uint_32)
9475 ((mng_info->ticks_per_second*
9476 image->delay)/MagickMax(image->ticks_per_second,1)));
9477 (void) WriteBlob(image,14,chunk);
9478 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9479 mng_info->delay=image->delay;
9481 mng_info->old_framing_mode=mng_info->framing_mode;
9484 #if defined(JNG_SUPPORTED)
9485 if (image_info->compression == JPEGCompression)
9490 if (logging != MagickFalse)
9491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9492 " Writing JNG object.");
9493 /* To do: specify the desired alpha compression method. */
9494 write_info=CloneImageInfo(image_info);
9495 write_info->compression=UndefinedCompression;
9496 status=WriteOneJNGImage(mng_info,write_info,image);
9497 write_info=DestroyImageInfo(write_info);
9502 if (logging != MagickFalse)
9503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9504 " Writing PNG object.");
9505 status=WriteOnePNGImage(mng_info,image_info,image);
9508 if (status == MagickFalse)
9510 MngInfoFreeStruct(mng_info,&have_mng_structure);
9511 (void) CloseBlob(image);
9512 return(MagickFalse);
9514 (void) CatchImageException(image);
9515 if (GetNextImageInList(image) == (Image *) NULL)
9517 image=SyncNextImageInList(image);
9518 status=SetImageProgress(image,SaveImagesTag,scene++,
9519 GetImageListLength(image));
9520 if (status == MagickFalse)
9522 } while (mng_info->adjoin);
9525 while (GetPreviousImageInList(image) != (Image *) NULL)
9526 image=GetPreviousImageInList(image);
9528 Write the MEND chunk.
9530 (void) WriteBlobMSBULong(image,0x00000000L);
9531 PNGType(chunk,mng_MEND);
9532 LogPNGChunk((int) logging,mng_MEND,0L);
9533 (void) WriteBlob(image,4,chunk);
9534 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9537 Relinquish resources.
9539 (void) CloseBlob(image);
9540 MngInfoFreeStruct(mng_info,&have_mng_structure);
9541 if (logging != MagickFalse)
9542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9545 #else /* PNG_LIBPNG_VER > 10011 */
9546 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9549 printf("Your PNG library is too old: You have libpng-%s\n",
9550 PNG_LIBPNG_VER_STRING);
9551 ThrowBinaryException(CoderError,"PNG library is too old",
9552 image_info->filename);
9554 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9556 return(WritePNGImage(image_info,image));
9558 #endif /* PNG_LIBPNG_VER > 10011 */