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);
2885 (void) SetImageProperty(image,text[i].key,value);
2887 if (logging != MagickFalse)
2889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2890 " length: %lu",(unsigned long) length);
2891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2892 " Keyword: %s",text[i].key);
2895 value=DestroyString(value);
2899 #ifdef MNG_OBJECT_BUFFERS
2901 Store the object if necessary.
2903 if (object_id && !mng_info->frozen[object_id])
2905 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2908 create a new object buffer.
2910 mng_info->ob[object_id]=(MngBuffer *)
2911 AcquireMagickMemory(sizeof(MngBuffer));
2913 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2915 mng_info->ob[object_id]->image=(Image *) NULL;
2916 mng_info->ob[object_id]->reference_count=1;
2920 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2921 mng_info->ob[object_id]->frozen)
2923 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2924 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2925 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2928 if (mng_info->ob[object_id]->frozen)
2929 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2930 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2931 "`%s'",image->filename);
2937 if (mng_info->ob[object_id]->image != (Image *) NULL)
2938 mng_info->ob[object_id]->image=DestroyImage
2939 (mng_info->ob[object_id]->image);
2941 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2944 if (mng_info->ob[object_id]->image != (Image *) NULL)
2945 mng_info->ob[object_id]->image->file=(FILE *) NULL;
2948 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2949 ResourceLimitError,"Cloning image for object buffer failed",
2950 "`%s'",image->filename);
2952 if (ping_width > 250000L || ping_height > 250000L)
2953 png_error(ping,"PNG Image dimensions are too large.");
2955 mng_info->ob[object_id]->width=ping_width;
2956 mng_info->ob[object_id]->height=ping_height;
2957 mng_info->ob[object_id]->color_type=ping_color_type;
2958 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
2959 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
2960 mng_info->ob[object_id]->compression_method=
2961 ping_compression_method;
2962 mng_info->ob[object_id]->filter_method=ping_filter_method;
2964 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2973 Copy the PLTE to the object buffer.
2975 png_get_PLTE(ping,ping_info,&plte,&number_colors);
2976 mng_info->ob[object_id]->plte_length=number_colors;
2978 for (i=0; i < number_colors; i++)
2980 mng_info->ob[object_id]->plte[i]=plte[i];
2985 mng_info->ob[object_id]->plte_length=0;
2990 Relinquish resources.
2992 png_destroy_read_struct(&ping,&ping_info,&end_info);
2994 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2995 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2996 UnlockSemaphoreInfo(ping_semaphore);
2999 if (logging != MagickFalse)
3000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3001 " exit ReadOnePNGImage()");
3005 /* end of reading one PNG image */
3008 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3023 magic_number[MaxTextExtent];
3031 assert(image_info != (const ImageInfo *) NULL);
3032 assert(image_info->signature == MagickSignature);
3034 if (image_info->debug != MagickFalse)
3035 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3036 image_info->filename);
3038 assert(exception != (ExceptionInfo *) NULL);
3039 assert(exception->signature == MagickSignature);
3040 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3041 image=AcquireImage(image_info);
3042 mng_info=(MngInfo *) NULL;
3043 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3045 if (status == MagickFalse)
3046 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3049 Verify PNG signature.
3051 count=ReadBlob(image,8,(unsigned char *) magic_number);
3053 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3054 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3057 Allocate a MngInfo structure.
3059 have_mng_structure=MagickFalse;
3060 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3062 if (mng_info == (MngInfo *) NULL)
3063 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3066 Initialize members of the MngInfo structure.
3068 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3069 mng_info->image=image;
3070 have_mng_structure=MagickTrue;
3073 image=ReadOnePNGImage(mng_info,image_info,exception);
3074 MngInfoFreeStruct(mng_info,&have_mng_structure);
3076 if (image == (Image *) NULL)
3078 if (previous != (Image *) NULL)
3080 if (previous->signature != MagickSignature)
3081 ThrowReaderException(CorruptImageError,"CorruptImage");
3083 (void) CloseBlob(previous);
3084 (void) DestroyImageList(previous);
3087 if (logging != MagickFalse)
3088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3089 "exit ReadPNGImage() with error");
3091 return((Image *) NULL);
3094 (void) CloseBlob(image);
3096 if ((image->columns == 0) || (image->rows == 0))
3098 if (logging != MagickFalse)
3099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3100 "exit ReadPNGImage() with error.");
3102 ThrowReaderException(CorruptImageError,"CorruptImage");
3105 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3107 (void) SetImageType(image,PaletteType);
3109 if (image->matte != MagickFalse)
3111 /* To do: Reduce to binary transparency */
3115 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3117 (void) SetImageType(image,TrueColorType);
3118 image->matte=MagickFalse;
3121 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3122 (void) SetImageType(image,TrueColorMatteType);
3124 if (logging != MagickFalse)
3125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3126 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3127 (double) image->page.width,(double) image->page.height,
3128 (double) image->page.x,(double) image->page.y);
3130 if (logging != MagickFalse)
3131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3138 #if defined(JNG_SUPPORTED)
3140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3144 % R e a d O n e J N G I m a g e %
3148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3150 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3151 % (minus the 8-byte signature) and returns it. It allocates the memory
3152 % necessary for the new Image structure and returns a pointer to the new
3155 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3157 % The format of the ReadOneJNGImage method is:
3159 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3160 % ExceptionInfo *exception)
3162 % A description of each parameter follows:
3164 % o mng_info: Specifies a pointer to a MngInfo structure.
3166 % o image_info: the image info.
3168 % o exception: return any errors or warnings in this structure.
3171 static Image *ReadOneJNGImage(MngInfo *mng_info,
3172 const ImageInfo *image_info, ExceptionInfo *exception)
3199 jng_image_sample_depth,
3200 jng_image_compression_method,
3201 jng_image_interlace_method,
3202 jng_alpha_sample_depth,
3203 jng_alpha_compression_method,
3204 jng_alpha_filter_method,
3205 jng_alpha_interlace_method;
3207 register const PixelPacket
3214 register PixelPacket
3217 register unsigned char
3228 jng_alpha_compression_method=0;
3229 jng_alpha_sample_depth=8;
3233 alpha_image=(Image *) NULL;
3234 color_image=(Image *) NULL;
3235 alpha_image_info=(ImageInfo *) NULL;
3236 color_image_info=(ImageInfo *) NULL;
3238 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3239 " Enter ReadOneJNGImage()");
3241 image=mng_info->image;
3243 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3246 Allocate next image structure.
3248 if (logging != MagickFalse)
3249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3250 " AcquireNextImage()");
3252 AcquireNextImage(image_info,image);
3254 if (GetNextImageInList(image) == (Image *) NULL)
3255 return((Image *) NULL);
3257 image=SyncNextImageInList(image);
3259 mng_info->image=image;
3262 Signature bytes have already been read.
3265 read_JSEP=MagickFalse;
3266 reading_idat=MagickFalse;
3267 skip_to_iend=MagickFalse;
3271 type[MaxTextExtent];
3280 Read a new JNG chunk.
3282 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3283 2*GetBlobSize(image));
3285 if (status == MagickFalse)
3289 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3290 length=ReadBlobMSBLong(image);
3291 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3293 if (logging != MagickFalse)
3294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3295 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3296 type[0],type[1],type[2],type[3],(double) length);
3298 if (length > PNG_UINT_31_MAX || count == 0)
3299 ThrowReaderException(CorruptImageError,"CorruptImage");
3302 chunk=(unsigned char *) NULL;
3306 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3308 if (chunk == (unsigned char *) NULL)
3309 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3311 for (i=0; i < (ssize_t) length; i++)
3312 chunk[i]=(unsigned char) ReadBlobByte(image);
3317 (void) ReadBlobMSBLong(image); /* read crc word */
3322 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3327 if (memcmp(type,mng_JHDR,4) == 0)
3331 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3332 (p[2] << 8) | p[3]);
3333 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3334 (p[6] << 8) | p[7]);
3335 jng_color_type=p[8];
3336 jng_image_sample_depth=p[9];
3337 jng_image_compression_method=p[10];
3338 jng_image_interlace_method=p[11];
3340 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3343 jng_alpha_sample_depth=p[12];
3344 jng_alpha_compression_method=p[13];
3345 jng_alpha_filter_method=p[14];
3346 jng_alpha_interlace_method=p[15];
3348 if (logging != MagickFalse)
3350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3351 " jng_width: %16lu",(unsigned long) jng_width);
3353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3354 " jng_width: %16lu",(unsigned long) jng_height);
3356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3357 " jng_color_type: %16d",jng_color_type);
3359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3360 " jng_image_sample_depth: %3d",
3361 jng_image_sample_depth);
3363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3364 " jng_image_compression_method:%3d",
3365 jng_image_compression_method);
3367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3368 " jng_image_interlace_method: %3d",
3369 jng_image_interlace_method);
3371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3372 " jng_alpha_sample_depth: %3d",
3373 jng_alpha_sample_depth);
3375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3376 " jng_alpha_compression_method:%3d",
3377 jng_alpha_compression_method);
3379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3380 " jng_alpha_filter_method: %3d",
3381 jng_alpha_filter_method);
3383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3384 " jng_alpha_interlace_method: %3d",
3385 jng_alpha_interlace_method);
3390 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3396 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3397 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3398 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3401 o create color_image
3402 o open color_blob, attached to color_image
3403 o if (color type has alpha)
3404 open alpha_blob, attached to alpha_image
3407 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3409 if (color_image_info == (ImageInfo *) NULL)
3410 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3412 GetImageInfo(color_image_info);
3413 color_image=AcquireImage(color_image_info);
3415 if (color_image == (Image *) NULL)
3416 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3418 if (logging != MagickFalse)
3419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3420 " Creating color_blob.");
3422 (void) AcquireUniqueFilename(color_image->filename);
3423 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3426 if (status == MagickFalse)
3427 return((Image *) NULL);
3429 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3431 alpha_image_info=(ImageInfo *)
3432 AcquireMagickMemory(sizeof(ImageInfo));
3434 if (alpha_image_info == (ImageInfo *) NULL)
3435 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3437 GetImageInfo(alpha_image_info);
3438 alpha_image=AcquireImage(alpha_image_info);
3440 if (alpha_image == (Image *) NULL)
3442 alpha_image=DestroyImage(alpha_image);
3443 ThrowReaderException(ResourceLimitError,
3444 "MemoryAllocationFailed");
3447 if (logging != MagickFalse)
3448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3449 " Creating alpha_blob.");
3451 (void) AcquireUniqueFilename(alpha_image->filename);
3452 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3455 if (status == MagickFalse)
3456 return((Image *) NULL);
3458 if (jng_alpha_compression_method == 0)
3463 if (logging != MagickFalse)
3464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3465 " Writing IHDR chunk to alpha_blob.");
3467 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3468 "\211PNG\r\n\032\n");
3470 (void) WriteBlobMSBULong(alpha_image,13L);
3471 PNGType(data,mng_IHDR);
3472 LogPNGChunk(logging,mng_IHDR,13L);
3473 PNGLong(data+4,jng_width);
3474 PNGLong(data+8,jng_height);
3475 data[12]=jng_alpha_sample_depth;
3476 data[13]=0; /* color_type gray */
3477 data[14]=0; /* compression method 0 */
3478 data[15]=0; /* filter_method 0 */
3479 data[16]=0; /* interlace_method 0 */
3480 (void) WriteBlob(alpha_image,17,data);
3481 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3484 reading_idat=MagickTrue;
3487 if (memcmp(type,mng_JDAT,4) == 0)
3489 /* Copy chunk to color_image->blob */
3491 if (logging != MagickFalse)
3492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3493 " Copying JDAT chunk data to color_blob.");
3495 (void) WriteBlob(color_image,length,chunk);
3498 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3503 if (memcmp(type,mng_IDAT,4) == 0)
3508 /* Copy IDAT header and chunk data to alpha_image->blob */
3510 if (image_info->ping == MagickFalse)
3512 if (logging != MagickFalse)
3513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3514 " Copying IDAT chunk data to alpha_blob.");
3516 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3517 PNGType(data,mng_IDAT);
3518 LogPNGChunk(logging,mng_IDAT,length);
3519 (void) WriteBlob(alpha_image,4,data);
3520 (void) WriteBlob(alpha_image,length,chunk);
3521 (void) WriteBlobMSBULong(alpha_image,
3522 crc32(crc32(0,data,4),chunk,(uInt) length));
3526 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3531 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3533 /* Copy chunk data to alpha_image->blob */
3535 if (image_info->ping == MagickFalse)
3537 if (logging != MagickFalse)
3538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3539 " Copying JDAA chunk data to alpha_blob.");
3541 (void) WriteBlob(alpha_image,length,chunk);
3545 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3550 if (memcmp(type,mng_JSEP,4) == 0)
3552 read_JSEP=MagickTrue;
3555 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3560 if (memcmp(type,mng_bKGD,4) == 0)
3564 image->background_color.red=ScaleCharToQuantum(p[1]);
3565 image->background_color.green=image->background_color.red;
3566 image->background_color.blue=image->background_color.red;
3571 image->background_color.red=ScaleCharToQuantum(p[1]);
3572 image->background_color.green=ScaleCharToQuantum(p[3]);
3573 image->background_color.blue=ScaleCharToQuantum(p[5]);
3576 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3580 if (memcmp(type,mng_gAMA,4) == 0)
3583 image->gamma=((float) mng_get_long(p))*0.00001;
3585 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3589 if (memcmp(type,mng_cHRM,4) == 0)
3593 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3594 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3595 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3596 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3597 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3598 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3599 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3600 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3603 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3607 if (memcmp(type,mng_sRGB,4) == 0)
3611 image->rendering_intent=
3612 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3613 image->gamma=0.45455f;
3614 image->chromaticity.red_primary.x=0.6400f;
3615 image->chromaticity.red_primary.y=0.3300f;
3616 image->chromaticity.green_primary.x=0.3000f;
3617 image->chromaticity.green_primary.y=0.6000f;
3618 image->chromaticity.blue_primary.x=0.1500f;
3619 image->chromaticity.blue_primary.y=0.0600f;
3620 image->chromaticity.white_point.x=0.3127f;
3621 image->chromaticity.white_point.y=0.3290f;
3624 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3628 if (memcmp(type,mng_oFFs,4) == 0)
3632 image->page.x=(ssize_t) mng_get_long(p);
3633 image->page.y=(ssize_t) mng_get_long(&p[4]);
3635 if ((int) p[8] != 0)
3637 image->page.x/=10000;
3638 image->page.y/=10000;
3643 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3648 if (memcmp(type,mng_pHYs,4) == 0)
3652 image->x_resolution=(double) mng_get_long(p);
3653 image->y_resolution=(double) mng_get_long(&p[4]);
3654 if ((int) p[8] == PNG_RESOLUTION_METER)
3656 image->units=PixelsPerCentimeterResolution;
3657 image->x_resolution=image->x_resolution/100.0f;
3658 image->y_resolution=image->y_resolution/100.0f;
3662 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3667 if (memcmp(type,mng_iCCP,4) == 0)
3671 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3678 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3680 if (memcmp(type,mng_IEND,4))
3690 Finish up reading image data:
3692 o read main image from color_blob.
3696 o if (color_type has alpha)
3697 if alpha_encoding is PNG
3698 read secondary image from alpha_blob via ReadPNG
3699 if alpha_encoding is JPEG
3700 read secondary image from alpha_blob via ReadJPEG
3704 o copy intensity of secondary image into
3705 opacity samples of main image.
3707 o destroy the secondary image.
3710 (void) CloseBlob(color_image);
3712 if (logging != MagickFalse)
3713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3714 " Reading jng_image from color_blob.");
3716 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3717 color_image->filename);
3719 color_image_info->ping=MagickFalse; /* To do: avoid this */
3720 jng_image=ReadImage(color_image_info,exception);
3722 if (jng_image == (Image *) NULL)
3723 return((Image *) NULL);
3725 (void) RelinquishUniqueFileResource(color_image->filename);
3726 color_image=DestroyImage(color_image);
3727 color_image_info=DestroyImageInfo(color_image_info);
3729 if (jng_image == (Image *) NULL)
3730 return((Image *) NULL);
3732 if (logging != MagickFalse)
3733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3734 " Copying jng_image pixels to main image.");
3736 image->rows=jng_height;
3737 image->columns=jng_width;
3738 length=image->columns*sizeof(PixelPacket);
3740 for (y=0; y < (ssize_t) image->rows; y++)
3742 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3743 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3744 (void) CopyMagickMemory(q,s,length);
3746 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3750 jng_image=DestroyImage(jng_image);
3752 if (image_info->ping == MagickFalse)
3754 if (jng_color_type >= 12)
3756 if (jng_alpha_compression_method == 0)
3760 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3761 PNGType(data,mng_IEND);
3762 LogPNGChunk(logging,mng_IEND,0L);
3763 (void) WriteBlob(alpha_image,4,data);
3764 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3767 (void) CloseBlob(alpha_image);
3769 if (logging != MagickFalse)
3770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3771 " Reading opacity from alpha_blob.");
3773 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3774 "%s",alpha_image->filename);
3776 jng_image=ReadImage(alpha_image_info,exception);
3778 if (jng_image != (Image *) NULL)
3779 for (y=0; y < (ssize_t) image->rows; y++)
3781 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3783 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3785 if (image->matte != MagickFalse)
3786 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3787 q->opacity=(Quantum) QuantumRange-s->red;
3790 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3792 q->opacity=(Quantum) QuantumRange-s->red;
3793 if (q->opacity != OpaqueOpacity)
3794 image->matte=MagickTrue;
3797 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3800 (void) RelinquishUniqueFileResource(alpha_image->filename);
3801 alpha_image=DestroyImage(alpha_image);
3802 alpha_image_info=DestroyImageInfo(alpha_image_info);
3803 if (jng_image != (Image *) NULL)
3804 jng_image=DestroyImage(jng_image);
3808 /* Read the JNG image. */
3810 if (mng_info->mng_type == 0)
3812 mng_info->mng_width=jng_width;
3813 mng_info->mng_height=jng_height;
3816 if (image->page.width == 0 && image->page.height == 0)
3818 image->page.width=jng_width;
3819 image->page.height=jng_height;
3822 if (image->page.x == 0 && image->page.y == 0)
3824 image->page.x=mng_info->x_off[mng_info->object_id];
3825 image->page.y=mng_info->y_off[mng_info->object_id];
3830 image->page.y=mng_info->y_off[mng_info->object_id];
3833 mng_info->image_found++;
3834 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3835 2*GetBlobSize(image));
3837 if (logging != MagickFalse)
3838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3839 " exit ReadOneJNGImage()");
3845 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3849 % R e a d J N G I m a g e %
3853 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3855 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3856 % (including the 8-byte signature) and returns it. It allocates the memory
3857 % necessary for the new Image structure and returns a pointer to the new
3860 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3862 % The format of the ReadJNGImage method is:
3864 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3867 % A description of each parameter follows:
3869 % o image_info: the image info.
3871 % o exception: return any errors or warnings in this structure.
3875 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3890 magic_number[MaxTextExtent];
3898 assert(image_info != (const ImageInfo *) NULL);
3899 assert(image_info->signature == MagickSignature);
3900 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3901 assert(exception != (ExceptionInfo *) NULL);
3902 assert(exception->signature == MagickSignature);
3903 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
3904 image=AcquireImage(image_info);
3905 mng_info=(MngInfo *) NULL;
3906 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3908 if (status == MagickFalse)
3909 return((Image *) NULL);
3911 if (LocaleCompare(image_info->magick,"JNG") != 0)
3912 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3914 /* Verify JNG signature. */
3916 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3918 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3919 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3921 /* Allocate a MngInfo structure. */
3923 have_mng_structure=MagickFalse;
3924 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
3926 if (mng_info == (MngInfo *) NULL)
3927 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3929 /* Initialize members of the MngInfo structure. */
3931 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3932 have_mng_structure=MagickTrue;
3934 mng_info->image=image;
3936 image=ReadOneJNGImage(mng_info,image_info,exception);
3937 MngInfoFreeStruct(mng_info,&have_mng_structure);
3939 if (image == (Image *) NULL)
3941 if (IsImageObject(previous) != MagickFalse)
3943 (void) CloseBlob(previous);
3944 (void) DestroyImageList(previous);
3947 if (logging != MagickFalse)
3948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3949 "exit ReadJNGImage() with error");
3951 return((Image *) NULL);
3953 (void) CloseBlob(image);
3955 if (image->columns == 0 || image->rows == 0)
3957 if (logging != MagickFalse)
3958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3959 "exit ReadJNGImage() with error");
3961 ThrowReaderException(CorruptImageError,"CorruptImage");
3964 if (logging != MagickFalse)
3965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3971 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3974 page_geometry[MaxTextExtent];
4007 #if defined(MNG_INSERT_LAYERS)
4009 mng_background_color;
4012 register unsigned char
4027 #if defined(MNG_INSERT_LAYERS)
4032 volatile unsigned int
4033 #ifdef MNG_OBJECT_BUFFERS
4034 mng_background_object=0,
4036 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4039 default_frame_timeout,
4041 #if defined(MNG_INSERT_LAYERS)
4047 /* These delays are all measured in image ticks_per_second,
4048 * not in MNG ticks_per_second
4051 default_frame_delay,
4055 #if defined(MNG_INSERT_LAYERS)
4064 previous_fb.bottom=0;
4066 previous_fb.right=0;
4068 default_fb.bottom=0;
4072 /* Open image file. */
4074 assert(image_info != (const ImageInfo *) NULL);
4075 assert(image_info->signature == MagickSignature);
4076 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4077 assert(exception != (ExceptionInfo *) NULL);
4078 assert(exception->signature == MagickSignature);
4079 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4080 image=AcquireImage(image_info);
4081 mng_info=(MngInfo *) NULL;
4082 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4084 if (status == MagickFalse)
4085 return((Image *) NULL);
4087 first_mng_object=MagickFalse;
4089 have_mng_structure=MagickFalse;
4091 /* Allocate a MngInfo structure. */
4093 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4095 if (mng_info == (MngInfo *) NULL)
4096 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4098 /* Initialize members of the MngInfo structure. */
4100 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4101 mng_info->image=image;
4102 have_mng_structure=MagickTrue;
4104 if (LocaleCompare(image_info->magick,"MNG") == 0)
4107 magic_number[MaxTextExtent];
4109 /* Verify MNG signature. */
4110 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4111 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4112 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4114 /* Initialize some nonzero members of the MngInfo structure. */
4115 for (i=0; i < MNG_MAX_OBJECTS; i++)
4117 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4118 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4120 mng_info->exists[0]=MagickTrue;
4123 first_mng_object=MagickTrue;
4125 #if defined(MNG_INSERT_LAYERS)
4126 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4128 default_frame_delay=0;
4129 default_frame_timeout=0;
4132 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4134 skip_to_iend=MagickFalse;
4135 term_chunk_found=MagickFalse;
4136 mng_info->framing_mode=1;
4137 #if defined(MNG_INSERT_LAYERS)
4138 mandatory_back=MagickFalse;
4140 #if defined(MNG_INSERT_LAYERS)
4141 mng_background_color=image->background_color;
4143 default_fb=mng_info->frame;
4144 previous_fb=mng_info->frame;
4148 type[MaxTextExtent];
4150 if (LocaleCompare(image_info->magick,"MNG") == 0)
4159 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4160 length=ReadBlobMSBLong(image);
4161 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4163 if (logging != MagickFalse)
4164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4165 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4166 type[0],type[1],type[2],type[3],(double) length);
4168 if (length > PNG_UINT_31_MAX)
4172 ThrowReaderException(CorruptImageError,"CorruptImage");
4175 chunk=(unsigned char *) NULL;
4179 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4181 if (chunk == (unsigned char *) NULL)
4182 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4184 for (i=0; i < (ssize_t) length; i++)
4185 chunk[i]=(unsigned char) ReadBlobByte(image);
4190 (void) ReadBlobMSBLong(image); /* read crc word */
4192 #if !defined(JNG_SUPPORTED)
4193 if (memcmp(type,mng_JHDR,4) == 0)
4195 skip_to_iend=MagickTrue;
4197 if (mng_info->jhdr_warning == 0)
4198 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4199 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4201 mng_info->jhdr_warning++;
4204 if (memcmp(type,mng_DHDR,4) == 0)
4206 skip_to_iend=MagickTrue;
4208 if (mng_info->dhdr_warning == 0)
4209 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4210 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4212 mng_info->dhdr_warning++;
4214 if (memcmp(type,mng_MEND,4) == 0)
4219 if (memcmp(type,mng_IEND,4) == 0)
4220 skip_to_iend=MagickFalse;
4223 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4225 if (logging != MagickFalse)
4226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4232 if (memcmp(type,mng_MHDR,4) == 0)
4234 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4235 (p[2] << 8) | p[3]);
4237 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4238 (p[6] << 8) | p[7]);
4240 if (logging != MagickFalse)
4242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4243 " MNG width: %.20g",(double) mng_info->mng_width);
4244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4245 " MNG height: %.20g",(double) mng_info->mng_height);
4249 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4251 if (mng_info->ticks_per_second == 0)
4252 default_frame_delay=0;
4255 default_frame_delay=1UL*image->ticks_per_second/
4256 mng_info->ticks_per_second;
4258 frame_delay=default_frame_delay;
4264 simplicity=(size_t) mng_get_long(p);
4267 mng_type=1; /* Full MNG */
4269 if ((simplicity != 0) && ((simplicity | 11) == 11))
4270 mng_type=2; /* LC */
4272 if ((simplicity != 0) && ((simplicity | 9) == 9))
4273 mng_type=3; /* VLC */
4275 #if defined(MNG_INSERT_LAYERS)
4277 insert_layers=MagickTrue;
4279 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4281 /* Allocate next image structure. */
4282 AcquireNextImage(image_info,image);
4284 if (GetNextImageInList(image) == (Image *) NULL)
4285 return((Image *) NULL);
4287 image=SyncNextImageInList(image);
4288 mng_info->image=image;
4291 if ((mng_info->mng_width > 65535L) ||
4292 (mng_info->mng_height > 65535L))
4293 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4295 (void) FormatMagickString(page_geometry,MaxTextExtent,
4296 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4297 mng_info->mng_height);
4299 mng_info->frame.left=0;
4300 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4301 mng_info->frame.top=0;
4302 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4303 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4305 for (i=0; i < MNG_MAX_OBJECTS; i++)
4306 mng_info->object_clip[i]=mng_info->frame;
4308 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4312 if (memcmp(type,mng_TERM,4) == 0)
4323 final_delay=(png_uint_32) mng_get_long(&p[2]);
4324 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4326 if (mng_iterations == PNG_UINT_31_MAX)
4329 image->iterations=mng_iterations;
4330 term_chunk_found=MagickTrue;
4333 if (logging != MagickFalse)
4335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4336 " repeat=%d",repeat);
4338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4339 " final_delay=%.20g",(double) final_delay);
4341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4342 " image->iterations=%.20g",(double) image->iterations);
4345 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4348 if (memcmp(type,mng_DEFI,4) == 0)
4351 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4352 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4355 object_id=(p[0] << 8) | p[1];
4357 if (mng_type == 2 && object_id != 0)
4358 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4359 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4362 if (object_id > MNG_MAX_OBJECTS)
4365 Instead ofsuing a warning we should allocate a larger
4366 MngInfo structure and continue.
4368 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4369 CoderError,"object id too large","`%s'",image->filename);
4370 object_id=MNG_MAX_OBJECTS;
4373 if (mng_info->exists[object_id])
4374 if (mng_info->frozen[object_id])
4376 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4377 (void) ThrowMagickException(&image->exception,
4378 GetMagickModule(),CoderError,
4379 "DEFI cannot redefine a frozen MNG object","`%s'",
4384 mng_info->exists[object_id]=MagickTrue;
4387 mng_info->invisible[object_id]=p[2];
4390 Extract object offset info.
4394 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4395 (p[5] << 16) | (p[6] << 8) | p[7]);
4397 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4398 (p[9] << 16) | (p[10] << 8) | p[11]);
4400 if (logging != MagickFalse)
4402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4403 " x_off[%d]: %.20g",object_id,(double)
4404 mng_info->x_off[object_id]);
4406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4407 " y_off[%d]: %.20g",object_id,(double)
4408 mng_info->y_off[object_id]);
4413 Extract object clipping info.
4416 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4419 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4422 if (memcmp(type,mng_bKGD,4) == 0)
4424 mng_info->have_global_bkgd=MagickFalse;
4428 mng_info->mng_global_bkgd.red=
4429 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4431 mng_info->mng_global_bkgd.green=
4432 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4434 mng_info->mng_global_bkgd.blue=
4435 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4437 mng_info->have_global_bkgd=MagickTrue;
4440 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4443 if (memcmp(type,mng_BACK,4) == 0)
4445 #if defined(MNG_INSERT_LAYERS)
4447 mandatory_back=p[6];
4452 if (mandatory_back && length > 5)
4454 mng_background_color.red=
4455 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4457 mng_background_color.green=
4458 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4460 mng_background_color.blue=
4461 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4463 mng_background_color.opacity=OpaqueOpacity;
4466 #ifdef MNG_OBJECT_BUFFERS
4468 mng_background_object=(p[7] << 8) | p[8];
4471 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4475 if (memcmp(type,mng_PLTE,4) == 0)
4477 /* Read global PLTE. */
4479 if (length && (length < 769))
4481 if (mng_info->global_plte == (png_colorp) NULL)
4482 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4483 sizeof(*mng_info->global_plte));
4485 for (i=0; i < (ssize_t) (length/3); i++)
4487 mng_info->global_plte[i].red=p[3*i];
4488 mng_info->global_plte[i].green=p[3*i+1];
4489 mng_info->global_plte[i].blue=p[3*i+2];
4492 mng_info->global_plte_length=(unsigned int) (length/3);
4495 for ( ; i < 256; i++)
4497 mng_info->global_plte[i].red=i;
4498 mng_info->global_plte[i].green=i;
4499 mng_info->global_plte[i].blue=i;
4503 mng_info->global_plte_length=256;
4506 mng_info->global_plte_length=0;
4508 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4512 if (memcmp(type,mng_tRNS,4) == 0)
4514 /* read global tRNS */
4517 for (i=0; i < (ssize_t) length; i++)
4518 mng_info->global_trns[i]=p[i];
4521 for ( ; i < 256; i++)
4522 mng_info->global_trns[i]=255;
4524 mng_info->global_trns_length=(unsigned int) length;
4525 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4528 if (memcmp(type,mng_gAMA,4) == 0)
4535 igamma=mng_get_long(p);
4536 mng_info->global_gamma=((float) igamma)*0.00001;
4537 mng_info->have_global_gama=MagickTrue;
4541 mng_info->have_global_gama=MagickFalse;
4543 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4547 if (memcmp(type,mng_cHRM,4) == 0)
4549 /* Read global cHRM */
4553 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4554 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4555 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4556 mng_info->global_chrm.red_primary.y=0.00001*
4557 mng_get_long(&p[12]);
4558 mng_info->global_chrm.green_primary.x=0.00001*
4559 mng_get_long(&p[16]);
4560 mng_info->global_chrm.green_primary.y=0.00001*
4561 mng_get_long(&p[20]);
4562 mng_info->global_chrm.blue_primary.x=0.00001*
4563 mng_get_long(&p[24]);
4564 mng_info->global_chrm.blue_primary.y=0.00001*
4565 mng_get_long(&p[28]);
4566 mng_info->have_global_chrm=MagickTrue;
4569 mng_info->have_global_chrm=MagickFalse;
4571 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4575 if (memcmp(type,mng_sRGB,4) == 0)
4582 mng_info->global_srgb_intent=
4583 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4584 mng_info->have_global_srgb=MagickTrue;
4587 mng_info->have_global_srgb=MagickFalse;
4589 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4593 if (memcmp(type,mng_iCCP,4) == 0)
4601 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4606 if (memcmp(type,mng_FRAM,4) == 0)
4609 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4610 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4613 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4614 image->delay=frame_delay;
4616 frame_delay=default_frame_delay;
4617 frame_timeout=default_frame_timeout;
4622 mng_info->framing_mode=p[0];
4624 if (logging != MagickFalse)
4625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4626 " Framing_mode=%d",mng_info->framing_mode);
4630 /* Note the delay and frame clipping boundaries. */
4632 p++; /* framing mode */
4634 while (*p && ((p-chunk) < (ssize_t) length))
4635 p++; /* frame name */
4637 p++; /* frame name terminator */
4639 if ((p-chunk) < (ssize_t) (length-4))
4646 change_delay=(*p++);
4647 change_timeout=(*p++);
4648 change_clipping=(*p++);
4649 p++; /* change_sync */
4653 frame_delay=1UL*image->ticks_per_second*
4656 if (mng_info->ticks_per_second != 0)
4657 frame_delay/=mng_info->ticks_per_second;
4660 frame_delay=PNG_UINT_31_MAX;
4662 if (change_delay == 2)
4663 default_frame_delay=frame_delay;
4667 if (logging != MagickFalse)
4668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4669 " Framing_delay=%.20g",(double) frame_delay);
4674 frame_timeout=1UL*image->ticks_per_second*
4677 if (mng_info->ticks_per_second != 0)
4678 frame_timeout/=mng_info->ticks_per_second;
4681 frame_timeout=PNG_UINT_31_MAX;
4683 if (change_delay == 2)
4684 default_frame_timeout=frame_timeout;
4688 if (logging != MagickFalse)
4689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4690 " Framing_timeout=%.20g",(double) frame_timeout);
4693 if (change_clipping)
4695 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4699 if (logging != MagickFalse)
4700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4701 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4702 (double) fb.left,(double) fb.right,(double) fb.top,
4703 (double) fb.bottom);
4705 if (change_clipping == 2)
4711 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4713 subframe_width=(size_t) (mng_info->clip.right
4714 -mng_info->clip.left);
4716 subframe_height=(size_t) (mng_info->clip.bottom
4717 -mng_info->clip.top);
4719 Insert a background layer behind the frame if framing_mode is 4.
4721 #if defined(MNG_INSERT_LAYERS)
4722 if (logging != MagickFalse)
4723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4724 " subframe_width=%.20g, subframe_height=%.20g",(double)
4725 subframe_width,(double) subframe_height);
4727 if (insert_layers && (mng_info->framing_mode == 4) &&
4728 (subframe_width) && (subframe_height))
4730 /* Allocate next image structure. */
4731 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4733 AcquireNextImage(image_info,image);
4735 if (GetNextImageInList(image) == (Image *) NULL)
4737 image=DestroyImageList(image);
4738 MngInfoFreeStruct(mng_info,&have_mng_structure);
4739 return((Image *) NULL);
4742 image=SyncNextImageInList(image);
4745 mng_info->image=image;
4747 if (term_chunk_found)
4749 image->start_loop=MagickTrue;
4750 image->iterations=mng_iterations;
4751 term_chunk_found=MagickFalse;
4755 image->start_loop=MagickFalse;
4757 image->columns=subframe_width;
4758 image->rows=subframe_height;
4759 image->page.width=subframe_width;
4760 image->page.height=subframe_height;
4761 image->page.x=mng_info->clip.left;
4762 image->page.y=mng_info->clip.top;
4763 image->background_color=mng_background_color;
4764 image->matte=MagickFalse;
4766 (void) SetImageBackgroundColor(image);
4768 if (logging != MagickFalse)
4769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4770 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4771 (double) mng_info->clip.left,(double) mng_info->clip.right,
4772 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4775 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4778 if (memcmp(type,mng_CLIP,4) == 0)
4787 first_object=(p[0] << 8) | p[1];
4788 last_object=(p[2] << 8) | p[3];
4790 for (i=(int) first_object; i <= (int) last_object; i++)
4792 if (mng_info->exists[i] && !mng_info->frozen[i])
4797 box=mng_info->object_clip[i];
4798 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4802 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4805 if (memcmp(type,mng_SAVE,4) == 0)
4807 for (i=1; i < MNG_MAX_OBJECTS; i++)
4808 if (mng_info->exists[i])
4810 mng_info->frozen[i]=MagickTrue;
4811 #ifdef MNG_OBJECT_BUFFERS
4812 if (mng_info->ob[i] != (MngBuffer *) NULL)
4813 mng_info->ob[i]->frozen=MagickTrue;
4818 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4823 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4825 /* Read DISC or SEEK. */
4827 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4829 for (i=1; i < MNG_MAX_OBJECTS; i++)
4830 MngInfoDiscardObject(mng_info,i);
4838 for (j=0; j < (ssize_t) length; j+=2)
4840 i=p[j] << 8 | p[j+1];
4841 MngInfoDiscardObject(mng_info,i);
4846 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4851 if (memcmp(type,mng_MOVE,4) == 0)
4859 first_object=(p[0] << 8) | p[1];
4860 last_object=(p[2] << 8) | p[3];
4861 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4863 if (mng_info->exists[i] && !mng_info->frozen[i])
4871 old_pair.a=mng_info->x_off[i];
4872 old_pair.b=mng_info->y_off[i];
4873 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4874 mng_info->x_off[i]=new_pair.a;
4875 mng_info->y_off[i]=new_pair.b;
4879 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4883 if (memcmp(type,mng_LOOP,4) == 0)
4885 ssize_t loop_iters=1;
4886 loop_level=chunk[0];
4887 mng_info->loop_active[loop_level]=1; /* mark loop active */
4889 /* Record starting point. */
4890 loop_iters=mng_get_long(&chunk[1]);
4892 if (logging != MagickFalse)
4893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4894 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
4895 (double) loop_iters);
4897 if (loop_iters == 0)
4898 skipping_loop=loop_level;
4902 mng_info->loop_jump[loop_level]=TellBlob(image);
4903 mng_info->loop_count[loop_level]=loop_iters;
4906 mng_info->loop_iteration[loop_level]=0;
4907 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4911 if (memcmp(type,mng_ENDL,4) == 0)
4913 loop_level=chunk[0];
4915 if (skipping_loop > 0)
4917 if (skipping_loop == loop_level)
4920 Found end of zero-iteration loop.
4923 mng_info->loop_active[loop_level]=0;
4929 if (mng_info->loop_active[loop_level] == 1)
4931 mng_info->loop_count[loop_level]--;
4932 mng_info->loop_iteration[loop_level]++;
4934 if (logging != MagickFalse)
4935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4936 " ENDL: LOOP level %.20g has %.20g remaining iters ",
4937 (double) loop_level,(double)
4938 mng_info->loop_count[loop_level]);
4940 if (mng_info->loop_count[loop_level] != 0)
4942 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4946 ThrowReaderException(CorruptImageError,
4947 "ImproperImageHeader");
4958 mng_info->loop_active[loop_level]=0;
4960 for (i=0; i < loop_level; i++)
4961 if (mng_info->loop_active[i] == 1)
4962 last_level=(short) i;
4963 loop_level=last_level;
4968 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4972 if (memcmp(type,mng_CLON,4) == 0)
4974 if (mng_info->clon_warning == 0)
4975 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4976 CoderError,"CLON is not implemented yet","`%s'",
4979 mng_info->clon_warning++;
4982 if (memcmp(type,mng_MAGN,4) == 0)
4997 magn_first=(p[0] << 8) | p[1];
5003 magn_last=(p[2] << 8) | p[3];
5006 magn_last=magn_first;
5007 #ifndef MNG_OBJECT_BUFFERS
5008 if (magn_first || magn_last)
5009 if (mng_info->magn_warning == 0)
5011 (void) ThrowMagickException(&image->exception,
5012 GetMagickModule(),CoderError,
5013 "MAGN is not implemented yet for nonzero objects",
5014 "`%s'",image->filename);
5016 mng_info->magn_warning++;
5026 magn_mx=(p[5] << 8) | p[6];
5035 magn_my=(p[7] << 8) | p[8];
5044 magn_ml=(p[9] << 8) | p[10];
5053 magn_mr=(p[11] << 8) | p[12];
5062 magn_mt=(p[13] << 8) | p[14];
5071 magn_mb=(p[15] << 8) | p[16];
5083 magn_methy=magn_methx;
5086 if (magn_methx > 5 || magn_methy > 5)
5087 if (mng_info->magn_warning == 0)
5089 (void) ThrowMagickException(&image->exception,
5090 GetMagickModule(),CoderError,
5091 "Unknown MAGN method in MNG datastream","`%s'",
5094 mng_info->magn_warning++;
5096 #ifdef MNG_OBJECT_BUFFERS
5097 /* Magnify existing objects in the range magn_first to magn_last */
5099 if (magn_first == 0 || magn_last == 0)
5101 /* Save the magnification factors for object 0 */
5102 mng_info->magn_mb=magn_mb;
5103 mng_info->magn_ml=magn_ml;
5104 mng_info->magn_mr=magn_mr;
5105 mng_info->magn_mt=magn_mt;
5106 mng_info->magn_mx=magn_mx;
5107 mng_info->magn_my=magn_my;
5108 mng_info->magn_methx=magn_methx;
5109 mng_info->magn_methy=magn_methy;
5113 if (memcmp(type,mng_PAST,4) == 0)
5115 if (mng_info->past_warning == 0)
5116 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5117 CoderError,"PAST is not implemented yet","`%s'",
5120 mng_info->past_warning++;
5123 if (memcmp(type,mng_SHOW,4) == 0)
5125 if (mng_info->show_warning == 0)
5126 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5127 CoderError,"SHOW is not implemented yet","`%s'",
5130 mng_info->show_warning++;
5133 if (memcmp(type,mng_sBIT,4) == 0)
5136 mng_info->have_global_sbit=MagickFalse;
5140 mng_info->global_sbit.gray=p[0];
5141 mng_info->global_sbit.red=p[0];
5142 mng_info->global_sbit.green=p[1];
5143 mng_info->global_sbit.blue=p[2];
5144 mng_info->global_sbit.alpha=p[3];
5145 mng_info->have_global_sbit=MagickTrue;
5148 if (memcmp(type,mng_pHYs,4) == 0)
5152 mng_info->global_x_pixels_per_unit=
5153 (size_t) mng_get_long(p);
5154 mng_info->global_y_pixels_per_unit=
5155 (size_t) mng_get_long(&p[4]);
5156 mng_info->global_phys_unit_type=p[8];
5157 mng_info->have_global_phys=MagickTrue;
5161 mng_info->have_global_phys=MagickFalse;
5163 if (memcmp(type,mng_pHYg,4) == 0)
5165 if (mng_info->phyg_warning == 0)
5166 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5167 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5169 mng_info->phyg_warning++;
5171 if (memcmp(type,mng_BASI,4) == 0)
5173 skip_to_iend=MagickTrue;
5175 if (mng_info->basi_warning == 0)
5176 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5177 CoderError,"BASI is not implemented yet","`%s'",
5180 mng_info->basi_warning++;
5181 #ifdef MNG_BASI_SUPPORTED
5182 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5183 (p[2] << 8) | p[3]);
5184 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5185 (p[6] << 8) | p[7]);
5186 basi_color_type=p[8];
5187 basi_compression_method=p[9];
5188 basi_filter_type=p[10];
5189 basi_interlace_method=p[11];
5191 basi_red=(p[12] << 8) & p[13];
5197 basi_green=(p[14] << 8) & p[15];
5203 basi_blue=(p[16] << 8) & p[17];
5209 basi_alpha=(p[18] << 8) & p[19];
5213 if (basi_sample_depth == 16)
5220 basi_viewable=p[20];
5226 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5230 if (memcmp(type,mng_IHDR,4)
5231 #if defined(JNG_SUPPORTED)
5232 && memcmp(type,mng_JHDR,4)
5236 /* Not an IHDR or JHDR chunk */
5238 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5243 if (logging != MagickFalse)
5244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5245 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5247 mng_info->exists[object_id]=MagickTrue;
5248 mng_info->viewable[object_id]=MagickTrue;
5250 if (mng_info->invisible[object_id])
5252 if (logging != MagickFalse)
5253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5254 " Skipping invisible object");
5256 skip_to_iend=MagickTrue;
5257 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5260 #if defined(MNG_INSERT_LAYERS)
5262 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5264 image_width=(size_t) mng_get_long(p);
5265 image_height=(size_t) mng_get_long(&p[4]);
5267 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5270 Insert a transparent background layer behind the entire animation
5271 if it is not full screen.
5273 #if defined(MNG_INSERT_LAYERS)
5274 if (insert_layers && mng_type && first_mng_object)
5276 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5277 (image_width < mng_info->mng_width) ||
5278 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5279 (image_height < mng_info->mng_height) ||
5280 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5282 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5285 Allocate next image structure.
5287 AcquireNextImage(image_info,image);
5289 if (GetNextImageInList(image) == (Image *) NULL)
5291 image=DestroyImageList(image);
5292 MngInfoFreeStruct(mng_info,&have_mng_structure);
5293 return((Image *) NULL);
5296 image=SyncNextImageInList(image);
5298 mng_info->image=image;
5300 if (term_chunk_found)
5302 image->start_loop=MagickTrue;
5303 image->iterations=mng_iterations;
5304 term_chunk_found=MagickFalse;
5308 image->start_loop=MagickFalse;
5310 /* Make a background rectangle. */
5313 image->columns=mng_info->mng_width;
5314 image->rows=mng_info->mng_height;
5315 image->page.width=mng_info->mng_width;
5316 image->page.height=mng_info->mng_height;
5319 image->background_color=mng_background_color;
5320 (void) SetImageBackgroundColor(image);
5321 if (logging != MagickFalse)
5322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5323 " Inserted transparent background layer, W=%.20g, H=%.20g",
5324 (double) mng_info->mng_width,(double) mng_info->mng_height);
5328 Insert a background layer behind the upcoming image if
5329 framing_mode is 3, and we haven't already inserted one.
5331 if (insert_layers && (mng_info->framing_mode == 3) &&
5332 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5333 (simplicity & 0x08)))
5335 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5338 Allocate next image structure.
5340 AcquireNextImage(image_info,image);
5342 if (GetNextImageInList(image) == (Image *) NULL)
5344 image=DestroyImageList(image);
5345 MngInfoFreeStruct(mng_info,&have_mng_structure);
5346 return((Image *) NULL);
5349 image=SyncNextImageInList(image);
5352 mng_info->image=image;
5354 if (term_chunk_found)
5356 image->start_loop=MagickTrue;
5357 image->iterations=mng_iterations;
5358 term_chunk_found=MagickFalse;
5362 image->start_loop=MagickFalse;
5365 image->columns=subframe_width;
5366 image->rows=subframe_height;
5367 image->page.width=subframe_width;
5368 image->page.height=subframe_height;
5369 image->page.x=mng_info->clip.left;
5370 image->page.y=mng_info->clip.top;
5371 image->background_color=mng_background_color;
5372 image->matte=MagickFalse;
5373 (void) SetImageBackgroundColor(image);
5375 if (logging != MagickFalse)
5376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5377 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5378 (double) mng_info->clip.left,(double) mng_info->clip.right,
5379 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5381 #endif /* MNG_INSERT_LAYERS */
5382 first_mng_object=MagickFalse;
5384 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5387 Allocate next image structure.
5389 AcquireNextImage(image_info,image);
5391 if (GetNextImageInList(image) == (Image *) NULL)
5393 image=DestroyImageList(image);
5394 MngInfoFreeStruct(mng_info,&have_mng_structure);
5395 return((Image *) NULL);
5398 image=SyncNextImageInList(image);
5400 mng_info->image=image;
5401 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5402 GetBlobSize(image));
5404 if (status == MagickFalse)
5407 if (term_chunk_found)
5409 image->start_loop=MagickTrue;
5410 term_chunk_found=MagickFalse;
5414 image->start_loop=MagickFalse;
5416 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5418 image->delay=frame_delay;
5419 frame_delay=default_frame_delay;
5425 image->page.width=mng_info->mng_width;
5426 image->page.height=mng_info->mng_height;
5427 image->page.x=mng_info->x_off[object_id];
5428 image->page.y=mng_info->y_off[object_id];
5429 image->iterations=mng_iterations;
5432 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5435 if (logging != MagickFalse)
5436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5437 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5440 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5443 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5447 mng_info->image=image;
5448 mng_info->mng_type=mng_type;
5449 mng_info->object_id=object_id;
5451 if (memcmp(type,mng_IHDR,4) == 0)
5452 image=ReadOnePNGImage(mng_info,image_info,exception);
5454 #if defined(JNG_SUPPORTED)
5456 image=ReadOneJNGImage(mng_info,image_info,exception);
5459 if (image == (Image *) NULL)
5461 if (IsImageObject(previous) != MagickFalse)
5463 (void) DestroyImageList(previous);
5464 (void) CloseBlob(previous);
5467 MngInfoFreeStruct(mng_info,&have_mng_structure);
5468 return((Image *) NULL);
5471 if (image->columns == 0 || image->rows == 0)
5473 (void) CloseBlob(image);
5474 image=DestroyImageList(image);
5475 MngInfoFreeStruct(mng_info,&have_mng_structure);
5476 return((Image *) NULL);
5479 mng_info->image=image;
5486 if (mng_info->magn_methx || mng_info->magn_methy)
5492 if (logging != MagickFalse)
5493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5494 " Processing MNG MAGN chunk");
5496 if (mng_info->magn_methx == 1)
5498 magnified_width=mng_info->magn_ml;
5500 if (image->columns > 1)
5501 magnified_width += mng_info->magn_mr;
5503 if (image->columns > 2)
5504 magnified_width += (png_uint_32)
5505 ((image->columns-2)*(mng_info->magn_mx));
5510 magnified_width=(png_uint_32) image->columns;
5512 if (image->columns > 1)
5513 magnified_width += mng_info->magn_ml-1;
5515 if (image->columns > 2)
5516 magnified_width += mng_info->magn_mr-1;
5518 if (image->columns > 3)
5519 magnified_width += (png_uint_32)
5520 ((image->columns-3)*(mng_info->magn_mx-1));
5523 if (mng_info->magn_methy == 1)
5525 magnified_height=mng_info->magn_mt;
5527 if (image->rows > 1)
5528 magnified_height += mng_info->magn_mb;
5530 if (image->rows > 2)
5531 magnified_height += (png_uint_32)
5532 ((image->rows-2)*(mng_info->magn_my));
5537 magnified_height=(png_uint_32) image->rows;
5539 if (image->rows > 1)
5540 magnified_height += mng_info->magn_mt-1;
5542 if (image->rows > 2)
5543 magnified_height += mng_info->magn_mb-1;
5545 if (image->rows > 3)
5546 magnified_height += (png_uint_32)
5547 ((image->rows-3)*(mng_info->magn_my-1));
5550 if (magnified_height > image->rows ||
5551 magnified_width > image->columns)
5566 register PixelPacket
5578 /* Allocate next image structure. */
5580 if (logging != MagickFalse)
5581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5582 " Allocate magnified image");
5584 AcquireNextImage(image_info,image);
5586 if (GetNextImageInList(image) == (Image *) NULL)
5588 image=DestroyImageList(image);
5589 MngInfoFreeStruct(mng_info,&have_mng_structure);
5590 return((Image *) NULL);
5593 large_image=SyncNextImageInList(image);
5595 large_image->columns=magnified_width;
5596 large_image->rows=magnified_height;
5598 magn_methx=mng_info->magn_methx;
5599 magn_methy=mng_info->magn_methy;
5601 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5602 #define QM unsigned short
5603 if (magn_methx != 1 || magn_methy != 1)
5606 Scale pixels to unsigned shorts to prevent
5607 overflow of intermediate values of interpolations
5609 for (y=0; y < (ssize_t) image->rows; y++)
5611 q=GetAuthenticPixels(image,0,y,image->columns,1,
5614 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5616 q->red=ScaleQuantumToShort(q->red);
5617 q->green=ScaleQuantumToShort(q->green);
5618 q->blue=ScaleQuantumToShort(q->blue);
5619 q->opacity=ScaleQuantumToShort(q->opacity);
5623 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5631 if (image->matte != MagickFalse)
5632 (void) SetImageBackgroundColor(large_image);
5636 large_image->background_color.opacity=OpaqueOpacity;
5637 (void) SetImageBackgroundColor(large_image);
5639 if (magn_methx == 4)
5642 if (magn_methx == 5)
5645 if (magn_methy == 4)
5648 if (magn_methy == 5)
5652 /* magnify the rows into the right side of the large image */
5654 if (logging != MagickFalse)
5655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5656 " Magnify the rows to %.20g",(double) large_image->rows);
5657 m=(ssize_t) mng_info->magn_mt;
5659 length=(size_t) image->columns;
5660 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5661 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5663 if ((prev == (PixelPacket *) NULL) ||
5664 (next == (PixelPacket *) NULL))
5666 image=DestroyImageList(image);
5667 MngInfoFreeStruct(mng_info,&have_mng_structure);
5668 ThrowReaderException(ResourceLimitError,
5669 "MemoryAllocationFailed");
5672 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5673 (void) CopyMagickMemory(next,n,length);
5675 for (y=0; y < (ssize_t) image->rows; y++)
5678 m=(ssize_t) mng_info->magn_mt;
5680 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5681 m=(ssize_t) mng_info->magn_mb;
5683 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5684 m=(ssize_t) mng_info->magn_mb;
5686 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5690 m=(ssize_t) mng_info->magn_my;
5696 if (y < (ssize_t) image->rows-1)
5698 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5700 (void) CopyMagickMemory(next,n,length);
5703 for (i=0; i < m; i++, yy++)
5705 register PixelPacket
5708 assert(yy < (ssize_t) large_image->rows);
5711 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5713 q+=(large_image->columns-image->columns);
5715 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5717 /* To do: get color as function of indexes[x] */
5719 if (image->storage_class == PseudoClass)
5724 if (magn_methy <= 1)
5726 *q=(*pixels); /* replicate previous */
5729 else if (magn_methy == 2 || magn_methy == 4)
5737 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5738 -(*pixels).red)+m))/((ssize_t) (m*2))
5740 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5741 -(*pixels).green)+m))/((ssize_t) (m*2))
5743 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5744 -(*pixels).blue)+m))/((ssize_t) (m*2))
5747 if (image->matte != MagickFalse)
5748 (*q).opacity=(QM) (((ssize_t)
5750 -(*pixels).opacity)+m))
5751 /((ssize_t) (m*2))+(*pixels).opacity);
5754 if (magn_methy == 4)
5756 /* Replicate nearest */
5757 if (i <= ((m+1) << 1))
5758 (*q).opacity=(*pixels).opacity+0;
5760 (*q).opacity=(*n).opacity+0;
5764 else /* if (magn_methy == 3 || magn_methy == 5) */
5766 /* Replicate nearest */
5767 if (i <= ((m+1) << 1))
5773 if (magn_methy == 5)
5775 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5776 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5777 +(*pixels).opacity);
5785 if (SyncAuthenticPixels(large_image,exception) == 0)
5791 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5792 next=(PixelPacket *) RelinquishMagickMemory(next);
5794 length=image->columns;
5796 if (logging != MagickFalse)
5797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5798 " Delete original image");
5800 DeleteImageFromList(&image);
5804 mng_info->image=image;
5806 /* magnify the columns */
5807 if (logging != MagickFalse)
5808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5809 " Magnify the columns to %.20g",(double) image->columns);
5811 for (y=0; y < (ssize_t) image->rows; y++)
5813 register PixelPacket
5816 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5817 pixels=q+(image->columns-length);
5820 for (x=(ssize_t) (image->columns-length);
5821 x < (ssize_t) image->columns; x++)
5823 if (x == (ssize_t) (image->columns-length))
5824 m=(ssize_t) mng_info->magn_ml;
5826 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5827 m=(ssize_t) mng_info->magn_mr;
5829 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5830 m=(ssize_t) mng_info->magn_mr;
5832 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5836 m=(ssize_t) mng_info->magn_mx;
5838 for (i=0; i < m; i++)
5840 if (magn_methx <= 1)
5842 /* replicate previous */
5846 else if (magn_methx == 2 || magn_methx == 4)
5854 (*q).red=(QM) ((2*i*((*n).red
5856 /((ssize_t) (m*2))+(*pixels).red);
5857 (*q).green=(QM) ((2*i*((*n).green
5859 +m)/((ssize_t) (m*2))+(*pixels).green);
5860 (*q).blue=(QM) ((2*i*((*n).blue
5862 /((ssize_t) (m*2))+(*pixels).blue);
5863 if (image->matte != MagickFalse)
5864 (*q).opacity=(QM) ((2*i*((*n).opacity
5865 -(*pixels).opacity)+m)/((ssize_t) (m*2))
5866 +(*pixels).opacity);
5869 if (magn_methx == 4)
5871 /* Replicate nearest */
5872 if (i <= ((m+1) << 1))
5873 (*q).opacity=(*pixels).opacity+0;
5875 (*q).opacity=(*n).opacity+0;
5879 else /* if (magn_methx == 3 || magn_methx == 5) */
5881 /* Replicate nearest */
5882 if (i <= ((m+1) << 1))
5888 if (magn_methx == 5)
5891 (*q).opacity=(QM) ((2*i*((*n).opacity
5892 -(*pixels).opacity)+m) /((ssize_t) (m*2))
5893 +(*pixels).opacity);
5902 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5905 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5906 if (magn_methx != 1 || magn_methy != 1)
5909 Rescale pixels to Quantum
5911 for (y=0; y < (ssize_t) image->rows; y++)
5913 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5915 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5917 q->red=ScaleShortToQuantum(q->red);
5918 q->green=ScaleShortToQuantum(q->green);
5919 q->blue=ScaleShortToQuantum(q->blue);
5920 q->opacity=ScaleShortToQuantum(q->opacity);
5924 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5929 if (logging != MagickFalse)
5930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5931 " Finished MAGN processing");
5936 Crop_box is with respect to the upper left corner of the MNG.
5938 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5939 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5940 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5941 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5942 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5943 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5944 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5945 if ((crop_box.left != (mng_info->image_box.left
5946 +mng_info->x_off[object_id])) ||
5947 (crop_box.right != (mng_info->image_box.right
5948 +mng_info->x_off[object_id])) ||
5949 (crop_box.top != (mng_info->image_box.top
5950 +mng_info->y_off[object_id])) ||
5951 (crop_box.bottom != (mng_info->image_box.bottom
5952 +mng_info->y_off[object_id])))
5954 if (logging != MagickFalse)
5955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5956 " Crop the PNG image");
5958 if ((crop_box.left < crop_box.right) &&
5959 (crop_box.top < crop_box.bottom))
5968 Crop_info is with respect to the upper left corner of
5971 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5972 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5973 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5974 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5975 image->page.width=image->columns;
5976 image->page.height=image->rows;
5979 im=CropImage(image,&crop_info,exception);
5981 if (im != (Image *) NULL)
5983 image->columns=im->columns;
5984 image->rows=im->rows;
5985 im=DestroyImage(im);
5986 image->page.width=image->columns;
5987 image->page.height=image->rows;
5988 image->page.x=crop_box.left;
5989 image->page.y=crop_box.top;
5996 No pixels in crop area. The MNG spec still requires
5997 a layer, though, so make a single transparent pixel in
5998 the top left corner.
6003 (void) SetImageBackgroundColor(image);
6004 image->page.width=1;
6005 image->page.height=1;
6010 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6011 image=mng_info->image;
6015 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6016 /* PNG does not handle depths greater than 16 so reduce it even
6019 if (image->depth > 16)
6023 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6024 if (LosslessReduceDepthOK(image) != MagickFalse)
6028 GetImageException(image,exception);
6030 if (image_info->number_scenes != 0)
6032 if (mng_info->scenes_found >
6033 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6037 if (logging != MagickFalse)
6038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6039 " Finished reading image datastream.");
6041 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6043 (void) CloseBlob(image);
6045 if (logging != MagickFalse)
6046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6047 " Finished reading all image datastreams.");
6049 #if defined(MNG_INSERT_LAYERS)
6050 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6051 (mng_info->mng_height))
6054 Insert a background layer if nothing else was found.
6056 if (logging != MagickFalse)
6057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6058 " No images found. Inserting a background layer.");
6060 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6063 Allocate next image structure.
6065 AcquireNextImage(image_info,image);
6066 if (GetNextImageInList(image) == (Image *) NULL)
6068 image=DestroyImageList(image);
6069 MngInfoFreeStruct(mng_info,&have_mng_structure);
6071 if (logging != MagickFalse)
6072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6073 " Allocation failed, returning NULL.");
6075 return((Image *) NULL);
6077 image=SyncNextImageInList(image);
6079 image->columns=mng_info->mng_width;
6080 image->rows=mng_info->mng_height;
6081 image->page.width=mng_info->mng_width;
6082 image->page.height=mng_info->mng_height;
6085 image->background_color=mng_background_color;
6086 image->matte=MagickFalse;
6088 if (image_info->ping == MagickFalse)
6089 (void) SetImageBackgroundColor(image);
6091 mng_info->image_found++;
6094 image->iterations=mng_iterations;
6096 if (mng_iterations == 1)
6097 image->start_loop=MagickTrue;
6099 while (GetPreviousImageInList(image) != (Image *) NULL)
6102 if (image_count > 10*mng_info->image_found)
6104 if (logging != MagickFalse)
6105 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6107 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6108 CoderError,"Linked list is corrupted, beginning of list not found",
6109 "`%s'",image_info->filename);
6111 return((Image *) NULL);
6114 image=GetPreviousImageInList(image);
6116 if (GetNextImageInList(image) == (Image *) NULL)
6118 if (logging != MagickFalse)
6119 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6121 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6122 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6123 image_info->filename);
6127 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6128 GetNextImageInList(image) ==
6131 if (logging != MagickFalse)
6132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6133 " First image null");
6135 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6136 CoderError,"image->next for first image is NULL but shouldn't be.",
6137 "`%s'",image_info->filename);
6140 if (mng_info->image_found == 0)
6142 if (logging != MagickFalse)
6143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6144 " No visible images found.");
6146 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6147 CoderError,"No visible images in file","`%s'",image_info->filename);
6149 if (image != (Image *) NULL)
6150 image=DestroyImageList(image);
6152 MngInfoFreeStruct(mng_info,&have_mng_structure);
6153 return((Image *) NULL);
6156 if (mng_info->ticks_per_second)
6157 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6158 final_delay/mng_info->ticks_per_second;
6161 image->start_loop=MagickTrue;
6163 /* Find final nonzero image delay */
6164 final_image_delay=0;
6166 while (GetNextImageInList(image) != (Image *) NULL)
6169 final_image_delay=image->delay;
6171 image=GetNextImageInList(image);
6174 if (final_delay < final_image_delay)
6175 final_delay=final_image_delay;
6177 image->delay=final_delay;
6179 if (logging != MagickFalse)
6180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6181 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6182 (double) final_delay);
6184 if (logging != MagickFalse)
6190 image=GetFirstImageInList(image);
6192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6193 " Before coalesce:");
6195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6196 " scene 0 delay=%.20g",(double) image->delay);
6198 while (GetNextImageInList(image) != (Image *) NULL)
6200 image=GetNextImageInList(image);
6201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6202 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6206 image=GetFirstImageInList(image);
6207 #ifdef MNG_COALESCE_LAYERS
6217 if (logging != MagickFalse)
6218 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6221 next_image=CoalesceImages(image,&image->exception);
6223 if (next_image == (Image *) NULL)
6224 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6226 image=DestroyImageList(image);
6229 for (next=image; next != (Image *) NULL; next=next_image)
6231 next->page.width=mng_info->mng_width;
6232 next->page.height=mng_info->mng_height;
6235 next->scene=scene++;
6236 next_image=GetNextImageInList(next);
6238 if (next_image == (Image *) NULL)
6241 if (next->delay == 0)
6244 next_image->previous=GetPreviousImageInList(next);
6245 if (GetPreviousImageInList(next) == (Image *) NULL)
6248 next->previous->next=next_image;
6249 next=DestroyImage(next);
6255 while (GetNextImageInList(image) != (Image *) NULL)
6256 image=GetNextImageInList(image);
6258 image->dispose=BackgroundDispose;
6260 if (logging != MagickFalse)
6266 image=GetFirstImageInList(image);
6268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6269 " After coalesce:");
6271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6272 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6273 (double) image->dispose);
6275 while (GetNextImageInList(image) != (Image *) NULL)
6277 image=GetNextImageInList(image);
6279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6280 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6281 (double) image->delay,(double) image->dispose);
6285 image=GetFirstImageInList(image);
6286 MngInfoFreeStruct(mng_info,&have_mng_structure);
6287 have_mng_structure=MagickFalse;
6289 if (logging != MagickFalse)
6290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6292 return(GetFirstImageInList(image));
6294 #else /* PNG_LIBPNG_VER > 10011 */
6295 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6297 printf("Your PNG library is too old: You have libpng-%s\n",
6298 PNG_LIBPNG_VER_STRING);
6300 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6301 "PNG library is too old","`%s'",image_info->filename);
6303 return(Image *) NULL;
6306 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6308 return(ReadPNGImage(image_info,exception));
6310 #endif /* PNG_LIBPNG_VER > 10011 */
6314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6318 % R e g i s t e r P N G I m a g e %
6322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6324 % RegisterPNGImage() adds properties for the PNG image format to
6325 % the list of supported formats. The properties include the image format
6326 % tag, a method to read and/or write the format, whether the format
6327 % supports the saving of more than one frame to the same file or blob,
6328 % whether the format supports native in-memory I/O, and a brief
6329 % description of the format.
6331 % The format of the RegisterPNGImage method is:
6333 % size_t RegisterPNGImage(void)
6336 ModuleExport size_t RegisterPNGImage(void)
6339 version[MaxTextExtent];
6347 "See http://www.libpng.org/ for details about the PNG format."
6352 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6358 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6364 #if defined(PNG_LIBPNG_VER_STRING)
6365 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6366 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6368 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6370 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6371 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6376 entry=SetMagickInfo("MNG");
6377 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6379 #if defined(MAGICKCORE_PNG_DELEGATE)
6380 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6381 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6384 entry->magick=(IsImageFormatHandler *) IsMNG;
6385 entry->description=ConstantString("Multiple-image Network Graphics");
6387 if (*version != '\0')
6388 entry->version=ConstantString(version);
6390 entry->module=ConstantString("PNG");
6391 entry->note=ConstantString(MNGNote);
6392 (void) RegisterMagickInfo(entry);
6394 entry=SetMagickInfo("PNG");
6396 #if defined(MAGICKCORE_PNG_DELEGATE)
6397 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6398 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6401 entry->magick=(IsImageFormatHandler *) IsPNG;
6402 entry->adjoin=MagickFalse;
6403 entry->description=ConstantString("Portable Network Graphics");
6404 entry->module=ConstantString("PNG");
6406 if (*version != '\0')
6407 entry->version=ConstantString(version);
6409 entry->note=ConstantString(PNGNote);
6410 (void) RegisterMagickInfo(entry);
6412 entry=SetMagickInfo("PNG8");
6414 #if defined(MAGICKCORE_PNG_DELEGATE)
6415 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6416 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6419 entry->magick=(IsImageFormatHandler *) IsPNG;
6420 entry->adjoin=MagickFalse;
6421 entry->description=ConstantString(
6422 "8-bit indexed with optional binary transparency");
6423 entry->module=ConstantString("PNG");
6424 (void) RegisterMagickInfo(entry);
6426 entry=SetMagickInfo("PNG24");
6429 #if defined(ZLIB_VERSION)
6430 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6431 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6433 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6435 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6436 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6440 if (*version != '\0')
6441 entry->version=ConstantString(version);
6443 #if defined(MAGICKCORE_PNG_DELEGATE)
6444 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6445 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6448 entry->magick=(IsImageFormatHandler *) IsPNG;
6449 entry->adjoin=MagickFalse;
6450 entry->description=ConstantString("opaque 24-bit RGB");
6451 entry->module=ConstantString("PNG");
6452 (void) RegisterMagickInfo(entry);
6454 entry=SetMagickInfo("PNG32");
6456 #if defined(MAGICKCORE_PNG_DELEGATE)
6457 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6458 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6461 entry->magick=(IsImageFormatHandler *) IsPNG;
6462 entry->adjoin=MagickFalse;
6463 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6464 entry->module=ConstantString("PNG");
6465 (void) RegisterMagickInfo(entry);
6467 entry=SetMagickInfo("JNG");
6469 #if defined(JNG_SUPPORTED)
6470 #if defined(MAGICKCORE_PNG_DELEGATE)
6471 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6472 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6476 entry->magick=(IsImageFormatHandler *) IsJNG;
6477 entry->adjoin=MagickFalse;
6478 entry->description=ConstantString("JPEG Network Graphics");
6479 entry->module=ConstantString("PNG");
6480 entry->note=ConstantString(JNGNote);
6481 (void) RegisterMagickInfo(entry);
6483 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6484 ping_semaphore=AllocateSemaphoreInfo();
6487 return(MagickImageCoderSignature);
6491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6495 % U n r e g i s t e r P N G I m a g e %
6499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6501 % UnregisterPNGImage() removes format registrations made by the
6502 % PNG module from the list of supported formats.
6504 % The format of the UnregisterPNGImage method is:
6506 % UnregisterPNGImage(void)
6509 ModuleExport void UnregisterPNGImage(void)
6511 (void) UnregisterMagickInfo("MNG");
6512 (void) UnregisterMagickInfo("PNG");
6513 (void) UnregisterMagickInfo("PNG8");
6514 (void) UnregisterMagickInfo("PNG24");
6515 (void) UnregisterMagickInfo("PNG32");
6516 (void) UnregisterMagickInfo("JNG");
6518 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6519 if (ping_semaphore != (SemaphoreInfo *) NULL)
6520 DestroySemaphoreInfo(&ping_semaphore);
6524 #if defined(MAGICKCORE_PNG_DELEGATE)
6525 #if PNG_LIBPNG_VER > 10011
6527 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6531 % W r i t e M N G I m a g e %
6535 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6537 % WriteMNGImage() writes an image in the Portable Network Graphics
6538 % Group's "Multiple-image Network Graphics" encoded image format.
6540 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6542 % The format of the WriteMNGImage method is:
6544 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6546 % A description of each parameter follows.
6548 % o image_info: the image info.
6550 % o image: The image.
6553 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6554 % "To do" under ReadPNGImage):
6556 % Preserve all unknown and not-yet-handled known chunks found in input
6557 % PNG file and copy them into output PNG files according to the PNG
6560 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6562 % Improve selection of color type (use indexed-colour or indexed-colour
6563 % with tRNS when 256 or fewer unique RGBA values are present).
6565 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6566 % This will be complicated if we limit ourselves to generating MNG-LC
6567 % files. For now we ignore disposal method 3 and simply overlay the next
6570 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6571 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6572 % [mostly done 15 June 1999 but still need to take care of tRNS]
6574 % Check for identical sRGB and replace with a global sRGB (and remove
6575 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6576 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6577 % local gAMA/cHRM with local sRGB if appropriate).
6579 % Check for identical sBIT chunks and write global ones.
6581 % Provide option to skip writing the signature tEXt chunks.
6583 % Use signatures to detect identical objects and reuse the first
6584 % instance of such objects instead of writing duplicate objects.
6586 % Use a smaller-than-32k value of compression window size when
6589 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6590 % ancillary text chunks and save profiles.
6592 % Provide an option to force LC files (to ensure exact framing rate)
6595 % Provide an option to force VLC files instead of LC, even when offsets
6596 % are present. This will involve expanding the embedded images with a
6597 % transparent region at the top and/or left.
6601 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6602 png_info *ping_info, unsigned char *profile_type, unsigned char
6603 *profile_description, unsigned char *profile_data, png_uint_32 length)
6622 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6624 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6627 if (image_info->verbose)
6629 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6630 (char *) profile_type, (double) length);
6633 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6634 description_length=(png_uint_32) strlen((const char *) profile_description);
6635 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6636 + description_length);
6637 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6638 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6639 text[0].key[0]='\0';
6640 (void) ConcatenateMagickString(text[0].key,
6641 "Raw profile type ",MaxTextExtent);
6642 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6646 (void) CopyMagickString(dp,(const char *) profile_description,
6648 dp+=description_length;
6650 (void) FormatMagickString(dp,allocated_length-
6651 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6654 for (i=0; i < (ssize_t) length; i++)
6658 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6659 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6664 text[0].text_length=(png_size_t) (dp-text[0].text);
6665 text[0].compression=image_info->compression == NoCompression ||
6666 (image_info->compression == UndefinedCompression &&
6667 text[0].text_length < 128) ? -1 : 0;
6669 if (text[0].text_length <= allocated_length)
6670 png_set_text(ping,ping_info,text,1);
6672 png_free(ping,text[0].text);
6673 png_free(ping,text[0].key);
6674 png_free(ping,text);
6677 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6678 const char *string, MagickBooleanType logging)
6691 ResetImageProfileIterator(image);
6693 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6695 profile=GetImageProfile(image,name);
6697 if (profile != (const StringInfo *) NULL)
6702 if (LocaleNCompare(name,string,11) == 0)
6704 if (logging != MagickFalse)
6705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6706 " Found %s profile",name);
6708 ping_profile=CloneStringInfo(profile);
6709 data=GetStringInfoDatum(ping_profile),
6710 length=(png_uint_32) GetStringInfoLength(ping_profile);
6715 (void) WriteBlobMSBULong(image,length-5); /* data length */
6716 (void) WriteBlob(image,length-1,data+1);
6717 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6718 ping_profile=DestroyStringInfo(ping_profile);
6722 name=GetNextImageProfile(image);
6729 /* Write one PNG image */
6730 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6731 const ImageInfo *IMimage_info,Image *IMimage)
6755 ping_trans_alpha[256];
6782 ping_have_cheap_transparency,
6792 /* ping_exclude_EXIF, */
6795 /* ping_exclude_iTXt, */
6800 /* ping_exclude_tRNS, */
6802 ping_exclude_zCCP, /* hex-encoded iCCP */
6805 ping_need_colortype_warning,
6823 ping_interlace_method,
6824 ping_compression_method,
6841 number_semitransparent,
6843 ping_pHYs_unit_type;
6846 ping_pHYs_x_resolution,
6847 ping_pHYs_y_resolution;
6849 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6850 " Enter WriteOnePNGImage()");
6852 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
6853 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
6855 if (mng_info->need_blob != MagickFalse)
6857 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
6860 image_info=DestroyImageInfo(image_info);
6861 image=DestroyImage(image);
6862 return(MagickFalse);
6866 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6867 LockSemaphoreInfo(ping_semaphore);
6870 /* Initialize some stuff */
6873 ping_interlace_method=0,
6874 ping_compression_method=0,
6875 ping_filter_method=0,
6878 ping_background.red = 0;
6879 ping_background.green = 0;
6880 ping_background.blue = 0;
6881 ping_background.gray = 0;
6882 ping_background.index = 0;
6884 ping_trans_color.red=0;
6885 ping_trans_color.green=0;
6886 ping_trans_color.blue=0;
6887 ping_trans_color.gray=0;
6889 ping_pHYs_unit_type = 0;
6890 ping_pHYs_x_resolution = 0;
6891 ping_pHYs_y_resolution = 0;
6893 ping_have_color=MagickTrue;
6894 ping_have_non_bw=MagickTrue;
6895 ping_have_PLTE=MagickFalse;
6896 ping_have_bKGD=MagickFalse;
6897 ping_have_pHYs=MagickFalse;
6898 ping_have_tRNS=MagickFalse;
6900 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
6901 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6902 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
6903 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
6904 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6905 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
6906 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
6907 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
6908 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
6909 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
6910 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
6911 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
6912 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
6913 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
6914 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
6916 ping_need_colortype_warning = MagickFalse;
6919 number_semitransparent = 0;
6920 number_transparent = 0;
6922 if (logging != MagickFalse)
6924 if (image->storage_class == UndefinedClass)
6925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6926 " storage_class=UndefinedClass");
6927 if (image->storage_class == DirectClass)
6928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6929 " storage_class=DirectClass");
6930 if (image->storage_class == PseudoClass)
6931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6932 " storage_class=PseudoClass");
6935 if (image->colorspace != RGBColorspace)
6936 (void) TransformImageColorspace(image,RGBColorspace);
6939 Sometimes we get PseudoClass images whose RGB values don't match
6940 the colors in the colormap. This code syncs the RGB values.
6942 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
6943 (void) SyncImage(image);
6945 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
6946 if (image->depth > 8)
6948 if (logging != MagickFalse)
6949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6950 " Reducing PNG bit depth to 8 since this is a Q8 build.");
6957 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6958 /* PNG does not handle depths greater than 16 so reduce it even
6961 if (image->depth > 16)
6965 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6966 if (image->depth == 16 && mng_info->write_png_depth != 16)
6967 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
6971 /* Normally we run this just once, but in the case of writing PNG8
6972 * we reduce the transparency to binary and run again, then reduce
6973 * the colors to a simple 3-3-2 palette and run once more.
6979 * Sometimes we get DirectClass images that have 256 colors or fewer.
6980 * This code will build a colormap.
6982 * Also, sometimes we get PseudoClass images with an out-of-date
6983 * colormap. This code will replace the colormap with a new one.
6984 * Sometimes we get PseudoClass images that have more than 256 colors.
6985 * This code will delete the colormap and change the image to
6988 * If image->matte is MagickFalse, we ignore the opacity channel
6989 * even though it sometimes contains left-over non-opaque values.
6991 * Also we gather some information (number of opaque, transparent,
6992 * and semitransparent pixels, and whether the image has any non-gray
6993 * pixels or only black-and-white pixels) that we might need later.
6995 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
6996 * we need to check for bogus non-opaque values, at least.
7007 semitransparent[260],
7010 register IndexPacket
7013 register const PixelPacket
7017 register PixelPacket
7020 if (logging != MagickFalse)
7021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7022 " Enter BUILD_PALETTE:");
7024 if (logging != MagickFalse)
7026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7027 " image->columns=%.20g",(double) image->columns);
7028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7029 " image->rows=%.20g",(double) image->rows);
7030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7031 " image->matte=%.20g",(double) image->matte);
7032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7033 " image->depth=%.20g",(double) image->depth);
7035 if (image->storage_class == PseudoClass && image->colormap != NULL)
7037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7038 " Original colormap:");
7039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7040 " i (red,green,blue,opacity)");
7042 for (i=0; i < 256; i++)
7044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7045 " %d (%d,%d,%d,%d)",
7047 (int) image->colormap[i].red,
7048 (int) image->colormap[i].green,
7049 (int) image->colormap[i].blue,
7050 (int) image->colormap[i].opacity);
7053 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7058 " %d (%d,%d,%d,%d)",
7060 (int) image->colormap[i].red,
7061 (int) image->colormap[i].green,
7062 (int) image->colormap[i].blue,
7063 (int) image->colormap[i].opacity);
7068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7069 " image->colors=%d",(int) image->colors);
7071 if (image->colors == 0)
7072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7073 " (zero means unknown)");
7075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7076 " Regenerate the colormap");
7079 exception=(&image->exception);
7083 number_semitransparent = 0;
7084 number_transparent = 0;
7086 for (y=0; y < (ssize_t) image->rows; y++)
7088 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7090 if (q == (PixelPacket *) NULL)
7093 for (x=0; x < (ssize_t) image->columns; x++)
7095 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7097 if (number_opaque < 259)
7099 if (number_opaque == 0)
7102 opaque[0].opacity=OpaqueOpacity;
7106 for (i=0; i< (ssize_t) number_opaque; i++)
7108 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7112 if (i == (ssize_t) number_opaque &&
7113 number_opaque < 259)
7117 opaque[i].opacity = OpaqueOpacity;
7121 else if (q->opacity == TransparentOpacity)
7123 if (number_transparent < 259)
7125 if (number_transparent == 0)
7128 ping_trans_color.red=(unsigned short)(q->red);
7129 ping_trans_color.green=(unsigned short) (q->green);
7130 ping_trans_color.blue=(unsigned short) (q->blue);
7131 ping_trans_color.gray=(unsigned short) (q->blue);
7132 number_transparent = 1;
7135 for (i=0; i< (ssize_t) number_transparent; i++)
7137 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7141 if (i == (ssize_t) number_transparent &&
7142 number_transparent < 259)
7144 number_transparent++;
7145 transparent[i] = *q;
7151 if (number_semitransparent < 259)
7153 if (number_semitransparent == 0)
7155 semitransparent[0]=*q;
7156 number_semitransparent = 1;
7159 for (i=0; i< (ssize_t) number_semitransparent; i++)
7161 if (IsColorEqual(semitransparent+i,
7162 (PixelPacket *) q) &&
7163 q->opacity == semitransparent[i].opacity)
7167 if (i == (ssize_t) number_semitransparent &&
7168 number_semitransparent < 259)
7170 number_semitransparent++;
7171 semitransparent[i] = *q;
7179 if (ping_exclude_bKGD == MagickFalse)
7181 /* Add the background color to the palette, if it
7182 * isn't already there.
7184 for (i=0; i<number_opaque; i++)
7186 if (IsColorEqual(opaque+i,
7187 &image->background_color))
7191 if (number_opaque < 259 && i == number_opaque)
7193 opaque[i]=image->background_color;
7194 opaque[i].opacity = OpaqueOpacity;
7199 image_colors=number_opaque+number_transparent+number_semitransparent;
7201 if (logging != MagickFalse)
7203 if (image_colors > 256)
7204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7205 " image has more than 256 colors");
7208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7209 " image has %d colors",image_colors);
7212 if (mng_info->write_png_colortype != 7) /* We won't need this info */
7214 ping_have_color=MagickFalse;
7215 ping_have_non_bw=MagickFalse;
7217 if(image_colors > 256)
7219 for (y=0; y < (ssize_t) image->rows; y++)
7221 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7223 if (q == (PixelPacket *) NULL)
7226 /* Worst case is black-and-white; we are looking at every
7230 if (ping_have_color == MagickFalse)
7233 for (x=0; x < (ssize_t) image->columns; x++)
7235 if (s->red != s->green || s->red != s->blue)
7237 ping_have_color=MagickTrue;
7238 ping_have_non_bw=MagickTrue;
7245 if (ping_have_non_bw == MagickFalse)
7248 for (x=0; x < (ssize_t) image->columns; x++)
7250 if (s->red != 0 && s->red != QuantumRange)
7252 ping_have_non_bw=MagickTrue;
7261 if (image_colors < 257)
7267 * Initialize image colormap.
7270 if (logging != MagickFalse)
7271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7272 " Sort the new colormap");
7274 /* Sort palette, transparent first */;
7278 for (i=0; i<number_transparent; i++)
7279 colormap[n++] = transparent[i];
7281 for (i=0; i<number_semitransparent; i++)
7282 colormap[n++] = semitransparent[i];
7284 for (i=0; i<number_opaque; i++)
7285 colormap[n++] = opaque[i];
7288 /* image_colors < 257; search the colormap instead of the pixels
7289 * to get ping_have_color and ping_have_non_bw
7293 if (ping_have_color == MagickFalse)
7295 if (colormap[i].red != colormap[i].green ||
7296 colormap[i].red != colormap[i].blue)
7298 ping_have_color=MagickTrue;
7299 ping_have_non_bw=MagickTrue;
7304 if (ping_have_non_bw == MagickFalse)
7306 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7307 ping_have_non_bw=MagickTrue;
7311 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7312 (number_transparent == 0 && number_semitransparent == 0)) &&
7313 (((mng_info->write_png_colortype-1) ==
7314 PNG_COLOR_TYPE_PALETTE) ||
7315 (mng_info->write_png_colortype == 0)))
7317 if (logging != MagickFalse)
7319 if (n != (ssize_t) image_colors)
7320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7321 " image_colors (%d) and n (%d) don't match",
7324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7325 " AcquireImageColormap");
7328 image->colors = image_colors;
7330 if (AcquireImageColormap(image,image_colors) ==
7332 ThrowWriterException(ResourceLimitError,
7333 "MemoryAllocationFailed");
7335 for (i=0; i< (ssize_t) image_colors; i++)
7336 image->colormap[i] = colormap[i];
7338 if (logging != MagickFalse)
7340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7341 " image->colors=%d (%d)",
7342 (int) image->colors, image_colors);
7344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7345 " Update the pixel indexes");
7348 /* Sync the pixel indices with the new colormap */
7350 for (y=0; y < (ssize_t) image->rows; y++)
7352 q=GetAuthenticPixels(image,0,y,image->columns,1,
7355 if (q == (PixelPacket *) NULL)
7358 indexes=GetAuthenticIndexQueue(image);
7360 for (x=0; x < (ssize_t) image->columns; x++)
7362 for (i=0; i< (ssize_t) image_colors; i++)
7364 if ((image->matte == MagickFalse ||
7365 image->colormap[i].opacity == q->opacity) &&
7366 (IsColorEqual(&image->colormap[i],
7367 (PixelPacket *) q)))
7369 indexes[x]=(IndexPacket) i;
7376 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7382 if (logging != MagickFalse)
7384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7385 " image->colors=%d", (int) image->colors);
7387 if (image->colormap != NULL)
7389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7390 " i (red,green,blue,opacity)");
7392 for (i=0; i < (ssize_t) image->colors; i++)
7394 if (i < 300 || i >= image->colors - 10)
7396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7397 " %d (%d,%d,%d,%d)",
7399 (int) image->colormap[i].red,
7400 (int) image->colormap[i].green,
7401 (int) image->colormap[i].blue,
7402 (int) image->colormap[i].opacity);
7407 if (number_transparent < 257)
7408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7409 " number_transparent = %d",
7410 number_transparent);
7413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7414 " number_transparent > 256");
7416 if (number_opaque < 257)
7417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7418 " number_opaque = %d",
7422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7423 " number_opaque > 256");
7425 if (number_semitransparent < 257)
7426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7427 " number_semitransparent = %d",
7428 number_semitransparent);
7431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7432 " number_semitransparent > 256");
7434 if (ping_have_non_bw == MagickFalse)
7435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7436 " All pixels and the background are black or white");
7438 else if (ping_have_color == MagickFalse)
7439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7440 " All pixels and the background are gray");
7443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7444 " At least one pixel or the background is non-gray");
7446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7447 " Exit BUILD_PALETTE:");
7450 if (mng_info->write_png8 == MagickFalse)
7453 /* Make any reductions necessary for the PNG8 format */
7454 if (image_colors <= 256 &&
7455 image_colors != 0 && image->colormap != NULL &&
7456 number_semitransparent == 0 &&
7457 number_transparent <= 1)
7460 /* PNG8 can't have semitransparent colors so we threshold the
7461 * opacity to 0 or OpaqueOpacity
7463 if (number_semitransparent != 0)
7465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7466 " Thresholding the alpha channel to binary");
7468 for (y=0; y < (ssize_t) image->rows; y++)
7470 r=GetAuthenticPixels(image,0,y,image->columns,1,
7473 if (r == (PixelPacket *) NULL)
7476 for (x=0; x < (ssize_t) image->columns; x++)
7478 r->opacity = r->opacity > OpaqueOpacity/2 ? 0 :
7483 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7486 if (image_colors != 0 && image_colors <= 256 &&
7487 image->colormap != NULL)
7488 for (i=0; i<image_colors; i++)
7489 image->colormap[i].opacity =
7490 image->colormap[i].opacity > OpaqueOpacity/2 ? 0 :
7496 /* PNG8 can't have more than 256 colors so we quantize the pixels and
7497 * background color to the 3-3-2 palette.
7501 if (image_colors == 0 || image_colors > 256)
7503 if (logging != MagickFalse)
7504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7505 " Quantizing the background color to 3-3-2");
7507 if (image->depth == 8)
7509 image->background_color.red=
7510 (image->background_color.red & 0xe0) |
7511 ((image->background_color.red & 0xe0) >> 3) |
7512 ((image->background_color.red & 0xc0) >> 6);
7513 image->background_color.green=
7514 (image->background_color.green & 0xe0) |
7515 ((image->background_color.green & 0xe0) >> 3) |
7516 ((image->background_color.green & 0xc0) >> 6);
7517 image->background_color.blue=
7518 (image->background_color.blue & 0xc0) |
7519 ((image->background_color.blue & 0xc0) >> 2) |
7520 ((image->background_color.blue & 0xc0) >> 4) |
7521 ((image->background_color.blue & 0xc0) >> 6);
7523 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
7526 image->background_color.red=
7527 (((image->background_color.red & 0xe0) >> 8) |
7528 ((image->background_color.red & 0xe0) >> 11) |
7529 ((image->background_color.red & 0xc0) >> 14))*0x0101;
7530 image->background_color.green=
7531 (((image->background_color.green & 0xe0) >> 8) |
7532 ((image->background_color.green & 0xe0) >> 11) |
7533 ((image->background_color.green & 0xc0) >> 14))*0x0101;
7534 image->background_color.blue=
7535 (((image->background_color.blue & 0xc0)>> 8) |
7536 ((image->background_color.blue & 0xc0) >> 10) |
7537 ((image->background_color.blue & 0xc0) >> 12) |
7538 ((image->background_color.blue & 0xc0) >> 14))*0x0101;
7541 /* To do: Add Q32 case */
7543 if (logging != MagickFalse)
7544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7545 " Quantizing the pixel colors to 3-3-2");
7547 if (image->colormap == NULL)
7549 for (y=0; y < (ssize_t) image->rows; y++)
7551 r=GetAuthenticPixels(image,0,y,image->columns,1,
7554 if (r == (PixelPacket *) NULL)
7557 for (x=0; x < (ssize_t) image->columns; x++)
7559 if (image->depth == 8)
7561 r->red= (r->red & 0xe0) |
7562 ((r->red & 0xe0) >> 3) |
7563 ((r->red & 0xc0) >> 6);
7564 r->green=(r->green & 0xe0) |
7565 ((r->green & 0xe0) >> 3) |
7566 ((r->green & 0xc0) >> 6);
7567 r->blue= (r->blue & 0xc0) |
7568 ((r->blue & 0xc0) >> 2) |
7569 ((r->blue & 0xc0) >> 4) |
7570 ((r->blue & 0xc0) >> 6);
7572 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
7575 r->red= (((r->red & 0xe0) >> 8) |
7576 ((r->red & 0xe0) >> 11) |
7577 ((r->red & 0xc0) >> 14))*0x0101;
7578 r->green=(((r->green & 0xe0) >> 8) |
7579 ((r->green & 0xe0) >> 11) |
7580 ((r->green & 0xc0) >> 14))*0x0101;
7581 r->blue=(((r->blue & 0xc0)>> 8) |
7582 ((r->blue & 0xc0) >> 10) |
7583 ((r->blue & 0xc0) >> 12) |
7584 ((r->blue & 0xc0) >> 14))*0x0101;
7590 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7595 else /* Should not reach this; colormap already exists and
7598 if (logging != MagickFalse)
7599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7600 " Quantizing the colormap to 3-3-2");
7601 for (i=0; i<image_colors; i++)
7603 if (image->depth == 8)
7605 image->colormap[i].red=
7606 (image->colormap[i].red & 0xe0) |
7607 ((image->colormap[i].red & 0xe0) >> 3) |
7608 ((image->colormap[i].red & 0xc0) >> 6);
7609 image->colormap[i].green=
7610 (image->colormap[i].green & 0xe0) |
7611 ((image->colormap[i].green & 0xe0) >> 3) |
7612 ((image->colormap[i].green & 0xc0) >> 6);
7613 image->colormap[i].blue=
7614 (image->colormap[i].blue & 0xc0) |
7615 ((image->colormap[i].blue & 0xc0) >> 2) |
7616 ((image->colormap[i].blue & 0xc0) >> 4) |
7617 ((image->colormap[i].blue & 0xc0) >> 6);
7619 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
7622 image->colormap[i].red=
7623 (((image->colormap[i].red & 0xe0) >> 8) |
7624 ((image->colormap[i].red & 0xe0) >> 11) |
7625 ((image->colormap[i].red & 0xc0) >> 14))*0x0101;
7626 image->colormap[i].green=
7627 (((image->colormap[i].green & 0xe0) >> 8) |
7628 ((image->colormap[i].green & 0xe0) >> 11) |
7629 ((image->colormap[i].green & 0xc0) >> 14))*0x0101;
7630 image->colormap[i].blue=
7631 (((image->colormap[i].blue & 0xc0)>> 8) |
7632 ((image->colormap[i].blue & 0xc0) >> 10) |
7633 ((image->colormap[i].blue & 0xc0) >> 12) |
7634 ((image->colormap[i].blue & 0xc0) >> 14))*0x0101;
7643 /* END OF BUILD_PALETTE */
7645 /* If we are excluding the tRNS chunk and there is transparency,
7646 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7649 if (mng_info->ping_exclude_tRNS != MagickFalse &&
7650 (number_transparent != 0 || number_semitransparent != 0))
7652 int colortype=mng_info->write_png_colortype;
7654 if (ping_have_color == MagickFalse)
7655 mng_info->write_png_colortype = 5;
7658 mng_info->write_png_colortype = 7;
7660 if (colortype != 0 &&
7661 mng_info->write_png_colortype != (ssize_t) colortype)
7662 ping_need_colortype_warning=MagickTrue;
7666 /* See if cheap transparency is possible. It is only possible
7667 * when there is a single transparent color, no semitransparent
7668 * color, and no opaque color that has the same RGB components
7669 * as the transparent color. We only need this information if
7670 * we are writing a PNG with colortype 0 or 2, and we have not
7671 * excluded the tRNS chunk.
7673 if (number_transparent == 1 &&
7674 mng_info->write_png_colortype < 4)
7676 ping_have_cheap_transparency = MagickTrue;
7678 if (number_semitransparent != 0)
7679 ping_have_cheap_transparency = MagickFalse;
7681 else if (image_colors == 0 || image_colors > 256 ||
7682 image->colormap == NULL)
7687 register const PixelPacket
7690 exception=(&image->exception);
7692 for (y=0; y < (ssize_t) image->rows; y++)
7694 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
7696 if (q == (PixelPacket *) NULL)
7699 for (x=0; x < (ssize_t) image->columns; x++)
7701 if (q->opacity != TransparentOpacity &&
7702 (unsigned short) q->red == ping_trans_color.red &&
7703 (unsigned short) q->green == ping_trans_color.green &&
7704 (unsigned short) q->blue == ping_trans_color.blue)
7706 ping_have_cheap_transparency = MagickFalse;
7713 if (ping_have_cheap_transparency == MagickFalse)
7719 /* Assuming that image->colormap[0] is the one transparent color
7720 * and that all others are opaque.
7722 if (image_colors > 1)
7723 for (i=1; i<image_colors; i++)
7724 if (image->colormap[i].red == image->colormap[0].red &&
7725 image->colormap[i].green == image->colormap[0].green &&
7726 image->colormap[i].blue == image->colormap[0].blue)
7728 ping_have_cheap_transparency = MagickFalse;
7733 if (logging != MagickFalse)
7735 if (ping_have_cheap_transparency == MagickFalse)
7736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7737 " Cheap transparency is not possible.");
7740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7741 " Cheap transparency is possible.");
7745 ping_have_cheap_transparency = MagickFalse;
7747 image_depth=image->depth;
7749 quantum_info = (QuantumInfo *) NULL;
7751 image_colors=(int) image->colors;
7752 image_matte=image->matte;
7754 mng_info->IsPalette=image->storage_class == PseudoClass &&
7755 image_colors <= 256 && image->colormap != NULL;
7758 Allocate the PNG structures
7760 #ifdef PNG_USER_MEM_SUPPORTED
7761 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
7762 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
7763 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
7766 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
7767 MagickPNGErrorHandler,MagickPNGWarningHandler);
7770 if (ping == (png_struct *) NULL)
7771 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7773 ping_info=png_create_info_struct(ping);
7775 if (ping_info == (png_info *) NULL)
7777 png_destroy_write_struct(&ping,(png_info **) NULL);
7778 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7781 png_set_write_fn(ping,image,png_put_data,png_flush_data);
7782 ping_pixels=(unsigned char *) NULL;
7784 if (setjmp(png_jmpbuf(ping)))
7790 if (image_info->verbose)
7791 (void) printf("PNG write has failed.\n");
7793 png_destroy_write_struct(&ping,&ping_info);
7794 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7795 UnlockSemaphoreInfo(ping_semaphore);
7797 if (mng_info->need_blob != MagickFalse)
7798 (void) CloseBlob(image);
7799 image_info=DestroyImageInfo(image_info);
7800 image=DestroyImage(image);
7801 return(MagickFalse);
7804 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
7805 (image->colors == 0 || image->colormap == NULL))
7807 png_error(ping, "Cannot write PNG8 or color-type 3; colormap is NULL");
7811 Prepare PNG for writing.
7813 #if defined(PNG_MNG_FEATURES_SUPPORTED)
7814 if (mng_info->write_mng)
7815 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
7818 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
7819 if (mng_info->write_mng)
7820 png_permit_empty_plte(ping,MagickTrue);
7827 ping_width=(png_uint_32) image->columns;
7828 ping_height=(png_uint_32) image->rows;
7830 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
7833 if (mng_info->write_png_depth != 0)
7834 image_depth=mng_info->write_png_depth;
7836 /* Adjust requested depth to next higher valid depth if necessary */
7837 if (image_depth > 8)
7840 if ((image_depth > 4) && (image_depth < 8))
7843 if (image_depth == 3)
7846 if (logging != MagickFalse)
7848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7849 " width=%.20g",(double) ping_width);
7850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7851 " height=%.20g",(double) ping_height);
7852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7853 " image_matte=%.20g",(double) image->matte);
7854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7855 " image->depth=%.20g",(double) image->depth);
7856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7857 " Tentative ping_bit_depth=%.20g",(double) image_depth);
7860 save_image_depth=image_depth;
7861 ping_bit_depth=(png_byte) save_image_depth;
7864 #if defined(PNG_pHYs_SUPPORTED)
7865 if (ping_exclude_pHYs == MagickFalse)
7867 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
7868 (!mng_info->write_mng || !mng_info->equal_physs))
7870 if (logging != MagickFalse)
7871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7872 " Setting up pHYs chunk");
7874 if (image->units == PixelsPerInchResolution)
7876 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
7877 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
7878 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
7881 else if (image->units == PixelsPerCentimeterResolution)
7883 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
7884 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution);
7885 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution);
7890 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
7891 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
7892 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
7895 ping_have_pHYs = MagickTrue;
7900 if (ping_exclude_bKGD == MagickFalse)
7902 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
7908 if (ping_bit_depth == 8)
7911 if (ping_bit_depth == 4)
7914 if (ping_bit_depth == 2)
7917 if (ping_bit_depth == 1)
7920 ping_background.red=(png_uint_16)
7921 (ScaleQuantumToShort(image->background_color.red) & mask);
7923 ping_background.green=(png_uint_16)
7924 (ScaleQuantumToShort(image->background_color.green) & mask);
7926 ping_background.blue=(png_uint_16)
7927 (ScaleQuantumToShort(image->background_color.blue) & mask);
7930 if (logging != MagickFalse)
7932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7933 " Setting up bKGD chunk (1)");
7935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7936 " ping_bit_depth=%d",ping_bit_depth);
7939 ping_have_bKGD = MagickTrue;
7943 Select the color type.
7948 if (mng_info->IsPalette && mng_info->write_png8)
7951 /* To do: make this a function cause it's used twice, except
7952 for reducing the sample depth from 8. */
7954 number_colors=image_colors;
7956 ping_have_tRNS=MagickFalse;
7961 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
7963 if (logging != MagickFalse)
7964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7965 " Setting up PLTE chunk with %d colors (%d)",
7966 number_colors, image_colors);
7968 for (i=0; i < (ssize_t) number_colors; i++)
7970 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7971 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7972 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7973 if (logging != MagickFalse)
7974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7975 #if MAGICKCORE_QUANTUM_DEPTH == 8
7976 " %3ld (%3d,%3d,%3d)",
7978 " %5ld (%5d,%5d,%5d)",
7980 (long) i,palette[i].red,palette[i].green,palette[i].blue);
7984 ping_have_PLTE=MagickTrue;
7985 image_depth=ping_bit_depth;
7988 if (matte != MagickFalse)
7991 Identify which colormap entry is transparent.
7993 assert(number_colors <= 256);
7994 assert(image->colormap != NULL);
7996 for (i=0; i < (ssize_t) number_transparent; i++)
7997 ping_trans_alpha[i]=0;
8000 ping_num_trans=(unsigned short) (number_transparent +
8001 number_semitransparent);
8003 if (ping_num_trans == 0)
8004 ping_have_tRNS=MagickFalse;
8007 ping_have_tRNS=MagickTrue;
8010 if (ping_exclude_bKGD == MagickFalse)
8013 * Identify which colormap entry is the background color.
8016 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8017 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8020 ping_background.index=(png_byte) i;
8022 } /* end of write_png8 */
8024 else if (mng_info->write_png24)
8026 image_matte=MagickFalse;
8027 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8030 else if (mng_info->write_png32)
8032 image_matte=MagickTrue;
8033 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8036 else /* mng_info->write_pngNN not specified */
8038 image_depth=ping_bit_depth;
8040 if (mng_info->write_png_colortype != 0)
8042 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8044 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8045 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8046 image_matte=MagickTrue;
8049 image_matte=MagickFalse;
8052 else /* write_ping_colortype not specified */
8054 if (logging != MagickFalse)
8055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8056 " Selecting PNG colortype:");
8058 ping_color_type=(png_byte) ((matte != MagickFalse)?
8059 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8061 if (image_info->type == TrueColorType)
8063 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8064 image_matte=MagickFalse;
8067 if (image_info->type == TrueColorMatteType)
8069 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8070 image_matte=MagickTrue;
8073 if (image_info->type == PaletteType ||
8074 image_info->type == PaletteMatteType)
8075 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8077 if (image_info->type == UndefinedType ||
8078 image_info->type == OptimizeType)
8080 if (ping_have_color == MagickFalse)
8082 if (image_matte == MagickFalse)
8084 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8085 image_matte=MagickFalse;
8090 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8091 image_matte=MagickTrue;
8096 if (image_matte == MagickFalse)
8098 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8099 image_matte=MagickFalse;
8104 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8105 image_matte=MagickTrue;
8112 if (logging != MagickFalse)
8113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8114 " Selected PNG colortype=%d",ping_color_type);
8116 if (ping_bit_depth < 8)
8118 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8119 ping_color_type == PNG_COLOR_TYPE_RGB ||
8120 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8124 old_bit_depth=ping_bit_depth;
8126 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8128 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8132 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8137 if (image->colors == 0)
8140 (void) ThrowMagickException(&image->exception,
8141 GetMagickModule(),CoderError,
8142 "image has 0 colors", "`%s'","");
8145 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8146 ping_bit_depth <<= 1;
8149 if (logging != MagickFalse)
8151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8152 " Number of colors: %.20g",(double) image_colors);
8154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8155 " Tentative PNG bit depth: %d",ping_bit_depth);
8158 if (ping_bit_depth < (int) mng_info->write_png_depth)
8159 ping_bit_depth = mng_info->write_png_depth;
8162 image_depth=ping_bit_depth;
8164 if (logging != MagickFalse)
8166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8167 " Tentative PNG color type: %.20g",(double) ping_color_type);
8169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8170 " image_info->type: %.20g",(double) image_info->type);
8172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8173 " image_depth: %.20g",(double) image_depth);
8175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8177 " image->depth: %.20g",(double) image->depth);
8179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8180 " ping_bit_depth: %.20g",(double) ping_bit_depth);
8183 if (matte != MagickFalse)
8185 if (mng_info->IsPalette)
8187 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8189 if (ping_have_color != MagickFalse)
8190 ping_color_type=PNG_COLOR_TYPE_RGBA;
8193 * Determine if there is any transparent color.
8195 if (number_transparent + number_semitransparent == 0)
8198 No transparent pixels are present. Change 4 or 6 to 0 or 2.
8201 image_matte=MagickFalse;
8202 ping_color_type&=0x03;
8212 if (ping_bit_depth == 8)
8215 if (ping_bit_depth == 4)
8218 if (ping_bit_depth == 2)
8221 if (ping_bit_depth == 1)
8224 ping_trans_color.red=(png_uint_16)
8225 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8227 ping_trans_color.green=(png_uint_16)
8228 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8230 ping_trans_color.blue=(png_uint_16)
8231 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8233 ping_trans_color.gray=(png_uint_16)
8234 (ScaleQuantumToShort(PixelIntensityToQuantum(
8235 image->colormap)) & mask);
8237 ping_trans_color.index=(png_byte) 0;
8239 ping_have_tRNS=MagickTrue;
8242 if (ping_have_tRNS != MagickFalse)
8245 * Determine if there is one and only one transparent color
8246 * and if so if it is fully transparent.
8248 if (ping_have_cheap_transparency == MagickFalse)
8249 ping_have_tRNS=MagickFalse;
8252 if (ping_have_tRNS != MagickFalse)
8254 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
8256 if (image_depth == 8)
8258 ping_trans_color.red&=0xff;
8259 ping_trans_color.green&=0xff;
8260 ping_trans_color.blue&=0xff;
8261 ping_trans_color.gray&=0xff;
8267 if (image_depth == 8)
8269 ping_trans_color.red&=0xff;
8270 ping_trans_color.green&=0xff;
8271 ping_trans_color.blue&=0xff;
8272 ping_trans_color.gray&=0xff;
8279 if (ping_have_tRNS != MagickFalse)
8280 image_matte=MagickFalse;
8282 if ((mng_info->IsPalette) &&
8283 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8284 ping_have_color == MagickFalse &&
8285 (image_matte == MagickFalse || image_depth >= 8))
8289 if (image_matte != MagickFalse)
8290 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8294 ping_color_type=PNG_COLOR_TYPE_GRAY;
8296 if (save_image_depth == 16 && image_depth == 8)
8298 if (logging != MagickFalse)
8300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8301 " Scaling ping_trans_color (0)");
8303 ping_trans_color.gray*=0x0101;
8307 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8308 image_depth=MAGICKCORE_QUANTUM_DEPTH;
8310 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
8311 image_colors=(int) (one << image_depth);
8313 if (image_depth > 8)
8319 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8321 if(!mng_info->write_png_depth)
8325 while ((int) (one << ping_bit_depth)
8326 < (ssize_t) image_colors)
8327 ping_bit_depth <<= 1;
8331 #if 1 /* To do: Enable this when low bit-depth grayscale is working */
8332 else if (ping_color_type ==
8333 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8334 mng_info->IsPalette)
8337 /* Check if grayscale is reducible */
8339 depth_4_ok=MagickTrue,
8340 depth_2_ok=MagickTrue,
8341 depth_1_ok=MagickTrue;
8343 for (i=0; i < (ssize_t) image_colors; i++)
8348 intensity=ScaleQuantumToChar(image->colormap[i].red);
8350 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8351 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8353 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8354 depth_2_ok=depth_1_ok=MagickFalse;
8356 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8357 depth_1_ok=MagickFalse;
8360 if (depth_1_ok && mng_info->write_png_depth <= 1)
8363 #if 0 /* To do: Enable this when bit depths 2 and 4 are working. */
8364 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8367 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8374 image_depth=ping_bit_depth;
8379 if (mng_info->IsPalette)
8381 number_colors=image_colors;
8383 if (image_depth <= 8)
8388 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8390 if (mng_info->have_write_global_plte && matte == MagickFalse)
8392 png_set_PLTE(ping,ping_info,NULL,0);
8394 if (logging != MagickFalse)
8395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8396 " Setting up empty PLTE chunk");
8401 for (i=0; i < (ssize_t) number_colors; i++)
8403 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8404 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8405 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8408 if (logging != MagickFalse)
8409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8410 " Setting up PLTE chunk with %d colors",
8413 ping_have_PLTE=MagickTrue;
8416 /* color_type is PNG_COLOR_TYPE_PALETTE */
8417 if (mng_info->write_png_depth == 0)
8425 while ((one << ping_bit_depth) < number_colors)
8426 ping_bit_depth <<= 1;
8431 if (matte != MagickFalse)
8434 * Set up trans_colors array.
8436 assert(number_colors <= 256);
8438 ping_num_trans=(unsigned short) (number_transparent +
8439 number_semitransparent);
8441 if (ping_num_trans == 0)
8442 ping_have_tRNS=MagickFalse;
8446 if (logging != MagickFalse)
8448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8449 " Scaling ping_trans_color (1)");
8451 ping_have_tRNS=MagickTrue;
8453 for (i=0; i < ping_num_trans; i++)
8455 ping_trans_alpha[i]= (png_byte) (255-
8456 ScaleQuantumToChar(image->colormap[i].opacity));
8466 if (image_depth < 8)
8469 if ((save_image_depth == 16) && (image_depth == 8))
8471 if (logging != MagickFalse)
8473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8474 " Scaling ping_trans_color from (%d,%d,%d)",
8475 (int) ping_trans_color.red,
8476 (int) ping_trans_color.green,
8477 (int) ping_trans_color.blue);
8480 ping_trans_color.red*=0x0101;
8481 ping_trans_color.green*=0x0101;
8482 ping_trans_color.blue*=0x0101;
8483 ping_trans_color.gray*=0x0101;
8485 if (logging != MagickFalse)
8487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 (int) ping_trans_color.red,
8490 (int) ping_trans_color.green,
8491 (int) ping_trans_color.blue);
8496 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8497 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
8500 Adjust background and transparency samples in sub-8-bit grayscale files.
8502 if (ping_bit_depth < 8 && ping_color_type ==
8503 PNG_COLOR_TYPE_GRAY)
8511 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8513 if (ping_exclude_bKGD == MagickFalse)
8516 ping_background.gray=(png_uint_16)
8517 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8519 if (logging != MagickFalse)
8520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8521 " Setting up bKGD chunk (2)");
8523 ping_have_bKGD = MagickTrue;
8526 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8527 ping_trans_color.gray));
8530 if (ping_exclude_bKGD == MagickFalse)
8532 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8535 Identify which colormap entry is the background color.
8538 number_colors=image_colors;
8540 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8541 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8544 ping_background.index=(png_byte) i;
8546 if (logging != MagickFalse)
8548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8549 " Setting up bKGD chunk with index=%d",(int) i);
8552 if (i < (ssize_t) number_colors)
8554 ping_have_bKGD = MagickTrue;
8556 if (logging != MagickFalse)
8558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8559 " background =(%d,%d,%d)",
8560 (int) ping_background.red,
8561 (int) ping_background.green,
8562 (int) ping_background.blue);
8566 else /* Can't happen */
8568 if (logging != MagickFalse)
8569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8570 " No room in PLTE to add bKGD color");
8571 ping_have_bKGD = MagickFalse;
8576 if (logging != MagickFalse)
8577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8578 " PNG color type: %d",ping_color_type);
8580 Initialize compression level and filtering.
8582 if (logging != MagickFalse)
8584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8585 " Setting up deflate compression");
8587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8588 " Compression buffer size: 32768");
8591 png_set_compression_buffer_size(ping,32768L);
8593 if (logging != MagickFalse)
8594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8595 " Compression mem level: 9");
8597 png_set_compression_mem_level(ping, 9);
8599 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8607 level=(int) MagickMin((ssize_t) quality/10,9);
8609 if (logging != MagickFalse)
8610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8611 " Compression level: %d",level);
8613 png_set_compression_level(ping,level);
8618 if (logging != MagickFalse)
8619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8620 " Compression strategy: Z_HUFFMAN_ONLY");
8622 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8625 if (logging != MagickFalse)
8626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8627 " Setting up filtering");
8629 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8630 /* This became available in libpng-1.0.9. Output must be a MNG. */
8631 if (mng_info->write_mng && ((quality % 10) == 7))
8633 if (logging != MagickFalse)
8634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8635 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8637 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8641 if (logging != MagickFalse)
8642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8650 if ((quality % 10) > 5)
8651 base_filter=PNG_ALL_FILTERS;
8654 if ((quality % 10) != 5)
8655 base_filter=(int) quality % 10;
8658 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8659 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8661 base_filter=PNG_NO_FILTERS;
8664 base_filter=PNG_ALL_FILTERS;
8666 if (logging != MagickFalse)
8668 if (base_filter == PNG_ALL_FILTERS)
8669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8670 " Base filter method: ADAPTIVE");
8672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8673 " Base filter method: NONE");
8676 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8679 if (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse)
8681 ResetImageProfileIterator(image);
8682 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8684 profile=GetImageProfile(image,name);
8686 if (profile != (StringInfo *) NULL)
8688 #ifdef PNG_WRITE_iCCP_SUPPORTED
8689 if ((LocaleCompare(name,"ICC") == 0) ||
8690 (LocaleCompare(name,"ICM") == 0))
8693 if (ping_exclude_iCCP == MagickFalse)
8695 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8696 #if (PNG_LIBPNG_VER < 10500)
8697 (png_charp) GetStringInfoDatum(profile),
8699 (png_const_bytep) GetStringInfoDatum(profile),
8701 (png_uint_32) GetStringInfoLength(profile));
8707 if (ping_exclude_zCCP == MagickFalse)
8709 Magick_png_write_raw_profile(image_info,ping,ping_info,
8710 (unsigned char *) name,(unsigned char *) name,
8711 GetStringInfoDatum(profile),
8712 (png_uint_32) GetStringInfoLength(profile));
8716 if (logging != MagickFalse)
8717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8718 " Setting up text chunk with %s profile",name);
8720 name=GetNextImageProfile(image);
8724 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8725 if ((mng_info->have_write_global_srgb == 0) &&
8726 ((image->rendering_intent != UndefinedIntent) ||
8727 (image->colorspace == sRGBColorspace)))
8729 if (ping_exclude_sRGB == MagickFalse)
8732 Note image rendering intent.
8734 if (logging != MagickFalse)
8735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8736 " Setting up sRGB chunk");
8738 (void) png_set_sRGB(ping,ping_info,(
8739 Magick_RenderingIntent_to_PNG_RenderingIntent(
8740 image->rendering_intent)));
8742 if (ping_exclude_gAMA == MagickFalse)
8743 png_set_gAMA(ping,ping_info,0.45455);
8747 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8750 if (ping_exclude_gAMA == MagickFalse &&
8751 (ping_exclude_sRGB == MagickFalse ||
8752 (image->gamma < .45 || image->gamma > .46)))
8754 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8758 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8760 if (logging != MagickFalse)
8761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8762 " Setting up gAMA chunk");
8764 png_set_gAMA(ping,ping_info,image->gamma);
8768 if (ping_exclude_cHRM == MagickFalse)
8770 if ((mng_info->have_write_global_chrm == 0) &&
8771 (image->chromaticity.red_primary.x != 0.0))
8774 Note image chromaticity.
8775 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8783 wp=image->chromaticity.white_point;
8784 rp=image->chromaticity.red_primary;
8785 gp=image->chromaticity.green_primary;
8786 bp=image->chromaticity.blue_primary;
8788 if (logging != MagickFalse)
8789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8790 " Setting up cHRM chunk");
8792 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
8798 ping_interlace_method=image_info->interlace != NoInterlace;
8800 if (mng_info->write_mng)
8801 png_set_sig_bytes(ping,8);
8803 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
8805 if (mng_info->write_png_colortype != 0)
8807 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
8808 if (ping_have_color != MagickFalse)
8810 ping_color_type = PNG_COLOR_TYPE_RGB;
8812 if (ping_bit_depth < 8)
8816 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
8817 if (ping_have_color != MagickFalse)
8818 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
8821 if (ping_need_colortype_warning != MagickFalse ||
8822 ((mng_info->write_png_depth &&
8823 (int) mng_info->write_png_depth != ping_bit_depth) ||
8824 (mng_info->write_png_colortype &&
8825 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
8826 mng_info->write_png_colortype != 7 &&
8827 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
8829 if (logging != MagickFalse)
8831 if (ping_need_colortype_warning != MagickFalse)
8833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8834 " Image has transparency but tRNS chunk was excluded");
8837 if (mng_info->write_png_depth)
8839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8840 " Defined PNG:bit-depth=%u, Computed depth=%u",
8841 mng_info->write_png_depth,
8845 if (mng_info->write_png_colortype)
8847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8848 " Defined PNG:color-type=%u, Computed color type=%u",
8849 mng_info->write_png_colortype-1,
8855 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
8858 if (image_matte != MagickFalse && image->matte == MagickFalse)
8860 /* Add an opaque matte channel */
8861 image->matte = MagickTrue;
8862 (void) SetImageOpacity(image,0);
8864 if (logging != MagickFalse)
8865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8866 " Added an opaque matte channel");
8869 if (number_transparent != 0 || number_semitransparent != 0)
8871 if (ping_color_type < 4)
8873 ping_have_tRNS=MagickTrue;
8874 if (logging != MagickFalse)
8875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8876 " Setting ping_have_tRNS=MagickTrue.");
8880 if (logging != MagickFalse)
8881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8882 " Writing PNG header chunks");
8884 png_set_IHDR(ping,ping_info,ping_width,ping_height,
8885 ping_bit_depth,ping_color_type,
8886 ping_interlace_method,ping_compression_method,
8887 ping_filter_method);
8889 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
8891 png_set_PLTE(ping,ping_info,palette,number_colors);
8893 if (logging != MagickFalse)
8895 for (i=0; i< (ssize_t) number_colors; i++)
8897 if (i < ping_num_trans)
8898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8899 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
8901 (int) palette[i].red,
8902 (int) palette[i].green,
8903 (int) palette[i].blue,
8905 (int) ping_trans_alpha[i]);
8907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8908 " PLTE[%d] = (%d,%d,%d)",
8910 (int) palette[i].red,
8911 (int) palette[i].green,
8912 (int) palette[i].blue);
8917 if (ping_exclude_bKGD == MagickFalse)
8919 if (ping_have_bKGD != MagickFalse)
8920 png_set_bKGD(ping,ping_info,&ping_background);
8923 if (ping_exclude_pHYs == MagickFalse)
8925 if (ping_have_pHYs != MagickFalse)
8927 png_set_pHYs(ping,ping_info,
8928 ping_pHYs_x_resolution,
8929 ping_pHYs_y_resolution,
8930 ping_pHYs_unit_type);
8934 #if defined(PNG_oFFs_SUPPORTED)
8935 if (ping_exclude_oFFs == MagickFalse)
8937 if (image->page.x || image->page.y)
8939 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
8940 (png_int_32) image->page.y, 0);
8942 if (logging != MagickFalse)
8943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8944 " Setting up oFFs chunk with x=%d, y=%d, units=0",
8945 (int) image->page.x, (int) image->page.y);
8950 png_write_info_before_PLTE(ping, ping_info);
8952 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
8954 if (logging != MagickFalse)
8956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8957 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
8960 if (ping_color_type == 3)
8961 (void) png_set_tRNS(ping, ping_info,
8968 (void) png_set_tRNS(ping, ping_info,
8973 if (logging != MagickFalse)
8975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8976 " tRNS color =(%d,%d,%d)",
8977 (int) ping_trans_color.red,
8978 (int) ping_trans_color.green,
8979 (int) ping_trans_color.blue);
8984 /* write any png-chunk-b profiles */
8985 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
8986 png_write_info(ping,ping_info);
8988 /* write any PNG-chunk-m profiles */
8989 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
8991 if (ping_exclude_vpAg == MagickFalse)
8993 if ((image->page.width != 0 && image->page.width != image->columns) ||
8994 (image->page.height != 0 && image->page.height != image->rows))
8999 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
9000 PNGType(chunk,mng_vpAg);
9001 LogPNGChunk(logging,mng_vpAg,9L);
9002 PNGLong(chunk+4,(png_uint_32) image->page.width);
9003 PNGLong(chunk+8,(png_uint_32) image->page.height);
9004 chunk[12]=0; /* unit = pixels */
9005 (void) WriteBlob(image,13,chunk);
9006 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9010 #if (PNG_LIBPNG_VER == 10206)
9011 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9012 #define PNG_HAVE_IDAT 0x04
9013 ping->mode |= PNG_HAVE_IDAT;
9014 #undef PNG_HAVE_IDAT
9017 png_set_packing(ping);
9021 rowbytes=image->columns;
9022 if (image_depth > 8)
9024 switch (ping_color_type)
9026 case PNG_COLOR_TYPE_RGB:
9030 case PNG_COLOR_TYPE_GRAY_ALPHA:
9034 case PNG_COLOR_TYPE_RGBA:
9042 if (logging != MagickFalse)
9044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9045 " Writing PNG image data");
9047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9048 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9050 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9051 sizeof(*ping_pixels));
9053 if (ping_pixels == (unsigned char *) NULL)
9054 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9057 Initialize image scanlines.
9059 if (setjmp(png_jmpbuf(ping)))
9065 if (image_info->verbose)
9066 (void) printf("PNG write has failed.\n");
9068 png_destroy_write_struct(&ping,&ping_info);
9069 if (quantum_info != (QuantumInfo *) NULL)
9070 quantum_info=DestroyQuantumInfo(quantum_info);
9071 if (ping_pixels != (unsigned char *) NULL)
9072 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9073 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9074 UnlockSemaphoreInfo(ping_semaphore);
9076 if (mng_info->need_blob != MagickFalse)
9077 (void) CloseBlob(image);
9078 image_info=DestroyImageInfo(image_info);
9079 image=DestroyImage(image);
9080 return(MagickFalse);
9082 quantum_info=AcquireQuantumInfo(image_info,image);
9083 if (quantum_info == (QuantumInfo *) NULL)
9084 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9085 quantum_info->format=UndefinedQuantumFormat;
9086 quantum_info->depth=image_depth;
9087 num_passes=png_set_interlace_handling(ping);
9089 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9090 !mng_info->write_png32) &&
9091 (mng_info->IsPalette ||
9092 (image_info->type == BilevelType)) &&
9093 image_matte == MagickFalse &&
9094 ping_have_non_bw == MagickFalse)
9096 /* Palette, Bilevel, or Opaque Monochrome */
9097 register const PixelPacket
9100 quantum_info->depth=8;
9101 for (pass=0; pass < num_passes; pass++)
9104 Convert PseudoClass image to a PNG monochrome image.
9106 for (y=0; y < (ssize_t) image->rows; y++)
9108 if (logging != MagickFalse && y == 0)
9109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9110 " Writing row of pixels (0)");
9112 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9114 if (p == (const PixelPacket *) NULL)
9117 if (mng_info->IsPalette)
9119 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9120 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9121 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9122 mng_info->write_png_depth &&
9123 mng_info->write_png_depth != old_bit_depth)
9125 /* Undo pixel scaling */
9126 for (i=0; i < (ssize_t) image->columns; i++)
9127 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9128 >> (8-old_bit_depth));
9134 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9135 quantum_info,RedQuantum,ping_pixels,&image->exception);
9138 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9139 for (i=0; i < (ssize_t) image->columns; i++)
9140 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9143 if (logging != MagickFalse && y == 0)
9144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9145 " Writing row of pixels (1)");
9147 png_write_row(ping,ping_pixels);
9149 if (image->previous == (Image *) NULL)
9151 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9152 if (status == MagickFalse)
9158 else /* Not Palette, Bilevel, or Opaque Monochrome */
9160 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9161 !mng_info->write_png32) &&
9162 (image_matte != MagickFalse ||
9163 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9164 (mng_info->IsPalette) && ping_have_color == MagickFalse)
9166 register const PixelPacket
9169 for (pass=0; pass < num_passes; pass++)
9172 for (y=0; y < (ssize_t) image->rows; y++)
9174 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9176 if (p == (const PixelPacket *) NULL)
9179 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9181 if (mng_info->IsPalette)
9182 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9183 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9186 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9187 quantum_info,RedQuantum,ping_pixels,&image->exception);
9189 if (logging != MagickFalse && y == 0)
9190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9191 " Writing GRAY PNG pixels (2)");
9194 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9196 if (logging != MagickFalse && y == 0)
9197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9198 " Writing GRAY_ALPHA PNG pixels (2)");
9200 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9201 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9204 if (logging != MagickFalse && y == 0)
9205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9206 " Writing row of pixels (2)");
9208 png_write_row(ping,ping_pixels);
9211 if (image->previous == (Image *) NULL)
9213 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9214 if (status == MagickFalse)
9222 register const PixelPacket
9225 for (pass=0; pass < num_passes; pass++)
9227 if ((image_depth > 8) || (mng_info->write_png24 ||
9228 mng_info->write_png32 ||
9229 (!mng_info->write_png8 && !mng_info->IsPalette)))
9231 for (y=0; y < (ssize_t) image->rows; y++)
9233 p=GetVirtualPixels(image,0,y,image->columns,1,
9236 if (p == (const PixelPacket *) NULL)
9239 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9241 if (image->storage_class == DirectClass)
9242 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9243 quantum_info,RedQuantum,ping_pixels,&image->exception);
9246 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9247 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9250 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9252 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9253 quantum_info,GrayAlphaQuantum,ping_pixels,
9256 if (logging != MagickFalse && y == 0)
9257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9258 " Writing GRAY_ALPHA PNG pixels (3)");
9261 else if (image_matte != MagickFalse)
9262 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9263 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9266 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9267 quantum_info,RGBQuantum,ping_pixels,&image->exception);
9269 if (logging != MagickFalse && y == 0)
9270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9271 " Writing row of pixels (3)");
9273 png_write_row(ping,ping_pixels);
9278 /* not ((image_depth > 8) || (mng_info->write_png24 ||
9279 mng_info->write_png32 ||
9280 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9282 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9283 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9285 if (logging != MagickFalse)
9286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9287 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9289 quantum_info->depth=8;
9293 for (y=0; y < (ssize_t) image->rows; y++)
9295 if (logging != MagickFalse && y == 0)
9296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9297 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9299 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9301 if (p == (const PixelPacket *) NULL)
9304 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9305 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9306 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9308 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9310 if (logging != MagickFalse && y == 0)
9311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9312 " Writing GRAY_ALPHA PNG pixels (4)");
9314 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9315 quantum_info,GrayAlphaQuantum,ping_pixels,
9323 * This is failing to account for 1, 2, 4-bit depths
9324 * The call to png_set_packing() above is supposed to
9325 * take care of those.
9328 /* GrayQuantum does not work here */
9329 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9330 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9332 if (logging != MagickFalse && y <= 2)
9334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9335 " Writing row of pixels (4)");
9337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9338 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9339 (int)ping_pixels[0],(int)ping_pixels[1]);
9342 png_write_row(ping,ping_pixels);
9346 if (image->previous == (Image *) NULL)
9348 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9349 if (status == MagickFalse)
9356 if (quantum_info != (QuantumInfo *) NULL)
9357 quantum_info=DestroyQuantumInfo(quantum_info);
9359 if (logging != MagickFalse)
9361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9362 " Wrote PNG image data");
9364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9365 " Width: %.20g",(double) ping_width);
9367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9368 " Height: %.20g",(double) ping_height);
9370 if (mng_info->write_png_depth)
9372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9373 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9377 " PNG bit-depth written: %d",ping_bit_depth);
9379 if (mng_info->write_png_colortype)
9381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9382 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9386 " PNG color-type written: %d",ping_color_type);
9388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9389 " PNG Interlace method: %d",ping_interlace_method);
9392 Generate text chunks.
9394 if (ping_exclude_tEXt == MagickFalse && ping_exclude_zTXt == MagickFalse)
9396 ResetImagePropertyIterator(image);
9397 property=GetNextImageProperty(image);
9398 while (property != (const char *) NULL)
9403 value=GetImageProperty(image,property);
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);
9437 property=GetNextImageProperty(image);
9441 /* write any PNG-chunk-e profiles */
9442 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9444 if (logging != MagickFalse)
9445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9446 " Writing PNG end info");
9448 png_write_end(ping,ping_info);
9450 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9452 if (mng_info->page.x || mng_info->page.y ||
9453 (ping_width != mng_info->page.width) ||
9454 (ping_height != mng_info->page.height))
9460 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9462 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9463 PNGType(chunk,mng_FRAM);
9464 LogPNGChunk(logging,mng_FRAM,27L);
9466 chunk[5]=0; /* frame name separator (no name) */
9467 chunk[6]=1; /* flag for changing delay, for next frame only */
9468 chunk[7]=0; /* flag for changing frame timeout */
9469 chunk[8]=1; /* flag for changing frame clipping for next frame */
9470 chunk[9]=0; /* flag for changing frame sync_id */
9471 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9472 chunk[14]=0; /* clipping boundaries delta type */
9473 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9475 (png_uint_32) (mng_info->page.x + ping_width));
9476 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9478 (png_uint_32) (mng_info->page.y + ping_height));
9479 (void) WriteBlob(image,31,chunk);
9480 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9481 mng_info->old_framing_mode=4;
9482 mng_info->framing_mode=1;
9486 mng_info->framing_mode=3;
9488 if (mng_info->write_mng && !mng_info->need_fram &&
9489 ((int) image->dispose == 3))
9490 (void) ThrowMagickException(&image->exception,GetMagickModule(),
9491 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9492 "`%s'",image->filename);
9498 png_destroy_write_struct(&ping,&ping_info);
9500 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9502 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9503 UnlockSemaphoreInfo(ping_semaphore);
9506 if (mng_info->need_blob != MagickFalse)
9507 (void) CloseBlob(image);
9509 image_info=DestroyImageInfo(image_info);
9510 image=DestroyImage(image);
9512 /* Store bit depth actually written */
9513 s[0]=(char) ping_bit_depth;
9516 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9518 if (logging != MagickFalse)
9519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9520 " exit WriteOnePNGImage()");
9523 /* End write one PNG image */
9527 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9531 % W r i t e P N G I m a g e %
9535 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9537 % WritePNGImage() writes a Portable Network Graphics (PNG) or
9538 % Multiple-image Network Graphics (MNG) image file.
9540 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
9542 % The format of the WritePNGImage method is:
9544 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9546 % A description of each parameter follows:
9548 % o image_info: the image info.
9550 % o image: The image.
9552 % Returns MagickTrue on success, MagickFalse on failure.
9554 % Communicating with the PNG encoder:
9556 % While the datastream written is always in PNG format and normally would
9557 % be given the "png" file extension, this method also writes the following
9558 % pseudo-formats which are subsets of PNG:
9560 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
9561 % a depth greater than 8, the depth is reduced. If transparency
9562 % is present, the tRNS chunk must only have values 0 and 255
9563 % (i.e., transparency is binary: fully opaque or fully
9564 % transparent). If other values are present they will be
9565 % 50%-thresholded to binary transparency. If more than 256
9566 % colors are present, they will be quantized to the 3-3-2
9567 % palette. If you want better quantization or dithering of
9568 % the colors or alpha, you need to do it before calling the
9569 % PNG encoder. The pixels contain 8-bit indices even if
9570 % they could be represented with 1, 2, or 4 bits. Grayscale
9571 % images will be written as indexed PNG files even though the
9572 % PNG grayscale type might be slightly more efficient. Please
9573 % note that writing to the PNG8 format may result in loss
9574 % of color and alpha data.
9576 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9577 % chunk can be present to convey binary transparency by naming
9578 % one of the colors as transparent. The only loss incurred
9579 % is reduction of sample depth to 8. If the image has more
9580 % than one transparent color, has semitransparent pixels, or
9581 % has an opaque pixel with the same RGB components as the
9582 % transparent color, an image is not written.
9584 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
9585 % transparency is permitted, i.e., the alpha sample for
9586 % each pixel can have any value from 0 to 255. The alpha
9587 % channel is present even if the image is fully opaque.
9588 % The only loss in data is the reduction of the sample depth
9591 % o -define: For more precise control of the PNG output, you can use the
9592 % Image options "png:bit-depth" and "png:color-type". These
9593 % can be set from the commandline with "-define" and also
9594 % from the application programming interfaces. The options
9595 % are case-independent and are converted to lowercase before
9596 % being passed to this encoder.
9598 % png:color-type can be 0, 2, 3, 4, or 6.
9600 % When png:color-type is 0 (Grayscale), png:bit-depth can
9601 % be 1, 2, 4, 8, or 16.
9603 % When png:color-type is 2 (RGB), png:bit-depth can
9606 % When png:color-type is 3 (Indexed), png:bit-depth can
9607 % be 1, 2, 4, or 8. This refers to the number of bits
9608 % used to store the index. The color samples always have
9609 % bit-depth 8 in indexed PNG files.
9611 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9612 % png:bit-depth can be 8 or 16.
9614 % If the image cannot be written without loss with the requested bit-depth
9615 % and color-type, a PNG file will not be written, and the encoder will
9616 % return MagickFalse.
9618 % Since image encoders should not be responsible for the "heavy lifting",
9619 % the user should make sure that ImageMagick has already reduced the
9620 % image depth and number of colors and limit transparency to binary
9621 % transparency prior to attempting to write the image with depth, color,
9622 % or transparency limitations.
9624 % TODO: Enforce the previous paragraph.
9626 % Note that another definition, "png:bit-depth-written" exists, but it
9627 % is not intended for external use. It is only used internally by the
9628 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9630 % It is possible to request that the PNG encoder write previously-formatted
9631 % ancillary chunks in the output PNG file, using the "-profile" commandline
9632 % option as shown below or by setting the profile via a programming
9635 % -profile PNG-chunk-x:<file>
9637 % where x is a location flag and <file> is a file containing the chunk
9638 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9639 % This encoder will compute the chunk length and CRC, so those must not
9640 % be included in the file.
9642 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9643 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
9644 % of the same type, then add a short unique string after the "x" to prevent
9645 % subsequent profiles from overwriting the preceding ones, e.g.,
9647 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9649 % As of version 6.6.6 the following optimizations are always done:
9651 % o 32-bit depth is reduced to 16.
9652 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
9653 % high byte and low byte are identical.
9654 % o Palette is sorted to remove unused entries and to put a
9655 % transparent color first, if BUILD_PNG_PALETTE is defined.
9656 % o Opaque matte channel is removed when writing an indexed PNG.
9657 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
9658 % this can be done without loss and a larger bit depth N was not
9659 % requested via the "-define PNG:bit-depth=N" option.
9660 % o If matte channel is present but only one transparent color is
9661 % present, RGB+tRNS is written instead of RGBA
9662 % o Opaque matte channel is removed (or added, if color-type 4 or 6
9663 % was requested when converting an opaque image).
9665 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9667 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9689 assert(image_info != (const ImageInfo *) NULL);
9690 assert(image_info->signature == MagickSignature);
9691 assert(image != (Image *) NULL);
9692 assert(image->signature == MagickSignature);
9693 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9694 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
9696 Allocate a MngInfo structure.
9698 have_mng_structure=MagickFalse;
9699 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9701 if (mng_info == (MngInfo *) NULL)
9702 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9705 Initialize members of the MngInfo structure.
9707 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9708 mng_info->image=image;
9709 mng_info->equal_backgrounds=MagickTrue;
9710 have_mng_structure=MagickTrue;
9712 /* See if user has requested a specific PNG subformat */
9714 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9715 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9716 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9718 if (mng_info->write_png8)
9720 mng_info->write_png_colortype = /* 3 */ 4;
9721 mng_info->write_png_depth = 8;
9725 if (mng_info->write_png24)
9727 mng_info->write_png_colortype = /* 2 */ 3;
9728 mng_info->write_png_depth = 8;
9731 if (image->matte == MagickTrue)
9732 (void) SetImageType(image,TrueColorMatteType);
9735 (void) SetImageType(image,TrueColorType);
9737 (void) SyncImage(image);
9740 if (mng_info->write_png32)
9742 mng_info->write_png_colortype = /* 6 */ 7;
9743 mng_info->write_png_depth = 8;
9746 if (image->matte == MagickTrue)
9747 (void) SetImageType(image,TrueColorMatteType);
9750 (void) SetImageType(image,TrueColorType);
9752 (void) SyncImage(image);
9755 value=GetImageOption(image_info,"png:bit-depth");
9757 if (value != (char *) NULL)
9759 if (LocaleCompare(value,"1") == 0)
9760 mng_info->write_png_depth = 1;
9762 else if (LocaleCompare(value,"2") == 0)
9763 mng_info->write_png_depth = 2;
9765 else if (LocaleCompare(value,"4") == 0)
9766 mng_info->write_png_depth = 4;
9768 else if (LocaleCompare(value,"8") == 0)
9769 mng_info->write_png_depth = 8;
9771 else if (LocaleCompare(value,"16") == 0)
9772 mng_info->write_png_depth = 16;
9775 (void) ThrowMagickException(&image->exception,
9776 GetMagickModule(),CoderWarning,
9777 "ignoring invalid defined png:bit-depth",
9780 if (logging != MagickFalse)
9781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9782 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
9785 value=GetImageOption(image_info,"png:color-type");
9787 if (value != (char *) NULL)
9789 /* We must store colortype+1 because 0 is a valid colortype */
9790 if (LocaleCompare(value,"0") == 0)
9791 mng_info->write_png_colortype = 1;
9793 else if (LocaleCompare(value,"2") == 0)
9794 mng_info->write_png_colortype = 3;
9796 else if (LocaleCompare(value,"3") == 0)
9797 mng_info->write_png_colortype = 4;
9799 else if (LocaleCompare(value,"4") == 0)
9800 mng_info->write_png_colortype = 5;
9802 else if (LocaleCompare(value,"6") == 0)
9803 mng_info->write_png_colortype = 7;
9806 (void) ThrowMagickException(&image->exception,
9807 GetMagickModule(),CoderWarning,
9808 "ignoring invalid defined png:color-type",
9811 if (logging != MagickFalse)
9812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9813 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
9816 /* Check for chunks to be excluded:
9818 * The default is to not exclude any known chunks except for any
9819 * listed in the "unused_chunks" array, above.
9821 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
9822 * define (in the image properties or in the image artifacts)
9823 * or via a mng_info member. For convenience, in addition
9824 * to or instead of a comma-separated list of chunks, the
9825 * "exclude-chunk" string can be simply "all" or "none".
9827 * The exclude-chunk define takes priority over the mng_info.
9829 * A "PNG:include-chunk" define takes priority over both the
9830 * mng_info and the "PNG:exclude-chunk" define. Like the
9831 * "exclude-chunk" string, it can define "all" or "none" as
9832 * well as a comma-separated list. Chunks that are unknown to
9833 * ImageMagick are always excluded, regardless of their "copy-safe"
9834 * status according to the PNG specification, and even if they
9835 * appear in the "include-chunk" list.
9837 * Finally, all chunks listed in the "unused_chunks" array are
9838 * automatically excluded, regardless of the other instructions
9841 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
9842 * will not be written and the gAMA chunk will only be written if it
9843 * is not between .45 and .46, or approximately (1.0/2.2).
9845 * If you exclude tRNS and the image has transparency, the colortype
9846 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
9848 * The -strip option causes StripImage() to set the png:include-chunk
9849 * artifact to "none,gama".
9852 mng_info->ping_exclude_bKGD=MagickFalse;
9853 mng_info->ping_exclude_cHRM=MagickFalse;
9854 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
9855 mng_info->ping_exclude_gAMA=MagickFalse;
9856 mng_info->ping_exclude_cHRM=MagickFalse;
9857 mng_info->ping_exclude_iCCP=MagickFalse;
9858 /* mng_info->ping_exclude_iTXt=MagickFalse; */
9859 mng_info->ping_exclude_oFFs=MagickFalse;
9860 mng_info->ping_exclude_pHYs=MagickFalse;
9861 mng_info->ping_exclude_sRGB=MagickFalse;
9862 mng_info->ping_exclude_tEXt=MagickFalse;
9863 mng_info->ping_exclude_tRNS=MagickFalse;
9864 mng_info->ping_exclude_vpAg=MagickFalse;
9865 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
9866 mng_info->ping_exclude_zTXt=MagickFalse;
9868 excluding=MagickFalse;
9870 for (source=0; source<1; source++)
9874 value=GetImageArtifact(image,"png:exclude-chunk");
9877 value=GetImageArtifact(image,"png:exclude-chunks");
9881 value=GetImageOption(image_info,"png:exclude-chunk");
9884 value=GetImageOption(image_info,"png:exclude-chunks");
9893 excluding=MagickTrue;
9895 if (logging != MagickFalse)
9898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9899 " png:exclude-chunk=%s found in image artifacts.\n", value);
9901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9902 " png:exclude-chunk=%s found in image properties.\n", value);
9907 for (i=0; i<(int) last; i+=5)
9910 if (LocaleNCompare(value+i,"all",3) == 0)
9912 mng_info->ping_exclude_bKGD=MagickTrue;
9913 mng_info->ping_exclude_cHRM=MagickTrue;
9914 mng_info->ping_exclude_EXIF=MagickTrue;
9915 mng_info->ping_exclude_gAMA=MagickTrue;
9916 mng_info->ping_exclude_iCCP=MagickTrue;
9917 /* mng_info->ping_exclude_iTXt=MagickTrue; */
9918 mng_info->ping_exclude_oFFs=MagickTrue;
9919 mng_info->ping_exclude_pHYs=MagickTrue;
9920 mng_info->ping_exclude_sRGB=MagickTrue;
9921 mng_info->ping_exclude_tEXt=MagickTrue;
9922 mng_info->ping_exclude_tRNS=MagickTrue;
9923 mng_info->ping_exclude_vpAg=MagickTrue;
9924 mng_info->ping_exclude_zCCP=MagickTrue;
9925 mng_info->ping_exclude_zTXt=MagickTrue;
9929 if (LocaleNCompare(value+i,"none",4) == 0)
9931 mng_info->ping_exclude_bKGD=MagickFalse;
9932 mng_info->ping_exclude_cHRM=MagickFalse;
9933 mng_info->ping_exclude_EXIF=MagickFalse;
9934 mng_info->ping_exclude_gAMA=MagickFalse;
9935 mng_info->ping_exclude_iCCP=MagickFalse;
9936 /* mng_info->ping_exclude_iTXt=MagickFalse; */
9937 mng_info->ping_exclude_oFFs=MagickFalse;
9938 mng_info->ping_exclude_pHYs=MagickFalse;
9939 mng_info->ping_exclude_sRGB=MagickFalse;
9940 mng_info->ping_exclude_tEXt=MagickFalse;
9941 mng_info->ping_exclude_tRNS=MagickFalse;
9942 mng_info->ping_exclude_vpAg=MagickFalse;
9943 mng_info->ping_exclude_zCCP=MagickFalse;
9944 mng_info->ping_exclude_zTXt=MagickFalse;
9947 if (LocaleNCompare(value+i,"bkgd",4) == 0)
9948 mng_info->ping_exclude_bKGD=MagickTrue;
9950 if (LocaleNCompare(value+i,"chrm",4) == 0)
9951 mng_info->ping_exclude_cHRM=MagickTrue;
9953 if (LocaleNCompare(value+i,"exif",4) == 0)
9954 mng_info->ping_exclude_EXIF=MagickTrue;
9956 if (LocaleNCompare(value+i,"gama",4) == 0)
9957 mng_info->ping_exclude_gAMA=MagickTrue;
9959 if (LocaleNCompare(value+i,"iccp",4) == 0)
9960 mng_info->ping_exclude_iCCP=MagickTrue;
9963 if (LocaleNCompare(value+i,"itxt",4) == 0)
9964 mng_info->ping_exclude_iTXt=MagickTrue;
9967 if (LocaleNCompare(value+i,"gama",4) == 0)
9968 mng_info->ping_exclude_gAMA=MagickTrue;
9970 if (LocaleNCompare(value+i,"offs",4) == 0)
9971 mng_info->ping_exclude_oFFs=MagickTrue;
9973 if (LocaleNCompare(value+i,"phys",4) == 0)
9974 mng_info->ping_exclude_pHYs=MagickTrue;
9976 if (LocaleNCompare(value+i,"srgb",4) == 0)
9977 mng_info->ping_exclude_sRGB=MagickTrue;
9979 if (LocaleNCompare(value+i,"text",4) == 0)
9980 mng_info->ping_exclude_tEXt=MagickTrue;
9982 if (LocaleNCompare(value+i,"trns",4) == 0)
9983 mng_info->ping_exclude_tRNS=MagickTrue;
9985 if (LocaleNCompare(value+i,"vpag",4) == 0)
9986 mng_info->ping_exclude_vpAg=MagickTrue;
9988 if (LocaleNCompare(value+i,"zccp",4) == 0)
9989 mng_info->ping_exclude_zCCP=MagickTrue;
9991 if (LocaleNCompare(value+i,"ztxt",4) == 0)
9992 mng_info->ping_exclude_zTXt=MagickTrue;
9998 for (source=0; source<1; source++)
10002 value=GetImageArtifact(image,"png:include-chunk");
10005 value=GetImageArtifact(image,"png:include-chunks");
10009 value=GetImageOption(image_info,"png:include-chunk");
10012 value=GetImageOption(image_info,"png:include-chunks");
10020 excluding=MagickTrue;
10022 if (logging != MagickFalse)
10025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10026 " png:include-chunk=%s found in image artifacts.\n", value);
10028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10029 " png:include-chunk=%s found in image properties.\n", value);
10032 last=strlen(value);
10034 for (i=0; i<(int) last; i+=5)
10036 if (LocaleNCompare(value+i,"all",3) == 0)
10038 mng_info->ping_exclude_bKGD=MagickFalse;
10039 mng_info->ping_exclude_cHRM=MagickFalse;
10040 mng_info->ping_exclude_EXIF=MagickFalse;
10041 mng_info->ping_exclude_gAMA=MagickFalse;
10042 mng_info->ping_exclude_iCCP=MagickFalse;
10043 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10044 mng_info->ping_exclude_oFFs=MagickFalse;
10045 mng_info->ping_exclude_pHYs=MagickFalse;
10046 mng_info->ping_exclude_sRGB=MagickFalse;
10047 mng_info->ping_exclude_tEXt=MagickFalse;
10048 mng_info->ping_exclude_tRNS=MagickFalse;
10049 mng_info->ping_exclude_vpAg=MagickFalse;
10050 mng_info->ping_exclude_zCCP=MagickFalse;
10051 mng_info->ping_exclude_zTXt=MagickFalse;
10055 if (LocaleNCompare(value+i,"none",4) == 0)
10057 mng_info->ping_exclude_bKGD=MagickTrue;
10058 mng_info->ping_exclude_cHRM=MagickTrue;
10059 mng_info->ping_exclude_EXIF=MagickTrue;
10060 mng_info->ping_exclude_gAMA=MagickTrue;
10061 mng_info->ping_exclude_iCCP=MagickTrue;
10062 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10063 mng_info->ping_exclude_oFFs=MagickTrue;
10064 mng_info->ping_exclude_pHYs=MagickTrue;
10065 mng_info->ping_exclude_sRGB=MagickTrue;
10066 mng_info->ping_exclude_tEXt=MagickTrue;
10067 mng_info->ping_exclude_tRNS=MagickTrue;
10068 mng_info->ping_exclude_vpAg=MagickTrue;
10069 mng_info->ping_exclude_zCCP=MagickTrue;
10070 mng_info->ping_exclude_zTXt=MagickTrue;
10073 if (LocaleNCompare(value+i,"bkgd",4) == 0)
10074 mng_info->ping_exclude_bKGD=MagickFalse;
10076 if (LocaleNCompare(value+i,"chrm",4) == 0)
10077 mng_info->ping_exclude_cHRM=MagickFalse;
10079 if (LocaleNCompare(value+i,"exif",4) == 0)
10080 mng_info->ping_exclude_EXIF=MagickFalse;
10082 if (LocaleNCompare(value+i,"gama",4) == 0)
10083 mng_info->ping_exclude_gAMA=MagickFalse;
10085 if (LocaleNCompare(value+i,"iccp",4) == 0)
10086 mng_info->ping_exclude_iCCP=MagickFalse;
10089 if (LocaleNCompare(value+i,"itxt",4) == 0)
10090 mng_info->ping_exclude_iTXt=MagickFalse;
10093 if (LocaleNCompare(value+i,"gama",4) == 0)
10094 mng_info->ping_exclude_gAMA=MagickFalse;
10096 if (LocaleNCompare(value+i,"offs",4) == 0)
10097 mng_info->ping_exclude_oFFs=MagickFalse;
10099 if (LocaleNCompare(value+i,"phys",4) == 0)
10100 mng_info->ping_exclude_pHYs=MagickFalse;
10102 if (LocaleNCompare(value+i,"srgb",4) == 0)
10103 mng_info->ping_exclude_sRGB=MagickFalse;
10105 if (LocaleNCompare(value+i,"text",4) == 0)
10106 mng_info->ping_exclude_tEXt=MagickFalse;
10108 if (LocaleNCompare(value+i,"trns",4) == 0)
10109 mng_info->ping_exclude_tRNS=MagickFalse;
10111 if (LocaleNCompare(value+i,"vpag",4) == 0)
10112 mng_info->ping_exclude_vpAg=MagickFalse;
10114 if (LocaleNCompare(value+i,"zccp",4) == 0)
10115 mng_info->ping_exclude_zCCP=MagickFalse;
10117 if (LocaleNCompare(value+i,"ztxt",4) == 0)
10118 mng_info->ping_exclude_zTXt=MagickFalse;
10124 if (excluding != MagickFalse && logging != MagickFalse)
10126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10127 " Chunks to be excluded from the output PNG:");
10128 if (mng_info->ping_exclude_bKGD != MagickFalse)
10129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10131 if (mng_info->ping_exclude_cHRM != MagickFalse)
10132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10134 if (mng_info->ping_exclude_EXIF != MagickFalse)
10135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10137 if (mng_info->ping_exclude_gAMA != MagickFalse)
10138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10140 if (mng_info->ping_exclude_iCCP != MagickFalse)
10141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10144 if (mng_info->ping_exclude_iTXt != MagickFalse)
10145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10148 if (mng_info->ping_exclude_oFFs != MagickFalse)
10149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10151 if (mng_info->ping_exclude_pHYs != MagickFalse)
10152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10154 if (mng_info->ping_exclude_sRGB != MagickFalse)
10155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10157 if (mng_info->ping_exclude_tEXt != MagickFalse)
10158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10160 if (mng_info->ping_exclude_tRNS != MagickFalse)
10161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10163 if (mng_info->ping_exclude_vpAg != MagickFalse)
10164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10166 if (mng_info->ping_exclude_zCCP != MagickFalse)
10167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10169 if (mng_info->ping_exclude_zTXt != MagickFalse)
10170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10174 mng_info->need_blob = MagickTrue;
10176 status=WriteOnePNGImage(mng_info,image_info,image);
10178 MngInfoFreeStruct(mng_info,&have_mng_structure);
10180 if (logging != MagickFalse)
10181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10186 #if defined(JNG_SUPPORTED)
10188 /* Write one JNG image */
10189 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10190 const ImageInfo *image_info,Image *image)
10211 jng_alpha_compression_method,
10212 jng_alpha_sample_depth,
10219 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10220 " Enter WriteOneJNGImage()");
10222 blob=(unsigned char *) NULL;
10223 jpeg_image=(Image *) NULL;
10224 jpeg_image_info=(ImageInfo *) NULL;
10227 transparent=image_info->type==GrayscaleMatteType ||
10228 image_info->type==TrueColorMatteType;
10230 jng_alpha_sample_depth=0;
10231 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10232 jng_alpha_compression_method=0;
10234 if (image->matte != MagickFalse)
10236 /* if any pixels are transparent */
10237 transparent=MagickTrue;
10238 if (image_info->compression==JPEGCompression)
10239 jng_alpha_compression_method=8;
10246 /* Create JPEG blob, image, and image_info */
10247 if (logging != MagickFalse)
10248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10249 " Creating jpeg_image_info for opacity.");
10251 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10253 if (jpeg_image_info == (ImageInfo *) NULL)
10254 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10256 if (logging != MagickFalse)
10257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10258 " Creating jpeg_image.");
10260 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10262 if (jpeg_image == (Image *) NULL)
10263 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10265 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10266 status=SeparateImageChannel(jpeg_image,OpacityChannel);
10267 status=NegateImage(jpeg_image,MagickFalse);
10268 jpeg_image->matte=MagickFalse;
10270 if (jng_quality >= 1000)
10271 jpeg_image_info->quality=jng_quality/1000;
10274 jpeg_image_info->quality=jng_quality;
10276 jpeg_image_info->type=GrayscaleType;
10277 (void) SetImageType(jpeg_image,GrayscaleType);
10278 (void) AcquireUniqueFilename(jpeg_image->filename);
10279 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10280 "%s",jpeg_image->filename);
10283 /* To do: check bit depth of PNG alpha channel */
10285 /* Check if image is grayscale. */
10286 if (image_info->type != TrueColorMatteType && image_info->type !=
10287 TrueColorType && ImageIsGray(image))
10292 if (jng_alpha_compression_method==0)
10297 /* Encode opacity as a grayscale PNG blob */
10298 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10299 &image->exception);
10300 if (logging != MagickFalse)
10301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10302 " Creating PNG blob.");
10305 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10306 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10307 jpeg_image_info->interlace=NoInterlace;
10309 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10310 &image->exception);
10312 /* Retrieve sample depth used */
10313 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10314 if (value != (char *) NULL)
10315 jng_alpha_sample_depth= (unsigned int) value[0];
10319 /* Encode opacity as a grayscale JPEG blob */
10321 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10322 &image->exception);
10324 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10325 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10326 jpeg_image_info->interlace=NoInterlace;
10327 if (logging != MagickFalse)
10328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10329 " Creating blob.");
10330 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10331 &image->exception);
10332 jng_alpha_sample_depth=8;
10334 if (logging != MagickFalse)
10335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10336 " Successfully read jpeg_image into a blob, length=%.20g.",
10340 /* Destroy JPEG image and image_info */
10341 jpeg_image=DestroyImage(jpeg_image);
10342 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10343 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10346 /* Write JHDR chunk */
10347 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10348 PNGType(chunk,mng_JHDR);
10349 LogPNGChunk(logging,mng_JHDR,16L);
10350 PNGLong(chunk+4,(png_uint_32) image->columns);
10351 PNGLong(chunk+8,(png_uint_32) image->rows);
10352 chunk[12]=jng_color_type;
10353 chunk[13]=8; /* sample depth */
10354 chunk[14]=8; /*jng_image_compression_method */
10355 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10356 chunk[16]=jng_alpha_sample_depth;
10357 chunk[17]=jng_alpha_compression_method;
10358 chunk[18]=0; /*jng_alpha_filter_method */
10359 chunk[19]=0; /*jng_alpha_interlace_method */
10360 (void) WriteBlob(image,20,chunk);
10361 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10362 if (logging != MagickFalse)
10364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10365 " JNG width:%15lu",(unsigned long) image->columns);
10367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10368 " JNG height:%14lu",(unsigned long) image->rows);
10370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10371 " JNG color type:%10d",jng_color_type);
10373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10374 " JNG sample depth:%8d",8);
10376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10377 " JNG compression:%9d",8);
10379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10380 " JNG interlace:%11d",0);
10382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10383 " JNG alpha depth:%9d",jng_alpha_sample_depth);
10385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10386 " JNG alpha compression:%3d",jng_alpha_compression_method);
10388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10389 " JNG alpha filter:%8d",0);
10391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10392 " JNG alpha interlace:%5d",0);
10395 /* Write any JNG-chunk-b profiles */
10396 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10399 Write leading ancillary chunks
10405 Write JNG bKGD chunk
10416 if (jng_color_type == 8 || jng_color_type == 12)
10420 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10421 PNGType(chunk,mng_bKGD);
10422 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10423 red=ScaleQuantumToChar(image->background_color.red);
10424 green=ScaleQuantumToChar(image->background_color.green);
10425 blue=ScaleQuantumToChar(image->background_color.blue);
10432 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10433 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10436 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10439 Write JNG sRGB chunk
10441 (void) WriteBlobMSBULong(image,1L);
10442 PNGType(chunk,mng_sRGB);
10443 LogPNGChunk(logging,mng_sRGB,1L);
10445 if (image->rendering_intent != UndefinedIntent)
10446 chunk[4]=(unsigned char)
10447 Magick_RenderingIntent_to_PNG_RenderingIntent(
10448 (image->rendering_intent));
10451 chunk[4]=(unsigned char)
10452 Magick_RenderingIntent_to_PNG_RenderingIntent(
10453 (PerceptualIntent));
10455 (void) WriteBlob(image,5,chunk);
10456 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10460 if (image->gamma != 0.0)
10463 Write JNG gAMA chunk
10465 (void) WriteBlobMSBULong(image,4L);
10466 PNGType(chunk,mng_gAMA);
10467 LogPNGChunk(logging,mng_gAMA,4L);
10468 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10469 (void) WriteBlob(image,8,chunk);
10470 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10473 if ((mng_info->equal_chrms == MagickFalse) &&
10474 (image->chromaticity.red_primary.x != 0.0))
10480 Write JNG cHRM chunk
10482 (void) WriteBlobMSBULong(image,32L);
10483 PNGType(chunk,mng_cHRM);
10484 LogPNGChunk(logging,mng_cHRM,32L);
10485 primary=image->chromaticity.white_point;
10486 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10487 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10488 primary=image->chromaticity.red_primary;
10489 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10490 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10491 primary=image->chromaticity.green_primary;
10492 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10493 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10494 primary=image->chromaticity.blue_primary;
10495 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10496 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10497 (void) WriteBlob(image,36,chunk);
10498 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10502 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10505 Write JNG pHYs chunk
10507 (void) WriteBlobMSBULong(image,9L);
10508 PNGType(chunk,mng_pHYs);
10509 LogPNGChunk(logging,mng_pHYs,9L);
10510 if (image->units == PixelsPerInchResolution)
10512 PNGLong(chunk+4,(png_uint_32)
10513 (image->x_resolution*100.0/2.54+0.5));
10515 PNGLong(chunk+8,(png_uint_32)
10516 (image->y_resolution*100.0/2.54+0.5));
10523 if (image->units == PixelsPerCentimeterResolution)
10525 PNGLong(chunk+4,(png_uint_32)
10526 (image->x_resolution*100.0+0.5));
10528 PNGLong(chunk+8,(png_uint_32)
10529 (image->y_resolution*100.0+0.5));
10536 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10537 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10541 (void) WriteBlob(image,13,chunk);
10542 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10545 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10548 Write JNG oFFs chunk
10550 (void) WriteBlobMSBULong(image,9L);
10551 PNGType(chunk,mng_oFFs);
10552 LogPNGChunk(logging,mng_oFFs,9L);
10553 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10554 PNGsLong(chunk+8,(ssize_t) (image->page.y));
10556 (void) WriteBlob(image,13,chunk);
10557 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10559 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10561 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10562 PNGType(chunk,mng_vpAg);
10563 LogPNGChunk(logging,mng_vpAg,9L);
10564 PNGLong(chunk+4,(png_uint_32) image->page.width);
10565 PNGLong(chunk+8,(png_uint_32) image->page.height);
10566 chunk[12]=0; /* unit = pixels */
10567 (void) WriteBlob(image,13,chunk);
10568 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10574 if (jng_alpha_compression_method==0)
10582 /* Write IDAT chunk header */
10583 if (logging != MagickFalse)
10584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10585 " Write IDAT chunks from blob, length=%.20g.",(double)
10588 /* Copy IDAT chunks */
10591 for (i=8; i<(ssize_t) length; i+=len+12)
10593 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10596 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10598 /* Found an IDAT chunk. */
10599 (void) WriteBlobMSBULong(image,(size_t) len);
10600 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10601 (void) WriteBlob(image,(size_t) len+4,p);
10602 (void) WriteBlobMSBULong(image,
10603 crc32(0,p,(uInt) len+4));
10608 if (logging != MagickFalse)
10609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610 " Skipping %c%c%c%c chunk, length=%.20g.",
10611 *(p),*(p+1),*(p+2),*(p+3),(double) len);
10618 /* Write JDAA chunk header */
10619 if (logging != MagickFalse)
10620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10621 " Write JDAA chunk, length=%.20g.",(double) length);
10622 (void) WriteBlobMSBULong(image,(size_t) length);
10623 PNGType(chunk,mng_JDAA);
10624 LogPNGChunk(logging,mng_JDAA,length);
10625 /* Write JDAT chunk(s) data */
10626 (void) WriteBlob(image,4,chunk);
10627 (void) WriteBlob(image,length,blob);
10628 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10631 blob=(unsigned char *) RelinquishMagickMemory(blob);
10634 /* Encode image as a JPEG blob */
10635 if (logging != MagickFalse)
10636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10637 " Creating jpeg_image_info.");
10638 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10639 if (jpeg_image_info == (ImageInfo *) NULL)
10640 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10642 if (logging != MagickFalse)
10643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10644 " Creating jpeg_image.");
10646 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10647 if (jpeg_image == (Image *) NULL)
10648 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10649 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10651 (void) AcquireUniqueFilename(jpeg_image->filename);
10652 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10653 jpeg_image->filename);
10655 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10656 &image->exception);
10658 if (logging != MagickFalse)
10659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10660 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10661 (double) jpeg_image->rows);
10663 if (jng_color_type == 8 || jng_color_type == 12)
10664 jpeg_image_info->type=GrayscaleType;
10666 jpeg_image_info->quality=jng_quality % 1000;
10667 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10668 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10670 if (logging != MagickFalse)
10671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10672 " Creating blob.");
10674 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10676 if (logging != MagickFalse)
10678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679 " Successfully read jpeg_image into a blob, length=%.20g.",
10682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10683 " Write JDAT chunk, length=%.20g.",(double) length);
10686 /* Write JDAT chunk(s) */
10687 (void) WriteBlobMSBULong(image,(size_t) length);
10688 PNGType(chunk,mng_JDAT);
10689 LogPNGChunk(logging,mng_JDAT,length);
10690 (void) WriteBlob(image,4,chunk);
10691 (void) WriteBlob(image,length,blob);
10692 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10694 jpeg_image=DestroyImage(jpeg_image);
10695 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10696 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10697 blob=(unsigned char *) RelinquishMagickMemory(blob);
10699 /* Write any JNG-chunk-e profiles */
10700 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10702 /* Write IEND chunk */
10703 (void) WriteBlobMSBULong(image,0L);
10704 PNGType(chunk,mng_IEND);
10705 LogPNGChunk(logging,mng_IEND,0);
10706 (void) WriteBlob(image,4,chunk);
10707 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10709 if (logging != MagickFalse)
10710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10711 " exit WriteOneJNGImage()");
10718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10722 % W r i t e J N G I m a g e %
10726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10728 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10730 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
10732 % The format of the WriteJNGImage method is:
10734 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10736 % A description of each parameter follows:
10738 % o image_info: the image info.
10740 % o image: The image.
10742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10744 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10747 have_mng_structure,
10757 assert(image_info != (const ImageInfo *) NULL);
10758 assert(image_info->signature == MagickSignature);
10759 assert(image != (Image *) NULL);
10760 assert(image->signature == MagickSignature);
10761 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10762 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
10763 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
10764 if (status == MagickFalse)
10768 Allocate a MngInfo structure.
10770 have_mng_structure=MagickFalse;
10771 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10772 if (mng_info == (MngInfo *) NULL)
10773 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10775 Initialize members of the MngInfo structure.
10777 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10778 mng_info->image=image;
10779 have_mng_structure=MagickTrue;
10781 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
10783 status=WriteOneJNGImage(mng_info,image_info,image);
10784 (void) CloseBlob(image);
10786 (void) CatchImageException(image);
10787 MngInfoFreeStruct(mng_info,&have_mng_structure);
10788 if (logging != MagickFalse)
10789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
10796 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
10805 have_mng_structure,
10808 volatile MagickBooleanType
10820 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
10821 defined(PNG_MNG_FEATURES_SUPPORTED)
10824 all_images_are_gray,
10834 volatile unsigned int
10845 #if (PNG_LIBPNG_VER < 10200)
10846 if (image_info->verbose)
10847 printf("Your PNG library (libpng-%s) is rather old.\n",
10848 PNG_LIBPNG_VER_STRING);
10854 assert(image_info != (const ImageInfo *) NULL);
10855 assert(image_info->signature == MagickSignature);
10856 assert(image != (Image *) NULL);
10857 assert(image->signature == MagickSignature);
10858 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10859 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
10860 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
10861 if (status == MagickFalse)
10865 Allocate a MngInfo structure.
10867 have_mng_structure=MagickFalse;
10868 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10869 if (mng_info == (MngInfo *) NULL)
10870 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10872 Initialize members of the MngInfo structure.
10874 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10875 mng_info->image=image;
10876 have_mng_structure=MagickTrue;
10877 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
10880 * See if user has requested a specific PNG subformat to be used
10881 * for all of the PNGs in the MNG being written, e.g.,
10883 * convert *.png png8:animation.mng
10885 * To do: check -define png:bit_depth and png:color_type as well,
10886 * or perhaps use mng:bit_depth and mng:color_type instead for
10890 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10891 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10892 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10894 write_jng=MagickFalse;
10895 if (image_info->compression == JPEGCompression)
10896 write_jng=MagickTrue;
10898 mng_info->adjoin=image_info->adjoin &&
10899 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
10901 if (logging != MagickFalse)
10903 /* Log some info about the input */
10907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10908 " Checking input image(s)");
10910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10911 " Image_info depth: %.20g",(double) image_info->depth);
10913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10914 " Type: %d",image_info->type);
10917 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
10919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10920 " Scene: %.20g",(double) scene++);
10922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10923 " Image depth: %.20g",(double) p->depth);
10926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10933 if (p->storage_class == PseudoClass)
10934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10935 " Storage class: PseudoClass");
10938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10939 " Storage class: DirectClass");
10942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10943 " Number of colors: %.20g",(double) p->colors);
10946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10947 " Number of colors: unspecified");
10949 if (mng_info->adjoin == MagickFalse)
10954 use_global_plte=MagickFalse;
10955 all_images_are_gray=MagickFalse;
10956 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
10957 need_local_plte=MagickTrue;
10959 need_defi=MagickFalse;
10960 need_matte=MagickFalse;
10961 mng_info->framing_mode=1;
10962 mng_info->old_framing_mode=1;
10965 if (image_info->page != (char *) NULL)
10968 Determine image bounding box.
10970 SetGeometry(image,&mng_info->page);
10971 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
10972 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
10984 mng_info->page=image->page;
10985 need_geom=MagickTrue;
10986 if (mng_info->page.width || mng_info->page.height)
10987 need_geom=MagickFalse;
10989 Check all the scenes.
10991 initial_delay=image->delay;
10992 need_iterations=MagickFalse;
10993 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
10994 mng_info->equal_physs=MagickTrue,
10995 mng_info->equal_gammas=MagickTrue;
10996 mng_info->equal_srgbs=MagickTrue;
10997 mng_info->equal_backgrounds=MagickTrue;
10999 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11000 defined(PNG_MNG_FEATURES_SUPPORTED)
11001 all_images_are_gray=MagickTrue;
11002 mng_info->equal_palettes=MagickFalse;
11003 need_local_plte=MagickFalse;
11005 for (next_image=image; next_image != (Image *) NULL; )
11009 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11010 mng_info->page.width=next_image->columns+next_image->page.x;
11012 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11013 mng_info->page.height=next_image->rows+next_image->page.y;
11016 if (next_image->page.x || next_image->page.y)
11017 need_defi=MagickTrue;
11019 if (next_image->matte)
11020 need_matte=MagickTrue;
11022 if ((int) next_image->dispose >= BackgroundDispose)
11023 if (next_image->matte || next_image->page.x || next_image->page.y ||
11024 ((next_image->columns < mng_info->page.width) &&
11025 (next_image->rows < mng_info->page.height)))
11026 mng_info->need_fram=MagickTrue;
11028 if (next_image->iterations)
11029 need_iterations=MagickTrue;
11031 final_delay=next_image->delay;
11033 if (final_delay != initial_delay || final_delay > 1UL*
11034 next_image->ticks_per_second)
11035 mng_info->need_fram=1;
11037 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11038 defined(PNG_MNG_FEATURES_SUPPORTED)
11040 check for global palette possibility.
11042 if (image->matte != MagickFalse)
11043 need_local_plte=MagickTrue;
11045 if (need_local_plte == 0)
11047 if (ImageIsGray(image) == MagickFalse)
11048 all_images_are_gray=MagickFalse;
11049 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11050 if (use_global_plte == 0)
11051 use_global_plte=mng_info->equal_palettes;
11052 need_local_plte=!mng_info->equal_palettes;
11055 if (GetNextImageInList(next_image) != (Image *) NULL)
11057 if (next_image->background_color.red !=
11058 next_image->next->background_color.red ||
11059 next_image->background_color.green !=
11060 next_image->next->background_color.green ||
11061 next_image->background_color.blue !=
11062 next_image->next->background_color.blue)
11063 mng_info->equal_backgrounds=MagickFalse;
11065 if (next_image->gamma != next_image->next->gamma)
11066 mng_info->equal_gammas=MagickFalse;
11068 if (next_image->rendering_intent !=
11069 next_image->next->rendering_intent)
11070 mng_info->equal_srgbs=MagickFalse;
11072 if ((next_image->units != next_image->next->units) ||
11073 (next_image->x_resolution != next_image->next->x_resolution) ||
11074 (next_image->y_resolution != next_image->next->y_resolution))
11075 mng_info->equal_physs=MagickFalse;
11077 if (mng_info->equal_chrms)
11079 if (next_image->chromaticity.red_primary.x !=
11080 next_image->next->chromaticity.red_primary.x ||
11081 next_image->chromaticity.red_primary.y !=
11082 next_image->next->chromaticity.red_primary.y ||
11083 next_image->chromaticity.green_primary.x !=
11084 next_image->next->chromaticity.green_primary.x ||
11085 next_image->chromaticity.green_primary.y !=
11086 next_image->next->chromaticity.green_primary.y ||
11087 next_image->chromaticity.blue_primary.x !=
11088 next_image->next->chromaticity.blue_primary.x ||
11089 next_image->chromaticity.blue_primary.y !=
11090 next_image->next->chromaticity.blue_primary.y ||
11091 next_image->chromaticity.white_point.x !=
11092 next_image->next->chromaticity.white_point.x ||
11093 next_image->chromaticity.white_point.y !=
11094 next_image->next->chromaticity.white_point.y)
11095 mng_info->equal_chrms=MagickFalse;
11099 next_image=GetNextImageInList(next_image);
11101 if (image_count < 2)
11103 mng_info->equal_backgrounds=MagickFalse;
11104 mng_info->equal_chrms=MagickFalse;
11105 mng_info->equal_gammas=MagickFalse;
11106 mng_info->equal_srgbs=MagickFalse;
11107 mng_info->equal_physs=MagickFalse;
11108 use_global_plte=MagickFalse;
11109 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11110 need_local_plte=MagickTrue;
11112 need_iterations=MagickFalse;
11115 if (mng_info->need_fram == MagickFalse)
11118 Only certain framing rates 100/n are exactly representable without
11119 the FRAM chunk but we'll allow some slop in VLC files
11121 if (final_delay == 0)
11123 if (need_iterations != MagickFalse)
11126 It's probably a GIF with loop; don't run it *too* fast.
11128 if (mng_info->adjoin)
11131 (void) ThrowMagickException(&image->exception,
11132 GetMagickModule(),CoderWarning,
11133 "input has zero delay between all frames; assuming",
11138 mng_info->ticks_per_second=0;
11140 if (final_delay != 0)
11141 mng_info->ticks_per_second=(png_uint_32)
11142 (image->ticks_per_second/final_delay);
11143 if (final_delay > 50)
11144 mng_info->ticks_per_second=2;
11146 if (final_delay > 75)
11147 mng_info->ticks_per_second=1;
11149 if (final_delay > 125)
11150 mng_info->need_fram=MagickTrue;
11152 if (need_defi && final_delay > 2 && (final_delay != 4) &&
11153 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11154 (final_delay != 25) && (final_delay != 50) && (final_delay !=
11155 1UL*image->ticks_per_second))
11156 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
11159 if (mng_info->need_fram != MagickFalse)
11160 mng_info->ticks_per_second=1UL*image->ticks_per_second;
11162 If pseudocolor, we should also check to see if all the
11163 palettes are identical and write a global PLTE if they are.
11167 Write the MNG version 1.0 signature and MHDR chunk.
11169 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11170 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
11171 PNGType(chunk,mng_MHDR);
11172 LogPNGChunk(logging,mng_MHDR,28L);
11173 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11174 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11175 PNGLong(chunk+12,mng_info->ticks_per_second);
11176 PNGLong(chunk+16,0L); /* layer count=unknown */
11177 PNGLong(chunk+20,0L); /* frame count=unknown */
11178 PNGLong(chunk+24,0L); /* play time=unknown */
11183 if (need_defi || mng_info->need_fram || use_global_plte)
11184 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
11187 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
11192 if (need_defi || mng_info->need_fram || use_global_plte)
11193 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
11196 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
11204 if (need_defi || mng_info->need_fram || use_global_plte)
11205 PNGLong(chunk+28,11L); /* simplicity=LC */
11208 PNGLong(chunk+28,9L); /* simplicity=VLC */
11213 if (need_defi || mng_info->need_fram || use_global_plte)
11214 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
11217 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
11220 (void) WriteBlob(image,32,chunk);
11221 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11222 option=GetImageOption(image_info,"mng:need-cacheoff");
11223 if (option != (const char *) NULL)
11229 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11231 PNGType(chunk,mng_nEED);
11232 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11233 (void) WriteBlobMSBULong(image,(size_t) length);
11234 LogPNGChunk(logging,mng_nEED,(size_t) length);
11236 (void) WriteBlob(image,length,chunk);
11237 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11239 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11240 (GetNextImageInList(image) != (Image *) NULL) &&
11241 (image->iterations != 1))
11244 Write MNG TERM chunk
11246 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11247 PNGType(chunk,mng_TERM);
11248 LogPNGChunk(logging,mng_TERM,10L);
11249 chunk[4]=3; /* repeat animation */
11250 chunk[5]=0; /* show last frame when done */
11251 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11252 final_delay/MagickMax(image->ticks_per_second,1)));
11254 if (image->iterations == 0)
11255 PNGLong(chunk+10,PNG_UINT_31_MAX);
11258 PNGLong(chunk+10,(png_uint_32) image->iterations);
11260 if (logging != MagickFalse)
11262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11263 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11264 final_delay/MagickMax(image->ticks_per_second,1)));
11266 if (image->iterations == 0)
11267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11268 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11272 " Image iterations: %.20g",(double) image->iterations);
11274 (void) WriteBlob(image,14,chunk);
11275 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11278 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11280 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11281 mng_info->equal_srgbs)
11284 Write MNG sRGB chunk
11286 (void) WriteBlobMSBULong(image,1L);
11287 PNGType(chunk,mng_sRGB);
11288 LogPNGChunk(logging,mng_sRGB,1L);
11290 if (image->rendering_intent != UndefinedIntent)
11291 chunk[4]=(unsigned char)
11292 Magick_RenderingIntent_to_PNG_RenderingIntent(
11293 (image->rendering_intent));
11296 chunk[4]=(unsigned char)
11297 Magick_RenderingIntent_to_PNG_RenderingIntent(
11298 (PerceptualIntent));
11300 (void) WriteBlob(image,5,chunk);
11301 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11302 mng_info->have_write_global_srgb=MagickTrue;
11307 if (image->gamma && mng_info->equal_gammas)
11310 Write MNG gAMA chunk
11312 (void) WriteBlobMSBULong(image,4L);
11313 PNGType(chunk,mng_gAMA);
11314 LogPNGChunk(logging,mng_gAMA,4L);
11315 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11316 (void) WriteBlob(image,8,chunk);
11317 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11318 mng_info->have_write_global_gama=MagickTrue;
11320 if (mng_info->equal_chrms)
11326 Write MNG cHRM chunk
11328 (void) WriteBlobMSBULong(image,32L);
11329 PNGType(chunk,mng_cHRM);
11330 LogPNGChunk(logging,mng_cHRM,32L);
11331 primary=image->chromaticity.white_point;
11332 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11333 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11334 primary=image->chromaticity.red_primary;
11335 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11336 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11337 primary=image->chromaticity.green_primary;
11338 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11339 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11340 primary=image->chromaticity.blue_primary;
11341 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11342 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11343 (void) WriteBlob(image,36,chunk);
11344 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11345 mng_info->have_write_global_chrm=MagickTrue;
11348 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11351 Write MNG pHYs chunk
11353 (void) WriteBlobMSBULong(image,9L);
11354 PNGType(chunk,mng_pHYs);
11355 LogPNGChunk(logging,mng_pHYs,9L);
11357 if (image->units == PixelsPerInchResolution)
11359 PNGLong(chunk+4,(png_uint_32)
11360 (image->x_resolution*100.0/2.54+0.5));
11362 PNGLong(chunk+8,(png_uint_32)
11363 (image->y_resolution*100.0/2.54+0.5));
11370 if (image->units == PixelsPerCentimeterResolution)
11372 PNGLong(chunk+4,(png_uint_32)
11373 (image->x_resolution*100.0+0.5));
11375 PNGLong(chunk+8,(png_uint_32)
11376 (image->y_resolution*100.0+0.5));
11383 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11384 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11388 (void) WriteBlob(image,13,chunk);
11389 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11392 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11393 or does not cover the entire frame.
11395 if (write_mng && (image->matte || image->page.x > 0 ||
11396 image->page.y > 0 || (image->page.width &&
11397 (image->page.width+image->page.x < mng_info->page.width))
11398 || (image->page.height && (image->page.height+image->page.y
11399 < mng_info->page.height))))
11401 (void) WriteBlobMSBULong(image,6L);
11402 PNGType(chunk,mng_BACK);
11403 LogPNGChunk(logging,mng_BACK,6L);
11404 red=ScaleQuantumToShort(image->background_color.red);
11405 green=ScaleQuantumToShort(image->background_color.green);
11406 blue=ScaleQuantumToShort(image->background_color.blue);
11407 PNGShort(chunk+4,red);
11408 PNGShort(chunk+6,green);
11409 PNGShort(chunk+8,blue);
11410 (void) WriteBlob(image,10,chunk);
11411 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11412 if (mng_info->equal_backgrounds)
11414 (void) WriteBlobMSBULong(image,6L);
11415 PNGType(chunk,mng_bKGD);
11416 LogPNGChunk(logging,mng_bKGD,6L);
11417 (void) WriteBlob(image,10,chunk);
11418 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11422 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11423 if ((need_local_plte == MagickFalse) &&
11424 (image->storage_class == PseudoClass) &&
11425 (all_images_are_gray == MagickFalse))
11431 Write MNG PLTE chunk
11433 data_length=3*image->colors;
11434 (void) WriteBlobMSBULong(image,data_length);
11435 PNGType(chunk,mng_PLTE);
11436 LogPNGChunk(logging,mng_PLTE,data_length);
11438 for (i=0; i < (ssize_t) image->colors; i++)
11440 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11441 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11442 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11445 (void) WriteBlob(image,data_length+4,chunk);
11446 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11447 mng_info->have_write_global_plte=MagickTrue;
11453 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11454 defined(PNG_MNG_FEATURES_SUPPORTED)
11455 mng_info->equal_palettes=MagickFalse;
11459 if (mng_info->adjoin)
11461 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11462 defined(PNG_MNG_FEATURES_SUPPORTED)
11464 If we aren't using a global palette for the entire MNG, check to
11465 see if we can use one for two or more consecutive images.
11467 if (need_local_plte && use_global_plte && !all_images_are_gray)
11469 if (mng_info->IsPalette)
11472 When equal_palettes is true, this image has the same palette
11473 as the previous PseudoClass image
11475 mng_info->have_write_global_plte=mng_info->equal_palettes;
11476 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11477 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11480 Write MNG PLTE chunk
11485 data_length=3*image->colors;
11486 (void) WriteBlobMSBULong(image,data_length);
11487 PNGType(chunk,mng_PLTE);
11488 LogPNGChunk(logging,mng_PLTE,data_length);
11490 for (i=0; i < (ssize_t) image->colors; i++)
11492 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11493 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11494 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11497 (void) WriteBlob(image,data_length+4,chunk);
11498 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11499 (uInt) (data_length+4)));
11500 mng_info->have_write_global_plte=MagickTrue;
11504 mng_info->have_write_global_plte=MagickFalse;
11515 previous_x=mng_info->page.x;
11516 previous_y=mng_info->page.y;
11523 mng_info->page=image->page;
11524 if ((mng_info->page.x != previous_x) ||
11525 (mng_info->page.y != previous_y))
11527 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11528 PNGType(chunk,mng_DEFI);
11529 LogPNGChunk(logging,mng_DEFI,12L);
11530 chunk[4]=0; /* object 0 MSB */
11531 chunk[5]=0; /* object 0 LSB */
11532 chunk[6]=0; /* visible */
11533 chunk[7]=0; /* abstract */
11534 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11535 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11536 (void) WriteBlob(image,16,chunk);
11537 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11542 mng_info->write_mng=write_mng;
11544 if ((int) image->dispose >= 3)
11545 mng_info->framing_mode=3;
11547 if (mng_info->need_fram && mng_info->adjoin &&
11548 ((image->delay != mng_info->delay) ||
11549 (mng_info->framing_mode != mng_info->old_framing_mode)))
11551 if (image->delay == mng_info->delay)
11554 Write a MNG FRAM chunk with the new framing mode.
11556 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11557 PNGType(chunk,mng_FRAM);
11558 LogPNGChunk(logging,mng_FRAM,1L);
11559 chunk[4]=(unsigned char) mng_info->framing_mode;
11560 (void) WriteBlob(image,5,chunk);
11561 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11566 Write a MNG FRAM chunk with the delay.
11568 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11569 PNGType(chunk,mng_FRAM);
11570 LogPNGChunk(logging,mng_FRAM,10L);
11571 chunk[4]=(unsigned char) mng_info->framing_mode;
11572 chunk[5]=0; /* frame name separator (no name) */
11573 chunk[6]=2; /* flag for changing default delay */
11574 chunk[7]=0; /* flag for changing frame timeout */
11575 chunk[8]=0; /* flag for changing frame clipping */
11576 chunk[9]=0; /* flag for changing frame sync_id */
11577 PNGLong(chunk+10,(png_uint_32)
11578 ((mng_info->ticks_per_second*
11579 image->delay)/MagickMax(image->ticks_per_second,1)));
11580 (void) WriteBlob(image,14,chunk);
11581 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11582 mng_info->delay=(png_uint_32) image->delay;
11584 mng_info->old_framing_mode=mng_info->framing_mode;
11587 #if defined(JNG_SUPPORTED)
11588 if (image_info->compression == JPEGCompression)
11593 if (logging != MagickFalse)
11594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11595 " Writing JNG object.");
11596 /* To do: specify the desired alpha compression method. */
11597 write_info=CloneImageInfo(image_info);
11598 write_info->compression=UndefinedCompression;
11599 status=WriteOneJNGImage(mng_info,write_info,image);
11600 write_info=DestroyImageInfo(write_info);
11605 if (logging != MagickFalse)
11606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11607 " Writing PNG object.");
11609 mng_info->need_blob = MagickFalse;
11611 /* We don't want any ancillary chunks written */
11612 mng_info->ping_exclude_bKGD=MagickTrue;
11613 mng_info->ping_exclude_cHRM=MagickTrue;
11614 mng_info->ping_exclude_EXIF=MagickTrue;
11615 mng_info->ping_exclude_gAMA=MagickTrue;
11616 mng_info->ping_exclude_cHRM=MagickTrue;
11617 mng_info->ping_exclude_iCCP=MagickTrue;
11618 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11619 mng_info->ping_exclude_oFFs=MagickTrue;
11620 mng_info->ping_exclude_pHYs=MagickTrue;
11621 mng_info->ping_exclude_sRGB=MagickTrue;
11622 mng_info->ping_exclude_tEXt=MagickTrue;
11623 mng_info->ping_exclude_tRNS=MagickTrue;
11624 mng_info->ping_exclude_vpAg=MagickTrue;
11625 mng_info->ping_exclude_zCCP=MagickTrue;
11626 mng_info->ping_exclude_zTXt=MagickTrue;
11628 status=WriteOnePNGImage(mng_info,image_info,image);
11631 if (status == MagickFalse)
11633 MngInfoFreeStruct(mng_info,&have_mng_structure);
11634 (void) CloseBlob(image);
11635 return(MagickFalse);
11637 (void) CatchImageException(image);
11638 if (GetNextImageInList(image) == (Image *) NULL)
11640 image=SyncNextImageInList(image);
11641 status=SetImageProgress(image,SaveImagesTag,scene++,
11642 GetImageListLength(image));
11644 if (status == MagickFalse)
11647 } while (mng_info->adjoin);
11651 while (GetPreviousImageInList(image) != (Image *) NULL)
11652 image=GetPreviousImageInList(image);
11654 Write the MEND chunk.
11656 (void) WriteBlobMSBULong(image,0x00000000L);
11657 PNGType(chunk,mng_MEND);
11658 LogPNGChunk(logging,mng_MEND,0L);
11659 (void) WriteBlob(image,4,chunk);
11660 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11663 Relinquish resources.
11665 (void) CloseBlob(image);
11666 MngInfoFreeStruct(mng_info,&have_mng_structure);
11668 if (logging != MagickFalse)
11669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11671 return(MagickTrue);
11673 #else /* PNG_LIBPNG_VER > 10011 */
11675 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11678 printf("Your PNG library is too old: You have libpng-%s\n",
11679 PNG_LIBPNG_VER_STRING);
11681 ThrowBinaryException(CoderError,"PNG library is too old",
11682 image_info->filename);
11685 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11687 return(WritePNGImage(image_info,image));
11689 #endif /* PNG_LIBPNG_VER > 10011 */