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 ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2968 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2969 image->colorspace=GRAYColorspace;
2970 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2971 for (i=0; i < (ssize_t) num_text; i++)
2973 /* Check for a profile */
2975 if (logging != MagickFalse)
2976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2977 " Reading PNG text chunk");
2978 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2979 (void) png_read_raw_profile(image,image_info,text,(int) i);
2985 length=text[i].text_length;
2986 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2988 if (value == (char *) NULL)
2990 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2991 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2996 (void) ConcatenateMagickString(value,text[i].text,length+2);
2997 (void) SetImageProperty(image,text[i].key,value);
2998 if (logging != MagickFalse)
2999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3000 " Keyword: %s",text[i].key);
3001 value=DestroyString(value);
3004 #ifdef MNG_OBJECT_BUFFERS
3006 Store the object if necessary.
3008 if (object_id && !mng_info->frozen[object_id])
3010 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3013 create a new object buffer.
3015 mng_info->ob[object_id]=(MngBuffer *)
3016 AcquireAlignedMemory(1,sizeof(MngBuffer));
3017 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3019 mng_info->ob[object_id]->image=(Image *) NULL;
3020 mng_info->ob[object_id]->reference_count=1;
3023 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3024 mng_info->ob[object_id]->frozen)
3026 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3027 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3028 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3030 if (mng_info->ob[object_id]->frozen)
3031 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3032 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3033 "`%s'",image->filename);
3038 if (mng_info->ob[object_id]->image != (Image *) NULL)
3039 mng_info->ob[object_id]->image=DestroyImage
3040 (mng_info->ob[object_id]->image);
3041 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3043 if (mng_info->ob[object_id]->image != (Image *) NULL)
3044 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3046 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3047 ResourceLimitError,"Cloning image for object buffer failed",
3048 "`%s'",image->filename);
3049 if (ping_width > 250000L || ping_height > 250000L)
3050 png_error(ping,"PNG Image dimensions are too large.");
3051 mng_info->ob[object_id]->width=ping_width;
3052 mng_info->ob[object_id]->height=ping_height;
3053 mng_info->ob[object_id]->color_type=ping_color_type;
3054 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3055 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3056 mng_info->ob[object_id]->compression_method=
3057 ping_compression_method;
3058 mng_info->ob[object_id]->filter_method=ping_filter_method;
3059 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3068 Copy the PLTE to the object buffer.
3070 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3071 mng_info->ob[object_id]->plte_length=number_colors;
3072 for (i=0; i < number_colors; i++)
3074 mng_info->ob[object_id]->plte[i]=plte[i];
3078 mng_info->ob[object_id]->plte_length=0;
3083 Relinquish resources.
3085 png_destroy_read_struct(&ping,&ping_info,&end_info);
3087 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
3088 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3089 UnlockSemaphoreInfo(png_semaphore);
3092 if (logging != MagickFalse)
3093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3094 " exit ReadOnePNGImage()");
3097 /* end of reading one PNG image */
3100 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3113 magic_number[MaxTextExtent];
3125 assert(image_info != (const ImageInfo *) NULL);
3126 assert(image_info->signature == MagickSignature);
3127 if (image_info->debug != MagickFalse)
3128 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3129 image_info->filename);
3130 assert(exception != (ExceptionInfo *) NULL);
3131 assert(exception->signature == MagickSignature);
3132 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()");
3133 image=AcquireImage(image_info);
3134 mng_info=(MngInfo *) NULL;
3135 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3136 if (status == MagickFalse)
3137 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3139 Verify PNG signature.
3141 count=ReadBlob(image,8,(unsigned char *) magic_number);
3142 if (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3143 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3145 Allocate a MngInfo structure.
3147 have_mng_structure=MagickFalse;
3148 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
3149 if (mng_info == (MngInfo *) NULL)
3150 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3152 Initialize members of the MngInfo structure.
3154 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3155 mng_info->image=image;
3156 have_mng_structure=MagickTrue;
3159 image=ReadOnePNGImage(mng_info,image_info,exception);
3160 MngInfoFreeStruct(mng_info,&have_mng_structure);
3161 if (image == (Image *) NULL)
3163 if (previous != (Image *) NULL)
3165 if (previous->signature != MagickSignature)
3166 ThrowReaderException(CorruptImageError,"CorruptImage");
3167 (void) CloseBlob(previous);
3168 (void) DestroyImageList(previous);
3170 if (logging != MagickFalse)
3171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3172 "exit ReadPNGImage() with error");
3173 return((Image *) NULL);
3175 (void) CloseBlob(image);
3176 if ((image->columns == 0) || (image->rows == 0))
3178 if (logging != MagickFalse)
3179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3180 "exit ReadPNGImage() with error.");
3181 ThrowReaderException(CorruptImageError,"CorruptImage");
3183 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3185 (void) SetImageType(image,PaletteType);
3186 if (image->matte != MagickFalse)
3188 /* To do: Reduce to binary transparency */
3191 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3193 (void) SetImageType(image,TrueColorType);
3194 image->matte=MagickFalse;
3196 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3197 (void) SetImageType(image,TrueColorMatteType);
3198 if (logging != MagickFalse)
3199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3205 #if defined(JNG_SUPPORTED)
3207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3211 % R e a d O n e J N G I m a g e %
3215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3217 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3218 % (minus the 8-byte signature) and returns it. It allocates the memory
3219 % necessary for the new Image structure and returns a pointer to the new
3222 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3224 % The format of the ReadOneJNGImage method is:
3226 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3227 % ExceptionInfo *exception)
3229 % A description of each parameter follows:
3231 % o mng_info: Specifies a pointer to a MngInfo structure.
3233 % o image_info: the image info.
3235 % o exception: return any errors or warnings in this structure.
3238 static Image *ReadOneJNGImage(MngInfo *mng_info,
3239 const ImageInfo *image_info, ExceptionInfo *exception)
3263 jng_image_sample_depth,
3264 jng_image_compression_method,
3265 jng_image_interlace_method,
3266 jng_alpha_sample_depth,
3267 jng_alpha_compression_method,
3268 jng_alpha_filter_method,
3269 jng_alpha_interlace_method;
3271 register const PixelPacket
3278 register PixelPacket
3281 register unsigned char
3293 jng_alpha_compression_method=0;
3294 jng_alpha_sample_depth=8;
3298 alpha_image=(Image *) NULL;
3299 color_image=(Image *) NULL;
3300 alpha_image_info=(ImageInfo *) NULL;
3301 color_image_info=(ImageInfo *) NULL;
3303 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3304 " enter ReadOneJNGImage()");
3306 image=mng_info->image;
3307 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3310 Allocate next image structure.
3312 if (logging != MagickFalse)
3313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3314 " AcquireNextImage()");
3315 AcquireNextImage(image_info,image);
3316 if (GetNextImageInList(image) == (Image *) NULL)
3317 return((Image *) NULL);
3318 image=SyncNextImageInList(image);
3320 mng_info->image=image;
3323 Signature bytes have already been read.
3326 read_JSEP=MagickFalse;
3327 reading_idat=MagickFalse;
3328 skip_to_iend=MagickFalse;
3332 type[MaxTextExtent];
3341 Read a new JNG chunk.
3343 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3344 2*GetBlobSize(image));
3345 if (status == MagickFalse)
3348 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3349 length=ReadBlobMSBLong(image);
3350 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3352 if (logging != MagickFalse)
3353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3354 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3355 type[0],type[1],type[2],type[3],(double) length);
3357 if (length > PNG_UINT_31_MAX || count == 0)
3358 ThrowReaderException(CorruptImageError,"CorruptImage");
3360 chunk=(unsigned char *) NULL;
3363 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3364 if (chunk == (unsigned char *) NULL)
3365 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3366 for (i=0; i < (ssize_t) length; i++)
3367 chunk[i]=(unsigned char) ReadBlobByte(image);
3370 (void) ReadBlobMSBLong(image); /* read crc word */
3375 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3379 if (memcmp(type,mng_JHDR,4) == 0)
3383 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3384 (p[2] << 8) | p[3]);
3385 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3386 (p[6] << 8) | p[7]);
3387 jng_color_type=p[8];
3388 jng_image_sample_depth=p[9];
3389 jng_image_compression_method=p[10];
3390 jng_image_interlace_method=p[11];
3391 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3393 jng_alpha_sample_depth=p[12];
3394 jng_alpha_compression_method=p[13];
3395 jng_alpha_filter_method=p[14];
3396 jng_alpha_interlace_method=p[15];
3397 if (logging != MagickFalse)
3399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3400 " jng_width: %16lu",(unsigned long) jng_width);
3401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3402 " jng_width: %16lu",(unsigned long) jng_height);
3403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3404 " jng_color_type: %16d",jng_color_type);
3405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3406 " jng_image_sample_depth: %3d",
3407 jng_image_sample_depth);
3408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3409 " jng_image_compression_method:%3d",
3410 jng_image_compression_method);
3411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3412 " jng_image_interlace_method: %3d",
3413 jng_image_interlace_method);
3414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3415 " jng_alpha_sample_depth: %3d",
3416 jng_alpha_sample_depth);
3417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3418 " jng_alpha_compression_method:%3d",
3419 jng_alpha_compression_method);
3420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3421 " jng_alpha_filter_method: %3d",
3422 jng_alpha_filter_method);
3423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3424 " jng_alpha_interlace_method: %3d",
3425 jng_alpha_interlace_method);
3429 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3434 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3435 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3436 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3439 o create color_image
3440 o open color_blob, attached to color_image
3441 o if (color type has alpha)
3442 open alpha_blob, attached to alpha_image
3445 color_image_info=(ImageInfo *)AcquireAlignedMemory(1,sizeof(ImageInfo));
3446 if (color_image_info == (ImageInfo *) NULL)
3447 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3448 GetImageInfo(color_image_info);
3449 color_image=AcquireImage(color_image_info);
3450 if (color_image == (Image *) NULL)
3451 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3453 if (logging != MagickFalse)
3454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3455 " Creating color_blob.");
3456 (void) AcquireUniqueFilename(color_image->filename);
3457 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3459 if (status == MagickFalse)
3460 return((Image *) NULL);
3462 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3464 alpha_image_info=(ImageInfo *)
3465 AcquireAlignedMemory(1,sizeof(ImageInfo));
3466 if (alpha_image_info == (ImageInfo *) NULL)
3467 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3468 GetImageInfo(alpha_image_info);
3469 alpha_image=AcquireImage(alpha_image_info);
3470 if (alpha_image == (Image *) NULL)
3472 alpha_image=DestroyImage(alpha_image);
3473 ThrowReaderException(ResourceLimitError,
3474 "MemoryAllocationFailed");
3476 if (logging != MagickFalse)
3477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3478 " Creating alpha_blob.");
3479 (void) AcquireUniqueFilename(alpha_image->filename);
3480 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3482 if (status == MagickFalse)
3483 return((Image *) NULL);
3484 if (jng_alpha_compression_method == 0)
3489 if (logging != MagickFalse)
3490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3491 " Writing IHDR chunk to alpha_blob.");
3492 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3493 "\211PNG\r\n\032\n");
3494 (void) WriteBlobMSBULong(alpha_image,13L);
3495 PNGType(data,mng_IHDR);
3496 LogPNGChunk((int) logging,mng_IHDR,13L);
3497 PNGLong(data+4,jng_width);
3498 PNGLong(data+8,jng_height);
3499 data[12]=jng_alpha_sample_depth;
3500 data[13]=0; /* color_type gray */
3501 data[14]=0; /* compression method 0 */
3502 data[15]=0; /* filter_method 0 */
3503 data[16]=0; /* interlace_method 0 */
3504 (void) WriteBlob(alpha_image,17,data);
3505 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3508 reading_idat=MagickTrue;
3511 if (memcmp(type,mng_JDAT,4) == 0)
3514 Copy chunk to color_image->blob
3517 if (logging != MagickFalse)
3518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3519 " Copying JDAT chunk data to color_blob.");
3521 (void) WriteBlob(color_image,length,chunk);
3523 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3527 if (memcmp(type,mng_IDAT,4) == 0)
3533 Copy IDAT header and chunk data to alpha_image->blob
3536 if (image_info->ping == MagickFalse)
3538 if (logging != MagickFalse)
3539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3540 " Copying IDAT chunk data to alpha_blob.");
3542 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3543 PNGType(data,mng_IDAT);
3544 LogPNGChunk((int) logging,mng_IDAT,length);
3545 (void) WriteBlob(alpha_image,4,data);
3546 (void) WriteBlob(alpha_image,length,chunk);
3547 (void) WriteBlobMSBULong(alpha_image,
3548 crc32(crc32(0,data,4),chunk,(uInt) length));
3551 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3555 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3558 Copy chunk data to alpha_image->blob
3561 if (image_info->ping == MagickFalse)
3563 if (logging != MagickFalse)
3564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3565 " Copying JDAA chunk data to alpha_blob.");
3567 (void) WriteBlob(alpha_image,length,chunk);
3570 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3574 if (memcmp(type,mng_JSEP,4) == 0)
3576 read_JSEP=MagickTrue;
3578 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3582 if (memcmp(type,mng_bKGD,4) == 0)
3586 image->background_color.red=ScaleCharToQuantum(p[1]);
3587 image->background_color.green=image->background_color.red;
3588 image->background_color.blue=image->background_color.red;
3592 image->background_color.red=ScaleCharToQuantum(p[1]);
3593 image->background_color.green=ScaleCharToQuantum(p[3]);
3594 image->background_color.blue=ScaleCharToQuantum(p[5]);
3596 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3600 if (memcmp(type,mng_gAMA,4) == 0)
3603 image->gamma=((float) mng_get_long(p))*0.00001;
3604 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3608 if (memcmp(type,mng_cHRM,4) == 0)
3612 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3613 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3614 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3615 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3616 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3617 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3618 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3619 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3621 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3625 if (memcmp(type,mng_sRGB,4) == 0)
3629 image->rendering_intent=
3630 PNG_RenderingIntent_to_Magick_RenderingIntent(p[0]);
3631 image->gamma=0.45455f;
3632 image->chromaticity.red_primary.x=0.6400f;
3633 image->chromaticity.red_primary.y=0.3300f;
3634 image->chromaticity.green_primary.x=0.3000f;
3635 image->chromaticity.green_primary.y=0.6000f;
3636 image->chromaticity.blue_primary.x=0.1500f;
3637 image->chromaticity.blue_primary.y=0.0600f;
3638 image->chromaticity.white_point.x=0.3127f;
3639 image->chromaticity.white_point.y=0.3290f;
3641 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3645 if (memcmp(type,mng_oFFs,4) == 0)
3649 image->page.x=mng_get_long(p);
3650 image->page.y=mng_get_long(&p[4]);
3651 if ((int) p[8] != 0)
3653 image->page.x/=10000;
3654 image->page.y/=10000;
3658 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3662 if (memcmp(type,mng_pHYs,4) == 0)
3666 image->x_resolution=(double) mng_get_long(p);
3667 image->y_resolution=(double) mng_get_long(&p[4]);
3668 if ((int) p[8] == PNG_RESOLUTION_METER)
3670 image->units=PixelsPerCentimeterResolution;
3671 image->x_resolution=image->x_resolution/100.0f;
3672 image->y_resolution=image->y_resolution/100.0f;
3675 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3680 if (memcmp(type,mng_iCCP,4) == 0)
3684 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3690 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3692 if (memcmp(type,mng_IEND,4))
3701 Finish up reading image data:
3703 o read main image from color_blob.
3707 o if (color_type has alpha)
3708 if alpha_encoding is PNG
3709 read secondary image from alpha_blob via ReadPNG
3710 if alpha_encoding is JPEG
3711 read secondary image from alpha_blob via ReadJPEG
3715 o copy intensity of secondary image into
3716 opacity samples of main image.
3718 o destroy the secondary image.
3721 (void) CloseBlob(color_image);
3722 if (logging != MagickFalse)
3723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3724 " Reading jng_image from color_blob.");
3725 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3726 color_image->filename);
3727 color_image_info->ping=MagickFalse; /* To do: avoid this */
3728 jng_image=ReadImage(color_image_info,exception);
3729 if (jng_image == (Image *) NULL)
3730 return((Image *) NULL);
3732 (void) RelinquishUniqueFileResource(color_image->filename);
3733 color_image=DestroyImage(color_image);
3734 color_image_info=DestroyImageInfo(color_image_info);
3736 if (jng_image == (Image *) NULL)
3737 return((Image *) NULL);
3739 if (logging != MagickFalse)
3740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3741 " Copying jng_image pixels to main image.");
3742 image->rows=jng_height;
3743 image->columns=jng_width;
3744 length=image->columns*sizeof(PixelPacket);
3745 for (y=0; y < (ssize_t) image->rows; y++)
3747 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3748 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3749 (void) CopyMagickMemory(q,s,length);
3750 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3753 jng_image=DestroyImage(jng_image);
3754 if (image_info->ping == MagickFalse)
3756 if (jng_color_type >= 12)
3758 if (jng_alpha_compression_method == 0)
3762 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3763 PNGType(data,mng_IEND);
3764 LogPNGChunk((int) logging,mng_IEND,0L);
3765 (void) WriteBlob(alpha_image,4,data);
3766 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3768 (void) CloseBlob(alpha_image);
3769 if (logging != MagickFalse)
3770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3771 " Reading opacity from alpha_blob.");
3773 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3774 "%s",alpha_image->filename);
3776 jng_image=ReadImage(alpha_image_info,exception);
3777 if (jng_image != (Image *) NULL)
3778 for (y=0; y < (ssize_t) image->rows; y++)
3780 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3782 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3783 if (image->matte != MagickFalse)
3784 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3785 q->opacity=(Quantum) QuantumRange-s->red;
3787 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3789 q->opacity=(Quantum) QuantumRange-s->red;
3790 if (q->opacity != OpaqueOpacity)
3791 image->matte=MagickTrue;
3793 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3796 (void) RelinquishUniqueFileResource(alpha_image->filename);
3797 alpha_image=DestroyImage(alpha_image);
3798 alpha_image_info=DestroyImageInfo(alpha_image_info);
3799 if (jng_image != (Image *) NULL)
3800 jng_image=DestroyImage(jng_image);
3807 if (mng_info->mng_type == 0)
3809 mng_info->mng_width=jng_width;
3810 mng_info->mng_height=jng_height;
3812 if (image->page.width == 0 && image->page.height == 0)
3814 image->page.width=jng_width;
3815 image->page.height=jng_height;
3817 if (image->page.x == 0 && image->page.y == 0)
3819 image->page.x=mng_info->x_off[mng_info->object_id];
3820 image->page.y=mng_info->y_off[mng_info->object_id];
3824 image->page.y=mng_info->y_off[mng_info->object_id];
3826 mng_info->image_found++;
3827 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3828 2*GetBlobSize(image));
3829 if (logging != MagickFalse)
3830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3831 " exit ReadOneJNGImage()");
3836 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3840 % R e a d J N G I m a g e %
3844 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3846 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3847 % (including the 8-byte signature) and returns it. It allocates the memory
3848 % necessary for the new Image structure and returns a pointer to the new
3851 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3853 % The format of the ReadJNGImage method is:
3855 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3858 % A description of each parameter follows:
3860 % o image_info: the image info.
3862 % o exception: return any errors or warnings in this structure.
3866 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3879 magic_number[MaxTextExtent];
3891 assert(image_info != (const ImageInfo *) NULL);
3892 assert(image_info->signature == MagickSignature);
3893 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3894 assert(exception != (ExceptionInfo *) NULL);
3895 assert(exception->signature == MagickSignature);
3896 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()");
3897 image=AcquireImage(image_info);
3898 mng_info=(MngInfo *) NULL;
3899 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3900 if (status == MagickFalse)
3901 return((Image *) NULL);
3902 if (LocaleCompare(image_info->magick,"JNG") != 0)
3903 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3905 Verify JNG signature.
3907 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3908 if (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3909 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3911 Allocate a MngInfo structure.
3913 have_mng_structure=MagickFalse;
3914 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(*mng_info));
3915 if (mng_info == (MngInfo *) NULL)
3916 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3918 Initialize members of the MngInfo structure.
3920 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3921 have_mng_structure=MagickTrue;
3923 mng_info->image=image;
3925 image=ReadOneJNGImage(mng_info,image_info,exception);
3926 MngInfoFreeStruct(mng_info,&have_mng_structure);
3927 if (image == (Image *) NULL)
3929 if (IsImageObject(previous) != MagickFalse)
3931 (void) CloseBlob(previous);
3932 (void) DestroyImageList(previous);
3934 if (logging != MagickFalse)
3935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3936 "exit ReadJNGImage() with error");
3937 return((Image *) NULL);
3939 (void) CloseBlob(image);
3940 if (image->columns == 0 || image->rows == 0)
3942 if (logging != MagickFalse)
3943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3944 "exit ReadJNGImage() with error");
3945 ThrowReaderException(CorruptImageError,"CorruptImage");
3947 if (logging != MagickFalse)
3948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3953 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3956 page_geometry[MaxTextExtent];
3989 #if defined(MNG_INSERT_LAYERS)
3991 mng_background_color;
3994 register unsigned char
4009 #if defined(MNG_INSERT_LAYERS)
4014 volatile unsigned int
4015 #ifdef MNG_OBJECT_BUFFERS
4016 mng_background_object=0,
4018 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4021 default_frame_timeout,
4023 #if defined(MNG_INSERT_LAYERS)
4029 /* These delays are all measured in image ticks_per_second,
4030 * not in MNG ticks_per_second
4033 default_frame_delay,
4037 #if defined(MNG_INSERT_LAYERS)
4046 previous_fb.bottom=0;
4048 previous_fb.right=0;
4050 default_fb.bottom=0;
4055 Set image_info->type=OptimizeType (new in version 5.4.0) to get the
4056 following optimizations:
4058 o 16-bit depth is reduced to 8 if all pixels contain samples whose
4059 high byte and low byte are identical.
4060 o Opaque matte channel is removed.
4061 o If matte channel is present but only one transparent color is
4062 present, RGB+tRNS is written instead of RGBA
4063 o Grayscale images are reduced to 1, 2, or 4 bit depth if
4064 this can be done without loss.
4065 o Palette is sorted to remove unused entries and to put a
4066 transparent color first, if PNG_SORT_PALETTE is also defined.
4072 assert(image_info != (const ImageInfo *) NULL);
4073 assert(image_info->signature == MagickSignature);
4074 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4075 assert(exception != (ExceptionInfo *) NULL);
4076 assert(exception->signature == MagickSignature);
4077 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()");
4078 image=AcquireImage(image_info);
4079 mng_info=(MngInfo *) NULL;
4080 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4081 if (status == MagickFalse)
4082 return((Image *) NULL);
4083 first_mng_object=MagickFalse;
4085 have_mng_structure=MagickFalse;
4087 Allocate a MngInfo structure.
4089 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
4090 if (mng_info == (MngInfo *) NULL)
4091 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4093 Initialize members of the MngInfo structure.
4095 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4096 mng_info->image=image;
4097 have_mng_structure=MagickTrue;
4098 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
4099 mng_info->optimize=image_info->type == OptimizeType;
4102 if (LocaleCompare(image_info->magick,"MNG") == 0)
4105 magic_number[MaxTextExtent];
4108 Verify MNG signature.
4110 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4111 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4112 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4114 Initialize some nonzero members of the MngInfo structure.
4116 for (i=0; i < MNG_MAX_OBJECTS; i++)
4118 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4119 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4121 mng_info->exists[0]=MagickTrue;
4123 first_mng_object=MagickTrue;
4125 #if defined(MNG_INSERT_LAYERS)
4126 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4128 default_frame_delay=0;
4129 default_frame_timeout=0;
4132 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4134 skip_to_iend=MagickFalse;
4135 term_chunk_found=MagickFalse;
4136 mng_info->framing_mode=1;
4137 #if defined(MNG_INSERT_LAYERS)
4138 mandatory_back=MagickFalse;
4140 #if defined(MNG_INSERT_LAYERS)
4141 mng_background_color=image->background_color;
4143 default_fb=mng_info->frame;
4144 previous_fb=mng_info->frame;
4148 type[MaxTextExtent];
4150 if (LocaleCompare(image_info->magick,"MNG") == 0)
4159 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4160 length=ReadBlobMSBLong(image);
4161 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4163 if (logging != MagickFalse)
4164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4165 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4166 type[0],type[1],type[2],type[3],(double) length);
4168 if (length > PNG_UINT_31_MAX)
4171 ThrowReaderException(CorruptImageError,"CorruptImage");
4173 chunk=(unsigned char *) NULL;
4176 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4177 if (chunk == (unsigned char *) NULL)
4178 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4179 for (i=0; i < (ssize_t) length; i++)
4180 chunk[i]=(unsigned char) ReadBlobByte(image);
4183 (void) ReadBlobMSBLong(image); /* read crc word */
4185 #if !defined(JNG_SUPPORTED)
4186 if (memcmp(type,mng_JHDR,4) == 0)
4188 skip_to_iend=MagickTrue;
4189 if (mng_info->jhdr_warning == 0)
4190 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4191 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4192 mng_info->jhdr_warning++;
4195 if (memcmp(type,mng_DHDR,4) == 0)
4197 skip_to_iend=MagickTrue;
4198 if (mng_info->dhdr_warning == 0)
4199 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4200 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4201 mng_info->dhdr_warning++;
4203 if (memcmp(type,mng_MEND,4) == 0)
4207 if (memcmp(type,mng_IEND,4) == 0)
4208 skip_to_iend=MagickFalse;
4210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4211 if (logging != MagickFalse)
4212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4216 if (memcmp(type,mng_MHDR,4) == 0)
4218 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4219 (p[2] << 8) | p[3]);
4220 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4221 (p[6] << 8) | p[7]);
4222 if (logging != MagickFalse)
4224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4225 " MNG width: %.20g",(double) mng_info->mng_width);
4226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4227 " MNG height: %.20g",(double) mng_info->mng_height);
4230 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4231 if (mng_info->ticks_per_second == 0)
4232 default_frame_delay=0;
4234 default_frame_delay=1UL*image->ticks_per_second/
4235 mng_info->ticks_per_second;
4236 frame_delay=default_frame_delay;
4241 simplicity=(size_t) mng_get_long(p);
4243 mng_type=1; /* Full MNG */
4244 if ((simplicity != 0) && ((simplicity | 11) == 11))
4245 mng_type=2; /* LC */
4246 if ((simplicity != 0) && ((simplicity | 9) == 9))
4247 mng_type=3; /* VLC */
4248 #if defined(MNG_INSERT_LAYERS)
4250 insert_layers=MagickTrue;
4252 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4255 Allocate next image structure.
4257 AcquireNextImage(image_info,image);
4258 if (GetNextImageInList(image) == (Image *) NULL)
4259 return((Image *) NULL);
4260 image=SyncNextImageInList(image);
4261 mng_info->image=image;
4264 if ((mng_info->mng_width > 65535L) ||
4265 (mng_info->mng_height > 65535L))
4266 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4267 (void) FormatMagickString(page_geometry,MaxTextExtent,
4268 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4269 mng_info->mng_height);
4270 mng_info->frame.left=0;
4271 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4272 mng_info->frame.top=0;
4273 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4274 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4275 for (i=0; i < MNG_MAX_OBJECTS; i++)
4276 mng_info->object_clip[i]=mng_info->frame;
4277 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4281 if (memcmp(type,mng_TERM,4) == 0)
4291 final_delay=(png_uint_32) mng_get_long(&p[2]);
4292 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4293 if (mng_iterations == PNG_UINT_31_MAX)
4295 image->iterations=mng_iterations;
4296 term_chunk_found=MagickTrue;
4298 if (logging != MagickFalse)
4300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4301 " repeat=%d",repeat);
4302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4303 " final_delay=%.20g",(double) final_delay);
4304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4305 " image->iterations=%.20g",(double) image->iterations);
4307 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4310 if (memcmp(type,mng_DEFI,4) == 0)
4313 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4314 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4316 object_id=(p[0] << 8) | p[1];
4317 if (mng_type == 2 && object_id != 0)
4318 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4319 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4321 if (object_id > MNG_MAX_OBJECTS)
4324 Instead ofsuing a warning we should allocate a larger
4325 MngInfo structure and continue.
4327 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4328 CoderError,"object id too large","`%s'",image->filename);
4329 object_id=MNG_MAX_OBJECTS;
4331 if (mng_info->exists[object_id])
4332 if (mng_info->frozen[object_id])
4334 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4335 (void) ThrowMagickException(&image->exception,
4336 GetMagickModule(),CoderError,
4337 "DEFI cannot redefine a frozen MNG object","`%s'",
4341 mng_info->exists[object_id]=MagickTrue;
4343 mng_info->invisible[object_id]=p[2];
4345 Extract object offset info.
4349 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) | (p[5] << 16) |
4350 (p[6] << 8) | p[7]);
4351 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) | (p[9] << 16) |
4352 (p[10] << 8) | p[11]);
4353 if (logging != MagickFalse)
4355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4356 " x_off[%d]: %.20g",object_id,(double)
4357 mng_info->x_off[object_id]);
4358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4359 " y_off[%d]: %.20g",object_id,(double)
4360 mng_info->y_off[object_id]);
4364 Extract object clipping info.
4367 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4369 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4372 if (memcmp(type,mng_bKGD,4) == 0)
4374 mng_info->have_global_bkgd=MagickFalse;
4377 mng_info->mng_global_bkgd.red=
4378 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4379 mng_info->mng_global_bkgd.green=
4380 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4381 mng_info->mng_global_bkgd.blue=
4382 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4383 mng_info->have_global_bkgd=MagickTrue;
4385 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4388 if (memcmp(type,mng_BACK,4) == 0)
4390 #if defined(MNG_INSERT_LAYERS)
4392 mandatory_back=p[6];
4395 if (mandatory_back && length > 5)
4397 mng_background_color.red=
4398 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4399 mng_background_color.green=
4400 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4401 mng_background_color.blue=
4402 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4403 mng_background_color.opacity=OpaqueOpacity;
4405 #ifdef MNG_OBJECT_BUFFERS
4407 mng_background_object=(p[7] << 8) | p[8];
4410 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4413 if (memcmp(type,mng_PLTE,4) == 0)
4418 if (length && (length < 769))
4420 if (mng_info->global_plte == (png_colorp) NULL)
4421 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4422 sizeof(*mng_info->global_plte));
4423 for (i=0; i < (ssize_t) (length/3); i++)
4425 mng_info->global_plte[i].red=p[3*i];
4426 mng_info->global_plte[i].green=p[3*i+1];
4427 mng_info->global_plte[i].blue=p[3*i+2];
4429 mng_info->global_plte_length=(unsigned int) (length/3);
4432 for ( ; i < 256; i++)
4434 mng_info->global_plte[i].red=i;
4435 mng_info->global_plte[i].green=i;
4436 mng_info->global_plte[i].blue=i;
4439 mng_info->global_plte_length=256;
4442 mng_info->global_plte_length=0;
4443 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4446 if (memcmp(type,mng_tRNS,4) == 0)
4448 /* read global tRNS */
4451 for (i=0; i < (ssize_t) length; i++)
4452 mng_info->global_trns[i]=p[i];
4455 for ( ; i < 256; i++)
4456 mng_info->global_trns[i]=255;
4458 mng_info->global_trns_length=(unsigned int) length;
4459 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4462 if (memcmp(type,mng_gAMA,4) == 0)
4469 igamma=mng_get_long(p);
4470 mng_info->global_gamma=((float) igamma)*0.00001;
4471 mng_info->have_global_gama=MagickTrue;
4474 mng_info->have_global_gama=MagickFalse;
4475 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4479 if (memcmp(type,mng_cHRM,4) == 0)
4486 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4487 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4488 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4489 mng_info->global_chrm.red_primary.y=0.00001*
4490 mng_get_long(&p[12]);
4491 mng_info->global_chrm.green_primary.x=0.00001*
4492 mng_get_long(&p[16]);
4493 mng_info->global_chrm.green_primary.y=0.00001*
4494 mng_get_long(&p[20]);
4495 mng_info->global_chrm.blue_primary.x=0.00001*
4496 mng_get_long(&p[24]);
4497 mng_info->global_chrm.blue_primary.y=0.00001*
4498 mng_get_long(&p[28]);
4499 mng_info->have_global_chrm=MagickTrue;
4502 mng_info->have_global_chrm=MagickFalse;
4503 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4506 if (memcmp(type,mng_sRGB,4) == 0)
4513 mng_info->global_srgb_intent=
4514 PNG_RenderingIntent_to_Magick_RenderingIntent(p[0]);
4515 mng_info->have_global_srgb=MagickTrue;
4518 mng_info->have_global_srgb=MagickFalse;
4519 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4522 if (memcmp(type,mng_iCCP,4) == 0)
4530 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4533 if (memcmp(type,mng_FRAM,4) == 0)
4536 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4537 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4539 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4540 image->delay=frame_delay;
4541 frame_delay=default_frame_delay;
4542 frame_timeout=default_frame_timeout;
4546 mng_info->framing_mode=p[0];
4547 if (logging != MagickFalse)
4548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4549 " Framing_mode=%d",mng_info->framing_mode);
4553 Note the delay and frame clipping boundaries.
4555 p++; /* framing mode */
4556 while (*p && ((p-chunk) < (ssize_t) length))
4557 p++; /* frame name */
4558 p++; /* frame name terminator */
4559 if ((p-chunk) < (ssize_t) (length-4))
4566 change_delay=(*p++);
4567 change_timeout=(*p++);
4568 change_clipping=(*p++);
4569 p++; /* change_sync */
4572 frame_delay=1UL*image->ticks_per_second*
4574 if (mng_info->ticks_per_second != 0)
4575 frame_delay/=mng_info->ticks_per_second;
4577 frame_delay=PNG_UINT_31_MAX;
4578 if (change_delay == 2)
4579 default_frame_delay=frame_delay;
4581 if (logging != MagickFalse)
4582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4583 " Framing_delay=%.20g",(double) frame_delay);
4587 frame_timeout=1UL*image->ticks_per_second*
4589 if (mng_info->ticks_per_second != 0)
4590 frame_timeout/=mng_info->ticks_per_second;
4592 frame_timeout=PNG_UINT_31_MAX;
4593 if (change_delay == 2)
4594 default_frame_timeout=frame_timeout;
4596 if (logging != MagickFalse)
4597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4598 " Framing_timeout=%.20g",(double) frame_timeout);
4600 if (change_clipping)
4602 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4605 if (logging != MagickFalse)
4606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4607 " Frame_clipping: L=%.20g R=%.20g T=%.20g B=%.20g",
4608 (double) fb.left,(double) fb.right,(double) fb.top,
4609 (double) fb.bottom);
4610 if (change_clipping == 2)
4616 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4617 subframe_width=(size_t) (mng_info->clip.right
4618 -mng_info->clip.left);
4619 subframe_height=(size_t) (mng_info->clip.bottom
4620 -mng_info->clip.top);
4622 Insert a background layer behind the frame if framing_mode is 4.
4624 #if defined(MNG_INSERT_LAYERS)
4625 if (logging != MagickFalse)
4626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4627 " subframe_width=%.20g, subframe_height=%.20g",(double)
4628 subframe_width,(double) subframe_height);
4629 if (insert_layers && (mng_info->framing_mode == 4) &&
4630 (subframe_width) && (subframe_height))
4633 Allocate next image structure.
4635 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4637 AcquireNextImage(image_info,image);
4638 if (GetNextImageInList(image) == (Image *) NULL)
4640 image=DestroyImageList(image);
4641 MngInfoFreeStruct(mng_info,&have_mng_structure);
4642 return((Image *) NULL);
4644 image=SyncNextImageInList(image);
4646 mng_info->image=image;
4647 if (term_chunk_found)
4649 image->start_loop=MagickTrue;
4650 image->iterations=mng_iterations;
4651 term_chunk_found=MagickFalse;
4654 image->start_loop=MagickFalse;
4655 image->columns=subframe_width;
4656 image->rows=subframe_height;
4657 image->page.width=subframe_width;
4658 image->page.height=subframe_height;
4659 image->page.x=mng_info->clip.left;
4660 image->page.y=mng_info->clip.top;
4661 image->background_color=mng_background_color;
4662 image->matte=MagickFalse;
4664 (void) SetImageBackgroundColor(image);
4665 if (logging != MagickFalse)
4666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4667 " Inserted background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4668 (double) mng_info->clip.left,(double) mng_info->clip.right,
4669 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4672 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4675 if (memcmp(type,mng_CLIP,4) == 0)
4684 first_object=(p[0] << 8) | p[1];
4685 last_object=(p[2] << 8) | p[3];
4686 for (i=(int) first_object; i <= (int) last_object; i++)
4688 if (mng_info->exists[i] && !mng_info->frozen[i])
4693 box=mng_info->object_clip[i];
4694 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4697 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4700 if (memcmp(type,mng_SAVE,4) == 0)
4702 for (i=1; i < MNG_MAX_OBJECTS; i++)
4703 if (mng_info->exists[i])
4705 mng_info->frozen[i]=MagickTrue;
4706 #ifdef MNG_OBJECT_BUFFERS
4707 if (mng_info->ob[i] != (MngBuffer *) NULL)
4708 mng_info->ob[i]->frozen=MagickTrue;
4712 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4716 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4721 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4723 for (i=1; i < MNG_MAX_OBJECTS; i++)
4724 MngInfoDiscardObject(mng_info,i);
4731 for (j=0; j < (ssize_t) length; j+=2)
4733 i=p[j] << 8 | p[j+1];
4734 MngInfoDiscardObject(mng_info,i);
4738 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4741 if (memcmp(type,mng_MOVE,4) == 0)
4750 first_object=(p[0] << 8) | p[1];
4751 last_object=(p[2] << 8) | p[3];
4752 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4754 if (mng_info->exists[i] && !mng_info->frozen[i])
4762 old_pair.a=mng_info->x_off[i];
4763 old_pair.b=mng_info->y_off[i];
4764 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4765 mng_info->x_off[i]=new_pair.a;
4766 mng_info->y_off[i]=new_pair.b;
4769 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4773 if (memcmp(type,mng_LOOP,4) == 0)
4775 ssize_t loop_iters=1;
4776 loop_level=chunk[0];
4777 mng_info->loop_active[loop_level]=1; /* mark loop active */
4779 Record starting point.
4781 loop_iters=mng_get_long(&chunk[1]);
4782 if (logging != MagickFalse)
4783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4784 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
4785 (double) loop_iters);
4786 if (loop_iters == 0)
4787 skipping_loop=loop_level;
4790 mng_info->loop_jump[loop_level]=TellBlob(image);
4791 mng_info->loop_count[loop_level]=loop_iters;
4793 mng_info->loop_iteration[loop_level]=0;
4794 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4797 if (memcmp(type,mng_ENDL,4) == 0)
4799 loop_level=chunk[0];
4800 if (skipping_loop > 0)
4802 if (skipping_loop == loop_level)
4805 Found end of zero-iteration loop.
4808 mng_info->loop_active[loop_level]=0;
4813 if (mng_info->loop_active[loop_level] == 1)
4815 mng_info->loop_count[loop_level]--;
4816 mng_info->loop_iteration[loop_level]++;
4817 if (logging != MagickFalse)
4818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4819 " ENDL: LOOP level %.20g has %.20g remaining iterations ",
4820 (double) loop_level,(double)
4821 mng_info->loop_count[loop_level]);
4822 if (mng_info->loop_count[loop_level] != 0)
4824 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4827 ThrowReaderException(CorruptImageError,
4828 "ImproperImageHeader");
4838 mng_info->loop_active[loop_level]=0;
4840 for (i=0; i < loop_level; i++)
4841 if (mng_info->loop_active[i] == 1)
4842 last_level=(short) i;
4843 loop_level=last_level;
4847 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4850 if (memcmp(type,mng_CLON,4) == 0)
4852 if (mng_info->clon_warning == 0)
4853 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4854 CoderError,"CLON is not implemented yet","`%s'",
4856 mng_info->clon_warning++;
4858 if (memcmp(type,mng_MAGN,4) == 0)
4873 magn_first=(p[0] << 8) | p[1];
4877 magn_last=(p[2] << 8) | p[3];
4879 magn_last=magn_first;
4880 #ifndef MNG_OBJECT_BUFFERS
4881 if (magn_first || magn_last)
4882 if (mng_info->magn_warning == 0)
4884 (void) ThrowMagickException(&image->exception,
4885 GetMagickModule(),CoderError,
4886 "MAGN is not implemented yet for nonzero objects",
4887 "`%s'",image->filename);
4888 mng_info->magn_warning++;
4897 magn_mx=(p[5] << 8) | p[6];
4904 magn_my=(p[7] << 8) | p[8];
4911 magn_ml=(p[9] << 8) | p[10];
4918 magn_mr=(p[11] << 8) | p[12];
4925 magn_mt=(p[13] << 8) | p[14];
4932 magn_mb=(p[15] << 8) | p[16];
4941 magn_methy=magn_methx;
4943 if (magn_methx > 5 || magn_methy > 5)
4944 if (mng_info->magn_warning == 0)
4946 (void) ThrowMagickException(&image->exception,
4947 GetMagickModule(),CoderError,
4948 "Unknown MAGN method in MNG datastream","`%s'",
4950 mng_info->magn_warning++;
4952 #ifdef MNG_OBJECT_BUFFERS
4953 /* Magnify existing objects in the range magn_first to magn_last */
4955 if (magn_first == 0 || magn_last == 0)
4957 /* Save the magnification factors for object 0 */
4958 mng_info->magn_mb=magn_mb;
4959 mng_info->magn_ml=magn_ml;
4960 mng_info->magn_mr=magn_mr;
4961 mng_info->magn_mt=magn_mt;
4962 mng_info->magn_mx=magn_mx;
4963 mng_info->magn_my=magn_my;
4964 mng_info->magn_methx=magn_methx;
4965 mng_info->magn_methy=magn_methy;
4968 if (memcmp(type,mng_PAST,4) == 0)
4970 if (mng_info->past_warning == 0)
4971 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4972 CoderError,"PAST is not implemented yet","`%s'",
4974 mng_info->past_warning++;
4976 if (memcmp(type,mng_SHOW,4) == 0)
4978 if (mng_info->show_warning == 0)
4979 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4980 CoderError,"SHOW is not implemented yet","`%s'",
4982 mng_info->show_warning++;
4984 if (memcmp(type,mng_sBIT,4) == 0)
4987 mng_info->have_global_sbit=MagickFalse;
4990 mng_info->global_sbit.gray=p[0];
4991 mng_info->global_sbit.red=p[0];
4992 mng_info->global_sbit.green=p[1];
4993 mng_info->global_sbit.blue=p[2];
4994 mng_info->global_sbit.alpha=p[3];
4995 mng_info->have_global_sbit=MagickTrue;
4998 if (memcmp(type,mng_pHYs,4) == 0)
5002 mng_info->global_x_pixels_per_unit=
5003 (size_t) mng_get_long(p);
5004 mng_info->global_y_pixels_per_unit=
5005 (size_t) mng_get_long(&p[4]);
5006 mng_info->global_phys_unit_type=p[8];
5007 mng_info->have_global_phys=MagickTrue;
5010 mng_info->have_global_phys=MagickFalse;
5012 if (memcmp(type,mng_pHYg,4) == 0)
5014 if (mng_info->phyg_warning == 0)
5015 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5016 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5017 mng_info->phyg_warning++;
5019 if (memcmp(type,mng_BASI,4) == 0)
5021 skip_to_iend=MagickTrue;
5022 if (mng_info->basi_warning == 0)
5023 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5024 CoderError,"BASI is not implemented yet","`%s'",
5026 mng_info->basi_warning++;
5027 #ifdef MNG_BASI_SUPPORTED
5028 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5029 (p[2] << 8) | p[3]);
5030 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5031 (p[6] << 8) | p[7]);
5032 basi_color_type=p[8];
5033 basi_compression_method=p[9];
5034 basi_filter_type=p[10];
5035 basi_interlace_method=p[11];
5037 basi_red=(p[12] << 8) & p[13];
5041 basi_green=(p[14] << 8) & p[15];
5045 basi_blue=(p[16] << 8) & p[17];
5049 basi_alpha=(p[18] << 8) & p[19];
5052 if (basi_sample_depth == 16)
5058 basi_viewable=p[20];
5062 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5065 if (memcmp(type,mng_IHDR,4)
5066 #if defined(JNG_SUPPORTED)
5067 && memcmp(type,mng_JHDR,4)
5071 /* Not an IHDR or JHDR chunk */
5073 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5077 if (logging != MagickFalse)
5078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5079 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5080 mng_info->exists[object_id]=MagickTrue;
5081 mng_info->viewable[object_id]=MagickTrue;
5082 if (mng_info->invisible[object_id])
5084 if (logging != MagickFalse)
5085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5086 " Skipping invisible object");
5087 skip_to_iend=MagickTrue;
5088 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5091 #if defined(MNG_INSERT_LAYERS)
5093 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5094 image_width=(size_t) mng_get_long(p);
5095 image_height=(size_t) mng_get_long(&p[4]);
5097 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5100 Insert a transparent background layer behind the entire animation
5101 if it is not full screen.
5103 #if defined(MNG_INSERT_LAYERS)
5104 if (insert_layers && mng_type && first_mng_object)
5106 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5107 (image_width < mng_info->mng_width) ||
5108 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5109 (image_height < mng_info->mng_height) ||
5110 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5112 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5115 Allocate next image structure.
5117 AcquireNextImage(image_info,image);
5118 if (GetNextImageInList(image) == (Image *) NULL)
5120 image=DestroyImageList(image);
5121 MngInfoFreeStruct(mng_info,&have_mng_structure);
5122 return((Image *) NULL);
5124 image=SyncNextImageInList(image);
5126 mng_info->image=image;
5127 if (term_chunk_found)
5129 image->start_loop=MagickTrue;
5130 image->iterations=mng_iterations;
5131 term_chunk_found=MagickFalse;
5134 image->start_loop=MagickFalse;
5136 Make a background rectangle.
5139 image->columns=mng_info->mng_width;
5140 image->rows=mng_info->mng_height;
5141 image->page.width=mng_info->mng_width;
5142 image->page.height=mng_info->mng_height;
5145 image->background_color=mng_background_color;
5146 (void) SetImageBackgroundColor(image);
5147 if (logging != MagickFalse)
5148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5149 " Inserted transparent background layer, W=%.20g, H=%.20g",
5150 (double) mng_info->mng_width,(double) mng_info->mng_height);
5154 Insert a background layer behind the upcoming image if
5155 framing_mode is 3, and we haven't already inserted one.
5157 if (insert_layers && (mng_info->framing_mode == 3) &&
5158 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5159 (simplicity & 0x08)))
5161 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5164 Allocate next image structure.
5166 AcquireNextImage(image_info,image);
5167 if (GetNextImageInList(image) == (Image *) NULL)
5169 image=DestroyImageList(image);
5170 MngInfoFreeStruct(mng_info,&have_mng_structure);
5171 return((Image *) NULL);
5173 image=SyncNextImageInList(image);
5175 mng_info->image=image;
5176 if (term_chunk_found)
5178 image->start_loop=MagickTrue;
5179 image->iterations=mng_iterations;
5180 term_chunk_found=MagickFalse;
5183 image->start_loop=MagickFalse;
5185 image->columns=subframe_width;
5186 image->rows=subframe_height;
5187 image->page.width=subframe_width;
5188 image->page.height=subframe_height;
5189 image->page.x=mng_info->clip.left;
5190 image->page.y=mng_info->clip.top;
5191 image->background_color=mng_background_color;
5192 image->matte=MagickFalse;
5193 (void) SetImageBackgroundColor(image);
5194 if (logging != MagickFalse)
5195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5196 " Inserted background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5197 (double) mng_info->clip.left,(double) mng_info->clip.right,
5198 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5200 #endif /* MNG_INSERT_LAYERS */
5201 first_mng_object=MagickFalse;
5202 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5205 Allocate next image structure.
5207 AcquireNextImage(image_info,image);
5208 if (GetNextImageInList(image) == (Image *) NULL)
5210 image=DestroyImageList(image);
5211 MngInfoFreeStruct(mng_info,&have_mng_structure);
5212 return((Image *) NULL);
5214 image=SyncNextImageInList(image);
5216 mng_info->image=image;
5217 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5218 GetBlobSize(image));
5219 if (status == MagickFalse)
5221 if (term_chunk_found)
5223 image->start_loop=MagickTrue;
5224 term_chunk_found=MagickFalse;
5227 image->start_loop=MagickFalse;
5228 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5230 image->delay=frame_delay;
5231 frame_delay=default_frame_delay;
5235 image->page.width=mng_info->mng_width;
5236 image->page.height=mng_info->mng_height;
5237 image->page.x=mng_info->x_off[object_id];
5238 image->page.y=mng_info->y_off[object_id];
5239 image->iterations=mng_iterations;
5241 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5243 if (logging != MagickFalse)
5244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5245 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5247 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5249 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5253 mng_info->image=image;
5254 mng_info->mng_type=mng_type;
5255 mng_info->object_id=object_id;
5257 if (memcmp(type,mng_IHDR,4) == 0)
5258 image=ReadOnePNGImage(mng_info,image_info,exception);
5259 #if defined(JNG_SUPPORTED)
5261 image=ReadOneJNGImage(mng_info,image_info,exception);
5264 if (image == (Image *) NULL)
5266 if (IsImageObject(previous) != MagickFalse)
5268 (void) DestroyImageList(previous);
5269 (void) CloseBlob(previous);
5271 MngInfoFreeStruct(mng_info,&have_mng_structure);
5272 return((Image *) NULL);
5274 if (image->columns == 0 || image->rows == 0)
5276 (void) CloseBlob(image);
5277 image=DestroyImageList(image);
5278 MngInfoFreeStruct(mng_info,&have_mng_structure);
5279 return((Image *) NULL);
5281 mng_info->image=image;
5288 if (mng_info->magn_methx || mng_info->magn_methy)
5294 if (logging != MagickFalse)
5295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5296 " Processing MNG MAGN chunk");
5298 if (mng_info->magn_methx == 1)
5300 magnified_width=mng_info->magn_ml;
5301 if (image->columns > 1)
5302 magnified_width += mng_info->magn_mr;
5303 if (image->columns > 2)
5304 magnified_width += (png_uint_32) ((image->columns-2)*(mng_info->magn_mx));
5308 magnified_width=(png_uint_32) image->columns;
5309 if (image->columns > 1)
5310 magnified_width += mng_info->magn_ml-1;
5311 if (image->columns > 2)
5312 magnified_width += mng_info->magn_mr-1;
5313 if (image->columns > 3)
5314 magnified_width += (png_uint_32) ((image->columns-3)*(mng_info->magn_mx-1));
5316 if (mng_info->magn_methy == 1)
5318 magnified_height=mng_info->magn_mt;
5319 if (image->rows > 1)
5320 magnified_height += mng_info->magn_mb;
5321 if (image->rows > 2)
5322 magnified_height += (image->rows-2)*(mng_info->magn_my);
5326 magnified_height=(png_uint_32) image->rows;
5327 if (image->rows > 1)
5328 magnified_height += mng_info->magn_mt-1;
5329 if (image->rows > 2)
5330 magnified_height += mng_info->magn_mb-1;
5331 if (image->rows > 3)
5332 magnified_height += (png_uint_32) ((image->rows-3)*(mng_info->magn_my-1));
5334 if (magnified_height > image->rows ||
5335 magnified_width > image->columns)
5350 register PixelPacket
5363 Allocate next image structure.
5365 if (logging != MagickFalse)
5366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5367 " Allocate magnified image");
5368 AcquireNextImage(image_info,image);
5369 if (GetNextImageInList(image) == (Image *) NULL)
5371 image=DestroyImageList(image);
5372 MngInfoFreeStruct(mng_info,&have_mng_structure);
5373 return((Image *) NULL);
5376 large_image=SyncNextImageInList(image);
5378 large_image->columns=magnified_width;
5379 large_image->rows=magnified_height;
5381 magn_methx=mng_info->magn_methx;
5382 magn_methy=mng_info->magn_methy;
5384 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5385 #define QM unsigned short
5386 if (magn_methx != 1 || magn_methy != 1)
5389 Scale pixels to unsigned shorts to prevent
5390 overflow of intermediate values of interpolations
5392 for (y=0; y < (ssize_t) image->rows; y++)
5394 q=GetAuthenticPixels(image,0,y,image->columns,1,
5396 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5398 q->red=ScaleQuantumToShort(q->red);
5399 q->green=ScaleQuantumToShort(q->green);
5400 q->blue=ScaleQuantumToShort(q->blue);
5401 q->opacity=ScaleQuantumToShort(q->opacity);
5404 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5412 if (image->matte != MagickFalse)
5413 (void) SetImageBackgroundColor(large_image);
5416 large_image->background_color.opacity=OpaqueOpacity;
5417 (void) SetImageBackgroundColor(large_image);
5418 if (magn_methx == 4)
5420 if (magn_methx == 5)
5422 if (magn_methy == 4)
5424 if (magn_methy == 5)
5428 /* magnify the rows into the right side of the large image */
5430 if (logging != MagickFalse)
5431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5432 " Magnify the rows to %.20g",(double) large_image->rows);
5433 m=(ssize_t) mng_info->magn_mt;
5435 length=(size_t) image->columns;
5436 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5437 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5438 if ((prev == (PixelPacket *) NULL) ||
5439 (next == (PixelPacket *) NULL))
5441 image=DestroyImageList(image);
5442 MngInfoFreeStruct(mng_info,&have_mng_structure);
5443 ThrowReaderException(ResourceLimitError,
5444 "MemoryAllocationFailed");
5446 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5447 (void) CopyMagickMemory(next,n,length);
5448 for (y=0; y < (ssize_t) image->rows; y++)
5451 m=(ssize_t) mng_info->magn_mt;
5452 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5453 m=(ssize_t) mng_info->magn_mb;
5454 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5455 m=(ssize_t) mng_info->magn_mb;
5456 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5459 m=(ssize_t) mng_info->magn_my;
5463 if (y < (ssize_t) image->rows-1)
5465 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5467 (void) CopyMagickMemory(next,n,length);
5469 for (i=0; i < m; i++, yy++)
5471 register PixelPacket
5474 assert(yy < (ssize_t) large_image->rows);
5477 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5479 q+=(large_image->columns-image->columns);
5480 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5482 /* TO DO: get color as function of indexes[x] */
5484 if (image->storage_class == PseudoClass)
5489 if (magn_methy <= 1)
5491 *q=(*pixels); /* replicate previous */
5493 else if (magn_methy == 2 || magn_methy == 4)
5500 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5501 -(*pixels).red)+m))/((ssize_t) (m*2))
5503 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5504 -(*pixels).green)+m))/((ssize_t) (m*2))
5506 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5507 -(*pixels).blue)+m))/((ssize_t) (m*2))
5509 if (image->matte != MagickFalse)
5510 (*q).opacity=(QM) (((ssize_t)
5512 -(*pixels).opacity)+m))
5513 /((ssize_t) (m*2))+(*pixels).opacity);
5515 if (magn_methy == 4)
5517 /* Replicate nearest */
5518 if (i <= ((m+1) << 1))
5519 (*q).opacity=(*pixels).opacity+0;
5521 (*q).opacity=(*n).opacity+0;
5524 else /* if (magn_methy == 3 || magn_methy == 5) */
5526 /* Replicate nearest */
5527 if (i <= ((m+1) << 1))
5531 if (magn_methy == 5)
5533 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5534 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5535 +(*pixels).opacity);
5542 if (SyncAuthenticPixels(large_image,exception) == 0)
5546 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5547 next=(PixelPacket *) RelinquishMagickMemory(next);
5549 length=image->columns;
5551 if (logging != MagickFalse)
5552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5553 " Delete original image");
5555 DeleteImageFromList(&image);
5559 mng_info->image=image;
5561 /* magnify the columns */
5562 if (logging != MagickFalse)
5563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5564 " Magnify the columns to %.20g",(double) image->columns);
5566 for (y=0; y < (ssize_t) image->rows; y++)
5568 register PixelPacket
5571 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5572 pixels=q+(image->columns-length);
5574 for (x=(ssize_t) (image->columns-length);
5575 x < (ssize_t) image->columns; x++)
5577 if (x == (ssize_t) (image->columns-length))
5578 m=(ssize_t) mng_info->magn_ml;
5579 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5580 m=(ssize_t) mng_info->magn_mr;
5581 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5582 m=(ssize_t) mng_info->magn_mr;
5583 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5586 m=(ssize_t) mng_info->magn_mx;
5587 for (i=0; i < m; i++)
5589 if (magn_methx <= 1)
5591 /* replicate previous */
5594 else if (magn_methx == 2 || magn_methx == 4)
5601 (*q).red=(QM) ((2*i*((*n).red
5603 /((ssize_t) (m*2))+(*pixels).red);
5604 (*q).green=(QM) ((2*i*((*n).green
5606 +m)/((ssize_t) (m*2))+(*pixels).green);
5607 (*q).blue=(QM) ((2*i*((*n).blue
5609 /((ssize_t) (m*2))+(*pixels).blue);
5610 if (image->matte != MagickFalse)
5611 (*q).opacity=(QM) ((2*i*((*n).opacity
5612 -(*pixels).opacity)+m)/((ssize_t) (m*2))
5613 +(*pixels).opacity);
5615 if (magn_methx == 4)
5617 /* Replicate nearest */
5618 if (i <= ((m+1) << 1))
5619 (*q).opacity=(*pixels).opacity+0;
5621 (*q).opacity=(*n).opacity+0;
5624 else /* if (magn_methx == 3 || magn_methx == 5) */
5626 /* Replicate nearest */
5627 if (i <= ((m+1) << 1))
5631 if (magn_methx == 5)
5634 (*q).opacity=(QM) ((2*i*((*n).opacity
5635 -(*pixels).opacity)+m) /((ssize_t) (m*2))
5636 +(*pixels).opacity);
5644 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5647 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5648 if (magn_methx != 1 || magn_methy != 1)
5651 Rescale pixels to Quantum
5653 for (y=0; y < (ssize_t) image->rows; y++)
5655 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5656 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5658 q->red=ScaleShortToQuantum(q->red);
5659 q->green=ScaleShortToQuantum(q->green);
5660 q->blue=ScaleShortToQuantum(q->blue);
5661 q->opacity=ScaleShortToQuantum(q->opacity);
5664 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5669 if (logging != MagickFalse)
5670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5671 " Finished MAGN processing");
5676 Crop_box is with respect to the upper left corner of the MNG.
5678 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5679 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5680 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5681 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5682 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5683 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5684 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5685 if ((crop_box.left != (mng_info->image_box.left
5686 +mng_info->x_off[object_id])) ||
5687 (crop_box.right != (mng_info->image_box.right
5688 +mng_info->x_off[object_id])) ||
5689 (crop_box.top != (mng_info->image_box.top
5690 +mng_info->y_off[object_id])) ||
5691 (crop_box.bottom != (mng_info->image_box.bottom
5692 +mng_info->y_off[object_id])))
5694 if (logging != MagickFalse)
5695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5696 " Crop the PNG image");
5697 if ((crop_box.left < crop_box.right) &&
5698 (crop_box.top < crop_box.bottom))
5707 Crop_info is with respect to the upper left corner of
5710 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5711 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5712 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5713 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5714 image->page.width=image->columns;
5715 image->page.height=image->rows;
5718 im=CropImage(image,&crop_info,exception);
5719 if (im != (Image *) NULL)
5721 image->columns=im->columns;
5722 image->rows=im->rows;
5723 im=DestroyImage(im);
5724 image->page.width=image->columns;
5725 image->page.height=image->rows;
5726 image->page.x=crop_box.left;
5727 image->page.y=crop_box.top;
5733 No pixels in crop area. The MNG spec still requires
5734 a layer, though, so make a single transparent pixel in
5735 the top left corner.
5740 (void) SetImageBackgroundColor(image);
5741 image->page.width=1;
5742 image->page.height=1;
5747 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
5748 image=mng_info->image;
5752 #if (MAGICKCORE_QUANTUM_DEPTH == 16) /* TO DO: treat Q:32 */
5753 /* Determine if bit depth can be reduced from 16 to 8.
5754 * Note that the method GetImageDepth doesn't check background
5755 * and doesn't handle PseudoClass specially. Also it uses
5756 * multiplication and division by 257 instead of shifting, so
5759 if (mng_info->optimize && image->depth == 16)
5767 ok_to_reduce=(((((size_t) image->background_color.red >> 8) &
5769 == ((size_t) image->background_color.red & 0xff)) &&
5770 ((((size_t) image->background_color.green >> 8) & 0xff)
5771 == ((size_t) image->background_color.green & 0xff)) &&
5772 ((((size_t) image->background_color.blue >> 8) & 0xff)
5773 == ((size_t) image->background_color.blue & 0xff)));
5774 if (ok_to_reduce && image->storage_class == PseudoClass)
5778 for (indx=0; indx < (ssize_t) image->colors; indx++)
5780 ok_to_reduce=(((((size_t) image->colormap[indx].red >>
5782 == ((size_t) image->colormap[indx].red & 0xff)) &&
5783 ((((size_t) image->colormap[indx].green >> 8) & 0xff)
5784 == ((size_t) image->colormap[indx].green & 0xff)) &&
5785 ((((size_t) image->colormap[indx].blue >> 8) & 0xff)
5786 == ((size_t) image->colormap[indx].blue & 0xff)));
5787 if (ok_to_reduce == MagickFalse)
5791 if ((ok_to_reduce != MagickFalse) &&
5792 (image->storage_class != PseudoClass))
5800 for (y=0; y < (ssize_t) image->rows; y++)
5802 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
5803 if (p == (const PixelPacket *) NULL)
5805 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5808 (((size_t) p->red >> 8) & 0xff) ==
5809 ((size_t) p->red & 0xff)) &&
5810 ((((size_t) p->green >> 8) & 0xff) ==
5811 ((size_t) p->green & 0xff)) &&
5812 ((((size_t) p->blue >> 8) & 0xff) ==
5813 ((size_t) p->blue & 0xff)) &&
5815 (((size_t) p->opacity >> 8) & 0xff) ==
5816 ((size_t) p->opacity & 0xff)))));
5817 if (ok_to_reduce == 0)
5828 if (logging != MagickFalse)
5829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5830 " Reducing PNG bit depth to 8 without loss of info");
5834 GetImageException(image,exception);
5835 if (image_info->number_scenes != 0)
5837 if (mng_info->scenes_found >
5838 (ssize_t) (image_info->first_scene+image_info->number_scenes))
5841 if (logging != MagickFalse)
5842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5843 " Finished reading image datastream.");
5844 } while (LocaleCompare(image_info->magick,"MNG") == 0);
5845 (void) CloseBlob(image);
5846 if (logging != MagickFalse)
5847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5848 " Finished reading all image datastreams.");
5849 #if defined(MNG_INSERT_LAYERS)
5850 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
5851 (mng_info->mng_height))
5854 Insert a background layer if nothing else was found.
5856 if (logging != MagickFalse)
5857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5858 " No images found. Inserting a background layer.");
5859 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5862 Allocate next image structure.
5864 AcquireNextImage(image_info,image);
5865 if (GetNextImageInList(image) == (Image *) NULL)
5867 image=DestroyImageList(image);
5868 MngInfoFreeStruct(mng_info,&have_mng_structure);
5869 if (logging != MagickFalse)
5870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5871 " Allocation failed, returning NULL.");
5872 return((Image *) NULL);
5874 image=SyncNextImageInList(image);
5876 image->columns=mng_info->mng_width;
5877 image->rows=mng_info->mng_height;
5878 image->page.width=mng_info->mng_width;
5879 image->page.height=mng_info->mng_height;
5882 image->background_color=mng_background_color;
5883 image->matte=MagickFalse;
5884 if (image_info->ping == MagickFalse)
5885 (void) SetImageBackgroundColor(image);
5886 mng_info->image_found++;
5889 image->iterations=mng_iterations;
5890 if (mng_iterations == 1)
5891 image->start_loop=MagickTrue;
5892 while (GetPreviousImageInList(image) != (Image *) NULL)
5895 if (image_count > 10*mng_info->image_found)
5897 if (logging != MagickFalse)
5898 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
5899 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5900 CoderError,"Linked list is corrupted, beginning of list not found",
5901 "`%s'",image_info->filename);
5902 return((Image *) NULL);
5904 image=GetPreviousImageInList(image);
5905 if (GetNextImageInList(image) == (Image *) NULL)
5907 if (logging != MagickFalse)
5908 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
5909 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5910 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
5911 image_info->filename);
5914 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
5915 GetNextImageInList(image) ==
5918 if (logging != MagickFalse)
5919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5920 " First image null");
5921 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5922 CoderError,"image->next for first image is NULL but shouldn't be.",
5923 "`%s'",image_info->filename);
5925 if (mng_info->image_found == 0)
5927 if (logging != MagickFalse)
5928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5929 " No visible images found.");
5930 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5931 CoderError,"No visible images in file","`%s'",image_info->filename);
5932 if (image != (Image *) NULL)
5933 image=DestroyImageList(image);
5934 MngInfoFreeStruct(mng_info,&have_mng_structure);
5935 return((Image *) NULL);
5938 if (mng_info->ticks_per_second)
5939 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
5940 final_delay/mng_info->ticks_per_second;
5942 image->start_loop=MagickTrue;
5943 /* Find final nonzero image delay */
5944 final_image_delay=0;
5945 while (GetNextImageInList(image) != (Image *) NULL)
5948 final_image_delay=image->delay;
5949 image=GetNextImageInList(image);
5951 if (final_delay < final_image_delay)
5952 final_delay=final_image_delay;
5953 image->delay=final_delay;
5954 if (logging != MagickFalse)
5955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5956 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
5957 (double) final_delay);
5958 if (logging != MagickFalse)
5964 image=GetFirstImageInList(image);
5965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5966 " Before coalesce:");
5967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5968 " scene 0 delay=%.20g",(double) image->delay);
5969 while (GetNextImageInList(image) != (Image *) NULL)
5971 image=GetNextImageInList(image);
5972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5973 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
5977 image=GetFirstImageInList(image);
5978 #ifdef MNG_COALESCE_LAYERS
5988 if (logging != MagickFalse)
5989 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
5991 next_image=CoalesceImages(image,&image->exception);
5992 if (next_image == (Image *) NULL)
5993 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5994 image=DestroyImageList(image);
5996 for (next=image; next != (Image *) NULL; next=next_image)
5998 next->page.width=mng_info->mng_width;
5999 next->page.height=mng_info->mng_height;
6002 next->scene=scene++;
6003 next_image=GetNextImageInList(next);
6004 if (next_image == (Image *) NULL)
6006 if (next->delay == 0)
6009 next_image->previous=GetPreviousImageInList(next);
6010 if (GetPreviousImageInList(next) == (Image *) NULL)
6013 next->previous->next=next_image;
6014 next=DestroyImage(next);
6020 while (GetNextImageInList(image) != (Image *) NULL)
6021 image=GetNextImageInList(image);
6022 image->dispose=BackgroundDispose;
6024 if (logging != MagickFalse)
6030 image=GetFirstImageInList(image);
6031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6032 " After coalesce:");
6033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6034 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6035 (double) image->dispose);
6036 while (GetNextImageInList(image) != (Image *) NULL)
6038 image=GetNextImageInList(image);
6039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6040 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6041 (double) image->delay,(double) image->dispose);
6044 image=GetFirstImageInList(image);
6045 MngInfoFreeStruct(mng_info,&have_mng_structure);
6046 have_mng_structure=MagickFalse;
6047 if (logging != MagickFalse)
6048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6049 return(GetFirstImageInList(image));
6051 #else /* PNG_LIBPNG_VER > 10011 */
6052 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6054 printf("Your PNG library is too old: You have libpng-%s\n",
6055 PNG_LIBPNG_VER_STRING);
6056 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6057 "PNG library is too old","`%s'",image_info->filename);
6058 return(Image *) NULL;
6060 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6062 return(ReadPNGImage(image_info,exception));
6064 #endif /* PNG_LIBPNG_VER > 10011 */
6068 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6072 % R e g i s t e r P N G I m a g e %
6076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6078 % RegisterPNGImage() adds properties for the PNG image format to
6079 % the list of supported formats. The properties include the image format
6080 % tag, a method to read and/or write the format, whether the format
6081 % supports the saving of more than one frame to the same file or blob,
6082 % whether the format supports native in-memory I/O, and a brief
6083 % description of the format.
6085 % The format of the RegisterPNGImage method is:
6087 % size_t RegisterPNGImage(void)
6090 ModuleExport size_t RegisterPNGImage(void)
6093 version[MaxTextExtent];
6101 "See http://www.libpng.org/ for details about the PNG format."
6105 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6110 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6115 #if defined(PNG_LIBPNG_VER_STRING)
6116 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6117 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6118 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6120 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6121 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6125 entry=SetMagickInfo("MNG");
6126 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6127 #if defined(MAGICKCORE_PNG_DELEGATE)
6128 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6129 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6131 entry->magick=(IsImageFormatHandler *) IsMNG;
6132 entry->description=ConstantString("Multiple-image Network Graphics");
6133 if (*version != '\0')
6134 entry->version=ConstantString(version);
6135 entry->module=ConstantString("PNG");
6136 entry->note=ConstantString(MNGNote);
6137 (void) RegisterMagickInfo(entry);
6139 entry=SetMagickInfo("PNG");
6140 #if defined(MAGICKCORE_PNG_DELEGATE)
6141 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6142 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6144 entry->magick=(IsImageFormatHandler *) IsPNG;
6145 entry->adjoin=MagickFalse;
6146 entry->description=ConstantString("Portable Network Graphics");
6147 entry->module=ConstantString("PNG");
6148 if (*version != '\0')
6149 entry->version=ConstantString(version);
6150 entry->note=ConstantString(PNGNote);
6151 (void) RegisterMagickInfo(entry);
6153 entry=SetMagickInfo("PNG8");
6154 #if defined(MAGICKCORE_PNG_DELEGATE)
6155 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6156 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6158 entry->magick=(IsImageFormatHandler *) IsPNG;
6159 entry->adjoin=MagickFalse;
6160 entry->description=ConstantString(
6161 "8-bit indexed with optional binary transparency");
6162 entry->module=ConstantString("PNG");
6163 (void) RegisterMagickInfo(entry);
6165 entry=SetMagickInfo("PNG24");
6167 #if defined(ZLIB_VERSION)
6168 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6169 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6170 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6172 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6173 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6176 if (*version != '\0')
6177 entry->version=ConstantString(version);
6178 #if defined(MAGICKCORE_PNG_DELEGATE)
6179 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6180 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6182 entry->magick=(IsImageFormatHandler *) IsPNG;
6183 entry->adjoin=MagickFalse;
6184 entry->description=ConstantString("opaque 24-bit RGB");
6185 entry->module=ConstantString("PNG");
6186 (void) RegisterMagickInfo(entry);
6188 entry=SetMagickInfo("PNG32");
6189 #if defined(MAGICKCORE_PNG_DELEGATE)
6190 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6191 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6193 entry->magick=(IsImageFormatHandler *) IsPNG;
6194 entry->adjoin=MagickFalse;
6195 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6196 entry->module=ConstantString("PNG");
6197 (void) RegisterMagickInfo(entry);
6199 entry=SetMagickInfo("JNG");
6200 #if defined(JNG_SUPPORTED)
6201 #if defined(MAGICKCORE_PNG_DELEGATE)
6202 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6203 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6206 entry->magick=(IsImageFormatHandler *) IsJNG;
6207 entry->adjoin=MagickFalse;
6208 entry->description=ConstantString("JPEG Network Graphics");
6209 entry->module=ConstantString("PNG");
6210 entry->note=ConstantString(JNGNote);
6211 (void) RegisterMagickInfo(entry);
6212 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6213 png_semaphore=AllocateSemaphoreInfo();
6215 return(MagickImageCoderSignature);
6219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6223 % U n r e g i s t e r P N G I m a g e %
6227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6229 % UnregisterPNGImage() removes format registrations made by the
6230 % PNG module from the list of supported formats.
6232 % The format of the UnregisterPNGImage method is:
6234 % UnregisterPNGImage(void)
6237 ModuleExport void UnregisterPNGImage(void)
6239 (void) UnregisterMagickInfo("MNG");
6240 (void) UnregisterMagickInfo("PNG");
6241 (void) UnregisterMagickInfo("PNG8");
6242 (void) UnregisterMagickInfo("PNG24");
6243 (void) UnregisterMagickInfo("PNG32");
6244 (void) UnregisterMagickInfo("JNG");
6245 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6246 if (png_semaphore != (SemaphoreInfo *) NULL)
6247 DestroySemaphoreInfo(&png_semaphore);
6251 #if defined(MAGICKCORE_PNG_DELEGATE)
6252 #if PNG_LIBPNG_VER > 10011
6254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6258 % W r i t e M N G I m a g e %
6262 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6264 % WriteMNGImage() writes an image in the Portable Network Graphics
6265 % Group's "Multiple-image Network Graphics" encoded image format.
6267 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6269 % The format of the WriteMNGImage method is:
6271 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6273 % A description of each parameter follows.
6275 % o image_info: the image info.
6277 % o image: The image.
6280 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6281 % "To do" under ReadPNGImage):
6283 % Fix problem with palette sorting (when PNG_SORT_PALETTE is enabled,
6284 % some GIF animations don't convert properly)
6286 % Preserve all unknown and not-yet-handled known chunks found in input
6287 % PNG file and copy them into output PNG files according to the PNG
6290 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6292 % Improve selection of color type (use indexed-colour or indexed-colour
6293 % with tRNS when 256 or fewer unique RGBA values are present).
6295 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6296 % This will be complicated if we limit ourselves to generating MNG-LC
6297 % files. For now we ignore disposal method 3 and simply overlay the next
6300 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6301 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6302 % [mostly done 15 June 1999 but still need to take care of tRNS]
6304 % Check for identical sRGB and replace with a global sRGB (and remove
6305 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6306 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6307 % local gAMA/cHRM with local sRGB if appropriate).
6309 % Check for identical sBIT chunks and write global ones.
6311 % Provide option to skip writing the signature tEXt chunks.
6313 % Use signatures to detect identical objects and reuse the first
6314 % instance of such objects instead of writing duplicate objects.
6316 % Use a smaller-than-32k value of compression window size when
6319 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6320 % ancillary text chunks and save profiles.
6322 % Provide an option to force LC files (to ensure exact framing rate)
6325 % Provide an option to force VLC files instead of LC, even when offsets
6326 % are present. This will involve expanding the embedded images with a
6327 % transparent region at the top and/or left.
6331 png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6332 png_info *ping_info, unsigned char *profile_type, unsigned char
6333 *profile_description, unsigned char *profile_data, png_uint_32 length)
6352 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6354 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6357 if (image_info->verbose)
6359 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6360 (char *) profile_type, (double) length);
6362 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6363 description_length=(png_uint_32) strlen((const char *) profile_description);
6364 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6365 + description_length);
6366 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6367 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6368 text[0].key[0]='\0';
6369 (void) ConcatenateMagickString(text[0].key,
6370 "Raw profile type ",MaxTextExtent);
6371 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6375 (void) CopyMagickString(dp,(const char *) profile_description,
6377 dp+=description_length;
6379 (void) FormatMagickString(dp,allocated_length-
6380 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6382 for (i=0; i < (ssize_t) length; i++)
6386 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6387 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6391 text[0].text_length=(png_size_t) (dp-text[0].text);
6392 text[0].compression=image_info->compression == NoCompression ||
6393 (image_info->compression == UndefinedCompression &&
6394 text[0].text_length < 128) ? -1 : 0;
6395 if (text[0].text_length <= allocated_length)
6396 png_set_text(ping,ping_info,text,1);
6397 png_free(ping,text[0].text);
6398 png_free(ping,text[0].key);
6399 png_free(ping,text);
6402 static MagickBooleanType png_write_chunk_from_profile(Image *image,
6403 const char *string, int logging)
6416 ResetImageProfileIterator(image);
6417 for (name=GetNextImageProfile(image); name != (const char *) NULL; ){
6418 profile=GetImageProfile(image,name);
6419 if (profile != (const StringInfo *) NULL)
6424 if (LocaleNCompare(name,string,11) == 0) {
6425 if (logging != MagickFalse)
6426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6427 " Found %s profile",name);
6429 png_profile=CloneStringInfo(profile);
6430 data=GetStringInfoDatum(png_profile),
6431 length=(png_uint_32) GetStringInfoLength(png_profile);
6436 (void) WriteBlobMSBULong(image,length-5); /* data length */
6437 (void) WriteBlob(image,length-1,data+1);
6438 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6439 png_profile=DestroyStringInfo(png_profile);
6442 name=GetNextImageProfile(image);
6447 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6448 const ImageInfo *image_info,Image *image)
6450 /* Write one PNG image */
6469 ping_trans_alpha[256];
6497 register IndexPacket
6514 ping_interlace_method,
6515 ping_compression_method,
6529 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6530 " enter WriteOnePNGImage()");
6532 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6533 LockSemaphoreInfo(png_semaphore);
6536 /* Initialize some stuff */
6539 ping_interlace_method=0,
6540 ping_compression_method=0,
6541 ping_filter_method=0,
6544 ping_background.red = 0;
6545 ping_background.green = 0;
6546 ping_background.blue = 0;
6547 ping_background.gray = 0;
6548 ping_background.index = 0;
6550 ping_trans_color.red=0;
6551 ping_trans_color.green=0;
6552 ping_trans_color.blue=0;
6553 ping_trans_color.gray=0;
6555 quantum_info = (QuantumInfo *) NULL;
6556 image_colors=image->colors;
6557 image_depth=image->depth;
6558 image_matte=image->matte;
6560 if (image->colorspace != RGBColorspace)
6561 (void) TransformImageColorspace(image,RGBColorspace);
6562 mng_info->IsPalette=image->storage_class == PseudoClass &&
6563 image_colors <= 256 && !IsOpaqueImage(image,&image->exception);
6564 mng_info->optimize=image_info->type == OptimizeType;
6567 Allocate the PNG structures
6569 #ifdef PNG_USER_MEM_SUPPORTED
6570 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
6571 PNGErrorHandler,PNGWarningHandler,(void *) NULL,
6572 (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
6574 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
6575 PNGErrorHandler,PNGWarningHandler);
6577 if (ping == (png_struct *) NULL)
6578 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6579 ping_info=png_create_info_struct(ping);
6580 if (ping_info == (png_info *) NULL)
6582 png_destroy_write_struct(&ping,(png_info **) NULL);
6583 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6585 png_set_write_fn(ping,image,png_put_data,png_flush_data);
6586 png_pixels=(unsigned char *) NULL;
6588 if (setjmp(png_jmpbuf(ping)))
6594 if (image_info->verbose)
6595 (void) printf("PNG write has failed.\n");
6597 png_destroy_write_struct(&ping,&ping_info);
6598 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6599 UnlockSemaphoreInfo(png_semaphore);
6601 return(MagickFalse);
6604 Prepare PNG for writing.
6606 #if defined(PNG_MNG_FEATURES_SUPPORTED)
6607 if (mng_info->write_mng)
6608 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
6610 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
6611 if (mng_info->write_mng)
6612 png_permit_empty_plte(ping,MagickTrue);
6616 ping_width=(png_uint_32) image->columns;
6617 ping_height=(png_uint_32) image->rows;
6618 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
6620 if (mng_info->write_png_depth != 0)
6621 image_depth=mng_info->write_png_depth;
6622 /* Adjust requested depth to next higher valid depth if necessary */
6623 if (image_depth > 8)
6625 if ((image_depth > 4) && (image_depth < 8))
6627 if (image_depth == 3)
6629 if (logging != MagickFalse)
6631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6632 " width=%.20g",(double) ping_width);
6633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6634 " height=%.20g",(double) ping_height);
6635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6636 " image_matte=%.20g",(double) image->matte);
6637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6638 " image_depth=%.20g",(double) image->depth);
6639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6640 " requested PNG image_depth=%.20g",(double) image->depth);
6642 save_image_depth=image_depth;
6643 ping_bit_depth=(png_byte) save_image_depth;
6644 #if defined(PNG_pHYs_SUPPORTED)
6645 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
6646 (!mng_info->write_mng || !mng_info->equal_physs))
6655 if (image->units == PixelsPerInchResolution)
6657 unit_type=PNG_RESOLUTION_METER;
6658 x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
6659 y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
6661 else if (image->units == PixelsPerCentimeterResolution)
6663 unit_type=PNG_RESOLUTION_METER;
6664 x_resolution=(png_uint_32) (100.0*image->x_resolution);
6665 y_resolution=(png_uint_32) (100.0*image->y_resolution);
6669 unit_type=PNG_RESOLUTION_UNKNOWN;
6670 x_resolution=(png_uint_32) image->x_resolution;
6671 y_resolution=(png_uint_32) image->y_resolution;
6673 png_set_pHYs(ping,ping_info,x_resolution,y_resolution,unit_type);
6674 if (logging != MagickFalse)
6675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6676 " Setting up pHYs chunk");
6679 #if defined(PNG_oFFs_SUPPORTED)
6680 if (image->page.x || image->page.y)
6682 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
6683 (png_int_32) image->page.y, 0);
6684 if (logging != MagickFalse)
6685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6686 " Setting up oFFs chunk");
6689 if (image_matte && (!mng_info->adjoin || !mng_info->equal_backgrounds))
6694 if (image_depth < MAGICKCORE_QUANTUM_DEPTH)
6699 maxval=(1UL << image_depth)-1;
6700 background.red=(png_uint_16)
6701 (QuantumScale*(maxval*image->background_color.red));
6702 background.green=(png_uint_16)
6703 (QuantumScale*(maxval*image->background_color.green));
6704 background.blue=(png_uint_16)
6705 (QuantumScale*(maxval*image->background_color.blue));
6706 background.gray=(png_uint_16)
6707 (QuantumScale*(maxval*PixelIntensity(&image->background_color)));
6711 background.red=image->background_color.red;
6712 background.green=image->background_color.green;
6713 background.blue=image->background_color.blue;
6715 (png_uint_16) PixelIntensity(&image->background_color);
6717 background.index=(png_byte) background.gray;
6718 if (logging != MagickFalse)
6719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6720 " Setting up bKGd chunk");
6721 png_set_bKGD(ping,ping_info,&background);
6724 Select the color type.
6728 if ((mng_info->write_png_colortype-1) == PNG_COLOR_TYPE_PALETTE)
6729 mng_info->write_png8=MagickTrue;
6730 if (mng_info->write_png8)
6732 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6734 image_depth=ping_bit_depth;
6736 /* TO DO: make this a function cause it's used twice, except
6737 for reducing the sample depth from 8. */
6746 number_colors=image_colors;
6747 if ((image->storage_class == DirectClass) || (number_colors > 256))
6749 GetQuantizeInfo(&quantize_info);
6750 quantize_info.dither=IsPaletteImage(image,&image->exception) ==
6751 MagickFalse ? MagickTrue : MagickFalse;
6752 quantize_info.number_colors= (matte != MagickFalse ? 255UL :
6754 (void) QuantizeImage(&quantize_info,image);
6755 number_colors=image_colors;
6756 (void) SyncImage(image);
6757 if (logging != MagickFalse)
6758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6759 " Colors quantized to %.20g",(double) number_colors);
6762 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
6766 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6767 #if defined(PNG_SORT_PALETTE)
6768 save_number_colors=image_colors;
6769 if (CompressColormapTransFirst(image) == MagickFalse)
6770 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6771 number_colors=image->colors;
6772 image_colors=save_number_colors;
6774 palette=(png_color *) AcquireQuantumMemory(257,
6776 if (palette == (png_color *) NULL)
6777 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6778 if (logging != MagickFalse)
6779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6780 " Setting up PLTE chunk with %d colors",
6781 (int) number_colors);
6782 for (i=0; i < (ssize_t) number_colors; i++)
6784 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6785 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6786 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6787 if (logging != MagickFalse)
6788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6789 #if MAGICKCORE_QUANTUM_DEPTH == 8
6790 " %3ld (%3d,%3d,%3d)",
6792 " %5ld (%5d,%5d,%5d)",
6794 (long) i,palette[i].red,palette[i].green,palette[i].blue);
6800 palette[i].red=ScaleQuantumToChar((Quantum) QuantumRange);
6801 palette[i].green=ScaleQuantumToChar((Quantum) QuantumRange);
6802 palette[i].blue=ScaleQuantumToChar((Quantum) QuantumRange);
6804 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6805 palette=(png_colorp) RelinquishMagickMemory(palette);
6806 image_depth=ping_bit_depth;
6817 Identify which colormap entry is transparent.
6819 assert(number_colors <= 256);
6820 for (i=0; i < (ssize_t) number_colors; i++)
6822 exception=(&image->exception);
6823 for (y=0; y < (ssize_t) image->rows; y++)
6825 register const PixelPacket
6828 p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6829 if (p == (PixelPacket *) NULL)
6831 indexes=GetAuthenticIndexQueue(image);
6832 for (x=0; x < (ssize_t) image->columns; x++)
6834 if (p->opacity != OpaqueOpacity)
6836 indexes[x]=(IndexPacket) (number_colors-1);
6837 trans_alpha[(ssize_t) indexes[x]]=(png_byte) (255-
6838 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
6842 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6845 for (i=0; i < (ssize_t) number_colors; i++)
6846 if (trans_alpha[i] != 255)
6847 ping_num_trans=(unsigned short) (i+1);
6849 if (ping_num_trans == 0)
6850 png_set_invalid(ping, ping_info, PNG_INFO_tRNS);
6851 if (!png_get_valid(ping, ping_info, PNG_INFO_tRNS))
6853 if (ping_num_trans != 0)
6855 for (i=0; i<256; i++)
6856 ping_trans_alpha[i]=(png_byte) trans_alpha[i];
6859 (void) png_set_tRNS(ping, ping_info,
6865 Identify which colormap entry is the background color.
6867 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
6868 if (IsPNGColorEqual(ping_background,image->colormap[i]))
6870 ping_background.index=(png_byte) i;
6872 if (image_matte != MagickFalse)
6874 /* TO DO: reduce to binary transparency */
6876 } /* end of write_png8 */
6877 else if (mng_info->write_png24)
6879 image_matte=MagickFalse;
6880 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6882 else if (mng_info->write_png32)
6884 image_matte=MagickTrue;
6885 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6889 image_depth=ping_bit_depth;
6890 if (mng_info->write_png_colortype)
6892 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
6893 image_matte=MagickFalse;
6894 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6895 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6896 image_matte=MagickTrue;
6900 if (logging != MagickFalse)
6901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6902 "Selecting PNG colortype");
6903 ping_color_type=(png_byte) ((matte == MagickTrue)?
6904 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6905 if(image_info->type == TrueColorType)
6907 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6908 image_matte=MagickFalse;
6910 if(image_info->type == TrueColorMatteType)
6912 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6913 image_matte=MagickTrue;
6915 if ((image_info->type == UndefinedType ||
6916 image_info->type == OptimizeType ||
6917 image_info->type == GrayscaleType) &&
6918 image_matte == MagickFalse && ImageIsGray(image))
6920 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
6921 image_matte=MagickFalse;
6923 if ((image_info->type == UndefinedType ||
6924 image_info->type == OptimizeType ||
6925 image_info->type == GrayscaleMatteType) &&
6926 image_matte == MagickTrue && ImageIsGray(image))
6928 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
6929 image_matte=MagickTrue;
6932 if (logging != MagickFalse)
6933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6934 "Selected PNG colortype=%d",ping_color_type);
6936 if (ping_bit_depth < 8)
6938 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6939 ping_color_type == PNG_COLOR_TYPE_RGB ||
6940 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6944 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
6946 if (image->matte == MagickFalse && image->colors < 256)
6948 if (ImageIsMonochrome(image))
6951 if (ping_bit_depth < (int)mng_info->write_png_depth)
6952 ping_bit_depth = mng_info->write_png_depth;
6956 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
6961 if (image->colors == 0)
6964 (void) ThrowMagickException(&image->exception,
6965 GetMagickModule(),CoderError,
6966 "image has 0 colors", "`%s'","");
6969 if (logging != MagickFalse)
6970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6972 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
6973 ping_bit_depth <<= 1;
6975 if (logging != MagickFalse)
6977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6978 " Number of colors: %.20g",(double) image_colors);
6979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6980 " Tentative PNG bit depth: %d",ping_bit_depth);
6982 if (mng_info->write_png_depth)
6984 old_bit_depth=ping_bit_depth;
6985 if (ping_bit_depth < (int)mng_info->write_png_depth)
6987 ping_bit_depth = mng_info->write_png_depth;
6988 if (ping_bit_depth > 8)
6990 if (ping_bit_depth != (int) old_bit_depth)
6992 if (logging != MagickFalse)
6993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6994 " Colors increased to %.20g",(double)
7001 image_depth=ping_bit_depth;
7002 if (logging != MagickFalse)
7004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7005 " Tentative PNG color type: %.20g",(double) ping_color_type);
7006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7007 " image_info->type: %.20g",(double) image_info->type);
7008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7009 " image_depth: %.20g",(double) image_depth);
7010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7011 " ping_bit_depth: %.20g",(double) ping_bit_depth);
7014 if (matte && (mng_info->optimize || mng_info->IsPalette))
7016 register const PixelPacket
7019 p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
7020 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
7021 for (y=0; y < (ssize_t) image->rows; y++)
7023 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7024 if (p == (const PixelPacket *) NULL)
7026 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7028 if (IsGray(p) == MagickFalse)
7030 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
7037 Determine if there is any transparent color.
7039 for (y=0; y < (ssize_t) image->rows; y++)
7041 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7042 if (p == (const PixelPacket *) NULL)
7044 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7046 if (p->opacity != OpaqueOpacity)
7053 if ((y == (ssize_t) image->rows) && (x == (ssize_t) image->columns))
7056 No transparent pixels are present. Change 4 or 6 to 0 or 2.
7058 image_matte=MagickFalse;
7059 ping_color_type&=0x03;
7067 if (ping_bit_depth == 8)
7069 if (ping_bit_depth == 4)
7071 if (ping_bit_depth == 2)
7073 if (ping_bit_depth == 1)
7075 ping_trans_color.red=(png_uint_16)
7076 (ScaleQuantumToShort(GetRedPixelComponent(p)) & mask);
7077 ping_trans_color.green=(png_uint_16)
7078 (ScaleQuantumToShort(GetGreenPixelComponent(p)) & mask);
7079 ping_trans_color.blue=(png_uint_16)
7080 (ScaleQuantumToShort(GetBluePixelComponent(p)) & mask);
7081 ping_trans_color.gray=(png_uint_16)
7082 (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
7083 ping_trans_color.index=(png_byte)
7084 (ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p))));
7085 (void) png_set_tRNS(ping, ping_info, NULL, 0,
7088 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7091 Determine if there is one and only one transparent color
7092 and if so if it is fully transparent.
7094 for (y=0; y < (ssize_t) image->rows; y++)
7096 p=GetVirtualPixels(image,0,y,image->columns,1,
7099 if (p == (const PixelPacket *) NULL)
7101 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7103 if (p->opacity != OpaqueOpacity)
7105 if (IsPNGColorEqual(ping_trans_color,*p) == 0)
7107 break; /* Can't use RGB + tRNS for multiple
7108 transparent colors. */
7110 if (p->opacity != (Quantum) TransparentOpacity)
7112 break; /* Can't use RGB + tRNS for
7113 semitransparency. */
7118 if (IsPNGColorEqual(ping_trans_color,*p))
7119 break; /* Can't use RGB + tRNS when another pixel
7120 having the same RGB samples is
7129 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7131 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7133 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
7134 if (image_depth == 8)
7136 ping_trans_color.red&=0xff;
7137 ping_trans_color.green&=0xff;
7138 ping_trans_color.blue&=0xff;
7139 ping_trans_color.gray&=0xff;
7144 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7145 image_matte=MagickFalse;
7146 if ((mng_info->optimize || mng_info->IsPalette) &&
7147 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
7148 ImageIsGray(image) && (!image_matte || image_depth >= 8))
7151 if (image_matte != MagickFalse)
7152 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
7155 ping_color_type=PNG_COLOR_TYPE_GRAY;
7156 if (save_image_depth == 16 && image_depth == 8)
7157 ping_trans_color.gray*=0x0101;
7159 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
7160 image_depth=MAGICKCORE_QUANTUM_DEPTH;
7161 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
7162 image_colors=one << image_depth;
7163 if (image_depth > 8)
7168 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7170 if(!mng_info->write_png_depth)
7173 while ((int) (one << ping_bit_depth)
7174 < (ssize_t) image_colors)
7175 ping_bit_depth <<= 1;
7178 else if (mng_info->optimize && ping_color_type ==
7179 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
7180 mng_info->IsPalette)
7183 /* Check if grayscale is reducible */
7185 depth_4_ok=MagickTrue,
7186 depth_2_ok=MagickTrue,
7187 depth_1_ok=MagickTrue;
7189 for (i=0; i < (ssize_t) image_colors; i++)
7194 intensity=ScaleQuantumToChar(image->colormap[i].red);
7196 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
7197 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
7198 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
7199 depth_2_ok=depth_1_ok=MagickFalse;
7200 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
7201 depth_1_ok=MagickFalse;
7203 if (depth_1_ok && mng_info->write_png_depth <= 1)
7205 else if (depth_2_ok && mng_info->write_png_depth <= 2)
7207 else if (depth_4_ok && mng_info->write_png_depth <= 4)
7211 image_depth=ping_bit_depth;
7214 if (mng_info->IsPalette)
7219 number_colors=image_colors;
7221 if (image_depth <= 8)
7226 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
7227 if (mng_info->have_write_global_plte && !matte)
7229 png_set_PLTE(ping,ping_info,NULL,0);
7231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7232 " Setting up empty PLTE chunk");
7236 #if defined(PNG_SORT_PALETTE)
7240 if (mng_info->optimize)
7242 save_number_colors=image_colors;
7243 if (CompressColormapTransFirst(image) == MagickFalse)
7244 ThrowWriterException(ResourceLimitError,
7245 "MemoryAllocationFailed");
7246 number_colors=image->colors;
7247 image_colors=save_number_colors;
7250 palette=(png_color *) AcquireQuantumMemory(257,
7252 if (palette == (png_color *) NULL)
7253 ThrowWriterException(ResourceLimitError,
7254 "MemoryAllocationFailed");
7255 for (i=0; i < (ssize_t) number_colors; i++)
7257 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7258 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7259 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7263 " Setting up PLTE chunk with %d colors",
7264 (int) number_colors);
7265 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
7266 palette=(png_colorp) RelinquishMagickMemory(palette);
7268 /* color_type is PNG_COLOR_TYPE_PALETTE */
7269 if (!mng_info->write_png_depth)
7276 while ((one << ping_bit_depth) < number_colors)
7277 ping_bit_depth <<= 1;
7285 register const PixelPacket
7291 register const IndexPacket
7295 Identify which colormap entry is transparent.
7297 assert(number_colors <= 256);
7298 for (i=0; i < (ssize_t) number_colors; i++)
7300 exception=(&image->exception);
7301 for (y=0; y < (ssize_t) image->rows; y++)
7303 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7304 if (p == (const PixelPacket *) NULL)
7306 packet_indexes=GetVirtualIndexQueue(image);
7307 for (x=0; x < (ssize_t) image->columns; x++)
7309 if (p->opacity != OpaqueOpacity)
7314 packet_index=packet_indexes[x];
7315 assert((size_t) packet_index < number_colors);
7316 if (trans[(ssize_t) packet_index] != 256)
7318 if (trans[(ssize_t) packet_index] != (png_byte) (255-
7319 ScaleQuantumToChar(GetOpacityPixelComponent(p))))
7321 ping_color_type=(png_byte)
7322 PNG_COLOR_TYPE_RGB_ALPHA;
7326 trans[(ssize_t) packet_index]=(png_byte) (255-
7327 ScaleQuantumToChar(GetOpacityPixelComponent(p)));
7331 if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
7334 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7335 png_set_invalid(ping,ping_info,PNG_INFO_PLTE);
7336 mng_info->IsPalette=MagickFalse;
7337 (void) SyncImage(image);
7339 (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7340 " Cannot write image as indexed PNG, writing RGBA.");
7344 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7346 for (i=0; i < (ssize_t) number_colors; i++)
7348 if (trans[i] == 256)
7350 if (trans[i] != 255)
7351 ping_num_trans=(unsigned short) (i+1);
7354 if (ping_num_trans == 0)
7355 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7356 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7358 if (ping_num_trans != 0)
7360 for (i=0; i < (ssize_t) number_colors; i++)
7361 ping_trans_alpha[i]=(png_byte) trans[i];
7369 if (image_depth < 8)
7371 if ((save_image_depth == 16) && (image_depth == 8))
7373 ping_trans_color.red*=0x0101;
7374 ping_trans_color.green*=0x0101;
7375 ping_trans_color.blue*=0x0101;
7376 ping_trans_color.gray*=0x0101;
7381 Adjust background and transparency samples in sub-8-bit grayscale files.
7383 if (ping_bit_depth < 8 && ping_color_type ==
7384 PNG_COLOR_TYPE_GRAY)
7395 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
7398 background.gray=(png_uint_16)
7399 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7401 if (logging != MagickFalse)
7402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7403 " Setting up bKGD chunk");
7404 png_set_bKGD(ping,ping_info,&background);
7406 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7407 ping_trans_color.gray));
7410 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7413 Identify which colormap entry is the background color.
7419 number_colors=image_colors;
7421 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
7422 if (IsPNGColorEqual(ping_background,image->colormap[i]))
7425 ping_background.index=(png_byte) i;
7428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7429 " Setting up bKGD chunk with index=%d",(int) i);
7431 png_set_bKGD(ping,ping_info,&ping_background);
7434 if (logging != MagickFalse)
7435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7436 " PNG color type: %d",ping_color_type);
7438 Initialize compression level and filtering.
7440 if (logging != MagickFalse)
7441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7442 " Setting up deflate compression");
7443 if (logging != MagickFalse)
7444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7445 " Compression buffer size: 32768");
7446 png_set_compression_buffer_size(ping,32768L);
7447 if (logging != MagickFalse)
7448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7449 " Compression mem level: 9");
7450 png_set_compression_mem_level(ping, 9);
7451 quality=image->quality == UndefinedCompressionQuality ? 75UL :
7458 level=(int) MagickMin((ssize_t) quality/10,9);
7459 if (logging != MagickFalse)
7460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7461 " Compression level: %d",level);
7462 png_set_compression_level(ping,level);
7466 if (logging != MagickFalse)
7467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7468 " Compression strategy: Z_HUFFMAN_ONLY");
7469 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7471 if (logging != MagickFalse)
7472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7473 " Setting up filtering");
7474 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7476 /* This became available in libpng-1.0.9. Output must be a MNG. */
7477 if (mng_info->write_mng && ((quality % 10) == 7))
7479 if (logging != MagickFalse)
7480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7481 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
7482 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
7485 if (logging != MagickFalse)
7486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7493 if ((quality % 10) > 5)
7494 base_filter=PNG_ALL_FILTERS;
7496 if ((quality % 10) != 5)
7497 base_filter=(int) quality % 10;
7499 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
7500 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
7502 base_filter=PNG_NO_FILTERS;
7504 base_filter=PNG_ALL_FILTERS;
7505 if (logging != MagickFalse)
7507 if (base_filter == PNG_ALL_FILTERS)
7508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7509 " Base filter method: ADAPTIVE");
7511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7512 " Base filter method: NONE");
7514 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7517 ResetImageProfileIterator(image);
7518 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7520 profile=GetImageProfile(image,name);
7521 if (profile != (StringInfo *) NULL)
7523 #ifdef PNG_WRITE_iCCP_SUPPORTED
7524 if ((LocaleCompare(name,"ICC") == 0) ||
7525 (LocaleCompare(name,"ICM") == 0))
7526 png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7527 GetStringInfoDatum(profile),
7528 (png_uint_32) GetStringInfoLength(profile));
7531 png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7532 name,(unsigned char *) name,GetStringInfoDatum(profile),
7533 (png_uint_32) GetStringInfoLength(profile));
7535 if (logging != MagickFalse)
7536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7537 " Setting up text chunk with %s profile",name);
7538 name=GetNextImageProfile(image);
7541 #if defined(PNG_WRITE_sRGB_SUPPORTED)
7542 if ((mng_info->have_write_global_srgb == 0) &&
7543 ((image->rendering_intent != UndefinedIntent) ||
7544 (image->colorspace == sRGBColorspace)))
7547 Note image rendering intent.
7549 if (logging != MagickFalse)
7550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7551 " Setting up sRGB chunk");
7552 (void) png_set_sRGB(ping,ping_info,(
7553 PNG_RenderingIntent_from_Magick_RenderingIntent(
7554 image->rendering_intent)));
7555 png_set_gAMA(ping,ping_info,0.45455);
7557 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
7560 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7564 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7566 if (logging != MagickFalse)
7567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7568 " Setting up gAMA chunk");
7569 png_set_gAMA(ping,ping_info,image->gamma);
7571 if ((mng_info->have_write_global_chrm == 0) &&
7572 (image->chromaticity.red_primary.x != 0.0))
7575 Note image chromaticity.
7576 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7584 wp=image->chromaticity.white_point;
7585 rp=image->chromaticity.red_primary;
7586 gp=image->chromaticity.green_primary;
7587 bp=image->chromaticity.blue_primary;
7589 if (logging != MagickFalse)
7590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7591 " Setting up cHRM chunk");
7592 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7596 ping_interlace_method=image_info->interlace != NoInterlace;
7598 if (mng_info->write_mng)
7599 png_set_sig_bytes(ping,8);
7601 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7603 if (mng_info->write_png_colortype)
7605 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7606 if (ImageIsGray(image) == MagickFalse)
7608 ping_color_type = PNG_COLOR_TYPE_RGB;
7609 if (ping_bit_depth < 8)
7613 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7614 if (ImageIsGray(image) == MagickFalse)
7615 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
7618 if ((mng_info->write_png_depth &&
7619 (int) mng_info->write_png_depth != ping_bit_depth) ||
7620 (mng_info->write_png_colortype &&
7621 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
7622 mng_info->write_png_colortype != 7 &&
7623 !(mng_info->write_png_colortype == 5 && ping_color_type == 0))))
7625 if (logging != MagickFalse)
7627 if (mng_info->write_png_depth)
7629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7630 " Defined PNG:bit-depth=%u, Computed depth=%u",
7631 mng_info->write_png_depth,
7634 if (mng_info->write_png_colortype)
7636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7637 " Defined PNG:color-type=%u, Computed color type=%u",
7638 mng_info->write_png_colortype-1,
7642 if (0) png_error(ping,
7643 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7646 if (image_matte && !image->matte)
7648 /* Add an opaque matte channel */
7649 image->matte = MagickTrue;
7650 (void) SetImageOpacity(image,0);
7651 if (logging != MagickFalse)
7652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7653 " Added an opaque matte channel");
7656 if (image->matte == MagickTrue && ping_color_type < 4)
7658 if (ping_color_type == 3 && ping_num_trans == 0)
7660 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7661 if (logging != MagickFalse)
7662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7663 " Ignoring request to write tRNS chunk with num_trans==0");
7666 (void) png_set_tRNS(ping, ping_info,
7672 if (logging != MagickFalse)
7673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7674 " Writing PNG header chunks");
7676 png_set_IHDR(ping,ping_info,ping_width,ping_height,
7677 ping_bit_depth,ping_color_type,
7678 ping_interlace_method,ping_compression_method,
7679 ping_filter_method);
7681 png_write_info_before_PLTE(ping, ping_info);
7682 /* write any png-chunk-b profiles */
7683 (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7684 png_write_info(ping,ping_info);
7685 /* write any PNG-chunk-m profiles */
7686 (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7688 if (image->page.width || image->page.height)
7693 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
7694 PNGType(chunk,mng_vpAg);
7695 LogPNGChunk((int) logging,mng_vpAg,9L);
7696 PNGLong(chunk+4,(png_uint_32) image->page.width);
7697 PNGLong(chunk+8,(png_uint_32) image->page.height);
7698 chunk[12]=0; /* unit = pixels */
7699 (void) WriteBlob(image,13,chunk);
7700 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
7703 #if (PNG_LIBPNG_VER == 10206)
7704 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
7705 #define PNG_HAVE_IDAT 0x04
7706 ping->mode |= PNG_HAVE_IDAT;
7707 #undef PNG_HAVE_IDAT
7710 png_set_packing(ping);
7714 rowbytes=image->columns;
7715 if (image_depth > 8)
7717 switch (ping_color_type)
7719 case PNG_COLOR_TYPE_RGB:
7722 case PNG_COLOR_TYPE_GRAY_ALPHA:
7725 case PNG_COLOR_TYPE_RGBA:
7733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7734 " Writing PNG image data");
7735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7736 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
7738 png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7739 sizeof(*png_pixels));
7740 if (png_pixels == (unsigned char *) NULL)
7741 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7743 Initialize image scanlines.
7745 if (setjmp(png_jmpbuf(ping)))
7751 if (image_info->verbose)
7752 (void) printf("PNG write has failed.\n");
7754 png_destroy_write_struct(&ping,&ping_info);
7755 if (quantum_info != (QuantumInfo *) NULL)
7756 quantum_info=DestroyQuantumInfo(quantum_info);
7757 if (png_pixels != (unsigned char *) NULL)
7758 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7759 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7760 UnlockSemaphoreInfo(png_semaphore);
7762 return(MagickFalse);
7764 quantum_info=AcquireQuantumInfo(image_info,image);
7765 if (quantum_info == (QuantumInfo *) NULL)
7766 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7767 quantum_info->format=UndefinedQuantumFormat;
7768 quantum_info->depth=image_depth;
7769 num_passes=png_set_interlace_handling(ping);
7770 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7771 !mng_info->write_png32) &&
7772 (mng_info->optimize || mng_info->IsPalette ||
7773 (image_info->type == BilevelType)) &&
7774 !image_matte && ImageIsMonochrome(image))
7776 register const PixelPacket
7779 quantum_info->depth=8;
7780 for (pass=0; pass < num_passes; pass++)
7783 Convert PseudoClass image to a PNG monochrome image.
7785 for (y=0; y < (ssize_t) image->rows; y++)
7787 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7788 if (p == (const PixelPacket *) NULL)
7790 if (mng_info->IsPalette)
7792 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7793 quantum_info,GrayQuantum,png_pixels,&image->exception);
7794 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7795 mng_info->write_png_depth &&
7796 mng_info->write_png_depth != old_bit_depth)
7798 /* Undo pixel scaling */
7799 for (i=0; i < (ssize_t) image->columns; i++)
7800 *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7801 >> (8-old_bit_depth));
7806 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7807 quantum_info,RedQuantum,png_pixels,&image->exception);
7809 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
7810 for (i=0; i < (ssize_t) image->columns; i++)
7811 *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7813 if (logging && y == 0)
7814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7815 " Writing row of pixels (1)");
7816 png_write_row(ping,png_pixels);
7818 if (image->previous == (Image *) NULL)
7820 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7821 if (status == MagickFalse)
7827 for (pass=0; pass < num_passes; pass++)
7829 register const PixelPacket
7832 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7833 !mng_info->write_png32) &&
7835 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
7836 (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7838 for (y=0; y < (ssize_t) image->rows; y++)
7840 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7841 if (p == (const PixelPacket *) NULL)
7843 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7845 if (mng_info->IsPalette)
7846 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7847 quantum_info,GrayQuantum,png_pixels,&image->exception);
7849 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7850 quantum_info,RedQuantum,png_pixels,&image->exception);
7851 if (logging && y == 0)
7852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7853 " Writing GRAY PNG pixels (2)");
7855 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7857 if (logging && y == 0)
7858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7859 " Writing GRAY_ALPHA PNG pixels (2)");
7860 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7861 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7863 if (logging && y == 0)
7864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7865 " Writing row of pixels (2)");
7866 png_write_row(ping,png_pixels);
7868 if (image->previous == (Image *) NULL)
7870 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7871 if (status == MagickFalse)
7876 for (pass=0; pass < num_passes; pass++)
7878 if ((image_depth > 8) || (mng_info->write_png24 ||
7879 mng_info->write_png32 ||
7880 (!mng_info->write_png8 && !mng_info->IsPalette)))
7881 for (y=0; y < (ssize_t) image->rows; y++)
7883 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7884 if (p == (const PixelPacket *) NULL)
7886 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7888 if (image->storage_class == DirectClass)
7889 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7890 quantum_info,RedQuantum,png_pixels,&image->exception);
7892 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7893 quantum_info,GrayQuantum,png_pixels,&image->exception);
7895 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7897 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7898 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7899 if (logging && y == 0)
7900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7901 " Writing GRAY_ALPHA PNG pixels (3)");
7903 else if (image_matte != MagickFalse)
7904 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7905 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7907 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7908 quantum_info,RGBQuantum,png_pixels,&image->exception);
7909 if (logging && y == 0)
7910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7911 " Writing row of pixels (3)");
7912 png_write_row(ping,png_pixels);
7915 /* not ((image_depth > 8) || (mng_info->write_png24 ||
7916 mng_info->write_png32 ||
7917 (!mng_info->write_png8 && !mng_info->IsPalette))) */
7919 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
7920 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
7923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7924 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7925 quantum_info->depth=8;
7928 for (y=0; y < (ssize_t) image->rows; y++)
7930 if (logging && y == 0)
7931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7932 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7933 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7934 if (p == (const PixelPacket *) NULL)
7936 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7937 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7938 quantum_info,GrayQuantum,png_pixels,&image->exception);
7939 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7941 if (logging && y == 0)
7942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7943 " Writing GRAY_ALPHA PNG pixels (4)");
7944 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7945 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7948 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7949 quantum_info,IndexQuantum,png_pixels,&image->exception);
7950 if (logging && y == 0)
7951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7952 " Writing row of pixels (4)");
7953 png_write_row(ping,png_pixels);
7956 if (image->previous == (Image *) NULL)
7958 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7959 if (status == MagickFalse)
7964 if (quantum_info != (QuantumInfo *) NULL)
7965 quantum_info=DestroyQuantumInfo(quantum_info);
7967 if (logging != MagickFalse)
7969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7970 " Wrote PNG image data");
7971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7972 " Width: %.20g",(double) ping_width);
7973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7974 " Height: %.20g",(double) ping_height);
7975 if (mng_info->write_png_depth)
7977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7978 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7981 " PNG bit-depth written: %d",ping_bit_depth);
7982 if (mng_info->write_png_colortype)
7984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7985 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7988 " PNG color-type written: %d",ping_color_type);
7989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7990 " PNG Interlace method: %d",ping_interlace_method);
7993 Generate text chunks.
7995 ResetImagePropertyIterator(image);
7996 property=GetNextImageProperty(image);
7997 while (property != (const char *) NULL)
8002 value=GetImageProperty(image,property);
8003 if (value != (const char *) NULL)
8005 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
8006 text[0].key=(char *) property;
8007 text[0].text=(char *) value;
8008 text[0].text_length=strlen(value);
8009 text[0].compression=image_info->compression == NoCompression ||
8010 (image_info->compression == UndefinedCompression &&
8011 text[0].text_length < 128) ? -1 : 0;
8012 if (logging != MagickFalse)
8014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8015 " Setting up text chunk");
8016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8017 " keyword: %s",text[0].key);
8019 png_set_text(ping,ping_info,text,1);
8020 png_free(ping,text);
8022 property=GetNextImageProperty(image);
8025 /* write any PNG-chunk-e profiles */
8026 (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
8028 if (logging != MagickFalse)
8029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8030 " Writing PNG end info");
8031 png_write_end(ping,ping_info);
8032 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
8034 if (mng_info->page.x || mng_info->page.y ||
8035 (ping_width != mng_info->page.width) ||
8036 (ping_height != mng_info->page.height))
8042 Write FRAM 4 with clipping boundaries followed by FRAM 1.
8044 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
8045 PNGType(chunk,mng_FRAM);
8046 LogPNGChunk((int) logging,mng_FRAM,27L);
8048 chunk[5]=0; /* frame name separator (no name) */
8049 chunk[6]=1; /* flag for changing delay, for next frame only */
8050 chunk[7]=0; /* flag for changing frame timeout */
8051 chunk[8]=1; /* flag for changing frame clipping for next frame */
8052 chunk[9]=0; /* flag for changing frame sync_id */
8053 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
8054 chunk[14]=0; /* clipping boundaries delta type */
8055 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
8057 (png_uint_32) (mng_info->page.x + ping_width));
8058 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
8060 (png_uint_32) (mng_info->page.y + ping_height));
8061 (void) WriteBlob(image,31,chunk);
8062 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
8063 mng_info->old_framing_mode=4;
8064 mng_info->framing_mode=1;
8067 mng_info->framing_mode=3;
8069 if (mng_info->write_mng && !mng_info->need_fram &&
8070 ((int) image->dispose == 3))
8071 (void) ThrowMagickException(&image->exception,GetMagickModule(),
8072 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
8073 "`%s'",image->filename);
8074 image_depth=save_image_depth;
8076 /* Save depth actually written */
8078 s[0]=(char) ping_bit_depth;
8081 (void) SetImageProperty(image,"png:bit-depth-written",s);
8087 png_destroy_write_struct(&ping,&ping_info);
8089 png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
8091 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8092 UnlockSemaphoreInfo(png_semaphore);
8095 if (logging != MagickFalse)
8096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8097 " exit WriteOnePNGImage()");
8099 /* End write one PNG image */
8103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8107 % W r i t e P N G I m a g e %
8111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8113 % WritePNGImage() writes a Portable Network Graphics (PNG) or
8114 % Multiple-image Network Graphics (MNG) image file.
8116 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
8118 % The format of the WritePNGImage method is:
8120 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
8122 % A description of each parameter follows:
8124 % o image_info: the image info.
8126 % o image: The image.
8128 % Returns MagickTrue on success, MagickFalse on failure.
8130 % Communicating with the PNG encoder:
8132 % While the datastream written is always in PNG format and normally would
8133 % be given the "png" file extension, this method also writes the following
8134 % pseudo-formats which are subsets of PNG:
8136 % o PNG8: An 8-bit indexed PNG datastream is written. If transparency
8137 % is present, the tRNS chunk must only have values 0 and 255
8138 % (i.e., transparency is binary: fully opaque or fully
8139 % transparent). The pixels contain 8-bit indices even if
8140 % they could be represented with 1, 2, or 4 bits. Note: grayscale
8141 % images will be written as indexed PNG files even though the
8142 % PNG grayscale type might be slightly more efficient.
8144 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
8145 % chunk can be present to convey binary transparency by naming
8146 % one of the colors as transparent.
8148 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
8149 % transparency is permitted, i.e., the alpha sample for
8150 % each pixel can have any value from 0 to 255. The alpha
8151 % channel is present even if the image is fully opaque.
8153 % o -define: For more precise control of the PNG output, you can use the
8154 % Image options "png:bit-depth" and "png:color-type". These
8155 % can be set from the commandline with "-define" and also
8156 % from the application programming interfaces.
8158 % png:color-type can be 0, 2, 3, 4, or 6.
8160 % When png:color-type is 0 (Grayscale), png:bit-depth can
8161 % be 1, 2, 4, 8, or 16.
8163 % When png:color-type is 2 (RGB), png:bit-depth can
8166 % When png:color-type is 3 (Indexed), png:bit-depth can
8167 % be 1, 2, 4, or 8. This refers to the number of bits
8168 % used to store the index. The color samples always have
8169 % bit-depth 8 in indexed PNG files.
8171 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
8172 % png:bit-depth can be 8 or 16.
8174 % If the image cannot be written without loss in the requested PNG8, PNG24,
8175 % or PNG32 format or with the requested bit-depth and color-type without loss,
8176 % a PNG file will not be written, and the encoder will return MagickFalse.
8177 % Since image encoders should not be responsible for the "heavy lifting",
8178 % the user should make sure that ImageMagick has already reduced the
8179 % image depth and number of colors and limit transparency to binary
8180 % transparency prior to attempting to write the image in a format that
8181 % is subject to depth, color, or transparency limitations.
8183 % TODO: Enforce the previous paragraph.
8185 % TODO: Allow all other PNG subformats to be requested via new
8186 % "-define png:bit-depth -define png:color-type" options.
8188 % Note that another definition, "png:bit-depth-written" exists, but it
8189 % is not intended for external use. It is only used internally by the
8190 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
8192 % It is possible to request that the PNG encoder write previously-formatted
8193 % ancillary chunks in the output PNG file, using the "-profile" commandline
8194 % option as shown below or by setting the profile via a programming
8197 % -profile PNG-chunk-x:<file>
8199 % where x is a location flag and <file> is a file containing the chunk
8200 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
8202 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
8203 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
8204 % of the same type, then add a short unique string after the "x" to prevent
8205 % subsequent profiles from overwriting the preceding ones:
8207 % -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
8209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8211 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
8232 assert(image_info != (const ImageInfo *) NULL);
8233 assert(image_info->signature == MagickSignature);
8234 assert(image != (Image *) NULL);
8235 assert(image->signature == MagickSignature);
8236 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8237 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
8238 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8239 if (status == MagickFalse)
8240 return(MagickFalse);
8242 Allocate a MngInfo structure.
8244 have_mng_structure=MagickFalse;
8245 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8246 if (mng_info == (MngInfo *) NULL)
8247 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8249 Initialize members of the MngInfo structure.
8251 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8252 mng_info->image=image;
8253 have_mng_structure=MagickTrue;
8255 /* See if user has requested a specific PNG subformat */
8257 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8258 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8259 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8261 if (mng_info->write_png8)
8263 mng_info->write_png_colortype = /* 3 */ 4;
8264 mng_info->write_png_depth = 8;
8266 #if 0 /* this does not work */
8267 if (image->matte == MagickTrue)
8268 (void) SetImageType(image,PaletteMatteType);
8270 (void) SetImageType(image,PaletteType);
8271 (void) SyncImage(image);
8275 if (mng_info->write_png24)
8277 mng_info->write_png_colortype = /* 2 */ 3;
8278 mng_info->write_png_depth = 8;
8280 if (image->matte == MagickTrue)
8281 (void) SetImageType(image,TrueColorMatteType);
8283 (void) SetImageType(image,TrueColorType);
8284 (void) SyncImage(image);
8287 if (mng_info->write_png32)
8289 mng_info->write_png_colortype = /* 6 */ 7;
8290 mng_info->write_png_depth = 8;
8292 if (image->matte == MagickTrue)
8293 (void) SetImageType(image,TrueColorMatteType);
8295 (void) SetImageType(image,TrueColorType);
8296 (void) SyncImage(image);
8299 value=GetImageOption(image_info,"png:bit-depth");
8300 if (value != (char *) NULL)
8302 if (LocaleCompare(value,"1") == 0)
8303 mng_info->write_png_depth = 1;
8304 else if (LocaleCompare(value,"2") == 0)
8305 mng_info->write_png_depth = 2;
8306 else if (LocaleCompare(value,"4") == 0)
8307 mng_info->write_png_depth = 4;
8308 else if (LocaleCompare(value,"8") == 0)
8309 mng_info->write_png_depth = 8;
8310 else if (LocaleCompare(value,"16") == 0)
8311 mng_info->write_png_depth = 16;
8312 if (logging != MagickFalse)
8313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8314 "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
8316 value=GetImageOption(image_info,"png:color-type");
8317 if (value != (char *) NULL)
8319 /* We must store colortype+1 because 0 is a valid colortype */
8320 if (LocaleCompare(value,"0") == 0)
8321 mng_info->write_png_colortype = 1;
8322 else if (LocaleCompare(value,"2") == 0)
8323 mng_info->write_png_colortype = 3;
8324 else if (LocaleCompare(value,"3") == 0)
8325 mng_info->write_png_colortype = 4;
8326 else if (LocaleCompare(value,"4") == 0)
8327 mng_info->write_png_colortype = 5;
8328 else if (LocaleCompare(value,"6") == 0)
8329 mng_info->write_png_colortype = 7;
8330 if (logging != MagickFalse)
8331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8332 "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
8335 status=WriteOnePNGImage(mng_info,image_info,image);
8337 (void) CloseBlob(image);
8339 MngInfoFreeStruct(mng_info,&have_mng_structure);
8340 if (logging != MagickFalse)
8341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8345 #if defined(JNG_SUPPORTED)
8347 /* Write one JNG image */
8348 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8349 const ImageInfo *image_info,Image *image)
8369 jng_alpha_compression_method,
8370 jng_alpha_sample_depth,
8378 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8379 " enter WriteOneJNGImage()");
8381 blob=(unsigned char *) NULL;
8382 jpeg_image=(Image *) NULL;
8383 jpeg_image_info=(ImageInfo *) NULL;
8386 transparent=image_info->type==GrayscaleMatteType ||
8387 image_info->type==TrueColorMatteType;
8389 jng_alpha_sample_depth=0;
8390 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8391 jng_alpha_compression_method=0;
8393 if (image->matte != MagickFalse)
8395 /* if any pixels are transparent */
8396 transparent=MagickTrue;
8397 if (image_info->compression==JPEGCompression)
8398 jng_alpha_compression_method=8;
8404 /* Create JPEG blob, image, and image_info */
8405 if (logging != MagickFalse)
8406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8407 " Creating jpeg_image_info for opacity.");
8408 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8409 if (jpeg_image_info == (ImageInfo *) NULL)
8410 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8411 if (logging != MagickFalse)
8412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8413 " Creating jpeg_image.");
8414 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8415 if (jpeg_image == (Image *) NULL)
8416 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8417 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8418 status=SeparateImageChannel(jpeg_image,OpacityChannel);
8419 status=NegateImage(jpeg_image,MagickFalse);
8420 jpeg_image->matte=MagickFalse;
8421 if (jng_quality >= 1000)
8422 jpeg_image_info->quality=jng_quality/1000;
8424 jpeg_image_info->quality=jng_quality;
8425 jpeg_image_info->type=GrayscaleType;
8426 (void) SetImageType(jpeg_image,GrayscaleType);
8427 (void) AcquireUniqueFilename(jpeg_image->filename);
8428 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8429 "%s",jpeg_image->filename);
8432 /* To do: check bit depth of PNG alpha channel */
8434 /* Check if image is grayscale. */
8435 if (image_info->type != TrueColorMatteType && image_info->type !=
8436 TrueColorType && ImageIsGray(image))
8441 if (jng_alpha_compression_method==0)
8446 /* Encode opacity as a grayscale PNG blob */
8447 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8449 if (logging != MagickFalse)
8450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451 " Creating PNG blob.");
8454 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8455 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8456 jpeg_image_info->interlace=NoInterlace;
8458 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8461 /* Retrieve sample depth used */
8462 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8463 if (value != (char *) NULL)
8464 jng_alpha_sample_depth= (unsigned int) value[0];
8468 /* Encode opacity as a grayscale JPEG blob */
8470 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8473 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8474 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8475 jpeg_image_info->interlace=NoInterlace;
8476 if (logging != MagickFalse)
8477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8479 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8481 jng_alpha_sample_depth=8;
8482 if (logging != MagickFalse)
8483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8484 " Successfully read jpeg_image into a blob, length=%.20g.",
8488 /* Destroy JPEG image and image_info */
8489 jpeg_image=DestroyImage(jpeg_image);
8490 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8491 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8494 /* Write JHDR chunk */
8495 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
8496 PNGType(chunk,mng_JHDR);
8497 LogPNGChunk((int) logging,mng_JHDR,16L);
8498 PNGLong(chunk+4,(png_uint_32) image->columns);
8499 PNGLong(chunk+8,(png_uint_32) image->rows);
8500 chunk[12]=jng_color_type;
8501 chunk[13]=8; /* sample depth */
8502 chunk[14]=8; /*jng_image_compression_method */
8503 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8504 chunk[16]=jng_alpha_sample_depth;
8505 chunk[17]=jng_alpha_compression_method;
8506 chunk[18]=0; /*jng_alpha_filter_method */
8507 chunk[19]=0; /*jng_alpha_interlace_method */
8508 (void) WriteBlob(image,20,chunk);
8509 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8510 if (logging != MagickFalse)
8512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8513 " JNG width:%15lu",(unsigned long) image->columns);
8514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8515 " JNG height:%14lu",(unsigned long) image->rows);
8516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8517 " JNG color type:%10d",jng_color_type);
8518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8519 " JNG sample depth:%8d",8);
8520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8521 " JNG compression:%9d",8);
8522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8523 " JNG interlace:%11d",0);
8524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8525 " JNG alpha depth:%9d",jng_alpha_sample_depth);
8526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8527 " JNG alpha compression:%3d",jng_alpha_compression_method);
8528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8529 " JNG alpha filter:%8d",0);
8530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8531 " JNG alpha interlace:%5d",0);
8534 /* Write any JNG-chunk-b profiles */
8535 (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8538 Write leading ancillary chunks
8544 Write JNG bKGD chunk
8555 if (jng_color_type == 8 || jng_color_type == 12)
8559 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
8560 PNGType(chunk,mng_bKGD);
8561 LogPNGChunk((int) logging,mng_bKGD,(size_t) (num_bytes-4L));
8562 red=ScaleQuantumToChar(image->background_color.red);
8563 green=ScaleQuantumToChar(image->background_color.green);
8564 blue=ScaleQuantumToChar(image->background_color.blue);
8571 (void) WriteBlob(image,(size_t) num_bytes,chunk);
8572 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8575 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8578 Write JNG sRGB chunk
8580 (void) WriteBlobMSBULong(image,1L);
8581 PNGType(chunk,mng_sRGB);
8582 LogPNGChunk((int) logging,mng_sRGB,1L);
8583 if (image->rendering_intent != UndefinedIntent)
8584 chunk[4]=(unsigned char)
8585 PNG_RenderingIntent_from_Magick_RenderingIntent(
8586 (image->rendering_intent));
8588 chunk[4]=(unsigned char)
8589 PNG_RenderingIntent_from_Magick_RenderingIntent(
8590 (PerceptualIntent));
8591 (void) WriteBlob(image,5,chunk);
8592 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8596 if (image->gamma != 0.0)
8599 Write JNG gAMA chunk
8601 (void) WriteBlobMSBULong(image,4L);
8602 PNGType(chunk,mng_gAMA);
8603 LogPNGChunk((int) logging,mng_gAMA,4L);
8604 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
8605 (void) WriteBlob(image,8,chunk);
8606 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8608 if ((mng_info->equal_chrms == MagickFalse) &&
8609 (image->chromaticity.red_primary.x != 0.0))
8615 Write JNG cHRM chunk
8617 (void) WriteBlobMSBULong(image,32L);
8618 PNGType(chunk,mng_cHRM);
8619 LogPNGChunk((int) logging,mng_cHRM,32L);
8620 primary=image->chromaticity.white_point;
8621 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
8622 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
8623 primary=image->chromaticity.red_primary;
8624 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
8625 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
8626 primary=image->chromaticity.green_primary;
8627 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
8628 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
8629 primary=image->chromaticity.blue_primary;
8630 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
8631 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
8632 (void) WriteBlob(image,36,chunk);
8633 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8636 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8639 Write JNG pHYs chunk
8641 (void) WriteBlobMSBULong(image,9L);
8642 PNGType(chunk,mng_pHYs);
8643 LogPNGChunk((int) logging,mng_pHYs,9L);
8644 if (image->units == PixelsPerInchResolution)
8646 PNGLong(chunk+4,(png_uint_32)
8647 (image->x_resolution*100.0/2.54+0.5));
8648 PNGLong(chunk+8,(png_uint_32)
8649 (image->y_resolution*100.0/2.54+0.5));
8654 if (image->units == PixelsPerCentimeterResolution)
8656 PNGLong(chunk+4,(png_uint_32)
8657 (image->x_resolution*100.0+0.5));
8658 PNGLong(chunk+8,(png_uint_32)
8659 (image->y_resolution*100.0+0.5));
8664 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
8665 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
8669 (void) WriteBlob(image,13,chunk);
8670 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8673 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8676 Write JNG oFFs chunk
8678 (void) WriteBlobMSBULong(image,9L);
8679 PNGType(chunk,mng_oFFs);
8680 LogPNGChunk((int) logging,mng_oFFs,9L);
8681 PNGsLong(chunk+4,(ssize_t) (image->page.x));
8682 PNGsLong(chunk+8,(ssize_t) (image->page.y));
8684 (void) WriteBlob(image,13,chunk);
8685 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8687 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8689 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8690 PNGType(chunk,mng_vpAg);
8691 LogPNGChunk((int) logging,mng_vpAg,9L);
8692 PNGLong(chunk+4,(png_uint_32) image->page.width);
8693 PNGLong(chunk+8,(png_uint_32) image->page.height);
8694 chunk[12]=0; /* unit = pixels */
8695 (void) WriteBlob(image,13,chunk);
8696 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8702 if (jng_alpha_compression_method==0)
8710 /* Write IDAT chunk header */
8711 if (logging != MagickFalse)
8712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8713 " Write IDAT chunks from blob, length=%.20g.",(double)
8716 /* Copy IDAT chunks */
8719 for (i=8; i<(ssize_t) length; i+=len+12)
8721 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8723 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8725 /* Found an IDAT chunk. */
8726 (void) WriteBlobMSBULong(image,(size_t) len);
8727 LogPNGChunk((int) logging,mng_IDAT,(size_t) len);
8728 (void) WriteBlob(image,(size_t) len+4,p);
8729 (void) WriteBlobMSBULong(image,
8730 crc32(0,p,(uInt) len+4));
8734 if (logging != MagickFalse)
8735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8736 " Skipping %c%c%c%c chunk, length=%.20g.",
8737 *(p),*(p+1),*(p+2),*(p+3),(double) len);
8744 /* Write JDAA chunk header */
8745 if (logging != MagickFalse)
8746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8747 " Write JDAA chunk, length=%.20g.",(double) length);
8748 (void) WriteBlobMSBULong(image,(size_t) length);
8749 PNGType(chunk,mng_JDAA);
8750 LogPNGChunk((int) logging,mng_JDAA,length);
8751 /* Write JDAT chunk(s) data */
8752 (void) WriteBlob(image,4,chunk);
8753 (void) WriteBlob(image,length,blob);
8754 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8757 blob=(unsigned char *) RelinquishMagickMemory(blob);
8760 /* Encode image as a JPEG blob */
8761 if (logging != MagickFalse)
8762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8763 " Creating jpeg_image_info.");
8764 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8765 if (jpeg_image_info == (ImageInfo *) NULL)
8766 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8768 if (logging != MagickFalse)
8769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8770 " Creating jpeg_image.");
8772 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8773 if (jpeg_image == (Image *) NULL)
8774 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8775 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8777 (void) AcquireUniqueFilename(jpeg_image->filename);
8778 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8779 jpeg_image->filename);
8781 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8784 if (logging != MagickFalse)
8785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8786 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
8787 (double) jpeg_image->rows);
8789 if (jng_color_type == 8 || jng_color_type == 12)
8790 jpeg_image_info->type=GrayscaleType;
8791 jpeg_image_info->quality=jng_quality % 1000;
8792 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8793 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8794 if (logging != MagickFalse)
8795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8797 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8798 if (logging != MagickFalse)
8800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8801 " Successfully read jpeg_image into a blob, length=%.20g.",
8804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8805 " Write JDAT chunk, length=%.20g.",(double) length);
8807 /* Write JDAT chunk(s) */
8808 (void) WriteBlobMSBULong(image,(size_t) length);
8809 PNGType(chunk,mng_JDAT);
8810 LogPNGChunk((int) logging,mng_JDAT,length);
8811 (void) WriteBlob(image,4,chunk);
8812 (void) WriteBlob(image,length,blob);
8813 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8815 jpeg_image=DestroyImage(jpeg_image);
8816 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8817 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8818 blob=(unsigned char *) RelinquishMagickMemory(blob);
8820 /* Write any JNG-chunk-e profiles */
8821 (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8823 /* Write IEND chunk */
8824 (void) WriteBlobMSBULong(image,0L);
8825 PNGType(chunk,mng_IEND);
8826 LogPNGChunk((int) logging,mng_IEND,0);
8827 (void) WriteBlob(image,4,chunk);
8828 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8830 if (logging != MagickFalse)
8831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8832 " exit WriteOneJNGImage()");
8838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8842 % W r i t e J N G I m a g e %
8846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8848 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8850 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
8852 % The format of the WriteJNGImage method is:
8854 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8856 % A description of each parameter follows:
8858 % o image_info: the image info.
8860 % o image: The image.
8862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8864 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8881 assert(image_info != (const ImageInfo *) NULL);
8882 assert(image_info->signature == MagickSignature);
8883 assert(image != (Image *) NULL);
8884 assert(image->signature == MagickSignature);
8885 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8886 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8887 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8888 if (status == MagickFalse)
8892 Allocate a MngInfo structure.
8894 have_mng_structure=MagickFalse;
8895 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8896 if (mng_info == (MngInfo *) NULL)
8897 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8899 Initialize members of the MngInfo structure.
8901 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8902 mng_info->image=image;
8903 have_mng_structure=MagickTrue;
8905 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8907 status=WriteOneJNGImage(mng_info,image_info,image);
8908 (void) CloseBlob(image);
8910 (void) CatchImageException(image);
8911 MngInfoFreeStruct(mng_info,&have_mng_structure);
8912 if (logging != MagickFalse)
8913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8920 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8941 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8942 defined(PNG_MNG_FEATURES_SUPPORTED)
8945 all_images_are_gray,
8957 volatile unsigned int
8968 #if (PNG_LIBPNG_VER < 10200)
8969 if (image_info->verbose)
8970 printf("Your PNG library (libpng-%s) is rather old.\n",
8971 PNG_LIBPNG_VER_STRING);
8977 assert(image_info != (const ImageInfo *) NULL);
8978 assert(image_info->signature == MagickSignature);
8979 assert(image != (Image *) NULL);
8980 assert(image->signature == MagickSignature);
8981 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8982 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8983 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8984 if (status == MagickFalse)
8988 Allocate a MngInfo structure.
8990 have_mng_structure=MagickFalse;
8991 mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8992 if (mng_info == (MngInfo *) NULL)
8993 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8995 Initialize members of the MngInfo structure.
8997 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8998 mng_info->image=image;
8999 have_mng_structure=MagickTrue;
9000 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
9003 * See if user has requested a specific PNG subformat to be used
9004 * for all of the PNGs in the MNG being written, e.g.,
9006 * convert *.png png8:animation.mng
9008 * To do: check -define png:bit_depth and png:color_type as well,
9009 * or perhaps use mng:bit_depth and mng:color_type instead for
9013 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9014 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9015 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9017 write_jng=MagickFalse;
9018 if (image_info->compression == JPEGCompression)
9019 write_jng=MagickTrue;
9021 mng_info->adjoin=image_info->adjoin &&
9022 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
9024 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9025 optimize=MagickFalse;
9027 optimize=(image_info->type == OptimizeType || image_info->type ==
9030 if (logging != MagickFalse)
9032 /* Log some info about the input */
9036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9037 " Checking input image(s)");
9039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 " Optimize: FALSE");
9044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9045 " Image_info depth: %.20g",(double) image_info->depth);
9046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9047 " Type: %d",image_info->type);
9050 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9053 " Scene: %.20g",(double) scene++);
9054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9055 " Image depth: %.20g",(double) p->depth);
9057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9062 if (p->storage_class == PseudoClass)
9063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064 " Storage class: PseudoClass");
9066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9067 " Storage class: DirectClass");
9069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9070 " Number of colors: %.20g",(double) p->colors);
9072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9073 " Number of colors: unspecified");
9074 if (mng_info->adjoin == MagickFalse)
9080 Sometimes we get PseudoClass images whose RGB values don't match
9081 the colors in the colormap. This code syncs the RGB values.
9087 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9089 if (p->taint && p->storage_class == PseudoClass)
9090 (void) SyncImage(p);
9091 if (mng_info->adjoin == MagickFalse)
9096 #ifdef PNG_BUILD_PALETTE
9100 Sometimes we get DirectClass images that have 256 colors or fewer.
9101 This code will convert them to PseudoClass and build a colormap.
9106 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9108 if (p->storage_class != PseudoClass)
9110 p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
9111 if (p->colors <= 256)
9114 if (p->matte != MagickFalse)
9115 (void) SetImageType(p,PaletteMatteType);
9117 (void) SetImageType(p,PaletteType);
9120 if (mng_info->adjoin == MagickFalse)
9126 use_global_plte=MagickFalse;
9127 all_images_are_gray=MagickFalse;
9128 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9129 need_local_plte=MagickTrue;
9131 need_defi=MagickFalse;
9132 need_matte=MagickFalse;
9133 mng_info->framing_mode=1;
9134 mng_info->old_framing_mode=1;
9137 if (image_info->page != (char *) NULL)
9140 Determine image bounding box.
9142 SetGeometry(image,&mng_info->page);
9143 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
9144 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
9156 mng_info->page=image->page;
9157 need_geom=MagickTrue;
9158 if (mng_info->page.width || mng_info->page.height)
9159 need_geom=MagickFalse;
9161 Check all the scenes.
9163 initial_delay=image->delay;
9164 need_iterations=MagickFalse;
9165 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
9166 mng_info->equal_physs=MagickTrue,
9167 mng_info->equal_gammas=MagickTrue;
9168 mng_info->equal_srgbs=MagickTrue;
9169 mng_info->equal_backgrounds=MagickTrue;
9171 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9172 defined(PNG_MNG_FEATURES_SUPPORTED)
9173 all_images_are_gray=MagickTrue;
9174 mng_info->equal_palettes=MagickFalse;
9175 need_local_plte=MagickFalse;
9177 for (next_image=image; next_image != (Image *) NULL; )
9181 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
9182 mng_info->page.width=next_image->columns+next_image->page.x;
9183 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
9184 mng_info->page.height=next_image->rows+next_image->page.y;
9186 if (next_image->page.x || next_image->page.y)
9187 need_defi=MagickTrue;
9188 if (next_image->matte)
9189 need_matte=MagickTrue;
9190 if ((int) next_image->dispose >= BackgroundDispose)
9191 if (next_image->matte || next_image->page.x || next_image->page.y ||
9192 ((next_image->columns < mng_info->page.width) &&
9193 (next_image->rows < mng_info->page.height)))
9194 mng_info->need_fram=MagickTrue;
9195 if (next_image->iterations)
9196 need_iterations=MagickTrue;
9197 final_delay=next_image->delay;
9198 if (final_delay != initial_delay || final_delay > 1UL*
9199 next_image->ticks_per_second)
9200 mng_info->need_fram=1;
9201 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9202 defined(PNG_MNG_FEATURES_SUPPORTED)
9204 check for global palette possibility.
9206 if (image->matte != MagickFalse)
9207 need_local_plte=MagickTrue;
9208 if (need_local_plte == 0)
9210 if (ImageIsGray(image) == MagickFalse)
9211 all_images_are_gray=MagickFalse;
9212 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
9213 if (use_global_plte == 0)
9214 use_global_plte=mng_info->equal_palettes;
9215 need_local_plte=!mng_info->equal_palettes;
9218 if (GetNextImageInList(next_image) != (Image *) NULL)
9220 if (next_image->background_color.red !=
9221 next_image->next->background_color.red ||
9222 next_image->background_color.green !=
9223 next_image->next->background_color.green ||
9224 next_image->background_color.blue !=
9225 next_image->next->background_color.blue)
9226 mng_info->equal_backgrounds=MagickFalse;
9227 if (next_image->gamma != next_image->next->gamma)
9228 mng_info->equal_gammas=MagickFalse;
9229 if (next_image->rendering_intent !=
9230 next_image->next->rendering_intent)
9231 mng_info->equal_srgbs=MagickFalse;
9232 if ((next_image->units != next_image->next->units) ||
9233 (next_image->x_resolution != next_image->next->x_resolution) ||
9234 (next_image->y_resolution != next_image->next->y_resolution))
9235 mng_info->equal_physs=MagickFalse;
9236 if (mng_info->equal_chrms)
9238 if (next_image->chromaticity.red_primary.x !=
9239 next_image->next->chromaticity.red_primary.x ||
9240 next_image->chromaticity.red_primary.y !=
9241 next_image->next->chromaticity.red_primary.y ||
9242 next_image->chromaticity.green_primary.x !=
9243 next_image->next->chromaticity.green_primary.x ||
9244 next_image->chromaticity.green_primary.y !=
9245 next_image->next->chromaticity.green_primary.y ||
9246 next_image->chromaticity.blue_primary.x !=
9247 next_image->next->chromaticity.blue_primary.x ||
9248 next_image->chromaticity.blue_primary.y !=
9249 next_image->next->chromaticity.blue_primary.y ||
9250 next_image->chromaticity.white_point.x !=
9251 next_image->next->chromaticity.white_point.x ||
9252 next_image->chromaticity.white_point.y !=
9253 next_image->next->chromaticity.white_point.y)
9254 mng_info->equal_chrms=MagickFalse;
9258 next_image=GetNextImageInList(next_image);
9260 if (image_count < 2)
9262 mng_info->equal_backgrounds=MagickFalse;
9263 mng_info->equal_chrms=MagickFalse;
9264 mng_info->equal_gammas=MagickFalse;
9265 mng_info->equal_srgbs=MagickFalse;
9266 mng_info->equal_physs=MagickFalse;
9267 use_global_plte=MagickFalse;
9268 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9269 need_local_plte=MagickTrue;
9271 need_iterations=MagickFalse;
9273 if (mng_info->need_fram == MagickFalse)
9276 Only certain framing rates 100/n are exactly representable without
9277 the FRAM chunk but we'll allow some slop in VLC files
9279 if (final_delay == 0)
9281 if (need_iterations != MagickFalse)
9284 It's probably a GIF with loop; don't run it *too* fast.
9286 if (mng_info->adjoin)
9289 (void) ThrowMagickException(&image->exception,
9290 GetMagickModule(),CoderWarning,
9291 "input has zero delay between all frames; assuming",
9296 mng_info->ticks_per_second=0;
9298 if (final_delay != 0)
9299 mng_info->ticks_per_second=(png_uint_32) (image->ticks_per_second/final_delay);
9300 if (final_delay > 50)
9301 mng_info->ticks_per_second=2;
9302 if (final_delay > 75)
9303 mng_info->ticks_per_second=1;
9304 if (final_delay > 125)
9305 mng_info->need_fram=MagickTrue;
9306 if (need_defi && final_delay > 2 && (final_delay != 4) &&
9307 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
9308 (final_delay != 25) && (final_delay != 50) && (final_delay !=
9309 1UL*image->ticks_per_second))
9310 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
9312 if (mng_info->need_fram != MagickFalse)
9313 mng_info->ticks_per_second=1UL*image->ticks_per_second;
9315 If pseudocolor, we should also check to see if all the
9316 palettes are identical and write a global PLTE if they are.
9320 Write the MNG version 1.0 signature and MHDR chunk.
9322 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9323 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
9324 PNGType(chunk,mng_MHDR);
9325 LogPNGChunk((int) logging,mng_MHDR,28L);
9326 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
9327 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
9328 PNGLong(chunk+12,mng_info->ticks_per_second);
9329 PNGLong(chunk+16,0L); /* layer count=unknown */
9330 PNGLong(chunk+20,0L); /* frame count=unknown */
9331 PNGLong(chunk+24,0L); /* play time=unknown */
9336 if (need_defi || mng_info->need_fram || use_global_plte)
9337 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
9339 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
9343 if (need_defi || mng_info->need_fram || use_global_plte)
9344 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
9346 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
9353 if (need_defi || mng_info->need_fram || use_global_plte)
9354 PNGLong(chunk+28,11L); /* simplicity=LC */
9356 PNGLong(chunk+28,9L); /* simplicity=VLC */
9360 if (need_defi || mng_info->need_fram || use_global_plte)
9361 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
9363 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
9366 (void) WriteBlob(image,32,chunk);
9367 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9368 option=GetImageOption(image_info,"mng:need-cacheoff");
9369 if (option != (const char *) NULL)
9375 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9377 PNGType(chunk,mng_nEED);
9378 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
9379 (void) WriteBlobMSBULong(image,(size_t) length);
9380 LogPNGChunk((int) logging,mng_nEED,(size_t) length);
9382 (void) WriteBlob(image,length,chunk);
9383 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9385 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9386 (GetNextImageInList(image) != (Image *) NULL) &&
9387 (image->iterations != 1))
9390 Write MNG TERM chunk
9392 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9393 PNGType(chunk,mng_TERM);
9394 LogPNGChunk((int) logging,mng_TERM,10L);
9395 chunk[4]=3; /* repeat animation */
9396 chunk[5]=0; /* show last frame when done */
9397 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9398 final_delay/MagickMax(image->ticks_per_second,1)));
9399 if (image->iterations == 0)
9400 PNGLong(chunk+10,PNG_UINT_31_MAX);
9402 PNGLong(chunk+10,(png_uint_32) image->iterations);
9403 if (logging != MagickFalse)
9405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9406 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
9407 final_delay/MagickMax(image->ticks_per_second,1)));
9408 if (image->iterations == 0)
9409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9410 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
9412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9413 " Image iterations: %.20g",(double) image->iterations);
9415 (void) WriteBlob(image,14,chunk);
9416 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9419 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9421 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9422 mng_info->equal_srgbs)
9425 Write MNG sRGB chunk
9427 (void) WriteBlobMSBULong(image,1L);
9428 PNGType(chunk,mng_sRGB);
9429 LogPNGChunk((int) logging,mng_sRGB,1L);
9430 if (image->rendering_intent != UndefinedIntent)
9431 chunk[4]=(unsigned char)
9432 PNG_RenderingIntent_from_Magick_RenderingIntent(
9433 (image->rendering_intent));
9435 chunk[4]=(unsigned char)
9436 PNG_RenderingIntent_from_Magick_RenderingIntent(
9437 (PerceptualIntent));
9438 (void) WriteBlob(image,5,chunk);
9439 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9440 mng_info->have_write_global_srgb=MagickTrue;
9444 if (image->gamma && mng_info->equal_gammas)
9447 Write MNG gAMA chunk
9449 (void) WriteBlobMSBULong(image,4L);
9450 PNGType(chunk,mng_gAMA);
9451 LogPNGChunk((int) logging,mng_gAMA,4L);
9452 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
9453 (void) WriteBlob(image,8,chunk);
9454 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9455 mng_info->have_write_global_gama=MagickTrue;
9457 if (mng_info->equal_chrms)
9463 Write MNG cHRM chunk
9465 (void) WriteBlobMSBULong(image,32L);
9466 PNGType(chunk,mng_cHRM);
9467 LogPNGChunk((int) logging,mng_cHRM,32L);
9468 primary=image->chromaticity.white_point;
9469 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
9470 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
9471 primary=image->chromaticity.red_primary;
9472 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
9473 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
9474 primary=image->chromaticity.green_primary;
9475 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
9476 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
9477 primary=image->chromaticity.blue_primary;
9478 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
9479 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
9480 (void) WriteBlob(image,36,chunk);
9481 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9482 mng_info->have_write_global_chrm=MagickTrue;
9485 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9488 Write MNG pHYs chunk
9490 (void) WriteBlobMSBULong(image,9L);
9491 PNGType(chunk,mng_pHYs);
9492 LogPNGChunk((int) logging,mng_pHYs,9L);
9493 if (image->units == PixelsPerInchResolution)
9495 PNGLong(chunk+4,(png_uint_32)
9496 (image->x_resolution*100.0/2.54+0.5));
9497 PNGLong(chunk+8,(png_uint_32)
9498 (image->y_resolution*100.0/2.54+0.5));
9503 if (image->units == PixelsPerCentimeterResolution)
9505 PNGLong(chunk+4,(png_uint_32)
9506 (image->x_resolution*100.0+0.5));
9507 PNGLong(chunk+8,(png_uint_32)
9508 (image->y_resolution*100.0+0.5));
9513 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
9514 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
9518 (void) WriteBlob(image,13,chunk);
9519 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9522 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9523 or does not cover the entire frame.
9525 if (write_mng && (image->matte || image->page.x > 0 ||
9526 image->page.y > 0 || (image->page.width &&
9527 (image->page.width+image->page.x < mng_info->page.width))
9528 || (image->page.height && (image->page.height+image->page.y
9529 < mng_info->page.height))))
9531 (void) WriteBlobMSBULong(image,6L);
9532 PNGType(chunk,mng_BACK);
9533 LogPNGChunk((int) logging,mng_BACK,6L);
9534 red=ScaleQuantumToShort(image->background_color.red);
9535 green=ScaleQuantumToShort(image->background_color.green);
9536 blue=ScaleQuantumToShort(image->background_color.blue);
9537 PNGShort(chunk+4,red);
9538 PNGShort(chunk+6,green);
9539 PNGShort(chunk+8,blue);
9540 (void) WriteBlob(image,10,chunk);
9541 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9542 if (mng_info->equal_backgrounds)
9544 (void) WriteBlobMSBULong(image,6L);
9545 PNGType(chunk,mng_bKGD);
9546 LogPNGChunk((int) logging,mng_bKGD,6L);
9547 (void) WriteBlob(image,10,chunk);
9548 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9552 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9553 if ((need_local_plte == MagickFalse) &&
9554 (image->storage_class == PseudoClass) &&
9555 (all_images_are_gray == MagickFalse))
9561 Write MNG PLTE chunk
9563 data_length=3*image->colors;
9564 (void) WriteBlobMSBULong(image,data_length);
9565 PNGType(chunk,mng_PLTE);
9566 LogPNGChunk((int) logging,mng_PLTE,data_length);
9567 for (i=0; i < (ssize_t) image->colors; i++)
9569 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9570 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9571 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9573 (void) WriteBlob(image,data_length+4,chunk);
9574 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9575 mng_info->have_write_global_plte=MagickTrue;
9581 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9582 defined(PNG_MNG_FEATURES_SUPPORTED)
9583 mng_info->equal_palettes=MagickFalse;
9587 if (mng_info->adjoin)
9589 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9590 defined(PNG_MNG_FEATURES_SUPPORTED)
9592 If we aren't using a global palette for the entire MNG, check to
9593 see if we can use one for two or more consecutive images.
9595 if (need_local_plte && use_global_plte && !all_images_are_gray)
9597 if (mng_info->IsPalette)
9600 When equal_palettes is true, this image has the same palette
9601 as the previous PseudoClass image
9603 mng_info->have_write_global_plte=mng_info->equal_palettes;
9604 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9605 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9608 Write MNG PLTE chunk
9613 data_length=3*image->colors;
9614 (void) WriteBlobMSBULong(image,data_length);
9615 PNGType(chunk,mng_PLTE);
9616 LogPNGChunk((int) logging,mng_PLTE,data_length);
9617 for (i=0; i < (ssize_t) image->colors; i++)
9619 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9620 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9621 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9623 (void) WriteBlob(image,data_length+4,chunk);
9624 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9625 (uInt) (data_length+4)));
9626 mng_info->have_write_global_plte=MagickTrue;
9630 mng_info->have_write_global_plte=MagickFalse;
9641 previous_x=mng_info->page.x;
9642 previous_y=mng_info->page.y;
9649 mng_info->page=image->page;
9650 if ((mng_info->page.x != previous_x) ||
9651 (mng_info->page.y != previous_y))
9653 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
9654 PNGType(chunk,mng_DEFI);
9655 LogPNGChunk((int) logging,mng_DEFI,12L);
9656 chunk[4]=0; /* object 0 MSB */
9657 chunk[5]=0; /* object 0 LSB */
9658 chunk[6]=0; /* visible */
9659 chunk[7]=0; /* abstract */
9660 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9661 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9662 (void) WriteBlob(image,16,chunk);
9663 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9668 mng_info->write_mng=write_mng;
9670 if ((int) image->dispose >= 3)
9671 mng_info->framing_mode=3;
9673 if (mng_info->need_fram && mng_info->adjoin &&
9674 ((image->delay != mng_info->delay) ||
9675 (mng_info->framing_mode != mng_info->old_framing_mode)))
9677 if (image->delay == mng_info->delay)
9680 Write a MNG FRAM chunk with the new framing mode.
9682 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
9683 PNGType(chunk,mng_FRAM);
9684 LogPNGChunk((int) logging,mng_FRAM,1L);
9685 chunk[4]=(unsigned char) mng_info->framing_mode;
9686 (void) WriteBlob(image,5,chunk);
9687 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9692 Write a MNG FRAM chunk with the delay.
9694 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
9695 PNGType(chunk,mng_FRAM);
9696 LogPNGChunk((int) logging,mng_FRAM,10L);
9697 chunk[4]=(unsigned char) mng_info->framing_mode;
9698 chunk[5]=0; /* frame name separator (no name) */
9699 chunk[6]=2; /* flag for changing default delay */
9700 chunk[7]=0; /* flag for changing frame timeout */
9701 chunk[8]=0; /* flag for changing frame clipping */
9702 chunk[9]=0; /* flag for changing frame sync_id */
9703 PNGLong(chunk+10,(png_uint_32)
9704 ((mng_info->ticks_per_second*
9705 image->delay)/MagickMax(image->ticks_per_second,1)));
9706 (void) WriteBlob(image,14,chunk);
9707 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9708 mng_info->delay=(png_uint_32) image->delay;
9710 mng_info->old_framing_mode=mng_info->framing_mode;
9713 #if defined(JNG_SUPPORTED)
9714 if (image_info->compression == JPEGCompression)
9719 if (logging != MagickFalse)
9720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9721 " Writing JNG object.");
9722 /* To do: specify the desired alpha compression method. */
9723 write_info=CloneImageInfo(image_info);
9724 write_info->compression=UndefinedCompression;
9725 status=WriteOneJNGImage(mng_info,write_info,image);
9726 write_info=DestroyImageInfo(write_info);
9731 if (logging != MagickFalse)
9732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9733 " Writing PNG object.");
9734 status=WriteOnePNGImage(mng_info,image_info,image);
9737 if (status == MagickFalse)
9739 MngInfoFreeStruct(mng_info,&have_mng_structure);
9740 (void) CloseBlob(image);
9741 return(MagickFalse);
9743 (void) CatchImageException(image);
9744 if (GetNextImageInList(image) == (Image *) NULL)
9746 image=SyncNextImageInList(image);
9747 status=SetImageProgress(image,SaveImagesTag,scene++,
9748 GetImageListLength(image));
9749 if (status == MagickFalse)
9751 } while (mng_info->adjoin);
9754 while (GetPreviousImageInList(image) != (Image *) NULL)
9755 image=GetPreviousImageInList(image);
9757 Write the MEND chunk.
9759 (void) WriteBlobMSBULong(image,0x00000000L);
9760 PNGType(chunk,mng_MEND);
9761 LogPNGChunk((int) logging,mng_MEND,0L);
9762 (void) WriteBlob(image,4,chunk);
9763 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9766 Relinquish resources.
9768 (void) CloseBlob(image);
9769 MngInfoFreeStruct(mng_info,&have_mng_structure);
9770 if (logging != MagickFalse)
9771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9774 #else /* PNG_LIBPNG_VER > 10011 */
9775 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9778 printf("Your PNG library is too old: You have libpng-%s\n",
9779 PNG_LIBPNG_VER_STRING);
9780 ThrowBinaryException(CoderError,"PNG library is too old",
9781 image_info->filename);
9783 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9785 return(WritePNGImage(image_info,image));
9787 #endif /* PNG_LIBPNG_VER > 10011 */