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 size_t 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 _UShortPixelPacket
255 typedef struct _MngBox
264 typedef struct _MngPair
271 #ifdef MNG_OBJECT_BUFFERS
272 typedef struct _MngBuffer
304 typedef struct _MngInfo
307 #ifdef MNG_OBJECT_BUFFERS
309 *ob[MNG_MAX_OBJECTS];
320 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
321 bytes_in_read_buffer,
327 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
328 defined(PNG_MNG_FEATURES_SUPPORTED)
340 have_saved_bkgd_index,
341 have_write_global_chrm,
342 have_write_global_gama,
343 have_write_global_plte,
344 have_write_global_srgb,
359 x_off[MNG_MAX_OBJECTS],
360 y_off[MNG_MAX_OBJECTS];
366 object_clip[MNG_MAX_OBJECTS];
369 /* These flags could be combined into one byte */
370 exists[MNG_MAX_OBJECTS],
371 frozen[MNG_MAX_OBJECTS],
373 invisible[MNG_MAX_OBJECTS],
374 viewable[MNG_MAX_OBJECTS];
386 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
404 global_x_pixels_per_unit,
405 global_y_pixels_per_unit,
412 global_phys_unit_type,
431 #ifdef MNG_BASI_SUPPORTED
439 basi_compression_method,
441 basi_interlace_method,
468 Forward declarations.
470 static MagickBooleanType
471 WritePNGImage(const ImageInfo *,Image *);
472 static MagickBooleanType
473 WriteMNGImage(const ImageInfo *,Image *);
474 #if defined(JNG_SUPPORTED)
475 static MagickBooleanType
476 WriteJNGImage(const ImageInfo *,Image *);
480 PNG_RenderingIntent_from_Magick_RenderingIntent(const RenderingIntent intent)
484 case PerceptualIntent:
488 case SaturationIntent:
497 static RenderingIntent
498 PNG_RenderingIntent_to_Magick_RenderingIntent(const int png_intent)
503 return PerceptualIntent;
505 return RelativeIntent;
507 return SaturationIntent;
509 return AbsoluteIntent;
511 return UndefinedIntent;
515 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
521 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
528 #if PNG_LIBPNG_VER > 10011
529 #if defined(PNG_SORT_PALETTE)
531 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
535 % 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 %
539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
541 % CompressColormapTransFirst compresses an image colormap removing
542 % any duplicate and unused color entries and putting the transparent colors
543 % first. Returns MagickTrue on success, MagickFalse on error.
545 % The format of the CompressColormapTransFirst method is:
547 % unsigned int CompressColormapTransFirst(Image *image)
549 % A description of each parameter follows:
551 % o image: the address of a structure of type Image.
552 % This function updates image->colors and image->colormap.
555 static MagickBooleanType CompressColormapTransFirst(Image *image)
570 register const IndexPacket
573 register const PixelPacket
592 Determine if colormap can be compressed.
594 assert(image != (Image *) NULL);
595 assert(image->signature == MagickSignature);
596 if (image->debug != MagickFalse)
597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
598 " CompressColorMapTransFirst %s (%.20g colors)",image->filename,
599 (double) image->colors);
600 if (image->storage_class != PseudoClass || image->colors > 256 ||
603 if (image->debug != MagickFalse)
605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
606 " Could not compress colormap");
607 if (image->colors > 256 || image->colors == 0)
613 marker=(unsigned char *) AcquireQuantumMemory(image->colors,sizeof(*marker));
614 if (marker == (unsigned char *) NULL)
615 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
617 opacity=(IndexPacket *) AcquireQuantumMemory(image->colors,sizeof(*opacity));
618 if (opacity == (IndexPacket *) NULL)
620 marker=(unsigned char *) RelinquishMagickMemory(marker);
621 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
625 Mark colors that are present.
627 number_colors=(ssize_t) image->colors;
628 for (i=0; i < number_colors; i++)
630 marker[i]=MagickFalse;
631 opacity[i]=OpaqueOpacity;
634 for (y=0; y < (ssize_t) image->rows; y++)
636 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
637 if (p == (const PixelPacket *) NULL)
639 indexes=GetVirtualIndexQueue(image);
640 if (image->matte != MagickFalse)
641 for (x=0; x < (ssize_t) image->columns; x++)
643 marker[(int) indexes[x]]=MagickTrue;
644 opacity[(int) indexes[x]]=GetOpacityPixelComponent(p);
645 if (indexes[x] > top_used)
650 for (x=0; x < (ssize_t) image->columns; x++)
652 marker[(int) indexes[x]]=MagickTrue;
653 if (indexes[x] > top_used)
658 if (image->matte != MagickFalse)
661 Mark background color, topmost occurrence if more than one.
663 for (i=number_colors-1; i; i--)
665 if (IsColorEqual(image->colormap+i,&image->background_color))
667 marker[i]=MagickTrue;
675 for (i=0; i < number_colors-1; i++)
678 for (j=i+1; j < number_colors; j++)
679 if ((opacity[i] == opacity[j]) &&
680 (IsColorEqual(image->colormap+i,image->colormap+j)))
681 marker[j]=MagickFalse;
684 Count colors that still remain.
686 have_transparency=MagickFalse;
688 for (i=0; i < number_colors; i++)
692 if (opacity[i] != OpaqueOpacity)
693 have_transparency=MagickTrue;
695 if ((!have_transparency || (marker[0] &&
696 (opacity[0] == (Quantum) TransparentOpacity)))
697 && (new_number_colors == number_colors))
700 No duplicate or unused entries, and transparency-swap not needed.
702 marker=(unsigned char *) RelinquishMagickMemory(marker);
703 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
707 remap_needed=MagickFalse;
708 if ((ssize_t) top_used >= new_number_colors)
709 remap_needed=MagickTrue;
715 colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
717 if (colormap == (PixelPacket *) NULL)
719 marker=(unsigned char *) RelinquishMagickMemory(marker);
720 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
721 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
725 Eliminate unused colormap entries.
727 map=(IndexPacket *) AcquireQuantumMemory((size_t) number_colors,
729 if (map == (IndexPacket *) NULL)
731 marker=(unsigned char *) RelinquishMagickMemory(marker);
732 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
733 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
734 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
738 for (i=0; i < number_colors; i++)
740 map[i]=(IndexPacket) k;
743 for (j=i+1; j < number_colors; j++)
745 if ((opacity[i] == opacity[j]) &&
746 (IsColorEqual(image->colormap+i,image->colormap+j)))
748 map[j]=(IndexPacket) k;
749 marker[j]=MagickFalse;
756 for (i=0; i < number_colors; i++)
760 colormap[j]=image->colormap[i];
764 if (have_transparency && (opacity[0] != (Quantum) TransparentOpacity))
767 Move the first transparent color to palette entry 0.
769 for (i=1; i < number_colors; i++)
771 if (marker[i] && opacity[i] == (Quantum) TransparentOpacity)
776 temp_colormap=colormap[0];
777 colormap[0]=colormap[(int) map[i]];
778 colormap[(ssize_t) map[i]]=temp_colormap;
779 for (j=0; j < number_colors; j++)
783 else if (map[j] == map[i])
786 remap_needed=MagickTrue;
792 opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
793 marker=(unsigned char *) RelinquishMagickMemory(marker);
809 exception=(&image->exception);
810 for (y=0; y < (ssize_t) image->rows; y++)
812 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
813 if (q == (PixelPacket *) NULL)
815 pixels=GetAuthenticIndexQueue(image);
816 for (x=0; x < (ssize_t) image->columns; x++)
821 if (SyncAuthenticPixels(image,exception) == MagickFalse)
824 for (i=0; i < new_number_colors; i++)
825 image->colormap[i]=colormap[i];
827 colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
828 image->colors=(size_t) new_number_colors;
829 map=(IndexPacket *) RelinquishMagickMemory(map);
835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839 % I m a g e I s G r a y %
843 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
845 % Like IsGrayImage except does not change DirectClass to PseudoClass %
847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
849 static MagickBooleanType ImageIsGray(Image *image)
851 register const PixelPacket
859 assert(image != (Image *) NULL);
860 assert(image->signature == MagickSignature);
861 if (image->debug != MagickFalse)
862 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
864 if (image->storage_class == PseudoClass)
866 for (i=0; i < (ssize_t) image->colors; i++)
867 if (IsGray(image->colormap+i) == MagickFalse)
871 for (y=0; y < (ssize_t) image->rows; y++)
873 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
874 if (p == (const PixelPacket *) NULL)
876 for (x=(ssize_t) image->columns-1; x >= 0; x--)
878 if (IsGray(p) == MagickFalse)
887 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
891 % I m a g e I s M o n o c h r o m e %
895 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
897 % Like IsMonochromeImage except does not change DirectClass to PseudoClass %
898 % and is more accurate. %
900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
902 static MagickBooleanType ImageIsMonochrome(Image *image)
904 register const PixelPacket
912 assert(image != (Image *) NULL);
913 assert(image->signature == MagickSignature);
914 if (image->debug != MagickFalse)
915 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
916 if (image->storage_class == PseudoClass)
918 for (i=0; i < (ssize_t) image->colors; i++)
920 if ((IsGray(image->colormap+i) == MagickFalse) ||
921 ((image->colormap[i].red != 0) &&
922 (image->colormap[i].red != (Quantum) QuantumRange)))
927 for (y=0; y < (ssize_t) image->rows; y++)
929 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
930 if (p == (const PixelPacket *) NULL)
932 for (x=(ssize_t) image->columns-1; x >= 0; x--)
934 if ((p->red != 0) && (p->red != (Quantum) QuantumRange))
936 if (IsGray(p) == MagickFalse)
938 if ((p->opacity != OpaqueOpacity) &&
939 (p->opacity != (Quantum) TransparentOpacity))
946 #endif /* PNG_LIBPNG_VER > 10011 */
947 #endif /* MAGICKCORE_PNG_DELEGATE */
950 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
958 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
960 % IsMNG() returns MagickTrue if the image format type, identified by the
961 % magick string, is MNG.
963 % The format of the IsMNG method is:
965 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
967 % A description of each parameter follows:
969 % o magick: compare image format pattern against these bytes.
971 % o length: Specifies the length of the magick string.
975 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
979 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
985 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
993 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
995 % IsJNG() returns MagickTrue if the image format type, identified by the
996 % magick string, is JNG.
998 % The format of the IsJNG method is:
1000 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1002 % A description of each parameter follows:
1004 % o magick: compare image format pattern against these bytes.
1006 % o length: Specifies the length of the magick string.
1010 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1013 return(MagickFalse);
1014 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1016 return(MagickFalse);
1020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1030 % IsPNG() returns MagickTrue if the image format type, identified by the
1031 % magick string, is PNG.
1033 % The format of the IsPNG method is:
1035 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1037 % A description of each parameter follows:
1039 % o magick: compare image format pattern against these bytes.
1041 % o length: Specifies the length of the magick string.
1044 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1047 return(MagickFalse);
1048 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1050 return(MagickFalse);
1053 #if defined(MAGICKCORE_PNG_DELEGATE)
1054 #if defined(__cplusplus) || defined(c_plusplus)
1058 #if (PNG_LIBPNG_VER > 10011)
1059 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1064 assert(image != (Image *) NULL);
1065 assert(image->signature == MagickSignature);
1066 buffer[0]=(unsigned char) (value >> 24);
1067 buffer[1]=(unsigned char) (value >> 16);
1068 buffer[2]=(unsigned char) (value >> 8);
1069 buffer[3]=(unsigned char) value;
1070 return((size_t) WriteBlob(image,4,buffer));
1073 static void PNGLong(png_bytep p,png_uint_32 value)
1075 *p++=(png_byte) ((value >> 24) & 0xff);
1076 *p++=(png_byte) ((value >> 16) & 0xff);
1077 *p++=(png_byte) ((value >> 8) & 0xff);
1078 *p++=(png_byte) (value & 0xff);
1081 static void PNGsLong(png_bytep p,png_int_32 value)
1083 *p++=(png_byte) ((value >> 24) & 0xff);
1084 *p++=(png_byte) ((value >> 16) & 0xff);
1085 *p++=(png_byte) ((value >> 8) & 0xff);
1086 *p++=(png_byte) (value & 0xff);
1089 static void PNGShort(png_bytep p,png_uint_16 value)
1091 *p++=(png_byte) ((value >> 8) & 0xff);
1092 *p++=(png_byte) (value & 0xff);
1095 static void PNGType(png_bytep p,png_bytep type)
1097 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1100 static void LogPNGChunk(int logging, png_bytep type, size_t length)
1102 if (logging != MagickFalse)
1103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1104 " Writing %c%c%c%c chunk, length: %.20g",
1105 type[0],type[1],type[2],type[3],(double) length);
1107 #endif /* PNG_LIBPNG_VER > 10011 */
1109 #if defined(__cplusplus) || defined(c_plusplus)
1113 #if PNG_LIBPNG_VER > 10011
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1119 % R e a d P N G I m a g e %
1123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1126 % Multiple-image Network Graphics (MNG) image file and returns it. It
1127 % allocates the memory necessary for the new Image structure and returns a
1128 % pointer to the new image or set of images.
1130 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1132 % The format of the ReadPNGImage method is:
1134 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1136 % A description of each parameter follows:
1138 % o image_info: the image info.
1140 % o exception: return any errors or warnings in this structure.
1142 % To do, more or less in chronological order (as of version 5.5.2,
1143 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1145 % Get 16-bit cheap transparency working.
1147 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1149 % Preserve all unknown and not-yet-handled known chunks found in input
1150 % PNG file and copy them into output PNG files according to the PNG
1153 % (At this point, PNG encoding should be in full MNG compliance)
1155 % Provide options for choice of background to use when the MNG BACK
1156 % chunk is not present or is not mandatory (i.e., leave transparent,
1157 % user specified, MNG BACK, PNG bKGD)
1159 % Implement LOOP/ENDL [done, but could do discretionary loops more
1160 % efficiently by linking in the duplicate frames.].
1162 % Decode and act on the MHDR simplicity profile (offer option to reject
1163 % files or attempt to process them anyway when the profile isn't LC or VLC).
1165 % Upgrade to full MNG without Delta-PNG.
1167 % o BACK [done a while ago except for background image ID]
1168 % o MOVE [done 15 May 1999]
1169 % o CLIP [done 15 May 1999]
1170 % o DISC [done 19 May 1999]
1171 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1172 % o SEEK [partially done 19 May 1999 (discard function only)]
1176 % o MNG-level tEXt/iTXt/zTXt
1181 % o iTXt (wait for libpng implementation).
1183 % Use the scene signature to discover when an identical scene is
1184 % being reused, and just point to the original image->exception instead
1185 % of storing another set of pixels. This not specific to MNG
1186 % but could be applied generally.
1188 % Upgrade to full MNG with Delta-PNG.
1190 % JNG tEXt/iTXt/zTXt
1192 % We will not attempt to read files containing the CgBI chunk.
1193 % They are really Xcode files meant for display on the iPhone.
1194 % These are not valid PNG files and it is impossible to recover
1195 % the orginal PNG from files that have been converted to Xcode-PNG,
1196 % since irretrievable loss of color data has occurred due to the
1197 % use of premultiplied alpha.
1200 #if defined(__cplusplus) || defined(c_plusplus)
1205 This the function that does the actual reading of data. It is
1206 the same as the one supplied in libpng, except that it receives the
1207 datastream from the ReadBlob() function instead of standard input.
1209 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1214 image=(Image *) png_get_io_ptr(png_ptr);
1220 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1221 if (check != length)
1226 (void) FormatMagickString(msg,MaxTextExtent,
1227 "Expected %.20g bytes; found %.20g bytes",(double) length,
1229 png_warning(png_ptr,msg);
1230 png_error(png_ptr,"Read Exception");
1235 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1236 !defined(PNG_MNG_FEATURES_SUPPORTED)
1237 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1238 * older than libpng-1.0.3a, which was the first to allow the empty
1239 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1240 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1241 * encountered after an empty PLTE, so we have to look ahead for bKGD
1242 * chunks and remove them from the datastream that is passed to libpng,
1243 * and store their contents for later use.
1245 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1260 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1261 image=(Image *) mng_info->image;
1262 while (mng_info->bytes_in_read_buffer && length)
1264 data[i]=mng_info->read_buffer[i];
1265 mng_info->bytes_in_read_buffer--;
1271 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1272 if (check != length)
1273 png_error(png_ptr,"Read Exception");
1276 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1279 check=(png_size_t) ReadBlob(image,(size_t) length,
1280 (char *) mng_info->read_buffer);
1281 mng_info->read_buffer[4]=0;
1282 mng_info->bytes_in_read_buffer=4;
1283 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1284 mng_info->found_empty_plte=MagickTrue;
1285 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1287 mng_info->found_empty_plte=MagickFalse;
1288 mng_info->have_saved_bkgd_index=MagickFalse;
1291 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1294 check=(png_size_t) ReadBlob(image,(size_t) length,
1295 (char *) mng_info->read_buffer);
1296 mng_info->read_buffer[4]=0;
1297 mng_info->bytes_in_read_buffer=4;
1298 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1299 if (mng_info->found_empty_plte)
1302 Skip the bKGD data byte and CRC.
1305 ReadBlob(image,5,(char *) mng_info->read_buffer);
1306 check=(png_size_t) ReadBlob(image,(size_t) length,
1307 (char *) mng_info->read_buffer);
1308 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1309 mng_info->have_saved_bkgd_index=MagickTrue;
1310 mng_info->bytes_in_read_buffer=0;
1318 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1323 image=(Image *) png_get_io_ptr(png_ptr);
1329 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1330 if (check != length)
1331 png_error(png_ptr,"WriteBlob Failed");
1335 static void png_flush_data(png_structp png_ptr)
1340 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1341 static int PalettesAreEqual(Image *a,Image *b)
1346 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1347 return((int) MagickFalse);
1348 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1349 return((int) MagickFalse);
1350 if (a->colors != b->colors)
1351 return((int) MagickFalse);
1352 for (i=0; i < (ssize_t) a->colors; i++)
1354 if ((a->colormap[i].red != b->colormap[i].red) ||
1355 (a->colormap[i].green != b->colormap[i].green) ||
1356 (a->colormap[i].blue != b->colormap[i].blue))
1357 return((int) MagickFalse);
1359 return((int) MagickTrue);
1363 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1365 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1366 mng_info->exists[i] && !mng_info->frozen[i])
1368 #ifdef MNG_OBJECT_BUFFERS
1369 if (mng_info->ob[i] != (MngBuffer *) NULL)
1371 if (mng_info->ob[i]->reference_count > 0)
1372 mng_info->ob[i]->reference_count--;
1373 if (mng_info->ob[i]->reference_count == 0)
1375 if (mng_info->ob[i]->image != (Image *) NULL)
1376 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1377 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1380 mng_info->ob[i]=(MngBuffer *) NULL;
1382 mng_info->exists[i]=MagickFalse;
1383 mng_info->invisible[i]=MagickFalse;
1384 mng_info->viewable[i]=MagickFalse;
1385 mng_info->frozen[i]=MagickFalse;
1386 mng_info->x_off[i]=0;
1387 mng_info->y_off[i]=0;
1388 mng_info->object_clip[i].left=0;
1389 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1390 mng_info->object_clip[i].top=0;
1391 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1395 static void MngInfoFreeStruct(MngInfo *mng_info,int *have_mng_structure)
1397 if (*have_mng_structure && (mng_info != (MngInfo *) NULL))
1402 for (i=1; i < MNG_MAX_OBJECTS; i++)
1403 MngInfoDiscardObject(mng_info,i);
1404 if (mng_info->global_plte != (png_colorp) NULL)
1405 mng_info->global_plte=(png_colorp)
1406 RelinquishMagickMemory(mng_info->global_plte);
1407 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1408 *have_mng_structure=MagickFalse;
1412 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1418 if (box.left < box2.left)
1420 if (box.top < box2.top)
1422 if (box.right > box2.right)
1423 box.right=box2.right;
1424 if (box.bottom > box2.bottom)
1425 box.bottom=box2.bottom;
1429 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1435 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1437 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1438 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1439 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1440 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1441 if (delta_type != 0)
1443 box.left+=previous_box.left;
1444 box.right+=previous_box.right;
1445 box.top+=previous_box.top;
1446 box.bottom+=previous_box.bottom;
1451 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1457 Read two ssize_ts from CLON, MOVE or PAST chunk
1459 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1460 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1461 if (delta_type != 0)
1463 pair.a+=previous_pair.a;
1464 pair.b+=previous_pair.b;
1469 static long mng_get_long(unsigned char *p)
1471 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1474 static void PNGErrorHandler(png_struct *ping,png_const_charp message)
1479 image=(Image *) png_get_error_ptr(ping);
1480 if (image->debug != MagickFalse)
1481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1482 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1483 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1484 message,"`%s'",image->filename);
1485 #if PNG_LIBPNG_VER < 10500
1486 longjmp(ping->jmpbuf,1);
1488 png_longjmp(ping,1);
1492 static void PNGWarningHandler(png_struct *ping,png_const_charp message)
1497 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1498 png_error(ping, message);
1499 image=(Image *) png_get_error_ptr(ping);
1500 if (image->debug != MagickFalse)
1501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1502 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1503 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1504 message,"`%s'",image->filename);
1507 #ifdef PNG_USER_MEM_SUPPORTED
1508 static png_voidp png_IM_malloc(png_structp png_ptr,png_uint_32 size)
1510 #if (PNG_LIBPNG_VER < 10011)
1515 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1517 png_error("Insufficient memory.");
1521 return((png_voidp) AcquireMagickMemory((size_t) size));
1526 Free a pointer. It is removed from the list at the same time.
1528 static png_free_ptr png_IM_free(png_structp png_ptr,png_voidp ptr)
1531 ptr=RelinquishMagickMemory(ptr);
1532 return((png_free_ptr) NULL);
1536 #if defined(__cplusplus) || defined(c_plusplus)
1541 png_read_raw_profile(Image *image, const ImageInfo *image_info,
1542 png_textp text,int ii)
1547 register unsigned char
1561 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1562 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1563 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1564 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1565 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1569 /* look for newline */
1572 /* look for length */
1573 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1575 length=(png_uint_32) StringToLong(sp);
1576 while (*sp != ' ' && *sp != '\n')
1578 /* allocate space */
1581 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1582 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1583 return(MagickFalse);
1585 profile=AcquireStringInfo(length);
1586 if (profile == (StringInfo *) NULL)
1588 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1589 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1590 "unable to copy profile");
1591 return(MagickFalse);
1593 /* copy profile, skipping white space and column 1 "=" signs */
1594 dp=GetStringInfoDatum(profile);
1596 for (i=0; i < (ssize_t) nibbles; i++)
1598 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1602 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1603 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1604 profile=DestroyStringInfo(profile);
1605 return(MagickFalse);
1610 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1612 (*dp++)+=unhex[(int) *sp++];
1615 We have already read "Raw profile type.
1617 (void) SetImageProfile(image,&text[ii].key[17],profile);
1618 profile=DestroyStringInfo(profile);
1619 if (image_info->verbose)
1620 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1624 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1625 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1631 /* The unknown chunk structure contains the chunk data:
1636 Note that libpng has already taken care of the CRC handling.
1640 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1641 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1642 return(0); /* Did not recognize */
1644 /* recognized vpAg */
1646 if (chunk->size != 9)
1647 return(-1); /* Error return */
1649 if (chunk->data[8] != 0)
1650 return(0); /* ImageMagick requires pixel units */
1652 image=(Image *) png_get_user_chunk_ptr(ping);
1654 image->page.width=(size_t) ((chunk->data[0] << 24) |
1655 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1656 image->page.height=(size_t) ((chunk->data[4] << 24) |
1657 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1659 /* Return one of the following: */
1660 /* return(-n); chunk had an error */
1661 /* return(0); did not recognize */
1662 /* return(n); success */
1670 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1674 % R e a d O n e P N G I m a g e %
1678 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1680 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1681 % (minus the 8-byte signature) and returns it. It allocates the memory
1682 % necessary for the new Image structure and returns a pointer to the new
1685 % The format of the ReadOnePNGImage method is:
1687 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1688 % ExceptionInfo *exception)
1690 % A description of each parameter follows:
1692 % o mng_info: Specifies a pointer to a MngInfo structure.
1694 % o image_info: the image info.
1696 % o exception: return any errors or warnings in this structure.
1699 static Image *ReadOnePNGImage(MngInfo *mng_info,
1700 const ImageInfo *image_info, ExceptionInfo *exception)
1702 /* Read one PNG image */
1714 ping_interlace_method,
1715 ping_compression_method,
1756 register unsigned char
1759 register IndexPacket
1766 register PixelPacket
1775 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1776 png_byte unused_chunks[]=
1778 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1779 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1780 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1781 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1782 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1783 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1787 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1788 " enter ReadOnePNGImage()");
1790 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1791 LockSemaphoreInfo(png_semaphore);
1794 #if (PNG_LIBPNG_VER < 10200)
1795 if (image_info->verbose)
1796 printf("Your PNG library (libpng-%s) is rather old.\n",
1797 PNG_LIBPNG_VER_STRING);
1800 #if (PNG_LIBPNG_VER >= 10400)
1801 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1802 if (image_info->verbose)
1804 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1805 PNG_LIBPNG_VER_STRING);
1806 printf("Please update it.\n");
1812 quantum_info = (QuantumInfo *) NULL;
1813 image=mng_info->image;
1816 Allocate the PNG structures
1818 #ifdef PNG_USER_MEM_SUPPORTED
1819 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1820 PNGErrorHandler,PNGWarningHandler, NULL,
1821 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
1823 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1824 PNGErrorHandler,PNGWarningHandler);
1826 if (ping == (png_struct *) NULL)
1827 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1828 ping_info=png_create_info_struct(ping);
1829 if (ping_info == (png_info *) NULL)
1831 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1832 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1834 end_info=png_create_info_struct(ping);
1835 if (end_info == (png_info *) NULL)
1837 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1838 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1840 png_pixels=(unsigned char *) NULL;
1841 if (setjmp(png_jmpbuf(ping)))
1844 PNG image is corrupt.
1846 png_destroy_read_struct(&ping,&ping_info,&end_info);
1847 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1848 UnlockSemaphoreInfo(png_semaphore);
1850 if (logging != MagickFalse)
1851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1852 " exit ReadOnePNGImage() with error.");
1853 if (image != (Image *) NULL)
1855 InheritException(exception,&image->exception);
1858 return(GetFirstImageInList(image));
1861 Prepare PNG for reading.
1864 mng_info->image_found++;
1865 png_set_sig_bytes(ping,8);
1866 if (LocaleCompare(image_info->magick,"MNG") == 0)
1868 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1869 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1870 png_set_read_fn(ping,image,png_get_data);
1872 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1873 png_permit_empty_plte(ping,MagickTrue);
1874 png_set_read_fn(ping,image,png_get_data);
1876 mng_info->image=image;
1877 mng_info->bytes_in_read_buffer=0;
1878 mng_info->found_empty_plte=MagickFalse;
1879 mng_info->have_saved_bkgd_index=MagickFalse;
1880 png_set_read_fn(ping,mng_info,mng_get_data);
1885 png_set_read_fn(ping,image,png_get_data);
1887 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1888 /* Ignore unused chunks and all unknown chunks except for vpAg */
1889 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1890 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1891 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1892 (int)sizeof(unused_chunks)/5);
1893 /* Callback for other unknown chunks */
1894 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1897 #if (PNG_LIBPNG_VER < 10400)
1898 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1899 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1900 /* Disable thread-unsafe features of pnggccrd */
1901 if (png_access_version_number() >= 10200)
1903 png_uint_32 mmx_disable_mask=0;
1904 png_uint_32 asm_flags;
1906 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1907 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1908 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1909 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1910 asm_flags=png_get_asm_flags(ping);
1911 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1916 png_read_info(ping,ping_info);
1918 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1919 &ping_bit_depth,&ping_color_type,
1920 &ping_interlace_method,&ping_compression_method,
1921 &ping_filter_method);
1923 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1926 (void) png_get_bKGD(ping, ping_info, &ping_background);
1928 if (ping_bit_depth < 8)
1930 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1932 png_set_packing(ping);
1937 image->depth=ping_bit_depth;
1938 image->depth=GetImageQuantumDepth(image,MagickFalse);
1939 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1940 if (logging != MagickFalse)
1942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1943 " PNG width: %.20g, height: %.20g",
1944 (double) ping_width, (double) ping_height);
1945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1946 " PNG color_type: %d, bit_depth: %d",
1947 ping_color_type, ping_bit_depth);
1948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1949 " PNG compression_method: %d",
1950 ping_compression_method);
1951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1952 " PNG interlace_method: %d, filter_method: %d",
1953 ping_interlace_method,ping_filter_method);
1956 #ifdef PNG_READ_iCCP_SUPPORTED
1957 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1969 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1971 if (profile_length != 0)
1976 if (logging != MagickFalse)
1977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1978 " Reading PNG iCCP chunk.");
1979 profile=AcquireStringInfo(profile_length);
1980 SetStringInfoDatum(profile,(const unsigned char *) info);
1981 (void) SetImageProfile(image,"icc",profile);
1982 profile=DestroyStringInfo(profile);
1986 #if defined(PNG_READ_sRGB_SUPPORTED)
1991 if (mng_info->have_global_srgb)
1992 image->rendering_intent=PNG_RenderingIntent_to_Magick_RenderingIntent(
1993 mng_info->global_srgb_intent);
1994 if (png_get_sRGB(ping,ping_info,&intent))
1996 image->rendering_intent=PNG_RenderingIntent_to_Magick_RenderingIntent(
1998 if (logging != MagickFalse)
1999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2000 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2008 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2009 if (mng_info->have_global_gama)
2010 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2011 if (png_get_gAMA(ping,ping_info,&file_gamma))
2013 image->gamma=(float) file_gamma;
2014 if (logging != MagickFalse)
2015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2016 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2019 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2021 if (mng_info->have_global_chrm != MagickFalse)
2023 (void) png_set_cHRM(ping,ping_info,
2024 mng_info->global_chrm.white_point.x,
2025 mng_info->global_chrm.white_point.y,
2026 mng_info->global_chrm.red_primary.x,
2027 mng_info->global_chrm.red_primary.y,
2028 mng_info->global_chrm.green_primary.x,
2029 mng_info->global_chrm.green_primary.y,
2030 mng_info->global_chrm.blue_primary.x,
2031 mng_info->global_chrm.blue_primary.y);
2034 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2036 (void) png_get_cHRM(ping,ping_info,
2037 &image->chromaticity.white_point.x,
2038 &image->chromaticity.white_point.y,
2039 &image->chromaticity.red_primary.x,
2040 &image->chromaticity.red_primary.y,
2041 &image->chromaticity.green_primary.x,
2042 &image->chromaticity.green_primary.y,
2043 &image->chromaticity.blue_primary.x,
2044 &image->chromaticity.blue_primary.y);
2045 if (logging != MagickFalse)
2046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2047 " Reading PNG cHRM chunk.");
2049 if (image->rendering_intent != UndefinedIntent)
2051 png_set_sRGB(ping,ping_info,
2052 PNG_RenderingIntent_from_Magick_RenderingIntent(
2053 image->rendering_intent));
2054 png_set_gAMA(ping,ping_info,0.45455f);
2055 png_set_cHRM(ping,ping_info,
2056 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2057 0.1500f, 0.0600f, 0.3127f, 0.3290f);
2059 #if defined(PNG_oFFs_SUPPORTED)
2060 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2062 image->page.x=png_get_x_offset_pixels(ping, ping_info);
2063 image->page.y=png_get_y_offset_pixels(ping, ping_info);
2064 if (logging != MagickFalse)
2065 if (image->page.x || image->page.y)
2066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2067 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2068 image->page.x,(double) image->page.y);
2071 #if defined(PNG_pHYs_SUPPORTED)
2072 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2074 if (mng_info->have_global_phys)
2076 png_set_pHYs(ping,ping_info,
2077 mng_info->global_x_pixels_per_unit,
2078 mng_info->global_y_pixels_per_unit,
2079 mng_info->global_phys_unit_type);
2083 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2093 Set image resolution.
2095 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2097 image->x_resolution=(double) x_resolution;
2098 image->y_resolution=(double) y_resolution;
2099 if (unit_type == PNG_RESOLUTION_METER)
2101 image->units=PixelsPerCentimeterResolution;
2102 image->x_resolution=(double) x_resolution/100.0;
2103 image->y_resolution=(double) y_resolution/100.0;
2105 if (logging != MagickFalse)
2106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2107 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2108 (double) x_resolution,(double) y_resolution,unit_type);
2111 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2119 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2120 if ((number_colors == 0) &&
2121 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2123 if (mng_info->global_plte_length)
2125 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2126 (int) mng_info->global_plte_length);
2127 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2128 if (mng_info->global_trns_length)
2130 if (mng_info->global_trns_length >
2131 mng_info->global_plte_length)
2132 (void) ThrowMagickException(&image->exception,
2133 GetMagickModule(),CoderError,
2134 "global tRNS has more entries than global PLTE",
2135 "`%s'",image_info->filename);
2136 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2137 (int) mng_info->global_trns_length,NULL);
2139 #if defined(PNG_READ_bKGD_SUPPORTED)
2141 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2142 mng_info->have_saved_bkgd_index ||
2144 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2149 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2150 if (mng_info->have_saved_bkgd_index)
2151 background.index=mng_info->saved_bkgd_index;
2153 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2154 background.index=ping_background->index;
2155 background.red=(png_uint_16)
2156 mng_info->global_plte[background.index].red;
2157 background.green=(png_uint_16)
2158 mng_info->global_plte[background.index].green;
2159 background.blue=(png_uint_16)
2160 mng_info->global_plte[background.index].blue;
2161 png_set_bKGD(ping,ping_info,&background);
2166 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2167 CoderError,"No global PLTE in file","`%s'",
2168 image_info->filename);
2172 #if defined(PNG_READ_bKGD_SUPPORTED)
2173 if (mng_info->have_global_bkgd &&
2174 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2175 image->background_color=mng_info->mng_global_bkgd;
2176 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2179 Set image background color.
2181 if (logging != MagickFalse)
2182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2183 " Reading PNG bKGD chunk.");
2184 if (ping_bit_depth == MAGICKCORE_QUANTUM_DEPTH)
2186 image->background_color.red=ping_background->red;
2187 image->background_color.green=ping_background->green;
2188 image->background_color.blue=ping_background->blue;
2190 else /* Scale background components to 16-bit */
2195 if (logging != MagickFalse)
2196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2197 " raw ping_background=(%d,%d,%d).",ping_background->red,
2198 ping_background->green,ping_background->blue);
2201 if (ping_bit_depth == 1)
2203 else if (ping_bit_depth == 2)
2205 else if (ping_bit_depth == 4)
2207 if (ping_bit_depth <= 8)
2210 ping_background->red *= bkgd_scale;
2211 ping_background->green *= bkgd_scale;
2212 ping_background->blue *= bkgd_scale;
2214 if (logging != MagickFalse)
2216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2217 " bkgd_scale=%d.",bkgd_scale);
2218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2219 " ping_background=(%d,%d,%d).",ping_background->red,
2220 ping_background->green,ping_background->blue);
2223 image->background_color.red=
2224 ScaleShortToQuantum(ping_background->red);
2225 image->background_color.green=
2226 ScaleShortToQuantum(ping_background->green);
2227 image->background_color.blue=
2228 ScaleShortToQuantum(ping_background->blue);
2230 if (logging != MagickFalse)
2231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2232 " image->background_color=(%.20g,%.20g,%.20g).",
2233 (double) image->background_color.red,
2234 (double) image->background_color.green,
2235 (double) image->background_color.blue);
2239 transparent_color.red=0;
2240 transparent_color.green=0;
2241 transparent_color.blue=0;
2242 transparent_color.opacity=0;
2243 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2246 Image has a transparent background.
2254 if (logging != MagickFalse)
2255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2256 " Reading PNG tRNS chunk.");
2258 max_sample = (int) ((one << ping_bit_depth) - 1);
2260 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2261 (int)ping_trans_color->gray > max_sample) ||
2262 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2263 ((int)ping_trans_color->red > max_sample ||
2264 (int)ping_trans_color->green > max_sample ||
2265 (int)ping_trans_color->blue > max_sample)))
2267 if (logging != MagickFalse)
2268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2269 " Ignoring PNG tRNS chunk with out-of-range sample.");
2270 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2271 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2272 image->matte=MagickFalse;
2276 transparent_color.red= (unsigned short)(ping_trans_color->red);
2277 transparent_color.green= (unsigned short) (ping_trans_color->green);
2278 transparent_color.blue= (unsigned short) (ping_trans_color->blue);
2279 transparent_color.opacity= (unsigned short) (ping_trans_color->gray);
2281 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2283 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2284 if (ping_bit_depth < MAGICKCORE_QUANTUM_DEPTH)
2286 transparent_color.opacity=(unsigned short) (
2287 ping_trans_color->gray *
2288 (65535L/((1UL << ping_bit_depth)-1)));
2290 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2292 transparent_color.opacity=(unsigned short) (
2293 (ping_trans_color->gray * 65535L)/
2294 ((1UL << ping_bit_depth)-1));
2296 if (logging != MagickFalse)
2298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2299 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2301 " scaled graylevel is %d.",transparent_color.opacity);
2303 transparent_color.red=transparent_color.opacity;
2304 transparent_color.green=transparent_color.opacity;
2305 transparent_color.blue=transparent_color.opacity;
2309 #if defined(PNG_READ_sBIT_SUPPORTED)
2310 if (mng_info->have_global_sbit)
2312 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2313 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2316 num_passes=png_set_interlace_handling(ping);
2318 png_read_update_info(ping,ping_info);
2320 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2323 Initialize image structure.
2325 mng_info->image_box.left=0;
2326 mng_info->image_box.right=(ssize_t) ping_width;
2327 mng_info->image_box.top=0;
2328 mng_info->image_box.bottom=(ssize_t) ping_height;
2329 if (mng_info->mng_type == 0)
2331 mng_info->mng_width=ping_width;
2332 mng_info->mng_height=ping_height;
2333 mng_info->frame=mng_info->image_box;
2334 mng_info->clip=mng_info->image_box;
2338 image->page.y=mng_info->y_off[mng_info->object_id];
2340 image->compression=ZipCompression;
2341 image->columns=ping_width;
2342 image->rows=ping_height;
2343 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2344 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2345 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2350 image->storage_class=PseudoClass;
2352 image->colors=one << ping_bit_depth;
2353 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2354 if (image->colors > 256)
2357 if (image->colors > 65536L)
2358 image->colors=65536L;
2360 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2368 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2369 image->colors=(size_t) number_colors;
2370 if (logging != MagickFalse)
2371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2372 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2376 if (image->storage_class == PseudoClass)
2379 Initialize image colormap.
2381 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2382 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2383 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2391 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2392 for (i=0; i < (ssize_t) image->colors; i++)
2394 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2395 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2396 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2404 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2407 for (i=0; i < (ssize_t) image->colors; i++)
2409 image->colormap[i].red=(Quantum) (i*scale);
2410 image->colormap[i].green=(Quantum) (i*scale);
2411 image->colormap[i].blue=(Quantum) (i*scale);
2416 Read image scanlines.
2418 if (image->delay != 0)
2419 mng_info->scenes_found++;
2420 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2421 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2422 (image_info->first_scene+image_info->number_scenes))))
2424 if (logging != MagickFalse)
2425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2426 " Skipping PNG image data for scene %.20g",(double)
2427 mng_info->scenes_found-1);
2428 png_destroy_read_struct(&ping,&ping_info,&end_info);
2429 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2430 UnlockSemaphoreInfo(png_semaphore);
2432 if (logging != MagickFalse)
2433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2434 " exit ReadOnePNGImage().");
2437 if (logging != MagickFalse)
2438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2439 " Reading PNG IDAT chunk(s)");
2441 png_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2442 ping_rowbytes*sizeof(*png_pixels));
2444 png_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2445 sizeof(*png_pixels));
2446 if (png_pixels == (unsigned char *) NULL)
2447 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2448 if (logging != MagickFalse)
2449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2450 " Converting PNG pixels to pixel packets");
2452 Convert PNG pixels to pixel packets.
2454 if (setjmp(png_jmpbuf(ping)))
2457 PNG image is corrupt.
2459 png_destroy_read_struct(&ping,&ping_info,&end_info);
2460 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2461 UnlockSemaphoreInfo(png_semaphore);
2463 if (quantum_info != (QuantumInfo *) NULL)
2464 quantum_info = DestroyQuantumInfo(quantum_info);
2465 if (png_pixels != (unsigned char *) NULL)
2466 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2467 if (logging != MagickFalse)
2468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2469 " exit ReadOnePNGImage() with error.");
2470 if (image != (Image *) NULL)
2472 InheritException(exception,&image->exception);
2475 return(GetFirstImageInList(image));
2477 quantum_info=AcquireQuantumInfo(image_info,image);
2478 if (quantum_info == (QuantumInfo *) NULL)
2479 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2480 if (image->storage_class == DirectClass)
2481 for (pass=0; pass < num_passes; pass++)
2484 Convert image to DirectClass pixel packets.
2486 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2490 depth=(ssize_t) ping_bit_depth;
2492 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2493 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2494 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2495 MagickTrue : MagickFalse;
2497 for (y=0; y < (ssize_t) image->rows; y++)
2500 row_offset=ping_rowbytes*y;
2503 png_read_row(ping,png_pixels+row_offset,NULL);
2504 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2505 if (q == (PixelPacket *) NULL)
2507 #if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2514 r=png_pixels+row_offset;
2516 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2518 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2522 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS)) &&
2523 (((*(p-2) << 8)|*(p-1)) == transparent_color.opacity))
2525 /* Cheap transparency */
2526 *r++=TransparentOpacity;
2532 else if (ping_color_type == PNG_COLOR_TYPE_RGB)
2534 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2535 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2543 if ((((*(p-6) << 8)|*(p-5)) == transparent_color.red) &&
2544 (((*(p-4) << 8)|*(p-3)) == transparent_color.green) &&
2545 (((*(p-2) << 8)|*(p-1)) == transparent_color.blue))
2547 /* Cheap transparency */
2548 *r++=TransparentOpacity;
2554 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2565 else if (ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2566 for (x=(ssize_t) (4*image->columns); x != 0; x--)
2571 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2572 for (x=(ssize_t) (2*image->columns); x != 0; x--)
2578 if (depth == 8 && ping_color_type == PNG_COLOR_TYPE_GRAY)
2579 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2580 GrayQuantum,png_pixels+row_offset);
2581 if (ping_color_type == PNG_COLOR_TYPE_GRAY ||
2582 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2584 quantum_info->depth=8;
2585 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2586 GrayAlphaQuantum,png_pixels+row_offset);
2588 else if (depth == 8 && ping_color_type == PNG_COLOR_TYPE_RGB)
2589 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2590 RGBQuantum,png_pixels+row_offset);
2591 else if (ping_color_type == PNG_COLOR_TYPE_RGB ||
2592 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2594 quantum_info->depth=8;
2595 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2596 RGBAQuantum,png_pixels+row_offset);
2598 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
2599 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2600 IndexQuantum,png_pixels+row_offset);
2601 #else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2602 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2603 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2604 GrayQuantum,png_pixels+row_offset,exception);
2605 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2606 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2607 GrayAlphaQuantum,png_pixels+row_offset,exception);
2608 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2609 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2610 RGBAQuantum,png_pixels+row_offset,exception);
2611 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2612 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2613 IndexQuantum,png_pixels+row_offset,exception);
2615 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2616 RGBQuantum,png_pixels+row_offset,exception);
2618 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2620 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2622 if (status == MagickFalse)
2625 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2628 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2630 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2631 if (status == MagickFalse)
2635 else /* image->storage_class != DirectClass */
2636 for (pass=0; pass < num_passes; pass++)
2645 Convert grayscale image to PseudoClass pixel packets.
2647 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2648 MagickTrue : MagickFalse;
2649 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2650 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2651 if (quantum_scanline == (Quantum *) NULL)
2652 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2653 for (y=0; y < (ssize_t) image->rows; y++)
2656 row_offset=ping_rowbytes*y;
2659 png_read_row(ping,png_pixels+row_offset,NULL);
2660 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2661 if (q == (PixelPacket *) NULL)
2663 indexes=GetAuthenticIndexQueue(image);
2664 p=png_pixels+row_offset;
2666 switch (ping_bit_depth)
2673 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
2675 for (bit=7; bit >= 0; bit--)
2676 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2679 if ((image->columns % 8) != 0)
2681 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
2682 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2688 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
2690 *r++=(*p >> 6) & 0x03;
2691 *r++=(*p >> 4) & 0x03;
2692 *r++=(*p >> 2) & 0x03;
2695 if ((image->columns % 4) != 0)
2697 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
2698 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2704 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
2706 *r++=(*p >> 4) & 0x0f;
2709 if ((image->columns % 2) != 0)
2710 *r++=(*p++ >> 4) & 0x0f;
2715 if (ping_color_type == 4)
2716 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2719 /* In image.h, OpaqueOpacity is 0
2720 * TransparentOpacity is QuantumRange
2721 * In a PNG datastream, Opaque is QuantumRange
2722 * and Transparent is 0.
2724 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2728 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2734 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2736 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2740 if (image->colors > 256)
2746 *r=(Quantum) quantum;
2748 if (ping_color_type == 4)
2750 quantum=((*p++) << 8);
2752 q->opacity=(Quantum) (QuantumRange-quantum);
2756 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2760 if (image->colors > 256)
2768 if (ping_color_type == 4)
2770 q->opacity=(*p << 8) | *(p+1);
2772 q->opacity=(Quantum) GetAlphaPixelComponent(q);
2776 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2778 p++; /* strip low byte */
2779 if (ping_color_type == 4)
2781 q->opacity=(Quantum) (QuantumRange-(*p++));
2794 Transfer image scanline.
2797 for (x=0; x < (ssize_t) image->columns; x++)
2798 indexes[x]=(IndexPacket) (*r++);
2799 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2801 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2803 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2805 if (status == MagickFalse)
2809 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2811 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2812 if (status == MagickFalse)
2815 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2817 if (quantum_info != (QuantumInfo *) NULL)
2818 quantum_info=DestroyQuantumInfo(quantum_info);
2819 if (image->storage_class == PseudoClass)
2825 image->matte=MagickFalse;
2826 (void) SyncImage(image);
2829 png_read_end(ping,ping_info);
2831 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2832 (ssize_t) image_info->first_scene && image->delay != 0)
2834 png_destroy_read_struct(&ping,&ping_info,&end_info);
2835 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
2837 (void) SetImageBackgroundColor(image);
2838 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2839 UnlockSemaphoreInfo(png_semaphore);
2841 if (logging != MagickFalse)
2842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2843 " exit ReadOnePNGImage() early.");
2846 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2852 Image has a transparent background.
2854 storage_class=image->storage_class;
2855 image->matte=MagickTrue;
2857 #if 1 /* balfour fix */
2858 /* From imagemagick discourse server, 5 Feb 2010 */
2860 if (storage_class == PseudoClass)
2862 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2864 for (x=0; x < ping_num_trans; x++)
2866 image->colormap[x].opacity =
2867 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2870 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2872 for (x=0; x < (int) image->colors; x++)
2874 if (ScaleQuantumToShort(image->colormap[x].red) ==
2875 transparent_color.opacity)
2877 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2881 (void) SyncImage(image);
2886 for (y=0; y < (ssize_t) image->rows; y++)
2888 image->storage_class=storage_class;
2889 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2890 if (q == (PixelPacket *) NULL)
2892 indexes=GetAuthenticIndexQueue(image);
2894 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2896 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2897 ScaleQuantumToShort(q->green) == transparent_color.green &&
2898 ScaleQuantumToShort(q->blue) == transparent_color.blue)
2899 q->opacity=(Quantum) TransparentOpacity;
2901 SetOpacityPixelComponent(q,OpaqueOpacity);
2904 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2909 #else /* not balfour */
2912 for (y=0; y < (ssize_t) image->rows; y++)
2914 image->storage_class=storage_class;
2915 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2916 if (q == (PixelPacket *) NULL)
2918 indexes=GetAuthenticIndexQueue(image);
2920 if (storage_class == PseudoClass)
2925 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2926 for (x=0; x < (ssize_t) image->columns; x++)
2928 indexpacket=indexes[x];
2929 if (indexpacket < ping_num_trans)
2930 q->opacity=ScaleCharToQuantum((unsigned char)
2931 (255-ping_trans_alpha[(ssize_t) indexpacket]));
2933 SetOpacityPixelComponent(q,OpaqueOpacity);
2936 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2937 for (x=0; x < (ssize_t) image->columns; x++)
2939 indexpacket=indexes[x];
2940 q->red=image->colormap[(ssize_t) indexpacket].red;
2943 if (ScaleQuantomToShort(q->red) == transparent_color.opacity)
2944 q->opacity=(Quantum) TransparentOpacity;
2946 SetOpacityPixelComponent(q,OpaqueOpacity);
2951 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2953 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2954 ScaleQuantumToShort(q->green) == transparent_color.green &&
2955 ScaleQuantumToShort(q->blue) == transparent_color.blue)
2956 q->opacity=(Quantum) TransparentOpacity;
2958 SetOpacityPixelComponent(q,OpaqueOpacity);
2961 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2964 #endif /* not balfour */
2965 image->storage_class=DirectClass;
2967 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2968 if (image->depth > 8)
2971 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2972 for (i=0; i < (ssize_t) num_text; i++)
2974 /* Check for a profile */
2976 if (logging != MagickFalse)
2977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2978 " Reading PNG text chunk");
2979 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2980 (void) png_read_raw_profile(image,image_info,text,(int) i);
2986 length=text[i].text_length;
2987 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2989 if (value == (char *) NULL)
2991 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2992 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2997 (void) ConcatenateMagickString(value,text[i].text,length+2);
2998 (void) SetImageProperty(image,text[i].key,value);
2999 if (logging != MagickFalse)
3000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3001 " Keyword: %s",text[i].key);
3002 value=DestroyString(value);
3005 #ifdef MNG_OBJECT_BUFFERS
3007 Store the object if necessary.
3009 if (object_id && !mng_info->frozen[object_id])
3011 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3014 create a new object buffer.
3016 mng_info->ob[object_id]=(MngBuffer *)
3017 AcquireAlignedMemory(1,sizeof(MngBuffer));
3018 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3020 mng_info->ob[object_id]->image=(Image *) NULL;
3021 mng_info->ob[object_id]->reference_count=1;
3024 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3025 mng_info->ob[object_id]->frozen)
3027 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3028 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3029 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3031 if (mng_info->ob[object_id]->frozen)
3032 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3033 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3034 "`%s'",image->filename);
3039 if (mng_info->ob[object_id]->image != (Image *) NULL)
3040 mng_info->ob[object_id]->image=DestroyImage
3041 (mng_info->ob[object_id]->image);
3042 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3044 if (mng_info->ob[object_id]->image != (Image *) NULL)
3045 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3047 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3048 ResourceLimitError,"Cloning image for object buffer failed",
3049 "`%s'",image->filename);
3050 if (ping_width > 250000L || ping_height > 250000L)
3051 png_error(ping,"PNG Image dimensions are too large.");
3052 mng_info->ob[object_id]->width=ping_width;
3053 mng_info->ob[object_id]->height=ping_height;
3054 mng_info->ob[object_id]->color_type=ping_color_type;
3055 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3056 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3057 mng_info->ob[object_id]->compression_method=
3058 ping_compression_method;
3059 mng_info->ob[object_id]->filter_method=ping_filter_method;
3060 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3069 Copy the PLTE to the object buffer.
3071 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3072 mng_info->ob[object_id]->plte_length=number_colors;
3073 for (i=0; i < number_colors; i++)
3075 mng_info->ob[object_id]->plte[i]=plte[i];
3079 mng_info->ob[object_id]->plte_length=0;
3084 Relinquish resources.
3086 png_destroy_read_struct(&ping,&ping_info,&end_info);
3088 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
3089 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3090 UnlockSemaphoreInfo(png_semaphore);
3093 if (logging != MagickFalse)
3094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3095 " exit ReadOnePNGImage()");
3098 /* end of reading one PNG image */
3101 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3114 magic_number[MaxTextExtent];
3126 assert(image_info != (const ImageInfo *) NULL);
3127 assert(image_info->signature == MagickSignature);
3128 if (image_info->debug != MagickFalse)
3129 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3130 image_info->filename);
3131 assert(exception != (ExceptionInfo *) NULL);
3132 assert(exception->signature == MagickSignature);
3133 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()");
3134 image=AcquireImage(image_info);
3135 mng_info=(MngInfo *) NULL;
3136 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3137 if (status == MagickFalse)
3138 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3140 Verify PNG signature.
3142 count=ReadBlob(image,8,(unsigned char *) magic_number);
3143 if (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3144 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3146 Allocate a MngInfo structure.
3148 have_mng_structure=MagickFalse;
3149 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
3150 if (mng_info == (MngInfo *) NULL)
3151 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3153 Initialize members of the MngInfo structure.
3155 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3156 mng_info->image=image;
3157 have_mng_structure=MagickTrue;
3160 image=ReadOnePNGImage(mng_info,image_info,exception);
3161 MngInfoFreeStruct(mng_info,&have_mng_structure);
3162 if (image == (Image *) NULL)
3164 if (previous != (Image *) NULL)
3166 if (previous->signature != MagickSignature)
3167 ThrowReaderException(CorruptImageError,"CorruptImage");
3168 (void) CloseBlob(previous);
3169 (void) DestroyImageList(previous);
3171 if (logging != MagickFalse)
3172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3173 "exit ReadPNGImage() with error");
3174 return((Image *) NULL);
3176 (void) CloseBlob(image);
3177 if ((image->columns == 0) || (image->rows == 0))
3179 if (logging != MagickFalse)
3180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3181 "exit ReadPNGImage() with error.");
3182 ThrowReaderException(CorruptImageError,"CorruptImage");
3184 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3186 (void) SetImageType(image,PaletteType);
3187 if (image->matte != MagickFalse)
3189 /* To do: Reduce to binary transparency */
3192 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3194 (void) SetImageType(image,TrueColorType);
3195 image->matte=MagickFalse;
3197 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3198 (void) SetImageType(image,TrueColorMatteType);
3199 if (logging != MagickFalse)
3200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3206 #if defined(JNG_SUPPORTED)
3208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3212 % R e a d O n e J N G I m a g e %
3216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3218 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3219 % (minus the 8-byte signature) and returns it. It allocates the memory
3220 % necessary for the new Image structure and returns a pointer to the new
3223 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3225 % The format of the ReadOneJNGImage method is:
3227 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3228 % ExceptionInfo *exception)
3230 % A description of each parameter follows:
3232 % o mng_info: Specifies a pointer to a MngInfo structure.
3234 % o image_info: the image info.
3236 % o exception: return any errors or warnings in this structure.
3239 static Image *ReadOneJNGImage(MngInfo *mng_info,
3240 const ImageInfo *image_info, ExceptionInfo *exception)
3264 jng_image_sample_depth,
3265 jng_image_compression_method,
3266 jng_image_interlace_method,
3267 jng_alpha_sample_depth,
3268 jng_alpha_compression_method,
3269 jng_alpha_filter_method,
3270 jng_alpha_interlace_method;
3272 register const PixelPacket
3279 register PixelPacket
3282 register unsigned char
3294 jng_alpha_compression_method=0;
3295 jng_alpha_sample_depth=8;
3299 alpha_image=(Image *) NULL;
3300 color_image=(Image *) NULL;
3301 alpha_image_info=(ImageInfo *) NULL;
3302 color_image_info=(ImageInfo *) NULL;
3304 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3305 " enter ReadOneJNGImage()");
3307 image=mng_info->image;
3308 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3311 Allocate next image structure.
3313 if (logging != MagickFalse)
3314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3315 " AcquireNextImage()");
3316 AcquireNextImage(image_info,image);
3317 if (GetNextImageInList(image) == (Image *) NULL)
3318 return((Image *) NULL);
3319 image=SyncNextImageInList(image);
3321 mng_info->image=image;
3324 Signature bytes have already been read.
3327 read_JSEP=MagickFalse;
3328 reading_idat=MagickFalse;
3329 skip_to_iend=MagickFalse;
3333 type[MaxTextExtent];
3342 Read a new JNG chunk.
3344 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3345 2*GetBlobSize(image));
3346 if (status == MagickFalse)
3349 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3350 length=ReadBlobMSBLong(image);
3351 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3353 if (logging != MagickFalse)
3354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3355 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3356 type[0],type[1],type[2],type[3],(double) length);
3358 if (length > PNG_UINT_31_MAX || count == 0)
3359 ThrowReaderException(CorruptImageError,"CorruptImage");
3361 chunk=(unsigned char *) NULL;
3364 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3365 if (chunk == (unsigned char *) NULL)
3366 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3367 for (i=0; i < (ssize_t) length; i++)
3368 chunk[i]=(unsigned char) ReadBlobByte(image);
3371 (void) ReadBlobMSBLong(image); /* read crc word */
3376 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3380 if (memcmp(type,mng_JHDR,4) == 0)
3384 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3385 (p[2] << 8) | p[3]);
3386 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3387 (p[6] << 8) | p[7]);
3388 jng_color_type=p[8];
3389 jng_image_sample_depth=p[9];
3390 jng_image_compression_method=p[10];
3391 jng_image_interlace_method=p[11];
3392 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3394 jng_alpha_sample_depth=p[12];
3395 jng_alpha_compression_method=p[13];
3396 jng_alpha_filter_method=p[14];
3397 jng_alpha_interlace_method=p[15];
3398 if (logging != MagickFalse)
3400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3401 " jng_width: %16lu",(unsigned long) jng_width);
3402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3403 " jng_width: %16lu",(unsigned long) jng_height);
3404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3405 " jng_color_type: %16d",jng_color_type);
3406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3407 " jng_image_sample_depth: %3d",
3408 jng_image_sample_depth);
3409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3410 " jng_image_compression_method:%3d",
3411 jng_image_compression_method);
3412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3413 " jng_image_interlace_method: %3d",
3414 jng_image_interlace_method);
3415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3416 " jng_alpha_sample_depth: %3d",
3417 jng_alpha_sample_depth);
3418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3419 " jng_alpha_compression_method:%3d",
3420 jng_alpha_compression_method);
3421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3422 " jng_alpha_filter_method: %3d",
3423 jng_alpha_filter_method);
3424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3425 " jng_alpha_interlace_method: %3d",
3426 jng_alpha_interlace_method);
3430 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3435 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3436 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3437 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3440 o create color_image
3441 o open color_blob, attached to color_image
3442 o if (color type has alpha)
3443 open alpha_blob, attached to alpha_image
3446 color_image_info=(ImageInfo *)AcquireAlignedMemory(1,sizeof(ImageInfo));
3447 if (color_image_info == (ImageInfo *) NULL)
3448 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3449 GetImageInfo(color_image_info);
3450 color_image=AcquireImage(color_image_info);
3451 if (color_image == (Image *) NULL)
3452 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3454 if (logging != MagickFalse)
3455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3456 " Creating color_blob.");
3457 (void) AcquireUniqueFilename(color_image->filename);
3458 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3460 if (status == MagickFalse)
3461 return((Image *) NULL);
3463 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3465 alpha_image_info=(ImageInfo *)
3466 AcquireAlignedMemory(1,sizeof(ImageInfo));
3467 if (alpha_image_info == (ImageInfo *) NULL)
3468 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3469 GetImageInfo(alpha_image_info);
3470 alpha_image=AcquireImage(alpha_image_info);
3471 if (alpha_image == (Image *) NULL)
3473 alpha_image=DestroyImage(alpha_image);
3474 ThrowReaderException(ResourceLimitError,
3475 "MemoryAllocationFailed");
3477 if (logging != MagickFalse)
3478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3479 " Creating alpha_blob.");
3480 (void) AcquireUniqueFilename(alpha_image->filename);
3481 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3483 if (status == MagickFalse)
3484 return((Image *) NULL);
3485 if (jng_alpha_compression_method == 0)
3490 if (logging != MagickFalse)
3491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3492 " Writing IHDR chunk to alpha_blob.");
3493 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3494 "\211PNG\r\n\032\n");
3495 (void) WriteBlobMSBULong(alpha_image,13L);
3496 PNGType(data,mng_IHDR);
3497 LogPNGChunk((int) logging,mng_IHDR,13L);
3498 PNGLong(data+4,jng_width);
3499 PNGLong(data+8,jng_height);
3500 data[12]=jng_alpha_sample_depth;
3501 data[13]=0; /* color_type gray */
3502 data[14]=0; /* compression method 0 */
3503 data[15]=0; /* filter_method 0 */
3504 data[16]=0; /* interlace_method 0 */
3505 (void) WriteBlob(alpha_image,17,data);
3506 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3509 reading_idat=MagickTrue;
3512 if (memcmp(type,mng_JDAT,4) == 0)
3515 Copy chunk to color_image->blob
3518 if (logging != MagickFalse)
3519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3520 " Copying JDAT chunk data to color_blob.");
3522 (void) WriteBlob(color_image,length,chunk);
3524 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3528 if (memcmp(type,mng_IDAT,4) == 0)
3534 Copy IDAT header and chunk data to alpha_image->blob
3537 if (image_info->ping == MagickFalse)
3539 if (logging != MagickFalse)
3540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3541 " Copying IDAT chunk data to alpha_blob.");
3543 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3544 PNGType(data,mng_IDAT);
3545 LogPNGChunk((int) logging,mng_IDAT,length);
3546 (void) WriteBlob(alpha_image,4,data);
3547 (void) WriteBlob(alpha_image,length,chunk);
3548 (void) WriteBlobMSBULong(alpha_image,
3549 crc32(crc32(0,data,4),chunk,(uInt) length));
3552 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3556 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3559 Copy chunk data to alpha_image->blob
3562 if (image_info->ping == MagickFalse)
3564 if (logging != MagickFalse)
3565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3566 " Copying JDAA chunk data to alpha_blob.");
3568 (void) WriteBlob(alpha_image,length,chunk);
3571 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3575 if (memcmp(type,mng_JSEP,4) == 0)
3577 read_JSEP=MagickTrue;
3579 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3583 if (memcmp(type,mng_bKGD,4) == 0)
3587 image->background_color.red=ScaleCharToQuantum(p[1]);
3588 image->background_color.green=image->background_color.red;
3589 image->background_color.blue=image->background_color.red;
3593 image->background_color.red=ScaleCharToQuantum(p[1]);
3594 image->background_color.green=ScaleCharToQuantum(p[3]);
3595 image->background_color.blue=ScaleCharToQuantum(p[5]);
3597 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3601 if (memcmp(type,mng_gAMA,4) == 0)
3604 image->gamma=((float) mng_get_long(p))*0.00001;
3605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3609 if (memcmp(type,mng_cHRM,4) == 0)
3613 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3614 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3615 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3616 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3617 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3618 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3619 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3620 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3622 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3626 if (memcmp(type,mng_sRGB,4) == 0)
3630 image->rendering_intent=
3631 PNG_RenderingIntent_to_Magick_RenderingIntent(p[0]);
3632 image->gamma=0.45455f;
3633 image->chromaticity.red_primary.x=0.6400f;
3634 image->chromaticity.red_primary.y=0.3300f;
3635 image->chromaticity.green_primary.x=0.3000f;
3636 image->chromaticity.green_primary.y=0.6000f;
3637 image->chromaticity.blue_primary.x=0.1500f;
3638 image->chromaticity.blue_primary.y=0.0600f;
3639 image->chromaticity.white_point.x=0.3127f;
3640 image->chromaticity.white_point.y=0.3290f;
3642 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3646 if (memcmp(type,mng_oFFs,4) == 0)
3650 image->page.x=mng_get_long(p);
3651 image->page.y=mng_get_long(&p[4]);
3652 if ((int) p[8] != 0)
3654 image->page.x/=10000;
3655 image->page.y/=10000;
3659 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3663 if (memcmp(type,mng_pHYs,4) == 0)
3667 image->x_resolution=(double) mng_get_long(p);
3668 image->y_resolution=(double) mng_get_long(&p[4]);
3669 if ((int) p[8] == PNG_RESOLUTION_METER)
3671 image->units=PixelsPerCentimeterResolution;
3672 image->x_resolution=image->x_resolution/100.0f;
3673 image->y_resolution=image->y_resolution/100.0f;
3676 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3681 if (memcmp(type,mng_iCCP,4) == 0)
3685 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3691 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3693 if (memcmp(type,mng_IEND,4))
3702 Finish up reading image data:
3704 o read main image from color_blob.
3708 o if (color_type has alpha)
3709 if alpha_encoding is PNG
3710 read secondary image from alpha_blob via ReadPNG
3711 if alpha_encoding is JPEG
3712 read secondary image from alpha_blob via ReadJPEG
3716 o copy intensity of secondary image into
3717 opacity samples of main image.
3719 o destroy the secondary image.
3722 (void) CloseBlob(color_image);
3723 if (logging != MagickFalse)
3724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3725 " Reading jng_image from color_blob.");
3726 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3727 color_image->filename);
3728 color_image_info->ping=MagickFalse; /* To do: avoid this */
3729 jng_image=ReadImage(color_image_info,exception);
3730 if (jng_image == (Image *) NULL)
3731 return((Image *) NULL);
3733 (void) RelinquishUniqueFileResource(color_image->filename);
3734 color_image=DestroyImage(color_image);
3735 color_image_info=DestroyImageInfo(color_image_info);
3737 if (jng_image == (Image *) NULL)
3738 return((Image *) NULL);
3740 if (logging != MagickFalse)
3741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3742 " Copying jng_image pixels to main image.");
3743 image->rows=jng_height;
3744 image->columns=jng_width;
3745 length=image->columns*sizeof(PixelPacket);
3746 for (y=0; y < (ssize_t) image->rows; y++)
3748 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3749 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3750 (void) CopyMagickMemory(q,s,length);
3751 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3754 jng_image=DestroyImage(jng_image);
3755 if (image_info->ping == MagickFalse)
3757 if (jng_color_type >= 12)
3759 if (jng_alpha_compression_method == 0)
3763 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3764 PNGType(data,mng_IEND);
3765 LogPNGChunk((int) logging,mng_IEND,0L);
3766 (void) WriteBlob(alpha_image,4,data);
3767 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3769 (void) CloseBlob(alpha_image);
3770 if (logging != MagickFalse)
3771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3772 " Reading opacity from alpha_blob.");
3774 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3775 "%s",alpha_image->filename);
3777 jng_image=ReadImage(alpha_image_info,exception);
3778 if (jng_image != (Image *) NULL)
3779 for (y=0; y < (ssize_t) image->rows; y++)
3781 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3783 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3784 if (image->matte != MagickFalse)
3785 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3786 q->opacity=(Quantum) QuantumRange-s->red;
3788 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3790 q->opacity=(Quantum) QuantumRange-s->red;
3791 if (q->opacity != OpaqueOpacity)
3792 image->matte=MagickTrue;
3794 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3797 (void) RelinquishUniqueFileResource(alpha_image->filename);
3798 alpha_image=DestroyImage(alpha_image);
3799 alpha_image_info=DestroyImageInfo(alpha_image_info);
3800 if (jng_image != (Image *) NULL)
3801 jng_image=DestroyImage(jng_image);
3808 if (mng_info->mng_type == 0)
3810 mng_info->mng_width=jng_width;
3811 mng_info->mng_height=jng_height;
3813 if (image->page.width == 0 && image->page.height == 0)
3815 image->page.width=jng_width;
3816 image->page.height=jng_height;
3818 if (image->page.x == 0 && image->page.y == 0)
3820 image->page.x=mng_info->x_off[mng_info->object_id];
3821 image->page.y=mng_info->y_off[mng_info->object_id];
3825 image->page.y=mng_info->y_off[mng_info->object_id];
3827 mng_info->image_found++;
3828 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3829 2*GetBlobSize(image));
3830 if (logging != MagickFalse)
3831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3832 " exit ReadOneJNGImage()");
3837 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3841 % R e a d J N G I m a g e %
3845 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3847 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3848 % (including the 8-byte signature) and returns it. It allocates the memory
3849 % necessary for the new Image structure and returns a pointer to the new
3852 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3854 % The format of the ReadJNGImage method is:
3856 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3859 % A description of each parameter follows:
3861 % o image_info: the image info.
3863 % o exception: return any errors or warnings in this structure.
3867 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3880 magic_number[MaxTextExtent];
3892 assert(image_info != (const ImageInfo *) NULL);
3893 assert(image_info->signature == MagickSignature);
3894 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3895 assert(exception != (ExceptionInfo *) NULL);
3896 assert(exception->signature == MagickSignature);
3897 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()");
3898 image=AcquireImage(image_info);
3899 mng_info=(MngInfo *) NULL;
3900 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3901 if (status == MagickFalse)
3902 return((Image *) NULL);
3903 if (LocaleCompare(image_info->magick,"JNG") != 0)
3904 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3906 Verify JNG signature.
3908 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3909 if (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3910 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3912 Allocate a MngInfo structure.
3914 have_mng_structure=MagickFalse;
3915 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(*mng_info));
3916 if (mng_info == (MngInfo *) NULL)
3917 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3919 Initialize members of the MngInfo structure.
3921 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3922 have_mng_structure=MagickTrue;
3924 mng_info->image=image;
3926 image=ReadOneJNGImage(mng_info,image_info,exception);
3927 MngInfoFreeStruct(mng_info,&have_mng_structure);
3928 if (image == (Image *) NULL)
3930 if (IsImageObject(previous) != MagickFalse)
3932 (void) CloseBlob(previous);
3933 (void) DestroyImageList(previous);
3935 if (logging != MagickFalse)
3936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3937 "exit ReadJNGImage() with error");
3938 return((Image *) NULL);
3940 (void) CloseBlob(image);
3941 if (image->columns == 0 || image->rows == 0)
3943 if (logging != MagickFalse)
3944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3945 "exit ReadJNGImage() with error");
3946 ThrowReaderException(CorruptImageError,"CorruptImage");
3948 if (logging != MagickFalse)
3949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3954 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3957 page_geometry[MaxTextExtent];
3990 #if defined(MNG_INSERT_LAYERS)
3992 mng_background_color;
3995 register unsigned char
4010 #if defined(MNG_INSERT_LAYERS)
4015 volatile unsigned int
4016 #ifdef MNG_OBJECT_BUFFERS
4017 mng_background_object=0,
4019 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4022 default_frame_timeout,
4024 #if defined(MNG_INSERT_LAYERS)
4030 /* These delays are all measured in image ticks_per_second,
4031 * not in MNG ticks_per_second
4034 default_frame_delay,
4038 #if defined(MNG_INSERT_LAYERS)
4047 previous_fb.bottom=0;
4049 previous_fb.right=0;
4051 default_fb.bottom=0;
4056 Set image_info->type=OptimizeType (new in version 5.4.0) to get the
4057 following optimizations:
4059 o 16-bit depth is reduced to 8 if all pixels contain samples whose
4060 high byte and low byte are identical.
4061 o Opaque matte channel is removed.
4062 o If matte channel is present but only one transparent color is
4063 present, RGB+tRNS is written instead of RGBA
4064 o Grayscale images are reduced to 1, 2, or 4 bit depth if
4065 this can be done without loss.
4066 o Palette is sorted to remove unused entries and to put a
4067 transparent color first, if PNG_SORT_PALETTE is also defined.
4073 assert(image_info != (const ImageInfo *) NULL);
4074 assert(image_info->signature == MagickSignature);
4075 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4076 assert(exception != (ExceptionInfo *) NULL);
4077 assert(exception->signature == MagickSignature);
4078 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()");
4079 image=AcquireImage(image_info);
4080 mng_info=(MngInfo *) NULL;
4081 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4082 if (status == MagickFalse)
4083 return((Image *) NULL);
4084 first_mng_object=MagickFalse;
4086 have_mng_structure=MagickFalse;
4088 Allocate a MngInfo structure.
4090 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
4091 if (mng_info == (MngInfo *) NULL)
4092 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4094 Initialize members of the MngInfo structure.
4096 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4097 mng_info->image=image;
4098 have_mng_structure=MagickTrue;
4099 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
4100 mng_info->optimize=image_info->type == OptimizeType;
4103 if (LocaleCompare(image_info->magick,"MNG") == 0)
4106 magic_number[MaxTextExtent];
4109 Verify MNG signature.
4111 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4112 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4113 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4115 Initialize some nonzero members of the MngInfo structure.
4117 for (i=0; i < MNG_MAX_OBJECTS; i++)
4119 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4120 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4122 mng_info->exists[0]=MagickTrue;
4124 first_mng_object=MagickTrue;
4126 #if defined(MNG_INSERT_LAYERS)
4127 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4129 default_frame_delay=0;
4130 default_frame_timeout=0;
4133 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4135 skip_to_iend=MagickFalse;
4136 term_chunk_found=MagickFalse;
4137 mng_info->framing_mode=1;
4138 #if defined(MNG_INSERT_LAYERS)
4139 mandatory_back=MagickFalse;
4141 #if defined(MNG_INSERT_LAYERS)
4142 mng_background_color=image->background_color;
4144 default_fb=mng_info->frame;
4145 previous_fb=mng_info->frame;
4149 type[MaxTextExtent];
4151 if (LocaleCompare(image_info->magick,"MNG") == 0)
4160 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4161 length=ReadBlobMSBLong(image);
4162 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4164 if (logging != MagickFalse)
4165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4166 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4167 type[0],type[1],type[2],type[3],(double) length);
4169 if (length > PNG_UINT_31_MAX)
4172 ThrowReaderException(CorruptImageError,"CorruptImage");
4174 chunk=(unsigned char *) NULL;
4177 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4178 if (chunk == (unsigned char *) NULL)
4179 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4180 for (i=0; i < (ssize_t) length; i++)
4181 chunk[i]=(unsigned char) ReadBlobByte(image);
4184 (void) ReadBlobMSBLong(image); /* read crc word */
4186 #if !defined(JNG_SUPPORTED)
4187 if (memcmp(type,mng_JHDR,4) == 0)
4189 skip_to_iend=MagickTrue;
4190 if (mng_info->jhdr_warning == 0)
4191 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4192 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4193 mng_info->jhdr_warning++;
4196 if (memcmp(type,mng_DHDR,4) == 0)
4198 skip_to_iend=MagickTrue;
4199 if (mng_info->dhdr_warning == 0)
4200 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4201 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4202 mng_info->dhdr_warning++;
4204 if (memcmp(type,mng_MEND,4) == 0)
4208 if (memcmp(type,mng_IEND,4) == 0)
4209 skip_to_iend=MagickFalse;
4211 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4212 if (logging != MagickFalse)
4213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4217 if (memcmp(type,mng_MHDR,4) == 0)
4219 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4220 (p[2] << 8) | p[3]);
4221 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4222 (p[6] << 8) | p[7]);
4223 if (logging != MagickFalse)
4225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4226 " MNG width: %.20g",(double) mng_info->mng_width);
4227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4228 " MNG height: %.20g",(double) mng_info->mng_height);
4231 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4232 if (mng_info->ticks_per_second == 0)
4233 default_frame_delay=0;
4235 default_frame_delay=1UL*image->ticks_per_second/
4236 mng_info->ticks_per_second;
4237 frame_delay=default_frame_delay;
4242 simplicity=(size_t) mng_get_long(p);
4244 mng_type=1; /* Full MNG */
4245 if ((simplicity != 0) && ((simplicity | 11) == 11))
4246 mng_type=2; /* LC */
4247 if ((simplicity != 0) && ((simplicity | 9) == 9))
4248 mng_type=3; /* VLC */
4249 #if defined(MNG_INSERT_LAYERS)
4251 insert_layers=MagickTrue;
4253 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4256 Allocate next image structure.
4258 AcquireNextImage(image_info,image);
4259 if (GetNextImageInList(image) == (Image *) NULL)
4260 return((Image *) NULL);
4261 image=SyncNextImageInList(image);
4262 mng_info->image=image;
4265 if ((mng_info->mng_width > 65535L) ||
4266 (mng_info->mng_height > 65535L))
4267 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4268 (void) FormatMagickString(page_geometry,MaxTextExtent,
4269 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4270 mng_info->mng_height);
4271 mng_info->frame.left=0;
4272 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4273 mng_info->frame.top=0;
4274 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4275 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4276 for (i=0; i < MNG_MAX_OBJECTS; i++)
4277 mng_info->object_clip[i]=mng_info->frame;
4278 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4282 if (memcmp(type,mng_TERM,4) == 0)
4292 final_delay=(png_uint_32) mng_get_long(&p[2]);
4293 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4294 if (mng_iterations == PNG_UINT_31_MAX)
4296 image->iterations=mng_iterations;
4297 term_chunk_found=MagickTrue;
4299 if (logging != MagickFalse)
4301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4302 " repeat=%d",repeat);
4303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4304 " final_delay=%.20g",(double) final_delay);
4305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4306 " image->iterations=%.20g",(double) image->iterations);
4308 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4311 if (memcmp(type,mng_DEFI,4) == 0)
4314 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4315 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4317 object_id=(p[0] << 8) | p[1];
4318 if (mng_type == 2 && object_id != 0)
4319 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4320 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4322 if (object_id > MNG_MAX_OBJECTS)
4325 Instead ofsuing a warning we should allocate a larger
4326 MngInfo structure and continue.
4328 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4329 CoderError,"object id too large","`%s'",image->filename);
4330 object_id=MNG_MAX_OBJECTS;
4332 if (mng_info->exists[object_id])
4333 if (mng_info->frozen[object_id])
4335 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4336 (void) ThrowMagickException(&image->exception,
4337 GetMagickModule(),CoderError,
4338 "DEFI cannot redefine a frozen MNG object","`%s'",
4342 mng_info->exists[object_id]=MagickTrue;
4344 mng_info->invisible[object_id]=p[2];
4346 Extract object offset info.
4350 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) | (p[5] << 16) |
4351 (p[6] << 8) | p[7]);
4352 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) | (p[9] << 16) |
4353 (p[10] << 8) | p[11]);
4354 if (logging != MagickFalse)
4356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4357 " x_off[%d]: %.20g",object_id,(double)
4358 mng_info->x_off[object_id]);
4359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4360 " y_off[%d]: %.20g",object_id,(double)
4361 mng_info->y_off[object_id]);
4365 Extract object clipping info.
4368 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4370 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4373 if (memcmp(type,mng_bKGD,4) == 0)
4375 mng_info->have_global_bkgd=MagickFalse;
4378 mng_info->mng_global_bkgd.red=
4379 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4380 mng_info->mng_global_bkgd.green=
4381 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4382 mng_info->mng_global_bkgd.blue=
4383 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4384 mng_info->have_global_bkgd=MagickTrue;
4386 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4389 if (memcmp(type,mng_BACK,4) == 0)
4391 #if defined(MNG_INSERT_LAYERS)
4393 mandatory_back=p[6];
4396 if (mandatory_back && length > 5)
4398 mng_background_color.red=
4399 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4400 mng_background_color.green=
4401 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4402 mng_background_color.blue=
4403 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4404 mng_background_color.opacity=OpaqueOpacity;
4406 #ifdef MNG_OBJECT_BUFFERS
4408 mng_background_object=(p[7] << 8) | p[8];
4411 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4414 if (memcmp(type,mng_PLTE,4) == 0)
4419 if (length && (length < 769))
4421 if (mng_info->global_plte == (png_colorp) NULL)
4422 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4423 sizeof(*mng_info->global_plte));
4424 for (i=0; i < (ssize_t) (length/3); i++)
4426 mng_info->global_plte[i].red=p[3*i];
4427 mng_info->global_plte[i].green=p[3*i+1];
4428 mng_info->global_plte[i].blue=p[3*i+2];
4430 mng_info->global_plte_length=(unsigned int) (length/3);
4433 for ( ; i < 256; i++)
4435 mng_info->global_plte[i].red=i;
4436 mng_info->global_plte[i].green=i;
4437 mng_info->global_plte[i].blue=i;
4440 mng_info->global_plte_length=256;
4443 mng_info->global_plte_length=0;
4444 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4447 if (memcmp(type,mng_tRNS,4) == 0)
4449 /* read global tRNS */
4452 for (i=0; i < (ssize_t) length; i++)
4453 mng_info->global_trns[i]=p[i];
4456 for ( ; i < 256; i++)
4457 mng_info->global_trns[i]=255;
4459 mng_info->global_trns_length=(unsigned int) length;
4460 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4463 if (memcmp(type,mng_gAMA,4) == 0)
4470 igamma=mng_get_long(p);
4471 mng_info->global_gamma=((float) igamma)*0.00001;
4472 mng_info->have_global_gama=MagickTrue;
4475 mng_info->have_global_gama=MagickFalse;
4476 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4480 if (memcmp(type,mng_cHRM,4) == 0)
4487 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4488 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4489 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4490 mng_info->global_chrm.red_primary.y=0.00001*
4491 mng_get_long(&p[12]);
4492 mng_info->global_chrm.green_primary.x=0.00001*
4493 mng_get_long(&p[16]);
4494 mng_info->global_chrm.green_primary.y=0.00001*
4495 mng_get_long(&p[20]);
4496 mng_info->global_chrm.blue_primary.x=0.00001*
4497 mng_get_long(&p[24]);
4498 mng_info->global_chrm.blue_primary.y=0.00001*
4499 mng_get_long(&p[28]);
4500 mng_info->have_global_chrm=MagickTrue;
4503 mng_info->have_global_chrm=MagickFalse;
4504 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4507 if (memcmp(type,mng_sRGB,4) == 0)
4514 mng_info->global_srgb_intent=
4515 PNG_RenderingIntent_to_Magick_RenderingIntent(p[0]);
4516 mng_info->have_global_srgb=MagickTrue;
4519 mng_info->have_global_srgb=MagickFalse;
4520 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4523 if (memcmp(type,mng_iCCP,4) == 0)
4531 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4534 if (memcmp(type,mng_FRAM,4) == 0)
4537 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4538 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4540 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4541 image->delay=frame_delay;
4542 frame_delay=default_frame_delay;
4543 frame_timeout=default_frame_timeout;
4547 mng_info->framing_mode=p[0];
4548 if (logging != MagickFalse)
4549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4550 " Framing_mode=%d",mng_info->framing_mode);
4554 Note the delay and frame clipping boundaries.
4556 p++; /* framing mode */
4557 while (*p && ((p-chunk) < (ssize_t) length))
4558 p++; /* frame name */
4559 p++; /* frame name terminator */
4560 if ((p-chunk) < (ssize_t) (length-4))
4567 change_delay=(*p++);
4568 change_timeout=(*p++);
4569 change_clipping=(*p++);
4570 p++; /* change_sync */
4573 frame_delay=1UL*image->ticks_per_second*
4575 if (mng_info->ticks_per_second != 0)
4576 frame_delay/=mng_info->ticks_per_second;
4578 frame_delay=PNG_UINT_31_MAX;
4579 if (change_delay == 2)
4580 default_frame_delay=frame_delay;
4582 if (logging != MagickFalse)
4583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4584 " Framing_delay=%.20g",(double) frame_delay);
4588 frame_timeout=1UL*image->ticks_per_second*
4590 if (mng_info->ticks_per_second != 0)
4591 frame_timeout/=mng_info->ticks_per_second;
4593 frame_timeout=PNG_UINT_31_MAX;
4594 if (change_delay == 2)
4595 default_frame_timeout=frame_timeout;
4597 if (logging != MagickFalse)
4598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4599 " Framing_timeout=%.20g",(double) frame_timeout);
4601 if (change_clipping)
4603 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4606 if (logging != MagickFalse)
4607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4608 " Frame_clipping: L=%.20g R=%.20g T=%.20g B=%.20g",
4609 (double) fb.left,(double) fb.right,(double) fb.top,
4610 (double) fb.bottom);
4611 if (change_clipping == 2)
4617 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4618 subframe_width=(size_t) (mng_info->clip.right
4619 -mng_info->clip.left);
4620 subframe_height=(size_t) (mng_info->clip.bottom
4621 -mng_info->clip.top);
4623 Insert a background layer behind the frame if framing_mode is 4.
4625 #if defined(MNG_INSERT_LAYERS)
4626 if (logging != MagickFalse)
4627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4628 " subframe_width=%.20g, subframe_height=%.20g",(double)
4629 subframe_width,(double) subframe_height);
4630 if (insert_layers && (mng_info->framing_mode == 4) &&
4631 (subframe_width) && (subframe_height))
4634 Allocate next image structure.
4636 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4638 AcquireNextImage(image_info,image);
4639 if (GetNextImageInList(image) == (Image *) NULL)
4641 image=DestroyImageList(image);
4642 MngInfoFreeStruct(mng_info,&have_mng_structure);
4643 return((Image *) NULL);
4645 image=SyncNextImageInList(image);
4647 mng_info->image=image;
4648 if (term_chunk_found)
4650 image->start_loop=MagickTrue;
4651 image->iterations=mng_iterations;
4652 term_chunk_found=MagickFalse;
4655 image->start_loop=MagickFalse;
4656 image->columns=subframe_width;
4657 image->rows=subframe_height;
4658 image->page.width=subframe_width;
4659 image->page.height=subframe_height;
4660 image->page.x=mng_info->clip.left;
4661 image->page.y=mng_info->clip.top;
4662 image->background_color=mng_background_color;
4663 image->matte=MagickFalse;
4665 (void) SetImageBackgroundColor(image);
4666 if (logging != MagickFalse)
4667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4668 " Inserted background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4669 (double) mng_info->clip.left,(double) mng_info->clip.right,
4670 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4673 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4676 if (memcmp(type,mng_CLIP,4) == 0)
4685 first_object=(p[0] << 8) | p[1];
4686 last_object=(p[2] << 8) | p[3];
4687 for (i=(int) first_object; i <= (int) last_object; i++)
4689 if (mng_info->exists[i] && !mng_info->frozen[i])
4694 box=mng_info->object_clip[i];
4695 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4698 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4701 if (memcmp(type,mng_SAVE,4) == 0)
4703 for (i=1; i < MNG_MAX_OBJECTS; i++)
4704 if (mng_info->exists[i])
4706 mng_info->frozen[i]=MagickTrue;
4707 #ifdef MNG_OBJECT_BUFFERS
4708 if (mng_info->ob[i] != (MngBuffer *) NULL)
4709 mng_info->ob[i]->frozen=MagickTrue;
4713 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4717 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4722 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4724 for (i=1; i < MNG_MAX_OBJECTS; i++)
4725 MngInfoDiscardObject(mng_info,i);
4732 for (j=0; j < (ssize_t) length; j+=2)
4734 i=p[j] << 8 | p[j+1];
4735 MngInfoDiscardObject(mng_info,i);
4739 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4742 if (memcmp(type,mng_MOVE,4) == 0)
4751 first_object=(p[0] << 8) | p[1];
4752 last_object=(p[2] << 8) | p[3];
4753 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4755 if (mng_info->exists[i] && !mng_info->frozen[i])
4763 old_pair.a=mng_info->x_off[i];
4764 old_pair.b=mng_info->y_off[i];
4765 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4766 mng_info->x_off[i]=new_pair.a;
4767 mng_info->y_off[i]=new_pair.b;
4770 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4774 if (memcmp(type,mng_LOOP,4) == 0)
4776 ssize_t loop_iters=1;
4777 loop_level=chunk[0];
4778 mng_info->loop_active[loop_level]=1; /* mark loop active */
4780 Record starting point.
4782 loop_iters=mng_get_long(&chunk[1]);
4783 if (logging != MagickFalse)
4784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4785 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
4786 (double) loop_iters);
4787 if (loop_iters == 0)
4788 skipping_loop=loop_level;
4791 mng_info->loop_jump[loop_level]=TellBlob(image);
4792 mng_info->loop_count[loop_level]=loop_iters;
4794 mng_info->loop_iteration[loop_level]=0;
4795 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4798 if (memcmp(type,mng_ENDL,4) == 0)
4800 loop_level=chunk[0];
4801 if (skipping_loop > 0)
4803 if (skipping_loop == loop_level)
4806 Found end of zero-iteration loop.
4809 mng_info->loop_active[loop_level]=0;
4814 if (mng_info->loop_active[loop_level] == 1)
4816 mng_info->loop_count[loop_level]--;
4817 mng_info->loop_iteration[loop_level]++;
4818 if (logging != MagickFalse)
4819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4820 " ENDL: LOOP level %.20g has %.20g remaining iterations ",
4821 (double) loop_level,(double)
4822 mng_info->loop_count[loop_level]);
4823 if (mng_info->loop_count[loop_level] != 0)
4825 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4828 ThrowReaderException(CorruptImageError,
4829 "ImproperImageHeader");
4839 mng_info->loop_active[loop_level]=0;
4841 for (i=0; i < loop_level; i++)
4842 if (mng_info->loop_active[i] == 1)
4843 last_level=(short) i;
4844 loop_level=last_level;
4848 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4851 if (memcmp(type,mng_CLON,4) == 0)
4853 if (mng_info->clon_warning == 0)
4854 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4855 CoderError,"CLON is not implemented yet","`%s'",
4857 mng_info->clon_warning++;
4859 if (memcmp(type,mng_MAGN,4) == 0)
4874 magn_first=(p[0] << 8) | p[1];
4878 magn_last=(p[2] << 8) | p[3];
4880 magn_last=magn_first;
4881 #ifndef MNG_OBJECT_BUFFERS
4882 if (magn_first || magn_last)
4883 if (mng_info->magn_warning == 0)
4885 (void) ThrowMagickException(&image->exception,
4886 GetMagickModule(),CoderError,
4887 "MAGN is not implemented yet for nonzero objects",
4888 "`%s'",image->filename);
4889 mng_info->magn_warning++;
4898 magn_mx=(p[5] << 8) | p[6];
4905 magn_my=(p[7] << 8) | p[8];
4912 magn_ml=(p[9] << 8) | p[10];
4919 magn_mr=(p[11] << 8) | p[12];
4926 magn_mt=(p[13] << 8) | p[14];
4933 magn_mb=(p[15] << 8) | p[16];
4942 magn_methy=magn_methx;
4944 if (magn_methx > 5 || magn_methy > 5)
4945 if (mng_info->magn_warning == 0)
4947 (void) ThrowMagickException(&image->exception,
4948 GetMagickModule(),CoderError,
4949 "Unknown MAGN method in MNG datastream","`%s'",
4951 mng_info->magn_warning++;
4953 #ifdef MNG_OBJECT_BUFFERS
4954 /* Magnify existing objects in the range magn_first to magn_last */
4956 if (magn_first == 0 || magn_last == 0)
4958 /* Save the magnification factors for object 0 */
4959 mng_info->magn_mb=magn_mb;
4960 mng_info->magn_ml=magn_ml;
4961 mng_info->magn_mr=magn_mr;
4962 mng_info->magn_mt=magn_mt;
4963 mng_info->magn_mx=magn_mx;
4964 mng_info->magn_my=magn_my;
4965 mng_info->magn_methx=magn_methx;
4966 mng_info->magn_methy=magn_methy;
4969 if (memcmp(type,mng_PAST,4) == 0)
4971 if (mng_info->past_warning == 0)
4972 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4973 CoderError,"PAST is not implemented yet","`%s'",
4975 mng_info->past_warning++;
4977 if (memcmp(type,mng_SHOW,4) == 0)
4979 if (mng_info->show_warning == 0)
4980 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4981 CoderError,"SHOW is not implemented yet","`%s'",
4983 mng_info->show_warning++;
4985 if (memcmp(type,mng_sBIT,4) == 0)
4988 mng_info->have_global_sbit=MagickFalse;
4991 mng_info->global_sbit.gray=p[0];
4992 mng_info->global_sbit.red=p[0];
4993 mng_info->global_sbit.green=p[1];
4994 mng_info->global_sbit.blue=p[2];
4995 mng_info->global_sbit.alpha=p[3];
4996 mng_info->have_global_sbit=MagickTrue;
4999 if (memcmp(type,mng_pHYs,4) == 0)
5003 mng_info->global_x_pixels_per_unit=
5004 (size_t) mng_get_long(p);
5005 mng_info->global_y_pixels_per_unit=
5006 (size_t) mng_get_long(&p[4]);
5007 mng_info->global_phys_unit_type=p[8];
5008 mng_info->have_global_phys=MagickTrue;
5011 mng_info->have_global_phys=MagickFalse;
5013 if (memcmp(type,mng_pHYg,4) == 0)
5015 if (mng_info->phyg_warning == 0)
5016 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5017 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5018 mng_info->phyg_warning++;
5020 if (memcmp(type,mng_BASI,4) == 0)
5022 skip_to_iend=MagickTrue;
5023 if (mng_info->basi_warning == 0)
5024 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5025 CoderError,"BASI is not implemented yet","`%s'",
5027 mng_info->basi_warning++;
5028 #ifdef MNG_BASI_SUPPORTED
5029 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5030 (p[2] << 8) | p[3]);
5031 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5032 (p[6] << 8) | p[7]);
5033 basi_color_type=p[8];
5034 basi_compression_method=p[9];
5035 basi_filter_type=p[10];
5036 basi_interlace_method=p[11];
5038 basi_red=(p[12] << 8) & p[13];
5042 basi_green=(p[14] << 8) & p[15];
5046 basi_blue=(p[16] << 8) & p[17];
5050 basi_alpha=(p[18] << 8) & p[19];
5053 if (basi_sample_depth == 16)
5059 basi_viewable=p[20];
5063 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5066 if (memcmp(type,mng_IHDR,4)
5067 #if defined(JNG_SUPPORTED)
5068 && memcmp(type,mng_JHDR,4)
5072 /* Not an IHDR or JHDR chunk */
5074 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5078 if (logging != MagickFalse)
5079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5080 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5081 mng_info->exists[object_id]=MagickTrue;
5082 mng_info->viewable[object_id]=MagickTrue;
5083 if (mng_info->invisible[object_id])
5085 if (logging != MagickFalse)
5086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5087 " Skipping invisible object");
5088 skip_to_iend=MagickTrue;
5089 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5092 #if defined(MNG_INSERT_LAYERS)
5094 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5095 image_width=(size_t) mng_get_long(p);
5096 image_height=(size_t) mng_get_long(&p[4]);
5098 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5101 Insert a transparent background layer behind the entire animation
5102 if it is not full screen.
5104 #if defined(MNG_INSERT_LAYERS)
5105 if (insert_layers && mng_type && first_mng_object)
5107 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5108 (image_width < mng_info->mng_width) ||
5109 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5110 (image_height < mng_info->mng_height) ||
5111 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5113 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5116 Allocate next image structure.
5118 AcquireNextImage(image_info,image);
5119 if (GetNextImageInList(image) == (Image *) NULL)
5121 image=DestroyImageList(image);
5122 MngInfoFreeStruct(mng_info,&have_mng_structure);
5123 return((Image *) NULL);
5125 image=SyncNextImageInList(image);
5127 mng_info->image=image;
5128 if (term_chunk_found)
5130 image->start_loop=MagickTrue;
5131 image->iterations=mng_iterations;
5132 term_chunk_found=MagickFalse;
5135 image->start_loop=MagickFalse;
5137 Make a background rectangle.
5140 image->columns=mng_info->mng_width;
5141 image->rows=mng_info->mng_height;
5142 image->page.width=mng_info->mng_width;
5143 image->page.height=mng_info->mng_height;
5146 image->background_color=mng_background_color;
5147 (void) SetImageBackgroundColor(image);
5148 if (logging != MagickFalse)
5149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5150 " Inserted transparent background layer, W=%.20g, H=%.20g",
5151 (double) mng_info->mng_width,(double) mng_info->mng_height);
5155 Insert a background layer behind the upcoming image if
5156 framing_mode is 3, and we haven't already inserted one.
5158 if (insert_layers && (mng_info->framing_mode == 3) &&
5159 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5160 (simplicity & 0x08)))
5162 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5165 Allocate next image structure.
5167 AcquireNextImage(image_info,image);
5168 if (GetNextImageInList(image) == (Image *) NULL)
5170 image=DestroyImageList(image);
5171 MngInfoFreeStruct(mng_info,&have_mng_structure);
5172 return((Image *) NULL);
5174 image=SyncNextImageInList(image);
5176 mng_info->image=image;
5177 if (term_chunk_found)
5179 image->start_loop=MagickTrue;
5180 image->iterations=mng_iterations;
5181 term_chunk_found=MagickFalse;
5184 image->start_loop=MagickFalse;
5186 image->columns=subframe_width;
5187 image->rows=subframe_height;
5188 image->page.width=subframe_width;
5189 image->page.height=subframe_height;
5190 image->page.x=mng_info->clip.left;
5191 image->page.y=mng_info->clip.top;
5192 image->background_color=mng_background_color;
5193 image->matte=MagickFalse;
5194 (void) SetImageBackgroundColor(image);
5195 if (logging != MagickFalse)
5196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5197 " Inserted background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5198 (double) mng_info->clip.left,(double) mng_info->clip.right,
5199 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5201 #endif /* MNG_INSERT_LAYERS */
5202 first_mng_object=MagickFalse;
5203 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5206 Allocate next image structure.
5208 AcquireNextImage(image_info,image);
5209 if (GetNextImageInList(image) == (Image *) NULL)
5211 image=DestroyImageList(image);
5212 MngInfoFreeStruct(mng_info,&have_mng_structure);
5213 return((Image *) NULL);
5215 image=SyncNextImageInList(image);
5217 mng_info->image=image;
5218 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5219 GetBlobSize(image));
5220 if (status == MagickFalse)
5222 if (term_chunk_found)
5224 image->start_loop=MagickTrue;
5225 term_chunk_found=MagickFalse;
5228 image->start_loop=MagickFalse;
5229 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5231 image->delay=frame_delay;
5232 frame_delay=default_frame_delay;
5236 image->page.width=mng_info->mng_width;
5237 image->page.height=mng_info->mng_height;
5238 image->page.x=mng_info->x_off[object_id];
5239 image->page.y=mng_info->y_off[object_id];
5240 image->iterations=mng_iterations;
5242 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5244 if (logging != MagickFalse)
5245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5246 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5248 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5250 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5254 mng_info->image=image;
5255 mng_info->mng_type=mng_type;
5256 mng_info->object_id=object_id;
5258 if (memcmp(type,mng_IHDR,4) == 0)
5259 image=ReadOnePNGImage(mng_info,image_info,exception);
5260 #if defined(JNG_SUPPORTED)
5262 image=ReadOneJNGImage(mng_info,image_info,exception);
5265 if (image == (Image *) NULL)
5267 if (IsImageObject(previous) != MagickFalse)
5269 (void) DestroyImageList(previous);
5270 (void) CloseBlob(previous);
5272 MngInfoFreeStruct(mng_info,&have_mng_structure);
5273 return((Image *) NULL);
5275 if (image->columns == 0 || image->rows == 0)
5277 (void) CloseBlob(image);
5278 image=DestroyImageList(image);
5279 MngInfoFreeStruct(mng_info,&have_mng_structure);
5280 return((Image *) NULL);
5282 mng_info->image=image;
5289 if (mng_info->magn_methx || mng_info->magn_methy)
5295 if (logging != MagickFalse)
5296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5297 " Processing MNG MAGN chunk");
5299 if (mng_info->magn_methx == 1)
5301 magnified_width=mng_info->magn_ml;
5302 if (image->columns > 1)
5303 magnified_width += mng_info->magn_mr;
5304 if (image->columns > 2)
5305 magnified_width += (png_uint_32) ((image->columns-2)*(mng_info->magn_mx));
5309 magnified_width=(png_uint_32) image->columns;
5310 if (image->columns > 1)
5311 magnified_width += mng_info->magn_ml-1;
5312 if (image->columns > 2)
5313 magnified_width += mng_info->magn_mr-1;
5314 if (image->columns > 3)
5315 magnified_width += (png_uint_32) ((image->columns-3)*(mng_info->magn_mx-1));
5317 if (mng_info->magn_methy == 1)
5319 magnified_height=mng_info->magn_mt;
5320 if (image->rows > 1)
5321 magnified_height += mng_info->magn_mb;
5322 if (image->rows > 2)
5323 magnified_height += (image->rows-2)*(mng_info->magn_my);
5327 magnified_height=(png_uint_32) image->rows;
5328 if (image->rows > 1)
5329 magnified_height += mng_info->magn_mt-1;
5330 if (image->rows > 2)
5331 magnified_height += mng_info->magn_mb-1;
5332 if (image->rows > 3)
5333 magnified_height += (png_uint_32) ((image->rows-3)*(mng_info->magn_my-1));
5335 if (magnified_height > image->rows ||
5336 magnified_width > image->columns)
5351 register PixelPacket
5364 Allocate next image structure.
5366 if (logging != MagickFalse)
5367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5368 " Allocate magnified image");
5369 AcquireNextImage(image_info,image);
5370 if (GetNextImageInList(image) == (Image *) NULL)
5372 image=DestroyImageList(image);
5373 MngInfoFreeStruct(mng_info,&have_mng_structure);
5374 return((Image *) NULL);
5377 large_image=SyncNextImageInList(image);
5379 large_image->columns=magnified_width;
5380 large_image->rows=magnified_height;
5382 magn_methx=mng_info->magn_methx;
5383 magn_methy=mng_info->magn_methy;
5385 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5386 #define QM unsigned short
5387 if (magn_methx != 1 || magn_methy != 1)
5390 Scale pixels to unsigned shorts to prevent
5391 overflow of intermediate values of interpolations
5393 for (y=0; y < (ssize_t) image->rows; y++)
5395 q=GetAuthenticPixels(image,0,y,image->columns,1,
5397 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5399 q->red=ScaleQuantumToShort(q->red);
5400 q->green=ScaleQuantumToShort(q->green);
5401 q->blue=ScaleQuantumToShort(q->blue);
5402 q->opacity=ScaleQuantumToShort(q->opacity);
5405 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5413 if (image->matte != MagickFalse)
5414 (void) SetImageBackgroundColor(large_image);
5417 large_image->background_color.opacity=OpaqueOpacity;
5418 (void) SetImageBackgroundColor(large_image);
5419 if (magn_methx == 4)
5421 if (magn_methx == 5)
5423 if (magn_methy == 4)
5425 if (magn_methy == 5)
5429 /* magnify the rows into the right side of the large image */
5431 if (logging != MagickFalse)
5432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5433 " Magnify the rows to %.20g",(double) large_image->rows);
5434 m=(ssize_t) mng_info->magn_mt;
5436 length=(size_t) image->columns;
5437 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5438 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5439 if ((prev == (PixelPacket *) NULL) ||
5440 (next == (PixelPacket *) NULL))
5442 image=DestroyImageList(image);
5443 MngInfoFreeStruct(mng_info,&have_mng_structure);
5444 ThrowReaderException(ResourceLimitError,
5445 "MemoryAllocationFailed");
5447 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5448 (void) CopyMagickMemory(next,n,length);
5449 for (y=0; y < (ssize_t) image->rows; y++)
5452 m=(ssize_t) mng_info->magn_mt;
5453 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5454 m=(ssize_t) mng_info->magn_mb;
5455 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5456 m=(ssize_t) mng_info->magn_mb;
5457 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5460 m=(ssize_t) mng_info->magn_my;
5464 if (y < (ssize_t) image->rows-1)
5466 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5468 (void) CopyMagickMemory(next,n,length);
5470 for (i=0; i < m; i++, yy++)
5472 register PixelPacket
5475 assert(yy < (ssize_t) large_image->rows);
5478 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5480 q+=(large_image->columns-image->columns);
5481 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5483 /* TO DO: get color as function of indexes[x] */
5485 if (image->storage_class == PseudoClass)
5490 if (magn_methy <= 1)
5492 *q=(*pixels); /* replicate previous */
5494 else if (magn_methy == 2 || magn_methy == 4)
5501 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5502 -(*pixels).red)+m))/((ssize_t) (m*2))
5504 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5505 -(*pixels).green)+m))/((ssize_t) (m*2))
5507 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5508 -(*pixels).blue)+m))/((ssize_t) (m*2))
5510 if (image->matte != MagickFalse)
5511 (*q).opacity=(QM) (((ssize_t)
5513 -(*pixels).opacity)+m))
5514 /((ssize_t) (m*2))+(*pixels).opacity);
5516 if (magn_methy == 4)
5518 /* Replicate nearest */
5519 if (i <= ((m+1) << 1))
5520 (*q).opacity=(*pixels).opacity+0;
5522 (*q).opacity=(*n).opacity+0;
5525 else /* if (magn_methy == 3 || magn_methy == 5) */
5527 /* Replicate nearest */
5528 if (i <= ((m+1) << 1))
5532 if (magn_methy == 5)
5534 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5535 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5536 +(*pixels).opacity);
5543 if (SyncAuthenticPixels(large_image,exception) == 0)
5547 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5548 next=(PixelPacket *) RelinquishMagickMemory(next);
5550 length=image->columns;
5552 if (logging != MagickFalse)
5553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5554 " Delete original image");
5556 DeleteImageFromList(&image);
5560 mng_info->image=image;
5562 /* magnify the columns */
5563 if (logging != MagickFalse)
5564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5565 " Magnify the columns to %.20g",(double) image->columns);
5567 for (y=0; y < (ssize_t) image->rows; y++)
5569 register PixelPacket
5572 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5573 pixels=q+(image->columns-length);
5575 for (x=(ssize_t) (image->columns-length);
5576 x < (ssize_t) image->columns; x++)
5578 if (x == (ssize_t) (image->columns-length))
5579 m=(ssize_t) mng_info->magn_ml;
5580 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5581 m=(ssize_t) mng_info->magn_mr;
5582 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5583 m=(ssize_t) mng_info->magn_mr;
5584 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5587 m=(ssize_t) mng_info->magn_mx;
5588 for (i=0; i < m; i++)
5590 if (magn_methx <= 1)
5592 /* replicate previous */
5595 else if (magn_methx == 2 || magn_methx == 4)
5602 (*q).red=(QM) ((2*i*((*n).red
5604 /((ssize_t) (m*2))+(*pixels).red);
5605 (*q).green=(QM) ((2*i*((*n).green
5607 +m)/((ssize_t) (m*2))+(*pixels).green);
5608 (*q).blue=(QM) ((2*i*((*n).blue
5610 /((ssize_t) (m*2))+(*pixels).blue);
5611 if (image->matte != MagickFalse)
5612 (*q).opacity=(QM) ((2*i*((*n).opacity
5613 -(*pixels).opacity)+m)/((ssize_t) (m*2))
5614 +(*pixels).opacity);
5616 if (magn_methx == 4)
5618 /* Replicate nearest */
5619 if (i <= ((m+1) << 1))
5620 (*q).opacity=(*pixels).opacity+0;
5622 (*q).opacity=(*n).opacity+0;
5625 else /* if (magn_methx == 3 || magn_methx == 5) */
5627 /* Replicate nearest */
5628 if (i <= ((m+1) << 1))
5632 if (magn_methx == 5)
5635 (*q).opacity=(QM) ((2*i*((*n).opacity
5636 -(*pixels).opacity)+m) /((ssize_t) (m*2))
5637 +(*pixels).opacity);
5645 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5648 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5649 if (magn_methx != 1 || magn_methy != 1)
5652 Rescale pixels to Quantum
5654 for (y=0; y < (ssize_t) image->rows; y++)
5656 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5657 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5659 q->red=ScaleShortToQuantum(q->red);
5660 q->green=ScaleShortToQuantum(q->green);
5661 q->blue=ScaleShortToQuantum(q->blue);
5662 q->opacity=ScaleShortToQuantum(q->opacity);
5665 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5670 if (logging != MagickFalse)
5671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5672 " Finished MAGN processing");
5677 Crop_box is with respect to the upper left corner of the MNG.
5679 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5680 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5681 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5682 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5683 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5684 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5685 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5686 if ((crop_box.left != (mng_info->image_box.left
5687 +mng_info->x_off[object_id])) ||
5688 (crop_box.right != (mng_info->image_box.right
5689 +mng_info->x_off[object_id])) ||
5690 (crop_box.top != (mng_info->image_box.top
5691 +mng_info->y_off[object_id])) ||
5692 (crop_box.bottom != (mng_info->image_box.bottom
5693 +mng_info->y_off[object_id])))
5695 if (logging != MagickFalse)
5696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5697 " Crop the PNG image");
5698 if ((crop_box.left < crop_box.right) &&
5699 (crop_box.top < crop_box.bottom))
5708 Crop_info is with respect to the upper left corner of
5711 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5712 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5713 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5714 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5715 image->page.width=image->columns;
5716 image->page.height=image->rows;
5719 im=CropImage(image,&crop_info,exception);
5720 if (im != (Image *) NULL)
5722 image->columns=im->columns;
5723 image->rows=im->rows;
5724 im=DestroyImage(im);
5725 image->page.width=image->columns;
5726 image->page.height=image->rows;
5727 image->page.x=crop_box.left;
5728 image->page.y=crop_box.top;
5734 No pixels in crop area. The MNG spec still requires
5735 a layer, though, so make a single transparent pixel in
5736 the top left corner.
5741 (void) SetImageBackgroundColor(image);
5742 image->page.width=1;
5743 image->page.height=1;
5748 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
5749 image=mng_info->image;
5753 #if (MAGICKCORE_QUANTUM_DEPTH == 16) /* TO DO: treat Q:32 */
5754 /* Determine if bit depth can be reduced from 16 to 8.
5755 * Note that the method GetImageDepth doesn't check background
5756 * and doesn't handle PseudoClass specially. Also it uses
5757 * multiplication and division by 257 instead of shifting, so
5760 if (mng_info->optimize && image->depth == 16)
5768 ok_to_reduce=(((((size_t) image->background_color.red >> 8) &
5770 == ((size_t) image->background_color.red & 0xff)) &&
5771 ((((size_t) image->background_color.green >> 8) & 0xff)
5772 == ((size_t) image->background_color.green & 0xff)) &&
5773 ((((size_t) image->background_color.blue >> 8) & 0xff)
5774 == ((size_t) image->background_color.blue & 0xff)));
5775 if (ok_to_reduce && image->storage_class == PseudoClass)
5779 for (indx=0; indx < (ssize_t) image->colors; indx++)
5781 ok_to_reduce=(((((size_t) image->colormap[indx].red >>
5783 == ((size_t) image->colormap[indx].red & 0xff)) &&
5784 ((((size_t) image->colormap[indx].green >> 8) & 0xff)
5785 == ((size_t) image->colormap[indx].green & 0xff)) &&
5786 ((((size_t) image->colormap[indx].blue >> 8) & 0xff)
5787 == ((size_t) image->colormap[indx].blue & 0xff)));
5788 if (ok_to_reduce == MagickFalse)
5792 if ((ok_to_reduce != MagickFalse) &&
5793 (image->storage_class != PseudoClass))
5801 for (y=0; y < (ssize_t) image->rows; y++)
5803 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
5804 if (p == (const PixelPacket *) NULL)
5806 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5809 (((size_t) p->red >> 8) & 0xff) ==
5810 ((size_t) p->red & 0xff)) &&
5811 ((((size_t) p->green >> 8) & 0xff) ==
5812 ((size_t) p->green & 0xff)) &&
5813 ((((size_t) p->blue >> 8) & 0xff) ==
5814 ((size_t) p->blue & 0xff)) &&
5816 (((size_t) p->opacity >> 8) & 0xff) ==
5817 ((size_t) p->opacity & 0xff)))));
5818 if (ok_to_reduce == 0)
5829 if (logging != MagickFalse)
5830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5831 " Reducing PNG bit depth to 8 without loss of info");
5835 GetImageException(image,exception);
5836 if (image_info->number_scenes != 0)
5838 if (mng_info->scenes_found >
5839 (ssize_t) (image_info->first_scene+image_info->number_scenes))
5842 if (logging != MagickFalse)
5843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5844 " Finished reading image datastream.");
5845 } while (LocaleCompare(image_info->magick,"MNG") == 0);
5846 (void) CloseBlob(image);
5847 if (logging != MagickFalse)
5848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5849 " Finished reading all image datastreams.");
5850 #if defined(MNG_INSERT_LAYERS)
5851 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
5852 (mng_info->mng_height))
5855 Insert a background layer if nothing else was found.
5857 if (logging != MagickFalse)
5858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5859 " No images found. Inserting a background layer.");
5860 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5863 Allocate next image structure.
5865 AcquireNextImage(image_info,image);
5866 if (GetNextImageInList(image) == (Image *) NULL)
5868 image=DestroyImageList(image);
5869 MngInfoFreeStruct(mng_info,&have_mng_structure);
5870 if (logging != MagickFalse)
5871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5872 " Allocation failed, returning NULL.");
5873 return((Image *) NULL);
5875 image=SyncNextImageInList(image);
5877 image->columns=mng_info->mng_width;
5878 image->rows=mng_info->mng_height;
5879 image->page.width=mng_info->mng_width;
5880 image->page.height=mng_info->mng_height;
5883 image->background_color=mng_background_color;
5884 image->matte=MagickFalse;
5885 if (image_info->ping == MagickFalse)
5886 (void) SetImageBackgroundColor(image);
5887 mng_info->image_found++;
5890 image->iterations=mng_iterations;
5891 if (mng_iterations == 1)
5892 image->start_loop=MagickTrue;
5893 while (GetPreviousImageInList(image) != (Image *) NULL)
5896 if (image_count > 10*mng_info->image_found)
5898 if (logging != MagickFalse)
5899 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
5900 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5901 CoderError,"Linked list is corrupted, beginning of list not found",
5902 "`%s'",image_info->filename);
5903 return((Image *) NULL);
5905 image=GetPreviousImageInList(image);
5906 if (GetNextImageInList(image) == (Image *) NULL)
5908 if (logging != MagickFalse)
5909 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
5910 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5911 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
5912 image_info->filename);
5915 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
5916 GetNextImageInList(image) ==
5919 if (logging != MagickFalse)
5920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5921 " First image null");
5922 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5923 CoderError,"image->next for first image is NULL but shouldn't be.",
5924 "`%s'",image_info->filename);
5926 if (mng_info->image_found == 0)
5928 if (logging != MagickFalse)
5929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5930 " No visible images found.");
5931 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5932 CoderError,"No visible images in file","`%s'",image_info->filename);
5933 if (image != (Image *) NULL)
5934 image=DestroyImageList(image);
5935 MngInfoFreeStruct(mng_info,&have_mng_structure);
5936 return((Image *) NULL);
5939 if (mng_info->ticks_per_second)
5940 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
5941 final_delay/mng_info->ticks_per_second;
5943 image->start_loop=MagickTrue;
5944 /* Find final nonzero image delay */
5945 final_image_delay=0;
5946 while (GetNextImageInList(image) != (Image *) NULL)
5949 final_image_delay=image->delay;
5950 image=GetNextImageInList(image);
5952 if (final_delay < final_image_delay)
5953 final_delay=final_image_delay;
5954 image->delay=final_delay;
5955 if (logging != MagickFalse)
5956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5957 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
5958 (double) final_delay);
5959 if (logging != MagickFalse)
5965 image=GetFirstImageInList(image);
5966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5967 " Before coalesce:");
5968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5969 " scene 0 delay=%.20g",(double) image->delay);
5970 while (GetNextImageInList(image) != (Image *) NULL)
5972 image=GetNextImageInList(image);
5973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5974 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
5978 image=GetFirstImageInList(image);
5979 #ifdef MNG_COALESCE_LAYERS
5989 if (logging != MagickFalse)
5990 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
5992 next_image=CoalesceImages(image,&image->exception);
5993 if (next_image == (Image *) NULL)
5994 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5995 image=DestroyImageList(image);
5997 for (next=image; next != (Image *) NULL; next=next_image)
5999 next->page.width=mng_info->mng_width;
6000 next->page.height=mng_info->mng_height;
6003 next->scene=scene++;
6004 next_image=GetNextImageInList(next);
6005 if (next_image == (Image *) NULL)
6007 if (next->delay == 0)
6010 next_image->previous=GetPreviousImageInList(next);
6011 if (GetPreviousImageInList(next) == (Image *) NULL)
6014 next->previous->next=next_image;
6015 next=DestroyImage(next);
6021 while (GetNextImageInList(image) != (Image *) NULL)
6022 image=GetNextImageInList(image);
6023 image->dispose=BackgroundDispose;
6025 if (logging != MagickFalse)
6031 image=GetFirstImageInList(image);
6032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6033 " After coalesce:");
6034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6035 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6036 (double) image->dispose);
6037 while (GetNextImageInList(image) != (Image *) NULL)
6039 image=GetNextImageInList(image);
6040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6041 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6042 (double) image->delay,(double) image->dispose);
6045 image=GetFirstImageInList(image);
6046 MngInfoFreeStruct(mng_info,&have_mng_structure);
6047 have_mng_structure=MagickFalse;
6048 if (logging != MagickFalse)
6049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6050 return(GetFirstImageInList(image));
6052 #else /* PNG_LIBPNG_VER > 10011 */
6053 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6055 printf("Your PNG library is too old: You have libpng-%s\n",
6056 PNG_LIBPNG_VER_STRING);
6057 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6058 "PNG library is too old","`%s'",image_info->filename);
6059 return(Image *) NULL;
6061 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6063 return(ReadPNGImage(image_info,exception));
6065 #endif /* PNG_LIBPNG_VER > 10011 */
6069 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6073 % R e g i s t e r P N G I m a g e %
6077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6079 % RegisterPNGImage() adds properties for the PNG image format to
6080 % the list of supported formats. The properties include the image format
6081 % tag, a method to read and/or write the format, whether the format
6082 % supports the saving of more than one frame to the same file or blob,
6083 % whether the format supports native in-memory I/O, and a brief
6084 % description of the format.
6086 % The format of the RegisterPNGImage method is:
6088 % size_t RegisterPNGImage(void)
6091 ModuleExport size_t RegisterPNGImage(void)
6094 version[MaxTextExtent];
6102 "See http://www.libpng.org/ for details about the PNG format."
6106 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6111 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6116 #if defined(PNG_LIBPNG_VER_STRING)
6117 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6118 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6119 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6121 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6122 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6126 entry=SetMagickInfo("MNG");
6127 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6128 #if defined(MAGICKCORE_PNG_DELEGATE)
6129 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6130 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6132 entry->magick=(IsImageFormatHandler *) IsMNG;
6133 entry->description=ConstantString("Multiple-image Network Graphics");
6134 if (*version != '\0')
6135 entry->version=ConstantString(version);
6136 entry->module=ConstantString("PNG");
6137 entry->note=ConstantString(MNGNote);
6138 (void) RegisterMagickInfo(entry);
6140 entry=SetMagickInfo("PNG");
6141 #if defined(MAGICKCORE_PNG_DELEGATE)
6142 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6143 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6145 entry->magick=(IsImageFormatHandler *) IsPNG;
6146 entry->adjoin=MagickFalse;
6147 entry->description=ConstantString("Portable Network Graphics");
6148 entry->module=ConstantString("PNG");
6149 if (*version != '\0')
6150 entry->version=ConstantString(version);
6151 entry->note=ConstantString(PNGNote);
6152 (void) RegisterMagickInfo(entry);
6154 entry=SetMagickInfo("PNG8");
6155 #if defined(MAGICKCORE_PNG_DELEGATE)
6156 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6157 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6159 entry->magick=(IsImageFormatHandler *) IsPNG;
6160 entry->adjoin=MagickFalse;
6161 entry->description=ConstantString(
6162 "8-bit indexed with optional binary transparency");
6163 entry->module=ConstantString("PNG");
6164 (void) RegisterMagickInfo(entry);
6166 entry=SetMagickInfo("PNG24");
6168 #if defined(ZLIB_VERSION)
6169 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6170 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6171 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6173 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6174 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6177 if (*version != '\0')
6178 entry->version=ConstantString(version);
6179 #if defined(MAGICKCORE_PNG_DELEGATE)
6180 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6181 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6183 entry->magick=(IsImageFormatHandler *) IsPNG;
6184 entry->adjoin=MagickFalse;
6185 entry->description=ConstantString("opaque 24-bit RGB");
6186 entry->module=ConstantString("PNG");
6187 (void) RegisterMagickInfo(entry);
6189 entry=SetMagickInfo("PNG32");
6190 #if defined(MAGICKCORE_PNG_DELEGATE)
6191 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6192 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6194 entry->magick=(IsImageFormatHandler *) IsPNG;
6195 entry->adjoin=MagickFalse;
6196 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6197 entry->module=ConstantString("PNG");
6198 (void) RegisterMagickInfo(entry);
6200 entry=SetMagickInfo("JNG");
6201 #if defined(JNG_SUPPORTED)
6202 #if defined(MAGICKCORE_PNG_DELEGATE)
6203 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6204 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6207 entry->magick=(IsImageFormatHandler *) IsJNG;
6208 entry->adjoin=MagickFalse;
6209 entry->description=ConstantString("JPEG Network Graphics");
6210 entry->module=ConstantString("PNG");
6211 entry->note=ConstantString(JNGNote);
6212 (void) RegisterMagickInfo(entry);
6213 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6214 png_semaphore=AllocateSemaphoreInfo();
6216 return(MagickImageCoderSignature);
6220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6224 % U n r e g i s t e r P N G I m a g e %
6228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6230 % UnregisterPNGImage() removes format registrations made by the
6231 % PNG module from the list of supported formats.
6233 % The format of the UnregisterPNGImage method is:
6235 % UnregisterPNGImage(void)
6238 ModuleExport void UnregisterPNGImage(void)
6240 (void) UnregisterMagickInfo("MNG");
6241 (void) UnregisterMagickInfo("PNG");
6242 (void) UnregisterMagickInfo("PNG8");
6243 (void) UnregisterMagickInfo("PNG24");
6244 (void) UnregisterMagickInfo("PNG32");
6245 (void) UnregisterMagickInfo("JNG");
6246 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6247 if (png_semaphore != (SemaphoreInfo *) NULL)
6248 DestroySemaphoreInfo(&png_semaphore);
6252 #if defined(MAGICKCORE_PNG_DELEGATE)
6253 #if PNG_LIBPNG_VER > 10011
6255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6259 % W r i t e M N G I m a g e %
6263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6265 % WriteMNGImage() writes an image in the Portable Network Graphics
6266 % Group's "Multiple-image Network Graphics" encoded image format.
6268 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6270 % The format of the WriteMNGImage method is:
6272 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6274 % A description of each parameter follows.
6276 % o image_info: the image info.
6278 % o image: The image.
6281 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6282 % "To do" under ReadPNGImage):
6284 % Fix problem with palette sorting (when PNG_SORT_PALETTE is enabled,
6285 % some GIF animations don't convert properly)
6287 % Preserve all unknown and not-yet-handled known chunks found in input
6288 % PNG file and copy them into output PNG files according to the PNG
6291 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6293 % Improve selection of color type (use indexed-colour or indexed-colour
6294 % with tRNS when 256 or fewer unique RGBA values are present).
6296 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6297 % This will be complicated if we limit ourselves to generating MNG-LC
6298 % files. For now we ignore disposal method 3 and simply overlay the next
6301 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6302 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6303 % [mostly done 15 June 1999 but still need to take care of tRNS]
6305 % Check for identical sRGB and replace with a global sRGB (and remove
6306 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6307 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6308 % local gAMA/cHRM with local sRGB if appropriate).
6310 % Check for identical sBIT chunks and write global ones.
6312 % Provide option to skip writing the signature tEXt chunks.
6314 % Use signatures to detect identical objects and reuse the first
6315 % instance of such objects instead of writing duplicate objects.
6317 % Use a smaller-than-32k value of compression window size when
6320 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6321 % ancillary text chunks and save profiles.
6323 % Provide an option to force LC files (to ensure exact framing rate)
6326 % Provide an option to force VLC files instead of LC, even when offsets
6327 % are present. This will involve expanding the embedded images with a
6328 % transparent region at the top and/or left.
6332 png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6333 png_info *ping_info, unsigned char *profile_type, unsigned char
6334 *profile_description, unsigned char *profile_data, png_uint_32 length)
6353 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6355 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6358 if (image_info->verbose)
6360 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6361 (char *) profile_type, (double) length);
6363 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6364 description_length=(png_uint_32) strlen((const char *) profile_description);
6365 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6366 + description_length);
6367 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6368 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6369 text[0].key[0]='\0';
6370 (void) ConcatenateMagickString(text[0].key,
6371 "Raw profile type ",MaxTextExtent);
6372 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6376 (void) CopyMagickString(dp,(const char *) profile_description,
6378 dp+=description_length;
6380 (void) FormatMagickString(dp,allocated_length-
6381 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6383 for (i=0; i < (ssize_t) length; i++)
6387 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6388 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6392 text[0].text_length=(png_size_t) (dp-text[0].text);
6393 text[0].compression=image_info->compression == NoCompression ||
6394 (image_info->compression == UndefinedCompression &&
6395 text[0].text_length < 128) ? -1 : 0;
6396 if (text[0].text_length <= allocated_length)
6397 png_set_text(ping,ping_info,text,1);
6398 png_free(ping,text[0].text);
6399 png_free(ping,text[0].key);
6400 png_free(ping,text);
6403 static MagickBooleanType png_write_chunk_from_profile(Image *image,
6404 const char *string, int logging)
6417 ResetImageProfileIterator(image);
6418 for (name=GetNextImageProfile(image); name != (const char *) NULL; ){
6419 profile=GetImageProfile(image,name);
6420 if (profile != (const StringInfo *) NULL)
6425 if (LocaleNCompare(name,string,11) == 0) {
6426 if (logging != MagickFalse)
6427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6428 " Found %s profile",name);
6430 png_profile=CloneStringInfo(profile);
6431 data=GetStringInfoDatum(png_profile),
6432 length=(png_uint_32) GetStringInfoLength(png_profile);
6437 (void) WriteBlobMSBULong(image,length-5); /* data length */
6438 (void) WriteBlob(image,length-1,data+1);
6439 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6440 png_profile=DestroyStringInfo(png_profile);
6443 name=GetNextImageProfile(image);
6448 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6449 const ImageInfo *image_info,Image *image)
6451 /* Write one PNG image */
6470 ping_trans_alpha[256];
6498 register IndexPacket
6515 ping_interlace_method,
6516 ping_compression_method,
6530 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6531 " enter WriteOnePNGImage()");
6533 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6534 LockSemaphoreInfo(png_semaphore);
6537 /* Initialize some stuff */
6540 ping_interlace_method=0,
6541 ping_compression_method=0,
6542 ping_filter_method=0,
6545 ping_background.red = 0;
6546 ping_background.green = 0;
6547 ping_background.blue = 0;
6548 ping_background.gray = 0;
6549 ping_background.index = 0;
6551 ping_trans_color.red=0;
6552 ping_trans_color.green=0;
6553 ping_trans_color.blue=0;
6554 ping_trans_color.gray=0;
6556 quantum_info = (QuantumInfo *) NULL;
6557 image_colors=image->colors;
6558 image_depth=image->depth;
6559 image_matte=image->matte;
6561 if (image->colorspace != RGBColorspace)
6562 (void) TransformImageColorspace(image,RGBColorspace);
6563 mng_info->IsPalette=image->storage_class == PseudoClass &&
6564 image_colors <= 256 && !IsOpaqueImage(image,&image->exception);
6565 mng_info->optimize=image_info->type == OptimizeType;
6568 Allocate the PNG structures
6570 #ifdef PNG_USER_MEM_SUPPORTED
6571 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
6572 PNGErrorHandler,PNGWarningHandler,(void *) NULL,
6573 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
6575 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
6576 PNGErrorHandler,PNGWarningHandler);
6578 if (ping == (png_struct *) NULL)
6579 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6580 ping_info=png_create_info_struct(ping);
6581 if (ping_info == (png_info *) NULL)
6583 png_destroy_write_struct(&ping,(png_info **) NULL);
6584 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6586 png_set_write_fn(ping,image,png_put_data,png_flush_data);
6587 png_pixels=(unsigned char *) NULL;
6589 if (setjmp(png_jmpbuf(ping)))
6595 if (image_info->verbose)
6596 (void) printf("PNG write has failed.\n");
6598 png_destroy_write_struct(&ping,&ping_info);
6599 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6600 UnlockSemaphoreInfo(png_semaphore);
6602 return(MagickFalse);
6605 Prepare PNG for writing.
6607 #if defined(PNG_MNG_FEATURES_SUPPORTED)
6608 if (mng_info->write_mng)
6609 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
6611 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
6612 if (mng_info->write_mng)
6613 png_permit_empty_plte(ping,MagickTrue);
6617 ping_width=(png_uint_32) image->columns;
6618 ping_height=(png_uint_32) image->rows;
6619 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
6621 if (mng_info->write_png_depth != 0)
6622 image_depth=mng_info->write_png_depth;
6623 /* Adjust requested depth to next higher valid depth if necessary */
6624 if (image_depth > 8)
6626 if ((image_depth > 4) && (image_depth < 8))
6628 if (image_depth == 3)
6630 if (logging != MagickFalse)
6632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6633 " width=%.20g",(double) ping_width);
6634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6635 " height=%.20g",(double) ping_height);
6636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6637 " image_matte=%.20g",(double) image->matte);
6638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6639 " image_depth=%.20g",(double) image->depth);
6640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6641 " requested PNG image_depth=%.20g",(double) image->depth);
6643 save_image_depth=image_depth;
6644 ping_bit_depth=(png_byte) save_image_depth;
6645 #if defined(PNG_pHYs_SUPPORTED)
6646 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
6647 (!mng_info->write_mng || !mng_info->equal_physs))
6656 if (image->units == PixelsPerInchResolution)
6658 unit_type=PNG_RESOLUTION_METER;
6659 x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
6660 y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
6662 else if (image->units == PixelsPerCentimeterResolution)
6664 unit_type=PNG_RESOLUTION_METER;
6665 x_resolution=(png_uint_32) (100.0*image->x_resolution);
6666 y_resolution=(png_uint_32) (100.0*image->y_resolution);
6670 unit_type=PNG_RESOLUTION_UNKNOWN;
6671 x_resolution=(png_uint_32) image->x_resolution;
6672 y_resolution=(png_uint_32) image->y_resolution;
6674 png_set_pHYs(ping,ping_info,x_resolution,y_resolution,unit_type);
6675 if (logging != MagickFalse)
6676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6677 " Setting up pHYs chunk");
6680 #if defined(PNG_oFFs_SUPPORTED)
6681 if (image->page.x || image->page.y)
6683 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
6684 (png_int_32) image->page.y, 0);
6685 if (logging != MagickFalse)
6686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6687 " Setting up oFFs chunk");
6690 if (image_matte && (!mng_info->adjoin || !mng_info->equal_backgrounds))
6695 if (image_depth < MAGICKCORE_QUANTUM_DEPTH)
6700 maxval=(1UL << image_depth)-1;
6701 background.red=(png_uint_16)
6702 (QuantumScale*(maxval*image->background_color.red));
6703 background.green=(png_uint_16)
6704 (QuantumScale*(maxval*image->background_color.green));
6705 background.blue=(png_uint_16)
6706 (QuantumScale*(maxval*image->background_color.blue));
6707 background.gray=(png_uint_16)
6708 (QuantumScale*(maxval*PixelIntensity(&image->background_color)));
6712 background.red=image->background_color.red;
6713 background.green=image->background_color.green;
6714 background.blue=image->background_color.blue;
6716 (png_uint_16) PixelIntensity(&image->background_color);
6718 background.index=(png_byte) background.gray;
6719 if (logging != MagickFalse)
6720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6721 " Setting up bKGd chunk");
6722 png_set_bKGD(ping,ping_info,&background);
6725 Select the color type.
6729 if ((mng_info->write_png_colortype-1) == PNG_COLOR_TYPE_PALETTE)
6730 mng_info->write_png8=MagickTrue;
6731 if (mng_info->write_png8)
6733 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6735 image_depth=ping_bit_depth;
6737 /* TO DO: make this a function cause it's used twice, except
6738 for reducing the sample depth from 8. */
6747 number_colors=image_colors;
6748 if ((image->storage_class == DirectClass) || (number_colors > 256))
6750 GetQuantizeInfo(&quantize_info);
6751 quantize_info.dither=IsPaletteImage(image,&image->exception) ==
6752 MagickFalse ? MagickTrue : MagickFalse;
6753 quantize_info.number_colors= (matte != MagickFalse ? 255UL :
6755 (void) QuantizeImage(&quantize_info,image);
6756 number_colors=image_colors;
6757 (void) SyncImage(image);
6758 if (logging != MagickFalse)
6759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6760 " Colors quantized to %.20g",(double) number_colors);
6763 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
6767 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6768 #if defined(PNG_SORT_PALETTE)
6769 save_number_colors=image_colors;
6770 if (CompressColormapTransFirst(image) == MagickFalse)
6771 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6772 number_colors=image->colors;
6773 image_colors=save_number_colors;
6775 palette=(png_color *) AcquireQuantumMemory(257,
6777 if (palette == (png_color *) NULL)
6778 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6779 if (logging != MagickFalse)
6780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6781 " Setting up PLTE chunk with %d colors",
6782 (int) number_colors);
6783 for (i=0; i < (ssize_t) number_colors; i++)
6785 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6786 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6787 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6788 if (logging != MagickFalse)
6789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6790 #if MAGICKCORE_QUANTUM_DEPTH == 8
6791 " %3ld (%3d,%3d,%3d)",
6793 " %5ld (%5d,%5d,%5d)",
6795 (long) i,palette[i].red,palette[i].green,palette[i].blue);
6801 palette[i].red=ScaleQuantumToChar((Quantum) QuantumRange);
6802 palette[i].green=ScaleQuantumToChar((Quantum) QuantumRange);
6803 palette[i].blue=ScaleQuantumToChar((Quantum) QuantumRange);
6805 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6806 palette=(png_colorp) RelinquishMagickMemory(palette);
6807 image_depth=ping_bit_depth;
6818 Identify which colormap entry is transparent.
6820 assert(number_colors <= 256);
6821 for (i=0; i < (ssize_t) number_colors; i++)
6823 exception=(&image->exception);
6824 for (y=0; y < (ssize_t) image->rows; y++)
6826 register const PixelPacket
6829 p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6830 if (p == (PixelPacket *) NULL)
6832 indexes=GetAuthenticIndexQueue(image);
6833 for (x=0; x < (ssize_t) image->columns; x++)
6835 if (p->opacity != OpaqueOpacity)
6837 indexes[x]=(IndexPacket) (number_colors-1);
6838 trans_alpha[(ssize_t) indexes[x]]=(png_byte) (255-
6839 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
6843 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6846 for (i=0; i < (ssize_t) number_colors; i++)
6847 if (trans_alpha[i] != 255)
6848 ping_num_trans=(unsigned short) (i+1);
6850 if (ping_num_trans == 0)
6851 png_set_invalid(ping, ping_info, PNG_INFO_tRNS);
6852 if (!png_get_valid(ping, ping_info, PNG_INFO_tRNS))
6854 if (ping_num_trans != 0)
6856 for (i=0; i<256; i++)
6857 ping_trans_alpha[i]=(png_byte) trans_alpha[i];
6860 (void) png_set_tRNS(ping, ping_info,
6866 Identify which colormap entry is the background color.
6868 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
6869 if (IsPNGColorEqual(ping_background,image->colormap[i]))
6871 ping_background.index=(png_byte) i;
6873 if (image_matte != MagickFalse)
6875 /* TO DO: reduce to binary transparency */
6877 } /* end of write_png8 */
6878 else if (mng_info->write_png24)
6880 image_matte=MagickFalse;
6881 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6883 else if (mng_info->write_png32)
6885 image_matte=MagickTrue;
6886 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6890 image_depth=ping_bit_depth;
6891 if (mng_info->write_png_colortype)
6893 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
6894 image_matte=MagickFalse;
6895 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6896 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6897 image_matte=MagickTrue;
6901 if (logging != MagickFalse)
6902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6903 "Selecting PNG colortype");
6904 ping_color_type=(png_byte) ((matte == MagickTrue)?
6905 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6906 if(image_info->type == TrueColorType)
6908 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6909 image_matte=MagickFalse;
6911 if(image_info->type == TrueColorMatteType)
6913 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6914 image_matte=MagickTrue;
6916 if ((image_info->type == UndefinedType ||
6917 image_info->type == OptimizeType ||
6918 image_info->type == GrayscaleType) &&
6919 image_matte == MagickFalse && ImageIsGray(image))
6921 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
6922 image_matte=MagickFalse;
6924 if ((image_info->type == UndefinedType ||
6925 image_info->type == OptimizeType ||
6926 image_info->type == GrayscaleMatteType) &&
6927 image_matte == MagickTrue && ImageIsGray(image))
6929 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
6930 image_matte=MagickTrue;
6933 if (logging != MagickFalse)
6934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6935 "Selected PNG colortype=%d",ping_color_type);
6937 if (ping_bit_depth < 8)
6939 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6940 ping_color_type == PNG_COLOR_TYPE_RGB ||
6941 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6945 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
6947 if (image->matte == MagickFalse && image->colors < 256)
6949 if (ImageIsMonochrome(image))
6952 if (ping_bit_depth < (int)mng_info->write_png_depth)
6953 ping_bit_depth = mng_info->write_png_depth;
6957 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
6962 if (image->colors == 0)
6965 (void) ThrowMagickException(&image->exception,
6966 GetMagickModule(),CoderError,
6967 "image has 0 colors", "`%s'","");
6970 if (logging != MagickFalse)
6971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6973 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
6974 ping_bit_depth <<= 1;
6976 if (logging != MagickFalse)
6978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6979 " Number of colors: %.20g",(double) image_colors);
6980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6981 " Tentative PNG bit depth: %d",ping_bit_depth);
6983 if (mng_info->write_png_depth)
6985 old_bit_depth=ping_bit_depth;
6986 if (ping_bit_depth < (int)mng_info->write_png_depth)
6988 ping_bit_depth = mng_info->write_png_depth;
6989 if (ping_bit_depth > 8)
6991 if (ping_bit_depth != (int) old_bit_depth)
6993 if (logging != MagickFalse)
6994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6995 " Colors increased to %.20g",(double)
7002 image_depth=ping_bit_depth;
7003 if (logging != MagickFalse)
7005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7006 " Tentative PNG color type: %.20g",(double) ping_color_type);
7007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7008 " image_info->type: %.20g",(double) image_info->type);
7009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7010 " image_depth: %.20g",(double) image_depth);
7011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7012 " ping_bit_depth: %.20g",(double) ping_bit_depth);
7015 if (matte && (mng_info->optimize || mng_info->IsPalette))
7017 register const PixelPacket
7020 p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
7021 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
7022 for (y=0; y < (ssize_t) image->rows; y++)
7024 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7025 if (p == (const PixelPacket *) NULL)
7027 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7029 if (IsGray(p) == MagickFalse)
7031 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
7038 Determine if there is any transparent color.
7040 for (y=0; y < (ssize_t) image->rows; y++)
7042 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7043 if (p == (const PixelPacket *) NULL)
7045 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7047 if (p->opacity != OpaqueOpacity)
7054 if ((y == (ssize_t) image->rows) && (x == (ssize_t) image->columns))
7057 No transparent pixels are present. Change 4 or 6 to 0 or 2.
7059 image_matte=MagickFalse;
7060 ping_color_type&=0x03;
7068 if (ping_bit_depth == 8)
7070 if (ping_bit_depth == 4)
7072 if (ping_bit_depth == 2)
7074 if (ping_bit_depth == 1)
7076 ping_trans_color.red=(png_uint_16)
7077 (ScaleQuantumToShort(GetRedPixelComponent(p)) & mask);
7078 ping_trans_color.green=(png_uint_16)
7079 (ScaleQuantumToShort(GetGreenPixelComponent(p)) & mask);
7080 ping_trans_color.blue=(png_uint_16)
7081 (ScaleQuantumToShort(GetBluePixelComponent(p)) & mask);
7082 ping_trans_color.gray=(png_uint_16)
7083 (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
7084 ping_trans_color.index=(png_byte)
7085 (ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p))));
7086 (void) png_set_tRNS(ping, ping_info, NULL, 0,
7089 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7092 Determine if there is one and only one transparent color
7093 and if so if it is fully transparent.
7095 for (y=0; y < (ssize_t) image->rows; y++)
7097 p=GetVirtualPixels(image,0,y,image->columns,1,
7100 if (p == (const PixelPacket *) NULL)
7102 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7104 if (p->opacity != OpaqueOpacity)
7106 if (IsPNGColorEqual(ping_trans_color,*p) == 0)
7108 break; /* Can't use RGB + tRNS for multiple
7109 transparent colors. */
7111 if (p->opacity != (Quantum) TransparentOpacity)
7113 break; /* Can't use RGB + tRNS for
7114 semitransparency. */
7119 if (IsPNGColorEqual(ping_trans_color,*p))
7120 break; /* Can't use RGB + tRNS when another pixel
7121 having the same RGB samples is
7130 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7132 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7134 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
7135 if (image_depth == 8)
7137 ping_trans_color.red&=0xff;
7138 ping_trans_color.green&=0xff;
7139 ping_trans_color.blue&=0xff;
7140 ping_trans_color.gray&=0xff;
7145 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7146 image_matte=MagickFalse;
7147 if ((mng_info->optimize || mng_info->IsPalette) &&
7148 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
7149 ImageIsGray(image) && (!image_matte || image_depth >= 8))
7152 if (image_matte != MagickFalse)
7153 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
7156 ping_color_type=PNG_COLOR_TYPE_GRAY;
7157 if (save_image_depth == 16 && image_depth == 8)
7158 ping_trans_color.gray*=0x0101;
7160 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
7161 image_depth=MAGICKCORE_QUANTUM_DEPTH;
7162 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
7163 image_colors=one << image_depth;
7164 if (image_depth > 8)
7169 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7171 if(!mng_info->write_png_depth)
7174 while ((int) (one << ping_bit_depth)
7175 < (ssize_t) image_colors)
7176 ping_bit_depth <<= 1;
7179 else if (mng_info->optimize && ping_color_type ==
7180 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
7181 mng_info->IsPalette)
7184 /* Check if grayscale is reducible */
7186 depth_4_ok=MagickTrue,
7187 depth_2_ok=MagickTrue,
7188 depth_1_ok=MagickTrue;
7190 for (i=0; i < (ssize_t) image_colors; i++)
7195 intensity=ScaleQuantumToChar(image->colormap[i].red);
7197 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
7198 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
7199 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
7200 depth_2_ok=depth_1_ok=MagickFalse;
7201 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
7202 depth_1_ok=MagickFalse;
7204 if (depth_1_ok && mng_info->write_png_depth <= 1)
7206 else if (depth_2_ok && mng_info->write_png_depth <= 2)
7208 else if (depth_4_ok && mng_info->write_png_depth <= 4)
7212 image_depth=ping_bit_depth;
7215 if (mng_info->IsPalette)
7220 number_colors=image_colors;
7222 if (image_depth <= 8)
7227 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
7228 if (mng_info->have_write_global_plte && !matte)
7230 png_set_PLTE(ping,ping_info,NULL,0);
7232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7233 " Setting up empty PLTE chunk");
7237 #if defined(PNG_SORT_PALETTE)
7241 if (mng_info->optimize)
7243 save_number_colors=image_colors;
7244 if (CompressColormapTransFirst(image) == MagickFalse)
7245 ThrowWriterException(ResourceLimitError,
7246 "MemoryAllocationFailed");
7247 number_colors=image->colors;
7248 image_colors=save_number_colors;
7251 palette=(png_color *) AcquireQuantumMemory(257,
7253 if (palette == (png_color *) NULL)
7254 ThrowWriterException(ResourceLimitError,
7255 "MemoryAllocationFailed");
7256 for (i=0; i < (ssize_t) number_colors; i++)
7258 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7259 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7260 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7264 " Setting up PLTE chunk with %d colors",
7265 (int) number_colors);
7266 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
7267 palette=(png_colorp) RelinquishMagickMemory(palette);
7269 /* color_type is PNG_COLOR_TYPE_PALETTE */
7270 if (!mng_info->write_png_depth)
7277 while ((one << ping_bit_depth) < number_colors)
7278 ping_bit_depth <<= 1;
7286 register const PixelPacket
7292 register const IndexPacket
7296 Identify which colormap entry is transparent.
7298 assert(number_colors <= 256);
7299 for (i=0; i < (ssize_t) number_colors; i++)
7301 exception=(&image->exception);
7302 for (y=0; y < (ssize_t) image->rows; y++)
7304 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7305 if (p == (const PixelPacket *) NULL)
7307 packet_indexes=GetVirtualIndexQueue(image);
7308 for (x=0; x < (ssize_t) image->columns; x++)
7310 if (p->opacity != OpaqueOpacity)
7315 packet_index=packet_indexes[x];
7316 assert((size_t) packet_index < number_colors);
7317 if (trans[(ssize_t) packet_index] != 256)
7319 if (trans[(ssize_t) packet_index] != (png_byte) (255-
7320 ScaleQuantumToChar(GetOpacityPixelComponent(p))))
7322 ping_color_type=(png_byte)
7323 PNG_COLOR_TYPE_RGB_ALPHA;
7327 trans[(ssize_t) packet_index]=(png_byte) (255-
7328 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
7332 if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
7335 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7336 png_set_invalid(ping,ping_info,PNG_INFO_PLTE);
7337 mng_info->IsPalette=MagickFalse;
7338 (void) SyncImage(image);
7340 (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7341 " Cannot write image as indexed PNG, writing RGBA.");
7345 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7347 for (i=0; i < (ssize_t) number_colors; i++)
7349 if (trans[i] == 256)
7351 if (trans[i] != 255)
7352 ping_num_trans=(unsigned short) (i+1);
7355 if (ping_num_trans == 0)
7356 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7357 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7359 if (ping_num_trans != 0)
7361 for (i=0; i < (ssize_t) number_colors; i++)
7362 ping_trans_alpha[i]=(png_byte) trans[i];
7370 if (image_depth < 8)
7372 if ((save_image_depth == 16) && (image_depth == 8))
7374 ping_trans_color.red*=0x0101;
7375 ping_trans_color.green*=0x0101;
7376 ping_trans_color.blue*=0x0101;
7377 ping_trans_color.gray*=0x0101;
7382 Adjust background and transparency samples in sub-8-bit grayscale files.
7384 if (ping_bit_depth < 8 && ping_color_type ==
7385 PNG_COLOR_TYPE_GRAY)
7396 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
7399 background.gray=(png_uint_16)
7400 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7402 if (logging != MagickFalse)
7403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7404 " Setting up bKGD chunk");
7405 png_set_bKGD(ping,ping_info,&background);
7407 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7408 ping_trans_color.gray));
7411 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7414 Identify which colormap entry is the background color.
7420 number_colors=image_colors;
7422 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
7423 if (IsPNGColorEqual(ping_background,image->colormap[i]))
7426 ping_background.index=(png_byte) i;
7429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7430 " Setting up bKGD chunk with index=%d",(int) i);
7432 png_set_bKGD(ping,ping_info,&ping_background);
7435 if (logging != MagickFalse)
7436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7437 " PNG color type: %d",ping_color_type);
7439 Initialize compression level and filtering.
7441 if (logging != MagickFalse)
7442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7443 " Setting up deflate compression");
7444 if (logging != MagickFalse)
7445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7446 " Compression buffer size: 32768");
7447 png_set_compression_buffer_size(ping,32768L);
7448 if (logging != MagickFalse)
7449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7450 " Compression mem level: 9");
7451 png_set_compression_mem_level(ping, 9);
7452 quality=image->quality == UndefinedCompressionQuality ? 75UL :
7459 level=(int) MagickMin((ssize_t) quality/10,9);
7460 if (logging != MagickFalse)
7461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7462 " Compression level: %d",level);
7463 png_set_compression_level(ping,level);
7467 if (logging != MagickFalse)
7468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7469 " Compression strategy: Z_HUFFMAN_ONLY");
7470 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7472 if (logging != MagickFalse)
7473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7474 " Setting up filtering");
7475 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7477 /* This became available in libpng-1.0.9. Output must be a MNG. */
7478 if (mng_info->write_mng && ((quality % 10) == 7))
7480 if (logging != MagickFalse)
7481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7482 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
7483 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
7486 if (logging != MagickFalse)
7487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7494 if ((quality % 10) > 5)
7495 base_filter=PNG_ALL_FILTERS;
7497 if ((quality % 10) != 5)
7498 base_filter=(int) quality % 10;
7500 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
7501 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
7503 base_filter=PNG_NO_FILTERS;
7505 base_filter=PNG_ALL_FILTERS;
7506 if (logging != MagickFalse)
7508 if (base_filter == PNG_ALL_FILTERS)
7509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7510 " Base filter method: ADAPTIVE");
7512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7513 " Base filter method: NONE");
7515 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7518 ResetImageProfileIterator(image);
7519 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7521 profile=GetImageProfile(image,name);
7522 if (profile != (StringInfo *) NULL)
7524 #ifdef PNG_WRITE_iCCP_SUPPORTED
7525 if ((LocaleCompare(name,"ICC") == 0) ||
7526 (LocaleCompare(name,"ICM") == 0))
7527 png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7528 GetStringInfoDatum(profile),
7529 (png_uint_32) GetStringInfoLength(profile));
7532 png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7533 name,(unsigned char *) name,GetStringInfoDatum(profile),
7534 (png_uint_32) GetStringInfoLength(profile));
7536 if (logging != MagickFalse)
7537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7538 " Setting up text chunk with %s profile",name);
7539 name=GetNextImageProfile(image);
7542 #if defined(PNG_WRITE_sRGB_SUPPORTED)
7543 if ((mng_info->have_write_global_srgb == 0) &&
7544 ((image->rendering_intent != UndefinedIntent) ||
7545 (image->colorspace == sRGBColorspace)))
7548 Note image rendering intent.
7550 if (logging != MagickFalse)
7551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7552 " Setting up sRGB chunk");
7553 (void) png_set_sRGB(ping,ping_info,(
7554 PNG_RenderingIntent_from_Magick_RenderingIntent(
7555 image->rendering_intent)));
7556 png_set_gAMA(ping,ping_info,0.45455);
7558 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
7561 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7565 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7567 if (logging != MagickFalse)
7568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7569 " Setting up gAMA chunk");
7570 png_set_gAMA(ping,ping_info,image->gamma);
7572 if ((mng_info->have_write_global_chrm == 0) &&
7573 (image->chromaticity.red_primary.x != 0.0))
7576 Note image chromaticity.
7577 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7585 wp=image->chromaticity.white_point;
7586 rp=image->chromaticity.red_primary;
7587 gp=image->chromaticity.green_primary;
7588 bp=image->chromaticity.blue_primary;
7590 if (logging != MagickFalse)
7591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7592 " Setting up cHRM chunk");
7593 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7597 ping_interlace_method=image_info->interlace != NoInterlace;
7599 if (mng_info->write_mng)
7600 png_set_sig_bytes(ping,8);
7602 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7604 if (mng_info->write_png_colortype)
7606 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7607 if (ImageIsGray(image) == MagickFalse)
7609 ping_color_type = PNG_COLOR_TYPE_RGB;
7610 if (ping_bit_depth < 8)
7614 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7615 if (ImageIsGray(image) == MagickFalse)
7616 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
7619 if ((mng_info->write_png_depth &&
7620 (int) mng_info->write_png_depth != ping_bit_depth) ||
7621 (mng_info->write_png_colortype &&
7622 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
7623 mng_info->write_png_colortype != 7 &&
7624 !(mng_info->write_png_colortype == 5 && ping_color_type == 0))))
7626 if (logging != MagickFalse)
7628 if (mng_info->write_png_depth)
7630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7631 " Defined PNG:bit-depth=%u, Computed depth=%u",
7632 mng_info->write_png_depth,
7635 if (mng_info->write_png_colortype)
7637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7638 " Defined PNG:color-type=%u, Computed color type=%u",
7639 mng_info->write_png_colortype-1,
7643 if (0) png_error(ping,
7644 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7647 if (image_matte && !image->matte)
7649 /* Add an opaque matte channel */
7650 image->matte = MagickTrue;
7651 (void) SetImageOpacity(image,0);
7652 if (logging != MagickFalse)
7653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7654 " Added an opaque matte channel");
7657 if (image->matte == MagickTrue && ping_color_type < 4)
7659 if (ping_color_type == 3 && ping_num_trans == 0)
7661 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7662 if (logging != MagickFalse)
7663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7664 " Ignoring request to write tRNS chunk with num_trans==0");
7667 (void) png_set_tRNS(ping, ping_info,
7673 if (logging != MagickFalse)
7674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7675 " Writing PNG header chunks");
7677 png_set_IHDR(ping,ping_info,ping_width,ping_height,
7678 ping_bit_depth,ping_color_type,
7679 ping_interlace_method,ping_compression_method,
7680 ping_filter_method);
7682 png_write_info_before_PLTE(ping, ping_info);
7683 /* write any png-chunk-b profiles */
7684 (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7685 png_write_info(ping,ping_info);
7686 /* write any PNG-chunk-m profiles */
7687 (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7689 if (image->page.width || image->page.height)
7694 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
7695 PNGType(chunk,mng_vpAg);
7696 LogPNGChunk((int) logging,mng_vpAg,9L);
7697 PNGLong(chunk+4,(png_uint_32) image->page.width);
7698 PNGLong(chunk+8,(png_uint_32) image->page.height);
7699 chunk[12]=0; /* unit = pixels */
7700 (void) WriteBlob(image,13,chunk);
7701 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
7704 #if (PNG_LIBPNG_VER == 10206)
7705 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
7706 #define PNG_HAVE_IDAT 0x04
7707 ping->mode |= PNG_HAVE_IDAT;
7708 #undef PNG_HAVE_IDAT
7711 png_set_packing(ping);
7715 rowbytes=image->columns;
7716 if (image_depth > 8)
7718 switch (ping_color_type)
7720 case PNG_COLOR_TYPE_RGB:
7723 case PNG_COLOR_TYPE_GRAY_ALPHA:
7726 case PNG_COLOR_TYPE_RGBA:
7734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7735 " Writing PNG image data");
7736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7737 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
7739 png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7740 sizeof(*png_pixels));
7741 if (png_pixels == (unsigned char *) NULL)
7742 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7744 Initialize image scanlines.
7746 if (setjmp(png_jmpbuf(ping)))
7752 if (image_info->verbose)
7753 (void) printf("PNG write has failed.\n");
7755 png_destroy_write_struct(&ping,&ping_info);
7756 if (quantum_info != (QuantumInfo *) NULL)
7757 quantum_info=DestroyQuantumInfo(quantum_info);
7758 if (png_pixels != (unsigned char *) NULL)
7759 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7760 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7761 UnlockSemaphoreInfo(png_semaphore);
7763 return(MagickFalse);
7765 quantum_info=AcquireQuantumInfo(image_info,image);
7766 if (quantum_info == (QuantumInfo *) NULL)
7767 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7768 quantum_info->format=UndefinedQuantumFormat;
7769 quantum_info->depth=image_depth;
7770 num_passes=png_set_interlace_handling(ping);
7771 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7772 !mng_info->write_png32) &&
7773 (mng_info->optimize || mng_info->IsPalette ||
7774 (image_info->type == BilevelType)) &&
7775 !image_matte && ImageIsMonochrome(image))
7777 register const PixelPacket
7780 quantum_info->depth=8;
7781 for (pass=0; pass < num_passes; pass++)
7784 Convert PseudoClass image to a PNG monochrome image.
7786 for (y=0; y < (ssize_t) image->rows; y++)
7788 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7789 if (p == (const PixelPacket *) NULL)
7791 if (mng_info->IsPalette)
7793 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7794 quantum_info,GrayQuantum,png_pixels,&image->exception);
7795 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7796 mng_info->write_png_depth &&
7797 mng_info->write_png_depth != old_bit_depth)
7799 /* Undo pixel scaling */
7800 for (i=0; i < (ssize_t) image->columns; i++)
7801 *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7802 >> (8-old_bit_depth));
7807 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7808 quantum_info,RedQuantum,png_pixels,&image->exception);
7810 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
7811 for (i=0; i < (ssize_t) image->columns; i++)
7812 *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7814 if (logging && y == 0)
7815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7816 " Writing row of pixels (1)");
7817 png_write_row(ping,png_pixels);
7819 if (image->previous == (Image *) NULL)
7821 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7822 if (status == MagickFalse)
7828 for (pass=0; pass < num_passes; pass++)
7830 register const PixelPacket
7833 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7834 !mng_info->write_png32) &&
7836 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
7837 (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7839 for (y=0; y < (ssize_t) image->rows; y++)
7841 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7842 if (p == (const PixelPacket *) NULL)
7844 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7846 if (mng_info->IsPalette)
7847 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7848 quantum_info,GrayQuantum,png_pixels,&image->exception);
7850 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7851 quantum_info,RedQuantum,png_pixels,&image->exception);
7852 if (logging && y == 0)
7853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7854 " Writing GRAY PNG pixels (2)");
7856 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7858 if (logging && y == 0)
7859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7860 " Writing GRAY_ALPHA PNG pixels (2)");
7861 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7862 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7864 if (logging && y == 0)
7865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7866 " Writing row of pixels (2)");
7867 png_write_row(ping,png_pixels);
7869 if (image->previous == (Image *) NULL)
7871 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7872 if (status == MagickFalse)
7877 for (pass=0; pass < num_passes; pass++)
7879 if ((image_depth > 8) || (mng_info->write_png24 ||
7880 mng_info->write_png32 ||
7881 (!mng_info->write_png8 && !mng_info->IsPalette)))
7882 for (y=0; y < (ssize_t) image->rows; y++)
7884 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7885 if (p == (const PixelPacket *) NULL)
7887 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7889 if (image->storage_class == DirectClass)
7890 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7891 quantum_info,RedQuantum,png_pixels,&image->exception);
7893 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7894 quantum_info,GrayQuantum,png_pixels,&image->exception);
7896 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7898 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7899 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7900 if (logging && y == 0)
7901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7902 " Writing GRAY_ALPHA PNG pixels (3)");
7904 else if (image_matte != MagickFalse)
7905 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7906 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7908 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7909 quantum_info,RGBQuantum,png_pixels,&image->exception);
7910 if (logging && y == 0)
7911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7912 " Writing row of pixels (3)");
7913 png_write_row(ping,png_pixels);
7916 /* not ((image_depth > 8) || (mng_info->write_png24 ||
7917 mng_info->write_png32 ||
7918 (!mng_info->write_png8 && !mng_info->IsPalette))) */
7920 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
7921 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
7924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7925 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7926 quantum_info->depth=8;
7929 for (y=0; y < (ssize_t) image->rows; y++)
7931 if (logging && y == 0)
7932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7933 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7934 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7935 if (p == (const PixelPacket *) NULL)
7937 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7938 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7939 quantum_info,GrayQuantum,png_pixels,&image->exception);
7940 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7942 if (logging && y == 0)
7943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7944 " Writing GRAY_ALPHA PNG pixels (4)");
7945 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7946 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7949 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7950 quantum_info,IndexQuantum,png_pixels,&image->exception);
7951 if (logging && y == 0)
7952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7953 " Writing row of pixels (4)");
7954 png_write_row(ping,png_pixels);
7957 if (image->previous == (Image *) NULL)
7959 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7960 if (status == MagickFalse)
7965 if (quantum_info != (QuantumInfo *) NULL)
7966 quantum_info=DestroyQuantumInfo(quantum_info);
7968 if (logging != MagickFalse)
7970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7971 " Wrote PNG image data");
7972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7973 " Width: %.20g",(double) ping_width);
7974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7975 " Height: %.20g",(double) ping_height);
7976 if (mng_info->write_png_depth)
7978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7979 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7982 " PNG bit-depth written: %d",ping_bit_depth);
7983 if (mng_info->write_png_colortype)
7985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7986 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7989 " PNG color-type written: %d",ping_color_type);
7990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7991 " PNG Interlace method: %d",ping_interlace_method);
7994 Generate text chunks.
7996 ResetImagePropertyIterator(image);
7997 property=GetNextImageProperty(image);
7998 while (property != (const char *) NULL)
8003 value=GetImageProperty(image,property);
8004 if (value != (const char *) NULL)
8006 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
8007 text[0].key=(char *) property;
8008 text[0].text=(char *) value;
8009 text[0].text_length=strlen(value);
8010 text[0].compression=image_info->compression == NoCompression ||
8011 (image_info->compression == UndefinedCompression &&
8012 text[0].text_length < 128) ? -1 : 0;
8013 if (logging != MagickFalse)
8015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8016 " Setting up text chunk");
8017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8018 " keyword: %s",text[0].key);
8020 png_set_text(ping,ping_info,text,1);
8021 png_free(ping,text);
8023 property=GetNextImageProperty(image);
8026 /* write any PNG-chunk-e profiles */
8027 (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
8029 if (logging != MagickFalse)
8030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8031 " Writing PNG end info");
8032 png_write_end(ping,ping_info);
8033 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
8035 if (mng_info->page.x || mng_info->page.y ||
8036 (ping_width != mng_info->page.width) ||
8037 (ping_height != mng_info->page.height))
8043 Write FRAM 4 with clipping boundaries followed by FRAM 1.
8045 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
8046 PNGType(chunk,mng_FRAM);
8047 LogPNGChunk((int) logging,mng_FRAM,27L);
8049 chunk[5]=0; /* frame name separator (no name) */
8050 chunk[6]=1; /* flag for changing delay, for next frame only */
8051 chunk[7]=0; /* flag for changing frame timeout */
8052 chunk[8]=1; /* flag for changing frame clipping for next frame */
8053 chunk[9]=0; /* flag for changing frame sync_id */
8054 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
8055 chunk[14]=0; /* clipping boundaries delta type */
8056 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
8058 (png_uint_32) (mng_info->page.x + ping_width));
8059 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
8061 (png_uint_32) (mng_info->page.y + ping_height));
8062 (void) WriteBlob(image,31,chunk);
8063 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
8064 mng_info->old_framing_mode=4;
8065 mng_info->framing_mode=1;
8068 mng_info->framing_mode=3;
8070 if (mng_info->write_mng && !mng_info->need_fram &&
8071 ((int) image->dispose == 3))
8072 (void) ThrowMagickException(&image->exception,GetMagickModule(),
8073 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
8074 "`%s'",image->filename);
8075 image_depth=save_image_depth;
8077 /* Save depth actually written */
8079 s[0]=(char) ping_bit_depth;
8082 (void) SetImageProperty(image,"png:bit-depth-written",s);
8088 png_destroy_write_struct(&ping,&ping_info);
8090 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
8092 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8093 UnlockSemaphoreInfo(png_semaphore);
8096 if (logging != MagickFalse)
8097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8098 " exit WriteOnePNGImage()");
8100 /* End write one PNG image */
8104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8108 % W r i t e P N G I m a g e %
8112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8114 % WritePNGImage() writes a Portable Network Graphics (PNG) or
8115 % Multiple-image Network Graphics (MNG) image file.
8117 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
8119 % The format of the WritePNGImage method is:
8121 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
8123 % A description of each parameter follows:
8125 % o image_info: the image info.
8127 % o image: The image.
8129 % Returns MagickTrue on success, MagickFalse on failure.
8131 % Communicating with the PNG encoder:
8133 % While the datastream written is always in PNG format and normally would
8134 % be given the "png" file extension, this method also writes the following
8135 % pseudo-formats which are subsets of PNG:
8137 % o PNG8: An 8-bit indexed PNG datastream is written. If transparency
8138 % is present, the tRNS chunk must only have values 0 and 255
8139 % (i.e., transparency is binary: fully opaque or fully
8140 % transparent). The pixels contain 8-bit indices even if
8141 % they could be represented with 1, 2, or 4 bits. Note: grayscale
8142 % images will be written as indexed PNG files even though the
8143 % PNG grayscale type might be slightly more efficient.
8145 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
8146 % chunk can be present to convey binary transparency by naming
8147 % one of the colors as transparent.
8149 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
8150 % transparency is permitted, i.e., the alpha sample for
8151 % each pixel can have any value from 0 to 255. The alpha
8152 % channel is present even if the image is fully opaque.
8154 % o -define: For more precise control of the PNG output, you can use the
8155 % Image options "png:bit-depth" and "png:color-type". These
8156 % can be set from the commandline with "-define" and also
8157 % from the application programming interfaces.
8159 % png:color-type can be 0, 2, 3, 4, or 6.
8161 % When png:color-type is 0 (Grayscale), png:bit-depth can
8162 % be 1, 2, 4, 8, or 16.
8164 % When png:color-type is 2 (RGB), png:bit-depth can
8167 % When png:color-type is 3 (Indexed), png:bit-depth can
8168 % be 1, 2, 4, or 8. This refers to the number of bits
8169 % used to store the index. The color samples always have
8170 % bit-depth 8 in indexed PNG files.
8172 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
8173 % png:bit-depth can be 8 or 16.
8175 % If the image cannot be written without loss in the requested PNG8, PNG24,
8176 % or PNG32 format or with the requested bit-depth and color-type without loss,
8177 % a PNG file will not be written, and the encoder will return MagickFalse.
8178 % Since image encoders should not be responsible for the "heavy lifting",
8179 % the user should make sure that ImageMagick has already reduced the
8180 % image depth and number of colors and limit transparency to binary
8181 % transparency prior to attempting to write the image in a format that
8182 % is subject to depth, color, or transparency limitations.
8184 % TODO: Enforce the previous paragraph.
8186 % TODO: Allow all other PNG subformats to be requested via new
8187 % "-define png:bit-depth -define png:color-type" options.
8189 % Note that another definition, "png:bit-depth-written" exists, but it
8190 % is not intended for external use. It is only used internally by the
8191 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
8193 % It is possible to request that the PNG encoder write previously-formatted
8194 % ancillary chunks in the output PNG file, using the "-profile" commandline
8195 % option as shown below or by setting the profile via a programming
8198 % -profile PNG-chunk-x:<file>
8200 % where x is a location flag and <file> is a file containing the chunk
8201 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
8203 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
8204 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
8205 % of the same type, then add a short unique string after the "x" to prevent
8206 % subsequent profiles from overwriting the preceding ones:
8208 % -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
8210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8212 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
8233 assert(image_info != (const ImageInfo *) NULL);
8234 assert(image_info->signature == MagickSignature);
8235 assert(image != (Image *) NULL);
8236 assert(image->signature == MagickSignature);
8237 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8238 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
8239 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8240 if (status == MagickFalse)
8241 return(MagickFalse);
8243 Allocate a MngInfo structure.
8245 have_mng_structure=MagickFalse;
8246 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8247 if (mng_info == (MngInfo *) NULL)
8248 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8250 Initialize members of the MngInfo structure.
8252 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8253 mng_info->image=image;
8254 have_mng_structure=MagickTrue;
8256 /* See if user has requested a specific PNG subformat */
8258 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8259 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8260 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8262 if (mng_info->write_png8)
8264 mng_info->write_png_colortype = /* 3 */ 4;
8265 mng_info->write_png_depth = 8;
8267 #if 0 /* this does not work */
8268 if (image->matte == MagickTrue)
8269 (void) SetImageType(image,PaletteMatteType);
8271 (void) SetImageType(image,PaletteType);
8272 (void) SyncImage(image);
8276 if (mng_info->write_png24)
8278 mng_info->write_png_colortype = /* 2 */ 3;
8279 mng_info->write_png_depth = 8;
8281 if (image->matte == MagickTrue)
8282 (void) SetImageType(image,TrueColorMatteType);
8284 (void) SetImageType(image,TrueColorType);
8285 (void) SyncImage(image);
8288 if (mng_info->write_png32)
8290 mng_info->write_png_colortype = /* 6 */ 7;
8291 mng_info->write_png_depth = 8;
8293 if (image->matte == MagickTrue)
8294 (void) SetImageType(image,TrueColorMatteType);
8296 (void) SetImageType(image,TrueColorType);
8297 (void) SyncImage(image);
8300 value=GetImageOption(image_info,"png:bit-depth");
8301 if (value != (char *) NULL)
8303 if (LocaleCompare(value,"1") == 0)
8304 mng_info->write_png_depth = 1;
8305 else if (LocaleCompare(value,"2") == 0)
8306 mng_info->write_png_depth = 2;
8307 else if (LocaleCompare(value,"4") == 0)
8308 mng_info->write_png_depth = 4;
8309 else if (LocaleCompare(value,"8") == 0)
8310 mng_info->write_png_depth = 8;
8311 else if (LocaleCompare(value,"16") == 0)
8312 mng_info->write_png_depth = 16;
8313 if (logging != MagickFalse)
8314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8315 "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
8317 value=GetImageOption(image_info,"png:color-type");
8318 if (value != (char *) NULL)
8320 /* We must store colortype+1 because 0 is a valid colortype */
8321 if (LocaleCompare(value,"0") == 0)
8322 mng_info->write_png_colortype = 1;
8323 else if (LocaleCompare(value,"2") == 0)
8324 mng_info->write_png_colortype = 3;
8325 else if (LocaleCompare(value,"3") == 0)
8326 mng_info->write_png_colortype = 4;
8327 else if (LocaleCompare(value,"4") == 0)
8328 mng_info->write_png_colortype = 5;
8329 else if (LocaleCompare(value,"6") == 0)
8330 mng_info->write_png_colortype = 7;
8331 if (logging != MagickFalse)
8332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333 "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
8336 status=WriteOnePNGImage(mng_info,image_info,image);
8338 (void) CloseBlob(image);
8340 MngInfoFreeStruct(mng_info,&have_mng_structure);
8341 if (logging != MagickFalse)
8342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8346 #if defined(JNG_SUPPORTED)
8348 /* Write one JNG image */
8349 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8350 const ImageInfo *image_info,Image *image)
8370 jng_alpha_compression_method,
8371 jng_alpha_sample_depth,
8379 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8380 " enter WriteOneJNGImage()");
8382 blob=(unsigned char *) NULL;
8383 jpeg_image=(Image *) NULL;
8384 jpeg_image_info=(ImageInfo *) NULL;
8387 transparent=image_info->type==GrayscaleMatteType ||
8388 image_info->type==TrueColorMatteType;
8390 jng_alpha_sample_depth=0;
8391 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8392 jng_alpha_compression_method=0;
8394 if (image->matte != MagickFalse)
8396 /* if any pixels are transparent */
8397 transparent=MagickTrue;
8398 if (image_info->compression==JPEGCompression)
8399 jng_alpha_compression_method=8;
8405 /* Create JPEG blob, image, and image_info */
8406 if (logging != MagickFalse)
8407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8408 " Creating jpeg_image_info for opacity.");
8409 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8410 if (jpeg_image_info == (ImageInfo *) NULL)
8411 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8412 if (logging != MagickFalse)
8413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8414 " Creating jpeg_image.");
8415 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8416 if (jpeg_image == (Image *) NULL)
8417 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8418 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8419 status=SeparateImageChannel(jpeg_image,OpacityChannel);
8420 status=NegateImage(jpeg_image,MagickFalse);
8421 jpeg_image->matte=MagickFalse;
8422 if (jng_quality >= 1000)
8423 jpeg_image_info->quality=jng_quality/1000;
8425 jpeg_image_info->quality=jng_quality;
8426 jpeg_image_info->type=GrayscaleType;
8427 (void) SetImageType(jpeg_image,GrayscaleType);
8428 (void) AcquireUniqueFilename(jpeg_image->filename);
8429 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8430 "%s",jpeg_image->filename);
8433 /* To do: check bit depth of PNG alpha channel */
8435 /* Check if image is grayscale. */
8436 if (image_info->type != TrueColorMatteType && image_info->type !=
8437 TrueColorType && ImageIsGray(image))
8442 if (jng_alpha_compression_method==0)
8447 /* Encode opacity as a grayscale PNG blob */
8448 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8450 if (logging != MagickFalse)
8451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8452 " Creating PNG blob.");
8455 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8456 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8457 jpeg_image_info->interlace=NoInterlace;
8459 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8462 /* Retrieve sample depth used */
8463 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8464 if (value != (char *) NULL)
8465 jng_alpha_sample_depth= (unsigned int) value[0];
8469 /* Encode opacity as a grayscale JPEG blob */
8471 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8474 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8475 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8476 jpeg_image_info->interlace=NoInterlace;
8477 if (logging != MagickFalse)
8478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8480 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8482 jng_alpha_sample_depth=8;
8483 if (logging != MagickFalse)
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " Successfully read jpeg_image into a blob, length=%.20g.",
8489 /* Destroy JPEG image and image_info */
8490 jpeg_image=DestroyImage(jpeg_image);
8491 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8492 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8495 /* Write JHDR chunk */
8496 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
8497 PNGType(chunk,mng_JHDR);
8498 LogPNGChunk((int) logging,mng_JHDR,16L);
8499 PNGLong(chunk+4,(png_uint_32) image->columns);
8500 PNGLong(chunk+8,(png_uint_32) image->rows);
8501 chunk[12]=jng_color_type;
8502 chunk[13]=8; /* sample depth */
8503 chunk[14]=8; /*jng_image_compression_method */
8504 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8505 chunk[16]=jng_alpha_sample_depth;
8506 chunk[17]=jng_alpha_compression_method;
8507 chunk[18]=0; /*jng_alpha_filter_method */
8508 chunk[19]=0; /*jng_alpha_interlace_method */
8509 (void) WriteBlob(image,20,chunk);
8510 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8511 if (logging != MagickFalse)
8513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8514 " JNG width:%15lu",(unsigned long) image->columns);
8515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8516 " JNG height:%14lu",(unsigned long) image->rows);
8517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8518 " JNG color type:%10d",jng_color_type);
8519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8520 " JNG sample depth:%8d",8);
8521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8522 " JNG compression:%9d",8);
8523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8524 " JNG interlace:%11d",0);
8525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8526 " JNG alpha depth:%9d",jng_alpha_sample_depth);
8527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8528 " JNG alpha compression:%3d",jng_alpha_compression_method);
8529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8530 " JNG alpha filter:%8d",0);
8531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8532 " JNG alpha interlace:%5d",0);
8535 /* Write any JNG-chunk-b profiles */
8536 (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8539 Write leading ancillary chunks
8545 Write JNG bKGD chunk
8556 if (jng_color_type == 8 || jng_color_type == 12)
8560 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
8561 PNGType(chunk,mng_bKGD);
8562 LogPNGChunk((int) logging,mng_bKGD,(size_t) (num_bytes-4L));
8563 red=ScaleQuantumToChar(image->background_color.red);
8564 green=ScaleQuantumToChar(image->background_color.green);
8565 blue=ScaleQuantumToChar(image->background_color.blue);
8572 (void) WriteBlob(image,(size_t) num_bytes,chunk);
8573 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8576 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8579 Write JNG sRGB chunk
8581 (void) WriteBlobMSBULong(image,1L);
8582 PNGType(chunk,mng_sRGB);
8583 LogPNGChunk((int) logging,mng_sRGB,1L);
8584 if (image->rendering_intent != UndefinedIntent)
8585 chunk[4]=(unsigned char)
8586 PNG_RenderingIntent_from_Magick_RenderingIntent(
8587 (image->rendering_intent));
8589 chunk[4]=(unsigned char)
8590 PNG_RenderingIntent_from_Magick_RenderingIntent(
8591 (PerceptualIntent));
8592 (void) WriteBlob(image,5,chunk);
8593 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8597 if (image->gamma != 0.0)
8600 Write JNG gAMA chunk
8602 (void) WriteBlobMSBULong(image,4L);
8603 PNGType(chunk,mng_gAMA);
8604 LogPNGChunk((int) logging,mng_gAMA,4L);
8605 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
8606 (void) WriteBlob(image,8,chunk);
8607 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8609 if ((mng_info->equal_chrms == MagickFalse) &&
8610 (image->chromaticity.red_primary.x != 0.0))
8616 Write JNG cHRM chunk
8618 (void) WriteBlobMSBULong(image,32L);
8619 PNGType(chunk,mng_cHRM);
8620 LogPNGChunk((int) logging,mng_cHRM,32L);
8621 primary=image->chromaticity.white_point;
8622 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
8623 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
8624 primary=image->chromaticity.red_primary;
8625 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
8626 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
8627 primary=image->chromaticity.green_primary;
8628 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
8629 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
8630 primary=image->chromaticity.blue_primary;
8631 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
8632 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
8633 (void) WriteBlob(image,36,chunk);
8634 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8637 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8640 Write JNG pHYs chunk
8642 (void) WriteBlobMSBULong(image,9L);
8643 PNGType(chunk,mng_pHYs);
8644 LogPNGChunk((int) logging,mng_pHYs,9L);
8645 if (image->units == PixelsPerInchResolution)
8647 PNGLong(chunk+4,(png_uint_32)
8648 (image->x_resolution*100.0/2.54+0.5));
8649 PNGLong(chunk+8,(png_uint_32)
8650 (image->y_resolution*100.0/2.54+0.5));
8655 if (image->units == PixelsPerCentimeterResolution)
8657 PNGLong(chunk+4,(png_uint_32)
8658 (image->x_resolution*100.0+0.5));
8659 PNGLong(chunk+8,(png_uint_32)
8660 (image->y_resolution*100.0+0.5));
8665 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
8666 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
8670 (void) WriteBlob(image,13,chunk);
8671 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8674 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8677 Write JNG oFFs chunk
8679 (void) WriteBlobMSBULong(image,9L);
8680 PNGType(chunk,mng_oFFs);
8681 LogPNGChunk((int) logging,mng_oFFs,9L);
8682 PNGsLong(chunk+4,(ssize_t) (image->page.x));
8683 PNGsLong(chunk+8,(ssize_t) (image->page.y));
8685 (void) WriteBlob(image,13,chunk);
8686 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8688 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8690 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8691 PNGType(chunk,mng_vpAg);
8692 LogPNGChunk((int) logging,mng_vpAg,9L);
8693 PNGLong(chunk+4,(png_uint_32) image->page.width);
8694 PNGLong(chunk+8,(png_uint_32) image->page.height);
8695 chunk[12]=0; /* unit = pixels */
8696 (void) WriteBlob(image,13,chunk);
8697 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8703 if (jng_alpha_compression_method==0)
8711 /* Write IDAT chunk header */
8712 if (logging != MagickFalse)
8713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8714 " Write IDAT chunks from blob, length=%.20g.",(double)
8717 /* Copy IDAT chunks */
8720 for (i=8; i<(ssize_t) length; i+=len+12)
8722 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8724 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8726 /* Found an IDAT chunk. */
8727 (void) WriteBlobMSBULong(image,(size_t) len);
8728 LogPNGChunk((int) logging,mng_IDAT,(size_t) len);
8729 (void) WriteBlob(image,(size_t) len+4,p);
8730 (void) WriteBlobMSBULong(image,
8731 crc32(0,p,(uInt) len+4));
8735 if (logging != MagickFalse)
8736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8737 " Skipping %c%c%c%c chunk, length=%.20g.",
8738 *(p),*(p+1),*(p+2),*(p+3),(double) len);
8745 /* Write JDAA chunk header */
8746 if (logging != MagickFalse)
8747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8748 " Write JDAA chunk, length=%.20g.",(double) length);
8749 (void) WriteBlobMSBULong(image,(size_t) length);
8750 PNGType(chunk,mng_JDAA);
8751 LogPNGChunk((int) logging,mng_JDAA,length);
8752 /* Write JDAT chunk(s) data */
8753 (void) WriteBlob(image,4,chunk);
8754 (void) WriteBlob(image,length,blob);
8755 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8758 blob=(unsigned char *) RelinquishMagickMemory(blob);
8761 /* Encode image as a JPEG blob */
8762 if (logging != MagickFalse)
8763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8764 " Creating jpeg_image_info.");
8765 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8766 if (jpeg_image_info == (ImageInfo *) NULL)
8767 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8769 if (logging != MagickFalse)
8770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8771 " Creating jpeg_image.");
8773 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8774 if (jpeg_image == (Image *) NULL)
8775 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8776 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8778 (void) AcquireUniqueFilename(jpeg_image->filename);
8779 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8780 jpeg_image->filename);
8782 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8785 if (logging != MagickFalse)
8786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8787 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
8788 (double) jpeg_image->rows);
8790 if (jng_color_type == 8 || jng_color_type == 12)
8791 jpeg_image_info->type=GrayscaleType;
8792 jpeg_image_info->quality=jng_quality % 1000;
8793 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8794 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8795 if (logging != MagickFalse)
8796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8798 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8799 if (logging != MagickFalse)
8801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8802 " Successfully read jpeg_image into a blob, length=%.20g.",
8805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8806 " Write JDAT chunk, length=%.20g.",(double) length);
8808 /* Write JDAT chunk(s) */
8809 (void) WriteBlobMSBULong(image,(size_t) length);
8810 PNGType(chunk,mng_JDAT);
8811 LogPNGChunk((int) logging,mng_JDAT,length);
8812 (void) WriteBlob(image,4,chunk);
8813 (void) WriteBlob(image,length,blob);
8814 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8816 jpeg_image=DestroyImage(jpeg_image);
8817 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8818 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8819 blob=(unsigned char *) RelinquishMagickMemory(blob);
8821 /* Write any JNG-chunk-e profiles */
8822 (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8824 /* Write IEND chunk */
8825 (void) WriteBlobMSBULong(image,0L);
8826 PNGType(chunk,mng_IEND);
8827 LogPNGChunk((int) logging,mng_IEND,0);
8828 (void) WriteBlob(image,4,chunk);
8829 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8831 if (logging != MagickFalse)
8832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8833 " exit WriteOneJNGImage()");
8839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8843 % W r i t e J N G I m a g e %
8847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8849 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8851 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
8853 % The format of the WriteJNGImage method is:
8855 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8857 % A description of each parameter follows:
8859 % o image_info: the image info.
8861 % o image: The image.
8863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8865 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8882 assert(image_info != (const ImageInfo *) NULL);
8883 assert(image_info->signature == MagickSignature);
8884 assert(image != (Image *) NULL);
8885 assert(image->signature == MagickSignature);
8886 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8887 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8888 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8889 if (status == MagickFalse)
8893 Allocate a MngInfo structure.
8895 have_mng_structure=MagickFalse;
8896 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8897 if (mng_info == (MngInfo *) NULL)
8898 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8900 Initialize members of the MngInfo structure.
8902 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8903 mng_info->image=image;
8904 have_mng_structure=MagickTrue;
8906 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8908 status=WriteOneJNGImage(mng_info,image_info,image);
8909 (void) CloseBlob(image);
8911 (void) CatchImageException(image);
8912 MngInfoFreeStruct(mng_info,&have_mng_structure);
8913 if (logging != MagickFalse)
8914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8921 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8942 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8943 defined(PNG_MNG_FEATURES_SUPPORTED)
8946 all_images_are_gray,
8958 volatile unsigned int
8969 #if (PNG_LIBPNG_VER < 10200)
8970 if (image_info->verbose)
8971 printf("Your PNG library (libpng-%s) is rather old.\n",
8972 PNG_LIBPNG_VER_STRING);
8978 assert(image_info != (const ImageInfo *) NULL);
8979 assert(image_info->signature == MagickSignature);
8980 assert(image != (Image *) NULL);
8981 assert(image->signature == MagickSignature);
8982 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8983 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8984 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8985 if (status == MagickFalse)
8989 Allocate a MngInfo structure.
8991 have_mng_structure=MagickFalse;
8992 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8993 if (mng_info == (MngInfo *) NULL)
8994 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8996 Initialize members of the MngInfo structure.
8998 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8999 mng_info->image=image;
9000 have_mng_structure=MagickTrue;
9001 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
9004 * See if user has requested a specific PNG subformat to be used
9005 * for all of the PNGs in the MNG being written, e.g.,
9007 * convert *.png png8:animation.mng
9009 * To do: check -define png:bit_depth and png:color_type as well,
9010 * or perhaps use mng:bit_depth and mng:color_type instead for
9014 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9015 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9016 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9018 write_jng=MagickFalse;
9019 if (image_info->compression == JPEGCompression)
9020 write_jng=MagickTrue;
9022 mng_info->adjoin=image_info->adjoin &&
9023 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
9025 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9026 optimize=MagickFalse;
9028 optimize=(image_info->type == OptimizeType || image_info->type ==
9031 if (logging != MagickFalse)
9033 /* Log some info about the input */
9037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9038 " Checking input image(s)");
9040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044 " Optimize: FALSE");
9045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9046 " Image_info depth: %.20g",(double) image_info->depth);
9047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9048 " Type: %d",image_info->type);
9051 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9054 " Scene: %.20g",(double) scene++);
9055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056 " Image depth: %.20g",(double) p->depth);
9058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9063 if (p->storage_class == PseudoClass)
9064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9065 " Storage class: PseudoClass");
9067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9068 " Storage class: DirectClass");
9070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071 " Number of colors: %.20g",(double) p->colors);
9073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9074 " Number of colors: unspecified");
9075 if (mng_info->adjoin == MagickFalse)
9081 Sometimes we get PseudoClass images whose RGB values don't match
9082 the colors in the colormap. This code syncs the RGB values.
9088 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9090 if (p->taint && p->storage_class == PseudoClass)
9091 (void) SyncImage(p);
9092 if (mng_info->adjoin == MagickFalse)
9097 #ifdef PNG_BUILD_PALETTE
9101 Sometimes we get DirectClass images that have 256 colors or fewer.
9102 This code will convert them to PseudoClass and build a colormap.
9107 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9109 if (p->storage_class != PseudoClass)
9111 p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
9112 if (p->colors <= 256)
9115 if (p->matte != MagickFalse)
9116 (void) SetImageType(p,PaletteMatteType);
9118 (void) SetImageType(p,PaletteType);
9121 if (mng_info->adjoin == MagickFalse)
9127 use_global_plte=MagickFalse;
9128 all_images_are_gray=MagickFalse;
9129 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9130 need_local_plte=MagickTrue;
9132 need_defi=MagickFalse;
9133 need_matte=MagickFalse;
9134 mng_info->framing_mode=1;
9135 mng_info->old_framing_mode=1;
9138 if (image_info->page != (char *) NULL)
9141 Determine image bounding box.
9143 SetGeometry(image,&mng_info->page);
9144 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
9145 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
9157 mng_info->page=image->page;
9158 need_geom=MagickTrue;
9159 if (mng_info->page.width || mng_info->page.height)
9160 need_geom=MagickFalse;
9162 Check all the scenes.
9164 initial_delay=image->delay;
9165 need_iterations=MagickFalse;
9166 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
9167 mng_info->equal_physs=MagickTrue,
9168 mng_info->equal_gammas=MagickTrue;
9169 mng_info->equal_srgbs=MagickTrue;
9170 mng_info->equal_backgrounds=MagickTrue;
9172 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9173 defined(PNG_MNG_FEATURES_SUPPORTED)
9174 all_images_are_gray=MagickTrue;
9175 mng_info->equal_palettes=MagickFalse;
9176 need_local_plte=MagickFalse;
9178 for (next_image=image; next_image != (Image *) NULL; )
9182 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
9183 mng_info->page.width=next_image->columns+next_image->page.x;
9184 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
9185 mng_info->page.height=next_image->rows+next_image->page.y;
9187 if (next_image->page.x || next_image->page.y)
9188 need_defi=MagickTrue;
9189 if (next_image->matte)
9190 need_matte=MagickTrue;
9191 if ((int) next_image->dispose >= BackgroundDispose)
9192 if (next_image->matte || next_image->page.x || next_image->page.y ||
9193 ((next_image->columns < mng_info->page.width) &&
9194 (next_image->rows < mng_info->page.height)))
9195 mng_info->need_fram=MagickTrue;
9196 if (next_image->iterations)
9197 need_iterations=MagickTrue;
9198 final_delay=next_image->delay;
9199 if (final_delay != initial_delay || final_delay > 1UL*
9200 next_image->ticks_per_second)
9201 mng_info->need_fram=1;
9202 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9203 defined(PNG_MNG_FEATURES_SUPPORTED)
9205 check for global palette possibility.
9207 if (image->matte != MagickFalse)
9208 need_local_plte=MagickTrue;
9209 if (need_local_plte == 0)
9211 if (ImageIsGray(image) == MagickFalse)
9212 all_images_are_gray=MagickFalse;
9213 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
9214 if (use_global_plte == 0)
9215 use_global_plte=mng_info->equal_palettes;
9216 need_local_plte=!mng_info->equal_palettes;
9219 if (GetNextImageInList(next_image) != (Image *) NULL)
9221 if (next_image->background_color.red !=
9222 next_image->next->background_color.red ||
9223 next_image->background_color.green !=
9224 next_image->next->background_color.green ||
9225 next_image->background_color.blue !=
9226 next_image->next->background_color.blue)
9227 mng_info->equal_backgrounds=MagickFalse;
9228 if (next_image->gamma != next_image->next->gamma)
9229 mng_info->equal_gammas=MagickFalse;
9230 if (next_image->rendering_intent !=
9231 next_image->next->rendering_intent)
9232 mng_info->equal_srgbs=MagickFalse;
9233 if ((next_image->units != next_image->next->units) ||
9234 (next_image->x_resolution != next_image->next->x_resolution) ||
9235 (next_image->y_resolution != next_image->next->y_resolution))
9236 mng_info->equal_physs=MagickFalse;
9237 if (mng_info->equal_chrms)
9239 if (next_image->chromaticity.red_primary.x !=
9240 next_image->next->chromaticity.red_primary.x ||
9241 next_image->chromaticity.red_primary.y !=
9242 next_image->next->chromaticity.red_primary.y ||
9243 next_image->chromaticity.green_primary.x !=
9244 next_image->next->chromaticity.green_primary.x ||
9245 next_image->chromaticity.green_primary.y !=
9246 next_image->next->chromaticity.green_primary.y ||
9247 next_image->chromaticity.blue_primary.x !=
9248 next_image->next->chromaticity.blue_primary.x ||
9249 next_image->chromaticity.blue_primary.y !=
9250 next_image->next->chromaticity.blue_primary.y ||
9251 next_image->chromaticity.white_point.x !=
9252 next_image->next->chromaticity.white_point.x ||
9253 next_image->chromaticity.white_point.y !=
9254 next_image->next->chromaticity.white_point.y)
9255 mng_info->equal_chrms=MagickFalse;
9259 next_image=GetNextImageInList(next_image);
9261 if (image_count < 2)
9263 mng_info->equal_backgrounds=MagickFalse;
9264 mng_info->equal_chrms=MagickFalse;
9265 mng_info->equal_gammas=MagickFalse;
9266 mng_info->equal_srgbs=MagickFalse;
9267 mng_info->equal_physs=MagickFalse;
9268 use_global_plte=MagickFalse;
9269 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9270 need_local_plte=MagickTrue;
9272 need_iterations=MagickFalse;
9274 if (mng_info->need_fram == MagickFalse)
9277 Only certain framing rates 100/n are exactly representable without
9278 the FRAM chunk but we'll allow some slop in VLC files
9280 if (final_delay == 0)
9282 if (need_iterations != MagickFalse)
9285 It's probably a GIF with loop; don't run it *too* fast.
9287 if (mng_info->adjoin)
9290 (void) ThrowMagickException(&image->exception,
9291 GetMagickModule(),CoderWarning,
9292 "input has zero delay between all frames; assuming",
9297 mng_info->ticks_per_second=0;
9299 if (final_delay != 0)
9300 mng_info->ticks_per_second=(png_uint_32) (image->ticks_per_second/final_delay);
9301 if (final_delay > 50)
9302 mng_info->ticks_per_second=2;
9303 if (final_delay > 75)
9304 mng_info->ticks_per_second=1;
9305 if (final_delay > 125)
9306 mng_info->need_fram=MagickTrue;
9307 if (need_defi && final_delay > 2 && (final_delay != 4) &&
9308 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
9309 (final_delay != 25) && (final_delay != 50) && (final_delay !=
9310 1UL*image->ticks_per_second))
9311 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
9313 if (mng_info->need_fram != MagickFalse)
9314 mng_info->ticks_per_second=1UL*image->ticks_per_second;
9316 If pseudocolor, we should also check to see if all the
9317 palettes are identical and write a global PLTE if they are.
9321 Write the MNG version 1.0 signature and MHDR chunk.
9323 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9324 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
9325 PNGType(chunk,mng_MHDR);
9326 LogPNGChunk((int) logging,mng_MHDR,28L);
9327 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
9328 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
9329 PNGLong(chunk+12,mng_info->ticks_per_second);
9330 PNGLong(chunk+16,0L); /* layer count=unknown */
9331 PNGLong(chunk+20,0L); /* frame count=unknown */
9332 PNGLong(chunk+24,0L); /* play time=unknown */
9337 if (need_defi || mng_info->need_fram || use_global_plte)
9338 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
9340 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
9344 if (need_defi || mng_info->need_fram || use_global_plte)
9345 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
9347 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
9354 if (need_defi || mng_info->need_fram || use_global_plte)
9355 PNGLong(chunk+28,11L); /* simplicity=LC */
9357 PNGLong(chunk+28,9L); /* simplicity=VLC */
9361 if (need_defi || mng_info->need_fram || use_global_plte)
9362 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
9364 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
9367 (void) WriteBlob(image,32,chunk);
9368 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9369 option=GetImageOption(image_info,"mng:need-cacheoff");
9370 if (option != (const char *) NULL)
9376 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9378 PNGType(chunk,mng_nEED);
9379 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
9380 (void) WriteBlobMSBULong(image,(size_t) length);
9381 LogPNGChunk((int) logging,mng_nEED,(size_t) length);
9383 (void) WriteBlob(image,length,chunk);
9384 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9386 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9387 (GetNextImageInList(image) != (Image *) NULL) &&
9388 (image->iterations != 1))
9391 Write MNG TERM chunk
9393 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9394 PNGType(chunk,mng_TERM);
9395 LogPNGChunk((int) logging,mng_TERM,10L);
9396 chunk[4]=3; /* repeat animation */
9397 chunk[5]=0; /* show last frame when done */
9398 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9399 final_delay/MagickMax(image->ticks_per_second,1)));
9400 if (image->iterations == 0)
9401 PNGLong(chunk+10,PNG_UINT_31_MAX);
9403 PNGLong(chunk+10,(png_uint_32) image->iterations);
9404 if (logging != MagickFalse)
9406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9407 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
9408 final_delay/MagickMax(image->ticks_per_second,1)));
9409 if (image->iterations == 0)
9410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9411 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
9413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9414 " Image iterations: %.20g",(double) image->iterations);
9416 (void) WriteBlob(image,14,chunk);
9417 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9420 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9422 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9423 mng_info->equal_srgbs)
9426 Write MNG sRGB chunk
9428 (void) WriteBlobMSBULong(image,1L);
9429 PNGType(chunk,mng_sRGB);
9430 LogPNGChunk((int) logging,mng_sRGB,1L);
9431 if (image->rendering_intent != UndefinedIntent)
9432 chunk[4]=(unsigned char)
9433 PNG_RenderingIntent_from_Magick_RenderingIntent(
9434 (image->rendering_intent));
9436 chunk[4]=(unsigned char)
9437 PNG_RenderingIntent_from_Magick_RenderingIntent(
9438 (PerceptualIntent));
9439 (void) WriteBlob(image,5,chunk);
9440 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9441 mng_info->have_write_global_srgb=MagickTrue;
9445 if (image->gamma && mng_info->equal_gammas)
9448 Write MNG gAMA chunk
9450 (void) WriteBlobMSBULong(image,4L);
9451 PNGType(chunk,mng_gAMA);
9452 LogPNGChunk((int) logging,mng_gAMA,4L);
9453 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
9454 (void) WriteBlob(image,8,chunk);
9455 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9456 mng_info->have_write_global_gama=MagickTrue;
9458 if (mng_info->equal_chrms)
9464 Write MNG cHRM chunk
9466 (void) WriteBlobMSBULong(image,32L);
9467 PNGType(chunk,mng_cHRM);
9468 LogPNGChunk((int) logging,mng_cHRM,32L);
9469 primary=image->chromaticity.white_point;
9470 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
9471 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
9472 primary=image->chromaticity.red_primary;
9473 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
9474 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
9475 primary=image->chromaticity.green_primary;
9476 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
9477 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
9478 primary=image->chromaticity.blue_primary;
9479 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
9480 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
9481 (void) WriteBlob(image,36,chunk);
9482 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9483 mng_info->have_write_global_chrm=MagickTrue;
9486 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9489 Write MNG pHYs chunk
9491 (void) WriteBlobMSBULong(image,9L);
9492 PNGType(chunk,mng_pHYs);
9493 LogPNGChunk((int) logging,mng_pHYs,9L);
9494 if (image->units == PixelsPerInchResolution)
9496 PNGLong(chunk+4,(png_uint_32)
9497 (image->x_resolution*100.0/2.54+0.5));
9498 PNGLong(chunk+8,(png_uint_32)
9499 (image->y_resolution*100.0/2.54+0.5));
9504 if (image->units == PixelsPerCentimeterResolution)
9506 PNGLong(chunk+4,(png_uint_32)
9507 (image->x_resolution*100.0+0.5));
9508 PNGLong(chunk+8,(png_uint_32)
9509 (image->y_resolution*100.0+0.5));
9514 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
9515 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
9519 (void) WriteBlob(image,13,chunk);
9520 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9523 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9524 or does not cover the entire frame.
9526 if (write_mng && (image->matte || image->page.x > 0 ||
9527 image->page.y > 0 || (image->page.width &&
9528 (image->page.width+image->page.x < mng_info->page.width))
9529 || (image->page.height && (image->page.height+image->page.y
9530 < mng_info->page.height))))
9532 (void) WriteBlobMSBULong(image,6L);
9533 PNGType(chunk,mng_BACK);
9534 LogPNGChunk((int) logging,mng_BACK,6L);
9535 red=ScaleQuantumToShort(image->background_color.red);
9536 green=ScaleQuantumToShort(image->background_color.green);
9537 blue=ScaleQuantumToShort(image->background_color.blue);
9538 PNGShort(chunk+4,red);
9539 PNGShort(chunk+6,green);
9540 PNGShort(chunk+8,blue);
9541 (void) WriteBlob(image,10,chunk);
9542 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9543 if (mng_info->equal_backgrounds)
9545 (void) WriteBlobMSBULong(image,6L);
9546 PNGType(chunk,mng_bKGD);
9547 LogPNGChunk((int) logging,mng_bKGD,6L);
9548 (void) WriteBlob(image,10,chunk);
9549 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9553 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9554 if ((need_local_plte == MagickFalse) &&
9555 (image->storage_class == PseudoClass) &&
9556 (all_images_are_gray == MagickFalse))
9562 Write MNG PLTE chunk
9564 data_length=3*image->colors;
9565 (void) WriteBlobMSBULong(image,data_length);
9566 PNGType(chunk,mng_PLTE);
9567 LogPNGChunk((int) logging,mng_PLTE,data_length);
9568 for (i=0; i < (ssize_t) image->colors; i++)
9570 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9571 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9572 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9574 (void) WriteBlob(image,data_length+4,chunk);
9575 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9576 mng_info->have_write_global_plte=MagickTrue;
9582 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9583 defined(PNG_MNG_FEATURES_SUPPORTED)
9584 mng_info->equal_palettes=MagickFalse;
9588 if (mng_info->adjoin)
9590 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9591 defined(PNG_MNG_FEATURES_SUPPORTED)
9593 If we aren't using a global palette for the entire MNG, check to
9594 see if we can use one for two or more consecutive images.
9596 if (need_local_plte && use_global_plte && !all_images_are_gray)
9598 if (mng_info->IsPalette)
9601 When equal_palettes is true, this image has the same palette
9602 as the previous PseudoClass image
9604 mng_info->have_write_global_plte=mng_info->equal_palettes;
9605 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9606 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9609 Write MNG PLTE chunk
9614 data_length=3*image->colors;
9615 (void) WriteBlobMSBULong(image,data_length);
9616 PNGType(chunk,mng_PLTE);
9617 LogPNGChunk((int) logging,mng_PLTE,data_length);
9618 for (i=0; i < (ssize_t) image->colors; i++)
9620 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9621 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9622 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9624 (void) WriteBlob(image,data_length+4,chunk);
9625 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9626 (uInt) (data_length+4)));
9627 mng_info->have_write_global_plte=MagickTrue;
9631 mng_info->have_write_global_plte=MagickFalse;
9642 previous_x=mng_info->page.x;
9643 previous_y=mng_info->page.y;
9650 mng_info->page=image->page;
9651 if ((mng_info->page.x != previous_x) ||
9652 (mng_info->page.y != previous_y))
9654 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
9655 PNGType(chunk,mng_DEFI);
9656 LogPNGChunk((int) logging,mng_DEFI,12L);
9657 chunk[4]=0; /* object 0 MSB */
9658 chunk[5]=0; /* object 0 LSB */
9659 chunk[6]=0; /* visible */
9660 chunk[7]=0; /* abstract */
9661 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9662 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9663 (void) WriteBlob(image,16,chunk);
9664 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9669 mng_info->write_mng=write_mng;
9671 if ((int) image->dispose >= 3)
9672 mng_info->framing_mode=3;
9674 if (mng_info->need_fram && mng_info->adjoin &&
9675 ((image->delay != mng_info->delay) ||
9676 (mng_info->framing_mode != mng_info->old_framing_mode)))
9678 if (image->delay == mng_info->delay)
9681 Write a MNG FRAM chunk with the new framing mode.
9683 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
9684 PNGType(chunk,mng_FRAM);
9685 LogPNGChunk((int) logging,mng_FRAM,1L);
9686 chunk[4]=(unsigned char) mng_info->framing_mode;
9687 (void) WriteBlob(image,5,chunk);
9688 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9693 Write a MNG FRAM chunk with the delay.
9695 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9696 PNGType(chunk,mng_FRAM);
9697 LogPNGChunk((int) logging,mng_FRAM,10L);
9698 chunk[4]=(unsigned char) mng_info->framing_mode;
9699 chunk[5]=0; /* frame name separator (no name) */
9700 chunk[6]=2; /* flag for changing default delay */
9701 chunk[7]=0; /* flag for changing frame timeout */
9702 chunk[8]=0; /* flag for changing frame clipping */
9703 chunk[9]=0; /* flag for changing frame sync_id */
9704 PNGLong(chunk+10,(png_uint_32)
9705 ((mng_info->ticks_per_second*
9706 image->delay)/MagickMax(image->ticks_per_second,1)));
9707 (void) WriteBlob(image,14,chunk);
9708 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9709 mng_info->delay=(png_uint_32) image->delay;
9711 mng_info->old_framing_mode=mng_info->framing_mode;
9714 #if defined(JNG_SUPPORTED)
9715 if (image_info->compression == JPEGCompression)
9720 if (logging != MagickFalse)
9721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9722 " Writing JNG object.");
9723 /* To do: specify the desired alpha compression method. */
9724 write_info=CloneImageInfo(image_info);
9725 write_info->compression=UndefinedCompression;
9726 status=WriteOneJNGImage(mng_info,write_info,image);
9727 write_info=DestroyImageInfo(write_info);
9732 if (logging != MagickFalse)
9733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9734 " Writing PNG object.");
9735 status=WriteOnePNGImage(mng_info,image_info,image);
9738 if (status == MagickFalse)
9740 MngInfoFreeStruct(mng_info,&have_mng_structure);
9741 (void) CloseBlob(image);
9742 return(MagickFalse);
9744 (void) CatchImageException(image);
9745 if (GetNextImageInList(image) == (Image *) NULL)
9747 image=SyncNextImageInList(image);
9748 status=SetImageProgress(image,SaveImagesTag,scene++,
9749 GetImageListLength(image));
9750 if (status == MagickFalse)
9752 } while (mng_info->adjoin);
9755 while (GetPreviousImageInList(image) != (Image *) NULL)
9756 image=GetPreviousImageInList(image);
9758 Write the MEND chunk.
9760 (void) WriteBlobMSBULong(image,0x00000000L);
9761 PNGType(chunk,mng_MEND);
9762 LogPNGChunk((int) logging,mng_MEND,0L);
9763 (void) WriteBlob(image,4,chunk);
9764 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9767 Relinquish resources.
9769 (void) CloseBlob(image);
9770 MngInfoFreeStruct(mng_info,&have_mng_structure);
9771 if (logging != MagickFalse)
9772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9775 #else /* PNG_LIBPNG_VER > 10011 */
9776 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9779 printf("Your PNG library is too old: You have libpng-%s\n",
9780 PNG_LIBPNG_VER_STRING);
9781 ThrowBinaryException(CoderError,"PNG library is too old",
9782 image_info->filename);
9784 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9786 return(WritePNGImage(image_info,image));
9788 #endif /* PNG_LIBPNG_VER > 10011 */