2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2011 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/artifact.h"
46 #include "magick/attribute.h"
47 #include "magick/blob.h"
48 #include "magick/blob-private.h"
49 #include "magick/cache.h"
50 #include "magick/color.h"
51 #include "magick/color-private.h"
52 #include "magick/colormap.h"
53 #include "magick/colorspace.h"
54 #include "magick/constitute.h"
55 #include "magick/enhance.h"
56 #include "magick/exception.h"
57 #include "magick/exception-private.h"
58 #include "magick/geometry.h"
59 #include "magick/histogram.h"
60 #include "magick/image.h"
61 #include "magick/image-private.h"
62 #include "magick/layer.h"
63 #include "magick/list.h"
64 #include "magick/log.h"
65 #include "magick/magick.h"
66 #include "magick/memory_.h"
67 #include "magick/module.h"
68 #include "magick/monitor.h"
69 #include "magick/monitor-private.h"
70 #include "magick/option.h"
71 #include "magick/quantum-private.h"
72 #include "magick/profile.h"
73 #include "magick/property.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 #if defined(MAGICKCORE_JPEG_DELEGATE)
116 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
118 #if !defined(RGBColorMatchExact)
119 #define IsPNGColorEqual(color,target) \
120 (((color).red == (target).red) && \
121 ((color).green == (target).green) && \
122 ((color).blue == (target).blue))
126 Establish thread safety.
127 setjmp/longjmp is claimed to be safe on these platforms:
128 setjmp/longjmp is alleged to be unsafe on these platforms:
130 #ifndef SETJMP_IS_THREAD_SAFE
131 #define PNG_SETJMP_NOT_THREAD_SAFE
134 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
136 *ping_semaphore = (SemaphoreInfo *) NULL;
140 This temporary until I set up malloc'ed object attributes array.
141 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
144 #define MNG_MAX_OBJECTS 256
147 If this not defined, spec is interpreted strictly. If it is
148 defined, an attempt will be made to recover from some errors,
150 o global PLTE too short
155 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
156 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
157 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
158 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
159 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
160 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
161 will be enabled by default in libpng-1.2.0.
163 #ifdef PNG_MNG_FEATURES_SUPPORTED
164 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
165 # define PNG_READ_EMPTY_PLTE_SUPPORTED
167 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
168 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
173 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
174 This macro is only defined in libpng-1.0.3 and later.
175 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
177 #ifndef PNG_UINT_31_MAX
178 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
182 Constant strings for known chunk types. If you need to add a chunk,
183 add a string holding the name here. To make the code more
184 portable, we use ASCII numbers like this, not characters.
187 static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
188 static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
189 static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
190 static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
191 static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
192 static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
193 static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
194 static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
195 static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
196 static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
197 static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
198 static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
199 static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
200 static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
201 static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
202 static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
203 static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
204 static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
205 static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
206 static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
207 static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
208 static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
209 static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
210 static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
211 static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
212 static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
213 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
214 static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
215 static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
216 static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
217 static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
218 static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
219 static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
220 static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
222 #if defined(JNG_SUPPORTED)
223 static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
224 static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
225 static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
226 static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
227 static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
228 static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
232 Other known chunks that are not yet supported by ImageMagick:
233 static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
234 static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
235 static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
236 static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
237 static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
238 static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
239 static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
240 static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
243 typedef struct _MngBox
252 typedef struct _MngPair
259 #ifdef MNG_OBJECT_BUFFERS
260 typedef struct _MngBuffer
292 typedef struct _MngInfo
295 #ifdef MNG_OBJECT_BUFFERS
297 *ob[MNG_MAX_OBJECTS];
308 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
309 bytes_in_read_buffer,
315 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
316 defined(PNG_MNG_FEATURES_SUPPORTED)
328 have_saved_bkgd_index,
329 have_write_global_chrm,
330 have_write_global_gama,
331 have_write_global_plte,
332 have_write_global_srgb,
346 x_off[MNG_MAX_OBJECTS],
347 y_off[MNG_MAX_OBJECTS];
353 object_clip[MNG_MAX_OBJECTS];
356 /* These flags could be combined into one byte */
357 exists[MNG_MAX_OBJECTS],
358 frozen[MNG_MAX_OBJECTS],
360 invisible[MNG_MAX_OBJECTS],
361 viewable[MNG_MAX_OBJECTS];
373 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
391 global_x_pixels_per_unit,
392 global_y_pixels_per_unit,
402 global_phys_unit_type,
421 #ifdef MNG_BASI_SUPPORTED
429 basi_compression_method,
431 basi_interlace_method,
454 /* Added at version 6.6.6-7 */
461 /* ping_exclude_iTXt, */
468 ping_exclude_zCCP, /* hex-encoded iCCP */
475 Forward declarations.
477 static MagickBooleanType
478 WritePNGImage(const ImageInfo *,Image *);
480 static MagickBooleanType
481 WriteMNGImage(const ImageInfo *,Image *);
483 #if defined(JNG_SUPPORTED)
484 static MagickBooleanType
485 WriteJNGImage(const ImageInfo *,Image *);
488 #if PNG_LIBPNG_VER > 10011
491 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
492 static MagickBooleanType
493 LosslessReduceDepthOK(Image *image)
496 ok_to_reduce=MagickFalse;
498 /* Reduce bit depth if it can be reduced losslessly from 16 to 8.
499 * Note that the method GetImageDepth doesn't check background
500 * and doesn't handle PseudoClass specially. Also it uses
501 * multiplication and division by 257 instead of shifting, so
505 if (image->depth == 16)
512 (((((size_t) image->background_color.red >> 8) & 0xff)
513 == ((size_t) image->background_color.red & 0xff)) &&
514 ((((size_t) image->background_color.green >> 8) & 0xff)
515 == ((size_t) image->background_color.green & 0xff)) &&
516 ((((size_t) image->background_color.blue >> 8) & 0xff)
517 == ((size_t) image->background_color.blue & 0xff))) ? MagickTrue :
520 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
524 for (indx=0; indx < (ssize_t) image->colors; indx++)
526 ok_to_reduce=(((((size_t) image->colormap[indx].red >>
528 == ((size_t) image->colormap[indx].red & 0xff)) &&
529 ((((size_t) image->colormap[indx].green >> 8) & 0xff)
530 == ((size_t) image->colormap[indx].green & 0xff)) &&
531 ((((size_t) image->colormap[indx].blue >> 8) & 0xff)
532 == ((size_t) image->colormap[indx].blue & 0xff)) &&
533 (image->matte == MagickFalse ||
534 (((size_t) image->colormap[indx].opacity >> 8) & 0xff)
535 == ((size_t) image->colormap[indx].opacity & 0xff))) ?
536 MagickTrue : MagickFalse;
537 if (ok_to_reduce == MagickFalse)
542 if ((ok_to_reduce != MagickFalse) &&
543 (image->storage_class != PseudoClass))
551 for (y=0; y < (ssize_t) image->rows; y++)
553 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
555 if (p == (const PixelPacket *) NULL)
557 ok_to_reduce = MagickFalse;
561 for (x=(ssize_t) image->columns-1; x >= 0; x--)
564 (((size_t) p->red >> 8) & 0xff) ==
565 ((size_t) p->red & 0xff)) &&
566 ((((size_t) p->green >> 8) & 0xff) ==
567 ((size_t) p->green & 0xff)) &&
568 ((((size_t) p->blue >> 8) & 0xff) ==
569 ((size_t) p->blue & 0xff)) &&
570 (((image->matte == MagickFalse ||
571 (((size_t) p->opacity >> 8) & 0xff) ==
572 ((size_t) p->opacity & 0xff))))) ? MagickTrue : MagickFalse;
574 if (ok_to_reduce == MagickFalse)
584 if (ok_to_reduce != MagickFalse)
586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
587 " OK to reduce PNG bit depth to 8 without loss of info");
591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
592 " Not OK to reduce PNG bit depth to 8 without loss of info");
598 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
601 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
605 case PerceptualIntent:
611 case SaturationIntent:
622 static RenderingIntent
623 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
628 return PerceptualIntent;
631 return RelativeIntent;
634 return SaturationIntent;
637 return AbsoluteIntent;
640 return UndefinedIntent;
644 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
652 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
666 % I m a g e I s G r a y %
670 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672 % Like IsGrayImage except does not change DirectClass to PseudoClass %
674 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
676 static MagickBooleanType ImageIsGray(Image *image)
678 register const PixelPacket
686 assert(image != (Image *) NULL);
687 assert(image->signature == MagickSignature);
688 if (image->debug != MagickFalse)
689 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
691 if (image->storage_class == PseudoClass)
693 for (i=0; i < (ssize_t) image->colors; i++)
694 if (IsGray(image->colormap+i) == MagickFalse)
698 for (y=0; y < (ssize_t) image->rows; y++)
700 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
701 if (p == (const PixelPacket *) NULL)
703 for (x=(ssize_t) image->columns-1; x >= 0; x--)
705 if (IsGray(p) == MagickFalse)
712 #endif /* PNG_LIBPNG_VER > 10011 */
713 #endif /* MAGICKCORE_PNG_DELEGATE */
716 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
724 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
726 % IsMNG() returns MagickTrue if the image format type, identified by the
727 % magick string, is MNG.
729 % The format of the IsMNG method is:
731 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
733 % A description of each parameter follows:
735 % o magick: compare image format pattern against these bytes.
737 % o length: Specifies the length of the magick string.
741 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
746 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
753 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
763 % IsJNG() returns MagickTrue if the image format type, identified by the
764 % magick string, is JNG.
766 % The format of the IsJNG method is:
768 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
770 % A description of each parameter follows:
772 % o magick: compare image format pattern against these bytes.
774 % o length: Specifies the length of the magick string.
778 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
783 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
798 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
800 % IsPNG() returns MagickTrue if the image format type, identified by the
801 % magick string, is PNG.
803 % The format of the IsPNG method is:
805 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
807 % A description of each parameter follows:
809 % o magick: compare image format pattern against these bytes.
811 % o length: Specifies the length of the magick string.
814 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
819 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
825 #if defined(MAGICKCORE_PNG_DELEGATE)
826 #if defined(__cplusplus) || defined(c_plusplus)
830 #if (PNG_LIBPNG_VER > 10011)
831 static size_t WriteBlobMSBULong(Image *image,const size_t value)
836 assert(image != (Image *) NULL);
837 assert(image->signature == MagickSignature);
838 buffer[0]=(unsigned char) (value >> 24);
839 buffer[1]=(unsigned char) (value >> 16);
840 buffer[2]=(unsigned char) (value >> 8);
841 buffer[3]=(unsigned char) value;
842 return((size_t) WriteBlob(image,4,buffer));
845 static void PNGLong(png_bytep p,png_uint_32 value)
847 *p++=(png_byte) ((value >> 24) & 0xff);
848 *p++=(png_byte) ((value >> 16) & 0xff);
849 *p++=(png_byte) ((value >> 8) & 0xff);
850 *p++=(png_byte) (value & 0xff);
853 #if defined(JNG_SUPPORTED)
854 static void PNGsLong(png_bytep p,png_int_32 value)
856 *p++=(png_byte) ((value >> 24) & 0xff);
857 *p++=(png_byte) ((value >> 16) & 0xff);
858 *p++=(png_byte) ((value >> 8) & 0xff);
859 *p++=(png_byte) (value & 0xff);
863 static void PNGShort(png_bytep p,png_uint_16 value)
865 *p++=(png_byte) ((value >> 8) & 0xff);
866 *p++=(png_byte) (value & 0xff);
869 static void PNGType(png_bytep p,png_bytep type)
871 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
874 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
877 if (logging != MagickFalse)
878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
879 " Writing %c%c%c%c chunk, length: %.20g",
880 type[0],type[1],type[2],type[3],(double) length);
882 #endif /* PNG_LIBPNG_VER > 10011 */
884 #if defined(__cplusplus) || defined(c_plusplus)
888 #if PNG_LIBPNG_VER > 10011
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
894 % R e a d P N G I m a g e %
898 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
901 % Multiple-image Network Graphics (MNG) image file and returns it. It
902 % allocates the memory necessary for the new Image structure and returns a
903 % pointer to the new image or set of images.
905 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
907 % The format of the ReadPNGImage method is:
909 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
911 % A description of each parameter follows:
913 % o image_info: the image info.
915 % o exception: return any errors or warnings in this structure.
917 % To do, more or less in chronological order (as of version 5.5.2,
918 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
920 % Get 16-bit cheap transparency working.
922 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
924 % Preserve all unknown and not-yet-handled known chunks found in input
925 % PNG file and copy them into output PNG files according to the PNG
928 % (At this point, PNG encoding should be in full MNG compliance)
930 % Provide options for choice of background to use when the MNG BACK
931 % chunk is not present or is not mandatory (i.e., leave transparent,
932 % user specified, MNG BACK, PNG bKGD)
934 % Implement LOOP/ENDL [done, but could do discretionary loops more
935 % efficiently by linking in the duplicate frames.].
937 % Decode and act on the MHDR simplicity profile (offer option to reject
938 % files or attempt to process them anyway when the profile isn't LC or VLC).
940 % Upgrade to full MNG without Delta-PNG.
942 % o BACK [done a while ago except for background image ID]
943 % o MOVE [done 15 May 1999]
944 % o CLIP [done 15 May 1999]
945 % o DISC [done 19 May 1999]
946 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
947 % o SEEK [partially done 19 May 1999 (discard function only)]
951 % o MNG-level tEXt/iTXt/zTXt
956 % o iTXt (wait for libpng implementation).
958 % Use the scene signature to discover when an identical scene is
959 % being reused, and just point to the original image->exception instead
960 % of storing another set of pixels. This not specific to MNG
961 % but could be applied generally.
963 % Upgrade to full MNG with Delta-PNG.
967 % We will not attempt to read files containing the CgBI chunk.
968 % They are really Xcode files meant for display on the iPhone.
969 % These are not valid PNG files and it is impossible to recover
970 % the orginal PNG from files that have been converted to Xcode-PNG,
971 % since irretrievable loss of color data has occurred due to the
972 % use of premultiplied alpha.
975 #if defined(__cplusplus) || defined(c_plusplus)
980 This the function that does the actual reading of data. It is
981 the same as the one supplied in libpng, except that it receives the
982 datastream from the ReadBlob() function instead of standard input.
984 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
989 image=(Image *) png_get_io_ptr(png_ptr);
995 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1001 (void) FormatMagickString(msg,MaxTextExtent,
1002 "Expected %.20g bytes; found %.20g bytes",(double) length,
1004 png_warning(png_ptr,msg);
1005 png_error(png_ptr,"Read Exception");
1010 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1011 !defined(PNG_MNG_FEATURES_SUPPORTED)
1012 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1013 * older than libpng-1.0.3a, which was the first to allow the empty
1014 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1015 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1016 * encountered after an empty PLTE, so we have to look ahead for bKGD
1017 * chunks and remove them from the datastream that is passed to libpng,
1018 * and store their contents for later use.
1020 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1035 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1036 image=(Image *) mng_info->image;
1037 while (mng_info->bytes_in_read_buffer && length)
1039 data[i]=mng_info->read_buffer[i];
1040 mng_info->bytes_in_read_buffer--;
1046 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1048 if (check != length)
1049 png_error(png_ptr,"Read Exception");
1053 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1056 check=(png_size_t) ReadBlob(image,(size_t) length,
1057 (char *) mng_info->read_buffer);
1058 mng_info->read_buffer[4]=0;
1059 mng_info->bytes_in_read_buffer=4;
1060 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1061 mng_info->found_empty_plte=MagickTrue;
1062 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1064 mng_info->found_empty_plte=MagickFalse;
1065 mng_info->have_saved_bkgd_index=MagickFalse;
1069 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1072 check=(png_size_t) ReadBlob(image,(size_t) length,
1073 (char *) mng_info->read_buffer);
1074 mng_info->read_buffer[4]=0;
1075 mng_info->bytes_in_read_buffer=4;
1076 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1077 if (mng_info->found_empty_plte)
1080 Skip the bKGD data byte and CRC.
1083 ReadBlob(image,5,(char *) mng_info->read_buffer);
1084 check=(png_size_t) ReadBlob(image,(size_t) length,
1085 (char *) mng_info->read_buffer);
1086 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1087 mng_info->have_saved_bkgd_index=MagickTrue;
1088 mng_info->bytes_in_read_buffer=0;
1096 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1101 image=(Image *) png_get_io_ptr(png_ptr);
1107 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1109 if (check != length)
1110 png_error(png_ptr,"WriteBlob Failed");
1114 static void png_flush_data(png_structp png_ptr)
1119 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1120 static int PalettesAreEqual(Image *a,Image *b)
1125 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1126 return((int) MagickFalse);
1128 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1129 return((int) MagickFalse);
1131 if (a->colors != b->colors)
1132 return((int) MagickFalse);
1134 for (i=0; i < (ssize_t) a->colors; i++)
1136 if ((a->colormap[i].red != b->colormap[i].red) ||
1137 (a->colormap[i].green != b->colormap[i].green) ||
1138 (a->colormap[i].blue != b->colormap[i].blue))
1139 return((int) MagickFalse);
1142 return((int) MagickTrue);
1146 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1148 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1149 mng_info->exists[i] && !mng_info->frozen[i])
1151 #ifdef MNG_OBJECT_BUFFERS
1152 if (mng_info->ob[i] != (MngBuffer *) NULL)
1154 if (mng_info->ob[i]->reference_count > 0)
1155 mng_info->ob[i]->reference_count--;
1157 if (mng_info->ob[i]->reference_count == 0)
1159 if (mng_info->ob[i]->image != (Image *) NULL)
1160 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1162 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1165 mng_info->ob[i]=(MngBuffer *) NULL;
1167 mng_info->exists[i]=MagickFalse;
1168 mng_info->invisible[i]=MagickFalse;
1169 mng_info->viewable[i]=MagickFalse;
1170 mng_info->frozen[i]=MagickFalse;
1171 mng_info->x_off[i]=0;
1172 mng_info->y_off[i]=0;
1173 mng_info->object_clip[i].left=0;
1174 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1175 mng_info->object_clip[i].top=0;
1176 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1180 static void MngInfoFreeStruct(MngInfo *mng_info,
1181 MagickBooleanType *have_mng_structure)
1183 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1188 for (i=1; i < MNG_MAX_OBJECTS; i++)
1189 MngInfoDiscardObject(mng_info,i);
1191 if (mng_info->global_plte != (png_colorp) NULL)
1192 mng_info->global_plte=(png_colorp)
1193 RelinquishMagickMemory(mng_info->global_plte);
1195 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1196 *have_mng_structure=MagickFalse;
1200 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1206 if (box.left < box2.left)
1209 if (box.top < box2.top)
1212 if (box.right > box2.right)
1213 box.right=box2.right;
1215 if (box.bottom > box2.bottom)
1216 box.bottom=box2.bottom;
1221 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1227 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1229 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1230 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1231 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1232 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1233 if (delta_type != 0)
1235 box.left+=previous_box.left;
1236 box.right+=previous_box.right;
1237 box.top+=previous_box.top;
1238 box.bottom+=previous_box.bottom;
1244 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1250 Read two ssize_ts from CLON, MOVE or PAST chunk
1252 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1253 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1255 if (delta_type != 0)
1257 pair.a+=previous_pair.a;
1258 pair.b+=previous_pair.b;
1264 static long mng_get_long(unsigned char *p)
1266 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1269 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1274 image=(Image *) png_get_error_ptr(ping);
1276 if (image->debug != MagickFalse)
1277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1278 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1280 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1281 message,"`%s'",image->filename);
1283 #if (PNG_LIBPNG_VER < 10500)
1284 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1285 * are building with libpng-1.4.x and can be ignored.
1287 longjmp(ping->jmpbuf,1);
1289 png_longjmp(ping,1);
1293 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1298 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1299 png_error(ping, message);
1301 image=(Image *) png_get_error_ptr(ping);
1302 if (image->debug != MagickFalse)
1303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1304 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1306 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1307 message,"`%s'",image->filename);
1310 #ifdef PNG_USER_MEM_SUPPORTED
1311 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1313 #if (PNG_LIBPNG_VER < 10011)
1318 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1321 png_error("Insufficient memory.");
1326 return((png_voidp) AcquireMagickMemory((size_t) size));
1331 Free a pointer. It is removed from the list at the same time.
1333 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1336 ptr=RelinquishMagickMemory(ptr);
1337 return((png_free_ptr) NULL);
1341 #if defined(__cplusplus) || defined(c_plusplus)
1346 Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
1347 png_textp text,int ii)
1352 register unsigned char
1366 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1367 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1368 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1369 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1370 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1374 /* look for newline */
1378 /* look for length */
1379 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1382 length=(png_uint_32) StringToLong(sp);
1384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1385 " length: %lu",(unsigned long) length);
1387 while (*sp != ' ' && *sp != '\n')
1390 /* allocate space */
1393 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1394 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1395 return(MagickFalse);
1398 profile=AcquireStringInfo(length);
1400 if (profile == (StringInfo *) NULL)
1402 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1403 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1404 "unable to copy profile");
1405 return(MagickFalse);
1408 /* copy profile, skipping white space and column 1 "=" signs */
1409 dp=GetStringInfoDatum(profile);
1412 for (i=0; i < (ssize_t) nibbles; i++)
1414 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1418 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1419 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1420 profile=DestroyStringInfo(profile);
1421 return(MagickFalse);
1427 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1430 (*dp++)+=unhex[(int) *sp++];
1433 We have already read "Raw profile type.
1435 (void) SetImageProfile(image,&text[ii].key[17],profile);
1436 profile=DestroyStringInfo(profile);
1438 if (image_info->verbose)
1439 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1444 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1445 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1451 /* The unknown chunk structure contains the chunk data:
1456 Note that libpng has already taken care of the CRC handling.
1460 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1461 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1462 return(0); /* Did not recognize */
1464 /* recognized vpAg */
1466 if (chunk->size != 9)
1467 return(-1); /* Error return */
1469 if (chunk->data[8] != 0)
1470 return(0); /* ImageMagick requires pixel units */
1472 image=(Image *) png_get_user_chunk_ptr(ping);
1474 image->page.width=(size_t) ((chunk->data[0] << 24) |
1475 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1477 image->page.height=(size_t) ((chunk->data[4] << 24) |
1478 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1480 /* Return one of the following: */
1481 /* return(-n); chunk had an error */
1482 /* return(0); did not recognize */
1483 /* return(n); success */
1491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1495 % R e a d O n e P N G I m a g e %
1499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1501 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1502 % (minus the 8-byte signature) and returns it. It allocates the memory
1503 % necessary for the new Image structure and returns a pointer to the new
1506 % The format of the ReadOnePNGImage method is:
1508 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1509 % ExceptionInfo *exception)
1511 % A description of each parameter follows:
1513 % o mng_info: Specifies a pointer to a MngInfo structure.
1515 % o image_info: the image info.
1517 % o exception: return any errors or warnings in this structure.
1520 static Image *ReadOnePNGImage(MngInfo *mng_info,
1521 const ImageInfo *image_info, ExceptionInfo *exception)
1523 /* Read one PNG image */
1534 ping_interlace_method,
1535 ping_compression_method,
1577 register unsigned char
1580 register IndexPacket
1587 register PixelPacket
1594 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1595 png_byte unused_chunks[]=
1597 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1598 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1599 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1600 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1601 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1602 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1606 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1607 " Enter ReadOnePNGImage()");
1609 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1610 LockSemaphoreInfo(ping_semaphore);
1613 #if (PNG_LIBPNG_VER < 10200)
1614 if (image_info->verbose)
1615 printf("Your PNG library (libpng-%s) is rather old.\n",
1616 PNG_LIBPNG_VER_STRING);
1619 #if (PNG_LIBPNG_VER >= 10400)
1620 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1621 if (image_info->verbose)
1623 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1624 PNG_LIBPNG_VER_STRING);
1625 printf("Please update it.\n");
1631 quantum_info = (QuantumInfo *) NULL;
1632 image=mng_info->image;
1634 if (logging != MagickFalse)
1635 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1636 " image->matte=%d",(int) image->matte);
1638 /* Set to an out-of-range color unless tRNS chunk is present */
1639 transparent_color.red=65537;
1640 transparent_color.green=65537;
1641 transparent_color.blue=65537;
1642 transparent_color.opacity=65537;
1645 Allocate the PNG structures
1647 #ifdef PNG_USER_MEM_SUPPORTED
1648 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1649 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1650 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
1652 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1653 MagickPNGErrorHandler,MagickPNGWarningHandler);
1655 if (ping == (png_struct *) NULL)
1656 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1658 ping_info=png_create_info_struct(ping);
1660 if (ping_info == (png_info *) NULL)
1662 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1663 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1666 end_info=png_create_info_struct(ping);
1668 if (end_info == (png_info *) NULL)
1670 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1671 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1674 ping_pixels=(unsigned char *) NULL;
1676 if (setjmp(png_jmpbuf(ping)))
1679 PNG image is corrupt.
1681 png_destroy_read_struct(&ping,&ping_info,&end_info);
1682 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1683 UnlockSemaphoreInfo(ping_semaphore);
1685 if (logging != MagickFalse)
1686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1687 " exit ReadOnePNGImage() with error.");
1689 if (image != (Image *) NULL)
1691 InheritException(exception,&image->exception);
1695 return(GetFirstImageInList(image));
1698 Prepare PNG for reading.
1701 mng_info->image_found++;
1702 png_set_sig_bytes(ping,8);
1704 if (LocaleCompare(image_info->magick,"MNG") == 0)
1706 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1707 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1708 png_set_read_fn(ping,image,png_get_data);
1710 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1711 png_permit_empty_plte(ping,MagickTrue);
1712 png_set_read_fn(ping,image,png_get_data);
1714 mng_info->image=image;
1715 mng_info->bytes_in_read_buffer=0;
1716 mng_info->found_empty_plte=MagickFalse;
1717 mng_info->have_saved_bkgd_index=MagickFalse;
1718 png_set_read_fn(ping,mng_info,mng_get_data);
1724 png_set_read_fn(ping,image,png_get_data);
1726 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1727 /* Ignore unused chunks and all unknown chunks except for vpAg */
1728 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1729 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1730 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1731 (int)sizeof(unused_chunks)/5);
1732 /* Callback for other unknown chunks */
1733 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1736 #if (PNG_LIBPNG_VER < 10400)
1737 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1738 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1739 /* Disable thread-unsafe features of pnggccrd */
1740 if (png_access_version_number() >= 10200)
1742 png_uint_32 mmx_disable_mask=0;
1743 png_uint_32 asm_flags;
1745 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1746 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1747 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1748 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1749 asm_flags=png_get_asm_flags(ping);
1750 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1755 png_read_info(ping,ping_info);
1757 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1758 &ping_bit_depth,&ping_color_type,
1759 &ping_interlace_method,&ping_compression_method,
1760 &ping_filter_method);
1762 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1765 (void) png_get_bKGD(ping, ping_info, &ping_background);
1767 if (ping_bit_depth < 8)
1769 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1771 png_set_packing(ping);
1776 image->depth=ping_bit_depth;
1777 image->depth=GetImageQuantumDepth(image,MagickFalse);
1778 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1779 if (logging != MagickFalse)
1781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1782 " PNG width: %.20g, height: %.20g",
1783 (double) ping_width, (double) ping_height);
1785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1786 " PNG color_type: %d, bit_depth: %d",
1787 ping_color_type, ping_bit_depth);
1789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1790 " PNG compression_method: %d",
1791 ping_compression_method);
1793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1794 " PNG interlace_method: %d, filter_method: %d",
1795 ping_interlace_method,ping_filter_method);
1798 #ifdef PNG_READ_iCCP_SUPPORTED
1799 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1804 #if (PNG_LIBPNG_VER < 10500)
1818 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1821 if (profile_length != 0)
1826 if (logging != MagickFalse)
1827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1828 " Reading PNG iCCP chunk.");
1829 profile=AcquireStringInfo(profile_length);
1830 SetStringInfoDatum(profile,(const unsigned char *) info);
1831 (void) SetImageProfile(image,"icc",profile);
1832 profile=DestroyStringInfo(profile);
1836 #if defined(PNG_READ_sRGB_SUPPORTED)
1841 if (mng_info->have_global_srgb)
1842 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1843 (mng_info->global_srgb_intent);
1845 if (png_get_sRGB(ping,ping_info,&intent))
1847 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1850 if (logging != MagickFalse)
1851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1852 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
1860 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1861 if (mng_info->have_global_gama)
1862 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
1864 if (png_get_gAMA(ping,ping_info,&file_gamma))
1866 image->gamma=(float) file_gamma;
1867 if (logging != MagickFalse)
1868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1869 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1872 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1874 if (mng_info->have_global_chrm != MagickFalse)
1876 (void) png_set_cHRM(ping,ping_info,
1877 mng_info->global_chrm.white_point.x,
1878 mng_info->global_chrm.white_point.y,
1879 mng_info->global_chrm.red_primary.x,
1880 mng_info->global_chrm.red_primary.y,
1881 mng_info->global_chrm.green_primary.x,
1882 mng_info->global_chrm.green_primary.y,
1883 mng_info->global_chrm.blue_primary.x,
1884 mng_info->global_chrm.blue_primary.y);
1888 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1890 (void) png_get_cHRM(ping,ping_info,
1891 &image->chromaticity.white_point.x,
1892 &image->chromaticity.white_point.y,
1893 &image->chromaticity.red_primary.x,
1894 &image->chromaticity.red_primary.y,
1895 &image->chromaticity.green_primary.x,
1896 &image->chromaticity.green_primary.y,
1897 &image->chromaticity.blue_primary.x,
1898 &image->chromaticity.blue_primary.y);
1900 if (logging != MagickFalse)
1901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1902 " Reading PNG cHRM chunk.");
1905 if (image->rendering_intent != UndefinedIntent)
1907 png_set_sRGB(ping,ping_info,
1908 Magick_RenderingIntent_to_PNG_RenderingIntent
1909 (image->rendering_intent));
1910 png_set_gAMA(ping,ping_info,0.45455f);
1911 png_set_cHRM(ping,ping_info,
1912 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1913 0.1500f, 0.0600f, 0.3127f, 0.3290f);
1915 #if defined(PNG_oFFs_SUPPORTED)
1916 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
1918 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1919 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
1921 if (logging != MagickFalse)
1922 if (image->page.x || image->page.y)
1923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1924 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1925 image->page.x,(double) image->page.y);
1928 #if defined(PNG_pHYs_SUPPORTED)
1929 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1931 if (mng_info->have_global_phys)
1933 png_set_pHYs(ping,ping_info,
1934 mng_info->global_x_pixels_per_unit,
1935 mng_info->global_y_pixels_per_unit,
1936 mng_info->global_phys_unit_type);
1940 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1950 Set image resolution.
1952 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
1954 image->x_resolution=(double) x_resolution;
1955 image->y_resolution=(double) y_resolution;
1957 if (unit_type == PNG_RESOLUTION_METER)
1959 image->units=PixelsPerCentimeterResolution;
1960 image->x_resolution=(double) x_resolution/100.0;
1961 image->y_resolution=(double) y_resolution/100.0;
1964 if (logging != MagickFalse)
1965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1966 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1967 (double) x_resolution,(double) y_resolution,unit_type);
1970 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
1978 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
1980 if ((number_colors == 0) &&
1981 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1983 if (mng_info->global_plte_length)
1985 png_set_PLTE(ping,ping_info,mng_info->global_plte,
1986 (int) mng_info->global_plte_length);
1988 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
1989 if (mng_info->global_trns_length)
1991 if (mng_info->global_trns_length >
1992 mng_info->global_plte_length)
1993 (void) ThrowMagickException(&image->exception,
1994 GetMagickModule(),CoderError,
1995 "global tRNS has more entries than global PLTE",
1996 "`%s'",image_info->filename);
1997 png_set_tRNS(ping,ping_info,mng_info->global_trns,
1998 (int) mng_info->global_trns_length,NULL);
2000 #if defined(PNG_READ_bKGD_SUPPORTED)
2002 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2003 mng_info->have_saved_bkgd_index ||
2005 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2010 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2011 if (mng_info->have_saved_bkgd_index)
2012 background.index=mng_info->saved_bkgd_index;
2014 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2015 background.index=ping_background->index;
2017 background.red=(png_uint_16)
2018 mng_info->global_plte[background.index].red;
2020 background.green=(png_uint_16)
2021 mng_info->global_plte[background.index].green;
2023 background.blue=(png_uint_16)
2024 mng_info->global_plte[background.index].blue;
2026 png_set_bKGD(ping,ping_info,&background);
2031 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2032 CoderError,"No global PLTE in file","`%s'",
2033 image_info->filename);
2037 #if defined(PNG_READ_bKGD_SUPPORTED)
2038 if (mng_info->have_global_bkgd &&
2039 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2040 image->background_color=mng_info->mng_global_bkgd;
2042 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2045 Set image background color.
2047 if (logging != MagickFalse)
2048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2049 " Reading PNG bKGD chunk.");
2051 if (ping_bit_depth == MAGICKCORE_QUANTUM_DEPTH)
2053 image->background_color.red=ping_background->red;
2054 image->background_color.green=ping_background->green;
2055 image->background_color.blue=ping_background->blue;
2058 else /* Scale background components to 16-bit */
2063 if (logging != MagickFalse)
2064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2065 " raw ping_background=(%d,%d,%d).",ping_background->red,
2066 ping_background->green,ping_background->blue);
2070 if (ping_bit_depth == 1)
2073 else if (ping_bit_depth == 2)
2076 else if (ping_bit_depth == 4)
2079 if (ping_bit_depth <= 8)
2082 ping_background->red *= bkgd_scale;
2083 ping_background->green *= bkgd_scale;
2084 ping_background->blue *= bkgd_scale;
2086 if (logging != MagickFalse)
2088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2089 " bkgd_scale=%d.",bkgd_scale);
2091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2092 " ping_background=(%d,%d,%d).",ping_background->red,
2093 ping_background->green,ping_background->blue);
2096 image->background_color.red=
2097 ScaleShortToQuantum(ping_background->red);
2099 image->background_color.green=
2100 ScaleShortToQuantum(ping_background->green);
2102 image->background_color.blue=
2103 ScaleShortToQuantum(ping_background->blue);
2105 image->background_color.opacity=OpaqueOpacity;
2107 if (logging != MagickFalse)
2108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2109 " image->background_color=(%.20g,%.20g,%.20g).",
2110 (double) image->background_color.red,
2111 (double) image->background_color.green,
2112 (double) image->background_color.blue);
2117 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2120 Image has a tRNS chunk.
2128 if (logging != MagickFalse)
2129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2130 " Reading PNG tRNS chunk.");
2132 max_sample = (int) ((one << ping_bit_depth) - 1);
2134 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2135 (int)ping_trans_color->gray > max_sample) ||
2136 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2137 ((int)ping_trans_color->red > max_sample ||
2138 (int)ping_trans_color->green > max_sample ||
2139 (int)ping_trans_color->blue > max_sample)))
2141 if (logging != MagickFalse)
2142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2143 " Ignoring PNG tRNS chunk with out-of-range sample.");
2144 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2145 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2146 image->matte=MagickFalse;
2153 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2155 /* Scale transparent_color to short */
2156 transparent_color.red= scale_to_short*ping_trans_color->red;
2157 transparent_color.green= scale_to_short*ping_trans_color->green;
2158 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2159 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
2161 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2163 if (logging != MagickFalse)
2165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2166 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2169 " scaled graylevel is %d.",transparent_color.opacity);
2171 transparent_color.red=transparent_color.opacity;
2172 transparent_color.green=transparent_color.opacity;
2173 transparent_color.blue=transparent_color.opacity;
2177 #if defined(PNG_READ_sBIT_SUPPORTED)
2178 if (mng_info->have_global_sbit)
2180 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2181 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2184 num_passes=png_set_interlace_handling(ping);
2186 png_read_update_info(ping,ping_info);
2188 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2191 Initialize image structure.
2193 mng_info->image_box.left=0;
2194 mng_info->image_box.right=(ssize_t) ping_width;
2195 mng_info->image_box.top=0;
2196 mng_info->image_box.bottom=(ssize_t) ping_height;
2197 if (mng_info->mng_type == 0)
2199 mng_info->mng_width=ping_width;
2200 mng_info->mng_height=ping_height;
2201 mng_info->frame=mng_info->image_box;
2202 mng_info->clip=mng_info->image_box;
2207 image->page.y=mng_info->y_off[mng_info->object_id];
2210 image->compression=ZipCompression;
2211 image->columns=ping_width;
2212 image->rows=ping_height;
2213 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2214 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2215 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2220 image->storage_class=PseudoClass;
2222 image->colors=one << ping_bit_depth;
2223 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2224 if (image->colors > 256)
2227 if (image->colors > 65536L)
2228 image->colors=65536L;
2230 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2238 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2239 image->colors=(size_t) number_colors;
2241 if (logging != MagickFalse)
2242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2243 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2247 if (image->storage_class == PseudoClass)
2250 Initialize image colormap.
2252 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2253 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2255 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2263 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2265 for (i=0; i < (ssize_t) image->colors; i++)
2267 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2268 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2269 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2278 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2283 for (i=0; i < (ssize_t) image->colors; i++)
2285 image->colormap[i].red=(Quantum) (i*scale);
2286 image->colormap[i].green=(Quantum) (i*scale);
2287 image->colormap[i].blue=(Quantum) (i*scale);
2292 Read image scanlines.
2294 if (image->delay != 0)
2295 mng_info->scenes_found++;
2297 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2298 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2299 (image_info->first_scene+image_info->number_scenes))))
2301 if (logging != MagickFalse)
2302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2303 " Skipping PNG image data for scene %.20g",(double)
2304 mng_info->scenes_found-1);
2305 png_destroy_read_struct(&ping,&ping_info,&end_info);
2306 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2307 UnlockSemaphoreInfo(ping_semaphore);
2309 if (logging != MagickFalse)
2310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2311 " exit ReadOnePNGImage().");
2316 if (logging != MagickFalse)
2317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2318 " Reading PNG IDAT chunk(s)");
2321 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2322 ping_rowbytes*sizeof(*ping_pixels));
2325 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2326 sizeof(*ping_pixels));
2328 if (ping_pixels == (unsigned char *) NULL)
2329 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2331 if (logging != MagickFalse)
2332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2333 " Converting PNG pixels to pixel packets");
2335 Convert PNG pixels to pixel packets.
2337 if (setjmp(png_jmpbuf(ping)))
2340 PNG image is corrupt.
2342 png_destroy_read_struct(&ping,&ping_info,&end_info);
2343 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2344 UnlockSemaphoreInfo(ping_semaphore);
2346 if (quantum_info != (QuantumInfo *) NULL)
2347 quantum_info = DestroyQuantumInfo(quantum_info);
2349 if (ping_pixels != (unsigned char *) NULL)
2350 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2352 if (logging != MagickFalse)
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2354 " exit ReadOnePNGImage() with error.");
2356 if (image != (Image *) NULL)
2358 InheritException(exception,&image->exception);
2362 return(GetFirstImageInList(image));
2365 quantum_info=AcquireQuantumInfo(image_info,image);
2367 if (quantum_info == (QuantumInfo *) NULL)
2368 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2373 found_transparent_pixel;
2375 found_transparent_pixel=MagickFalse;
2377 if (image->storage_class == DirectClass)
2379 for (pass=0; pass < num_passes; pass++)
2382 Convert image to DirectClass pixel packets.
2384 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2388 depth=(ssize_t) ping_bit_depth;
2390 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2391 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2392 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2393 MagickTrue : MagickFalse;
2395 for (y=0; y < (ssize_t) image->rows; y++)
2398 row_offset=ping_rowbytes*y;
2403 png_read_row(ping,ping_pixels+row_offset,NULL);
2404 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2406 if (q == (PixelPacket *) NULL)
2409 #if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2410 /* code deleted from version 6.6.6-8 */
2411 #else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2413 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2414 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2415 GrayQuantum,ping_pixels+row_offset,exception);
2417 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2418 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2419 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2421 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2422 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2423 RGBAQuantum,ping_pixels+row_offset,exception);
2425 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2426 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2427 IndexQuantum,ping_pixels+row_offset,exception);
2429 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2430 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2431 RGBQuantum,ping_pixels+row_offset,exception);
2433 if (found_transparent_pixel == MagickFalse)
2435 /* Is there a transparent pixel in the row? */
2436 if (y== 0 && logging != MagickFalse)
2437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2438 " Looking for cheap transparent pixel");
2440 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2442 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2443 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2444 (q->opacity != OpaqueOpacity))
2446 if (logging != MagickFalse)
2447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2450 found_transparent_pixel = MagickTrue;
2453 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2454 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2455 (ScaleQuantumToShort(q->red) == transparent_color.red &&
2456 ScaleQuantumToShort(q->green) == transparent_color.green &&
2457 ScaleQuantumToShort(q->blue) == transparent_color.blue))
2459 if (logging != MagickFalse)
2460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2462 found_transparent_pixel = MagickTrue;
2469 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2471 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2474 if (status == MagickFalse)
2477 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2481 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2483 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2484 if (status == MagickFalse)
2490 else /* image->storage_class != DirectClass */
2492 for (pass=0; pass < num_passes; pass++)
2501 Convert grayscale image to PseudoClass pixel packets.
2503 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2504 MagickTrue : MagickFalse;
2506 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2507 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2509 if (quantum_scanline == (Quantum *) NULL)
2510 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2512 for (y=0; y < (ssize_t) image->rows; y++)
2515 row_offset=ping_rowbytes*y;
2520 png_read_row(ping,ping_pixels+row_offset,NULL);
2521 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2523 if (q == (PixelPacket *) NULL)
2526 indexes=GetAuthenticIndexQueue(image);
2527 p=ping_pixels+row_offset;
2530 switch (ping_bit_depth)
2537 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
2539 for (bit=7; bit >= 0; bit--)
2540 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2544 if ((image->columns % 8) != 0)
2546 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
2547 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2555 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
2557 *r++=(*p >> 6) & 0x03;
2558 *r++=(*p >> 4) & 0x03;
2559 *r++=(*p >> 2) & 0x03;
2563 if ((image->columns % 4) != 0)
2565 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
2566 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2574 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
2576 *r++=(*p >> 4) & 0x0f;
2580 if ((image->columns % 2) != 0)
2581 *r++=(*p++ >> 4) & 0x0f;
2588 if (ping_color_type == 4)
2589 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2592 /* In image.h, OpaqueOpacity is 0
2593 * TransparentOpacity is QuantumRange
2594 * In a PNG datastream, Opaque is QuantumRange
2595 * and Transparent is 0.
2597 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2598 if (q->opacity != OpaqueOpacity)
2599 found_transparent_pixel = MagickTrue;
2604 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2612 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2614 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2618 if (image->colors > 256)
2626 *r=(Quantum) quantum;
2629 if (ping_color_type == 4)
2631 quantum=((*p++) << 8);
2633 q->opacity=(Quantum) (QuantumRange-quantum);
2634 if (q->opacity != OpaqueOpacity)
2635 found_transparent_pixel = MagickTrue;
2639 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2643 if (image->colors > 256)
2654 if (ping_color_type == 4)
2656 q->opacity=(*p << 8) | *(p+1);
2658 q->opacity=(Quantum) GetAlphaPixelComponent(q);
2659 if (q->opacity != OpaqueOpacity)
2660 found_transparent_pixel = MagickTrue;
2665 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2667 p++; /* strip low byte */
2669 if (ping_color_type == 4)
2671 q->opacity=(Quantum) (QuantumRange-(*p++));
2672 if (q->opacity != OpaqueOpacity)
2673 found_transparent_pixel = MagickTrue;
2688 Transfer image scanline.
2692 for (x=0; x < (ssize_t) image->columns; x++)
2693 indexes[x]=(IndexPacket) (*r++);
2695 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2698 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2700 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2703 if (status == MagickFalse)
2708 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2710 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2712 if (status == MagickFalse)
2716 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2719 image->matte=found_transparent_pixel;
2721 if (logging != MagickFalse)
2723 if (found_transparent_pixel != MagickFalse)
2724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2725 " Found transparent pixel");
2728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2729 " No transparent pixel was found");
2731 ping_color_type&=0x03;
2736 if (quantum_info != (QuantumInfo *) NULL)
2737 quantum_info=DestroyQuantumInfo(quantum_info);
2739 if (image->storage_class == PseudoClass)
2745 image->matte=MagickFalse;
2746 (void) SyncImage(image);
2750 png_read_end(ping,ping_info);
2752 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2753 (ssize_t) image_info->first_scene && image->delay != 0)
2755 png_destroy_read_struct(&ping,&ping_info,&end_info);
2756 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2758 (void) SetImageBackgroundColor(image);
2759 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2760 UnlockSemaphoreInfo(ping_semaphore);
2762 if (logging != MagickFalse)
2763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2764 " exit ReadOnePNGImage() early.");
2768 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2774 Image has a transparent background.
2776 storage_class=image->storage_class;
2777 image->matte=MagickTrue;
2779 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
2781 if (storage_class == PseudoClass)
2783 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2785 for (x=0; x < ping_num_trans; x++)
2787 image->colormap[x].opacity =
2788 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2792 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2794 for (x=0; x < (int) image->colors; x++)
2796 if (ScaleQuantumToShort(image->colormap[x].red) ==
2797 transparent_color.opacity)
2799 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2803 (void) SyncImage(image);
2806 #if 1 /* Should have already been done above, but glennrp problem P10
2811 for (y=0; y < (ssize_t) image->rows; y++)
2813 image->storage_class=storage_class;
2814 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2816 if (q == (PixelPacket *) NULL)
2819 indexes=GetAuthenticIndexQueue(image);
2821 /* Caution: on a Q8 build, this does not distinguish between
2822 * 16-bit colors that differ only in the low byte
2824 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2826 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2827 ScaleQuantumToShort(q->green) == transparent_color.green &&
2828 ScaleQuantumToShort(q->blue) == transparent_color.blue)
2830 q->opacity=(Quantum) TransparentOpacity;
2833 #if 0 /* I have not found a case where this is needed. */
2836 q->opacity=(Quantum) OpaqueOpacity;
2843 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2849 image->storage_class=DirectClass;
2852 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2853 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2854 image->colorspace=GRAYColorspace;
2856 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2857 for (i=0; i < (ssize_t) num_text; i++)
2859 /* Check for a profile */
2861 if (logging != MagickFalse)
2862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2863 " Reading PNG text chunk");
2865 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2866 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2873 length=text[i].text_length;
2874 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2876 if (value == (char *) NULL)
2878 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2879 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2884 (void) ConcatenateMagickString(value,text[i].text,length+2);
2886 /* Don't save "density" property if we have a pHYs chunk */
2887 if (LocaleCompare(text[i].key,"density") != 0 ||
2888 !png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2889 (void) SetImageProperty(image,text[i].key,value);
2891 if (logging != MagickFalse)
2893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2894 " length: %lu",(unsigned long) length);
2895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2896 " Keyword: %s",text[i].key);
2899 value=DestroyString(value);
2903 #ifdef MNG_OBJECT_BUFFERS
2905 Store the object if necessary.
2907 if (object_id && !mng_info->frozen[object_id])
2909 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2912 create a new object buffer.
2914 mng_info->ob[object_id]=(MngBuffer *)
2915 AcquireMagickMemory(sizeof(MngBuffer));
2917 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2919 mng_info->ob[object_id]->image=(Image *) NULL;
2920 mng_info->ob[object_id]->reference_count=1;
2924 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2925 mng_info->ob[object_id]->frozen)
2927 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2928 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2929 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2932 if (mng_info->ob[object_id]->frozen)
2933 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2934 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2935 "`%s'",image->filename);
2941 if (mng_info->ob[object_id]->image != (Image *) NULL)
2942 mng_info->ob[object_id]->image=DestroyImage
2943 (mng_info->ob[object_id]->image);
2945 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2948 if (mng_info->ob[object_id]->image != (Image *) NULL)
2949 mng_info->ob[object_id]->image->file=(FILE *) NULL;
2952 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2953 ResourceLimitError,"Cloning image for object buffer failed",
2954 "`%s'",image->filename);
2956 if (ping_width > 250000L || ping_height > 250000L)
2957 png_error(ping,"PNG Image dimensions are too large.");
2959 mng_info->ob[object_id]->width=ping_width;
2960 mng_info->ob[object_id]->height=ping_height;
2961 mng_info->ob[object_id]->color_type=ping_color_type;
2962 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
2963 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
2964 mng_info->ob[object_id]->compression_method=
2965 ping_compression_method;
2966 mng_info->ob[object_id]->filter_method=ping_filter_method;
2968 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2977 Copy the PLTE to the object buffer.
2979 png_get_PLTE(ping,ping_info,&plte,&number_colors);
2980 mng_info->ob[object_id]->plte_length=number_colors;
2982 for (i=0; i < number_colors; i++)
2984 mng_info->ob[object_id]->plte[i]=plte[i];
2989 mng_info->ob[object_id]->plte_length=0;
2994 Relinquish resources.
2996 png_destroy_read_struct(&ping,&ping_info,&end_info);
2998 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2999 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3000 UnlockSemaphoreInfo(ping_semaphore);
3003 if (logging != MagickFalse)
3004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3005 " exit ReadOnePNGImage()");
3009 /* end of reading one PNG image */
3012 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3027 magic_number[MaxTextExtent];
3035 assert(image_info != (const ImageInfo *) NULL);
3036 assert(image_info->signature == MagickSignature);
3038 if (image_info->debug != MagickFalse)
3039 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3040 image_info->filename);
3042 assert(exception != (ExceptionInfo *) NULL);
3043 assert(exception->signature == MagickSignature);
3044 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3045 image=AcquireImage(image_info);
3046 mng_info=(MngInfo *) NULL;
3047 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3049 if (status == MagickFalse)
3050 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3053 Verify PNG signature.
3055 count=ReadBlob(image,8,(unsigned char *) magic_number);
3057 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3058 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3061 Allocate a MngInfo structure.
3063 have_mng_structure=MagickFalse;
3064 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3066 if (mng_info == (MngInfo *) NULL)
3067 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3070 Initialize members of the MngInfo structure.
3072 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3073 mng_info->image=image;
3074 have_mng_structure=MagickTrue;
3077 image=ReadOnePNGImage(mng_info,image_info,exception);
3078 MngInfoFreeStruct(mng_info,&have_mng_structure);
3080 if (image == (Image *) NULL)
3082 if (previous != (Image *) NULL)
3084 if (previous->signature != MagickSignature)
3085 ThrowReaderException(CorruptImageError,"CorruptImage");
3087 (void) CloseBlob(previous);
3088 (void) DestroyImageList(previous);
3091 if (logging != MagickFalse)
3092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3093 "exit ReadPNGImage() with error");
3095 return((Image *) NULL);
3098 (void) CloseBlob(image);
3100 if ((image->columns == 0) || (image->rows == 0))
3102 if (logging != MagickFalse)
3103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3104 "exit ReadPNGImage() with error.");
3106 ThrowReaderException(CorruptImageError,"CorruptImage");
3109 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3111 (void) SetImageType(image,PaletteType);
3113 if (image->matte != MagickFalse)
3115 /* To do: Reduce to binary transparency */
3119 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3121 (void) SetImageType(image,TrueColorType);
3122 image->matte=MagickFalse;
3125 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3126 (void) SetImageType(image,TrueColorMatteType);
3128 if (logging != MagickFalse)
3129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3130 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3131 (double) image->page.width,(double) image->page.height,
3132 (double) image->page.x,(double) image->page.y);
3134 if (logging != MagickFalse)
3135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3142 #if defined(JNG_SUPPORTED)
3144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3148 % R e a d O n e J N G I m a g e %
3152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3154 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3155 % (minus the 8-byte signature) and returns it. It allocates the memory
3156 % necessary for the new Image structure and returns a pointer to the new
3159 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3161 % The format of the ReadOneJNGImage method is:
3163 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3164 % ExceptionInfo *exception)
3166 % A description of each parameter follows:
3168 % o mng_info: Specifies a pointer to a MngInfo structure.
3170 % o image_info: the image info.
3172 % o exception: return any errors or warnings in this structure.
3175 static Image *ReadOneJNGImage(MngInfo *mng_info,
3176 const ImageInfo *image_info, ExceptionInfo *exception)
3203 jng_image_sample_depth,
3204 jng_image_compression_method,
3205 jng_image_interlace_method,
3206 jng_alpha_sample_depth,
3207 jng_alpha_compression_method,
3208 jng_alpha_filter_method,
3209 jng_alpha_interlace_method;
3211 register const PixelPacket
3218 register PixelPacket
3221 register unsigned char
3232 jng_alpha_compression_method=0;
3233 jng_alpha_sample_depth=8;
3237 alpha_image=(Image *) NULL;
3238 color_image=(Image *) NULL;
3239 alpha_image_info=(ImageInfo *) NULL;
3240 color_image_info=(ImageInfo *) NULL;
3242 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3243 " Enter ReadOneJNGImage()");
3245 image=mng_info->image;
3247 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3250 Allocate next image structure.
3252 if (logging != MagickFalse)
3253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3254 " AcquireNextImage()");
3256 AcquireNextImage(image_info,image);
3258 if (GetNextImageInList(image) == (Image *) NULL)
3259 return((Image *) NULL);
3261 image=SyncNextImageInList(image);
3263 mng_info->image=image;
3266 Signature bytes have already been read.
3269 read_JSEP=MagickFalse;
3270 reading_idat=MagickFalse;
3271 skip_to_iend=MagickFalse;
3275 type[MaxTextExtent];
3284 Read a new JNG chunk.
3286 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3287 2*GetBlobSize(image));
3289 if (status == MagickFalse)
3293 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3294 length=ReadBlobMSBLong(image);
3295 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3297 if (logging != MagickFalse)
3298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3299 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3300 type[0],type[1],type[2],type[3],(double) length);
3302 if (length > PNG_UINT_31_MAX || count == 0)
3303 ThrowReaderException(CorruptImageError,"CorruptImage");
3306 chunk=(unsigned char *) NULL;
3310 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3312 if (chunk == (unsigned char *) NULL)
3313 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3315 for (i=0; i < (ssize_t) length; i++)
3316 chunk[i]=(unsigned char) ReadBlobByte(image);
3321 (void) ReadBlobMSBLong(image); /* read crc word */
3326 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3331 if (memcmp(type,mng_JHDR,4) == 0)
3335 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3336 (p[2] << 8) | p[3]);
3337 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3338 (p[6] << 8) | p[7]);
3339 jng_color_type=p[8];
3340 jng_image_sample_depth=p[9];
3341 jng_image_compression_method=p[10];
3342 jng_image_interlace_method=p[11];
3344 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3347 jng_alpha_sample_depth=p[12];
3348 jng_alpha_compression_method=p[13];
3349 jng_alpha_filter_method=p[14];
3350 jng_alpha_interlace_method=p[15];
3352 if (logging != MagickFalse)
3354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3355 " jng_width: %16lu",(unsigned long) jng_width);
3357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3358 " jng_width: %16lu",(unsigned long) jng_height);
3360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3361 " jng_color_type: %16d",jng_color_type);
3363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3364 " jng_image_sample_depth: %3d",
3365 jng_image_sample_depth);
3367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3368 " jng_image_compression_method:%3d",
3369 jng_image_compression_method);
3371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3372 " jng_image_interlace_method: %3d",
3373 jng_image_interlace_method);
3375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3376 " jng_alpha_sample_depth: %3d",
3377 jng_alpha_sample_depth);
3379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3380 " jng_alpha_compression_method:%3d",
3381 jng_alpha_compression_method);
3383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3384 " jng_alpha_filter_method: %3d",
3385 jng_alpha_filter_method);
3387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3388 " jng_alpha_interlace_method: %3d",
3389 jng_alpha_interlace_method);
3394 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3400 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3401 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3402 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3405 o create color_image
3406 o open color_blob, attached to color_image
3407 o if (color type has alpha)
3408 open alpha_blob, attached to alpha_image
3411 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3413 if (color_image_info == (ImageInfo *) NULL)
3414 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3416 GetImageInfo(color_image_info);
3417 color_image=AcquireImage(color_image_info);
3419 if (color_image == (Image *) NULL)
3420 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3422 if (logging != MagickFalse)
3423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3424 " Creating color_blob.");
3426 (void) AcquireUniqueFilename(color_image->filename);
3427 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3430 if (status == MagickFalse)
3431 return((Image *) NULL);
3433 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3435 alpha_image_info=(ImageInfo *)
3436 AcquireMagickMemory(sizeof(ImageInfo));
3438 if (alpha_image_info == (ImageInfo *) NULL)
3439 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3441 GetImageInfo(alpha_image_info);
3442 alpha_image=AcquireImage(alpha_image_info);
3444 if (alpha_image == (Image *) NULL)
3446 alpha_image=DestroyImage(alpha_image);
3447 ThrowReaderException(ResourceLimitError,
3448 "MemoryAllocationFailed");
3451 if (logging != MagickFalse)
3452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3453 " Creating alpha_blob.");
3455 (void) AcquireUniqueFilename(alpha_image->filename);
3456 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3459 if (status == MagickFalse)
3460 return((Image *) NULL);
3462 if (jng_alpha_compression_method == 0)
3467 if (logging != MagickFalse)
3468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3469 " Writing IHDR chunk to alpha_blob.");
3471 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3472 "\211PNG\r\n\032\n");
3474 (void) WriteBlobMSBULong(alpha_image,13L);
3475 PNGType(data,mng_IHDR);
3476 LogPNGChunk(logging,mng_IHDR,13L);
3477 PNGLong(data+4,jng_width);
3478 PNGLong(data+8,jng_height);
3479 data[12]=jng_alpha_sample_depth;
3480 data[13]=0; /* color_type gray */
3481 data[14]=0; /* compression method 0 */
3482 data[15]=0; /* filter_method 0 */
3483 data[16]=0; /* interlace_method 0 */
3484 (void) WriteBlob(alpha_image,17,data);
3485 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3488 reading_idat=MagickTrue;
3491 if (memcmp(type,mng_JDAT,4) == 0)
3493 /* Copy chunk to color_image->blob */
3495 if (logging != MagickFalse)
3496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3497 " Copying JDAT chunk data to color_blob.");
3499 (void) WriteBlob(color_image,length,chunk);
3502 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3507 if (memcmp(type,mng_IDAT,4) == 0)
3512 /* Copy IDAT header and chunk data to alpha_image->blob */
3514 if (image_info->ping == MagickFalse)
3516 if (logging != MagickFalse)
3517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3518 " Copying IDAT chunk data to alpha_blob.");
3520 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3521 PNGType(data,mng_IDAT);
3522 LogPNGChunk(logging,mng_IDAT,length);
3523 (void) WriteBlob(alpha_image,4,data);
3524 (void) WriteBlob(alpha_image,length,chunk);
3525 (void) WriteBlobMSBULong(alpha_image,
3526 crc32(crc32(0,data,4),chunk,(uInt) length));
3530 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3535 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3537 /* Copy chunk data to alpha_image->blob */
3539 if (image_info->ping == MagickFalse)
3541 if (logging != MagickFalse)
3542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3543 " Copying JDAA chunk data to alpha_blob.");
3545 (void) WriteBlob(alpha_image,length,chunk);
3549 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3554 if (memcmp(type,mng_JSEP,4) == 0)
3556 read_JSEP=MagickTrue;
3559 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3564 if (memcmp(type,mng_bKGD,4) == 0)
3568 image->background_color.red=ScaleCharToQuantum(p[1]);
3569 image->background_color.green=image->background_color.red;
3570 image->background_color.blue=image->background_color.red;
3575 image->background_color.red=ScaleCharToQuantum(p[1]);
3576 image->background_color.green=ScaleCharToQuantum(p[3]);
3577 image->background_color.blue=ScaleCharToQuantum(p[5]);
3580 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3584 if (memcmp(type,mng_gAMA,4) == 0)
3587 image->gamma=((float) mng_get_long(p))*0.00001;
3589 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3593 if (memcmp(type,mng_cHRM,4) == 0)
3597 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3598 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3599 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3600 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3601 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3602 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3603 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3604 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3607 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3611 if (memcmp(type,mng_sRGB,4) == 0)
3615 image->rendering_intent=
3616 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3617 image->gamma=0.45455f;
3618 image->chromaticity.red_primary.x=0.6400f;
3619 image->chromaticity.red_primary.y=0.3300f;
3620 image->chromaticity.green_primary.x=0.3000f;
3621 image->chromaticity.green_primary.y=0.6000f;
3622 image->chromaticity.blue_primary.x=0.1500f;
3623 image->chromaticity.blue_primary.y=0.0600f;
3624 image->chromaticity.white_point.x=0.3127f;
3625 image->chromaticity.white_point.y=0.3290f;
3628 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3632 if (memcmp(type,mng_oFFs,4) == 0)
3636 image->page.x=(ssize_t) mng_get_long(p);
3637 image->page.y=(ssize_t) mng_get_long(&p[4]);
3639 if ((int) p[8] != 0)
3641 image->page.x/=10000;
3642 image->page.y/=10000;
3647 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3652 if (memcmp(type,mng_pHYs,4) == 0)
3656 image->x_resolution=(double) mng_get_long(p);
3657 image->y_resolution=(double) mng_get_long(&p[4]);
3658 if ((int) p[8] == PNG_RESOLUTION_METER)
3660 image->units=PixelsPerCentimeterResolution;
3661 image->x_resolution=image->x_resolution/100.0f;
3662 image->y_resolution=image->y_resolution/100.0f;
3666 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3671 if (memcmp(type,mng_iCCP,4) == 0)
3675 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3682 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3684 if (memcmp(type,mng_IEND,4))
3694 Finish up reading image data:
3696 o read main image from color_blob.
3700 o if (color_type has alpha)
3701 if alpha_encoding is PNG
3702 read secondary image from alpha_blob via ReadPNG
3703 if alpha_encoding is JPEG
3704 read secondary image from alpha_blob via ReadJPEG
3708 o copy intensity of secondary image into
3709 opacity samples of main image.
3711 o destroy the secondary image.
3714 (void) CloseBlob(color_image);
3716 if (logging != MagickFalse)
3717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3718 " Reading jng_image from color_blob.");
3720 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3721 color_image->filename);
3723 color_image_info->ping=MagickFalse; /* To do: avoid this */
3724 jng_image=ReadImage(color_image_info,exception);
3726 if (jng_image == (Image *) NULL)
3727 return((Image *) NULL);
3729 (void) RelinquishUniqueFileResource(color_image->filename);
3730 color_image=DestroyImage(color_image);
3731 color_image_info=DestroyImageInfo(color_image_info);
3733 if (jng_image == (Image *) NULL)
3734 return((Image *) NULL);
3736 if (logging != MagickFalse)
3737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3738 " Copying jng_image pixels to main image.");
3740 image->rows=jng_height;
3741 image->columns=jng_width;
3742 length=image->columns*sizeof(PixelPacket);
3744 for (y=0; y < (ssize_t) image->rows; y++)
3746 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3747 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3748 (void) CopyMagickMemory(q,s,length);
3750 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3754 jng_image=DestroyImage(jng_image);
3756 if (image_info->ping == MagickFalse)
3758 if (jng_color_type >= 12)
3760 if (jng_alpha_compression_method == 0)
3764 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3765 PNGType(data,mng_IEND);
3766 LogPNGChunk(logging,mng_IEND,0L);
3767 (void) WriteBlob(alpha_image,4,data);
3768 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3771 (void) CloseBlob(alpha_image);
3773 if (logging != MagickFalse)
3774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3775 " Reading opacity from alpha_blob.");
3777 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3778 "%s",alpha_image->filename);
3780 jng_image=ReadImage(alpha_image_info,exception);
3782 if (jng_image != (Image *) NULL)
3783 for (y=0; y < (ssize_t) image->rows; y++)
3785 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3787 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3789 if (image->matte != MagickFalse)
3790 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3791 q->opacity=(Quantum) QuantumRange-s->red;
3794 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3796 q->opacity=(Quantum) QuantumRange-s->red;
3797 if (q->opacity != OpaqueOpacity)
3798 image->matte=MagickTrue;
3801 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3804 (void) RelinquishUniqueFileResource(alpha_image->filename);
3805 alpha_image=DestroyImage(alpha_image);
3806 alpha_image_info=DestroyImageInfo(alpha_image_info);
3807 if (jng_image != (Image *) NULL)
3808 jng_image=DestroyImage(jng_image);
3812 /* Read the JNG image. */
3814 if (mng_info->mng_type == 0)
3816 mng_info->mng_width=jng_width;
3817 mng_info->mng_height=jng_height;
3820 if (image->page.width == 0 && image->page.height == 0)
3822 image->page.width=jng_width;
3823 image->page.height=jng_height;
3826 if (image->page.x == 0 && image->page.y == 0)
3828 image->page.x=mng_info->x_off[mng_info->object_id];
3829 image->page.y=mng_info->y_off[mng_info->object_id];
3834 image->page.y=mng_info->y_off[mng_info->object_id];
3837 mng_info->image_found++;
3838 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3839 2*GetBlobSize(image));
3841 if (logging != MagickFalse)
3842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3843 " exit ReadOneJNGImage()");
3849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3853 % R e a d J N G I m a g e %
3857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3859 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3860 % (including the 8-byte signature) and returns it. It allocates the memory
3861 % necessary for the new Image structure and returns a pointer to the new
3864 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3866 % The format of the ReadJNGImage method is:
3868 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3871 % A description of each parameter follows:
3873 % o image_info: the image info.
3875 % o exception: return any errors or warnings in this structure.
3879 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3894 magic_number[MaxTextExtent];
3902 assert(image_info != (const ImageInfo *) NULL);
3903 assert(image_info->signature == MagickSignature);
3904 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3905 assert(exception != (ExceptionInfo *) NULL);
3906 assert(exception->signature == MagickSignature);
3907 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
3908 image=AcquireImage(image_info);
3909 mng_info=(MngInfo *) NULL;
3910 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3912 if (status == MagickFalse)
3913 return((Image *) NULL);
3915 if (LocaleCompare(image_info->magick,"JNG") != 0)
3916 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3918 /* Verify JNG signature. */
3920 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3922 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3923 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3925 /* Allocate a MngInfo structure. */
3927 have_mng_structure=MagickFalse;
3928 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
3930 if (mng_info == (MngInfo *) NULL)
3931 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3933 /* Initialize members of the MngInfo structure. */
3935 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3936 have_mng_structure=MagickTrue;
3938 mng_info->image=image;
3940 image=ReadOneJNGImage(mng_info,image_info,exception);
3941 MngInfoFreeStruct(mng_info,&have_mng_structure);
3943 if (image == (Image *) NULL)
3945 if (IsImageObject(previous) != MagickFalse)
3947 (void) CloseBlob(previous);
3948 (void) DestroyImageList(previous);
3951 if (logging != MagickFalse)
3952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3953 "exit ReadJNGImage() with error");
3955 return((Image *) NULL);
3957 (void) CloseBlob(image);
3959 if (image->columns == 0 || image->rows == 0)
3961 if (logging != MagickFalse)
3962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3963 "exit ReadJNGImage() with error");
3965 ThrowReaderException(CorruptImageError,"CorruptImage");
3968 if (logging != MagickFalse)
3969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3975 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3978 page_geometry[MaxTextExtent];
4011 #if defined(MNG_INSERT_LAYERS)
4013 mng_background_color;
4016 register unsigned char
4031 #if defined(MNG_INSERT_LAYERS)
4036 volatile unsigned int
4037 #ifdef MNG_OBJECT_BUFFERS
4038 mng_background_object=0,
4040 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4043 default_frame_timeout,
4045 #if defined(MNG_INSERT_LAYERS)
4051 /* These delays are all measured in image ticks_per_second,
4052 * not in MNG ticks_per_second
4055 default_frame_delay,
4059 #if defined(MNG_INSERT_LAYERS)
4068 previous_fb.bottom=0;
4070 previous_fb.right=0;
4072 default_fb.bottom=0;
4076 /* Open image file. */
4078 assert(image_info != (const ImageInfo *) NULL);
4079 assert(image_info->signature == MagickSignature);
4080 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4081 assert(exception != (ExceptionInfo *) NULL);
4082 assert(exception->signature == MagickSignature);
4083 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4084 image=AcquireImage(image_info);
4085 mng_info=(MngInfo *) NULL;
4086 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4088 if (status == MagickFalse)
4089 return((Image *) NULL);
4091 first_mng_object=MagickFalse;
4093 have_mng_structure=MagickFalse;
4095 /* Allocate a MngInfo structure. */
4097 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4099 if (mng_info == (MngInfo *) NULL)
4100 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4102 /* Initialize members of the MngInfo structure. */
4104 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4105 mng_info->image=image;
4106 have_mng_structure=MagickTrue;
4108 if (LocaleCompare(image_info->magick,"MNG") == 0)
4111 magic_number[MaxTextExtent];
4113 /* Verify MNG signature. */
4114 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4115 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4116 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4118 /* Initialize some nonzero members of the MngInfo structure. */
4119 for (i=0; i < MNG_MAX_OBJECTS; i++)
4121 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4122 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4124 mng_info->exists[0]=MagickTrue;
4127 first_mng_object=MagickTrue;
4129 #if defined(MNG_INSERT_LAYERS)
4130 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4132 default_frame_delay=0;
4133 default_frame_timeout=0;
4136 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4138 skip_to_iend=MagickFalse;
4139 term_chunk_found=MagickFalse;
4140 mng_info->framing_mode=1;
4141 #if defined(MNG_INSERT_LAYERS)
4142 mandatory_back=MagickFalse;
4144 #if defined(MNG_INSERT_LAYERS)
4145 mng_background_color=image->background_color;
4147 default_fb=mng_info->frame;
4148 previous_fb=mng_info->frame;
4152 type[MaxTextExtent];
4154 if (LocaleCompare(image_info->magick,"MNG") == 0)
4163 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4164 length=ReadBlobMSBLong(image);
4165 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4167 if (logging != MagickFalse)
4168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4169 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4170 type[0],type[1],type[2],type[3],(double) length);
4172 if (length > PNG_UINT_31_MAX)
4176 ThrowReaderException(CorruptImageError,"CorruptImage");
4179 chunk=(unsigned char *) NULL;
4183 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4185 if (chunk == (unsigned char *) NULL)
4186 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4188 for (i=0; i < (ssize_t) length; i++)
4189 chunk[i]=(unsigned char) ReadBlobByte(image);
4194 (void) ReadBlobMSBLong(image); /* read crc word */
4196 #if !defined(JNG_SUPPORTED)
4197 if (memcmp(type,mng_JHDR,4) == 0)
4199 skip_to_iend=MagickTrue;
4201 if (mng_info->jhdr_warning == 0)
4202 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4203 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4205 mng_info->jhdr_warning++;
4208 if (memcmp(type,mng_DHDR,4) == 0)
4210 skip_to_iend=MagickTrue;
4212 if (mng_info->dhdr_warning == 0)
4213 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4214 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4216 mng_info->dhdr_warning++;
4218 if (memcmp(type,mng_MEND,4) == 0)
4223 if (memcmp(type,mng_IEND,4) == 0)
4224 skip_to_iend=MagickFalse;
4227 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4229 if (logging != MagickFalse)
4230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4236 if (memcmp(type,mng_MHDR,4) == 0)
4238 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4239 (p[2] << 8) | p[3]);
4241 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4242 (p[6] << 8) | p[7]);
4244 if (logging != MagickFalse)
4246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4247 " MNG width: %.20g",(double) mng_info->mng_width);
4248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4249 " MNG height: %.20g",(double) mng_info->mng_height);
4253 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4255 if (mng_info->ticks_per_second == 0)
4256 default_frame_delay=0;
4259 default_frame_delay=1UL*image->ticks_per_second/
4260 mng_info->ticks_per_second;
4262 frame_delay=default_frame_delay;
4268 simplicity=(size_t) mng_get_long(p);
4271 mng_type=1; /* Full MNG */
4273 if ((simplicity != 0) && ((simplicity | 11) == 11))
4274 mng_type=2; /* LC */
4276 if ((simplicity != 0) && ((simplicity | 9) == 9))
4277 mng_type=3; /* VLC */
4279 #if defined(MNG_INSERT_LAYERS)
4281 insert_layers=MagickTrue;
4283 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4285 /* Allocate next image structure. */
4286 AcquireNextImage(image_info,image);
4288 if (GetNextImageInList(image) == (Image *) NULL)
4289 return((Image *) NULL);
4291 image=SyncNextImageInList(image);
4292 mng_info->image=image;
4295 if ((mng_info->mng_width > 65535L) ||
4296 (mng_info->mng_height > 65535L))
4297 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4299 (void) FormatMagickString(page_geometry,MaxTextExtent,
4300 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4301 mng_info->mng_height);
4303 mng_info->frame.left=0;
4304 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4305 mng_info->frame.top=0;
4306 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4307 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4309 for (i=0; i < MNG_MAX_OBJECTS; i++)
4310 mng_info->object_clip[i]=mng_info->frame;
4312 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4316 if (memcmp(type,mng_TERM,4) == 0)
4327 final_delay=(png_uint_32) mng_get_long(&p[2]);
4328 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4330 if (mng_iterations == PNG_UINT_31_MAX)
4333 image->iterations=mng_iterations;
4334 term_chunk_found=MagickTrue;
4337 if (logging != MagickFalse)
4339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4340 " repeat=%d",repeat);
4342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4343 " final_delay=%.20g",(double) final_delay);
4345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4346 " image->iterations=%.20g",(double) image->iterations);
4349 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4352 if (memcmp(type,mng_DEFI,4) == 0)
4355 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4356 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4359 object_id=(p[0] << 8) | p[1];
4361 if (mng_type == 2 && object_id != 0)
4362 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4363 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4366 if (object_id > MNG_MAX_OBJECTS)
4369 Instead ofsuing a warning we should allocate a larger
4370 MngInfo structure and continue.
4372 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4373 CoderError,"object id too large","`%s'",image->filename);
4374 object_id=MNG_MAX_OBJECTS;
4377 if (mng_info->exists[object_id])
4378 if (mng_info->frozen[object_id])
4380 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4381 (void) ThrowMagickException(&image->exception,
4382 GetMagickModule(),CoderError,
4383 "DEFI cannot redefine a frozen MNG object","`%s'",
4388 mng_info->exists[object_id]=MagickTrue;
4391 mng_info->invisible[object_id]=p[2];
4394 Extract object offset info.
4398 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4399 (p[5] << 16) | (p[6] << 8) | p[7]);
4401 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4402 (p[9] << 16) | (p[10] << 8) | p[11]);
4404 if (logging != MagickFalse)
4406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4407 " x_off[%d]: %.20g",object_id,(double)
4408 mng_info->x_off[object_id]);
4410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4411 " y_off[%d]: %.20g",object_id,(double)
4412 mng_info->y_off[object_id]);
4417 Extract object clipping info.
4420 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4423 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4426 if (memcmp(type,mng_bKGD,4) == 0)
4428 mng_info->have_global_bkgd=MagickFalse;
4432 mng_info->mng_global_bkgd.red=
4433 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4435 mng_info->mng_global_bkgd.green=
4436 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4438 mng_info->mng_global_bkgd.blue=
4439 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4441 mng_info->have_global_bkgd=MagickTrue;
4444 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4447 if (memcmp(type,mng_BACK,4) == 0)
4449 #if defined(MNG_INSERT_LAYERS)
4451 mandatory_back=p[6];
4456 if (mandatory_back && length > 5)
4458 mng_background_color.red=
4459 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4461 mng_background_color.green=
4462 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4464 mng_background_color.blue=
4465 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4467 mng_background_color.opacity=OpaqueOpacity;
4470 #ifdef MNG_OBJECT_BUFFERS
4472 mng_background_object=(p[7] << 8) | p[8];
4475 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4479 if (memcmp(type,mng_PLTE,4) == 0)
4481 /* Read global PLTE. */
4483 if (length && (length < 769))
4485 if (mng_info->global_plte == (png_colorp) NULL)
4486 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4487 sizeof(*mng_info->global_plte));
4489 for (i=0; i < (ssize_t) (length/3); i++)
4491 mng_info->global_plte[i].red=p[3*i];
4492 mng_info->global_plte[i].green=p[3*i+1];
4493 mng_info->global_plte[i].blue=p[3*i+2];
4496 mng_info->global_plte_length=(unsigned int) (length/3);
4499 for ( ; i < 256; i++)
4501 mng_info->global_plte[i].red=i;
4502 mng_info->global_plte[i].green=i;
4503 mng_info->global_plte[i].blue=i;
4507 mng_info->global_plte_length=256;
4510 mng_info->global_plte_length=0;
4512 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4516 if (memcmp(type,mng_tRNS,4) == 0)
4518 /* read global tRNS */
4521 for (i=0; i < (ssize_t) length; i++)
4522 mng_info->global_trns[i]=p[i];
4525 for ( ; i < 256; i++)
4526 mng_info->global_trns[i]=255;
4528 mng_info->global_trns_length=(unsigned int) length;
4529 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4532 if (memcmp(type,mng_gAMA,4) == 0)
4539 igamma=mng_get_long(p);
4540 mng_info->global_gamma=((float) igamma)*0.00001;
4541 mng_info->have_global_gama=MagickTrue;
4545 mng_info->have_global_gama=MagickFalse;
4547 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4551 if (memcmp(type,mng_cHRM,4) == 0)
4553 /* Read global cHRM */
4557 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4558 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4559 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4560 mng_info->global_chrm.red_primary.y=0.00001*
4561 mng_get_long(&p[12]);
4562 mng_info->global_chrm.green_primary.x=0.00001*
4563 mng_get_long(&p[16]);
4564 mng_info->global_chrm.green_primary.y=0.00001*
4565 mng_get_long(&p[20]);
4566 mng_info->global_chrm.blue_primary.x=0.00001*
4567 mng_get_long(&p[24]);
4568 mng_info->global_chrm.blue_primary.y=0.00001*
4569 mng_get_long(&p[28]);
4570 mng_info->have_global_chrm=MagickTrue;
4573 mng_info->have_global_chrm=MagickFalse;
4575 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4579 if (memcmp(type,mng_sRGB,4) == 0)
4586 mng_info->global_srgb_intent=
4587 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4588 mng_info->have_global_srgb=MagickTrue;
4591 mng_info->have_global_srgb=MagickFalse;
4593 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4597 if (memcmp(type,mng_iCCP,4) == 0)
4605 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4610 if (memcmp(type,mng_FRAM,4) == 0)
4613 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4614 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4617 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4618 image->delay=frame_delay;
4620 frame_delay=default_frame_delay;
4621 frame_timeout=default_frame_timeout;
4626 mng_info->framing_mode=p[0];
4628 if (logging != MagickFalse)
4629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4630 " Framing_mode=%d",mng_info->framing_mode);
4634 /* Note the delay and frame clipping boundaries. */
4636 p++; /* framing mode */
4638 while (*p && ((p-chunk) < (ssize_t) length))
4639 p++; /* frame name */
4641 p++; /* frame name terminator */
4643 if ((p-chunk) < (ssize_t) (length-4))
4650 change_delay=(*p++);
4651 change_timeout=(*p++);
4652 change_clipping=(*p++);
4653 p++; /* change_sync */
4657 frame_delay=1UL*image->ticks_per_second*
4660 if (mng_info->ticks_per_second != 0)
4661 frame_delay/=mng_info->ticks_per_second;
4664 frame_delay=PNG_UINT_31_MAX;
4666 if (change_delay == 2)
4667 default_frame_delay=frame_delay;
4671 if (logging != MagickFalse)
4672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4673 " Framing_delay=%.20g",(double) frame_delay);
4678 frame_timeout=1UL*image->ticks_per_second*
4681 if (mng_info->ticks_per_second != 0)
4682 frame_timeout/=mng_info->ticks_per_second;
4685 frame_timeout=PNG_UINT_31_MAX;
4687 if (change_delay == 2)
4688 default_frame_timeout=frame_timeout;
4692 if (logging != MagickFalse)
4693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4694 " Framing_timeout=%.20g",(double) frame_timeout);
4697 if (change_clipping)
4699 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4703 if (logging != MagickFalse)
4704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4705 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4706 (double) fb.left,(double) fb.right,(double) fb.top,
4707 (double) fb.bottom);
4709 if (change_clipping == 2)
4715 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4717 subframe_width=(size_t) (mng_info->clip.right
4718 -mng_info->clip.left);
4720 subframe_height=(size_t) (mng_info->clip.bottom
4721 -mng_info->clip.top);
4723 Insert a background layer behind the frame if framing_mode is 4.
4725 #if defined(MNG_INSERT_LAYERS)
4726 if (logging != MagickFalse)
4727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4728 " subframe_width=%.20g, subframe_height=%.20g",(double)
4729 subframe_width,(double) subframe_height);
4731 if (insert_layers && (mng_info->framing_mode == 4) &&
4732 (subframe_width) && (subframe_height))
4734 /* Allocate next image structure. */
4735 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4737 AcquireNextImage(image_info,image);
4739 if (GetNextImageInList(image) == (Image *) NULL)
4741 image=DestroyImageList(image);
4742 MngInfoFreeStruct(mng_info,&have_mng_structure);
4743 return((Image *) NULL);
4746 image=SyncNextImageInList(image);
4749 mng_info->image=image;
4751 if (term_chunk_found)
4753 image->start_loop=MagickTrue;
4754 image->iterations=mng_iterations;
4755 term_chunk_found=MagickFalse;
4759 image->start_loop=MagickFalse;
4761 image->columns=subframe_width;
4762 image->rows=subframe_height;
4763 image->page.width=subframe_width;
4764 image->page.height=subframe_height;
4765 image->page.x=mng_info->clip.left;
4766 image->page.y=mng_info->clip.top;
4767 image->background_color=mng_background_color;
4768 image->matte=MagickFalse;
4770 (void) SetImageBackgroundColor(image);
4772 if (logging != MagickFalse)
4773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4774 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4775 (double) mng_info->clip.left,(double) mng_info->clip.right,
4776 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4779 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4782 if (memcmp(type,mng_CLIP,4) == 0)
4791 first_object=(p[0] << 8) | p[1];
4792 last_object=(p[2] << 8) | p[3];
4794 for (i=(int) first_object; i <= (int) last_object; i++)
4796 if (mng_info->exists[i] && !mng_info->frozen[i])
4801 box=mng_info->object_clip[i];
4802 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4806 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4809 if (memcmp(type,mng_SAVE,4) == 0)
4811 for (i=1; i < MNG_MAX_OBJECTS; i++)
4812 if (mng_info->exists[i])
4814 mng_info->frozen[i]=MagickTrue;
4815 #ifdef MNG_OBJECT_BUFFERS
4816 if (mng_info->ob[i] != (MngBuffer *) NULL)
4817 mng_info->ob[i]->frozen=MagickTrue;
4822 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4827 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4829 /* Read DISC or SEEK. */
4831 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4833 for (i=1; i < MNG_MAX_OBJECTS; i++)
4834 MngInfoDiscardObject(mng_info,i);
4842 for (j=0; j < (ssize_t) length; j+=2)
4844 i=p[j] << 8 | p[j+1];
4845 MngInfoDiscardObject(mng_info,i);
4850 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4855 if (memcmp(type,mng_MOVE,4) == 0)
4863 first_object=(p[0] << 8) | p[1];
4864 last_object=(p[2] << 8) | p[3];
4865 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4867 if (mng_info->exists[i] && !mng_info->frozen[i])
4875 old_pair.a=mng_info->x_off[i];
4876 old_pair.b=mng_info->y_off[i];
4877 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4878 mng_info->x_off[i]=new_pair.a;
4879 mng_info->y_off[i]=new_pair.b;
4883 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4887 if (memcmp(type,mng_LOOP,4) == 0)
4889 ssize_t loop_iters=1;
4890 loop_level=chunk[0];
4891 mng_info->loop_active[loop_level]=1; /* mark loop active */
4893 /* Record starting point. */
4894 loop_iters=mng_get_long(&chunk[1]);
4896 if (logging != MagickFalse)
4897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4898 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
4899 (double) loop_iters);
4901 if (loop_iters == 0)
4902 skipping_loop=loop_level;
4906 mng_info->loop_jump[loop_level]=TellBlob(image);
4907 mng_info->loop_count[loop_level]=loop_iters;
4910 mng_info->loop_iteration[loop_level]=0;
4911 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4915 if (memcmp(type,mng_ENDL,4) == 0)
4917 loop_level=chunk[0];
4919 if (skipping_loop > 0)
4921 if (skipping_loop == loop_level)
4924 Found end of zero-iteration loop.
4927 mng_info->loop_active[loop_level]=0;
4933 if (mng_info->loop_active[loop_level] == 1)
4935 mng_info->loop_count[loop_level]--;
4936 mng_info->loop_iteration[loop_level]++;
4938 if (logging != MagickFalse)
4939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4940 " ENDL: LOOP level %.20g has %.20g remaining iters ",
4941 (double) loop_level,(double)
4942 mng_info->loop_count[loop_level]);
4944 if (mng_info->loop_count[loop_level] != 0)
4946 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4950 ThrowReaderException(CorruptImageError,
4951 "ImproperImageHeader");
4962 mng_info->loop_active[loop_level]=0;
4964 for (i=0; i < loop_level; i++)
4965 if (mng_info->loop_active[i] == 1)
4966 last_level=(short) i;
4967 loop_level=last_level;
4972 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4976 if (memcmp(type,mng_CLON,4) == 0)
4978 if (mng_info->clon_warning == 0)
4979 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4980 CoderError,"CLON is not implemented yet","`%s'",
4983 mng_info->clon_warning++;
4986 if (memcmp(type,mng_MAGN,4) == 0)
5001 magn_first=(p[0] << 8) | p[1];
5007 magn_last=(p[2] << 8) | p[3];
5010 magn_last=magn_first;
5011 #ifndef MNG_OBJECT_BUFFERS
5012 if (magn_first || magn_last)
5013 if (mng_info->magn_warning == 0)
5015 (void) ThrowMagickException(&image->exception,
5016 GetMagickModule(),CoderError,
5017 "MAGN is not implemented yet for nonzero objects",
5018 "`%s'",image->filename);
5020 mng_info->magn_warning++;
5030 magn_mx=(p[5] << 8) | p[6];
5039 magn_my=(p[7] << 8) | p[8];
5048 magn_ml=(p[9] << 8) | p[10];
5057 magn_mr=(p[11] << 8) | p[12];
5066 magn_mt=(p[13] << 8) | p[14];
5075 magn_mb=(p[15] << 8) | p[16];
5087 magn_methy=magn_methx;
5090 if (magn_methx > 5 || magn_methy > 5)
5091 if (mng_info->magn_warning == 0)
5093 (void) ThrowMagickException(&image->exception,
5094 GetMagickModule(),CoderError,
5095 "Unknown MAGN method in MNG datastream","`%s'",
5098 mng_info->magn_warning++;
5100 #ifdef MNG_OBJECT_BUFFERS
5101 /* Magnify existing objects in the range magn_first to magn_last */
5103 if (magn_first == 0 || magn_last == 0)
5105 /* Save the magnification factors for object 0 */
5106 mng_info->magn_mb=magn_mb;
5107 mng_info->magn_ml=magn_ml;
5108 mng_info->magn_mr=magn_mr;
5109 mng_info->magn_mt=magn_mt;
5110 mng_info->magn_mx=magn_mx;
5111 mng_info->magn_my=magn_my;
5112 mng_info->magn_methx=magn_methx;
5113 mng_info->magn_methy=magn_methy;
5117 if (memcmp(type,mng_PAST,4) == 0)
5119 if (mng_info->past_warning == 0)
5120 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5121 CoderError,"PAST is not implemented yet","`%s'",
5124 mng_info->past_warning++;
5127 if (memcmp(type,mng_SHOW,4) == 0)
5129 if (mng_info->show_warning == 0)
5130 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5131 CoderError,"SHOW is not implemented yet","`%s'",
5134 mng_info->show_warning++;
5137 if (memcmp(type,mng_sBIT,4) == 0)
5140 mng_info->have_global_sbit=MagickFalse;
5144 mng_info->global_sbit.gray=p[0];
5145 mng_info->global_sbit.red=p[0];
5146 mng_info->global_sbit.green=p[1];
5147 mng_info->global_sbit.blue=p[2];
5148 mng_info->global_sbit.alpha=p[3];
5149 mng_info->have_global_sbit=MagickTrue;
5152 if (memcmp(type,mng_pHYs,4) == 0)
5156 mng_info->global_x_pixels_per_unit=
5157 (size_t) mng_get_long(p);
5158 mng_info->global_y_pixels_per_unit=
5159 (size_t) mng_get_long(&p[4]);
5160 mng_info->global_phys_unit_type=p[8];
5161 mng_info->have_global_phys=MagickTrue;
5165 mng_info->have_global_phys=MagickFalse;
5167 if (memcmp(type,mng_pHYg,4) == 0)
5169 if (mng_info->phyg_warning == 0)
5170 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5171 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5173 mng_info->phyg_warning++;
5175 if (memcmp(type,mng_BASI,4) == 0)
5177 skip_to_iend=MagickTrue;
5179 if (mng_info->basi_warning == 0)
5180 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5181 CoderError,"BASI is not implemented yet","`%s'",
5184 mng_info->basi_warning++;
5185 #ifdef MNG_BASI_SUPPORTED
5186 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5187 (p[2] << 8) | p[3]);
5188 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5189 (p[6] << 8) | p[7]);
5190 basi_color_type=p[8];
5191 basi_compression_method=p[9];
5192 basi_filter_type=p[10];
5193 basi_interlace_method=p[11];
5195 basi_red=(p[12] << 8) & p[13];
5201 basi_green=(p[14] << 8) & p[15];
5207 basi_blue=(p[16] << 8) & p[17];
5213 basi_alpha=(p[18] << 8) & p[19];
5217 if (basi_sample_depth == 16)
5224 basi_viewable=p[20];
5230 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5234 if (memcmp(type,mng_IHDR,4)
5235 #if defined(JNG_SUPPORTED)
5236 && memcmp(type,mng_JHDR,4)
5240 /* Not an IHDR or JHDR chunk */
5242 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5247 if (logging != MagickFalse)
5248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5249 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5251 mng_info->exists[object_id]=MagickTrue;
5252 mng_info->viewable[object_id]=MagickTrue;
5254 if (mng_info->invisible[object_id])
5256 if (logging != MagickFalse)
5257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5258 " Skipping invisible object");
5260 skip_to_iend=MagickTrue;
5261 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5264 #if defined(MNG_INSERT_LAYERS)
5266 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5268 image_width=(size_t) mng_get_long(p);
5269 image_height=(size_t) mng_get_long(&p[4]);
5271 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5274 Insert a transparent background layer behind the entire animation
5275 if it is not full screen.
5277 #if defined(MNG_INSERT_LAYERS)
5278 if (insert_layers && mng_type && first_mng_object)
5280 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5281 (image_width < mng_info->mng_width) ||
5282 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5283 (image_height < mng_info->mng_height) ||
5284 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5286 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5289 Allocate next image structure.
5291 AcquireNextImage(image_info,image);
5293 if (GetNextImageInList(image) == (Image *) NULL)
5295 image=DestroyImageList(image);
5296 MngInfoFreeStruct(mng_info,&have_mng_structure);
5297 return((Image *) NULL);
5300 image=SyncNextImageInList(image);
5302 mng_info->image=image;
5304 if (term_chunk_found)
5306 image->start_loop=MagickTrue;
5307 image->iterations=mng_iterations;
5308 term_chunk_found=MagickFalse;
5312 image->start_loop=MagickFalse;
5314 /* Make a background rectangle. */
5317 image->columns=mng_info->mng_width;
5318 image->rows=mng_info->mng_height;
5319 image->page.width=mng_info->mng_width;
5320 image->page.height=mng_info->mng_height;
5323 image->background_color=mng_background_color;
5324 (void) SetImageBackgroundColor(image);
5325 if (logging != MagickFalse)
5326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5327 " Inserted transparent background layer, W=%.20g, H=%.20g",
5328 (double) mng_info->mng_width,(double) mng_info->mng_height);
5332 Insert a background layer behind the upcoming image if
5333 framing_mode is 3, and we haven't already inserted one.
5335 if (insert_layers && (mng_info->framing_mode == 3) &&
5336 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5337 (simplicity & 0x08)))
5339 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5342 Allocate next image structure.
5344 AcquireNextImage(image_info,image);
5346 if (GetNextImageInList(image) == (Image *) NULL)
5348 image=DestroyImageList(image);
5349 MngInfoFreeStruct(mng_info,&have_mng_structure);
5350 return((Image *) NULL);
5353 image=SyncNextImageInList(image);
5356 mng_info->image=image;
5358 if (term_chunk_found)
5360 image->start_loop=MagickTrue;
5361 image->iterations=mng_iterations;
5362 term_chunk_found=MagickFalse;
5366 image->start_loop=MagickFalse;
5369 image->columns=subframe_width;
5370 image->rows=subframe_height;
5371 image->page.width=subframe_width;
5372 image->page.height=subframe_height;
5373 image->page.x=mng_info->clip.left;
5374 image->page.y=mng_info->clip.top;
5375 image->background_color=mng_background_color;
5376 image->matte=MagickFalse;
5377 (void) SetImageBackgroundColor(image);
5379 if (logging != MagickFalse)
5380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5381 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5382 (double) mng_info->clip.left,(double) mng_info->clip.right,
5383 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5385 #endif /* MNG_INSERT_LAYERS */
5386 first_mng_object=MagickFalse;
5388 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5391 Allocate next image structure.
5393 AcquireNextImage(image_info,image);
5395 if (GetNextImageInList(image) == (Image *) NULL)
5397 image=DestroyImageList(image);
5398 MngInfoFreeStruct(mng_info,&have_mng_structure);
5399 return((Image *) NULL);
5402 image=SyncNextImageInList(image);
5404 mng_info->image=image;
5405 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5406 GetBlobSize(image));
5408 if (status == MagickFalse)
5411 if (term_chunk_found)
5413 image->start_loop=MagickTrue;
5414 term_chunk_found=MagickFalse;
5418 image->start_loop=MagickFalse;
5420 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5422 image->delay=frame_delay;
5423 frame_delay=default_frame_delay;
5429 image->page.width=mng_info->mng_width;
5430 image->page.height=mng_info->mng_height;
5431 image->page.x=mng_info->x_off[object_id];
5432 image->page.y=mng_info->y_off[object_id];
5433 image->iterations=mng_iterations;
5436 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5439 if (logging != MagickFalse)
5440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5441 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5444 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5447 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5451 mng_info->image=image;
5452 mng_info->mng_type=mng_type;
5453 mng_info->object_id=object_id;
5455 if (memcmp(type,mng_IHDR,4) == 0)
5456 image=ReadOnePNGImage(mng_info,image_info,exception);
5458 #if defined(JNG_SUPPORTED)
5460 image=ReadOneJNGImage(mng_info,image_info,exception);
5463 if (image == (Image *) NULL)
5465 if (IsImageObject(previous) != MagickFalse)
5467 (void) DestroyImageList(previous);
5468 (void) CloseBlob(previous);
5471 MngInfoFreeStruct(mng_info,&have_mng_structure);
5472 return((Image *) NULL);
5475 if (image->columns == 0 || image->rows == 0)
5477 (void) CloseBlob(image);
5478 image=DestroyImageList(image);
5479 MngInfoFreeStruct(mng_info,&have_mng_structure);
5480 return((Image *) NULL);
5483 mng_info->image=image;
5490 if (mng_info->magn_methx || mng_info->magn_methy)
5496 if (logging != MagickFalse)
5497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5498 " Processing MNG MAGN chunk");
5500 if (mng_info->magn_methx == 1)
5502 magnified_width=mng_info->magn_ml;
5504 if (image->columns > 1)
5505 magnified_width += mng_info->magn_mr;
5507 if (image->columns > 2)
5508 magnified_width += (png_uint_32)
5509 ((image->columns-2)*(mng_info->magn_mx));
5514 magnified_width=(png_uint_32) image->columns;
5516 if (image->columns > 1)
5517 magnified_width += mng_info->magn_ml-1;
5519 if (image->columns > 2)
5520 magnified_width += mng_info->magn_mr-1;
5522 if (image->columns > 3)
5523 magnified_width += (png_uint_32)
5524 ((image->columns-3)*(mng_info->magn_mx-1));
5527 if (mng_info->magn_methy == 1)
5529 magnified_height=mng_info->magn_mt;
5531 if (image->rows > 1)
5532 magnified_height += mng_info->magn_mb;
5534 if (image->rows > 2)
5535 magnified_height += (png_uint_32)
5536 ((image->rows-2)*(mng_info->magn_my));
5541 magnified_height=(png_uint_32) image->rows;
5543 if (image->rows > 1)
5544 magnified_height += mng_info->magn_mt-1;
5546 if (image->rows > 2)
5547 magnified_height += mng_info->magn_mb-1;
5549 if (image->rows > 3)
5550 magnified_height += (png_uint_32)
5551 ((image->rows-3)*(mng_info->magn_my-1));
5554 if (magnified_height > image->rows ||
5555 magnified_width > image->columns)
5570 register PixelPacket
5582 /* Allocate next image structure. */
5584 if (logging != MagickFalse)
5585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5586 " Allocate magnified image");
5588 AcquireNextImage(image_info,image);
5590 if (GetNextImageInList(image) == (Image *) NULL)
5592 image=DestroyImageList(image);
5593 MngInfoFreeStruct(mng_info,&have_mng_structure);
5594 return((Image *) NULL);
5597 large_image=SyncNextImageInList(image);
5599 large_image->columns=magnified_width;
5600 large_image->rows=magnified_height;
5602 magn_methx=mng_info->magn_methx;
5603 magn_methy=mng_info->magn_methy;
5605 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5606 #define QM unsigned short
5607 if (magn_methx != 1 || magn_methy != 1)
5610 Scale pixels to unsigned shorts to prevent
5611 overflow of intermediate values of interpolations
5613 for (y=0; y < (ssize_t) image->rows; y++)
5615 q=GetAuthenticPixels(image,0,y,image->columns,1,
5618 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5620 q->red=ScaleQuantumToShort(q->red);
5621 q->green=ScaleQuantumToShort(q->green);
5622 q->blue=ScaleQuantumToShort(q->blue);
5623 q->opacity=ScaleQuantumToShort(q->opacity);
5627 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5635 if (image->matte != MagickFalse)
5636 (void) SetImageBackgroundColor(large_image);
5640 large_image->background_color.opacity=OpaqueOpacity;
5641 (void) SetImageBackgroundColor(large_image);
5643 if (magn_methx == 4)
5646 if (magn_methx == 5)
5649 if (magn_methy == 4)
5652 if (magn_methy == 5)
5656 /* magnify the rows into the right side of the large image */
5658 if (logging != MagickFalse)
5659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5660 " Magnify the rows to %.20g",(double) large_image->rows);
5661 m=(ssize_t) mng_info->magn_mt;
5663 length=(size_t) image->columns;
5664 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5665 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5667 if ((prev == (PixelPacket *) NULL) ||
5668 (next == (PixelPacket *) NULL))
5670 image=DestroyImageList(image);
5671 MngInfoFreeStruct(mng_info,&have_mng_structure);
5672 ThrowReaderException(ResourceLimitError,
5673 "MemoryAllocationFailed");
5676 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5677 (void) CopyMagickMemory(next,n,length);
5679 for (y=0; y < (ssize_t) image->rows; y++)
5682 m=(ssize_t) mng_info->magn_mt;
5684 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5685 m=(ssize_t) mng_info->magn_mb;
5687 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5688 m=(ssize_t) mng_info->magn_mb;
5690 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5694 m=(ssize_t) mng_info->magn_my;
5700 if (y < (ssize_t) image->rows-1)
5702 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5704 (void) CopyMagickMemory(next,n,length);
5707 for (i=0; i < m; i++, yy++)
5709 register PixelPacket
5712 assert(yy < (ssize_t) large_image->rows);
5715 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5717 q+=(large_image->columns-image->columns);
5719 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5721 /* To do: get color as function of indexes[x] */
5723 if (image->storage_class == PseudoClass)
5728 if (magn_methy <= 1)
5730 *q=(*pixels); /* replicate previous */
5733 else if (magn_methy == 2 || magn_methy == 4)
5741 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5742 -(*pixels).red)+m))/((ssize_t) (m*2))
5744 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5745 -(*pixels).green)+m))/((ssize_t) (m*2))
5747 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5748 -(*pixels).blue)+m))/((ssize_t) (m*2))
5751 if (image->matte != MagickFalse)
5752 (*q).opacity=(QM) (((ssize_t)
5754 -(*pixels).opacity)+m))
5755 /((ssize_t) (m*2))+(*pixels).opacity);
5758 if (magn_methy == 4)
5760 /* Replicate nearest */
5761 if (i <= ((m+1) << 1))
5762 (*q).opacity=(*pixels).opacity+0;
5764 (*q).opacity=(*n).opacity+0;
5768 else /* if (magn_methy == 3 || magn_methy == 5) */
5770 /* Replicate nearest */
5771 if (i <= ((m+1) << 1))
5777 if (magn_methy == 5)
5779 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5780 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5781 +(*pixels).opacity);
5789 if (SyncAuthenticPixels(large_image,exception) == 0)
5795 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5796 next=(PixelPacket *) RelinquishMagickMemory(next);
5798 length=image->columns;
5800 if (logging != MagickFalse)
5801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5802 " Delete original image");
5804 DeleteImageFromList(&image);
5808 mng_info->image=image;
5810 /* magnify the columns */
5811 if (logging != MagickFalse)
5812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5813 " Magnify the columns to %.20g",(double) image->columns);
5815 for (y=0; y < (ssize_t) image->rows; y++)
5817 register PixelPacket
5820 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5821 pixels=q+(image->columns-length);
5824 for (x=(ssize_t) (image->columns-length);
5825 x < (ssize_t) image->columns; x++)
5827 if (x == (ssize_t) (image->columns-length))
5828 m=(ssize_t) mng_info->magn_ml;
5830 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5831 m=(ssize_t) mng_info->magn_mr;
5833 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5834 m=(ssize_t) mng_info->magn_mr;
5836 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5840 m=(ssize_t) mng_info->magn_mx;
5842 for (i=0; i < m; i++)
5844 if (magn_methx <= 1)
5846 /* replicate previous */
5850 else if (magn_methx == 2 || magn_methx == 4)
5858 (*q).red=(QM) ((2*i*((*n).red
5860 /((ssize_t) (m*2))+(*pixels).red);
5861 (*q).green=(QM) ((2*i*((*n).green
5863 +m)/((ssize_t) (m*2))+(*pixels).green);
5864 (*q).blue=(QM) ((2*i*((*n).blue
5866 /((ssize_t) (m*2))+(*pixels).blue);
5867 if (image->matte != MagickFalse)
5868 (*q).opacity=(QM) ((2*i*((*n).opacity
5869 -(*pixels).opacity)+m)/((ssize_t) (m*2))
5870 +(*pixels).opacity);
5873 if (magn_methx == 4)
5875 /* Replicate nearest */
5876 if (i <= ((m+1) << 1))
5877 (*q).opacity=(*pixels).opacity+0;
5879 (*q).opacity=(*n).opacity+0;
5883 else /* if (magn_methx == 3 || magn_methx == 5) */
5885 /* Replicate nearest */
5886 if (i <= ((m+1) << 1))
5892 if (magn_methx == 5)
5895 (*q).opacity=(QM) ((2*i*((*n).opacity
5896 -(*pixels).opacity)+m) /((ssize_t) (m*2))
5897 +(*pixels).opacity);
5906 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5909 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5910 if (magn_methx != 1 || magn_methy != 1)
5913 Rescale pixels to Quantum
5915 for (y=0; y < (ssize_t) image->rows; y++)
5917 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5919 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5921 q->red=ScaleShortToQuantum(q->red);
5922 q->green=ScaleShortToQuantum(q->green);
5923 q->blue=ScaleShortToQuantum(q->blue);
5924 q->opacity=ScaleShortToQuantum(q->opacity);
5928 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5933 if (logging != MagickFalse)
5934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5935 " Finished MAGN processing");
5940 Crop_box is with respect to the upper left corner of the MNG.
5942 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5943 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5944 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5945 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5946 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5947 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5948 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5949 if ((crop_box.left != (mng_info->image_box.left
5950 +mng_info->x_off[object_id])) ||
5951 (crop_box.right != (mng_info->image_box.right
5952 +mng_info->x_off[object_id])) ||
5953 (crop_box.top != (mng_info->image_box.top
5954 +mng_info->y_off[object_id])) ||
5955 (crop_box.bottom != (mng_info->image_box.bottom
5956 +mng_info->y_off[object_id])))
5958 if (logging != MagickFalse)
5959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5960 " Crop the PNG image");
5962 if ((crop_box.left < crop_box.right) &&
5963 (crop_box.top < crop_box.bottom))
5972 Crop_info is with respect to the upper left corner of
5975 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5976 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5977 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5978 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5979 image->page.width=image->columns;
5980 image->page.height=image->rows;
5983 im=CropImage(image,&crop_info,exception);
5985 if (im != (Image *) NULL)
5987 image->columns=im->columns;
5988 image->rows=im->rows;
5989 im=DestroyImage(im);
5990 image->page.width=image->columns;
5991 image->page.height=image->rows;
5992 image->page.x=crop_box.left;
5993 image->page.y=crop_box.top;
6000 No pixels in crop area. The MNG spec still requires
6001 a layer, though, so make a single transparent pixel in
6002 the top left corner.
6007 (void) SetImageBackgroundColor(image);
6008 image->page.width=1;
6009 image->page.height=1;
6014 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6015 image=mng_info->image;
6019 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6020 /* PNG does not handle depths greater than 16 so reduce it even
6023 if (image->depth > 16)
6027 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6028 if (LosslessReduceDepthOK(image) != MagickFalse)
6032 GetImageException(image,exception);
6034 if (image_info->number_scenes != 0)
6036 if (mng_info->scenes_found >
6037 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6041 if (logging != MagickFalse)
6042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6043 " Finished reading image datastream.");
6045 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6047 (void) CloseBlob(image);
6049 if (logging != MagickFalse)
6050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6051 " Finished reading all image datastreams.");
6053 #if defined(MNG_INSERT_LAYERS)
6054 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6055 (mng_info->mng_height))
6058 Insert a background layer if nothing else was found.
6060 if (logging != MagickFalse)
6061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6062 " No images found. Inserting a background layer.");
6064 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6067 Allocate next image structure.
6069 AcquireNextImage(image_info,image);
6070 if (GetNextImageInList(image) == (Image *) NULL)
6072 image=DestroyImageList(image);
6073 MngInfoFreeStruct(mng_info,&have_mng_structure);
6075 if (logging != MagickFalse)
6076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6077 " Allocation failed, returning NULL.");
6079 return((Image *) NULL);
6081 image=SyncNextImageInList(image);
6083 image->columns=mng_info->mng_width;
6084 image->rows=mng_info->mng_height;
6085 image->page.width=mng_info->mng_width;
6086 image->page.height=mng_info->mng_height;
6089 image->background_color=mng_background_color;
6090 image->matte=MagickFalse;
6092 if (image_info->ping == MagickFalse)
6093 (void) SetImageBackgroundColor(image);
6095 mng_info->image_found++;
6098 image->iterations=mng_iterations;
6100 if (mng_iterations == 1)
6101 image->start_loop=MagickTrue;
6103 while (GetPreviousImageInList(image) != (Image *) NULL)
6106 if (image_count > 10*mng_info->image_found)
6108 if (logging != MagickFalse)
6109 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6111 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6112 CoderError,"Linked list is corrupted, beginning of list not found",
6113 "`%s'",image_info->filename);
6115 return((Image *) NULL);
6118 image=GetPreviousImageInList(image);
6120 if (GetNextImageInList(image) == (Image *) NULL)
6122 if (logging != MagickFalse)
6123 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6125 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6126 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6127 image_info->filename);
6131 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6132 GetNextImageInList(image) ==
6135 if (logging != MagickFalse)
6136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6137 " First image null");
6139 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6140 CoderError,"image->next for first image is NULL but shouldn't be.",
6141 "`%s'",image_info->filename);
6144 if (mng_info->image_found == 0)
6146 if (logging != MagickFalse)
6147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6148 " No visible images found.");
6150 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6151 CoderError,"No visible images in file","`%s'",image_info->filename);
6153 if (image != (Image *) NULL)
6154 image=DestroyImageList(image);
6156 MngInfoFreeStruct(mng_info,&have_mng_structure);
6157 return((Image *) NULL);
6160 if (mng_info->ticks_per_second)
6161 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6162 final_delay/mng_info->ticks_per_second;
6165 image->start_loop=MagickTrue;
6167 /* Find final nonzero image delay */
6168 final_image_delay=0;
6170 while (GetNextImageInList(image) != (Image *) NULL)
6173 final_image_delay=image->delay;
6175 image=GetNextImageInList(image);
6178 if (final_delay < final_image_delay)
6179 final_delay=final_image_delay;
6181 image->delay=final_delay;
6183 if (logging != MagickFalse)
6184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6185 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6186 (double) final_delay);
6188 if (logging != MagickFalse)
6194 image=GetFirstImageInList(image);
6196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6197 " Before coalesce:");
6199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6200 " scene 0 delay=%.20g",(double) image->delay);
6202 while (GetNextImageInList(image) != (Image *) NULL)
6204 image=GetNextImageInList(image);
6205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6206 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6210 image=GetFirstImageInList(image);
6211 #ifdef MNG_COALESCE_LAYERS
6221 if (logging != MagickFalse)
6222 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6225 next_image=CoalesceImages(image,&image->exception);
6227 if (next_image == (Image *) NULL)
6228 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6230 image=DestroyImageList(image);
6233 for (next=image; next != (Image *) NULL; next=next_image)
6235 next->page.width=mng_info->mng_width;
6236 next->page.height=mng_info->mng_height;
6239 next->scene=scene++;
6240 next_image=GetNextImageInList(next);
6242 if (next_image == (Image *) NULL)
6245 if (next->delay == 0)
6248 next_image->previous=GetPreviousImageInList(next);
6249 if (GetPreviousImageInList(next) == (Image *) NULL)
6252 next->previous->next=next_image;
6253 next=DestroyImage(next);
6259 while (GetNextImageInList(image) != (Image *) NULL)
6260 image=GetNextImageInList(image);
6262 image->dispose=BackgroundDispose;
6264 if (logging != MagickFalse)
6270 image=GetFirstImageInList(image);
6272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6273 " After coalesce:");
6275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6276 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6277 (double) image->dispose);
6279 while (GetNextImageInList(image) != (Image *) NULL)
6281 image=GetNextImageInList(image);
6283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6284 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6285 (double) image->delay,(double) image->dispose);
6289 image=GetFirstImageInList(image);
6290 MngInfoFreeStruct(mng_info,&have_mng_structure);
6291 have_mng_structure=MagickFalse;
6293 if (logging != MagickFalse)
6294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6296 return(GetFirstImageInList(image));
6298 #else /* PNG_LIBPNG_VER > 10011 */
6299 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6301 printf("Your PNG library is too old: You have libpng-%s\n",
6302 PNG_LIBPNG_VER_STRING);
6304 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6305 "PNG library is too old","`%s'",image_info->filename);
6307 return(Image *) NULL;
6310 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6312 return(ReadPNGImage(image_info,exception));
6314 #endif /* PNG_LIBPNG_VER > 10011 */
6318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6322 % R e g i s t e r P N G I m a g e %
6326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6328 % RegisterPNGImage() adds properties for the PNG image format to
6329 % the list of supported formats. The properties include the image format
6330 % tag, a method to read and/or write the format, whether the format
6331 % supports the saving of more than one frame to the same file or blob,
6332 % whether the format supports native in-memory I/O, and a brief
6333 % description of the format.
6335 % The format of the RegisterPNGImage method is:
6337 % size_t RegisterPNGImage(void)
6340 ModuleExport size_t RegisterPNGImage(void)
6343 version[MaxTextExtent];
6351 "See http://www.libpng.org/ for details about the PNG format."
6356 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6362 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6368 #if defined(PNG_LIBPNG_VER_STRING)
6369 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6370 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6372 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6374 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6375 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6380 entry=SetMagickInfo("MNG");
6381 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6383 #if defined(MAGICKCORE_PNG_DELEGATE)
6384 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6385 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6388 entry->magick=(IsImageFormatHandler *) IsMNG;
6389 entry->description=ConstantString("Multiple-image Network Graphics");
6391 if (*version != '\0')
6392 entry->version=ConstantString(version);
6394 entry->module=ConstantString("PNG");
6395 entry->note=ConstantString(MNGNote);
6396 (void) RegisterMagickInfo(entry);
6398 entry=SetMagickInfo("PNG");
6400 #if defined(MAGICKCORE_PNG_DELEGATE)
6401 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6402 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6405 entry->magick=(IsImageFormatHandler *) IsPNG;
6406 entry->adjoin=MagickFalse;
6407 entry->description=ConstantString("Portable Network Graphics");
6408 entry->module=ConstantString("PNG");
6410 if (*version != '\0')
6411 entry->version=ConstantString(version);
6413 entry->note=ConstantString(PNGNote);
6414 (void) RegisterMagickInfo(entry);
6416 entry=SetMagickInfo("PNG8");
6418 #if defined(MAGICKCORE_PNG_DELEGATE)
6419 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6420 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6423 entry->magick=(IsImageFormatHandler *) IsPNG;
6424 entry->adjoin=MagickFalse;
6425 entry->description=ConstantString(
6426 "8-bit indexed with optional binary transparency");
6427 entry->module=ConstantString("PNG");
6428 (void) RegisterMagickInfo(entry);
6430 entry=SetMagickInfo("PNG24");
6433 #if defined(ZLIB_VERSION)
6434 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6435 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6437 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6439 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6440 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6444 if (*version != '\0')
6445 entry->version=ConstantString(version);
6447 #if defined(MAGICKCORE_PNG_DELEGATE)
6448 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6449 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6452 entry->magick=(IsImageFormatHandler *) IsPNG;
6453 entry->adjoin=MagickFalse;
6454 entry->description=ConstantString("opaque 24-bit RGB");
6455 entry->module=ConstantString("PNG");
6456 (void) RegisterMagickInfo(entry);
6458 entry=SetMagickInfo("PNG32");
6460 #if defined(MAGICKCORE_PNG_DELEGATE)
6461 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6462 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6465 entry->magick=(IsImageFormatHandler *) IsPNG;
6466 entry->adjoin=MagickFalse;
6467 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6468 entry->module=ConstantString("PNG");
6469 (void) RegisterMagickInfo(entry);
6471 entry=SetMagickInfo("JNG");
6473 #if defined(JNG_SUPPORTED)
6474 #if defined(MAGICKCORE_PNG_DELEGATE)
6475 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6476 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6480 entry->magick=(IsImageFormatHandler *) IsJNG;
6481 entry->adjoin=MagickFalse;
6482 entry->description=ConstantString("JPEG Network Graphics");
6483 entry->module=ConstantString("PNG");
6484 entry->note=ConstantString(JNGNote);
6485 (void) RegisterMagickInfo(entry);
6487 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6488 ping_semaphore=AllocateSemaphoreInfo();
6491 return(MagickImageCoderSignature);
6495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6499 % U n r e g i s t e r P N G I m a g e %
6503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6505 % UnregisterPNGImage() removes format registrations made by the
6506 % PNG module from the list of supported formats.
6508 % The format of the UnregisterPNGImage method is:
6510 % UnregisterPNGImage(void)
6513 ModuleExport void UnregisterPNGImage(void)
6515 (void) UnregisterMagickInfo("MNG");
6516 (void) UnregisterMagickInfo("PNG");
6517 (void) UnregisterMagickInfo("PNG8");
6518 (void) UnregisterMagickInfo("PNG24");
6519 (void) UnregisterMagickInfo("PNG32");
6520 (void) UnregisterMagickInfo("JNG");
6522 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6523 if (ping_semaphore != (SemaphoreInfo *) NULL)
6524 DestroySemaphoreInfo(&ping_semaphore);
6528 #if defined(MAGICKCORE_PNG_DELEGATE)
6529 #if PNG_LIBPNG_VER > 10011
6531 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6535 % W r i t e M N G I m a g e %
6539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6541 % WriteMNGImage() writes an image in the Portable Network Graphics
6542 % Group's "Multiple-image Network Graphics" encoded image format.
6544 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6546 % The format of the WriteMNGImage method is:
6548 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6550 % A description of each parameter follows.
6552 % o image_info: the image info.
6554 % o image: The image.
6557 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6558 % "To do" under ReadPNGImage):
6560 % Preserve all unknown and not-yet-handled known chunks found in input
6561 % PNG file and copy them into output PNG files according to the PNG
6564 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6566 % Improve selection of color type (use indexed-colour or indexed-colour
6567 % with tRNS when 256 or fewer unique RGBA values are present).
6569 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6570 % This will be complicated if we limit ourselves to generating MNG-LC
6571 % files. For now we ignore disposal method 3 and simply overlay the next
6574 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6575 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6576 % [mostly done 15 June 1999 but still need to take care of tRNS]
6578 % Check for identical sRGB and replace with a global sRGB (and remove
6579 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6580 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6581 % local gAMA/cHRM with local sRGB if appropriate).
6583 % Check for identical sBIT chunks and write global ones.
6585 % Provide option to skip writing the signature tEXt chunks.
6587 % Use signatures to detect identical objects and reuse the first
6588 % instance of such objects instead of writing duplicate objects.
6590 % Use a smaller-than-32k value of compression window size when
6593 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6594 % ancillary text chunks and save profiles.
6596 % Provide an option to force LC files (to ensure exact framing rate)
6599 % Provide an option to force VLC files instead of LC, even when offsets
6600 % are present. This will involve expanding the embedded images with a
6601 % transparent region at the top and/or left.
6605 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6606 png_info *ping_info, unsigned char *profile_type, unsigned char
6607 *profile_description, unsigned char *profile_data, png_uint_32 length)
6626 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6628 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6631 if (image_info->verbose)
6633 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6634 (char *) profile_type, (double) length);
6637 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6638 description_length=(png_uint_32) strlen((const char *) profile_description);
6639 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6640 + description_length);
6641 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6642 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6643 text[0].key[0]='\0';
6644 (void) ConcatenateMagickString(text[0].key,
6645 "Raw profile type ",MaxTextExtent);
6646 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6650 (void) CopyMagickString(dp,(const char *) profile_description,
6652 dp+=description_length;
6654 (void) FormatMagickString(dp,allocated_length-
6655 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6658 for (i=0; i < (ssize_t) length; i++)
6662 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6663 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6668 text[0].text_length=(png_size_t) (dp-text[0].text);
6669 text[0].compression=image_info->compression == NoCompression ||
6670 (image_info->compression == UndefinedCompression &&
6671 text[0].text_length < 128) ? -1 : 0;
6673 if (text[0].text_length <= allocated_length)
6674 png_set_text(ping,ping_info,text,1);
6676 png_free(ping,text[0].text);
6677 png_free(ping,text[0].key);
6678 png_free(ping,text);
6681 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6682 const char *string, MagickBooleanType logging)
6695 ResetImageProfileIterator(image);
6697 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6699 profile=GetImageProfile(image,name);
6701 if (profile != (const StringInfo *) NULL)
6706 if (LocaleNCompare(name,string,11) == 0)
6708 if (logging != MagickFalse)
6709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6710 " Found %s profile",name);
6712 ping_profile=CloneStringInfo(profile);
6713 data=GetStringInfoDatum(ping_profile),
6714 length=(png_uint_32) GetStringInfoLength(ping_profile);
6719 (void) WriteBlobMSBULong(image,length-5); /* data length */
6720 (void) WriteBlob(image,length-1,data+1);
6721 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6722 ping_profile=DestroyStringInfo(ping_profile);
6726 name=GetNextImageProfile(image);
6733 /* Write one PNG image */
6734 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6735 const ImageInfo *IMimage_info,Image *IMimage)
6759 ping_trans_alpha[256];
6787 ping_have_cheap_transparency,
6797 /* ping_exclude_EXIF, */
6800 /* ping_exclude_iTXt, */
6805 /* ping_exclude_tRNS, */
6807 ping_exclude_zCCP, /* hex-encoded iCCP */
6810 ping_need_colortype_warning,
6828 ping_interlace_method,
6829 ping_compression_method,
6846 number_semitransparent,
6848 ping_pHYs_unit_type;
6851 ping_pHYs_x_resolution,
6852 ping_pHYs_y_resolution;
6854 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6855 " Enter WriteOnePNGImage()");
6857 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
6858 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
6860 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6861 LockSemaphoreInfo(ping_semaphore);
6864 /* Initialize some stuff */
6867 ping_interlace_method=0,
6868 ping_compression_method=0,
6869 ping_filter_method=0,
6872 ping_background.red = 0;
6873 ping_background.green = 0;
6874 ping_background.blue = 0;
6875 ping_background.gray = 0;
6876 ping_background.index = 0;
6878 ping_trans_color.red=0;
6879 ping_trans_color.green=0;
6880 ping_trans_color.blue=0;
6881 ping_trans_color.gray=0;
6883 ping_pHYs_unit_type = 0;
6884 ping_pHYs_x_resolution = 0;
6885 ping_pHYs_y_resolution = 0;
6887 ping_have_blob=MagickFalse;
6888 ping_have_color=MagickTrue;
6889 ping_have_non_bw=MagickTrue;
6890 ping_have_PLTE=MagickFalse;
6891 ping_have_bKGD=MagickFalse;
6892 ping_have_pHYs=MagickFalse;
6893 ping_have_tRNS=MagickFalse;
6895 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
6896 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6897 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
6898 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
6899 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6900 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
6901 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
6902 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
6903 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
6904 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
6905 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
6906 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
6907 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
6908 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
6909 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
6911 ping_need_colortype_warning = MagickFalse;
6914 number_semitransparent = 0;
6915 number_transparent = 0;
6917 if (logging != MagickFalse)
6919 if (image->storage_class == UndefinedClass)
6920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6921 " storage_class=UndefinedClass");
6922 if (image->storage_class == DirectClass)
6923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6924 " storage_class=DirectClass");
6925 if (image->storage_class == PseudoClass)
6926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6927 " storage_class=PseudoClass");
6930 if (image->colorspace != RGBColorspace)
6931 (void) TransformImageColorspace(image,RGBColorspace);
6934 Sometimes we get PseudoClass images whose RGB values don't match
6935 the colors in the colormap. This code syncs the RGB values.
6937 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
6938 (void) SyncImage(image);
6940 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
6941 if (image->depth > 8)
6943 if (logging != MagickFalse)
6944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6945 " Reducing PNG bit depth to 8 since this is a Q8 build.");
6952 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6953 /* PNG does not handle depths greater than 16 so reduce it even
6956 if (image->depth > 16)
6960 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6961 if (image->depth == 16 && mng_info->write_png_depth != 16)
6962 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
6966 /* Normally we run this just once, but in the case of writing PNG8
6967 * we reduce the transparency to binary and run again, then reduce
6968 * the colors to a simple 3-3-2 palette and run once more.
6974 * Sometimes we get DirectClass images that have 256 colors or fewer.
6975 * This code will build a colormap.
6977 * Also, sometimes we get PseudoClass images with an out-of-date
6978 * colormap. This code will replace the colormap with a new one.
6979 * Sometimes we get PseudoClass images that have more than 256 colors.
6980 * This code will delete the colormap and change the image to
6983 * If image->matte is MagickFalse, we ignore the opacity channel
6984 * even though it sometimes contains left-over non-opaque values.
6986 * Also we gather some information (number of opaque, transparent,
6987 * and semitransparent pixels, and whether the image has any non-gray
6988 * pixels or only black-and-white pixels) that we might need later.
6990 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
6991 * we need to check for bogus non-opaque values, at least.
6994 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
6995 # define PNGK 0 /* Shift */
6996 # define PNGM 1 /* Scale */
6997 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
6999 # define PNGM 0x0101
7002 # define PNGM 0x01010101
7013 semitransparent[260],
7016 register IndexPacket
7019 register const PixelPacket
7023 register PixelPacket
7026 if (logging != MagickFalse)
7027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7028 " Enter BUILD_PALETTE:");
7030 if (logging != MagickFalse)
7032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7033 " image->columns=%.20g",(double) image->columns);
7034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7035 " image->rows=%.20g",(double) image->rows);
7036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7037 " image->matte=%.20g",(double) image->matte);
7038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7039 " image->depth=%.20g",(double) image->depth);
7041 if (image->storage_class == PseudoClass && image->colormap != NULL)
7043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7044 " Original colormap:");
7045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7046 " i (red,green,blue,opacity)");
7048 for (i=0; i < 256; i++)
7050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7051 " %d (%d,%d,%d,%d)",
7053 (int) image->colormap[i].red,
7054 (int) image->colormap[i].green,
7055 (int) image->colormap[i].blue,
7056 (int) image->colormap[i].opacity);
7059 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7064 " %d (%d,%d,%d,%d)",
7066 (int) image->colormap[i].red,
7067 (int) image->colormap[i].green,
7068 (int) image->colormap[i].blue,
7069 (int) image->colormap[i].opacity);
7074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7075 " image->colors=%d",(int) image->colors);
7077 if (image->colors == 0)
7078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7079 " (zero means unknown)");
7081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7082 " Regenerate the colormap");
7085 exception=(&image->exception);
7089 number_semitransparent = 0;
7090 number_transparent = 0;
7092 for (y=0; y < (ssize_t) image->rows; y++)
7094 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7096 if (q == (PixelPacket *) NULL)
7099 for (x=0; x < (ssize_t) image->columns; x++)
7101 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7103 if (number_opaque < 259)
7105 if (number_opaque == 0)
7108 opaque[0].opacity=OpaqueOpacity;
7112 for (i=0; i< (ssize_t) number_opaque; i++)
7114 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7118 if (i == (ssize_t) number_opaque &&
7119 number_opaque < 259)
7123 opaque[i].opacity = OpaqueOpacity;
7127 else if (q->opacity == TransparentOpacity)
7129 if (number_transparent < 259)
7131 if (number_transparent == 0)
7134 ping_trans_color.red=(unsigned short)(q->red);
7135 ping_trans_color.green=(unsigned short) (q->green);
7136 ping_trans_color.blue=(unsigned short) (q->blue);
7137 ping_trans_color.gray=(unsigned short) (q->blue);
7138 number_transparent = 1;
7141 for (i=0; i< (ssize_t) number_transparent; i++)
7143 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7147 if (i == (ssize_t) number_transparent &&
7148 number_transparent < 259)
7150 number_transparent++;
7151 transparent[i] = *q;
7157 if (number_semitransparent < 259)
7159 if (number_semitransparent == 0)
7161 semitransparent[0]=*q;
7162 number_semitransparent = 1;
7165 for (i=0; i< (ssize_t) number_semitransparent; i++)
7167 if (IsColorEqual(semitransparent+i,
7168 (PixelPacket *) q) &&
7169 q->opacity == semitransparent[i].opacity)
7173 if (i == (ssize_t) number_semitransparent &&
7174 number_semitransparent < 259)
7176 number_semitransparent++;
7177 semitransparent[i] = *q;
7185 if (ping_exclude_bKGD == MagickFalse)
7187 /* Add the background color to the palette, if it
7188 * isn't already there.
7190 for (i=0; i<number_opaque; i++)
7192 if (IsColorEqual(opaque+i, &image->background_color))
7196 if (number_opaque < 259 && i == number_opaque)
7198 opaque[i]=image->background_color;
7199 opaque[i].opacity = OpaqueOpacity;
7202 else if (logging != MagickFalse)
7203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7204 " No room in the colormap to add background color");
7207 image_colors=number_opaque+number_transparent+number_semitransparent;
7209 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7211 /* No room for the background color; remove it. */
7216 if (logging != MagickFalse)
7218 if (image_colors > 256)
7219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7220 " image has more than 256 colors");
7223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7224 " image has %d colors",image_colors);
7227 if (mng_info->write_png_colortype != 7) /* We won't need this info */
7229 ping_have_color=MagickFalse;
7230 ping_have_non_bw=MagickFalse;
7232 if(image_colors > 256)
7234 for (y=0; y < (ssize_t) image->rows; y++)
7236 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7238 if (q == (PixelPacket *) NULL)
7241 /* Worst case is black-and-white; we are looking at every
7245 if (ping_have_color == MagickFalse)
7248 for (x=0; x < (ssize_t) image->columns; x++)
7250 if (s->red != s->green || s->red != s->blue)
7252 ping_have_color=MagickTrue;
7253 ping_have_non_bw=MagickTrue;
7260 if (ping_have_non_bw == MagickFalse)
7263 for (x=0; x < (ssize_t) image->columns; x++)
7265 if (s->red != 0 && s->red != QuantumRange)
7267 ping_have_non_bw=MagickTrue;
7276 if (image_colors < 257)
7282 * Initialize image colormap.
7285 if (logging != MagickFalse)
7286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7287 " Sort the new colormap");
7289 /* Sort palette, transparent first */;
7293 for (i=0; i<number_transparent; i++)
7294 colormap[n++] = transparent[i];
7296 for (i=0; i<number_semitransparent; i++)
7297 colormap[n++] = semitransparent[i];
7299 for (i=0; i<number_opaque; i++)
7300 colormap[n++] = opaque[i];
7303 /* image_colors < 257; search the colormap instead of the pixels
7304 * to get ping_have_color and ping_have_non_bw
7308 if (ping_have_color == MagickFalse)
7310 if (colormap[i].red != colormap[i].green ||
7311 colormap[i].red != colormap[i].blue)
7313 ping_have_color=MagickTrue;
7314 ping_have_non_bw=MagickTrue;
7319 if (ping_have_non_bw == MagickFalse)
7321 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7322 ping_have_non_bw=MagickTrue;
7326 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7327 (number_transparent == 0 && number_semitransparent == 0)) &&
7328 (((mng_info->write_png_colortype-1) ==
7329 PNG_COLOR_TYPE_PALETTE) ||
7330 (mng_info->write_png_colortype == 0)))
7332 if (logging != MagickFalse)
7334 if (n != (ssize_t) image_colors)
7335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7336 " image_colors (%d) and n (%d) don't match",
7339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7340 " AcquireImageColormap");
7343 image->colors = image_colors;
7345 if (AcquireImageColormap(image,image_colors) ==
7347 ThrowWriterException(ResourceLimitError,
7348 "MemoryAllocationFailed");
7350 for (i=0; i< (ssize_t) image_colors; i++)
7351 image->colormap[i] = colormap[i];
7353 if (logging != MagickFalse)
7355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7356 " image->colors=%d (%d)",
7357 (int) image->colors, image_colors);
7359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7360 " Update the pixel indexes");
7363 /* Sync the pixel indices with the new colormap */
7365 for (y=0; y < (ssize_t) image->rows; y++)
7367 q=GetAuthenticPixels(image,0,y,image->columns,1,
7370 if (q == (PixelPacket *) NULL)
7373 indexes=GetAuthenticIndexQueue(image);
7375 for (x=0; x < (ssize_t) image->columns; x++)
7377 for (i=0; i< (ssize_t) image_colors; i++)
7379 if ((image->matte == MagickFalse ||
7380 image->colormap[i].opacity == q->opacity) &&
7381 (IsColorEqual(&image->colormap[i],
7382 (PixelPacket *) q)))
7384 indexes[x]=(IndexPacket) i;
7391 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7397 if (logging != MagickFalse)
7399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7400 " image->colors=%d", (int) image->colors);
7402 if (image->colormap != NULL)
7404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7405 " i (red,green,blue,opacity)");
7407 for (i=0; i < (ssize_t) image->colors; i++)
7409 if (i < 300 || i >= image->colors - 10)
7411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7412 " %d (%d,%d,%d,%d)",
7414 (int) image->colormap[i].red,
7415 (int) image->colormap[i].green,
7416 (int) image->colormap[i].blue,
7417 (int) image->colormap[i].opacity);
7422 if (number_transparent < 257)
7423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7424 " number_transparent = %d",
7425 number_transparent);
7428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7429 " number_transparent > 256");
7431 if (number_opaque < 257)
7432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7433 " number_opaque = %d",
7437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7438 " number_opaque > 256");
7440 if (number_semitransparent < 257)
7441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7442 " number_semitransparent = %d",
7443 number_semitransparent);
7446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7447 " number_semitransparent > 256");
7449 if (ping_have_non_bw == MagickFalse)
7450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7451 " All pixels and the background are black or white");
7453 else if (ping_have_color == MagickFalse)
7454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7455 " All pixels and the background are gray");
7458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7459 " At least one pixel or the background is non-gray");
7461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7462 " Exit BUILD_PALETTE:");
7465 if (mng_info->write_png8 == MagickFalse)
7468 /* Make any reductions necessary for the PNG8 format */
7469 if (image_colors <= 256 &&
7470 image_colors != 0 && image->colormap != NULL &&
7471 number_semitransparent == 0 &&
7472 number_transparent <= 1)
7475 /* PNG8 can't have semitransparent colors so we threshold the
7476 * opacity to 0 or OpaqueOpacity
7478 if (number_semitransparent != 0)
7480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7481 " Thresholding the alpha channel to binary");
7483 for (y=0; y < (ssize_t) image->rows; y++)
7485 r=GetAuthenticPixels(image,0,y,image->columns,1,
7488 if (r == (PixelPacket *) NULL)
7491 for (x=0; x < (ssize_t) image->columns; x++)
7493 r->opacity = r->opacity > OpaqueOpacity/2 ? 0 :
7498 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7501 if (image_colors != 0 && image_colors <= 256 &&
7502 image->colormap != NULL)
7503 for (i=0; i<image_colors; i++)
7504 image->colormap[i].opacity =
7505 image->colormap[i].opacity > OpaqueOpacity/2 ? 0 :
7511 /* PNG8 can't have more than 256 colors so we quantize the pixels and
7512 * background color to the 3-3-2 palette.
7515 if (image_colors == 0 || image_colors > 256)
7517 if (logging != MagickFalse)
7518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7519 " Quantizing the background color to 3-3-2");
7521 image->background_color.red=
7523 image->background_color.red) >> PNGK) & 0xe0) ) |
7525 image->background_color.red) >> PNGK) & 0xe0) >> 3) |
7527 image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7528 image->background_color.green=
7530 image->background_color.green) >> PNGK) & 0xe0) ) |
7532 image->background_color.green) >> PNGK) & 0xe0) >> 3) |
7534 image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7535 image->background_color.blue=
7537 image->background_color.blue) >> PNGK) & 0xc0) ) |
7539 image->background_color.blue) >> PNGK) & 0xc0) >> 2) |
7541 image->background_color.blue) >> PNGK) & 0xc0) >> 4) |
7543 image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7545 if (logging != MagickFalse)
7546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7547 " Quantizing the pixel colors to 3-3-2");
7549 if (image->colormap == NULL)
7551 for (y=0; y < (ssize_t) image->rows; y++)
7553 r=GetAuthenticPixels(image,0,y,image->columns,1,
7556 if (r == (PixelPacket *) NULL)
7559 for (x=0; x < (ssize_t) image->columns; x++)
7562 ((((((size_t) r->red) >> PNGK) & 0xe0) ) |
7563 (((((size_t) r->red) >> PNGK) & 0xe0) >> 3) |
7564 (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7566 ((((((size_t) r->green) >> PNGK) & 0xe0) ) |
7567 (((((size_t) r->green) >> PNGK) & 0xe0) >> 3) |
7568 (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7570 ((((((size_t) r->blue) >> PNGK) & 0xc0) ) |
7571 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2) |
7572 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4) |
7573 (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7577 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7582 else /* Should not reach this; colormap already exists and
7585 if (logging != MagickFalse)
7586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7587 " Quantizing the colormap to 3-3-2");
7588 for (i=0; i<image_colors; i++)
7590 image->colormap[i].red=
7592 image->colormap[i].red) >> PNGK) & 0xe0) ) |
7594 image->colormap[i].red) >> PNGK) & 0xe0) >> 3) |
7596 image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7597 image->colormap[i].green=
7599 image->colormap[i].green) >> PNGK) & 0xe0) ) |
7601 image->colormap[i].green) >> PNGK) & 0xe0) >> 3) |
7603 image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7604 image->colormap[i].blue=
7606 image->colormap[i].blue) >> PNGK) & 0xc0) ) |
7608 image->colormap[i].blue) >> PNGK) & 0xc0) >> 2) |
7610 image->colormap[i].blue) >> PNGK) & 0xc0) >> 4) |
7612 image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7619 /* END OF BUILD_PALETTE */
7621 /* If we are excluding the tRNS chunk and there is transparency,
7622 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7625 if (mng_info->ping_exclude_tRNS != MagickFalse &&
7626 (number_transparent != 0 || number_semitransparent != 0))
7628 int colortype=mng_info->write_png_colortype;
7630 if (ping_have_color == MagickFalse)
7631 mng_info->write_png_colortype = 5;
7634 mng_info->write_png_colortype = 7;
7636 if (colortype != 0 &&
7637 mng_info->write_png_colortype != (ssize_t) colortype)
7638 ping_need_colortype_warning=MagickTrue;
7642 /* See if cheap transparency is possible. It is only possible
7643 * when there is a single transparent color, no semitransparent
7644 * color, and no opaque color that has the same RGB components
7645 * as the transparent color. We only need this information if
7646 * we are writing a PNG with colortype 0 or 2, and we have not
7647 * excluded the tRNS chunk.
7649 if (number_transparent == 1 &&
7650 mng_info->write_png_colortype < 4)
7652 ping_have_cheap_transparency = MagickTrue;
7654 if (number_semitransparent != 0)
7655 ping_have_cheap_transparency = MagickFalse;
7657 else if (image_colors == 0 || image_colors > 256 ||
7658 image->colormap == NULL)
7663 register const PixelPacket
7666 exception=(&image->exception);
7668 for (y=0; y < (ssize_t) image->rows; y++)
7670 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
7672 if (q == (PixelPacket *) NULL)
7675 for (x=0; x < (ssize_t) image->columns; x++)
7677 if (q->opacity != TransparentOpacity &&
7678 (unsigned short) q->red == ping_trans_color.red &&
7679 (unsigned short) q->green == ping_trans_color.green &&
7680 (unsigned short) q->blue == ping_trans_color.blue)
7682 ping_have_cheap_transparency = MagickFalse;
7689 if (ping_have_cheap_transparency == MagickFalse)
7695 /* Assuming that image->colormap[0] is the one transparent color
7696 * and that all others are opaque.
7698 if (image_colors > 1)
7699 for (i=1; i<image_colors; i++)
7700 if (image->colormap[i].red == image->colormap[0].red &&
7701 image->colormap[i].green == image->colormap[0].green &&
7702 image->colormap[i].blue == image->colormap[0].blue)
7704 ping_have_cheap_transparency = MagickFalse;
7709 if (logging != MagickFalse)
7711 if (ping_have_cheap_transparency == MagickFalse)
7712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7713 " Cheap transparency is not possible.");
7716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7717 " Cheap transparency is possible.");
7721 ping_have_cheap_transparency = MagickFalse;
7723 image_depth=image->depth;
7725 quantum_info = (QuantumInfo *) NULL;
7727 image_colors=(int) image->colors;
7728 image_matte=image->matte;
7730 mng_info->IsPalette=image->storage_class == PseudoClass &&
7731 image_colors <= 256 && image->colormap != NULL;
7733 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
7734 (image->colors == 0 || image->colormap == NULL))
7736 image_info=DestroyImageInfo(image_info);
7737 image=DestroyImage(image);
7738 (void) ThrowMagickException(&IMimage->exception,
7739 GetMagickModule(),CoderError,
7740 "Cannot write PNG8 or color-type 3; colormap is NULL",
7741 "`%s'",IMimage->filename);
7742 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7743 UnlockSemaphoreInfo(ping_semaphore);
7745 return(MagickFalse);
7749 Allocate the PNG structures
7751 #ifdef PNG_USER_MEM_SUPPORTED
7752 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
7753 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
7754 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
7757 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
7758 MagickPNGErrorHandler,MagickPNGWarningHandler);
7761 if (ping == (png_struct *) NULL)
7762 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7764 ping_info=png_create_info_struct(ping);
7766 if (ping_info == (png_info *) NULL)
7768 png_destroy_write_struct(&ping,(png_info **) NULL);
7769 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7772 png_set_write_fn(ping,image,png_put_data,png_flush_data);
7773 ping_pixels=(unsigned char *) NULL;
7775 if (setjmp(png_jmpbuf(ping)))
7781 if (image_info->verbose)
7782 (void) printf("PNG write has failed.\n");
7784 png_destroy_write_struct(&ping,&ping_info);
7785 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7786 UnlockSemaphoreInfo(ping_semaphore);
7788 if (ping_have_blob != MagickFalse)
7789 (void) CloseBlob(image);
7790 image_info=DestroyImageInfo(image_info);
7791 image=DestroyImage(image);
7792 return(MagickFalse);
7795 Prepare PNG for writing.
7797 #if defined(PNG_MNG_FEATURES_SUPPORTED)
7798 if (mng_info->write_mng)
7799 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
7802 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
7803 if (mng_info->write_mng)
7804 png_permit_empty_plte(ping,MagickTrue);
7811 ping_width=(png_uint_32) image->columns;
7812 ping_height=(png_uint_32) image->rows;
7814 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
7817 if (mng_info->write_png_depth != 0)
7818 image_depth=mng_info->write_png_depth;
7820 /* Adjust requested depth to next higher valid depth if necessary */
7821 if (image_depth > 8)
7824 if ((image_depth > 4) && (image_depth < 8))
7827 if (image_depth == 3)
7830 if (logging != MagickFalse)
7832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7833 " width=%.20g",(double) ping_width);
7834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7835 " height=%.20g",(double) ping_height);
7836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7837 " image_matte=%.20g",(double) image->matte);
7838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7839 " image->depth=%.20g",(double) image->depth);
7840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7841 " Tentative ping_bit_depth=%.20g",(double) image_depth);
7844 save_image_depth=image_depth;
7845 ping_bit_depth=(png_byte) save_image_depth;
7848 #if defined(PNG_pHYs_SUPPORTED)
7849 if (ping_exclude_pHYs == MagickFalse)
7851 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
7852 (!mng_info->write_mng || !mng_info->equal_physs))
7854 if (logging != MagickFalse)
7855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7856 " Setting up pHYs chunk");
7858 if (image->units == PixelsPerInchResolution)
7860 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
7861 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
7862 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
7865 else if (image->units == PixelsPerCentimeterResolution)
7867 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
7868 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution);
7869 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution);
7874 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
7875 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
7876 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
7879 ping_have_pHYs = MagickTrue;
7884 if (ping_exclude_bKGD == MagickFalse)
7886 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
7892 if (ping_bit_depth == 8)
7895 if (ping_bit_depth == 4)
7898 if (ping_bit_depth == 2)
7901 if (ping_bit_depth == 1)
7904 ping_background.red=(png_uint_16)
7905 (ScaleQuantumToShort(image->background_color.red) & mask);
7907 ping_background.green=(png_uint_16)
7908 (ScaleQuantumToShort(image->background_color.green) & mask);
7910 ping_background.blue=(png_uint_16)
7911 (ScaleQuantumToShort(image->background_color.blue) & mask);
7914 if (logging != MagickFalse)
7916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7917 " Setting up bKGD chunk (1)");
7919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7920 " ping_bit_depth=%d",ping_bit_depth);
7923 ping_have_bKGD = MagickTrue;
7927 Select the color type.
7932 if (mng_info->IsPalette && mng_info->write_png8)
7935 /* To do: make this a function cause it's used twice, except
7936 for reducing the sample depth from 8. */
7938 number_colors=image_colors;
7940 ping_have_tRNS=MagickFalse;
7945 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
7947 if (logging != MagickFalse)
7948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7949 " Setting up PLTE chunk with %d colors (%d)",
7950 number_colors, image_colors);
7952 for (i=0; i < (ssize_t) number_colors; i++)
7954 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7955 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7956 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7957 if (logging != MagickFalse)
7958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7959 #if MAGICKCORE_QUANTUM_DEPTH == 8
7960 " %3ld (%3d,%3d,%3d)",
7962 " %5ld (%5d,%5d,%5d)",
7964 (long) i,palette[i].red,palette[i].green,palette[i].blue);
7968 ping_have_PLTE=MagickTrue;
7969 image_depth=ping_bit_depth;
7972 if (matte != MagickFalse)
7975 Identify which colormap entry is transparent.
7977 assert(number_colors <= 256);
7978 assert(image->colormap != NULL);
7980 for (i=0; i < (ssize_t) number_transparent; i++)
7981 ping_trans_alpha[i]=0;
7984 ping_num_trans=(unsigned short) (number_transparent +
7985 number_semitransparent);
7987 if (ping_num_trans == 0)
7988 ping_have_tRNS=MagickFalse;
7991 ping_have_tRNS=MagickTrue;
7994 if (ping_exclude_bKGD == MagickFalse)
7997 * Identify which colormap entry is the background color.
8000 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8001 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8004 ping_background.index=(png_byte) i;
8006 } /* end of write_png8 */
8008 else if (mng_info->write_png24)
8010 image_matte=MagickFalse;
8011 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8014 else if (mng_info->write_png32)
8016 image_matte=MagickTrue;
8017 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8020 else /* mng_info->write_pngNN not specified */
8022 image_depth=ping_bit_depth;
8024 if (mng_info->write_png_colortype != 0)
8026 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8028 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8029 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8030 image_matte=MagickTrue;
8033 image_matte=MagickFalse;
8036 else /* write_ping_colortype not specified */
8038 if (logging != MagickFalse)
8039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8040 " Selecting PNG colortype:");
8042 ping_color_type=(png_byte) ((matte != MagickFalse)?
8043 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8045 if (image_info->type == TrueColorType)
8047 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8048 image_matte=MagickFalse;
8051 if (image_info->type == TrueColorMatteType)
8053 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8054 image_matte=MagickTrue;
8057 if (image_info->type == PaletteType ||
8058 image_info->type == PaletteMatteType)
8059 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8061 if (image_info->type == UndefinedType ||
8062 image_info->type == OptimizeType)
8064 if (ping_have_color == MagickFalse)
8066 if (image_matte == MagickFalse)
8068 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8069 image_matte=MagickFalse;
8074 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8075 image_matte=MagickTrue;
8080 if (image_matte == MagickFalse)
8082 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8083 image_matte=MagickFalse;
8088 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8089 image_matte=MagickTrue;
8096 if (logging != MagickFalse)
8097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8098 " Selected PNG colortype=%d",ping_color_type);
8100 if (ping_bit_depth < 8)
8102 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8103 ping_color_type == PNG_COLOR_TYPE_RGB ||
8104 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8108 old_bit_depth=ping_bit_depth;
8110 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8112 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8116 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8121 if (image->colors == 0)
8124 (void) ThrowMagickException(&image->exception,
8125 GetMagickModule(),CoderError,
8126 "image has 0 colors", "`%s'","");
8129 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8130 ping_bit_depth <<= 1;
8133 if (logging != MagickFalse)
8135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8136 " Number of colors: %.20g",(double) image_colors);
8138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8139 " Tentative PNG bit depth: %d",ping_bit_depth);
8142 if (ping_bit_depth < (int) mng_info->write_png_depth)
8143 ping_bit_depth = mng_info->write_png_depth;
8146 image_depth=ping_bit_depth;
8148 if (logging != MagickFalse)
8150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8151 " Tentative PNG color type: %.20g",(double) ping_color_type);
8153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8154 " image_info->type: %.20g",(double) image_info->type);
8156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8157 " image_depth: %.20g",(double) image_depth);
8159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8161 " image->depth: %.20g",(double) image->depth);
8163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8164 " ping_bit_depth: %.20g",(double) ping_bit_depth);
8167 if (matte != MagickFalse)
8169 if (mng_info->IsPalette)
8171 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8173 if (ping_have_color != MagickFalse)
8174 ping_color_type=PNG_COLOR_TYPE_RGBA;
8177 * Determine if there is any transparent color.
8179 if (number_transparent + number_semitransparent == 0)
8182 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8185 image_matte=MagickFalse;
8186 ping_color_type&=0x03;
8196 if (ping_bit_depth == 8)
8199 if (ping_bit_depth == 4)
8202 if (ping_bit_depth == 2)
8205 if (ping_bit_depth == 1)
8208 ping_trans_color.red=(png_uint_16)
8209 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8211 ping_trans_color.green=(png_uint_16)
8212 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8214 ping_trans_color.blue=(png_uint_16)
8215 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8217 ping_trans_color.gray=(png_uint_16)
8218 (ScaleQuantumToShort(PixelIntensityToQuantum(
8219 image->colormap)) & mask);
8221 ping_trans_color.index=(png_byte) 0;
8223 ping_have_tRNS=MagickTrue;
8226 if (ping_have_tRNS != MagickFalse)
8229 * Determine if there is one and only one transparent color
8230 * and if so if it is fully transparent.
8232 if (ping_have_cheap_transparency == MagickFalse)
8233 ping_have_tRNS=MagickFalse;
8236 if (ping_have_tRNS != MagickFalse)
8238 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
8240 if (image_depth == 8)
8242 ping_trans_color.red&=0xff;
8243 ping_trans_color.green&=0xff;
8244 ping_trans_color.blue&=0xff;
8245 ping_trans_color.gray&=0xff;
8251 if (image_depth == 8)
8253 ping_trans_color.red&=0xff;
8254 ping_trans_color.green&=0xff;
8255 ping_trans_color.blue&=0xff;
8256 ping_trans_color.gray&=0xff;
8263 if (ping_have_tRNS != MagickFalse)
8264 image_matte=MagickFalse;
8266 if ((mng_info->IsPalette) &&
8267 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8268 ping_have_color == MagickFalse &&
8269 (image_matte == MagickFalse || image_depth >= 8))
8273 if (image_matte != MagickFalse)
8274 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8278 ping_color_type=PNG_COLOR_TYPE_GRAY;
8280 if (save_image_depth == 16 && image_depth == 8)
8282 if (logging != MagickFalse)
8284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8285 " Scaling ping_trans_color (0)");
8287 ping_trans_color.gray*=0x0101;
8291 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8292 image_depth=MAGICKCORE_QUANTUM_DEPTH;
8294 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
8295 image_colors=(int) (one << image_depth);
8297 if (image_depth > 8)
8303 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8305 if(!mng_info->write_png_depth)
8309 while ((int) (one << ping_bit_depth)
8310 < (ssize_t) image_colors)
8311 ping_bit_depth <<= 1;
8315 else if (ping_color_type ==
8316 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8317 mng_info->IsPalette)
8319 /* Check if grayscale is reducible */
8321 #define LOW_DEPTH_OK 0 /* To do: eliminate this when low bit-depths work */
8324 depth_4_ok=MagickTrue,
8325 depth_2_ok=MagickTrue,
8327 depth_1_ok=MagickTrue;
8329 for (i=0; i < (ssize_t) image_colors; i++)
8334 intensity=ScaleQuantumToChar(image->colormap[i].red);
8337 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8338 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8339 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8340 depth_2_ok=depth_1_ok=MagickFalse;
8343 if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8344 depth_1_ok=MagickFalse;
8347 if (depth_1_ok && mng_info->write_png_depth <= 1)
8351 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8354 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8360 image_depth=ping_bit_depth;
8365 if (mng_info->IsPalette)
8367 number_colors=image_colors;
8369 if (image_depth <= 8)
8374 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8376 if (mng_info->have_write_global_plte && matte == MagickFalse)
8378 png_set_PLTE(ping,ping_info,NULL,0);
8380 if (logging != MagickFalse)
8381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8382 " Setting up empty PLTE chunk");
8387 for (i=0; i < (ssize_t) number_colors; i++)
8389 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8390 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8391 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8394 if (logging != MagickFalse)
8395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8396 " Setting up PLTE chunk with %d colors",
8399 ping_have_PLTE=MagickTrue;
8402 /* color_type is PNG_COLOR_TYPE_PALETTE */
8403 if (mng_info->write_png_depth == 0)
8411 while ((one << ping_bit_depth) < number_colors)
8412 ping_bit_depth <<= 1;
8417 if (matte != MagickFalse)
8420 * Set up trans_colors array.
8422 assert(number_colors <= 256);
8424 ping_num_trans=(unsigned short) (number_transparent +
8425 number_semitransparent);
8427 if (ping_num_trans == 0)
8428 ping_have_tRNS=MagickFalse;
8432 if (logging != MagickFalse)
8434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435 " Scaling ping_trans_color (1)");
8437 ping_have_tRNS=MagickTrue;
8439 for (i=0; i < ping_num_trans; i++)
8441 ping_trans_alpha[i]= (png_byte) (255-
8442 ScaleQuantumToChar(image->colormap[i].opacity));
8452 if (image_depth < 8)
8455 if ((save_image_depth == 16) && (image_depth == 8))
8457 if (logging != MagickFalse)
8459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8460 " Scaling ping_trans_color from (%d,%d,%d)",
8461 (int) ping_trans_color.red,
8462 (int) ping_trans_color.green,
8463 (int) ping_trans_color.blue);
8466 ping_trans_color.red*=0x0101;
8467 ping_trans_color.green*=0x0101;
8468 ping_trans_color.blue*=0x0101;
8469 ping_trans_color.gray*=0x0101;
8471 if (logging != MagickFalse)
8473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8475 (int) ping_trans_color.red,
8476 (int) ping_trans_color.green,
8477 (int) ping_trans_color.blue);
8482 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8483 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
8486 Adjust background and transparency samples in sub-8-bit grayscale files.
8488 if (ping_bit_depth < 8 && ping_color_type ==
8489 PNG_COLOR_TYPE_GRAY)
8497 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8499 if (ping_exclude_bKGD == MagickFalse)
8502 ping_background.gray=(png_uint_16)
8503 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8505 if (logging != MagickFalse)
8506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8507 " Setting up bKGD chunk (2)");
8509 ping_have_bKGD = MagickTrue;
8512 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8513 ping_trans_color.gray));
8516 if (ping_exclude_bKGD == MagickFalse)
8518 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8521 Identify which colormap entry is the background color.
8524 number_colors=image_colors;
8526 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8527 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8530 ping_background.index=(png_byte) i;
8532 if (logging != MagickFalse)
8534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8535 " Setting up bKGD chunk with index=%d",(int) i);
8538 if (i < (ssize_t) number_colors)
8540 ping_have_bKGD = MagickTrue;
8542 if (logging != MagickFalse)
8544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8545 " background =(%d,%d,%d)",
8546 (int) ping_background.red,
8547 (int) ping_background.green,
8548 (int) ping_background.blue);
8552 else /* Can't happen */
8554 if (logging != MagickFalse)
8555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8556 " No room in PLTE to add bKGD color");
8557 ping_have_bKGD = MagickFalse;
8562 if (logging != MagickFalse)
8563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8564 " PNG color type: %d",ping_color_type);
8566 Initialize compression level and filtering.
8568 if (logging != MagickFalse)
8570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8571 " Setting up deflate compression");
8573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8574 " Compression buffer size: 32768");
8577 png_set_compression_buffer_size(ping,32768L);
8579 if (logging != MagickFalse)
8580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8581 " Compression mem level: 9");
8583 png_set_compression_mem_level(ping, 9);
8585 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8593 level=(int) MagickMin((ssize_t) quality/10,9);
8595 if (logging != MagickFalse)
8596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8597 " Compression level: %d",level);
8599 png_set_compression_level(ping,level);
8604 if (logging != MagickFalse)
8605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8606 " Compression strategy: Z_HUFFMAN_ONLY");
8608 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8611 if (logging != MagickFalse)
8612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8613 " Setting up filtering");
8615 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8616 /* This became available in libpng-1.0.9. Output must be a MNG. */
8617 if (mng_info->write_mng && ((quality % 10) == 7))
8619 if (logging != MagickFalse)
8620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8621 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8623 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8627 if (logging != MagickFalse)
8628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8636 if ((quality % 10) > 5)
8637 base_filter=PNG_ALL_FILTERS;
8640 if ((quality % 10) != 5)
8641 base_filter=(int) quality % 10;
8644 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8645 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8647 base_filter=PNG_NO_FILTERS;
8650 base_filter=PNG_ALL_FILTERS;
8652 if (logging != MagickFalse)
8654 if (base_filter == PNG_ALL_FILTERS)
8655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8656 " Base filter method: ADAPTIVE");
8658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8659 " Base filter method: NONE");
8662 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8665 if (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse)
8667 ResetImageProfileIterator(image);
8668 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8670 profile=GetImageProfile(image,name);
8672 if (profile != (StringInfo *) NULL)
8674 #ifdef PNG_WRITE_iCCP_SUPPORTED
8675 if ((LocaleCompare(name,"ICC") == 0) ||
8676 (LocaleCompare(name,"ICM") == 0))
8679 if (ping_exclude_iCCP == MagickFalse)
8681 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8682 #if (PNG_LIBPNG_VER < 10500)
8683 (png_charp) GetStringInfoDatum(profile),
8685 (png_const_bytep) GetStringInfoDatum(profile),
8687 (png_uint_32) GetStringInfoLength(profile));
8693 if (ping_exclude_zCCP == MagickFalse)
8695 Magick_png_write_raw_profile(image_info,ping,ping_info,
8696 (unsigned char *) name,(unsigned char *) name,
8697 GetStringInfoDatum(profile),
8698 (png_uint_32) GetStringInfoLength(profile));
8702 if (logging != MagickFalse)
8703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8704 " Setting up text chunk with %s profile",name);
8706 name=GetNextImageProfile(image);
8710 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8711 if ((mng_info->have_write_global_srgb == 0) &&
8712 ((image->rendering_intent != UndefinedIntent) ||
8713 (image->colorspace == sRGBColorspace)))
8715 if (ping_exclude_sRGB == MagickFalse)
8718 Note image rendering intent.
8720 if (logging != MagickFalse)
8721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8722 " Setting up sRGB chunk");
8724 (void) png_set_sRGB(ping,ping_info,(
8725 Magick_RenderingIntent_to_PNG_RenderingIntent(
8726 image->rendering_intent)));
8728 if (ping_exclude_gAMA == MagickFalse)
8729 png_set_gAMA(ping,ping_info,0.45455);
8733 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8736 if (ping_exclude_gAMA == MagickFalse &&
8737 (ping_exclude_sRGB == MagickFalse ||
8738 (image->gamma < .45 || image->gamma > .46)))
8740 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8744 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8746 if (logging != MagickFalse)
8747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8748 " Setting up gAMA chunk");
8750 png_set_gAMA(ping,ping_info,image->gamma);
8754 if (ping_exclude_cHRM == MagickFalse)
8756 if ((mng_info->have_write_global_chrm == 0) &&
8757 (image->chromaticity.red_primary.x != 0.0))
8760 Note image chromaticity.
8761 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8769 wp=image->chromaticity.white_point;
8770 rp=image->chromaticity.red_primary;
8771 gp=image->chromaticity.green_primary;
8772 bp=image->chromaticity.blue_primary;
8774 if (logging != MagickFalse)
8775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8776 " Setting up cHRM chunk");
8778 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
8784 ping_interlace_method=image_info->interlace != NoInterlace;
8786 if (mng_info->write_mng)
8787 png_set_sig_bytes(ping,8);
8789 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
8791 if (mng_info->write_png_colortype != 0)
8793 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
8794 if (ping_have_color != MagickFalse)
8796 ping_color_type = PNG_COLOR_TYPE_RGB;
8798 if (ping_bit_depth < 8)
8802 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
8803 if (ping_have_color != MagickFalse)
8804 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
8807 if (ping_need_colortype_warning != MagickFalse ||
8808 ((mng_info->write_png_depth &&
8809 (int) mng_info->write_png_depth != ping_bit_depth) ||
8810 (mng_info->write_png_colortype &&
8811 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
8812 mng_info->write_png_colortype != 7 &&
8813 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
8815 if (logging != MagickFalse)
8817 if (ping_need_colortype_warning != MagickFalse)
8819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8820 " Image has transparency but tRNS chunk was excluded");
8823 if (mng_info->write_png_depth)
8825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8826 " Defined PNG:bit-depth=%u, Computed depth=%u",
8827 mng_info->write_png_depth,
8831 if (mng_info->write_png_colortype)
8833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8834 " Defined PNG:color-type=%u, Computed color type=%u",
8835 mng_info->write_png_colortype-1,
8841 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
8844 if (image_matte != MagickFalse && image->matte == MagickFalse)
8846 /* Add an opaque matte channel */
8847 image->matte = MagickTrue;
8848 (void) SetImageOpacity(image,0);
8850 if (logging != MagickFalse)
8851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8852 " Added an opaque matte channel");
8855 if (number_transparent != 0 || number_semitransparent != 0)
8857 if (ping_color_type < 4)
8859 ping_have_tRNS=MagickTrue;
8860 if (logging != MagickFalse)
8861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8862 " Setting ping_have_tRNS=MagickTrue.");
8866 if (logging != MagickFalse)
8867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8868 " Writing PNG header chunks");
8870 png_set_IHDR(ping,ping_info,ping_width,ping_height,
8871 ping_bit_depth,ping_color_type,
8872 ping_interlace_method,ping_compression_method,
8873 ping_filter_method);
8875 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
8877 png_set_PLTE(ping,ping_info,palette,number_colors);
8879 if (logging != MagickFalse)
8881 for (i=0; i< (ssize_t) number_colors; i++)
8883 if (i < ping_num_trans)
8884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8885 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
8887 (int) palette[i].red,
8888 (int) palette[i].green,
8889 (int) palette[i].blue,
8891 (int) ping_trans_alpha[i]);
8893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8894 " PLTE[%d] = (%d,%d,%d)",
8896 (int) palette[i].red,
8897 (int) palette[i].green,
8898 (int) palette[i].blue);
8903 if (ping_exclude_bKGD == MagickFalse)
8905 if (ping_have_bKGD != MagickFalse)
8906 png_set_bKGD(ping,ping_info,&ping_background);
8909 if (ping_exclude_pHYs == MagickFalse)
8911 if (ping_have_pHYs != MagickFalse)
8913 png_set_pHYs(ping,ping_info,
8914 ping_pHYs_x_resolution,
8915 ping_pHYs_y_resolution,
8916 ping_pHYs_unit_type);
8920 #if defined(PNG_oFFs_SUPPORTED)
8921 if (ping_exclude_oFFs == MagickFalse)
8923 if (image->page.x || image->page.y)
8925 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
8926 (png_int_32) image->page.y, 0);
8928 if (logging != MagickFalse)
8929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8930 " Setting up oFFs chunk with x=%d, y=%d, units=0",
8931 (int) image->page.x, (int) image->page.y);
8936 if (mng_info->need_blob != MagickFalse)
8938 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
8940 png_error(ping,"WriteBlob Failed");
8942 ping_have_blob=MagickTrue;
8945 png_write_info_before_PLTE(ping, ping_info);
8947 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
8949 if (logging != MagickFalse)
8951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8952 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
8955 if (ping_color_type == 3)
8956 (void) png_set_tRNS(ping, ping_info,
8963 (void) png_set_tRNS(ping, ping_info,
8968 if (logging != MagickFalse)
8970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8971 " tRNS color =(%d,%d,%d)",
8972 (int) ping_trans_color.red,
8973 (int) ping_trans_color.green,
8974 (int) ping_trans_color.blue);
8979 /* write any png-chunk-b profiles */
8980 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
8982 png_write_info(ping,ping_info);
8984 /* write any PNG-chunk-m profiles */
8985 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
8987 if (ping_exclude_vpAg == MagickFalse)
8989 if ((image->page.width != 0 && image->page.width != image->columns) ||
8990 (image->page.height != 0 && image->page.height != image->rows))
8995 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8996 PNGType(chunk,mng_vpAg);
8997 LogPNGChunk(logging,mng_vpAg,9L);
8998 PNGLong(chunk+4,(png_uint_32) image->page.width);
8999 PNGLong(chunk+8,(png_uint_32) image->page.height);
9000 chunk[12]=0; /* unit = pixels */
9001 (void) WriteBlob(image,13,chunk);
9002 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9006 #if (PNG_LIBPNG_VER == 10206)
9007 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9008 #define PNG_HAVE_IDAT 0x04
9009 ping->mode |= PNG_HAVE_IDAT;
9010 #undef PNG_HAVE_IDAT
9013 png_set_packing(ping);
9017 rowbytes=image->columns;
9018 if (image_depth > 8)
9020 switch (ping_color_type)
9022 case PNG_COLOR_TYPE_RGB:
9026 case PNG_COLOR_TYPE_GRAY_ALPHA:
9030 case PNG_COLOR_TYPE_RGBA:
9038 if (logging != MagickFalse)
9040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9041 " Writing PNG image data");
9043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9046 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9047 sizeof(*ping_pixels));
9049 if (ping_pixels == (unsigned char *) NULL)
9050 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9053 Initialize image scanlines.
9055 if (setjmp(png_jmpbuf(ping)))
9061 if (image_info->verbose)
9062 (void) printf("PNG write has failed.\n");
9064 png_destroy_write_struct(&ping,&ping_info);
9065 if (quantum_info != (QuantumInfo *) NULL)
9066 quantum_info=DestroyQuantumInfo(quantum_info);
9067 if (ping_pixels != (unsigned char *) NULL)
9068 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9069 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9070 UnlockSemaphoreInfo(ping_semaphore);
9072 if (ping_have_blob != MagickFalse)
9073 (void) CloseBlob(image);
9074 image_info=DestroyImageInfo(image_info);
9075 image=DestroyImage(image);
9076 return(MagickFalse);
9078 quantum_info=AcquireQuantumInfo(image_info,image);
9079 if (quantum_info == (QuantumInfo *) NULL)
9080 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9081 quantum_info->format=UndefinedQuantumFormat;
9082 quantum_info->depth=image_depth;
9083 num_passes=png_set_interlace_handling(ping);
9085 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9086 !mng_info->write_png32) &&
9087 (mng_info->IsPalette ||
9088 (image_info->type == BilevelType)) &&
9089 image_matte == MagickFalse &&
9090 ping_have_non_bw == MagickFalse)
9092 /* Palette, Bilevel, or Opaque Monochrome */
9093 register const PixelPacket
9096 quantum_info->depth=8;
9097 for (pass=0; pass < num_passes; pass++)
9100 Convert PseudoClass image to a PNG monochrome image.
9102 for (y=0; y < (ssize_t) image->rows; y++)
9104 if (logging != MagickFalse && y == 0)
9105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9106 " Writing row of pixels (0)");
9108 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9110 if (p == (const PixelPacket *) NULL)
9113 if (mng_info->IsPalette)
9115 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9116 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9117 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9118 mng_info->write_png_depth &&
9119 mng_info->write_png_depth != old_bit_depth)
9121 /* Undo pixel scaling */
9122 for (i=0; i < (ssize_t) image->columns; i++)
9123 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9124 >> (8-old_bit_depth));
9130 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9131 quantum_info,RedQuantum,ping_pixels,&image->exception);
9134 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9135 for (i=0; i < (ssize_t) image->columns; i++)
9136 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9139 if (logging != MagickFalse && y == 0)
9140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9141 " Writing row of pixels (1)");
9143 png_write_row(ping,ping_pixels);
9145 if (image->previous == (Image *) NULL)
9147 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9148 if (status == MagickFalse)
9154 else /* Not Palette, Bilevel, or Opaque Monochrome */
9156 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9157 !mng_info->write_png32) &&
9158 (image_matte != MagickFalse ||
9159 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9160 (mng_info->IsPalette) && ping_have_color == MagickFalse)
9162 register const PixelPacket
9165 for (pass=0; pass < num_passes; pass++)
9168 for (y=0; y < (ssize_t) image->rows; y++)
9170 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9172 if (p == (const PixelPacket *) NULL)
9175 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9177 if (mng_info->IsPalette)
9178 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9179 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9182 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9183 quantum_info,RedQuantum,ping_pixels,&image->exception);
9185 if (logging != MagickFalse && y == 0)
9186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9187 " Writing GRAY PNG pixels (2)");
9190 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9192 if (logging != MagickFalse && y == 0)
9193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9194 " Writing GRAY_ALPHA PNG pixels (2)");
9196 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9197 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9200 if (logging != MagickFalse && y == 0)
9201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9202 " Writing row of pixels (2)");
9204 png_write_row(ping,ping_pixels);
9207 if (image->previous == (Image *) NULL)
9209 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9210 if (status == MagickFalse)
9218 register const PixelPacket
9221 for (pass=0; pass < num_passes; pass++)
9223 if ((image_depth > 8) || (mng_info->write_png24 ||
9224 mng_info->write_png32 ||
9225 (!mng_info->write_png8 && !mng_info->IsPalette)))
9227 for (y=0; y < (ssize_t) image->rows; y++)
9229 p=GetVirtualPixels(image,0,y,image->columns,1,
9232 if (p == (const PixelPacket *) NULL)
9235 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9237 if (image->storage_class == DirectClass)
9238 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9239 quantum_info,RedQuantum,ping_pixels,&image->exception);
9242 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9243 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9246 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9248 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9249 quantum_info,GrayAlphaQuantum,ping_pixels,
9252 if (logging != MagickFalse && y == 0)
9253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9254 " Writing GRAY_ALPHA PNG pixels (3)");
9257 else if (image_matte != MagickFalse)
9258 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9259 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9262 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9263 quantum_info,RGBQuantum,ping_pixels,&image->exception);
9265 if (logging != MagickFalse && y == 0)
9266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9267 " Writing row of pixels (3)");
9269 png_write_row(ping,ping_pixels);
9274 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9275 mng_info->write_png32 ||
9276 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9278 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9279 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9281 if (logging != MagickFalse)
9282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9283 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9285 quantum_info->depth=8;
9289 for (y=0; y < (ssize_t) image->rows; y++)
9291 if (logging != MagickFalse && y == 0)
9292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9293 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9295 p=GetVirtualPixels(image,0,y,image->columns,1,
9298 if (p == (const PixelPacket *) NULL)
9301 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9302 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9303 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9305 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9307 if (logging != MagickFalse && y == 0)
9308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9309 " Writing GRAY_ALPHA PNG pixels (4)");
9311 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9312 quantum_info,GrayAlphaQuantum,ping_pixels,
9320 * This is failing to account for 1, 2, 4-bit depths
9321 * The call to png_set_packing() above is supposed to
9322 * take care of those.
9325 /* GrayQuantum does not work here */
9326 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9327 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9329 if (logging != MagickFalse && y <= 2)
9331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9332 " Writing row of pixels (4)");
9334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9335 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9336 (int)ping_pixels[0],(int)ping_pixels[1]);
9339 png_write_row(ping,ping_pixels);
9343 if (image->previous == (Image *) NULL)
9345 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9346 if (status == MagickFalse)
9353 if (quantum_info != (QuantumInfo *) NULL)
9354 quantum_info=DestroyQuantumInfo(quantum_info);
9356 if (logging != MagickFalse)
9358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9359 " Wrote PNG image data");
9361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9362 " Width: %.20g",(double) ping_width);
9364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9365 " Height: %.20g",(double) ping_height);
9367 if (mng_info->write_png_depth)
9369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9370 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9374 " PNG bit-depth written: %d",ping_bit_depth);
9376 if (mng_info->write_png_colortype)
9378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9379 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9383 " PNG color-type written: %d",ping_color_type);
9385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9386 " PNG Interlace method: %d",ping_interlace_method);
9389 Generate text chunks.
9391 if (ping_exclude_tEXt == MagickFalse && ping_exclude_zTXt == MagickFalse)
9393 ResetImagePropertyIterator(image);
9394 property=GetNextImageProperty(image);
9395 while (property != (const char *) NULL)
9400 value=GetImageProperty(image,property);
9401 if (LocaleCompare(property,"density") != 0 ||
9402 ping_exclude_pHYs != MagickFalse)
9404 if (value != (const char *) NULL)
9406 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9407 text[0].key=(char *) property;
9408 text[0].text=(char *) value;
9409 text[0].text_length=strlen(value);
9411 if (ping_exclude_tEXt != MagickFalse)
9412 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9414 else if (ping_exclude_zTXt != MagickFalse)
9415 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9419 text[0].compression=image_info->compression == NoCompression ||
9420 (image_info->compression == UndefinedCompression &&
9421 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9422 PNG_TEXT_COMPRESSION_zTXt ;
9425 if (logging != MagickFalse)
9427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9428 " Setting up text chunk");
9430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9431 " keyword: %s",text[0].key);
9434 png_set_text(ping,ping_info,text,1);
9435 png_free(ping,text);
9438 property=GetNextImageProperty(image);
9442 /* write any PNG-chunk-e profiles */
9443 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9445 if (logging != MagickFalse)
9446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9447 " Writing PNG end info");
9449 png_write_end(ping,ping_info);
9451 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9453 if (mng_info->page.x || mng_info->page.y ||
9454 (ping_width != mng_info->page.width) ||
9455 (ping_height != mng_info->page.height))
9461 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9463 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9464 PNGType(chunk,mng_FRAM);
9465 LogPNGChunk(logging,mng_FRAM,27L);
9467 chunk[5]=0; /* frame name separator (no name) */
9468 chunk[6]=1; /* flag for changing delay, for next frame only */
9469 chunk[7]=0; /* flag for changing frame timeout */
9470 chunk[8]=1; /* flag for changing frame clipping for next frame */
9471 chunk[9]=0; /* flag for changing frame sync_id */
9472 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9473 chunk[14]=0; /* clipping boundaries delta type */
9474 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9476 (png_uint_32) (mng_info->page.x + ping_width));
9477 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9479 (png_uint_32) (mng_info->page.y + ping_height));
9480 (void) WriteBlob(image,31,chunk);
9481 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9482 mng_info->old_framing_mode=4;
9483 mng_info->framing_mode=1;
9487 mng_info->framing_mode=3;
9489 if (mng_info->write_mng && !mng_info->need_fram &&
9490 ((int) image->dispose == 3))
9491 (void) ThrowMagickException(&image->exception,GetMagickModule(),
9492 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9493 "`%s'",image->filename);
9499 png_destroy_write_struct(&ping,&ping_info);
9501 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9503 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9504 UnlockSemaphoreInfo(ping_semaphore);
9507 if (ping_have_blob != MagickFalse)
9508 (void) CloseBlob(image);
9510 image_info=DestroyImageInfo(image_info);
9511 image=DestroyImage(image);
9513 /* Store bit depth actually written */
9514 s[0]=(char) ping_bit_depth;
9517 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9519 if (logging != MagickFalse)
9520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9521 " exit WriteOnePNGImage()");
9524 /* End write one PNG image */
9528 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9532 % W r i t e P N G I m a g e %
9536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9538 % WritePNGImage() writes a Portable Network Graphics (PNG) or
9539 % Multiple-image Network Graphics (MNG) image file.
9541 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
9543 % The format of the WritePNGImage method is:
9545 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9547 % A description of each parameter follows:
9549 % o image_info: the image info.
9551 % o image: The image.
9553 % Returns MagickTrue on success, MagickFalse on failure.
9555 % Communicating with the PNG encoder:
9557 % While the datastream written is always in PNG format and normally would
9558 % be given the "png" file extension, this method also writes the following
9559 % pseudo-formats which are subsets of PNG:
9561 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
9562 % a depth greater than 8, the depth is reduced. If transparency
9563 % is present, the tRNS chunk must only have values 0 and 255
9564 % (i.e., transparency is binary: fully opaque or fully
9565 % transparent). If other values are present they will be
9566 % 50%-thresholded to binary transparency. If more than 256
9567 % colors are present, they will be quantized to the 3-3-2
9568 % palette. If you want better quantization or dithering of
9569 % the colors or alpha, you need to do it before calling the
9570 % PNG encoder. The pixels contain 8-bit indices even if
9571 % they could be represented with 1, 2, or 4 bits. Grayscale
9572 % images will be written as indexed PNG files even though the
9573 % PNG grayscale type might be slightly more efficient. Please
9574 % note that writing to the PNG8 format may result in loss
9575 % of color and alpha data.
9577 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9578 % chunk can be present to convey binary transparency by naming
9579 % one of the colors as transparent. The only loss incurred
9580 % is reduction of sample depth to 8. If the image has more
9581 % than one transparent color, has semitransparent pixels, or
9582 % has an opaque pixel with the same RGB components as the
9583 % transparent color, an image is not written.
9585 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
9586 % transparency is permitted, i.e., the alpha sample for
9587 % each pixel can have any value from 0 to 255. The alpha
9588 % channel is present even if the image is fully opaque.
9589 % The only loss in data is the reduction of the sample depth
9592 % o -define: For more precise control of the PNG output, you can use the
9593 % Image options "png:bit-depth" and "png:color-type". These
9594 % can be set from the commandline with "-define" and also
9595 % from the application programming interfaces. The options
9596 % are case-independent and are converted to lowercase before
9597 % being passed to this encoder.
9599 % png:color-type can be 0, 2, 3, 4, or 6.
9601 % When png:color-type is 0 (Grayscale), png:bit-depth can
9602 % be 1, 2, 4, 8, or 16.
9604 % When png:color-type is 2 (RGB), png:bit-depth can
9607 % When png:color-type is 3 (Indexed), png:bit-depth can
9608 % be 1, 2, 4, or 8. This refers to the number of bits
9609 % used to store the index. The color samples always have
9610 % bit-depth 8 in indexed PNG files.
9612 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9613 % png:bit-depth can be 8 or 16.
9615 % If the image cannot be written without loss with the requested bit-depth
9616 % and color-type, a PNG file will not be written, and the encoder will
9617 % return MagickFalse.
9619 % Since image encoders should not be responsible for the "heavy lifting",
9620 % the user should make sure that ImageMagick has already reduced the
9621 % image depth and number of colors and limit transparency to binary
9622 % transparency prior to attempting to write the image with depth, color,
9623 % or transparency limitations.
9625 % TODO: Enforce the previous paragraph.
9627 % Note that another definition, "png:bit-depth-written" exists, but it
9628 % is not intended for external use. It is only used internally by the
9629 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9631 % It is possible to request that the PNG encoder write previously-formatted
9632 % ancillary chunks in the output PNG file, using the "-profile" commandline
9633 % option as shown below or by setting the profile via a programming
9636 % -profile PNG-chunk-x:<file>
9638 % where x is a location flag and <file> is a file containing the chunk
9639 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9640 % This encoder will compute the chunk length and CRC, so those must not
9641 % be included in the file.
9643 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9644 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
9645 % of the same type, then add a short unique string after the "x" to prevent
9646 % subsequent profiles from overwriting the preceding ones, e.g.,
9648 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9650 % As of version 6.6.6 the following optimizations are always done:
9652 % o 32-bit depth is reduced to 16.
9653 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
9654 % high byte and low byte are identical.
9655 % o Palette is sorted to remove unused entries and to put a
9656 % transparent color first, if BUILD_PNG_PALETTE is defined.
9657 % o Opaque matte channel is removed when writing an indexed PNG.
9658 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
9659 % this can be done without loss and a larger bit depth N was not
9660 % requested via the "-define PNG:bit-depth=N" option.
9661 % o If matte channel is present but only one transparent color is
9662 % present, RGB+tRNS is written instead of RGBA
9663 % o Opaque matte channel is removed (or added, if color-type 4 or 6
9664 % was requested when converting an opaque image).
9666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9668 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9690 assert(image_info != (const ImageInfo *) NULL);
9691 assert(image_info->signature == MagickSignature);
9692 assert(image != (Image *) NULL);
9693 assert(image->signature == MagickSignature);
9694 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9695 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
9697 Allocate a MngInfo structure.
9699 have_mng_structure=MagickFalse;
9700 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9702 if (mng_info == (MngInfo *) NULL)
9703 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9706 Initialize members of the MngInfo structure.
9708 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9709 mng_info->image=image;
9710 mng_info->equal_backgrounds=MagickTrue;
9711 have_mng_structure=MagickTrue;
9713 /* See if user has requested a specific PNG subformat */
9715 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9716 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9717 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9719 if (mng_info->write_png8)
9721 mng_info->write_png_colortype = /* 3 */ 4;
9722 mng_info->write_png_depth = 8;
9726 if (mng_info->write_png24)
9728 mng_info->write_png_colortype = /* 2 */ 3;
9729 mng_info->write_png_depth = 8;
9732 if (image->matte == MagickTrue)
9733 (void) SetImageType(image,TrueColorMatteType);
9736 (void) SetImageType(image,TrueColorType);
9738 (void) SyncImage(image);
9741 if (mng_info->write_png32)
9743 mng_info->write_png_colortype = /* 6 */ 7;
9744 mng_info->write_png_depth = 8;
9747 if (image->matte == MagickTrue)
9748 (void) SetImageType(image,TrueColorMatteType);
9751 (void) SetImageType(image,TrueColorType);
9753 (void) SyncImage(image);
9756 value=GetImageOption(image_info,"png:bit-depth");
9758 if (value != (char *) NULL)
9760 if (LocaleCompare(value,"1") == 0)
9761 mng_info->write_png_depth = 1;
9763 else if (LocaleCompare(value,"2") == 0)
9764 mng_info->write_png_depth = 2;
9766 else if (LocaleCompare(value,"4") == 0)
9767 mng_info->write_png_depth = 4;
9769 else if (LocaleCompare(value,"8") == 0)
9770 mng_info->write_png_depth = 8;
9772 else if (LocaleCompare(value,"16") == 0)
9773 mng_info->write_png_depth = 16;
9776 (void) ThrowMagickException(&image->exception,
9777 GetMagickModule(),CoderWarning,
9778 "ignoring invalid defined png:bit-depth",
9781 if (logging != MagickFalse)
9782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9783 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
9786 value=GetImageOption(image_info,"png:color-type");
9788 if (value != (char *) NULL)
9790 /* We must store colortype+1 because 0 is a valid colortype */
9791 if (LocaleCompare(value,"0") == 0)
9792 mng_info->write_png_colortype = 1;
9794 else if (LocaleCompare(value,"2") == 0)
9795 mng_info->write_png_colortype = 3;
9797 else if (LocaleCompare(value,"3") == 0)
9798 mng_info->write_png_colortype = 4;
9800 else if (LocaleCompare(value,"4") == 0)
9801 mng_info->write_png_colortype = 5;
9803 else if (LocaleCompare(value,"6") == 0)
9804 mng_info->write_png_colortype = 7;
9807 (void) ThrowMagickException(&image->exception,
9808 GetMagickModule(),CoderWarning,
9809 "ignoring invalid defined png:color-type",
9812 if (logging != MagickFalse)
9813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9814 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
9817 /* Check for chunks to be excluded:
9819 * The default is to not exclude any known chunks except for any
9820 * listed in the "unused_chunks" array, above.
9822 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
9823 * define (in the image properties or in the image artifacts)
9824 * or via a mng_info member. For convenience, in addition
9825 * to or instead of a comma-separated list of chunks, the
9826 * "exclude-chunk" string can be simply "all" or "none".
9828 * The exclude-chunk define takes priority over the mng_info.
9830 * A "PNG:include-chunk" define takes priority over both the
9831 * mng_info and the "PNG:exclude-chunk" define. Like the
9832 * "exclude-chunk" string, it can define "all" or "none" as
9833 * well as a comma-separated list. Chunks that are unknown to
9834 * ImageMagick are always excluded, regardless of their "copy-safe"
9835 * status according to the PNG specification, and even if they
9836 * appear in the "include-chunk" list.
9838 * Finally, all chunks listed in the "unused_chunks" array are
9839 * automatically excluded, regardless of the other instructions
9842 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
9843 * will not be written and the gAMA chunk will only be written if it
9844 * is not between .45 and .46, or approximately (1.0/2.2).
9846 * If you exclude tRNS and the image has transparency, the colortype
9847 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
9849 * The -strip option causes StripImage() to set the png:include-chunk
9850 * artifact to "none,gama".
9853 mng_info->ping_exclude_bKGD=MagickFalse;
9854 mng_info->ping_exclude_cHRM=MagickFalse;
9855 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
9856 mng_info->ping_exclude_gAMA=MagickFalse;
9857 mng_info->ping_exclude_cHRM=MagickFalse;
9858 mng_info->ping_exclude_iCCP=MagickFalse;
9859 /* mng_info->ping_exclude_iTXt=MagickFalse; */
9860 mng_info->ping_exclude_oFFs=MagickFalse;
9861 mng_info->ping_exclude_pHYs=MagickFalse;
9862 mng_info->ping_exclude_sRGB=MagickFalse;
9863 mng_info->ping_exclude_tEXt=MagickFalse;
9864 mng_info->ping_exclude_tRNS=MagickFalse;
9865 mng_info->ping_exclude_vpAg=MagickFalse;
9866 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
9867 mng_info->ping_exclude_zTXt=MagickFalse;
9869 excluding=MagickFalse;
9871 for (source=0; source<1; source++)
9875 value=GetImageArtifact(image,"png:exclude-chunk");
9878 value=GetImageArtifact(image,"png:exclude-chunks");
9882 value=GetImageOption(image_info,"png:exclude-chunk");
9885 value=GetImageOption(image_info,"png:exclude-chunks");
9894 excluding=MagickTrue;
9896 if (logging != MagickFalse)
9899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9900 " png:exclude-chunk=%s found in image artifacts.\n", value);
9902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9903 " png:exclude-chunk=%s found in image properties.\n", value);
9908 for (i=0; i<(int) last; i+=5)
9911 if (LocaleNCompare(value+i,"all",3) == 0)
9913 mng_info->ping_exclude_bKGD=MagickTrue;
9914 mng_info->ping_exclude_cHRM=MagickTrue;
9915 mng_info->ping_exclude_EXIF=MagickTrue;
9916 mng_info->ping_exclude_gAMA=MagickTrue;
9917 mng_info->ping_exclude_iCCP=MagickTrue;
9918 /* mng_info->ping_exclude_iTXt=MagickTrue; */
9919 mng_info->ping_exclude_oFFs=MagickTrue;
9920 mng_info->ping_exclude_pHYs=MagickTrue;
9921 mng_info->ping_exclude_sRGB=MagickTrue;
9922 mng_info->ping_exclude_tEXt=MagickTrue;
9923 mng_info->ping_exclude_tRNS=MagickTrue;
9924 mng_info->ping_exclude_vpAg=MagickTrue;
9925 mng_info->ping_exclude_zCCP=MagickTrue;
9926 mng_info->ping_exclude_zTXt=MagickTrue;
9930 if (LocaleNCompare(value+i,"none",4) == 0)
9932 mng_info->ping_exclude_bKGD=MagickFalse;
9933 mng_info->ping_exclude_cHRM=MagickFalse;
9934 mng_info->ping_exclude_EXIF=MagickFalse;
9935 mng_info->ping_exclude_gAMA=MagickFalse;
9936 mng_info->ping_exclude_iCCP=MagickFalse;
9937 /* mng_info->ping_exclude_iTXt=MagickFalse; */
9938 mng_info->ping_exclude_oFFs=MagickFalse;
9939 mng_info->ping_exclude_pHYs=MagickFalse;
9940 mng_info->ping_exclude_sRGB=MagickFalse;
9941 mng_info->ping_exclude_tEXt=MagickFalse;
9942 mng_info->ping_exclude_tRNS=MagickFalse;
9943 mng_info->ping_exclude_vpAg=MagickFalse;
9944 mng_info->ping_exclude_zCCP=MagickFalse;
9945 mng_info->ping_exclude_zTXt=MagickFalse;
9948 if (LocaleNCompare(value+i,"bkgd",4) == 0)
9949 mng_info->ping_exclude_bKGD=MagickTrue;
9951 if (LocaleNCompare(value+i,"chrm",4) == 0)
9952 mng_info->ping_exclude_cHRM=MagickTrue;
9954 if (LocaleNCompare(value+i,"exif",4) == 0)
9955 mng_info->ping_exclude_EXIF=MagickTrue;
9957 if (LocaleNCompare(value+i,"gama",4) == 0)
9958 mng_info->ping_exclude_gAMA=MagickTrue;
9960 if (LocaleNCompare(value+i,"iccp",4) == 0)
9961 mng_info->ping_exclude_iCCP=MagickTrue;
9964 if (LocaleNCompare(value+i,"itxt",4) == 0)
9965 mng_info->ping_exclude_iTXt=MagickTrue;
9968 if (LocaleNCompare(value+i,"gama",4) == 0)
9969 mng_info->ping_exclude_gAMA=MagickTrue;
9971 if (LocaleNCompare(value+i,"offs",4) == 0)
9972 mng_info->ping_exclude_oFFs=MagickTrue;
9974 if (LocaleNCompare(value+i,"phys",4) == 0)
9975 mng_info->ping_exclude_pHYs=MagickTrue;
9977 if (LocaleNCompare(value+i,"srgb",4) == 0)
9978 mng_info->ping_exclude_sRGB=MagickTrue;
9980 if (LocaleNCompare(value+i,"text",4) == 0)
9981 mng_info->ping_exclude_tEXt=MagickTrue;
9983 if (LocaleNCompare(value+i,"trns",4) == 0)
9984 mng_info->ping_exclude_tRNS=MagickTrue;
9986 if (LocaleNCompare(value+i,"vpag",4) == 0)
9987 mng_info->ping_exclude_vpAg=MagickTrue;
9989 if (LocaleNCompare(value+i,"zccp",4) == 0)
9990 mng_info->ping_exclude_zCCP=MagickTrue;
9992 if (LocaleNCompare(value+i,"ztxt",4) == 0)
9993 mng_info->ping_exclude_zTXt=MagickTrue;
9999 for (source=0; source<1; source++)
10003 value=GetImageArtifact(image,"png:include-chunk");
10006 value=GetImageArtifact(image,"png:include-chunks");
10010 value=GetImageOption(image_info,"png:include-chunk");
10013 value=GetImageOption(image_info,"png:include-chunks");
10021 excluding=MagickTrue;
10023 if (logging != MagickFalse)
10026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10027 " png:include-chunk=%s found in image artifacts.\n", value);
10029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10030 " png:include-chunk=%s found in image properties.\n", value);
10033 last=strlen(value);
10035 for (i=0; i<(int) last; i+=5)
10037 if (LocaleNCompare(value+i,"all",3) == 0)
10039 mng_info->ping_exclude_bKGD=MagickFalse;
10040 mng_info->ping_exclude_cHRM=MagickFalse;
10041 mng_info->ping_exclude_EXIF=MagickFalse;
10042 mng_info->ping_exclude_gAMA=MagickFalse;
10043 mng_info->ping_exclude_iCCP=MagickFalse;
10044 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10045 mng_info->ping_exclude_oFFs=MagickFalse;
10046 mng_info->ping_exclude_pHYs=MagickFalse;
10047 mng_info->ping_exclude_sRGB=MagickFalse;
10048 mng_info->ping_exclude_tEXt=MagickFalse;
10049 mng_info->ping_exclude_tRNS=MagickFalse;
10050 mng_info->ping_exclude_vpAg=MagickFalse;
10051 mng_info->ping_exclude_zCCP=MagickFalse;
10052 mng_info->ping_exclude_zTXt=MagickFalse;
10056 if (LocaleNCompare(value+i,"none",4) == 0)
10058 mng_info->ping_exclude_bKGD=MagickTrue;
10059 mng_info->ping_exclude_cHRM=MagickTrue;
10060 mng_info->ping_exclude_EXIF=MagickTrue;
10061 mng_info->ping_exclude_gAMA=MagickTrue;
10062 mng_info->ping_exclude_iCCP=MagickTrue;
10063 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10064 mng_info->ping_exclude_oFFs=MagickTrue;
10065 mng_info->ping_exclude_pHYs=MagickTrue;
10066 mng_info->ping_exclude_sRGB=MagickTrue;
10067 mng_info->ping_exclude_tEXt=MagickTrue;
10068 mng_info->ping_exclude_tRNS=MagickTrue;
10069 mng_info->ping_exclude_vpAg=MagickTrue;
10070 mng_info->ping_exclude_zCCP=MagickTrue;
10071 mng_info->ping_exclude_zTXt=MagickTrue;
10074 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10075 mng_info->ping_exclude_bKGD=MagickFalse;
10077 if (LocaleNCompare(value+i,"chrm",4) == 0)
10078 mng_info->ping_exclude_cHRM=MagickFalse;
10080 if (LocaleNCompare(value+i,"exif",4) == 0)
10081 mng_info->ping_exclude_EXIF=MagickFalse;
10083 if (LocaleNCompare(value+i,"gama",4) == 0)
10084 mng_info->ping_exclude_gAMA=MagickFalse;
10086 if (LocaleNCompare(value+i,"iccp",4) == 0)
10087 mng_info->ping_exclude_iCCP=MagickFalse;
10090 if (LocaleNCompare(value+i,"itxt",4) == 0)
10091 mng_info->ping_exclude_iTXt=MagickFalse;
10094 if (LocaleNCompare(value+i,"gama",4) == 0)
10095 mng_info->ping_exclude_gAMA=MagickFalse;
10097 if (LocaleNCompare(value+i,"offs",4) == 0)
10098 mng_info->ping_exclude_oFFs=MagickFalse;
10100 if (LocaleNCompare(value+i,"phys",4) == 0)
10101 mng_info->ping_exclude_pHYs=MagickFalse;
10103 if (LocaleNCompare(value+i,"srgb",4) == 0)
10104 mng_info->ping_exclude_sRGB=MagickFalse;
10106 if (LocaleNCompare(value+i,"text",4) == 0)
10107 mng_info->ping_exclude_tEXt=MagickFalse;
10109 if (LocaleNCompare(value+i,"trns",4) == 0)
10110 mng_info->ping_exclude_tRNS=MagickFalse;
10112 if (LocaleNCompare(value+i,"vpag",4) == 0)
10113 mng_info->ping_exclude_vpAg=MagickFalse;
10115 if (LocaleNCompare(value+i,"zccp",4) == 0)
10116 mng_info->ping_exclude_zCCP=MagickFalse;
10118 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10119 mng_info->ping_exclude_zTXt=MagickFalse;
10125 if (excluding != MagickFalse && logging != MagickFalse)
10127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10128 " Chunks to be excluded from the output PNG:");
10129 if (mng_info->ping_exclude_bKGD != MagickFalse)
10130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10132 if (mng_info->ping_exclude_cHRM != MagickFalse)
10133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10135 if (mng_info->ping_exclude_EXIF != MagickFalse)
10136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10138 if (mng_info->ping_exclude_gAMA != MagickFalse)
10139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10141 if (mng_info->ping_exclude_iCCP != MagickFalse)
10142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10145 if (mng_info->ping_exclude_iTXt != MagickFalse)
10146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10149 if (mng_info->ping_exclude_oFFs != MagickFalse)
10150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10152 if (mng_info->ping_exclude_pHYs != MagickFalse)
10153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10155 if (mng_info->ping_exclude_sRGB != MagickFalse)
10156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10158 if (mng_info->ping_exclude_tEXt != MagickFalse)
10159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10161 if (mng_info->ping_exclude_tRNS != MagickFalse)
10162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10164 if (mng_info->ping_exclude_vpAg != MagickFalse)
10165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10167 if (mng_info->ping_exclude_zCCP != MagickFalse)
10168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10170 if (mng_info->ping_exclude_zTXt != MagickFalse)
10171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10175 mng_info->need_blob = MagickTrue;
10177 status=WriteOnePNGImage(mng_info,image_info,image);
10179 MngInfoFreeStruct(mng_info,&have_mng_structure);
10181 if (logging != MagickFalse)
10182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10187 #if defined(JNG_SUPPORTED)
10189 /* Write one JNG image */
10190 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10191 const ImageInfo *image_info,Image *image)
10212 jng_alpha_compression_method,
10213 jng_alpha_sample_depth,
10220 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10221 " Enter WriteOneJNGImage()");
10223 blob=(unsigned char *) NULL;
10224 jpeg_image=(Image *) NULL;
10225 jpeg_image_info=(ImageInfo *) NULL;
10228 transparent=image_info->type==GrayscaleMatteType ||
10229 image_info->type==TrueColorMatteType;
10231 jng_alpha_sample_depth=0;
10232 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10233 jng_alpha_compression_method=0;
10235 if (image->matte != MagickFalse)
10237 /* if any pixels are transparent */
10238 transparent=MagickTrue;
10239 if (image_info->compression==JPEGCompression)
10240 jng_alpha_compression_method=8;
10247 /* Create JPEG blob, image, and image_info */
10248 if (logging != MagickFalse)
10249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250 " Creating jpeg_image_info for opacity.");
10252 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10254 if (jpeg_image_info == (ImageInfo *) NULL)
10255 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10257 if (logging != MagickFalse)
10258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10259 " Creating jpeg_image.");
10261 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10263 if (jpeg_image == (Image *) NULL)
10264 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10266 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10267 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10268 status=NegateImage(jpeg_image,MagickFalse);
10269 jpeg_image->matte=MagickFalse;
10271 if (jng_quality >= 1000)
10272 jpeg_image_info->quality=jng_quality/1000;
10275 jpeg_image_info->quality=jng_quality;
10277 jpeg_image_info->type=GrayscaleType;
10278 (void) SetImageType(jpeg_image,GrayscaleType);
10279 (void) AcquireUniqueFilename(jpeg_image->filename);
10280 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10281 "%s",jpeg_image->filename);
10284 /* To do: check bit depth of PNG alpha channel */
10286 /* Check if image is grayscale. */
10287 if (image_info->type != TrueColorMatteType && image_info->type !=
10288 TrueColorType && ImageIsGray(image))
10293 if (jng_alpha_compression_method==0)
10298 /* Encode opacity as a grayscale PNG blob */
10299 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10300 &image->exception);
10301 if (logging != MagickFalse)
10302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10303 " Creating PNG blob.");
10306 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10307 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10308 jpeg_image_info->interlace=NoInterlace;
10310 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10311 &image->exception);
10313 /* Retrieve sample depth used */
10314 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10315 if (value != (char *) NULL)
10316 jng_alpha_sample_depth= (unsigned int) value[0];
10320 /* Encode opacity as a grayscale JPEG blob */
10322 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10323 &image->exception);
10325 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10326 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10327 jpeg_image_info->interlace=NoInterlace;
10328 if (logging != MagickFalse)
10329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10330 " Creating blob.");
10331 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10332 &image->exception);
10333 jng_alpha_sample_depth=8;
10335 if (logging != MagickFalse)
10336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10337 " Successfully read jpeg_image into a blob, length=%.20g.",
10341 /* Destroy JPEG image and image_info */
10342 jpeg_image=DestroyImage(jpeg_image);
10343 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10344 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10347 /* Write JHDR chunk */
10348 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10349 PNGType(chunk,mng_JHDR);
10350 LogPNGChunk(logging,mng_JHDR,16L);
10351 PNGLong(chunk+4,(png_uint_32) image->columns);
10352 PNGLong(chunk+8,(png_uint_32) image->rows);
10353 chunk[12]=jng_color_type;
10354 chunk[13]=8; /* sample depth */
10355 chunk[14]=8; /*jng_image_compression_method */
10356 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10357 chunk[16]=jng_alpha_sample_depth;
10358 chunk[17]=jng_alpha_compression_method;
10359 chunk[18]=0; /*jng_alpha_filter_method */
10360 chunk[19]=0; /*jng_alpha_interlace_method */
10361 (void) WriteBlob(image,20,chunk);
10362 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10363 if (logging != MagickFalse)
10365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10366 " JNG width:%15lu",(unsigned long) image->columns);
10368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10369 " JNG height:%14lu",(unsigned long) image->rows);
10371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10372 " JNG color type:%10d",jng_color_type);
10374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10375 " JNG sample depth:%8d",8);
10377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10378 " JNG compression:%9d",8);
10380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10381 " JNG interlace:%11d",0);
10383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384 " JNG alpha depth:%9d",jng_alpha_sample_depth);
10386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10387 " JNG alpha compression:%3d",jng_alpha_compression_method);
10389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10390 " JNG alpha filter:%8d",0);
10392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10393 " JNG alpha interlace:%5d",0);
10396 /* Write any JNG-chunk-b profiles */
10397 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10400 Write leading ancillary chunks
10406 Write JNG bKGD chunk
10417 if (jng_color_type == 8 || jng_color_type == 12)
10421 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10422 PNGType(chunk,mng_bKGD);
10423 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10424 red=ScaleQuantumToChar(image->background_color.red);
10425 green=ScaleQuantumToChar(image->background_color.green);
10426 blue=ScaleQuantumToChar(image->background_color.blue);
10433 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10434 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10437 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10440 Write JNG sRGB chunk
10442 (void) WriteBlobMSBULong(image,1L);
10443 PNGType(chunk,mng_sRGB);
10444 LogPNGChunk(logging,mng_sRGB,1L);
10446 if (image->rendering_intent != UndefinedIntent)
10447 chunk[4]=(unsigned char)
10448 Magick_RenderingIntent_to_PNG_RenderingIntent(
10449 (image->rendering_intent));
10452 chunk[4]=(unsigned char)
10453 Magick_RenderingIntent_to_PNG_RenderingIntent(
10454 (PerceptualIntent));
10456 (void) WriteBlob(image,5,chunk);
10457 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10461 if (image->gamma != 0.0)
10464 Write JNG gAMA chunk
10466 (void) WriteBlobMSBULong(image,4L);
10467 PNGType(chunk,mng_gAMA);
10468 LogPNGChunk(logging,mng_gAMA,4L);
10469 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10470 (void) WriteBlob(image,8,chunk);
10471 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10474 if ((mng_info->equal_chrms == MagickFalse) &&
10475 (image->chromaticity.red_primary.x != 0.0))
10481 Write JNG cHRM chunk
10483 (void) WriteBlobMSBULong(image,32L);
10484 PNGType(chunk,mng_cHRM);
10485 LogPNGChunk(logging,mng_cHRM,32L);
10486 primary=image->chromaticity.white_point;
10487 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10488 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10489 primary=image->chromaticity.red_primary;
10490 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10491 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10492 primary=image->chromaticity.green_primary;
10493 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10494 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10495 primary=image->chromaticity.blue_primary;
10496 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10497 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10498 (void) WriteBlob(image,36,chunk);
10499 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10503 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10506 Write JNG pHYs chunk
10508 (void) WriteBlobMSBULong(image,9L);
10509 PNGType(chunk,mng_pHYs);
10510 LogPNGChunk(logging,mng_pHYs,9L);
10511 if (image->units == PixelsPerInchResolution)
10513 PNGLong(chunk+4,(png_uint_32)
10514 (image->x_resolution*100.0/2.54+0.5));
10516 PNGLong(chunk+8,(png_uint_32)
10517 (image->y_resolution*100.0/2.54+0.5));
10524 if (image->units == PixelsPerCentimeterResolution)
10526 PNGLong(chunk+4,(png_uint_32)
10527 (image->x_resolution*100.0+0.5));
10529 PNGLong(chunk+8,(png_uint_32)
10530 (image->y_resolution*100.0+0.5));
10537 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10538 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10542 (void) WriteBlob(image,13,chunk);
10543 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10546 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10549 Write JNG oFFs chunk
10551 (void) WriteBlobMSBULong(image,9L);
10552 PNGType(chunk,mng_oFFs);
10553 LogPNGChunk(logging,mng_oFFs,9L);
10554 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10555 PNGsLong(chunk+8,(ssize_t) (image->page.y));
10557 (void) WriteBlob(image,13,chunk);
10558 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10560 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10562 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10563 PNGType(chunk,mng_vpAg);
10564 LogPNGChunk(logging,mng_vpAg,9L);
10565 PNGLong(chunk+4,(png_uint_32) image->page.width);
10566 PNGLong(chunk+8,(png_uint_32) image->page.height);
10567 chunk[12]=0; /* unit = pixels */
10568 (void) WriteBlob(image,13,chunk);
10569 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10575 if (jng_alpha_compression_method==0)
10583 /* Write IDAT chunk header */
10584 if (logging != MagickFalse)
10585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10586 " Write IDAT chunks from blob, length=%.20g.",(double)
10589 /* Copy IDAT chunks */
10592 for (i=8; i<(ssize_t) length; i+=len+12)
10594 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10597 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10599 /* Found an IDAT chunk. */
10600 (void) WriteBlobMSBULong(image,(size_t) len);
10601 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10602 (void) WriteBlob(image,(size_t) len+4,p);
10603 (void) WriteBlobMSBULong(image,
10604 crc32(0,p,(uInt) len+4));
10609 if (logging != MagickFalse)
10610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10611 " Skipping %c%c%c%c chunk, length=%.20g.",
10612 *(p),*(p+1),*(p+2),*(p+3),(double) len);
10619 /* Write JDAA chunk header */
10620 if (logging != MagickFalse)
10621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 " Write JDAA chunk, length=%.20g.",(double) length);
10623 (void) WriteBlobMSBULong(image,(size_t) length);
10624 PNGType(chunk,mng_JDAA);
10625 LogPNGChunk(logging,mng_JDAA,length);
10626 /* Write JDAT chunk(s) data */
10627 (void) WriteBlob(image,4,chunk);
10628 (void) WriteBlob(image,length,blob);
10629 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10632 blob=(unsigned char *) RelinquishMagickMemory(blob);
10635 /* Encode image as a JPEG blob */
10636 if (logging != MagickFalse)
10637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10638 " Creating jpeg_image_info.");
10639 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10640 if (jpeg_image_info == (ImageInfo *) NULL)
10641 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10643 if (logging != MagickFalse)
10644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10645 " Creating jpeg_image.");
10647 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10648 if (jpeg_image == (Image *) NULL)
10649 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10650 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10652 (void) AcquireUniqueFilename(jpeg_image->filename);
10653 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10654 jpeg_image->filename);
10656 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10657 &image->exception);
10659 if (logging != MagickFalse)
10660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10661 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10662 (double) jpeg_image->rows);
10664 if (jng_color_type == 8 || jng_color_type == 12)
10665 jpeg_image_info->type=GrayscaleType;
10667 jpeg_image_info->quality=jng_quality % 1000;
10668 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10669 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10671 if (logging != MagickFalse)
10672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10673 " Creating blob.");
10675 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10677 if (logging != MagickFalse)
10679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10680 " Successfully read jpeg_image into a blob, length=%.20g.",
10683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10684 " Write JDAT chunk, length=%.20g.",(double) length);
10687 /* Write JDAT chunk(s) */
10688 (void) WriteBlobMSBULong(image,(size_t) length);
10689 PNGType(chunk,mng_JDAT);
10690 LogPNGChunk(logging,mng_JDAT,length);
10691 (void) WriteBlob(image,4,chunk);
10692 (void) WriteBlob(image,length,blob);
10693 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10695 jpeg_image=DestroyImage(jpeg_image);
10696 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10697 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10698 blob=(unsigned char *) RelinquishMagickMemory(blob);
10700 /* Write any JNG-chunk-e profiles */
10701 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10703 /* Write IEND chunk */
10704 (void) WriteBlobMSBULong(image,0L);
10705 PNGType(chunk,mng_IEND);
10706 LogPNGChunk(logging,mng_IEND,0);
10707 (void) WriteBlob(image,4,chunk);
10708 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10710 if (logging != MagickFalse)
10711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10712 " exit WriteOneJNGImage()");
10719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10723 % W r i t e J N G I m a g e %
10727 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10729 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10731 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
10733 % The format of the WriteJNGImage method is:
10735 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10737 % A description of each parameter follows:
10739 % o image_info: the image info.
10741 % o image: The image.
10743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10745 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10748 have_mng_structure,
10758 assert(image_info != (const ImageInfo *) NULL);
10759 assert(image_info->signature == MagickSignature);
10760 assert(image != (Image *) NULL);
10761 assert(image->signature == MagickSignature);
10762 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10763 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
10764 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
10765 if (status == MagickFalse)
10769 Allocate a MngInfo structure.
10771 have_mng_structure=MagickFalse;
10772 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10773 if (mng_info == (MngInfo *) NULL)
10774 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10776 Initialize members of the MngInfo structure.
10778 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10779 mng_info->image=image;
10780 have_mng_structure=MagickTrue;
10782 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
10784 status=WriteOneJNGImage(mng_info,image_info,image);
10785 (void) CloseBlob(image);
10787 (void) CatchImageException(image);
10788 MngInfoFreeStruct(mng_info,&have_mng_structure);
10789 if (logging != MagickFalse)
10790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
10797 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
10806 have_mng_structure,
10809 volatile MagickBooleanType
10821 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
10822 defined(PNG_MNG_FEATURES_SUPPORTED)
10825 all_images_are_gray,
10835 volatile unsigned int
10846 #if (PNG_LIBPNG_VER < 10200)
10847 if (image_info->verbose)
10848 printf("Your PNG library (libpng-%s) is rather old.\n",
10849 PNG_LIBPNG_VER_STRING);
10855 assert(image_info != (const ImageInfo *) NULL);
10856 assert(image_info->signature == MagickSignature);
10857 assert(image != (Image *) NULL);
10858 assert(image->signature == MagickSignature);
10859 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10860 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
10861 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
10862 if (status == MagickFalse)
10866 Allocate a MngInfo structure.
10868 have_mng_structure=MagickFalse;
10869 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10870 if (mng_info == (MngInfo *) NULL)
10871 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10873 Initialize members of the MngInfo structure.
10875 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10876 mng_info->image=image;
10877 have_mng_structure=MagickTrue;
10878 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
10881 * See if user has requested a specific PNG subformat to be used
10882 * for all of the PNGs in the MNG being written, e.g.,
10884 * convert *.png png8:animation.mng
10886 * To do: check -define png:bit_depth and png:color_type as well,
10887 * or perhaps use mng:bit_depth and mng:color_type instead for
10891 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10892 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10893 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10895 write_jng=MagickFalse;
10896 if (image_info->compression == JPEGCompression)
10897 write_jng=MagickTrue;
10899 mng_info->adjoin=image_info->adjoin &&
10900 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
10902 if (logging != MagickFalse)
10904 /* Log some info about the input */
10908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10909 " Checking input image(s)");
10911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10912 " Image_info depth: %.20g",(double) image_info->depth);
10914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10915 " Type: %d",image_info->type);
10918 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
10920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10921 " Scene: %.20g",(double) scene++);
10923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10924 " Image depth: %.20g",(double) p->depth);
10927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10934 if (p->storage_class == PseudoClass)
10935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10936 " Storage class: PseudoClass");
10939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10940 " Storage class: DirectClass");
10943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10944 " Number of colors: %.20g",(double) p->colors);
10947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10948 " Number of colors: unspecified");
10950 if (mng_info->adjoin == MagickFalse)
10955 use_global_plte=MagickFalse;
10956 all_images_are_gray=MagickFalse;
10957 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
10958 need_local_plte=MagickTrue;
10960 need_defi=MagickFalse;
10961 need_matte=MagickFalse;
10962 mng_info->framing_mode=1;
10963 mng_info->old_framing_mode=1;
10966 if (image_info->page != (char *) NULL)
10969 Determine image bounding box.
10971 SetGeometry(image,&mng_info->page);
10972 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
10973 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
10985 mng_info->page=image->page;
10986 need_geom=MagickTrue;
10987 if (mng_info->page.width || mng_info->page.height)
10988 need_geom=MagickFalse;
10990 Check all the scenes.
10992 initial_delay=image->delay;
10993 need_iterations=MagickFalse;
10994 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
10995 mng_info->equal_physs=MagickTrue,
10996 mng_info->equal_gammas=MagickTrue;
10997 mng_info->equal_srgbs=MagickTrue;
10998 mng_info->equal_backgrounds=MagickTrue;
11000 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11001 defined(PNG_MNG_FEATURES_SUPPORTED)
11002 all_images_are_gray=MagickTrue;
11003 mng_info->equal_palettes=MagickFalse;
11004 need_local_plte=MagickFalse;
11006 for (next_image=image; next_image != (Image *) NULL; )
11010 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11011 mng_info->page.width=next_image->columns+next_image->page.x;
11013 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11014 mng_info->page.height=next_image->rows+next_image->page.y;
11017 if (next_image->page.x || next_image->page.y)
11018 need_defi=MagickTrue;
11020 if (next_image->matte)
11021 need_matte=MagickTrue;
11023 if ((int) next_image->dispose >= BackgroundDispose)
11024 if (next_image->matte || next_image->page.x || next_image->page.y ||
11025 ((next_image->columns < mng_info->page.width) &&
11026 (next_image->rows < mng_info->page.height)))
11027 mng_info->need_fram=MagickTrue;
11029 if (next_image->iterations)
11030 need_iterations=MagickTrue;
11032 final_delay=next_image->delay;
11034 if (final_delay != initial_delay || final_delay > 1UL*
11035 next_image->ticks_per_second)
11036 mng_info->need_fram=1;
11038 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11039 defined(PNG_MNG_FEATURES_SUPPORTED)
11041 check for global palette possibility.
11043 if (image->matte != MagickFalse)
11044 need_local_plte=MagickTrue;
11046 if (need_local_plte == 0)
11048 if (ImageIsGray(image) == MagickFalse)
11049 all_images_are_gray=MagickFalse;
11050 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11051 if (use_global_plte == 0)
11052 use_global_plte=mng_info->equal_palettes;
11053 need_local_plte=!mng_info->equal_palettes;
11056 if (GetNextImageInList(next_image) != (Image *) NULL)
11058 if (next_image->background_color.red !=
11059 next_image->next->background_color.red ||
11060 next_image->background_color.green !=
11061 next_image->next->background_color.green ||
11062 next_image->background_color.blue !=
11063 next_image->next->background_color.blue)
11064 mng_info->equal_backgrounds=MagickFalse;
11066 if (next_image->gamma != next_image->next->gamma)
11067 mng_info->equal_gammas=MagickFalse;
11069 if (next_image->rendering_intent !=
11070 next_image->next->rendering_intent)
11071 mng_info->equal_srgbs=MagickFalse;
11073 if ((next_image->units != next_image->next->units) ||
11074 (next_image->x_resolution != next_image->next->x_resolution) ||
11075 (next_image->y_resolution != next_image->next->y_resolution))
11076 mng_info->equal_physs=MagickFalse;
11078 if (mng_info->equal_chrms)
11080 if (next_image->chromaticity.red_primary.x !=
11081 next_image->next->chromaticity.red_primary.x ||
11082 next_image->chromaticity.red_primary.y !=
11083 next_image->next->chromaticity.red_primary.y ||
11084 next_image->chromaticity.green_primary.x !=
11085 next_image->next->chromaticity.green_primary.x ||
11086 next_image->chromaticity.green_primary.y !=
11087 next_image->next->chromaticity.green_primary.y ||
11088 next_image->chromaticity.blue_primary.x !=
11089 next_image->next->chromaticity.blue_primary.x ||
11090 next_image->chromaticity.blue_primary.y !=
11091 next_image->next->chromaticity.blue_primary.y ||
11092 next_image->chromaticity.white_point.x !=
11093 next_image->next->chromaticity.white_point.x ||
11094 next_image->chromaticity.white_point.y !=
11095 next_image->next->chromaticity.white_point.y)
11096 mng_info->equal_chrms=MagickFalse;
11100 next_image=GetNextImageInList(next_image);
11102 if (image_count < 2)
11104 mng_info->equal_backgrounds=MagickFalse;
11105 mng_info->equal_chrms=MagickFalse;
11106 mng_info->equal_gammas=MagickFalse;
11107 mng_info->equal_srgbs=MagickFalse;
11108 mng_info->equal_physs=MagickFalse;
11109 use_global_plte=MagickFalse;
11110 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11111 need_local_plte=MagickTrue;
11113 need_iterations=MagickFalse;
11116 if (mng_info->need_fram == MagickFalse)
11119 Only certain framing rates 100/n are exactly representable without
11120 the FRAM chunk but we'll allow some slop in VLC files
11122 if (final_delay == 0)
11124 if (need_iterations != MagickFalse)
11127 It's probably a GIF with loop; don't run it *too* fast.
11129 if (mng_info->adjoin)
11132 (void) ThrowMagickException(&image->exception,
11133 GetMagickModule(),CoderWarning,
11134 "input has zero delay between all frames; assuming",
11139 mng_info->ticks_per_second=0;
11141 if (final_delay != 0)
11142 mng_info->ticks_per_second=(png_uint_32)
11143 (image->ticks_per_second/final_delay);
11144 if (final_delay > 50)
11145 mng_info->ticks_per_second=2;
11147 if (final_delay > 75)
11148 mng_info->ticks_per_second=1;
11150 if (final_delay > 125)
11151 mng_info->need_fram=MagickTrue;
11153 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11154 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11155 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11156 1UL*image->ticks_per_second))
11157 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11160 if (mng_info->need_fram != MagickFalse)
11161 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11163 If pseudocolor, we should also check to see if all the
11164 palettes are identical and write a global PLTE if they are.
11168 Write the MNG version 1.0 signature and MHDR chunk.
11170 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11171 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11172 PNGType(chunk,mng_MHDR);
11173 LogPNGChunk(logging,mng_MHDR,28L);
11174 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11175 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11176 PNGLong(chunk+12,mng_info->ticks_per_second);
11177 PNGLong(chunk+16,0L); /* layer count=unknown */
11178 PNGLong(chunk+20,0L); /* frame count=unknown */
11179 PNGLong(chunk+24,0L); /* play time=unknown */
11184 if (need_defi || mng_info->need_fram || use_global_plte)
11185 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
11188 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11193 if (need_defi || mng_info->need_fram || use_global_plte)
11194 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
11197 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11205 if (need_defi || mng_info->need_fram || use_global_plte)
11206 PNGLong(chunk+28,11L); /* simplicity=LC */
11209 PNGLong(chunk+28,9L); /* simplicity=VLC */
11214 if (need_defi || mng_info->need_fram || use_global_plte)
11215 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
11218 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11221 (void) WriteBlob(image,32,chunk);
11222 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11223 option=GetImageOption(image_info,"mng:need-cacheoff");
11224 if (option != (const char *) NULL)
11230 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11232 PNGType(chunk,mng_nEED);
11233 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11234 (void) WriteBlobMSBULong(image,(size_t) length);
11235 LogPNGChunk(logging,mng_nEED,(size_t) length);
11237 (void) WriteBlob(image,length,chunk);
11238 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11240 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11241 (GetNextImageInList(image) != (Image *) NULL) &&
11242 (image->iterations != 1))
11245 Write MNG TERM chunk
11247 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11248 PNGType(chunk,mng_TERM);
11249 LogPNGChunk(logging,mng_TERM,10L);
11250 chunk[4]=3; /* repeat animation */
11251 chunk[5]=0; /* show last frame when done */
11252 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11253 final_delay/MagickMax(image->ticks_per_second,1)));
11255 if (image->iterations == 0)
11256 PNGLong(chunk+10,PNG_UINT_31_MAX);
11259 PNGLong(chunk+10,(png_uint_32) image->iterations);
11261 if (logging != MagickFalse)
11263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11264 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11265 final_delay/MagickMax(image->ticks_per_second,1)));
11267 if (image->iterations == 0)
11268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11269 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11273 " Image iterations: %.20g",(double) image->iterations);
11275 (void) WriteBlob(image,14,chunk);
11276 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11279 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11281 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11282 mng_info->equal_srgbs)
11285 Write MNG sRGB chunk
11287 (void) WriteBlobMSBULong(image,1L);
11288 PNGType(chunk,mng_sRGB);
11289 LogPNGChunk(logging,mng_sRGB,1L);
11291 if (image->rendering_intent != UndefinedIntent)
11292 chunk[4]=(unsigned char)
11293 Magick_RenderingIntent_to_PNG_RenderingIntent(
11294 (image->rendering_intent));
11297 chunk[4]=(unsigned char)
11298 Magick_RenderingIntent_to_PNG_RenderingIntent(
11299 (PerceptualIntent));
11301 (void) WriteBlob(image,5,chunk);
11302 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11303 mng_info->have_write_global_srgb=MagickTrue;
11308 if (image->gamma && mng_info->equal_gammas)
11311 Write MNG gAMA chunk
11313 (void) WriteBlobMSBULong(image,4L);
11314 PNGType(chunk,mng_gAMA);
11315 LogPNGChunk(logging,mng_gAMA,4L);
11316 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11317 (void) WriteBlob(image,8,chunk);
11318 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11319 mng_info->have_write_global_gama=MagickTrue;
11321 if (mng_info->equal_chrms)
11327 Write MNG cHRM chunk
11329 (void) WriteBlobMSBULong(image,32L);
11330 PNGType(chunk,mng_cHRM);
11331 LogPNGChunk(logging,mng_cHRM,32L);
11332 primary=image->chromaticity.white_point;
11333 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11334 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11335 primary=image->chromaticity.red_primary;
11336 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11337 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11338 primary=image->chromaticity.green_primary;
11339 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11340 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11341 primary=image->chromaticity.blue_primary;
11342 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11343 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11344 (void) WriteBlob(image,36,chunk);
11345 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11346 mng_info->have_write_global_chrm=MagickTrue;
11349 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11352 Write MNG pHYs chunk
11354 (void) WriteBlobMSBULong(image,9L);
11355 PNGType(chunk,mng_pHYs);
11356 LogPNGChunk(logging,mng_pHYs,9L);
11358 if (image->units == PixelsPerInchResolution)
11360 PNGLong(chunk+4,(png_uint_32)
11361 (image->x_resolution*100.0/2.54+0.5));
11363 PNGLong(chunk+8,(png_uint_32)
11364 (image->y_resolution*100.0/2.54+0.5));
11371 if (image->units == PixelsPerCentimeterResolution)
11373 PNGLong(chunk+4,(png_uint_32)
11374 (image->x_resolution*100.0+0.5));
11376 PNGLong(chunk+8,(png_uint_32)
11377 (image->y_resolution*100.0+0.5));
11384 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11385 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11389 (void) WriteBlob(image,13,chunk);
11390 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11393 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11394 or does not cover the entire frame.
11396 if (write_mng && (image->matte || image->page.x > 0 ||
11397 image->page.y > 0 || (image->page.width &&
11398 (image->page.width+image->page.x < mng_info->page.width))
11399 || (image->page.height && (image->page.height+image->page.y
11400 < mng_info->page.height))))
11402 (void) WriteBlobMSBULong(image,6L);
11403 PNGType(chunk,mng_BACK);
11404 LogPNGChunk(logging,mng_BACK,6L);
11405 red=ScaleQuantumToShort(image->background_color.red);
11406 green=ScaleQuantumToShort(image->background_color.green);
11407 blue=ScaleQuantumToShort(image->background_color.blue);
11408 PNGShort(chunk+4,red);
11409 PNGShort(chunk+6,green);
11410 PNGShort(chunk+8,blue);
11411 (void) WriteBlob(image,10,chunk);
11412 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11413 if (mng_info->equal_backgrounds)
11415 (void) WriteBlobMSBULong(image,6L);
11416 PNGType(chunk,mng_bKGD);
11417 LogPNGChunk(logging,mng_bKGD,6L);
11418 (void) WriteBlob(image,10,chunk);
11419 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11423 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11424 if ((need_local_plte == MagickFalse) &&
11425 (image->storage_class == PseudoClass) &&
11426 (all_images_are_gray == MagickFalse))
11432 Write MNG PLTE chunk
11434 data_length=3*image->colors;
11435 (void) WriteBlobMSBULong(image,data_length);
11436 PNGType(chunk,mng_PLTE);
11437 LogPNGChunk(logging,mng_PLTE,data_length);
11439 for (i=0; i < (ssize_t) image->colors; i++)
11441 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11442 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11443 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11446 (void) WriteBlob(image,data_length+4,chunk);
11447 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11448 mng_info->have_write_global_plte=MagickTrue;
11454 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11455 defined(PNG_MNG_FEATURES_SUPPORTED)
11456 mng_info->equal_palettes=MagickFalse;
11460 if (mng_info->adjoin)
11462 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11463 defined(PNG_MNG_FEATURES_SUPPORTED)
11465 If we aren't using a global palette for the entire MNG, check to
11466 see if we can use one for two or more consecutive images.
11468 if (need_local_plte && use_global_plte && !all_images_are_gray)
11470 if (mng_info->IsPalette)
11473 When equal_palettes is true, this image has the same palette
11474 as the previous PseudoClass image
11476 mng_info->have_write_global_plte=mng_info->equal_palettes;
11477 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11478 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11481 Write MNG PLTE chunk
11486 data_length=3*image->colors;
11487 (void) WriteBlobMSBULong(image,data_length);
11488 PNGType(chunk,mng_PLTE);
11489 LogPNGChunk(logging,mng_PLTE,data_length);
11491 for (i=0; i < (ssize_t) image->colors; i++)
11493 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11494 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11495 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11498 (void) WriteBlob(image,data_length+4,chunk);
11499 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11500 (uInt) (data_length+4)));
11501 mng_info->have_write_global_plte=MagickTrue;
11505 mng_info->have_write_global_plte=MagickFalse;
11516 previous_x=mng_info->page.x;
11517 previous_y=mng_info->page.y;
11524 mng_info->page=image->page;
11525 if ((mng_info->page.x != previous_x) ||
11526 (mng_info->page.y != previous_y))
11528 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11529 PNGType(chunk,mng_DEFI);
11530 LogPNGChunk(logging,mng_DEFI,12L);
11531 chunk[4]=0; /* object 0 MSB */
11532 chunk[5]=0; /* object 0 LSB */
11533 chunk[6]=0; /* visible */
11534 chunk[7]=0; /* abstract */
11535 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11536 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11537 (void) WriteBlob(image,16,chunk);
11538 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11543 mng_info->write_mng=write_mng;
11545 if ((int) image->dispose >= 3)
11546 mng_info->framing_mode=3;
11548 if (mng_info->need_fram && mng_info->adjoin &&
11549 ((image->delay != mng_info->delay) ||
11550 (mng_info->framing_mode != mng_info->old_framing_mode)))
11552 if (image->delay == mng_info->delay)
11555 Write a MNG FRAM chunk with the new framing mode.
11557 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11558 PNGType(chunk,mng_FRAM);
11559 LogPNGChunk(logging,mng_FRAM,1L);
11560 chunk[4]=(unsigned char) mng_info->framing_mode;
11561 (void) WriteBlob(image,5,chunk);
11562 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11567 Write a MNG FRAM chunk with the delay.
11569 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11570 PNGType(chunk,mng_FRAM);
11571 LogPNGChunk(logging,mng_FRAM,10L);
11572 chunk[4]=(unsigned char) mng_info->framing_mode;
11573 chunk[5]=0; /* frame name separator (no name) */
11574 chunk[6]=2; /* flag for changing default delay */
11575 chunk[7]=0; /* flag for changing frame timeout */
11576 chunk[8]=0; /* flag for changing frame clipping */
11577 chunk[9]=0; /* flag for changing frame sync_id */
11578 PNGLong(chunk+10,(png_uint_32)
11579 ((mng_info->ticks_per_second*
11580 image->delay)/MagickMax(image->ticks_per_second,1)));
11581 (void) WriteBlob(image,14,chunk);
11582 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11583 mng_info->delay=(png_uint_32) image->delay;
11585 mng_info->old_framing_mode=mng_info->framing_mode;
11588 #if defined(JNG_SUPPORTED)
11589 if (image_info->compression == JPEGCompression)
11594 if (logging != MagickFalse)
11595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11596 " Writing JNG object.");
11597 /* To do: specify the desired alpha compression method. */
11598 write_info=CloneImageInfo(image_info);
11599 write_info->compression=UndefinedCompression;
11600 status=WriteOneJNGImage(mng_info,write_info,image);
11601 write_info=DestroyImageInfo(write_info);
11606 if (logging != MagickFalse)
11607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11608 " Writing PNG object.");
11610 mng_info->need_blob = MagickFalse;
11612 /* We don't want any ancillary chunks written */
11613 mng_info->ping_exclude_bKGD=MagickTrue;
11614 mng_info->ping_exclude_cHRM=MagickTrue;
11615 mng_info->ping_exclude_EXIF=MagickTrue;
11616 mng_info->ping_exclude_gAMA=MagickTrue;
11617 mng_info->ping_exclude_cHRM=MagickTrue;
11618 mng_info->ping_exclude_iCCP=MagickTrue;
11619 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11620 mng_info->ping_exclude_oFFs=MagickTrue;
11621 mng_info->ping_exclude_pHYs=MagickTrue;
11622 mng_info->ping_exclude_sRGB=MagickTrue;
11623 mng_info->ping_exclude_tEXt=MagickTrue;
11624 mng_info->ping_exclude_tRNS=MagickTrue;
11625 mng_info->ping_exclude_vpAg=MagickTrue;
11626 mng_info->ping_exclude_zCCP=MagickTrue;
11627 mng_info->ping_exclude_zTXt=MagickTrue;
11629 status=WriteOnePNGImage(mng_info,image_info,image);
11632 if (status == MagickFalse)
11634 MngInfoFreeStruct(mng_info,&have_mng_structure);
11635 (void) CloseBlob(image);
11636 return(MagickFalse);
11638 (void) CatchImageException(image);
11639 if (GetNextImageInList(image) == (Image *) NULL)
11641 image=SyncNextImageInList(image);
11642 status=SetImageProgress(image,SaveImagesTag,scene++,
11643 GetImageListLength(image));
11645 if (status == MagickFalse)
11648 } while (mng_info->adjoin);
11652 while (GetPreviousImageInList(image) != (Image *) NULL)
11653 image=GetPreviousImageInList(image);
11655 Write the MEND chunk.
11657 (void) WriteBlobMSBULong(image,0x00000000L);
11658 PNGType(chunk,mng_MEND);
11659 LogPNGChunk(logging,mng_MEND,0L);
11660 (void) WriteBlob(image,4,chunk);
11661 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11664 Relinquish resources.
11666 (void) CloseBlob(image);
11667 MngInfoFreeStruct(mng_info,&have_mng_structure);
11669 if (logging != MagickFalse)
11670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11672 return(MagickTrue);
11674 #else /* PNG_LIBPNG_VER > 10011 */
11676 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11679 printf("Your PNG library is too old: You have libpng-%s\n",
11680 PNG_LIBPNG_VER_STRING);
11682 ThrowBinaryException(CoderError,"PNG library is too old",
11683 image_info->filename);
11686 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11688 return(WritePNGImage(image_info,image));
11690 #endif /* PNG_LIBPNG_VER > 10011 */