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
490 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
491 static MagickBooleanType
492 LosslessReduceDepthOK(Image *image)
495 ok_to_reduce=MagickFalse;
497 /* Reduce bit depth if it can be reduced losslessly from 16 to 8.
498 * Note that the method GetImageDepth doesn't check background
499 * and doesn't handle PseudoClass specially. Also it uses
500 * multiplication and division by 257 instead of shifting, so
504 if (image->depth == 16)
511 (((((size_t) image->background_color.red >> 8) & 0xff)
512 == ((size_t) image->background_color.red & 0xff)) &&
513 ((((size_t) image->background_color.green >> 8) & 0xff)
514 == ((size_t) image->background_color.green & 0xff)) &&
515 ((((size_t) image->background_color.blue >> 8) & 0xff)
516 == ((size_t) image->background_color.blue & 0xff))) ? MagickTrue :
519 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
523 for (indx=0; indx < (ssize_t) image->colors; indx++)
525 ok_to_reduce=(((((size_t) image->colormap[indx].red >>
527 == ((size_t) image->colormap[indx].red & 0xff)) &&
528 ((((size_t) image->colormap[indx].green >> 8) & 0xff)
529 == ((size_t) image->colormap[indx].green & 0xff)) &&
530 ((((size_t) image->colormap[indx].blue >> 8) & 0xff)
531 == ((size_t) image->colormap[indx].blue & 0xff)) &&
532 (image->matte == MagickFalse ||
533 (((size_t) image->colormap[indx].opacity >> 8) & 0xff)
534 == ((size_t) image->colormap[indx].opacity & 0xff))) ?
535 MagickTrue : MagickFalse;
536 if (ok_to_reduce == MagickFalse)
541 if ((ok_to_reduce != MagickFalse) &&
542 (image->storage_class != PseudoClass))
550 for (y=0; y < (ssize_t) image->rows; y++)
552 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
554 if (p == (const PixelPacket *) NULL)
556 ok_to_reduce = MagickFalse;
560 for (x=(ssize_t) image->columns-1; x >= 0; x--)
563 (((size_t) p->red >> 8) & 0xff) ==
564 ((size_t) p->red & 0xff)) &&
565 ((((size_t) p->green >> 8) & 0xff) ==
566 ((size_t) p->green & 0xff)) &&
567 ((((size_t) p->blue >> 8) & 0xff) ==
568 ((size_t) p->blue & 0xff)) &&
569 (((image->matte == MagickFalse ||
570 (((size_t) p->opacity >> 8) & 0xff) ==
571 ((size_t) p->opacity & 0xff))))) ? MagickTrue : MagickFalse;
573 if (ok_to_reduce == MagickFalse)
583 if (ok_to_reduce != MagickFalse)
585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
586 " OK to reduce PNG bit depth to 8 without loss of info");
590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
591 " Not OK to reduce PNG bit depth to 8 without loss of info");
597 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
600 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
604 case PerceptualIntent:
610 case SaturationIntent:
621 static RenderingIntent
622 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
627 return PerceptualIntent;
630 return RelativeIntent;
633 return SaturationIntent;
636 return AbsoluteIntent;
639 return UndefinedIntent;
643 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
651 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
665 % I m a g e I s G r a y %
669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
671 % Like IsGrayImage except does not change DirectClass to PseudoClass %
673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
675 static MagickBooleanType ImageIsGray(Image *image)
677 register const PixelPacket
685 assert(image != (Image *) NULL);
686 assert(image->signature == MagickSignature);
687 if (image->debug != MagickFalse)
688 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
690 if (image->storage_class == PseudoClass)
692 for (i=0; i < (ssize_t) image->colors; i++)
693 if (IsGray(image->colormap+i) == MagickFalse)
697 for (y=0; y < (ssize_t) image->rows; y++)
699 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
700 if (p == (const PixelPacket *) NULL)
702 for (x=(ssize_t) image->columns-1; x >= 0; x--)
704 if (IsGray(p) == MagickFalse)
711 #endif /* PNG_LIBPNG_VER > 10011 */
712 #endif /* MAGICKCORE_PNG_DELEGATE */
715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725 % IsMNG() returns MagickTrue if the image format type, identified by the
726 % magick string, is MNG.
728 % The format of the IsMNG method is:
730 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
732 % A description of each parameter follows:
734 % o magick: compare image format pattern against these bytes.
736 % o length: Specifies the length of the magick string.
740 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
745 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
752 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
762 % IsJNG() returns MagickTrue if the image format type, identified by the
763 % magick string, is JNG.
765 % The format of the IsJNG method is:
767 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
769 % A description of each parameter follows:
771 % o magick: compare image format pattern against these bytes.
773 % o length: Specifies the length of the magick string.
777 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
782 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799 % IsPNG() returns MagickTrue if the image format type, identified by the
800 % magick string, is PNG.
802 % The format of the IsPNG method is:
804 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
806 % A description of each parameter follows:
808 % o magick: compare image format pattern against these bytes.
810 % o length: Specifies the length of the magick string.
813 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
818 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
824 #if defined(MAGICKCORE_PNG_DELEGATE)
825 #if defined(__cplusplus) || defined(c_plusplus)
829 #if (PNG_LIBPNG_VER > 10011)
830 static size_t WriteBlobMSBULong(Image *image,const size_t value)
835 assert(image != (Image *) NULL);
836 assert(image->signature == MagickSignature);
837 buffer[0]=(unsigned char) (value >> 24);
838 buffer[1]=(unsigned char) (value >> 16);
839 buffer[2]=(unsigned char) (value >> 8);
840 buffer[3]=(unsigned char) value;
841 return((size_t) WriteBlob(image,4,buffer));
844 static void PNGLong(png_bytep p,png_uint_32 value)
846 *p++=(png_byte) ((value >> 24) & 0xff);
847 *p++=(png_byte) ((value >> 16) & 0xff);
848 *p++=(png_byte) ((value >> 8) & 0xff);
849 *p++=(png_byte) (value & 0xff);
852 #if defined(JNG_SUPPORTED)
853 static void PNGsLong(png_bytep p,png_int_32 value)
855 *p++=(png_byte) ((value >> 24) & 0xff);
856 *p++=(png_byte) ((value >> 16) & 0xff);
857 *p++=(png_byte) ((value >> 8) & 0xff);
858 *p++=(png_byte) (value & 0xff);
862 static void PNGShort(png_bytep p,png_uint_16 value)
864 *p++=(png_byte) ((value >> 8) & 0xff);
865 *p++=(png_byte) (value & 0xff);
868 static void PNGType(png_bytep p,png_bytep type)
870 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
873 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
876 if (logging != MagickFalse)
877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
878 " Writing %c%c%c%c chunk, length: %.20g",
879 type[0],type[1],type[2],type[3],(double) length);
881 #endif /* PNG_LIBPNG_VER > 10011 */
883 #if defined(__cplusplus) || defined(c_plusplus)
887 #if PNG_LIBPNG_VER > 10011
889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
893 % R e a d P N G I m a g e %
897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
899 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
900 % Multiple-image Network Graphics (MNG) image file and returns it. It
901 % allocates the memory necessary for the new Image structure and returns a
902 % pointer to the new image or set of images.
904 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
906 % The format of the ReadPNGImage method is:
908 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
910 % A description of each parameter follows:
912 % o image_info: the image info.
914 % o exception: return any errors or warnings in this structure.
916 % To do, more or less in chronological order (as of version 5.5.2,
917 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
919 % Get 16-bit cheap transparency working.
921 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
923 % Preserve all unknown and not-yet-handled known chunks found in input
924 % PNG file and copy them into output PNG files according to the PNG
927 % (At this point, PNG encoding should be in full MNG compliance)
929 % Provide options for choice of background to use when the MNG BACK
930 % chunk is not present or is not mandatory (i.e., leave transparent,
931 % user specified, MNG BACK, PNG bKGD)
933 % Implement LOOP/ENDL [done, but could do discretionary loops more
934 % efficiently by linking in the duplicate frames.].
936 % Decode and act on the MHDR simplicity profile (offer option to reject
937 % files or attempt to process them anyway when the profile isn't LC or VLC).
939 % Upgrade to full MNG without Delta-PNG.
941 % o BACK [done a while ago except for background image ID]
942 % o MOVE [done 15 May 1999]
943 % o CLIP [done 15 May 1999]
944 % o DISC [done 19 May 1999]
945 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
946 % o SEEK [partially done 19 May 1999 (discard function only)]
950 % o MNG-level tEXt/iTXt/zTXt
955 % o iTXt (wait for libpng implementation).
957 % Use the scene signature to discover when an identical scene is
958 % being reused, and just point to the original image->exception instead
959 % of storing another set of pixels. This not specific to MNG
960 % but could be applied generally.
962 % Upgrade to full MNG with Delta-PNG.
966 % We will not attempt to read files containing the CgBI chunk.
967 % They are really Xcode files meant for display on the iPhone.
968 % These are not valid PNG files and it is impossible to recover
969 % the orginal PNG from files that have been converted to Xcode-PNG,
970 % since irretrievable loss of color data has occurred due to the
971 % use of premultiplied alpha.
974 #if defined(__cplusplus) || defined(c_plusplus)
979 This the function that does the actual reading of data. It is
980 the same as the one supplied in libpng, except that it receives the
981 datastream from the ReadBlob() function instead of standard input.
983 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
988 image=(Image *) png_get_io_ptr(png_ptr);
994 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1000 (void) FormatMagickString(msg,MaxTextExtent,
1001 "Expected %.20g bytes; found %.20g bytes",(double) length,
1003 png_warning(png_ptr,msg);
1004 png_error(png_ptr,"Read Exception");
1009 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1010 !defined(PNG_MNG_FEATURES_SUPPORTED)
1011 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1012 * older than libpng-1.0.3a, which was the first to allow the empty
1013 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1014 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1015 * encountered after an empty PLTE, so we have to look ahead for bKGD
1016 * chunks and remove them from the datastream that is passed to libpng,
1017 * and store their contents for later use.
1019 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1034 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1035 image=(Image *) mng_info->image;
1036 while (mng_info->bytes_in_read_buffer && length)
1038 data[i]=mng_info->read_buffer[i];
1039 mng_info->bytes_in_read_buffer--;
1045 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1047 if (check != length)
1048 png_error(png_ptr,"Read Exception");
1052 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1055 check=(png_size_t) ReadBlob(image,(size_t) length,
1056 (char *) mng_info->read_buffer);
1057 mng_info->read_buffer[4]=0;
1058 mng_info->bytes_in_read_buffer=4;
1059 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1060 mng_info->found_empty_plte=MagickTrue;
1061 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1063 mng_info->found_empty_plte=MagickFalse;
1064 mng_info->have_saved_bkgd_index=MagickFalse;
1068 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1071 check=(png_size_t) ReadBlob(image,(size_t) length,
1072 (char *) mng_info->read_buffer);
1073 mng_info->read_buffer[4]=0;
1074 mng_info->bytes_in_read_buffer=4;
1075 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1076 if (mng_info->found_empty_plte)
1079 Skip the bKGD data byte and CRC.
1082 ReadBlob(image,5,(char *) mng_info->read_buffer);
1083 check=(png_size_t) ReadBlob(image,(size_t) length,
1084 (char *) mng_info->read_buffer);
1085 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1086 mng_info->have_saved_bkgd_index=MagickTrue;
1087 mng_info->bytes_in_read_buffer=0;
1095 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1100 image=(Image *) png_get_io_ptr(png_ptr);
1106 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1108 if (check != length)
1109 png_error(png_ptr,"WriteBlob Failed");
1113 static void png_flush_data(png_structp png_ptr)
1118 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1119 static int PalettesAreEqual(Image *a,Image *b)
1124 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1125 return((int) MagickFalse);
1127 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1128 return((int) MagickFalse);
1130 if (a->colors != b->colors)
1131 return((int) MagickFalse);
1133 for (i=0; i < (ssize_t) a->colors; i++)
1135 if ((a->colormap[i].red != b->colormap[i].red) ||
1136 (a->colormap[i].green != b->colormap[i].green) ||
1137 (a->colormap[i].blue != b->colormap[i].blue))
1138 return((int) MagickFalse);
1141 return((int) MagickTrue);
1145 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1147 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1148 mng_info->exists[i] && !mng_info->frozen[i])
1150 #ifdef MNG_OBJECT_BUFFERS
1151 if (mng_info->ob[i] != (MngBuffer *) NULL)
1153 if (mng_info->ob[i]->reference_count > 0)
1154 mng_info->ob[i]->reference_count--;
1156 if (mng_info->ob[i]->reference_count == 0)
1158 if (mng_info->ob[i]->image != (Image *) NULL)
1159 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1161 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1164 mng_info->ob[i]=(MngBuffer *) NULL;
1166 mng_info->exists[i]=MagickFalse;
1167 mng_info->invisible[i]=MagickFalse;
1168 mng_info->viewable[i]=MagickFalse;
1169 mng_info->frozen[i]=MagickFalse;
1170 mng_info->x_off[i]=0;
1171 mng_info->y_off[i]=0;
1172 mng_info->object_clip[i].left=0;
1173 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1174 mng_info->object_clip[i].top=0;
1175 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1179 static void MngInfoFreeStruct(MngInfo *mng_info,
1180 MagickBooleanType *have_mng_structure)
1182 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1187 for (i=1; i < MNG_MAX_OBJECTS; i++)
1188 MngInfoDiscardObject(mng_info,i);
1190 if (mng_info->global_plte != (png_colorp) NULL)
1191 mng_info->global_plte=(png_colorp)
1192 RelinquishMagickMemory(mng_info->global_plte);
1194 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1195 *have_mng_structure=MagickFalse;
1199 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1205 if (box.left < box2.left)
1208 if (box.top < box2.top)
1211 if (box.right > box2.right)
1212 box.right=box2.right;
1214 if (box.bottom > box2.bottom)
1215 box.bottom=box2.bottom;
1220 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1226 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1228 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1229 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1230 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1231 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1232 if (delta_type != 0)
1234 box.left+=previous_box.left;
1235 box.right+=previous_box.right;
1236 box.top+=previous_box.top;
1237 box.bottom+=previous_box.bottom;
1243 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1249 Read two ssize_ts from CLON, MOVE or PAST chunk
1251 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1252 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1254 if (delta_type != 0)
1256 pair.a+=previous_pair.a;
1257 pair.b+=previous_pair.b;
1263 static long mng_get_long(unsigned char *p)
1265 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1268 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1273 image=(Image *) png_get_error_ptr(ping);
1275 if (image->debug != MagickFalse)
1276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1277 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1279 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1280 message,"`%s'",image->filename);
1282 #if (PNG_LIBPNG_VER < 10500)
1283 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1284 * are building with libpng-1.4.x and can be ignored.
1286 longjmp(ping->jmpbuf,1);
1288 png_longjmp(ping,1);
1292 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1297 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1298 png_error(ping, message);
1300 image=(Image *) png_get_error_ptr(ping);
1301 if (image->debug != MagickFalse)
1302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1303 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1305 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1306 message,"`%s'",image->filename);
1309 #ifdef PNG_USER_MEM_SUPPORTED
1310 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1312 #if (PNG_LIBPNG_VER < 10011)
1317 ret=((png_voidp) AcquireMagickMemory((size_t) size));
1320 png_error("Insufficient memory.");
1325 return((png_voidp) AcquireMagickMemory((size_t) size));
1330 Free a pointer. It is removed from the list at the same time.
1332 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1335 ptr=RelinquishMagickMemory(ptr);
1336 return((png_free_ptr) NULL);
1340 #if defined(__cplusplus) || defined(c_plusplus)
1345 Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
1346 png_textp text,int ii)
1351 register unsigned char
1365 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1366 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,1, 2,3,4,5,6,7,8,9,0,0,
1368 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1369 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1373 /* look for newline */
1377 /* look for length */
1378 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1381 length=(png_uint_32) StringToLong(sp);
1383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1384 " length: %lu",(unsigned long) length);
1386 while (*sp != ' ' && *sp != '\n')
1389 /* allocate space */
1392 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1393 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1394 return(MagickFalse);
1397 profile=AcquireStringInfo(length);
1399 if (profile == (StringInfo *) NULL)
1401 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1402 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1403 "unable to copy profile");
1404 return(MagickFalse);
1407 /* copy profile, skipping white space and column 1 "=" signs */
1408 dp=GetStringInfoDatum(profile);
1411 for (i=0; i < (ssize_t) nibbles; i++)
1413 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1417 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1418 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1419 profile=DestroyStringInfo(profile);
1420 return(MagickFalse);
1426 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1429 (*dp++)+=unhex[(int) *sp++];
1432 We have already read "Raw profile type.
1434 (void) SetImageProfile(image,&text[ii].key[17],profile);
1435 profile=DestroyStringInfo(profile);
1437 if (image_info->verbose)
1438 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1443 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1444 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1450 /* The unknown chunk structure contains the chunk data:
1455 Note that libpng has already taken care of the CRC handling.
1459 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1460 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1461 return(0); /* Did not recognize */
1463 /* recognized vpAg */
1465 if (chunk->size != 9)
1466 return(-1); /* Error return */
1468 if (chunk->data[8] != 0)
1469 return(0); /* ImageMagick requires pixel units */
1471 image=(Image *) png_get_user_chunk_ptr(ping);
1473 image->page.width=(size_t) ((chunk->data[0] << 24) |
1474 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1476 image->page.height=(size_t) ((chunk->data[4] << 24) |
1477 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1479 /* Return one of the following: */
1480 /* return(-n); chunk had an error */
1481 /* return(0); did not recognize */
1482 /* return(n); success */
1490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1494 % R e a d O n e P N G I m a g e %
1498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1500 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1501 % (minus the 8-byte signature) and returns it. It allocates the memory
1502 % necessary for the new Image structure and returns a pointer to the new
1505 % The format of the ReadOnePNGImage method is:
1507 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1508 % ExceptionInfo *exception)
1510 % A description of each parameter follows:
1512 % o mng_info: Specifies a pointer to a MngInfo structure.
1514 % o image_info: the image info.
1516 % o exception: return any errors or warnings in this structure.
1519 static Image *ReadOnePNGImage(MngInfo *mng_info,
1520 const ImageInfo *image_info, ExceptionInfo *exception)
1522 /* Read one PNG image */
1533 ping_interlace_method,
1534 ping_compression_method,
1576 register unsigned char
1579 register IndexPacket
1586 register PixelPacket
1593 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1594 png_byte unused_chunks[]=
1596 104, 73, 83, 84, (png_byte) '\0', /* hIST */
1597 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
1598 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
1599 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
1600 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
1601 116, 73, 77, 69, (png_byte) '\0', /* tIME */
1605 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1606 " enter ReadOnePNGImage()");
1608 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1609 LockSemaphoreInfo(ping_semaphore);
1612 #if (PNG_LIBPNG_VER < 10200)
1613 if (image_info->verbose)
1614 printf("Your PNG library (libpng-%s) is rather old.\n",
1615 PNG_LIBPNG_VER_STRING);
1618 #if (PNG_LIBPNG_VER >= 10400)
1619 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
1620 if (image_info->verbose)
1622 printf("Your PNG library (libpng-%s) is an old beta version.\n",
1623 PNG_LIBPNG_VER_STRING);
1624 printf("Please update it.\n");
1630 quantum_info = (QuantumInfo *) NULL;
1631 image=mng_info->image;
1633 if (logging != MagickFalse)
1634 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1635 " image->matte=%d",(int) image->matte);
1637 /* Set to an out-of-range color unless tRNS chunk is present */
1638 transparent_color.red=65537;
1639 transparent_color.green=65537;
1640 transparent_color.blue=65537;
1641 transparent_color.opacity=65537;
1644 Allocate the PNG structures
1646 #ifdef PNG_USER_MEM_SUPPORTED
1647 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1648 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1649 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
1651 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1652 MagickPNGErrorHandler,MagickPNGWarningHandler);
1654 if (ping == (png_struct *) NULL)
1655 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1657 ping_info=png_create_info_struct(ping);
1659 if (ping_info == (png_info *) NULL)
1661 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1662 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1665 end_info=png_create_info_struct(ping);
1667 if (end_info == (png_info *) NULL)
1669 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1670 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1673 ping_pixels=(unsigned char *) NULL;
1675 if (setjmp(png_jmpbuf(ping)))
1678 PNG image is corrupt.
1680 png_destroy_read_struct(&ping,&ping_info,&end_info);
1681 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1682 UnlockSemaphoreInfo(ping_semaphore);
1684 if (logging != MagickFalse)
1685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1686 " exit ReadOnePNGImage() with error.");
1688 if (image != (Image *) NULL)
1690 InheritException(exception,&image->exception);
1694 return(GetFirstImageInList(image));
1697 Prepare PNG for reading.
1700 mng_info->image_found++;
1701 png_set_sig_bytes(ping,8);
1703 if (LocaleCompare(image_info->magick,"MNG") == 0)
1705 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1706 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1707 png_set_read_fn(ping,image,png_get_data);
1709 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1710 png_permit_empty_plte(ping,MagickTrue);
1711 png_set_read_fn(ping,image,png_get_data);
1713 mng_info->image=image;
1714 mng_info->bytes_in_read_buffer=0;
1715 mng_info->found_empty_plte=MagickFalse;
1716 mng_info->have_saved_bkgd_index=MagickFalse;
1717 png_set_read_fn(ping,mng_info,mng_get_data);
1723 png_set_read_fn(ping,image,png_get_data);
1725 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1726 /* Ignore unused chunks and all unknown chunks except for vpAg */
1727 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1728 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1729 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1730 (int)sizeof(unused_chunks)/5);
1731 /* Callback for other unknown chunks */
1732 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1735 #if (PNG_LIBPNG_VER < 10400)
1736 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1737 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1738 /* Disable thread-unsafe features of pnggccrd */
1739 if (png_access_version_number() >= 10200)
1741 png_uint_32 mmx_disable_mask=0;
1742 png_uint_32 asm_flags;
1744 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
1745 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
1746 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
1747 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1748 asm_flags=png_get_asm_flags(ping);
1749 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1754 png_read_info(ping,ping_info);
1756 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1757 &ping_bit_depth,&ping_color_type,
1758 &ping_interlace_method,&ping_compression_method,
1759 &ping_filter_method);
1761 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1764 (void) png_get_bKGD(ping, ping_info, &ping_background);
1766 if (ping_bit_depth < 8)
1768 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1770 png_set_packing(ping);
1775 image->depth=ping_bit_depth;
1776 image->depth=GetImageQuantumDepth(image,MagickFalse);
1777 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1778 if (logging != MagickFalse)
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " PNG width: %.20g, height: %.20g",
1782 (double) ping_width, (double) ping_height);
1784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1785 " PNG color_type: %d, bit_depth: %d",
1786 ping_color_type, ping_bit_depth);
1788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1789 " PNG compression_method: %d",
1790 ping_compression_method);
1792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1793 " PNG interlace_method: %d, filter_method: %d",
1794 ping_interlace_method,ping_filter_method);
1797 #ifdef PNG_READ_iCCP_SUPPORTED
1798 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1803 #if (PNG_LIBPNG_VER < 10500)
1817 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1820 if (profile_length != 0)
1825 if (logging != MagickFalse)
1826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1827 " Reading PNG iCCP chunk.");
1828 profile=AcquireStringInfo(profile_length);
1829 SetStringInfoDatum(profile,(const unsigned char *) info);
1830 (void) SetImageProfile(image,"icc",profile);
1831 profile=DestroyStringInfo(profile);
1835 #if defined(PNG_READ_sRGB_SUPPORTED)
1840 if (mng_info->have_global_srgb)
1841 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1842 (mng_info->global_srgb_intent);
1844 if (png_get_sRGB(ping,ping_info,&intent))
1846 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1849 if (logging != MagickFalse)
1850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1851 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
1859 if (!png_get_gAMA(ping,ping_info,&file_gamma))
1860 if (mng_info->have_global_gama)
1861 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
1863 if (png_get_gAMA(ping,ping_info,&file_gamma))
1865 image->gamma=(float) file_gamma;
1866 if (logging != MagickFalse)
1867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1868 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
1871 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1873 if (mng_info->have_global_chrm != MagickFalse)
1875 (void) png_set_cHRM(ping,ping_info,
1876 mng_info->global_chrm.white_point.x,
1877 mng_info->global_chrm.white_point.y,
1878 mng_info->global_chrm.red_primary.x,
1879 mng_info->global_chrm.red_primary.y,
1880 mng_info->global_chrm.green_primary.x,
1881 mng_info->global_chrm.green_primary.y,
1882 mng_info->global_chrm.blue_primary.x,
1883 mng_info->global_chrm.blue_primary.y);
1887 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1889 (void) png_get_cHRM(ping,ping_info,
1890 &image->chromaticity.white_point.x,
1891 &image->chromaticity.white_point.y,
1892 &image->chromaticity.red_primary.x,
1893 &image->chromaticity.red_primary.y,
1894 &image->chromaticity.green_primary.x,
1895 &image->chromaticity.green_primary.y,
1896 &image->chromaticity.blue_primary.x,
1897 &image->chromaticity.blue_primary.y);
1899 if (logging != MagickFalse)
1900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1901 " Reading PNG cHRM chunk.");
1904 if (image->rendering_intent != UndefinedIntent)
1906 png_set_sRGB(ping,ping_info,
1907 Magick_RenderingIntent_to_PNG_RenderingIntent
1908 (image->rendering_intent));
1909 png_set_gAMA(ping,ping_info,0.45455f);
1910 png_set_cHRM(ping,ping_info,
1911 0.6400f, 0.3300f, 0.3000f, 0.6000f,
1912 0.1500f, 0.0600f, 0.3127f, 0.3290f);
1914 #if defined(PNG_oFFs_SUPPORTED)
1915 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
1917 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1918 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
1920 if (logging != MagickFalse)
1921 if (image->page.x || image->page.y)
1922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1923 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1924 image->page.x,(double) image->page.y);
1927 #if defined(PNG_pHYs_SUPPORTED)
1928 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1930 if (mng_info->have_global_phys)
1932 png_set_pHYs(ping,ping_info,
1933 mng_info->global_x_pixels_per_unit,
1934 mng_info->global_y_pixels_per_unit,
1935 mng_info->global_phys_unit_type);
1939 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1949 Set image resolution.
1951 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
1953 image->x_resolution=(double) x_resolution;
1954 image->y_resolution=(double) y_resolution;
1956 if (unit_type == PNG_RESOLUTION_METER)
1958 image->units=PixelsPerCentimeterResolution;
1959 image->x_resolution=(double) x_resolution/100.0;
1960 image->y_resolution=(double) y_resolution/100.0;
1963 if (logging != MagickFalse)
1964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1965 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1966 (double) x_resolution,(double) y_resolution,unit_type);
1969 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
1977 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
1979 if ((number_colors == 0) &&
1980 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1982 if (mng_info->global_plte_length)
1984 png_set_PLTE(ping,ping_info,mng_info->global_plte,
1985 (int) mng_info->global_plte_length);
1987 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
1988 if (mng_info->global_trns_length)
1990 if (mng_info->global_trns_length >
1991 mng_info->global_plte_length)
1992 (void) ThrowMagickException(&image->exception,
1993 GetMagickModule(),CoderError,
1994 "global tRNS has more entries than global PLTE",
1995 "`%s'",image_info->filename);
1996 png_set_tRNS(ping,ping_info,mng_info->global_trns,
1997 (int) mng_info->global_trns_length,NULL);
1999 #if defined(PNG_READ_bKGD_SUPPORTED)
2001 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2002 mng_info->have_saved_bkgd_index ||
2004 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2009 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2010 if (mng_info->have_saved_bkgd_index)
2011 background.index=mng_info->saved_bkgd_index;
2013 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2014 background.index=ping_background->index;
2016 background.red=(png_uint_16)
2017 mng_info->global_plte[background.index].red;
2019 background.green=(png_uint_16)
2020 mng_info->global_plte[background.index].green;
2022 background.blue=(png_uint_16)
2023 mng_info->global_plte[background.index].blue;
2025 png_set_bKGD(ping,ping_info,&background);
2030 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2031 CoderError,"No global PLTE in file","`%s'",
2032 image_info->filename);
2036 #if defined(PNG_READ_bKGD_SUPPORTED)
2037 if (mng_info->have_global_bkgd &&
2038 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2039 image->background_color=mng_info->mng_global_bkgd;
2041 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2044 Set image background color.
2046 if (logging != MagickFalse)
2047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2048 " Reading PNG bKGD chunk.");
2050 if (ping_bit_depth == MAGICKCORE_QUANTUM_DEPTH)
2052 image->background_color.red=ping_background->red;
2053 image->background_color.green=ping_background->green;
2054 image->background_color.blue=ping_background->blue;
2057 else /* Scale background components to 16-bit */
2062 if (logging != MagickFalse)
2063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2064 " raw ping_background=(%d,%d,%d).",ping_background->red,
2065 ping_background->green,ping_background->blue);
2069 if (ping_bit_depth == 1)
2072 else if (ping_bit_depth == 2)
2075 else if (ping_bit_depth == 4)
2078 if (ping_bit_depth <= 8)
2081 ping_background->red *= bkgd_scale;
2082 ping_background->green *= bkgd_scale;
2083 ping_background->blue *= bkgd_scale;
2085 if (logging != MagickFalse)
2087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2088 " bkgd_scale=%d.",bkgd_scale);
2090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2091 " ping_background=(%d,%d,%d).",ping_background->red,
2092 ping_background->green,ping_background->blue);
2095 image->background_color.red=
2096 ScaleShortToQuantum(ping_background->red);
2098 image->background_color.green=
2099 ScaleShortToQuantum(ping_background->green);
2101 image->background_color.blue=
2102 ScaleShortToQuantum(ping_background->blue);
2104 image->background_color.opacity=OpaqueOpacity;
2106 if (logging != MagickFalse)
2107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2108 " image->background_color=(%.20g,%.20g,%.20g).",
2109 (double) image->background_color.red,
2110 (double) image->background_color.green,
2111 (double) image->background_color.blue);
2116 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2119 Image has a tRNS chunk.
2127 if (logging != MagickFalse)
2128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2129 " Reading PNG tRNS chunk.");
2131 max_sample = (int) ((one << ping_bit_depth) - 1);
2133 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2134 (int)ping_trans_color->gray > max_sample) ||
2135 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2136 ((int)ping_trans_color->red > max_sample ||
2137 (int)ping_trans_color->green > max_sample ||
2138 (int)ping_trans_color->blue > max_sample)))
2140 if (logging != MagickFalse)
2141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2142 " Ignoring PNG tRNS chunk with out-of-range sample.");
2143 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2144 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2145 image->matte=MagickFalse;
2152 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2154 /* Scale transparent_color to short */
2155 transparent_color.red= scale_to_short*ping_trans_color->red;
2156 transparent_color.green= scale_to_short*ping_trans_color->green;
2157 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2158 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
2160 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2162 if (logging != MagickFalse)
2164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2165 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
2167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2168 " scaled graylevel is %d.",transparent_color.opacity);
2170 transparent_color.red=transparent_color.opacity;
2171 transparent_color.green=transparent_color.opacity;
2172 transparent_color.blue=transparent_color.opacity;
2176 #if defined(PNG_READ_sBIT_SUPPORTED)
2177 if (mng_info->have_global_sbit)
2179 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2180 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2183 num_passes=png_set_interlace_handling(ping);
2185 png_read_update_info(ping,ping_info);
2187 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2190 Initialize image structure.
2192 mng_info->image_box.left=0;
2193 mng_info->image_box.right=(ssize_t) ping_width;
2194 mng_info->image_box.top=0;
2195 mng_info->image_box.bottom=(ssize_t) ping_height;
2196 if (mng_info->mng_type == 0)
2198 mng_info->mng_width=ping_width;
2199 mng_info->mng_height=ping_height;
2200 mng_info->frame=mng_info->image_box;
2201 mng_info->clip=mng_info->image_box;
2206 image->page.y=mng_info->y_off[mng_info->object_id];
2209 image->compression=ZipCompression;
2210 image->columns=ping_width;
2211 image->rows=ping_height;
2212 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2213 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2214 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2219 image->storage_class=PseudoClass;
2221 image->colors=one << ping_bit_depth;
2222 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2223 if (image->colors > 256)
2226 if (image->colors > 65536L)
2227 image->colors=65536L;
2229 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2237 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2238 image->colors=(size_t) number_colors;
2240 if (logging != MagickFalse)
2241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2242 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2246 if (image->storage_class == PseudoClass)
2249 Initialize image colormap.
2251 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2252 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2254 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2262 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2264 for (i=0; i < (ssize_t) image->colors; i++)
2266 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2267 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2268 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2277 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2282 for (i=0; i < (ssize_t) image->colors; i++)
2284 image->colormap[i].red=(Quantum) (i*scale);
2285 image->colormap[i].green=(Quantum) (i*scale);
2286 image->colormap[i].blue=(Quantum) (i*scale);
2291 Read image scanlines.
2293 if (image->delay != 0)
2294 mng_info->scenes_found++;
2296 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2297 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2298 (image_info->first_scene+image_info->number_scenes))))
2300 if (logging != MagickFalse)
2301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2302 " Skipping PNG image data for scene %.20g",(double)
2303 mng_info->scenes_found-1);
2304 png_destroy_read_struct(&ping,&ping_info,&end_info);
2305 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2306 UnlockSemaphoreInfo(ping_semaphore);
2308 if (logging != MagickFalse)
2309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2310 " exit ReadOnePNGImage().");
2315 if (logging != MagickFalse)
2316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2317 " Reading PNG IDAT chunk(s)");
2320 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2321 ping_rowbytes*sizeof(*ping_pixels));
2324 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2325 sizeof(*ping_pixels));
2327 if (ping_pixels == (unsigned char *) NULL)
2328 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2330 if (logging != MagickFalse)
2331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2332 " Converting PNG pixels to pixel packets");
2334 Convert PNG pixels to pixel packets.
2336 if (setjmp(png_jmpbuf(ping)))
2339 PNG image is corrupt.
2341 png_destroy_read_struct(&ping,&ping_info,&end_info);
2342 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2343 UnlockSemaphoreInfo(ping_semaphore);
2345 if (quantum_info != (QuantumInfo *) NULL)
2346 quantum_info = DestroyQuantumInfo(quantum_info);
2348 if (ping_pixels != (unsigned char *) NULL)
2349 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2351 if (logging != MagickFalse)
2352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2353 " exit ReadOnePNGImage() with error.");
2355 if (image != (Image *) NULL)
2357 InheritException(exception,&image->exception);
2361 return(GetFirstImageInList(image));
2364 quantum_info=AcquireQuantumInfo(image_info,image);
2366 if (quantum_info == (QuantumInfo *) NULL)
2367 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2372 found_transparent_pixel;
2374 found_transparent_pixel=MagickFalse;
2376 if (image->storage_class == DirectClass)
2378 for (pass=0; pass < num_passes; pass++)
2381 Convert image to DirectClass pixel packets.
2383 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2387 depth=(ssize_t) ping_bit_depth;
2389 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2390 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2391 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2392 MagickTrue : MagickFalse;
2394 for (y=0; y < (ssize_t) image->rows; y++)
2397 row_offset=ping_rowbytes*y;
2402 png_read_row(ping,ping_pixels+row_offset,NULL);
2403 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2405 if (q == (PixelPacket *) NULL)
2408 #if (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2409 /* code deleted from version 6.6.6-8 */
2410 #else /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2412 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2413 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2414 GrayQuantum,ping_pixels+row_offset,exception);
2416 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2417 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2418 GrayAlphaQuantum,ping_pixels+row_offset,exception);
2420 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2421 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2422 RGBAQuantum,ping_pixels+row_offset,exception);
2424 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2425 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2426 IndexQuantum,ping_pixels+row_offset,exception);
2428 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2429 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2430 RGBQuantum,ping_pixels+row_offset,exception);
2432 if (found_transparent_pixel == MagickFalse)
2434 /* Is there a transparent pixel in the row? */
2435 if (y== 0 && logging != MagickFalse)
2436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2437 " Looking for cheap transparent pixel");
2439 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2441 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2442 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2443 (q->opacity != OpaqueOpacity))
2445 if (logging != MagickFalse)
2446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2449 found_transparent_pixel = MagickTrue;
2452 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2453 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2454 (ScaleQuantumToShort(q->red) == transparent_color.red &&
2455 ScaleQuantumToShort(q->green) == transparent_color.green &&
2456 ScaleQuantumToShort(q->blue) == transparent_color.blue))
2458 if (logging != MagickFalse)
2459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2461 found_transparent_pixel = MagickTrue;
2468 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2470 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2473 if (status == MagickFalse)
2476 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2480 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2482 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2483 if (status == MagickFalse)
2489 else /* image->storage_class != DirectClass */
2491 for (pass=0; pass < num_passes; pass++)
2500 Convert grayscale image to PseudoClass pixel packets.
2502 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2503 MagickTrue : MagickFalse;
2505 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2506 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
2508 if (quantum_scanline == (Quantum *) NULL)
2509 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2511 for (y=0; y < (ssize_t) image->rows; y++)
2514 row_offset=ping_rowbytes*y;
2519 png_read_row(ping,ping_pixels+row_offset,NULL);
2520 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2522 if (q == (PixelPacket *) NULL)
2525 indexes=GetAuthenticIndexQueue(image);
2526 p=ping_pixels+row_offset;
2529 switch (ping_bit_depth)
2536 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
2538 for (bit=7; bit >= 0; bit--)
2539 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2543 if ((image->columns % 8) != 0)
2545 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
2546 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2554 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
2556 *r++=(*p >> 6) & 0x03;
2557 *r++=(*p >> 4) & 0x03;
2558 *r++=(*p >> 2) & 0x03;
2562 if ((image->columns % 4) != 0)
2564 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
2565 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2573 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
2575 *r++=(*p >> 4) & 0x0f;
2579 if ((image->columns % 2) != 0)
2580 *r++=(*p++ >> 4) & 0x0f;
2587 if (ping_color_type == 4)
2588 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2591 /* In image.h, OpaqueOpacity is 0
2592 * TransparentOpacity is QuantumRange
2593 * In a PNG datastream, Opaque is QuantumRange
2594 * and Transparent is 0.
2596 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2597 if (q->opacity != OpaqueOpacity)
2598 found_transparent_pixel = MagickTrue;
2603 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2611 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2613 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2617 if (image->colors > 256)
2625 *r=(Quantum) quantum;
2628 if (ping_color_type == 4)
2630 quantum=((*p++) << 8);
2632 q->opacity=(Quantum) (QuantumRange-quantum);
2633 if (q->opacity != OpaqueOpacity)
2634 found_transparent_pixel = MagickTrue;
2638 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2642 if (image->colors > 256)
2653 if (ping_color_type == 4)
2655 q->opacity=(*p << 8) | *(p+1);
2657 q->opacity=(Quantum) GetAlphaPixelComponent(q);
2658 if (q->opacity != OpaqueOpacity)
2659 found_transparent_pixel = MagickTrue;
2664 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2666 p++; /* strip low byte */
2668 if (ping_color_type == 4)
2670 q->opacity=(Quantum) (QuantumRange-(*p++));
2671 if (q->opacity != OpaqueOpacity)
2672 found_transparent_pixel = MagickTrue;
2687 Transfer image scanline.
2691 for (x=0; x < (ssize_t) image->columns; x++)
2692 indexes[x]=(IndexPacket) (*r++);
2694 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2697 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2699 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2702 if (status == MagickFalse)
2707 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2709 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2711 if (status == MagickFalse)
2715 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2718 image->matte=found_transparent_pixel;
2720 if (logging != MagickFalse)
2722 if (found_transparent_pixel != MagickFalse)
2723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2724 " Found transparent pixel");
2727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2728 " No transparent pixel was found");
2730 ping_color_type&=0x03;
2735 if (quantum_info != (QuantumInfo *) NULL)
2736 quantum_info=DestroyQuantumInfo(quantum_info);
2738 if (image->storage_class == PseudoClass)
2744 image->matte=MagickFalse;
2745 (void) SyncImage(image);
2749 png_read_end(ping,ping_info);
2751 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2752 (ssize_t) image_info->first_scene && image->delay != 0)
2754 png_destroy_read_struct(&ping,&ping_info,&end_info);
2755 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2757 (void) SetImageBackgroundColor(image);
2758 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2759 UnlockSemaphoreInfo(ping_semaphore);
2761 if (logging != MagickFalse)
2762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2763 " exit ReadOnePNGImage() early.");
2767 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2773 Image has a transparent background.
2775 storage_class=image->storage_class;
2776 image->matte=MagickTrue;
2778 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
2780 if (storage_class == PseudoClass)
2782 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2784 for (x=0; x < ping_num_trans; x++)
2786 image->colormap[x].opacity =
2787 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2791 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2793 for (x=0; x < (int) image->colors; x++)
2795 if (ScaleQuantumToShort(image->colormap[x].red) ==
2796 transparent_color.opacity)
2798 image->colormap[x].opacity = (Quantum) TransparentOpacity;
2802 (void) SyncImage(image);
2805 #if 1 /* Should have already been done above, but glennrp problem P10
2810 for (y=0; y < (ssize_t) image->rows; y++)
2812 image->storage_class=storage_class;
2813 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2815 if (q == (PixelPacket *) NULL)
2818 indexes=GetAuthenticIndexQueue(image);
2820 /* Caution: on a Q8 build, this does not distinguish between
2821 * 16-bit colors that differ only in the low byte
2823 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2825 if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2826 ScaleQuantumToShort(q->green) == transparent_color.green &&
2827 ScaleQuantumToShort(q->blue) == transparent_color.blue)
2829 q->opacity=(Quantum) TransparentOpacity;
2832 #if 0 /* I have not found a case where this is needed. */
2835 q->opacity=(Quantum) OpaqueOpacity;
2842 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2848 image->storage_class=DirectClass;
2851 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2852 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2853 image->colorspace=GRAYColorspace;
2855 if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2856 for (i=0; i < (ssize_t) num_text; i++)
2858 /* Check for a profile */
2860 if (logging != MagickFalse)
2861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2862 " Reading PNG text chunk");
2864 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2865 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2872 length=text[i].text_length;
2873 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2875 if (value == (char *) NULL)
2877 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2878 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2883 (void) ConcatenateMagickString(value,text[i].text,length+2);
2884 (void) SetImageProperty(image,text[i].key,value);
2886 if (logging != MagickFalse)
2888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2889 " length: %lu",(unsigned long) length);
2890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2891 " Keyword: %s",text[i].key);
2894 value=DestroyString(value);
2898 #ifdef MNG_OBJECT_BUFFERS
2900 Store the object if necessary.
2902 if (object_id && !mng_info->frozen[object_id])
2904 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2907 create a new object buffer.
2909 mng_info->ob[object_id]=(MngBuffer *)
2910 AcquireMagickMemory(sizeof(MngBuffer));
2912 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2914 mng_info->ob[object_id]->image=(Image *) NULL;
2915 mng_info->ob[object_id]->reference_count=1;
2919 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2920 mng_info->ob[object_id]->frozen)
2922 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2923 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2924 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2927 if (mng_info->ob[object_id]->frozen)
2928 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2929 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2930 "`%s'",image->filename);
2936 if (mng_info->ob[object_id]->image != (Image *) NULL)
2937 mng_info->ob[object_id]->image=DestroyImage
2938 (mng_info->ob[object_id]->image);
2940 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2943 if (mng_info->ob[object_id]->image != (Image *) NULL)
2944 mng_info->ob[object_id]->image->file=(FILE *) NULL;
2947 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2948 ResourceLimitError,"Cloning image for object buffer failed",
2949 "`%s'",image->filename);
2951 if (ping_width > 250000L || ping_height > 250000L)
2952 png_error(ping,"PNG Image dimensions are too large.");
2954 mng_info->ob[object_id]->width=ping_width;
2955 mng_info->ob[object_id]->height=ping_height;
2956 mng_info->ob[object_id]->color_type=ping_color_type;
2957 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
2958 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
2959 mng_info->ob[object_id]->compression_method=
2960 ping_compression_method;
2961 mng_info->ob[object_id]->filter_method=ping_filter_method;
2963 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2972 Copy the PLTE to the object buffer.
2974 png_get_PLTE(ping,ping_info,&plte,&number_colors);
2975 mng_info->ob[object_id]->plte_length=number_colors;
2977 for (i=0; i < number_colors; i++)
2979 mng_info->ob[object_id]->plte[i]=plte[i];
2984 mng_info->ob[object_id]->plte_length=0;
2989 Relinquish resources.
2991 png_destroy_read_struct(&ping,&ping_info,&end_info);
2993 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2994 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2995 UnlockSemaphoreInfo(ping_semaphore);
2998 if (logging != MagickFalse)
2999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3000 " exit ReadOnePNGImage()");
3004 /* end of reading one PNG image */
3007 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3022 magic_number[MaxTextExtent];
3030 assert(image_info != (const ImageInfo *) NULL);
3031 assert(image_info->signature == MagickSignature);
3033 if (image_info->debug != MagickFalse)
3034 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3035 image_info->filename);
3037 assert(exception != (ExceptionInfo *) NULL);
3038 assert(exception->signature == MagickSignature);
3039 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()");
3040 image=AcquireImage(image_info);
3041 mng_info=(MngInfo *) NULL;
3042 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3044 if (status == MagickFalse)
3045 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3048 Verify PNG signature.
3050 count=ReadBlob(image,8,(unsigned char *) magic_number);
3052 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3053 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3056 Allocate a MngInfo structure.
3058 have_mng_structure=MagickFalse;
3059 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3061 if (mng_info == (MngInfo *) NULL)
3062 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3065 Initialize members of the MngInfo structure.
3067 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3068 mng_info->image=image;
3069 have_mng_structure=MagickTrue;
3072 image=ReadOnePNGImage(mng_info,image_info,exception);
3073 MngInfoFreeStruct(mng_info,&have_mng_structure);
3075 if (image == (Image *) NULL)
3077 if (previous != (Image *) NULL)
3079 if (previous->signature != MagickSignature)
3080 ThrowReaderException(CorruptImageError,"CorruptImage");
3082 (void) CloseBlob(previous);
3083 (void) DestroyImageList(previous);
3086 if (logging != MagickFalse)
3087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3088 "exit ReadPNGImage() with error");
3090 return((Image *) NULL);
3093 (void) CloseBlob(image);
3095 if ((image->columns == 0) || (image->rows == 0))
3097 if (logging != MagickFalse)
3098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3099 "exit ReadPNGImage() with error.");
3101 ThrowReaderException(CorruptImageError,"CorruptImage");
3104 if (LocaleCompare(image_info->magick,"PNG8") == 0)
3106 (void) SetImageType(image,PaletteType);
3108 if (image->matte != MagickFalse)
3110 /* To do: Reduce to binary transparency */
3114 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3116 (void) SetImageType(image,TrueColorType);
3117 image->matte=MagickFalse;
3120 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3121 (void) SetImageType(image,TrueColorMatteType);
3123 if (logging != MagickFalse)
3124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3125 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3126 (double) image->page.width,(double) image->page.height,
3127 (double) image->page.x,(double) image->page.y);
3129 if (logging != MagickFalse)
3130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3137 #if defined(JNG_SUPPORTED)
3139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3143 % R e a d O n e J N G I m a g e %
3147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3149 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3150 % (minus the 8-byte signature) and returns it. It allocates the memory
3151 % necessary for the new Image structure and returns a pointer to the new
3154 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3156 % The format of the ReadOneJNGImage method is:
3158 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3159 % ExceptionInfo *exception)
3161 % A description of each parameter follows:
3163 % o mng_info: Specifies a pointer to a MngInfo structure.
3165 % o image_info: the image info.
3167 % o exception: return any errors or warnings in this structure.
3170 static Image *ReadOneJNGImage(MngInfo *mng_info,
3171 const ImageInfo *image_info, ExceptionInfo *exception)
3198 jng_image_sample_depth,
3199 jng_image_compression_method,
3200 jng_image_interlace_method,
3201 jng_alpha_sample_depth,
3202 jng_alpha_compression_method,
3203 jng_alpha_filter_method,
3204 jng_alpha_interlace_method;
3206 register const PixelPacket
3213 register PixelPacket
3216 register unsigned char
3227 jng_alpha_compression_method=0;
3228 jng_alpha_sample_depth=8;
3232 alpha_image=(Image *) NULL;
3233 color_image=(Image *) NULL;
3234 alpha_image_info=(ImageInfo *) NULL;
3235 color_image_info=(ImageInfo *) NULL;
3237 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3238 " enter ReadOneJNGImage()");
3240 image=mng_info->image;
3242 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3245 Allocate next image structure.
3247 if (logging != MagickFalse)
3248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3249 " AcquireNextImage()");
3251 AcquireNextImage(image_info,image);
3253 if (GetNextImageInList(image) == (Image *) NULL)
3254 return((Image *) NULL);
3256 image=SyncNextImageInList(image);
3258 mng_info->image=image;
3261 Signature bytes have already been read.
3264 read_JSEP=MagickFalse;
3265 reading_idat=MagickFalse;
3266 skip_to_iend=MagickFalse;
3270 type[MaxTextExtent];
3279 Read a new JNG chunk.
3281 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3282 2*GetBlobSize(image));
3284 if (status == MagickFalse)
3288 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3289 length=ReadBlobMSBLong(image);
3290 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3292 if (logging != MagickFalse)
3293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3294 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3295 type[0],type[1],type[2],type[3],(double) length);
3297 if (length > PNG_UINT_31_MAX || count == 0)
3298 ThrowReaderException(CorruptImageError,"CorruptImage");
3301 chunk=(unsigned char *) NULL;
3305 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3307 if (chunk == (unsigned char *) NULL)
3308 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3310 for (i=0; i < (ssize_t) length; i++)
3311 chunk[i]=(unsigned char) ReadBlobByte(image);
3316 (void) ReadBlobMSBLong(image); /* read crc word */
3321 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3326 if (memcmp(type,mng_JHDR,4) == 0)
3330 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3331 (p[2] << 8) | p[3]);
3332 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3333 (p[6] << 8) | p[7]);
3334 jng_color_type=p[8];
3335 jng_image_sample_depth=p[9];
3336 jng_image_compression_method=p[10];
3337 jng_image_interlace_method=p[11];
3339 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3342 jng_alpha_sample_depth=p[12];
3343 jng_alpha_compression_method=p[13];
3344 jng_alpha_filter_method=p[14];
3345 jng_alpha_interlace_method=p[15];
3347 if (logging != MagickFalse)
3349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3350 " jng_width: %16lu",(unsigned long) jng_width);
3352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3353 " jng_width: %16lu",(unsigned long) jng_height);
3355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3356 " jng_color_type: %16d",jng_color_type);
3358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3359 " jng_image_sample_depth: %3d",
3360 jng_image_sample_depth);
3362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3363 " jng_image_compression_method:%3d",
3364 jng_image_compression_method);
3366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3367 " jng_image_interlace_method: %3d",
3368 jng_image_interlace_method);
3370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3371 " jng_alpha_sample_depth: %3d",
3372 jng_alpha_sample_depth);
3374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3375 " jng_alpha_compression_method:%3d",
3376 jng_alpha_compression_method);
3378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3379 " jng_alpha_filter_method: %3d",
3380 jng_alpha_filter_method);
3382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3383 " jng_alpha_interlace_method: %3d",
3384 jng_alpha_interlace_method);
3389 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3395 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3396 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3397 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3400 o create color_image
3401 o open color_blob, attached to color_image
3402 o if (color type has alpha)
3403 open alpha_blob, attached to alpha_image
3406 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3408 if (color_image_info == (ImageInfo *) NULL)
3409 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3411 GetImageInfo(color_image_info);
3412 color_image=AcquireImage(color_image_info);
3414 if (color_image == (Image *) NULL)
3415 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3417 if (logging != MagickFalse)
3418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3419 " Creating color_blob.");
3421 (void) AcquireUniqueFilename(color_image->filename);
3422 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3425 if (status == MagickFalse)
3426 return((Image *) NULL);
3428 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3430 alpha_image_info=(ImageInfo *)
3431 AcquireMagickMemory(sizeof(ImageInfo));
3433 if (alpha_image_info == (ImageInfo *) NULL)
3434 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3436 GetImageInfo(alpha_image_info);
3437 alpha_image=AcquireImage(alpha_image_info);
3439 if (alpha_image == (Image *) NULL)
3441 alpha_image=DestroyImage(alpha_image);
3442 ThrowReaderException(ResourceLimitError,
3443 "MemoryAllocationFailed");
3446 if (logging != MagickFalse)
3447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3448 " Creating alpha_blob.");
3450 (void) AcquireUniqueFilename(alpha_image->filename);
3451 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3454 if (status == MagickFalse)
3455 return((Image *) NULL);
3457 if (jng_alpha_compression_method == 0)
3462 if (logging != MagickFalse)
3463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3464 " Writing IHDR chunk to alpha_blob.");
3466 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3467 "\211PNG\r\n\032\n");
3469 (void) WriteBlobMSBULong(alpha_image,13L);
3470 PNGType(data,mng_IHDR);
3471 LogPNGChunk(logging,mng_IHDR,13L);
3472 PNGLong(data+4,jng_width);
3473 PNGLong(data+8,jng_height);
3474 data[12]=jng_alpha_sample_depth;
3475 data[13]=0; /* color_type gray */
3476 data[14]=0; /* compression method 0 */
3477 data[15]=0; /* filter_method 0 */
3478 data[16]=0; /* interlace_method 0 */
3479 (void) WriteBlob(alpha_image,17,data);
3480 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3483 reading_idat=MagickTrue;
3486 if (memcmp(type,mng_JDAT,4) == 0)
3488 /* Copy chunk to color_image->blob */
3490 if (logging != MagickFalse)
3491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3492 " Copying JDAT chunk data to color_blob.");
3494 (void) WriteBlob(color_image,length,chunk);
3497 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3502 if (memcmp(type,mng_IDAT,4) == 0)
3507 /* Copy IDAT header and chunk data to alpha_image->blob */
3509 if (image_info->ping == MagickFalse)
3511 if (logging != MagickFalse)
3512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3513 " Copying IDAT chunk data to alpha_blob.");
3515 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3516 PNGType(data,mng_IDAT);
3517 LogPNGChunk(logging,mng_IDAT,length);
3518 (void) WriteBlob(alpha_image,4,data);
3519 (void) WriteBlob(alpha_image,length,chunk);
3520 (void) WriteBlobMSBULong(alpha_image,
3521 crc32(crc32(0,data,4),chunk,(uInt) length));
3525 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3530 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3532 /* Copy chunk data to alpha_image->blob */
3534 if (image_info->ping == MagickFalse)
3536 if (logging != MagickFalse)
3537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3538 " Copying JDAA chunk data to alpha_blob.");
3540 (void) WriteBlob(alpha_image,length,chunk);
3544 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3549 if (memcmp(type,mng_JSEP,4) == 0)
3551 read_JSEP=MagickTrue;
3554 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3559 if (memcmp(type,mng_bKGD,4) == 0)
3563 image->background_color.red=ScaleCharToQuantum(p[1]);
3564 image->background_color.green=image->background_color.red;
3565 image->background_color.blue=image->background_color.red;
3570 image->background_color.red=ScaleCharToQuantum(p[1]);
3571 image->background_color.green=ScaleCharToQuantum(p[3]);
3572 image->background_color.blue=ScaleCharToQuantum(p[5]);
3575 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3579 if (memcmp(type,mng_gAMA,4) == 0)
3582 image->gamma=((float) mng_get_long(p))*0.00001;
3584 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3588 if (memcmp(type,mng_cHRM,4) == 0)
3592 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3593 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3594 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3595 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3596 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3597 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3598 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3599 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3602 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3606 if (memcmp(type,mng_sRGB,4) == 0)
3610 image->rendering_intent=
3611 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3612 image->gamma=0.45455f;
3613 image->chromaticity.red_primary.x=0.6400f;
3614 image->chromaticity.red_primary.y=0.3300f;
3615 image->chromaticity.green_primary.x=0.3000f;
3616 image->chromaticity.green_primary.y=0.6000f;
3617 image->chromaticity.blue_primary.x=0.1500f;
3618 image->chromaticity.blue_primary.y=0.0600f;
3619 image->chromaticity.white_point.x=0.3127f;
3620 image->chromaticity.white_point.y=0.3290f;
3623 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3627 if (memcmp(type,mng_oFFs,4) == 0)
3631 image->page.x=(ssize_t) mng_get_long(p);
3632 image->page.y=(ssize_t) mng_get_long(&p[4]);
3634 if ((int) p[8] != 0)
3636 image->page.x/=10000;
3637 image->page.y/=10000;
3642 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3647 if (memcmp(type,mng_pHYs,4) == 0)
3651 image->x_resolution=(double) mng_get_long(p);
3652 image->y_resolution=(double) mng_get_long(&p[4]);
3653 if ((int) p[8] == PNG_RESOLUTION_METER)
3655 image->units=PixelsPerCentimeterResolution;
3656 image->x_resolution=image->x_resolution/100.0f;
3657 image->y_resolution=image->y_resolution/100.0f;
3661 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3666 if (memcmp(type,mng_iCCP,4) == 0)
3670 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3677 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3679 if (memcmp(type,mng_IEND,4))
3689 Finish up reading image data:
3691 o read main image from color_blob.
3695 o if (color_type has alpha)
3696 if alpha_encoding is PNG
3697 read secondary image from alpha_blob via ReadPNG
3698 if alpha_encoding is JPEG
3699 read secondary image from alpha_blob via ReadJPEG
3703 o copy intensity of secondary image into
3704 opacity samples of main image.
3706 o destroy the secondary image.
3709 (void) CloseBlob(color_image);
3711 if (logging != MagickFalse)
3712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3713 " Reading jng_image from color_blob.");
3715 (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3716 color_image->filename);
3718 color_image_info->ping=MagickFalse; /* To do: avoid this */
3719 jng_image=ReadImage(color_image_info,exception);
3721 if (jng_image == (Image *) NULL)
3722 return((Image *) NULL);
3724 (void) RelinquishUniqueFileResource(color_image->filename);
3725 color_image=DestroyImage(color_image);
3726 color_image_info=DestroyImageInfo(color_image_info);
3728 if (jng_image == (Image *) NULL)
3729 return((Image *) NULL);
3731 if (logging != MagickFalse)
3732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3733 " Copying jng_image pixels to main image.");
3735 image->rows=jng_height;
3736 image->columns=jng_width;
3737 length=image->columns*sizeof(PixelPacket);
3739 for (y=0; y < (ssize_t) image->rows; y++)
3741 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3742 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3743 (void) CopyMagickMemory(q,s,length);
3745 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3749 jng_image=DestroyImage(jng_image);
3751 if (image_info->ping == MagickFalse)
3753 if (jng_color_type >= 12)
3755 if (jng_alpha_compression_method == 0)
3759 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3760 PNGType(data,mng_IEND);
3761 LogPNGChunk(logging,mng_IEND,0L);
3762 (void) WriteBlob(alpha_image,4,data);
3763 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3766 (void) CloseBlob(alpha_image);
3768 if (logging != MagickFalse)
3769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3770 " Reading opacity from alpha_blob.");
3772 (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3773 "%s",alpha_image->filename);
3775 jng_image=ReadImage(alpha_image_info,exception);
3777 if (jng_image != (Image *) NULL)
3778 for (y=0; y < (ssize_t) image->rows; y++)
3780 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3782 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3784 if (image->matte != MagickFalse)
3785 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3786 q->opacity=(Quantum) QuantumRange-s->red;
3789 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3791 q->opacity=(Quantum) QuantumRange-s->red;
3792 if (q->opacity != OpaqueOpacity)
3793 image->matte=MagickTrue;
3796 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3799 (void) RelinquishUniqueFileResource(alpha_image->filename);
3800 alpha_image=DestroyImage(alpha_image);
3801 alpha_image_info=DestroyImageInfo(alpha_image_info);
3802 if (jng_image != (Image *) NULL)
3803 jng_image=DestroyImage(jng_image);
3807 /* Read the JNG image. */
3809 if (mng_info->mng_type == 0)
3811 mng_info->mng_width=jng_width;
3812 mng_info->mng_height=jng_height;
3815 if (image->page.width == 0 && image->page.height == 0)
3817 image->page.width=jng_width;
3818 image->page.height=jng_height;
3821 if (image->page.x == 0 && image->page.y == 0)
3823 image->page.x=mng_info->x_off[mng_info->object_id];
3824 image->page.y=mng_info->y_off[mng_info->object_id];
3829 image->page.y=mng_info->y_off[mng_info->object_id];
3832 mng_info->image_found++;
3833 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3834 2*GetBlobSize(image));
3836 if (logging != MagickFalse)
3837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3838 " exit ReadOneJNGImage()");
3844 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3848 % R e a d J N G I m a g e %
3852 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3854 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3855 % (including the 8-byte signature) and returns it. It allocates the memory
3856 % necessary for the new Image structure and returns a pointer to the new
3859 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
3861 % The format of the ReadJNGImage method is:
3863 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3866 % A description of each parameter follows:
3868 % o image_info: the image info.
3870 % o exception: return any errors or warnings in this structure.
3874 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3889 magic_number[MaxTextExtent];
3897 assert(image_info != (const ImageInfo *) NULL);
3898 assert(image_info->signature == MagickSignature);
3899 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3900 assert(exception != (ExceptionInfo *) NULL);
3901 assert(exception->signature == MagickSignature);
3902 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()");
3903 image=AcquireImage(image_info);
3904 mng_info=(MngInfo *) NULL;
3905 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3907 if (status == MagickFalse)
3908 return((Image *) NULL);
3910 if (LocaleCompare(image_info->magick,"JNG") != 0)
3911 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3913 /* Verify JNG signature. */
3915 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3917 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3918 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3920 /* Allocate a MngInfo structure. */
3922 have_mng_structure=MagickFalse;
3923 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
3925 if (mng_info == (MngInfo *) NULL)
3926 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3928 /* Initialize members of the MngInfo structure. */
3930 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3931 have_mng_structure=MagickTrue;
3933 mng_info->image=image;
3935 image=ReadOneJNGImage(mng_info,image_info,exception);
3936 MngInfoFreeStruct(mng_info,&have_mng_structure);
3938 if (image == (Image *) NULL)
3940 if (IsImageObject(previous) != MagickFalse)
3942 (void) CloseBlob(previous);
3943 (void) DestroyImageList(previous);
3946 if (logging != MagickFalse)
3947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3948 "exit ReadJNGImage() with error");
3950 return((Image *) NULL);
3952 (void) CloseBlob(image);
3954 if (image->columns == 0 || image->rows == 0)
3956 if (logging != MagickFalse)
3957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3958 "exit ReadJNGImage() with error");
3960 ThrowReaderException(CorruptImageError,"CorruptImage");
3963 if (logging != MagickFalse)
3964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3970 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3973 page_geometry[MaxTextExtent];
4006 #if defined(MNG_INSERT_LAYERS)
4008 mng_background_color;
4011 register unsigned char
4026 #if defined(MNG_INSERT_LAYERS)
4031 volatile unsigned int
4032 #ifdef MNG_OBJECT_BUFFERS
4033 mng_background_object=0,
4035 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4038 default_frame_timeout,
4040 #if defined(MNG_INSERT_LAYERS)
4046 /* These delays are all measured in image ticks_per_second,
4047 * not in MNG ticks_per_second
4050 default_frame_delay,
4054 #if defined(MNG_INSERT_LAYERS)
4063 previous_fb.bottom=0;
4065 previous_fb.right=0;
4067 default_fb.bottom=0;
4071 /* Open image file. */
4073 assert(image_info != (const ImageInfo *) NULL);
4074 assert(image_info->signature == MagickSignature);
4075 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4076 assert(exception != (ExceptionInfo *) NULL);
4077 assert(exception->signature == MagickSignature);
4078 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()");
4079 image=AcquireImage(image_info);
4080 mng_info=(MngInfo *) NULL;
4081 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4083 if (status == MagickFalse)
4084 return((Image *) NULL);
4086 first_mng_object=MagickFalse;
4088 have_mng_structure=MagickFalse;
4090 /* Allocate a MngInfo structure. */
4092 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4094 if (mng_info == (MngInfo *) NULL)
4095 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4097 /* Initialize members of the MngInfo structure. */
4099 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4100 mng_info->image=image;
4101 have_mng_structure=MagickTrue;
4103 if (LocaleCompare(image_info->magick,"MNG") == 0)
4106 magic_number[MaxTextExtent];
4108 /* Verify MNG signature. */
4109 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4110 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4111 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4113 /* Initialize some nonzero members of the MngInfo structure. */
4114 for (i=0; i < MNG_MAX_OBJECTS; i++)
4116 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4117 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4119 mng_info->exists[0]=MagickTrue;
4122 first_mng_object=MagickTrue;
4124 #if defined(MNG_INSERT_LAYERS)
4125 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4127 default_frame_delay=0;
4128 default_frame_timeout=0;
4131 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4133 skip_to_iend=MagickFalse;
4134 term_chunk_found=MagickFalse;
4135 mng_info->framing_mode=1;
4136 #if defined(MNG_INSERT_LAYERS)
4137 mandatory_back=MagickFalse;
4139 #if defined(MNG_INSERT_LAYERS)
4140 mng_background_color=image->background_color;
4142 default_fb=mng_info->frame;
4143 previous_fb=mng_info->frame;
4147 type[MaxTextExtent];
4149 if (LocaleCompare(image_info->magick,"MNG") == 0)
4158 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4159 length=ReadBlobMSBLong(image);
4160 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4162 if (logging != MagickFalse)
4163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4164 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4165 type[0],type[1],type[2],type[3],(double) length);
4167 if (length > PNG_UINT_31_MAX)
4171 ThrowReaderException(CorruptImageError,"CorruptImage");
4174 chunk=(unsigned char *) NULL;
4178 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4180 if (chunk == (unsigned char *) NULL)
4181 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4183 for (i=0; i < (ssize_t) length; i++)
4184 chunk[i]=(unsigned char) ReadBlobByte(image);
4189 (void) ReadBlobMSBLong(image); /* read crc word */
4191 #if !defined(JNG_SUPPORTED)
4192 if (memcmp(type,mng_JHDR,4) == 0)
4194 skip_to_iend=MagickTrue;
4196 if (mng_info->jhdr_warning == 0)
4197 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4198 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4200 mng_info->jhdr_warning++;
4203 if (memcmp(type,mng_DHDR,4) == 0)
4205 skip_to_iend=MagickTrue;
4207 if (mng_info->dhdr_warning == 0)
4208 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4209 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4211 mng_info->dhdr_warning++;
4213 if (memcmp(type,mng_MEND,4) == 0)
4218 if (memcmp(type,mng_IEND,4) == 0)
4219 skip_to_iend=MagickFalse;
4222 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4224 if (logging != MagickFalse)
4225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4231 if (memcmp(type,mng_MHDR,4) == 0)
4233 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4234 (p[2] << 8) | p[3]);
4236 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4237 (p[6] << 8) | p[7]);
4239 if (logging != MagickFalse)
4241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4242 " MNG width: %.20g",(double) mng_info->mng_width);
4243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4244 " MNG height: %.20g",(double) mng_info->mng_height);
4248 mng_info->ticks_per_second=(size_t) mng_get_long(p);
4250 if (mng_info->ticks_per_second == 0)
4251 default_frame_delay=0;
4254 default_frame_delay=1UL*image->ticks_per_second/
4255 mng_info->ticks_per_second;
4257 frame_delay=default_frame_delay;
4263 simplicity=(size_t) mng_get_long(p);
4266 mng_type=1; /* Full MNG */
4268 if ((simplicity != 0) && ((simplicity | 11) == 11))
4269 mng_type=2; /* LC */
4271 if ((simplicity != 0) && ((simplicity | 9) == 9))
4272 mng_type=3; /* VLC */
4274 #if defined(MNG_INSERT_LAYERS)
4276 insert_layers=MagickTrue;
4278 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4280 /* Allocate next image structure. */
4281 AcquireNextImage(image_info,image);
4283 if (GetNextImageInList(image) == (Image *) NULL)
4284 return((Image *) NULL);
4286 image=SyncNextImageInList(image);
4287 mng_info->image=image;
4290 if ((mng_info->mng_width > 65535L) ||
4291 (mng_info->mng_height > 65535L))
4292 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4294 (void) FormatMagickString(page_geometry,MaxTextExtent,
4295 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4296 mng_info->mng_height);
4298 mng_info->frame.left=0;
4299 mng_info->frame.right=(ssize_t) mng_info->mng_width;
4300 mng_info->frame.top=0;
4301 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4302 mng_info->clip=default_fb=previous_fb=mng_info->frame;
4304 for (i=0; i < MNG_MAX_OBJECTS; i++)
4305 mng_info->object_clip[i]=mng_info->frame;
4307 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4311 if (memcmp(type,mng_TERM,4) == 0)
4322 final_delay=(png_uint_32) mng_get_long(&p[2]);
4323 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4325 if (mng_iterations == PNG_UINT_31_MAX)
4328 image->iterations=mng_iterations;
4329 term_chunk_found=MagickTrue;
4332 if (logging != MagickFalse)
4334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4335 " repeat=%d",repeat);
4337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4338 " final_delay=%.20g",(double) final_delay);
4340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4341 " image->iterations=%.20g",(double) image->iterations);
4344 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4347 if (memcmp(type,mng_DEFI,4) == 0)
4350 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4351 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4354 object_id=(p[0] << 8) | p[1];
4356 if (mng_type == 2 && object_id != 0)
4357 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4358 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4361 if (object_id > MNG_MAX_OBJECTS)
4364 Instead ofsuing a warning we should allocate a larger
4365 MngInfo structure and continue.
4367 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4368 CoderError,"object id too large","`%s'",image->filename);
4369 object_id=MNG_MAX_OBJECTS;
4372 if (mng_info->exists[object_id])
4373 if (mng_info->frozen[object_id])
4375 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4376 (void) ThrowMagickException(&image->exception,
4377 GetMagickModule(),CoderError,
4378 "DEFI cannot redefine a frozen MNG object","`%s'",
4383 mng_info->exists[object_id]=MagickTrue;
4386 mng_info->invisible[object_id]=p[2];
4389 Extract object offset info.
4393 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4394 (p[5] << 16) | (p[6] << 8) | p[7]);
4396 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4397 (p[9] << 16) | (p[10] << 8) | p[11]);
4399 if (logging != MagickFalse)
4401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4402 " x_off[%d]: %.20g",object_id,(double)
4403 mng_info->x_off[object_id]);
4405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4406 " y_off[%d]: %.20g",object_id,(double)
4407 mng_info->y_off[object_id]);
4412 Extract object clipping info.
4415 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4418 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4421 if (memcmp(type,mng_bKGD,4) == 0)
4423 mng_info->have_global_bkgd=MagickFalse;
4427 mng_info->mng_global_bkgd.red=
4428 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4430 mng_info->mng_global_bkgd.green=
4431 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4433 mng_info->mng_global_bkgd.blue=
4434 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4436 mng_info->have_global_bkgd=MagickTrue;
4439 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4442 if (memcmp(type,mng_BACK,4) == 0)
4444 #if defined(MNG_INSERT_LAYERS)
4446 mandatory_back=p[6];
4451 if (mandatory_back && length > 5)
4453 mng_background_color.red=
4454 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4456 mng_background_color.green=
4457 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4459 mng_background_color.blue=
4460 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4462 mng_background_color.opacity=OpaqueOpacity;
4465 #ifdef MNG_OBJECT_BUFFERS
4467 mng_background_object=(p[7] << 8) | p[8];
4470 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4474 if (memcmp(type,mng_PLTE,4) == 0)
4476 /* Read global PLTE. */
4478 if (length && (length < 769))
4480 if (mng_info->global_plte == (png_colorp) NULL)
4481 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4482 sizeof(*mng_info->global_plte));
4484 for (i=0; i < (ssize_t) (length/3); i++)
4486 mng_info->global_plte[i].red=p[3*i];
4487 mng_info->global_plte[i].green=p[3*i+1];
4488 mng_info->global_plte[i].blue=p[3*i+2];
4491 mng_info->global_plte_length=(unsigned int) (length/3);
4494 for ( ; i < 256; i++)
4496 mng_info->global_plte[i].red=i;
4497 mng_info->global_plte[i].green=i;
4498 mng_info->global_plte[i].blue=i;
4502 mng_info->global_plte_length=256;
4505 mng_info->global_plte_length=0;
4507 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4511 if (memcmp(type,mng_tRNS,4) == 0)
4513 /* read global tRNS */
4516 for (i=0; i < (ssize_t) length; i++)
4517 mng_info->global_trns[i]=p[i];
4520 for ( ; i < 256; i++)
4521 mng_info->global_trns[i]=255;
4523 mng_info->global_trns_length=(unsigned int) length;
4524 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4527 if (memcmp(type,mng_gAMA,4) == 0)
4534 igamma=mng_get_long(p);
4535 mng_info->global_gamma=((float) igamma)*0.00001;
4536 mng_info->have_global_gama=MagickTrue;
4540 mng_info->have_global_gama=MagickFalse;
4542 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4546 if (memcmp(type,mng_cHRM,4) == 0)
4548 /* Read global cHRM */
4552 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4553 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4554 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4555 mng_info->global_chrm.red_primary.y=0.00001*
4556 mng_get_long(&p[12]);
4557 mng_info->global_chrm.green_primary.x=0.00001*
4558 mng_get_long(&p[16]);
4559 mng_info->global_chrm.green_primary.y=0.00001*
4560 mng_get_long(&p[20]);
4561 mng_info->global_chrm.blue_primary.x=0.00001*
4562 mng_get_long(&p[24]);
4563 mng_info->global_chrm.blue_primary.y=0.00001*
4564 mng_get_long(&p[28]);
4565 mng_info->have_global_chrm=MagickTrue;
4568 mng_info->have_global_chrm=MagickFalse;
4570 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4574 if (memcmp(type,mng_sRGB,4) == 0)
4581 mng_info->global_srgb_intent=
4582 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4583 mng_info->have_global_srgb=MagickTrue;
4586 mng_info->have_global_srgb=MagickFalse;
4588 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4592 if (memcmp(type,mng_iCCP,4) == 0)
4600 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4605 if (memcmp(type,mng_FRAM,4) == 0)
4608 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4609 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4612 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4613 image->delay=frame_delay;
4615 frame_delay=default_frame_delay;
4616 frame_timeout=default_frame_timeout;
4621 mng_info->framing_mode=p[0];
4623 if (logging != MagickFalse)
4624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4625 " Framing_mode=%d",mng_info->framing_mode);
4629 /* Note the delay and frame clipping boundaries. */
4631 p++; /* framing mode */
4633 while (*p && ((p-chunk) < (ssize_t) length))
4634 p++; /* frame name */
4636 p++; /* frame name terminator */
4638 if ((p-chunk) < (ssize_t) (length-4))
4645 change_delay=(*p++);
4646 change_timeout=(*p++);
4647 change_clipping=(*p++);
4648 p++; /* change_sync */
4652 frame_delay=1UL*image->ticks_per_second*
4655 if (mng_info->ticks_per_second != 0)
4656 frame_delay/=mng_info->ticks_per_second;
4659 frame_delay=PNG_UINT_31_MAX;
4661 if (change_delay == 2)
4662 default_frame_delay=frame_delay;
4666 if (logging != MagickFalse)
4667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4668 " Framing_delay=%.20g",(double) frame_delay);
4673 frame_timeout=1UL*image->ticks_per_second*
4676 if (mng_info->ticks_per_second != 0)
4677 frame_timeout/=mng_info->ticks_per_second;
4680 frame_timeout=PNG_UINT_31_MAX;
4682 if (change_delay == 2)
4683 default_frame_timeout=frame_timeout;
4687 if (logging != MagickFalse)
4688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4689 " Framing_timeout=%.20g",(double) frame_timeout);
4692 if (change_clipping)
4694 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4698 if (logging != MagickFalse)
4699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4700 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4701 (double) fb.left,(double) fb.right,(double) fb.top,
4702 (double) fb.bottom);
4704 if (change_clipping == 2)
4710 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4712 subframe_width=(size_t) (mng_info->clip.right
4713 -mng_info->clip.left);
4715 subframe_height=(size_t) (mng_info->clip.bottom
4716 -mng_info->clip.top);
4718 Insert a background layer behind the frame if framing_mode is 4.
4720 #if defined(MNG_INSERT_LAYERS)
4721 if (logging != MagickFalse)
4722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4723 " subframe_width=%.20g, subframe_height=%.20g",(double)
4724 subframe_width,(double) subframe_height);
4726 if (insert_layers && (mng_info->framing_mode == 4) &&
4727 (subframe_width) && (subframe_height))
4729 /* Allocate next image structure. */
4730 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4732 AcquireNextImage(image_info,image);
4734 if (GetNextImageInList(image) == (Image *) NULL)
4736 image=DestroyImageList(image);
4737 MngInfoFreeStruct(mng_info,&have_mng_structure);
4738 return((Image *) NULL);
4741 image=SyncNextImageInList(image);
4744 mng_info->image=image;
4746 if (term_chunk_found)
4748 image->start_loop=MagickTrue;
4749 image->iterations=mng_iterations;
4750 term_chunk_found=MagickFalse;
4754 image->start_loop=MagickFalse;
4756 image->columns=subframe_width;
4757 image->rows=subframe_height;
4758 image->page.width=subframe_width;
4759 image->page.height=subframe_height;
4760 image->page.x=mng_info->clip.left;
4761 image->page.y=mng_info->clip.top;
4762 image->background_color=mng_background_color;
4763 image->matte=MagickFalse;
4765 (void) SetImageBackgroundColor(image);
4767 if (logging != MagickFalse)
4768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4769 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4770 (double) mng_info->clip.left,(double) mng_info->clip.right,
4771 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4774 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4777 if (memcmp(type,mng_CLIP,4) == 0)
4786 first_object=(p[0] << 8) | p[1];
4787 last_object=(p[2] << 8) | p[3];
4789 for (i=(int) first_object; i <= (int) last_object; i++)
4791 if (mng_info->exists[i] && !mng_info->frozen[i])
4796 box=mng_info->object_clip[i];
4797 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4801 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4804 if (memcmp(type,mng_SAVE,4) == 0)
4806 for (i=1; i < MNG_MAX_OBJECTS; i++)
4807 if (mng_info->exists[i])
4809 mng_info->frozen[i]=MagickTrue;
4810 #ifdef MNG_OBJECT_BUFFERS
4811 if (mng_info->ob[i] != (MngBuffer *) NULL)
4812 mng_info->ob[i]->frozen=MagickTrue;
4817 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4822 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4824 /* Read DISC or SEEK. */
4826 if ((length == 0) || !memcmp(type,mng_SEEK,4))
4828 for (i=1; i < MNG_MAX_OBJECTS; i++)
4829 MngInfoDiscardObject(mng_info,i);
4837 for (j=0; j < (ssize_t) length; j+=2)
4839 i=p[j] << 8 | p[j+1];
4840 MngInfoDiscardObject(mng_info,i);
4845 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4850 if (memcmp(type,mng_MOVE,4) == 0)
4858 first_object=(p[0] << 8) | p[1];
4859 last_object=(p[2] << 8) | p[3];
4860 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4862 if (mng_info->exists[i] && !mng_info->frozen[i])
4870 old_pair.a=mng_info->x_off[i];
4871 old_pair.b=mng_info->y_off[i];
4872 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4873 mng_info->x_off[i]=new_pair.a;
4874 mng_info->y_off[i]=new_pair.b;
4878 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4882 if (memcmp(type,mng_LOOP,4) == 0)
4884 ssize_t loop_iters=1;
4885 loop_level=chunk[0];
4886 mng_info->loop_active[loop_level]=1; /* mark loop active */
4888 /* Record starting point. */
4889 loop_iters=mng_get_long(&chunk[1]);
4891 if (logging != MagickFalse)
4892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4893 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
4894 (double) loop_iters);
4896 if (loop_iters == 0)
4897 skipping_loop=loop_level;
4901 mng_info->loop_jump[loop_level]=TellBlob(image);
4902 mng_info->loop_count[loop_level]=loop_iters;
4905 mng_info->loop_iteration[loop_level]=0;
4906 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4910 if (memcmp(type,mng_ENDL,4) == 0)
4912 loop_level=chunk[0];
4914 if (skipping_loop > 0)
4916 if (skipping_loop == loop_level)
4919 Found end of zero-iteration loop.
4922 mng_info->loop_active[loop_level]=0;
4928 if (mng_info->loop_active[loop_level] == 1)
4930 mng_info->loop_count[loop_level]--;
4931 mng_info->loop_iteration[loop_level]++;
4933 if (logging != MagickFalse)
4934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4935 " ENDL: LOOP level %.20g has %.20g remaining iters ",
4936 (double) loop_level,(double)
4937 mng_info->loop_count[loop_level]);
4939 if (mng_info->loop_count[loop_level] != 0)
4941 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4945 ThrowReaderException(CorruptImageError,
4946 "ImproperImageHeader");
4957 mng_info->loop_active[loop_level]=0;
4959 for (i=0; i < loop_level; i++)
4960 if (mng_info->loop_active[i] == 1)
4961 last_level=(short) i;
4962 loop_level=last_level;
4967 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4971 if (memcmp(type,mng_CLON,4) == 0)
4973 if (mng_info->clon_warning == 0)
4974 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4975 CoderError,"CLON is not implemented yet","`%s'",
4978 mng_info->clon_warning++;
4981 if (memcmp(type,mng_MAGN,4) == 0)
4996 magn_first=(p[0] << 8) | p[1];
5002 magn_last=(p[2] << 8) | p[3];
5005 magn_last=magn_first;
5006 #ifndef MNG_OBJECT_BUFFERS
5007 if (magn_first || magn_last)
5008 if (mng_info->magn_warning == 0)
5010 (void) ThrowMagickException(&image->exception,
5011 GetMagickModule(),CoderError,
5012 "MAGN is not implemented yet for nonzero objects",
5013 "`%s'",image->filename);
5015 mng_info->magn_warning++;
5025 magn_mx=(p[5] << 8) | p[6];
5034 magn_my=(p[7] << 8) | p[8];
5043 magn_ml=(p[9] << 8) | p[10];
5052 magn_mr=(p[11] << 8) | p[12];
5061 magn_mt=(p[13] << 8) | p[14];
5070 magn_mb=(p[15] << 8) | p[16];
5082 magn_methy=magn_methx;
5085 if (magn_methx > 5 || magn_methy > 5)
5086 if (mng_info->magn_warning == 0)
5088 (void) ThrowMagickException(&image->exception,
5089 GetMagickModule(),CoderError,
5090 "Unknown MAGN method in MNG datastream","`%s'",
5093 mng_info->magn_warning++;
5095 #ifdef MNG_OBJECT_BUFFERS
5096 /* Magnify existing objects in the range magn_first to magn_last */
5098 if (magn_first == 0 || magn_last == 0)
5100 /* Save the magnification factors for object 0 */
5101 mng_info->magn_mb=magn_mb;
5102 mng_info->magn_ml=magn_ml;
5103 mng_info->magn_mr=magn_mr;
5104 mng_info->magn_mt=magn_mt;
5105 mng_info->magn_mx=magn_mx;
5106 mng_info->magn_my=magn_my;
5107 mng_info->magn_methx=magn_methx;
5108 mng_info->magn_methy=magn_methy;
5112 if (memcmp(type,mng_PAST,4) == 0)
5114 if (mng_info->past_warning == 0)
5115 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5116 CoderError,"PAST is not implemented yet","`%s'",
5119 mng_info->past_warning++;
5122 if (memcmp(type,mng_SHOW,4) == 0)
5124 if (mng_info->show_warning == 0)
5125 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5126 CoderError,"SHOW is not implemented yet","`%s'",
5129 mng_info->show_warning++;
5132 if (memcmp(type,mng_sBIT,4) == 0)
5135 mng_info->have_global_sbit=MagickFalse;
5139 mng_info->global_sbit.gray=p[0];
5140 mng_info->global_sbit.red=p[0];
5141 mng_info->global_sbit.green=p[1];
5142 mng_info->global_sbit.blue=p[2];
5143 mng_info->global_sbit.alpha=p[3];
5144 mng_info->have_global_sbit=MagickTrue;
5147 if (memcmp(type,mng_pHYs,4) == 0)
5151 mng_info->global_x_pixels_per_unit=
5152 (size_t) mng_get_long(p);
5153 mng_info->global_y_pixels_per_unit=
5154 (size_t) mng_get_long(&p[4]);
5155 mng_info->global_phys_unit_type=p[8];
5156 mng_info->have_global_phys=MagickTrue;
5160 mng_info->have_global_phys=MagickFalse;
5162 if (memcmp(type,mng_pHYg,4) == 0)
5164 if (mng_info->phyg_warning == 0)
5165 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5166 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5168 mng_info->phyg_warning++;
5170 if (memcmp(type,mng_BASI,4) == 0)
5172 skip_to_iend=MagickTrue;
5174 if (mng_info->basi_warning == 0)
5175 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5176 CoderError,"BASI is not implemented yet","`%s'",
5179 mng_info->basi_warning++;
5180 #ifdef MNG_BASI_SUPPORTED
5181 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5182 (p[2] << 8) | p[3]);
5183 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5184 (p[6] << 8) | p[7]);
5185 basi_color_type=p[8];
5186 basi_compression_method=p[9];
5187 basi_filter_type=p[10];
5188 basi_interlace_method=p[11];
5190 basi_red=(p[12] << 8) & p[13];
5196 basi_green=(p[14] << 8) & p[15];
5202 basi_blue=(p[16] << 8) & p[17];
5208 basi_alpha=(p[18] << 8) & p[19];
5212 if (basi_sample_depth == 16)
5219 basi_viewable=p[20];
5225 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5229 if (memcmp(type,mng_IHDR,4)
5230 #if defined(JNG_SUPPORTED)
5231 && memcmp(type,mng_JHDR,4)
5235 /* Not an IHDR or JHDR chunk */
5237 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5242 if (logging != MagickFalse)
5243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5244 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5246 mng_info->exists[object_id]=MagickTrue;
5247 mng_info->viewable[object_id]=MagickTrue;
5249 if (mng_info->invisible[object_id])
5251 if (logging != MagickFalse)
5252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5253 " Skipping invisible object");
5255 skip_to_iend=MagickTrue;
5256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5259 #if defined(MNG_INSERT_LAYERS)
5261 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5263 image_width=(size_t) mng_get_long(p);
5264 image_height=(size_t) mng_get_long(&p[4]);
5266 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5269 Insert a transparent background layer behind the entire animation
5270 if it is not full screen.
5272 #if defined(MNG_INSERT_LAYERS)
5273 if (insert_layers && mng_type && first_mng_object)
5275 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5276 (image_width < mng_info->mng_width) ||
5277 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5278 (image_height < mng_info->mng_height) ||
5279 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5281 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5284 Allocate next image structure.
5286 AcquireNextImage(image_info,image);
5288 if (GetNextImageInList(image) == (Image *) NULL)
5290 image=DestroyImageList(image);
5291 MngInfoFreeStruct(mng_info,&have_mng_structure);
5292 return((Image *) NULL);
5295 image=SyncNextImageInList(image);
5297 mng_info->image=image;
5299 if (term_chunk_found)
5301 image->start_loop=MagickTrue;
5302 image->iterations=mng_iterations;
5303 term_chunk_found=MagickFalse;
5307 image->start_loop=MagickFalse;
5309 /* Make a background rectangle. */
5312 image->columns=mng_info->mng_width;
5313 image->rows=mng_info->mng_height;
5314 image->page.width=mng_info->mng_width;
5315 image->page.height=mng_info->mng_height;
5318 image->background_color=mng_background_color;
5319 (void) SetImageBackgroundColor(image);
5320 if (logging != MagickFalse)
5321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5322 " Inserted transparent background layer, W=%.20g, H=%.20g",
5323 (double) mng_info->mng_width,(double) mng_info->mng_height);
5327 Insert a background layer behind the upcoming image if
5328 framing_mode is 3, and we haven't already inserted one.
5330 if (insert_layers && (mng_info->framing_mode == 3) &&
5331 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5332 (simplicity & 0x08)))
5334 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5337 Allocate next image structure.
5339 AcquireNextImage(image_info,image);
5341 if (GetNextImageInList(image) == (Image *) NULL)
5343 image=DestroyImageList(image);
5344 MngInfoFreeStruct(mng_info,&have_mng_structure);
5345 return((Image *) NULL);
5348 image=SyncNextImageInList(image);
5351 mng_info->image=image;
5353 if (term_chunk_found)
5355 image->start_loop=MagickTrue;
5356 image->iterations=mng_iterations;
5357 term_chunk_found=MagickFalse;
5361 image->start_loop=MagickFalse;
5364 image->columns=subframe_width;
5365 image->rows=subframe_height;
5366 image->page.width=subframe_width;
5367 image->page.height=subframe_height;
5368 image->page.x=mng_info->clip.left;
5369 image->page.y=mng_info->clip.top;
5370 image->background_color=mng_background_color;
5371 image->matte=MagickFalse;
5372 (void) SetImageBackgroundColor(image);
5374 if (logging != MagickFalse)
5375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5376 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5377 (double) mng_info->clip.left,(double) mng_info->clip.right,
5378 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5380 #endif /* MNG_INSERT_LAYERS */
5381 first_mng_object=MagickFalse;
5383 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5386 Allocate next image structure.
5388 AcquireNextImage(image_info,image);
5390 if (GetNextImageInList(image) == (Image *) NULL)
5392 image=DestroyImageList(image);
5393 MngInfoFreeStruct(mng_info,&have_mng_structure);
5394 return((Image *) NULL);
5397 image=SyncNextImageInList(image);
5399 mng_info->image=image;
5400 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5401 GetBlobSize(image));
5403 if (status == MagickFalse)
5406 if (term_chunk_found)
5408 image->start_loop=MagickTrue;
5409 term_chunk_found=MagickFalse;
5413 image->start_loop=MagickFalse;
5415 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5417 image->delay=frame_delay;
5418 frame_delay=default_frame_delay;
5424 image->page.width=mng_info->mng_width;
5425 image->page.height=mng_info->mng_height;
5426 image->page.x=mng_info->x_off[object_id];
5427 image->page.y=mng_info->y_off[object_id];
5428 image->iterations=mng_iterations;
5431 Seek back to the beginning of the IHDR or JHDR chunk's length field.
5434 if (logging != MagickFalse)
5435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5436 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5439 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5442 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5446 mng_info->image=image;
5447 mng_info->mng_type=mng_type;
5448 mng_info->object_id=object_id;
5450 if (memcmp(type,mng_IHDR,4) == 0)
5451 image=ReadOnePNGImage(mng_info,image_info,exception);
5453 #if defined(JNG_SUPPORTED)
5455 image=ReadOneJNGImage(mng_info,image_info,exception);
5458 if (image == (Image *) NULL)
5460 if (IsImageObject(previous) != MagickFalse)
5462 (void) DestroyImageList(previous);
5463 (void) CloseBlob(previous);
5466 MngInfoFreeStruct(mng_info,&have_mng_structure);
5467 return((Image *) NULL);
5470 if (image->columns == 0 || image->rows == 0)
5472 (void) CloseBlob(image);
5473 image=DestroyImageList(image);
5474 MngInfoFreeStruct(mng_info,&have_mng_structure);
5475 return((Image *) NULL);
5478 mng_info->image=image;
5485 if (mng_info->magn_methx || mng_info->magn_methy)
5491 if (logging != MagickFalse)
5492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5493 " Processing MNG MAGN chunk");
5495 if (mng_info->magn_methx == 1)
5497 magnified_width=mng_info->magn_ml;
5499 if (image->columns > 1)
5500 magnified_width += mng_info->magn_mr;
5502 if (image->columns > 2)
5503 magnified_width += (png_uint_32)
5504 ((image->columns-2)*(mng_info->magn_mx));
5509 magnified_width=(png_uint_32) image->columns;
5511 if (image->columns > 1)
5512 magnified_width += mng_info->magn_ml-1;
5514 if (image->columns > 2)
5515 magnified_width += mng_info->magn_mr-1;
5517 if (image->columns > 3)
5518 magnified_width += (png_uint_32)
5519 ((image->columns-3)*(mng_info->magn_mx-1));
5522 if (mng_info->magn_methy == 1)
5524 magnified_height=mng_info->magn_mt;
5526 if (image->rows > 1)
5527 magnified_height += mng_info->magn_mb;
5529 if (image->rows > 2)
5530 magnified_height += (png_uint_32)
5531 ((image->rows-2)*(mng_info->magn_my));
5536 magnified_height=(png_uint_32) image->rows;
5538 if (image->rows > 1)
5539 magnified_height += mng_info->magn_mt-1;
5541 if (image->rows > 2)
5542 magnified_height += mng_info->magn_mb-1;
5544 if (image->rows > 3)
5545 magnified_height += (png_uint_32)
5546 ((image->rows-3)*(mng_info->magn_my-1));
5549 if (magnified_height > image->rows ||
5550 magnified_width > image->columns)
5565 register PixelPacket
5577 /* Allocate next image structure. */
5579 if (logging != MagickFalse)
5580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5581 " Allocate magnified image");
5583 AcquireNextImage(image_info,image);
5585 if (GetNextImageInList(image) == (Image *) NULL)
5587 image=DestroyImageList(image);
5588 MngInfoFreeStruct(mng_info,&have_mng_structure);
5589 return((Image *) NULL);
5592 large_image=SyncNextImageInList(image);
5594 large_image->columns=magnified_width;
5595 large_image->rows=magnified_height;
5597 magn_methx=mng_info->magn_methx;
5598 magn_methy=mng_info->magn_methy;
5600 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5601 #define QM unsigned short
5602 if (magn_methx != 1 || magn_methy != 1)
5605 Scale pixels to unsigned shorts to prevent
5606 overflow of intermediate values of interpolations
5608 for (y=0; y < (ssize_t) image->rows; y++)
5610 q=GetAuthenticPixels(image,0,y,image->columns,1,
5613 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5615 q->red=ScaleQuantumToShort(q->red);
5616 q->green=ScaleQuantumToShort(q->green);
5617 q->blue=ScaleQuantumToShort(q->blue);
5618 q->opacity=ScaleQuantumToShort(q->opacity);
5622 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5630 if (image->matte != MagickFalse)
5631 (void) SetImageBackgroundColor(large_image);
5635 large_image->background_color.opacity=OpaqueOpacity;
5636 (void) SetImageBackgroundColor(large_image);
5638 if (magn_methx == 4)
5641 if (magn_methx == 5)
5644 if (magn_methy == 4)
5647 if (magn_methy == 5)
5651 /* magnify the rows into the right side of the large image */
5653 if (logging != MagickFalse)
5654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5655 " Magnify the rows to %.20g",(double) large_image->rows);
5656 m=(ssize_t) mng_info->magn_mt;
5658 length=(size_t) image->columns;
5659 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5660 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5662 if ((prev == (PixelPacket *) NULL) ||
5663 (next == (PixelPacket *) NULL))
5665 image=DestroyImageList(image);
5666 MngInfoFreeStruct(mng_info,&have_mng_structure);
5667 ThrowReaderException(ResourceLimitError,
5668 "MemoryAllocationFailed");
5671 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5672 (void) CopyMagickMemory(next,n,length);
5674 for (y=0; y < (ssize_t) image->rows; y++)
5677 m=(ssize_t) mng_info->magn_mt;
5679 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5680 m=(ssize_t) mng_info->magn_mb;
5682 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5683 m=(ssize_t) mng_info->magn_mb;
5685 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5689 m=(ssize_t) mng_info->magn_my;
5695 if (y < (ssize_t) image->rows-1)
5697 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5699 (void) CopyMagickMemory(next,n,length);
5702 for (i=0; i < m; i++, yy++)
5704 register PixelPacket
5707 assert(yy < (ssize_t) large_image->rows);
5710 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5712 q+=(large_image->columns-image->columns);
5714 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5716 /* TO DO: get color as function of indexes[x] */
5718 if (image->storage_class == PseudoClass)
5723 if (magn_methy <= 1)
5725 *q=(*pixels); /* replicate previous */
5728 else if (magn_methy == 2 || magn_methy == 4)
5736 (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5737 -(*pixels).red)+m))/((ssize_t) (m*2))
5739 (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5740 -(*pixels).green)+m))/((ssize_t) (m*2))
5742 (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5743 -(*pixels).blue)+m))/((ssize_t) (m*2))
5746 if (image->matte != MagickFalse)
5747 (*q).opacity=(QM) (((ssize_t)
5749 -(*pixels).opacity)+m))
5750 /((ssize_t) (m*2))+(*pixels).opacity);
5753 if (magn_methy == 4)
5755 /* Replicate nearest */
5756 if (i <= ((m+1) << 1))
5757 (*q).opacity=(*pixels).opacity+0;
5759 (*q).opacity=(*n).opacity+0;
5763 else /* if (magn_methy == 3 || magn_methy == 5) */
5765 /* Replicate nearest */
5766 if (i <= ((m+1) << 1))
5772 if (magn_methy == 5)
5774 (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5775 -(*pixels).opacity)+m))/((ssize_t) (m*2))
5776 +(*pixels).opacity);
5784 if (SyncAuthenticPixels(large_image,exception) == 0)
5790 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5791 next=(PixelPacket *) RelinquishMagickMemory(next);
5793 length=image->columns;
5795 if (logging != MagickFalse)
5796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5797 " Delete original image");
5799 DeleteImageFromList(&image);
5803 mng_info->image=image;
5805 /* magnify the columns */
5806 if (logging != MagickFalse)
5807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5808 " Magnify the columns to %.20g",(double) image->columns);
5810 for (y=0; y < (ssize_t) image->rows; y++)
5812 register PixelPacket
5815 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5816 pixels=q+(image->columns-length);
5819 for (x=(ssize_t) (image->columns-length);
5820 x < (ssize_t) image->columns; x++)
5822 if (x == (ssize_t) (image->columns-length))
5823 m=(ssize_t) mng_info->magn_ml;
5825 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5826 m=(ssize_t) mng_info->magn_mr;
5828 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5829 m=(ssize_t) mng_info->magn_mr;
5831 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5835 m=(ssize_t) mng_info->magn_mx;
5837 for (i=0; i < m; i++)
5839 if (magn_methx <= 1)
5841 /* replicate previous */
5845 else if (magn_methx == 2 || magn_methx == 4)
5853 (*q).red=(QM) ((2*i*((*n).red
5855 /((ssize_t) (m*2))+(*pixels).red);
5856 (*q).green=(QM) ((2*i*((*n).green
5858 +m)/((ssize_t) (m*2))+(*pixels).green);
5859 (*q).blue=(QM) ((2*i*((*n).blue
5861 /((ssize_t) (m*2))+(*pixels).blue);
5862 if (image->matte != MagickFalse)
5863 (*q).opacity=(QM) ((2*i*((*n).opacity
5864 -(*pixels).opacity)+m)/((ssize_t) (m*2))
5865 +(*pixels).opacity);
5868 if (magn_methx == 4)
5870 /* Replicate nearest */
5871 if (i <= ((m+1) << 1))
5872 (*q).opacity=(*pixels).opacity+0;
5874 (*q).opacity=(*n).opacity+0;
5878 else /* if (magn_methx == 3 || magn_methx == 5) */
5880 /* Replicate nearest */
5881 if (i <= ((m+1) << 1))
5887 if (magn_methx == 5)
5890 (*q).opacity=(QM) ((2*i*((*n).opacity
5891 -(*pixels).opacity)+m) /((ssize_t) (m*2))
5892 +(*pixels).opacity);
5901 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5904 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5905 if (magn_methx != 1 || magn_methy != 1)
5908 Rescale pixels to Quantum
5910 for (y=0; y < (ssize_t) image->rows; y++)
5912 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5914 for (x=(ssize_t) image->columns-1; x >= 0; x--)
5916 q->red=ScaleShortToQuantum(q->red);
5917 q->green=ScaleShortToQuantum(q->green);
5918 q->blue=ScaleShortToQuantum(q->blue);
5919 q->opacity=ScaleShortToQuantum(q->opacity);
5923 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5928 if (logging != MagickFalse)
5929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5930 " Finished MAGN processing");
5935 Crop_box is with respect to the upper left corner of the MNG.
5937 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5938 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5939 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5940 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5941 crop_box=mng_minimum_box(crop_box,mng_info->clip);
5942 crop_box=mng_minimum_box(crop_box,mng_info->frame);
5943 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5944 if ((crop_box.left != (mng_info->image_box.left
5945 +mng_info->x_off[object_id])) ||
5946 (crop_box.right != (mng_info->image_box.right
5947 +mng_info->x_off[object_id])) ||
5948 (crop_box.top != (mng_info->image_box.top
5949 +mng_info->y_off[object_id])) ||
5950 (crop_box.bottom != (mng_info->image_box.bottom
5951 +mng_info->y_off[object_id])))
5953 if (logging != MagickFalse)
5954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5955 " Crop the PNG image");
5957 if ((crop_box.left < crop_box.right) &&
5958 (crop_box.top < crop_box.bottom))
5967 Crop_info is with respect to the upper left corner of
5970 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5971 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5972 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5973 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5974 image->page.width=image->columns;
5975 image->page.height=image->rows;
5978 im=CropImage(image,&crop_info,exception);
5980 if (im != (Image *) NULL)
5982 image->columns=im->columns;
5983 image->rows=im->rows;
5984 im=DestroyImage(im);
5985 image->page.width=image->columns;
5986 image->page.height=image->rows;
5987 image->page.x=crop_box.left;
5988 image->page.y=crop_box.top;
5995 No pixels in crop area. The MNG spec still requires
5996 a layer, though, so make a single transparent pixel in
5997 the top left corner.
6002 (void) SetImageBackgroundColor(image);
6003 image->page.width=1;
6004 image->page.height=1;
6009 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6010 image=mng_info->image;
6014 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6015 /* PNG does not handle depths greater than 16 so reduce it even
6018 if (image->depth > 16)
6022 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6023 if (LosslessReduceDepthOK(image) != MagickFalse)
6027 GetImageException(image,exception);
6029 if (image_info->number_scenes != 0)
6031 if (mng_info->scenes_found >
6032 (ssize_t) (image_info->first_scene+image_info->number_scenes))
6036 if (logging != MagickFalse)
6037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6038 " Finished reading image datastream.");
6040 } while (LocaleCompare(image_info->magick,"MNG") == 0);
6042 (void) CloseBlob(image);
6044 if (logging != MagickFalse)
6045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6046 " Finished reading all image datastreams.");
6048 #if defined(MNG_INSERT_LAYERS)
6049 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6050 (mng_info->mng_height))
6053 Insert a background layer if nothing else was found.
6055 if (logging != MagickFalse)
6056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6057 " No images found. Inserting a background layer.");
6059 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6062 Allocate next image structure.
6064 AcquireNextImage(image_info,image);
6065 if (GetNextImageInList(image) == (Image *) NULL)
6067 image=DestroyImageList(image);
6068 MngInfoFreeStruct(mng_info,&have_mng_structure);
6070 if (logging != MagickFalse)
6071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6072 " Allocation failed, returning NULL.");
6074 return((Image *) NULL);
6076 image=SyncNextImageInList(image);
6078 image->columns=mng_info->mng_width;
6079 image->rows=mng_info->mng_height;
6080 image->page.width=mng_info->mng_width;
6081 image->page.height=mng_info->mng_height;
6084 image->background_color=mng_background_color;
6085 image->matte=MagickFalse;
6087 if (image_info->ping == MagickFalse)
6088 (void) SetImageBackgroundColor(image);
6090 mng_info->image_found++;
6093 image->iterations=mng_iterations;
6095 if (mng_iterations == 1)
6096 image->start_loop=MagickTrue;
6098 while (GetPreviousImageInList(image) != (Image *) NULL)
6101 if (image_count > 10*mng_info->image_found)
6103 if (logging != MagickFalse)
6104 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
6106 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6107 CoderError,"Linked list is corrupted, beginning of list not found",
6108 "`%s'",image_info->filename);
6110 return((Image *) NULL);
6113 image=GetPreviousImageInList(image);
6115 if (GetNextImageInList(image) == (Image *) NULL)
6117 if (logging != MagickFalse)
6118 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
6120 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6121 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6122 image_info->filename);
6126 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6127 GetNextImageInList(image) ==
6130 if (logging != MagickFalse)
6131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6132 " First image null");
6134 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6135 CoderError,"image->next for first image is NULL but shouldn't be.",
6136 "`%s'",image_info->filename);
6139 if (mng_info->image_found == 0)
6141 if (logging != MagickFalse)
6142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6143 " No visible images found.");
6145 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6146 CoderError,"No visible images in file","`%s'",image_info->filename);
6148 if (image != (Image *) NULL)
6149 image=DestroyImageList(image);
6151 MngInfoFreeStruct(mng_info,&have_mng_structure);
6152 return((Image *) NULL);
6155 if (mng_info->ticks_per_second)
6156 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6157 final_delay/mng_info->ticks_per_second;
6160 image->start_loop=MagickTrue;
6162 /* Find final nonzero image delay */
6163 final_image_delay=0;
6165 while (GetNextImageInList(image) != (Image *) NULL)
6168 final_image_delay=image->delay;
6170 image=GetNextImageInList(image);
6173 if (final_delay < final_image_delay)
6174 final_delay=final_image_delay;
6176 image->delay=final_delay;
6178 if (logging != MagickFalse)
6179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6180 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6181 (double) final_delay);
6183 if (logging != MagickFalse)
6189 image=GetFirstImageInList(image);
6191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6192 " Before coalesce:");
6194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6195 " scene 0 delay=%.20g",(double) image->delay);
6197 while (GetNextImageInList(image) != (Image *) NULL)
6199 image=GetNextImageInList(image);
6200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6201 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6205 image=GetFirstImageInList(image);
6206 #ifdef MNG_COALESCE_LAYERS
6216 if (logging != MagickFalse)
6217 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
6220 next_image=CoalesceImages(image,&image->exception);
6222 if (next_image == (Image *) NULL)
6223 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6225 image=DestroyImageList(image);
6228 for (next=image; next != (Image *) NULL; next=next_image)
6230 next->page.width=mng_info->mng_width;
6231 next->page.height=mng_info->mng_height;
6234 next->scene=scene++;
6235 next_image=GetNextImageInList(next);
6237 if (next_image == (Image *) NULL)
6240 if (next->delay == 0)
6243 next_image->previous=GetPreviousImageInList(next);
6244 if (GetPreviousImageInList(next) == (Image *) NULL)
6247 next->previous->next=next_image;
6248 next=DestroyImage(next);
6254 while (GetNextImageInList(image) != (Image *) NULL)
6255 image=GetNextImageInList(image);
6257 image->dispose=BackgroundDispose;
6259 if (logging != MagickFalse)
6265 image=GetFirstImageInList(image);
6267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6268 " After coalesce:");
6270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6271 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6272 (double) image->dispose);
6274 while (GetNextImageInList(image) != (Image *) NULL)
6276 image=GetNextImageInList(image);
6278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6279 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6280 (double) image->delay,(double) image->dispose);
6284 image=GetFirstImageInList(image);
6285 MngInfoFreeStruct(mng_info,&have_mng_structure);
6286 have_mng_structure=MagickFalse;
6288 if (logging != MagickFalse)
6289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6291 return(GetFirstImageInList(image));
6293 #else /* PNG_LIBPNG_VER > 10011 */
6294 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6296 printf("Your PNG library is too old: You have libpng-%s\n",
6297 PNG_LIBPNG_VER_STRING);
6299 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6300 "PNG library is too old","`%s'",image_info->filename);
6302 return(Image *) NULL;
6305 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6307 return(ReadPNGImage(image_info,exception));
6309 #endif /* PNG_LIBPNG_VER > 10011 */
6313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6317 % R e g i s t e r P N G I m a g e %
6321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6323 % RegisterPNGImage() adds properties for the PNG image format to
6324 % the list of supported formats. The properties include the image format
6325 % tag, a method to read and/or write the format, whether the format
6326 % supports the saving of more than one frame to the same file or blob,
6327 % whether the format supports native in-memory I/O, and a brief
6328 % description of the format.
6330 % The format of the RegisterPNGImage method is:
6332 % size_t RegisterPNGImage(void)
6335 ModuleExport size_t RegisterPNGImage(void)
6338 version[MaxTextExtent];
6346 "See http://www.libpng.org/ for details about the PNG format."
6351 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6357 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6363 #if defined(PNG_LIBPNG_VER_STRING)
6364 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6365 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6367 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6369 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6370 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6375 entry=SetMagickInfo("MNG");
6376 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
6378 #if defined(MAGICKCORE_PNG_DELEGATE)
6379 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6380 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6383 entry->magick=(IsImageFormatHandler *) IsMNG;
6384 entry->description=ConstantString("Multiple-image Network Graphics");
6386 if (*version != '\0')
6387 entry->version=ConstantString(version);
6389 entry->module=ConstantString("PNG");
6390 entry->note=ConstantString(MNGNote);
6391 (void) RegisterMagickInfo(entry);
6393 entry=SetMagickInfo("PNG");
6395 #if defined(MAGICKCORE_PNG_DELEGATE)
6396 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6397 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6400 entry->magick=(IsImageFormatHandler *) IsPNG;
6401 entry->adjoin=MagickFalse;
6402 entry->description=ConstantString("Portable Network Graphics");
6403 entry->module=ConstantString("PNG");
6405 if (*version != '\0')
6406 entry->version=ConstantString(version);
6408 entry->note=ConstantString(PNGNote);
6409 (void) RegisterMagickInfo(entry);
6411 entry=SetMagickInfo("PNG8");
6413 #if defined(MAGICKCORE_PNG_DELEGATE)
6414 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6415 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6418 entry->magick=(IsImageFormatHandler *) IsPNG;
6419 entry->adjoin=MagickFalse;
6420 entry->description=ConstantString(
6421 "8-bit indexed with optional binary transparency");
6422 entry->module=ConstantString("PNG");
6423 (void) RegisterMagickInfo(entry);
6425 entry=SetMagickInfo("PNG24");
6428 #if defined(ZLIB_VERSION)
6429 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6430 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6432 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6434 (void) ConcatenateMagickString(version,",",MaxTextExtent);
6435 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6439 if (*version != '\0')
6440 entry->version=ConstantString(version);
6442 #if defined(MAGICKCORE_PNG_DELEGATE)
6443 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6444 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6447 entry->magick=(IsImageFormatHandler *) IsPNG;
6448 entry->adjoin=MagickFalse;
6449 entry->description=ConstantString("opaque 24-bit RGB");
6450 entry->module=ConstantString("PNG");
6451 (void) RegisterMagickInfo(entry);
6453 entry=SetMagickInfo("PNG32");
6455 #if defined(MAGICKCORE_PNG_DELEGATE)
6456 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6457 entry->encoder=(EncodeImageHandler *) WritePNGImage;
6460 entry->magick=(IsImageFormatHandler *) IsPNG;
6461 entry->adjoin=MagickFalse;
6462 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6463 entry->module=ConstantString("PNG");
6464 (void) RegisterMagickInfo(entry);
6466 entry=SetMagickInfo("JNG");
6468 #if defined(JNG_SUPPORTED)
6469 #if defined(MAGICKCORE_PNG_DELEGATE)
6470 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6471 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6475 entry->magick=(IsImageFormatHandler *) IsJNG;
6476 entry->adjoin=MagickFalse;
6477 entry->description=ConstantString("JPEG Network Graphics");
6478 entry->module=ConstantString("PNG");
6479 entry->note=ConstantString(JNGNote);
6480 (void) RegisterMagickInfo(entry);
6482 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6483 ping_semaphore=AllocateSemaphoreInfo();
6486 return(MagickImageCoderSignature);
6490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6494 % U n r e g i s t e r P N G I m a g e %
6498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6500 % UnregisterPNGImage() removes format registrations made by the
6501 % PNG module from the list of supported formats.
6503 % The format of the UnregisterPNGImage method is:
6505 % UnregisterPNGImage(void)
6508 ModuleExport void UnregisterPNGImage(void)
6510 (void) UnregisterMagickInfo("MNG");
6511 (void) UnregisterMagickInfo("PNG");
6512 (void) UnregisterMagickInfo("PNG8");
6513 (void) UnregisterMagickInfo("PNG24");
6514 (void) UnregisterMagickInfo("PNG32");
6515 (void) UnregisterMagickInfo("JNG");
6517 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6518 if (ping_semaphore != (SemaphoreInfo *) NULL)
6519 DestroySemaphoreInfo(&ping_semaphore);
6523 #if defined(MAGICKCORE_PNG_DELEGATE)
6524 #if PNG_LIBPNG_VER > 10011
6526 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6530 % W r i t e M N G I m a g e %
6534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6536 % WriteMNGImage() writes an image in the Portable Network Graphics
6537 % Group's "Multiple-image Network Graphics" encoded image format.
6539 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
6541 % The format of the WriteMNGImage method is:
6543 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6545 % A description of each parameter follows.
6547 % o image_info: the image info.
6549 % o image: The image.
6552 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6553 % "To do" under ReadPNGImage):
6555 % Preserve all unknown and not-yet-handled known chunks found in input
6556 % PNG file and copy them into output PNG files according to the PNG
6559 % Write the iCCP chunk at MNG level when (icc profile length > 0)
6561 % Improve selection of color type (use indexed-colour or indexed-colour
6562 % with tRNS when 256 or fewer unique RGBA values are present).
6564 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6565 % This will be complicated if we limit ourselves to generating MNG-LC
6566 % files. For now we ignore disposal method 3 and simply overlay the next
6569 % Check for identical PLTE's or PLTE/tRNS combinations and use a
6570 % global MNG PLTE or PLTE/tRNS combination when appropriate.
6571 % [mostly done 15 June 1999 but still need to take care of tRNS]
6573 % Check for identical sRGB and replace with a global sRGB (and remove
6574 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6575 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6576 % local gAMA/cHRM with local sRGB if appropriate).
6578 % Check for identical sBIT chunks and write global ones.
6580 % Provide option to skip writing the signature tEXt chunks.
6582 % Use signatures to detect identical objects and reuse the first
6583 % instance of such objects instead of writing duplicate objects.
6585 % Use a smaller-than-32k value of compression window size when
6588 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
6589 % ancillary text chunks and save profiles.
6591 % Provide an option to force LC files (to ensure exact framing rate)
6594 % Provide an option to force VLC files instead of LC, even when offsets
6595 % are present. This will involve expanding the embedded images with a
6596 % transparent region at the top and/or left.
6600 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6601 png_info *ping_info, unsigned char *profile_type, unsigned char
6602 *profile_description, unsigned char *profile_data, png_uint_32 length)
6621 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6623 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6626 if (image_info->verbose)
6628 (void) printf("writing raw profile: type=%s, length=%.20g\n",
6629 (char *) profile_type, (double) length);
6632 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6633 description_length=(png_uint_32) strlen((const char *) profile_description);
6634 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6635 + description_length);
6636 text[0].text=(png_charp) png_malloc(ping,allocated_length);
6637 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6638 text[0].key[0]='\0';
6639 (void) ConcatenateMagickString(text[0].key,
6640 "Raw profile type ",MaxTextExtent);
6641 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6645 (void) CopyMagickString(dp,(const char *) profile_description,
6647 dp+=description_length;
6649 (void) FormatMagickString(dp,allocated_length-
6650 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6653 for (i=0; i < (ssize_t) length; i++)
6657 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6658 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6663 text[0].text_length=(png_size_t) (dp-text[0].text);
6664 text[0].compression=image_info->compression == NoCompression ||
6665 (image_info->compression == UndefinedCompression &&
6666 text[0].text_length < 128) ? -1 : 0;
6668 if (text[0].text_length <= allocated_length)
6669 png_set_text(ping,ping_info,text,1);
6671 png_free(ping,text[0].text);
6672 png_free(ping,text[0].key);
6673 png_free(ping,text);
6676 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6677 const char *string, MagickBooleanType logging)
6690 ResetImageProfileIterator(image);
6692 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6694 profile=GetImageProfile(image,name);
6696 if (profile != (const StringInfo *) NULL)
6701 if (LocaleNCompare(name,string,11) == 0)
6703 if (logging != MagickFalse)
6704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6705 " Found %s profile",name);
6707 ping_profile=CloneStringInfo(profile);
6708 data=GetStringInfoDatum(ping_profile),
6709 length=(png_uint_32) GetStringInfoLength(ping_profile);
6714 (void) WriteBlobMSBULong(image,length-5); /* data length */
6715 (void) WriteBlob(image,length-1,data+1);
6716 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6717 ping_profile=DestroyStringInfo(ping_profile);
6721 name=GetNextImageProfile(image);
6728 /* Write one PNG image */
6729 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6730 const ImageInfo *IMimage_info,Image *IMimage)
6754 ping_trans_alpha[256];
6790 /* ping_exclude_EXIF, */
6793 /* ping_exclude_iTXt, */
6798 /* ping_exclude_tRNS, */
6800 ping_exclude_zCCP, /* hex-encoded iCCP */
6803 ping_need_colortype_warning,
6821 ping_interlace_method,
6822 ping_compression_method,
6838 number_semitransparent,
6840 ping_pHYs_unit_type;
6843 ping_pHYs_x_resolution,
6844 ping_pHYs_y_resolution;
6846 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6847 " enter WriteOnePNGImage()");
6849 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
6850 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
6852 if (mng_info->need_blob != MagickFalse)
6854 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
6857 image_info=DestroyImageInfo(image_info);
6858 image=DestroyImage(image);
6859 return(MagickFalse);
6863 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6864 LockSemaphoreInfo(ping_semaphore);
6867 /* Initialize some stuff */
6870 ping_interlace_method=0,
6871 ping_compression_method=0,
6872 ping_filter_method=0,
6875 ping_background.red = 0;
6876 ping_background.green = 0;
6877 ping_background.blue = 0;
6878 ping_background.gray = 0;
6879 ping_background.index = 0;
6881 ping_trans_color.red=0;
6882 ping_trans_color.green=0;
6883 ping_trans_color.blue=0;
6884 ping_trans_color.gray=0;
6886 ping_pHYs_unit_type = 0;
6887 ping_pHYs_x_resolution = 0;
6888 ping_pHYs_y_resolution = 0;
6890 ping_have_color=MagickTrue;
6891 ping_have_non_bw=MagickTrue;
6892 ping_have_PLTE=MagickFalse;
6893 ping_have_bKGD=MagickFalse;
6894 ping_have_pHYs=MagickFalse;
6895 ping_have_tRNS=MagickFalse;
6897 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
6898 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6899 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
6900 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
6901 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6902 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
6903 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
6904 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
6905 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
6906 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
6907 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
6908 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
6909 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
6910 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
6911 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
6913 ping_need_colortype_warning = MagickFalse;
6916 number_semitransparent = 0;
6917 number_transparent = 0;
6919 if (image->colorspace != RGBColorspace)
6920 (void) TransformImageColorspace(image,RGBColorspace);
6923 Sometimes we get PseudoClass images whose RGB values don't match
6924 the colors in the colormap. This code syncs the RGB values.
6926 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
6927 (void) SyncImage(image);
6929 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
6930 if (image->depth > 8)
6932 if (logging != MagickFalse)
6933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6934 " Reducing PNG bit depth to 8 since this is a Q8 build.");
6941 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6942 /* PNG does not handle depths greater than 16 so reduce it even
6945 if (image->depth > 16)
6949 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6950 if (image->depth == 16 && mng_info->write_png_colortype != 16)
6951 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
6958 * Sometimes we get DirectClass images that have 256 colors or fewer.
6959 * This code will build a colormap.
6961 * Also, sometimes we get PseudoClass images with an out-of-date
6962 * colormap. This code will replace the colormap with a new one.
6963 * Sometimes we get PseudoClass images that have more than 256 colors.
6964 * This code will delete the colormap and change the image to
6967 * If image->matte is MagickFalse, we ignore the opacity channel
6968 * even though it sometimes contains left-over non-opaque values.
6970 * Also we gather some information (number of opaque, transparent,
6971 * and semitransparent pixels, and whether the image has any non-gray
6972 * pixels or only black-and-white pixels) that we might need later.
6974 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
6975 * we need to check for bogus non-opaque values, at least.
6986 semitransparent[260],
6989 register IndexPacket
6992 register const PixelPacket
6996 if (logging != MagickFalse)
6997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6998 " Enter BUILD_PALETTE:");
7000 if (logging != MagickFalse)
7002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7003 " image->columns=%.20g",(double) image->columns);
7004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7005 " image->rows=%.20g",(double) image->rows);
7006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7007 " image->matte=%.20g",(double) image->matte);
7008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7009 " image->depth=%.20g",(double) image->depth);
7011 if (image->colormap != NULL)
7013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7014 " Original colormap:");
7015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7016 " i (red,green,blue,opacity)");
7018 for (i=0; i < 256; i++)
7020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7021 " %d (%d,%d,%d,%d)",
7023 (int) image->colormap[i].red,
7024 (int) image->colormap[i].green,
7025 (int) image->colormap[i].blue,
7026 (int) image->colormap[i].opacity);
7029 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7034 " %d (%d,%d,%d,%d)",
7036 (int) image->colormap[i].red,
7037 (int) image->colormap[i].green,
7038 (int) image->colormap[i].blue,
7039 (int) image->colormap[i].opacity);
7044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7045 " image->colors=%d",(int) image->colors);
7047 if (image->colors == 0)
7048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7049 " (zero means unknown)");
7051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7052 " Regenerate the colormap");
7055 exception=(&image->exception);
7059 for (y=0; y < (ssize_t) image->rows; y++)
7061 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7063 if (q == (PixelPacket *) NULL)
7066 for (x=0; x < (ssize_t) image->columns; x++)
7068 if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7070 if (number_opaque < 259)
7072 if (number_opaque == 0)
7075 opaque[0].opacity=OpaqueOpacity;
7079 for (i=0; i< (ssize_t) number_opaque; i++)
7081 if (IsColorEqual(opaque+i, (PixelPacket *) q))
7085 if (i == (ssize_t) number_opaque &&
7086 number_opaque < 259)
7090 opaque[i].opacity = OpaqueOpacity;
7094 else if (q->opacity == TransparentOpacity)
7096 if (number_transparent < 259)
7098 if (number_transparent == 0)
7101 ping_trans_color.red=(unsigned short)(q->red);
7102 ping_trans_color.green=(unsigned short) (q->green);
7103 ping_trans_color.blue=(unsigned short) (q->blue);
7104 ping_trans_color.gray=(unsigned short) (q->blue);
7105 number_transparent = 1;
7108 for (i=0; i< (ssize_t) number_transparent; i++)
7110 if (IsColorEqual(transparent+i, (PixelPacket *) q))
7114 if (i == (ssize_t) number_transparent &&
7115 number_transparent < 259)
7117 number_transparent++;
7118 transparent[i] = *q;
7124 if (number_semitransparent < 259)
7126 if (number_semitransparent == 0)
7128 semitransparent[0]=*q;
7129 number_semitransparent = 1;
7132 for (i=0; i< (ssize_t) number_semitransparent; i++)
7134 if (IsColorEqual(semitransparent+i,
7135 (PixelPacket *) q) &&
7136 q->opacity == semitransparent[i].opacity)
7140 if (i == (ssize_t) number_semitransparent &&
7141 number_semitransparent < 259)
7143 number_semitransparent++;
7144 semitransparent[i] = *q;
7152 if (ping_exclude_bKGD == MagickFalse)
7154 /* Add the background color to the palette, if it
7155 * isn't already there.
7157 for (i=0; i<number_opaque; i++)
7159 if (IsColorEqual(opaque+i,
7160 &image->background_color))
7164 if (number_opaque < 259 && i == number_opaque)
7166 opaque[i]=image->background_color;
7167 opaque[i].opacity = OpaqueOpacity;
7172 image_colors=number_opaque+number_transparent+number_semitransparent;
7174 if (logging != MagickFalse)
7176 if (image_colors > 256)
7177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7178 " image has more than 256 colors");
7181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7182 " image has %d colors",image_colors);
7185 if (mng_info->write_png_colortype != 7 && /* We won't need this info */
7186 mng_info->write_png_colortype != 3) /* for these color types */
7188 ping_have_color=MagickFalse;
7189 ping_have_non_bw=MagickFalse;
7191 if(image_colors > 256)
7193 for (y=0; y < (ssize_t) image->rows; y++)
7195 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7197 if (q == (PixelPacket *) NULL)
7200 /* Worst case is black-and-white; we are looking at every
7204 if (ping_have_color == MagickFalse)
7207 for (x=0; x < (ssize_t) image->columns; x++)
7209 if (s->red != s->green || s->red != s->blue)
7211 ping_have_color=MagickTrue;
7212 ping_have_non_bw=MagickTrue;
7219 if (ping_have_non_bw == MagickFalse)
7222 for (x=0; x < (ssize_t) image->columns; x++)
7224 if (s->red != 0 && s->red != QuantumRange)
7226 ping_have_non_bw=MagickTrue;
7235 if (image_colors < 257)
7241 * Initialize image colormap.
7244 if (logging != MagickFalse)
7245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7246 " Sort the new colormap");
7248 /* Sort palette, transparent first */;
7252 for (i=0; i<number_transparent; i++)
7253 colormap[n++] = transparent[i];
7255 for (i=0; i<number_semitransparent; i++)
7256 colormap[n++] = semitransparent[i];
7258 for (i=0; i<number_opaque; i++)
7259 colormap[n++] = opaque[i];
7262 /* image_colors < 257; search the colormap instead of the pixels
7263 * to get ping_have_color and ping_have_non_bw
7267 if (ping_have_color == MagickFalse)
7269 if (colormap[i].red != colormap[i].green ||
7270 colormap[i].red != colormap[i].blue)
7272 ping_have_color=MagickTrue;
7273 ping_have_non_bw=MagickTrue;
7278 if (ping_have_non_bw == MagickFalse)
7280 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7281 ping_have_non_bw=MagickTrue;
7285 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7286 (number_transparent == 0 && number_semitransparent == 0)) &&
7287 (((mng_info->write_png_colortype-1) ==
7288 PNG_COLOR_TYPE_PALETTE) ||
7289 (mng_info->write_png_colortype == 0)))
7291 if (logging != MagickFalse)
7293 if (n != (ssize_t) image_colors)
7294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7295 " image_colors (%d) and n (%d) don't match",
7298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7299 " AcquireImageColormap");
7302 image->colors = image_colors;
7304 if (AcquireImageColormap(image,image_colors) ==
7306 ThrowWriterException(ResourceLimitError,
7307 "MemoryAllocationFailed");
7309 for (i=0; i< (ssize_t) image_colors; i++)
7310 image->colormap[i] = colormap[i];
7312 if (logging != MagickFalse)
7314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7315 " image->colors=%d (%d)",
7316 (int) image->colors, image_colors);
7318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7319 " Update the pixel indexes");
7322 for (y=0; y < (ssize_t) image->rows; y++)
7324 q=GetAuthenticPixels(image,0,y,image->columns,1,
7327 if (q == (PixelPacket *) NULL)
7330 indexes=GetAuthenticIndexQueue(image);
7332 for (x=0; x < (ssize_t) image->columns; x++)
7334 for (i=0; i< (ssize_t) image_colors; i++)
7336 if ((image->matte == MagickFalse ||
7337 image->colormap[i].opacity == q->opacity) &&
7338 (IsColorEqual(&image->colormap[i],
7339 (PixelPacket *) q)))
7341 indexes[x]=(IndexPacket) i;
7348 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7354 if (logging != MagickFalse)
7356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7357 " image->colors=%d", (int) image->colors);
7359 if (image->colormap != NULL)
7361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7362 " i (red,green,blue,opacity)");
7364 for (i=0; i < (ssize_t) image->colors; i++)
7366 if (i < 300 || i >= image->colors - 10)
7368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7369 " %d (%d,%d,%d,%d)",
7371 (int) image->colormap[i].red,
7372 (int) image->colormap[i].green,
7373 (int) image->colormap[i].blue,
7374 (int) image->colormap[i].opacity);
7379 if (number_transparent < 257)
7380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7381 " number_transparent = %d",
7382 number_transparent);
7385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7386 " number_transparent > 256");
7388 if (number_opaque < 257)
7389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7390 " number_opaque = %d",
7394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7395 " number_opaque > 256");
7397 if (number_semitransparent < 257)
7398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7399 " number_semitransparent = %d",
7400 number_semitransparent);
7403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7404 " number_semitransparent > 256");
7406 if (ping_have_non_bw == MagickFalse)
7407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7408 " All pixels and the background are black or white");
7410 else if (ping_have_color == MagickFalse)
7411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7412 " All pixels and the background are gray");
7415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7416 " At least one pixel or the background is non-gray");
7418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7419 " Exit BUILD_PALETTE:");
7423 /* Force sub-8-bit grayscale images, except for black-and-white images,
7424 * to be written as indexed-color PNG
7426 if ((number_transparent == 0 && number_semitransparent == 0) &&
7427 mng_info->write_png_colortype == 0 &&
7428 ((number_opaque <= 2 && ping_have_non_bw != MagickFalse) ||
7429 (number_opaque > 2 && number_opaque < 17)))
7430 ping_have_color = MagickTrue;
7432 if (mng_info->ping_exclude_tRNS != MagickFalse &&
7433 (number_transparent != 0 || number_semitransparent != 0))
7435 int colortype=mng_info->write_png_colortype;
7437 if (ping_have_color == MagickFalse)
7438 mng_info->write_png_colortype = 5;
7441 mng_info->write_png_colortype = 7;
7443 if (colortype != 0 &&
7444 mng_info->write_png_colortype != (ssize_t) colortype)
7445 ping_need_colortype_warning=MagickTrue;
7449 image_depth=image->depth;
7451 quantum_info = (QuantumInfo *) NULL;
7453 image_colors=(int) image->colors;
7454 image_matte=image->matte;
7456 mng_info->IsPalette=image->storage_class == PseudoClass &&
7457 image_colors <= 256;
7460 Allocate the PNG structures
7462 #ifdef PNG_USER_MEM_SUPPORTED
7463 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
7464 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
7465 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
7468 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
7469 MagickPNGErrorHandler,MagickPNGWarningHandler);
7472 if (ping == (png_struct *) NULL)
7473 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7475 ping_info=png_create_info_struct(ping);
7477 if (ping_info == (png_info *) NULL)
7479 png_destroy_write_struct(&ping,(png_info **) NULL);
7480 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7483 png_set_write_fn(ping,image,png_put_data,png_flush_data);
7484 ping_pixels=(unsigned char *) NULL;
7486 if (setjmp(png_jmpbuf(ping)))
7492 if (image_info->verbose)
7493 (void) printf("PNG write has failed.\n");
7495 png_destroy_write_struct(&ping,&ping_info);
7496 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7497 UnlockSemaphoreInfo(ping_semaphore);
7499 if (mng_info->need_blob != MagickFalse)
7500 (void) CloseBlob(image);
7501 image_info=DestroyImageInfo(image_info);
7502 image=DestroyImage(image);
7503 return(MagickFalse);
7506 Prepare PNG for writing.
7508 #if defined(PNG_MNG_FEATURES_SUPPORTED)
7509 if (mng_info->write_mng)
7510 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
7513 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
7514 if (mng_info->write_mng)
7515 png_permit_empty_plte(ping,MagickTrue);
7522 ping_width=(png_uint_32) image->columns;
7523 ping_height=(png_uint_32) image->rows;
7525 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
7528 if (mng_info->write_png_depth != 0)
7529 image_depth=mng_info->write_png_depth;
7531 /* Adjust requested depth to next higher valid depth if necessary */
7532 if (image_depth > 8)
7535 if ((image_depth > 4) && (image_depth < 8))
7538 if (image_depth == 3)
7541 if (logging != MagickFalse)
7543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7544 " width=%.20g",(double) ping_width);
7545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7546 " height=%.20g",(double) ping_height);
7547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7548 " image_matte=%.20g",(double) image->matte);
7549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7550 " image->depth=%.20g",(double) image->depth);
7551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7552 " Tentative ping_bit_depth=%.20g",(double) image_depth);
7555 save_image_depth=image_depth;
7556 ping_bit_depth=(png_byte) save_image_depth;
7559 #if defined(PNG_pHYs_SUPPORTED)
7560 if (ping_exclude_pHYs == MagickFalse)
7562 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
7563 (!mng_info->write_mng || !mng_info->equal_physs))
7565 if (logging != MagickFalse)
7566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7567 " Setting up pHYs chunk");
7569 if (image->units == PixelsPerInchResolution)
7571 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
7572 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
7573 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
7576 else if (image->units == PixelsPerCentimeterResolution)
7578 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
7579 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution);
7580 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution);
7585 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
7586 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
7587 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
7590 ping_have_pHYs = MagickTrue;
7595 if (ping_exclude_bKGD == MagickFalse)
7597 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
7603 if (ping_bit_depth == 8)
7606 if (ping_bit_depth == 4)
7609 if (ping_bit_depth == 2)
7612 if (ping_bit_depth == 1)
7615 ping_background.red=(png_uint_16)
7616 (ScaleQuantumToShort(image->background_color.red) & mask);
7618 ping_background.green=(png_uint_16)
7619 (ScaleQuantumToShort(image->background_color.green) & mask);
7621 ping_background.blue=(png_uint_16)
7622 (ScaleQuantumToShort(image->background_color.blue) & mask);
7625 if (logging != MagickFalse)
7627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7628 " Setting up bKGD chunk (1)");
7630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7631 " ping_bit_depth=%d",ping_bit_depth);
7634 ping_have_bKGD = MagickTrue;
7638 Select the color type.
7643 if (mng_info->write_png8)
7646 /* TO DO: make this a function cause it's used twice, except
7647 for reducing the sample depth from 8. */
7649 number_colors=image_colors;
7651 ping_have_tRNS=MagickFalse;
7656 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
7658 if (logging != MagickFalse)
7659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7660 " Setting up PLTE chunk with %d colors (%d)",
7661 number_colors, image_colors);
7663 for (i=0; i < (ssize_t) number_colors; i++)
7665 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7666 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7667 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7668 if (logging != MagickFalse)
7669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7670 #if MAGICKCORE_QUANTUM_DEPTH == 8
7671 " %3ld (%3d,%3d,%3d)",
7673 " %5ld (%5d,%5d,%5d)",
7675 (long) i,palette[i].red,palette[i].green,palette[i].blue);
7679 ping_have_PLTE=MagickTrue;
7680 image_depth=ping_bit_depth;
7683 if (matte != MagickFalse)
7686 Identify which colormap entry is transparent.
7688 assert(number_colors <= 256);
7689 assert(image->colormap != NULL);
7691 for (i=0; i < (ssize_t) number_transparent; i++)
7692 ping_trans_alpha[i]=0;
7694 /* PNG8 can't have semitransparent colors so we threshold them
7697 for (; i < (ssize_t) number_semitransparent; i++)
7698 ping_trans_alpha[i]=image->colormap[i].opacity >
7699 OpaqueOpacity/2 ? 0 : 255;
7701 ping_num_trans=(unsigned short) (number_transparent +
7702 number_semitransparent);
7704 if (ping_num_trans == 0)
7705 ping_have_tRNS=MagickFalse;
7708 ping_have_tRNS=MagickTrue;
7711 if (ping_exclude_bKGD == MagickFalse)
7714 * Identify which colormap entry is the background color.
7716 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
7717 if (IsPNGColorEqual(ping_background,image->colormap[i]))
7720 ping_background.index=(png_byte) i;
7722 } /* end of write_png8 */
7724 else if (mng_info->write_png24)
7726 image_matte=MagickFalse;
7727 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
7730 else if (mng_info->write_png32)
7732 image_matte=MagickTrue;
7733 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
7736 else /* mng_info->write_pngNN not specified */
7738 image_depth=ping_bit_depth;
7740 if (mng_info->write_png_colortype != 0)
7742 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
7744 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
7745 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
7746 image_matte=MagickTrue;
7749 image_matte=MagickFalse;
7752 else /* write_ping_colortype not specified */
7754 if (logging != MagickFalse)
7755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7756 " Selecting PNG colortype:");
7758 ping_color_type=(png_byte) ((matte != MagickFalse)?
7759 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
7761 if (image_info->type == TrueColorType)
7763 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
7764 image_matte=MagickFalse;
7767 if (image_info->type == TrueColorMatteType)
7769 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
7770 image_matte=MagickTrue;
7773 if (image_info->type == PaletteType ||
7774 image_info->type == PaletteMatteType)
7775 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
7777 if (image_info->type == UndefinedType ||
7778 image_info->type == OptimizeType)
7780 if (ping_have_color == MagickFalse)
7782 if (image_matte == MagickFalse)
7784 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
7785 image_matte=MagickFalse;
7790 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
7791 image_matte=MagickTrue;
7796 if (image_matte == MagickFalse)
7798 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
7799 image_matte=MagickFalse;
7804 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
7805 image_matte=MagickTrue;
7812 if (logging != MagickFalse)
7813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7814 " Selected PNG colortype=%d",ping_color_type);
7816 if (ping_bit_depth < 8)
7818 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
7819 ping_color_type == PNG_COLOR_TYPE_RGB ||
7820 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
7824 old_bit_depth=ping_bit_depth;
7826 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7828 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
7832 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
7837 if (image->colors == 0)
7840 (void) ThrowMagickException(&image->exception,
7841 GetMagickModule(),CoderError,
7842 "image has 0 colors", "`%s'","");
7845 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
7846 ping_bit_depth <<= 1;
7849 if (logging != MagickFalse)
7851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7852 " Number of colors: %.20g",(double) image_colors);
7854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7855 " Tentative PNG bit depth: %d",ping_bit_depth);
7858 if (ping_bit_depth < (int) mng_info->write_png_depth)
7859 ping_bit_depth = mng_info->write_png_depth;
7862 image_depth=ping_bit_depth;
7864 if (logging != MagickFalse)
7866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7867 " Tentative PNG color type: %.20g",(double) ping_color_type);
7869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7870 " image_info->type: %.20g",(double) image_info->type);
7872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7873 " image_depth: %.20g",(double) image_depth);
7875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7877 " image->depth: %.20g",(double) image->depth);
7879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7880 " ping_bit_depth: %.20g",(double) ping_bit_depth);
7883 if (matte != MagickFalse)
7885 if (mng_info->IsPalette)
7888 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
7890 if (ping_have_color != MagickFalse)
7891 ping_color_type=PNG_COLOR_TYPE_RGBA;
7894 * Determine if there is any transparent color.
7896 if (number_transparent + number_semitransparent == 0)
7899 No transparent pixels are present. Change 4 or 6 to 0 or 2.
7902 image_matte=MagickFalse;
7903 ping_color_type&=0x03;
7913 if (ping_bit_depth == 8)
7916 if (ping_bit_depth == 4)
7919 if (ping_bit_depth == 2)
7922 if (ping_bit_depth == 1)
7925 ping_trans_color.red=(png_uint_16)
7926 (ScaleQuantumToShort(image->colormap[0].red) & mask);
7928 ping_trans_color.green=(png_uint_16)
7929 (ScaleQuantumToShort(image->colormap[0].green) & mask);
7931 ping_trans_color.blue=(png_uint_16)
7932 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
7934 ping_trans_color.gray=(png_uint_16)
7935 (ScaleQuantumToShort(PixelIntensityToQuantum(
7936 image->colormap)) & mask);
7938 ping_trans_color.index=(png_byte) 0;
7940 ping_have_tRNS=MagickTrue;
7943 if (ping_have_tRNS != MagickFalse)
7946 Determine if there is one and only one transparent color
7947 and if so if it is fully transparent.
7949 if (logging != MagickFalse)
7950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7951 " Is there a single fully transparent color?");
7953 if (number_transparent > 1 || number_semitransparent > 0)
7955 ping_have_tRNS = MagickFalse;
7956 if (logging != MagickFalse)
7957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7962 if (logging != MagickFalse)
7963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7964 " ... Yes: (%d,%d,%d), (gray: %d)",
7965 (int) ping_trans_color.red,
7966 (int) ping_trans_color.green,
7967 (int) ping_trans_color.blue,
7968 (int) ping_trans_color.gray);
7972 if (ping_have_tRNS != MagickFalse)
7974 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
7976 if (image_depth == 8)
7978 ping_trans_color.red&=0xff;
7979 ping_trans_color.green&=0xff;
7980 ping_trans_color.blue&=0xff;
7981 ping_trans_color.gray&=0xff;
7987 if (image_depth == 8)
7989 ping_trans_color.red&=0xff;
7990 ping_trans_color.green&=0xff;
7991 ping_trans_color.blue&=0xff;
7992 ping_trans_color.gray&=0xff;
7999 if (ping_have_tRNS != MagickFalse)
8000 image_matte=MagickFalse;
8002 if ((mng_info->IsPalette) &&
8003 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8004 ping_have_color == MagickFalse &&
8005 (image_matte == MagickFalse || image_depth >= 8))
8009 if (image_matte != MagickFalse)
8010 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8014 ping_color_type=PNG_COLOR_TYPE_GRAY;
8016 if (save_image_depth == 16 && image_depth == 8)
8018 if (logging != MagickFalse)
8020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8021 " Scaling ping_trans_color (0)");
8023 ping_trans_color.gray*=0x0101;
8027 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8028 image_depth=MAGICKCORE_QUANTUM_DEPTH;
8030 if (image_colors == 0 || image_colors-1 > MaxColormapSize)
8031 image_colors=(int) (one << image_depth);
8033 if (image_depth > 8)
8039 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8041 if(!mng_info->write_png_depth)
8045 while ((int) (one << ping_bit_depth)
8046 < (ssize_t) image_colors)
8047 ping_bit_depth <<= 1;
8051 #if 1 /* TO DO: Enable this when low bit-depth grayscale is working */
8052 else if (ping_color_type ==
8053 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8054 mng_info->IsPalette)
8057 /* Check if grayscale is reducible */
8059 depth_4_ok=MagickTrue,
8060 depth_2_ok=MagickTrue,
8061 depth_1_ok=MagickTrue;
8063 for (i=0; i < (ssize_t) image_colors; i++)
8068 intensity=ScaleQuantumToChar(image->colormap[i].red);
8070 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8071 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8073 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8074 depth_2_ok=depth_1_ok=MagickFalse;
8076 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8077 depth_1_ok=MagickFalse;
8080 if (depth_1_ok && mng_info->write_png_depth <= 1)
8083 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8086 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8092 image_depth=ping_bit_depth;
8097 if (mng_info->IsPalette)
8099 number_colors=image_colors;
8101 if (image_depth <= 8)
8106 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8108 if (mng_info->have_write_global_plte && matte == MagickFalse)
8110 png_set_PLTE(ping,ping_info,NULL,0);
8112 if (logging != MagickFalse)
8113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8114 " Setting up empty PLTE chunk");
8119 for (i=0; i < (ssize_t) number_colors; i++)
8121 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8122 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8123 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8126 if (logging != MagickFalse)
8127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8128 " Setting up PLTE chunk with %d colors",
8131 ping_have_PLTE=MagickTrue;
8134 /* color_type is PNG_COLOR_TYPE_PALETTE */
8135 if (mng_info->write_png_depth == 0)
8143 while ((one << ping_bit_depth) < number_colors)
8144 ping_bit_depth <<= 1;
8149 if (matte != MagickFalse)
8152 * Set up trans_colors array.
8154 assert(number_colors <= 256);
8156 ping_num_trans=(unsigned short) (number_transparent +
8157 number_semitransparent);
8159 if (ping_num_trans == 0)
8160 ping_have_tRNS=MagickFalse;
8164 if (logging != MagickFalse)
8166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8167 " Scaling ping_trans_color (1)");
8169 ping_have_tRNS=MagickTrue;
8171 for (i=0; i < ping_num_trans; i++)
8173 ping_trans_alpha[i]= (png_byte) (255-
8174 ScaleQuantumToChar(image->colormap[i].opacity));
8184 if (image_depth < 8)
8187 if ((save_image_depth == 16) && (image_depth == 8))
8189 if (logging != MagickFalse)
8191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " Scaling ping_trans_color from (%d,%d,%d)",
8193 (int) ping_trans_color.red,
8194 (int) ping_trans_color.green,
8195 (int) ping_trans_color.blue);
8198 ping_trans_color.red*=0x0101;
8199 ping_trans_color.green*=0x0101;
8200 ping_trans_color.blue*=0x0101;
8201 ping_trans_color.gray*=0x0101;
8203 if (logging != MagickFalse)
8205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8207 (int) ping_trans_color.red,
8208 (int) ping_trans_color.green,
8209 (int) ping_trans_color.blue);
8214 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
8215 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
8218 Adjust background and transparency samples in sub-8-bit grayscale files.
8220 if (ping_bit_depth < 8 && ping_color_type ==
8221 PNG_COLOR_TYPE_GRAY)
8229 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8231 if (ping_exclude_bKGD == MagickFalse)
8234 ping_background.gray=(png_uint_16)
8235 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8237 if (logging != MagickFalse)
8238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239 " Setting up bKGD chunk (2)");
8241 ping_have_bKGD = MagickTrue;
8244 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8245 ping_trans_color.gray));
8248 if (ping_exclude_bKGD == MagickFalse)
8250 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8253 Identify which colormap entry is the background color.
8256 number_colors=image_colors;
8258 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8259 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8262 ping_background.index=(png_byte) i;
8264 if (logging != MagickFalse)
8266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8267 " Setting up bKGD chunk with index=%d",(int) i);
8270 if (i < (ssize_t) number_colors)
8272 ping_have_bKGD = MagickTrue;
8274 if (logging != MagickFalse)
8276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8277 " background =(%d,%d,%d)",
8278 (int) ping_background.red,
8279 (int) ping_background.green,
8280 (int) ping_background.blue);
8284 else /* Can't happen */
8286 if (logging != MagickFalse)
8287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8288 " No room in PLTE to add bKGD color");
8289 ping_have_bKGD = MagickFalse;
8294 if (logging != MagickFalse)
8295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8296 " PNG color type: %d",ping_color_type);
8298 Initialize compression level and filtering.
8300 if (logging != MagickFalse)
8302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8303 " Setting up deflate compression");
8305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8306 " Compression buffer size: 32768");
8309 png_set_compression_buffer_size(ping,32768L);
8311 if (logging != MagickFalse)
8312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8313 " Compression mem level: 9");
8315 png_set_compression_mem_level(ping, 9);
8317 quality=image->quality == UndefinedCompressionQuality ? 75UL :
8325 level=(int) MagickMin((ssize_t) quality/10,9);
8327 if (logging != MagickFalse)
8328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8329 " Compression level: %d",level);
8331 png_set_compression_level(ping,level);
8336 if (logging != MagickFalse)
8337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8338 " Compression strategy: Z_HUFFMAN_ONLY");
8340 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8343 if (logging != MagickFalse)
8344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8345 " Setting up filtering");
8347 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8348 /* This became available in libpng-1.0.9. Output must be a MNG. */
8349 if (mng_info->write_mng && ((quality % 10) == 7))
8351 if (logging != MagickFalse)
8352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8353 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8355 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8359 if (logging != MagickFalse)
8360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8368 if ((quality % 10) > 5)
8369 base_filter=PNG_ALL_FILTERS;
8372 if ((quality % 10) != 5)
8373 base_filter=(int) quality % 10;
8376 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8377 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8379 base_filter=PNG_NO_FILTERS;
8382 base_filter=PNG_ALL_FILTERS;
8384 if (logging != MagickFalse)
8386 if (base_filter == PNG_ALL_FILTERS)
8387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8388 " Base filter method: ADAPTIVE");
8390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8391 " Base filter method: NONE");
8394 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8397 if (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse)
8399 ResetImageProfileIterator(image);
8400 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8402 profile=GetImageProfile(image,name);
8404 if (profile != (StringInfo *) NULL)
8406 #ifdef PNG_WRITE_iCCP_SUPPORTED
8407 if ((LocaleCompare(name,"ICC") == 0) ||
8408 (LocaleCompare(name,"ICM") == 0))
8411 if (ping_exclude_iCCP == MagickFalse)
8413 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8414 #if (PNG_LIBPNG_VER < 10500)
8415 (png_charp) GetStringInfoDatum(profile),
8417 (png_const_bytep) GetStringInfoDatum(profile),
8419 (png_uint_32) GetStringInfoLength(profile));
8425 if (ping_exclude_zCCP == MagickFalse)
8427 Magick_png_write_raw_profile(image_info,ping,ping_info,
8428 (unsigned char *) name,(unsigned char *) name,
8429 GetStringInfoDatum(profile),
8430 (png_uint_32) GetStringInfoLength(profile));
8434 if (logging != MagickFalse)
8435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8436 " Setting up text chunk with %s profile",name);
8438 name=GetNextImageProfile(image);
8442 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8443 if ((mng_info->have_write_global_srgb == 0) &&
8444 ((image->rendering_intent != UndefinedIntent) ||
8445 (image->colorspace == sRGBColorspace)))
8447 if (ping_exclude_sRGB == MagickFalse)
8450 Note image rendering intent.
8452 if (logging != MagickFalse)
8453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8454 " Setting up sRGB chunk");
8456 (void) png_set_sRGB(ping,ping_info,(
8457 Magick_RenderingIntent_to_PNG_RenderingIntent(
8458 image->rendering_intent)));
8460 if (ping_exclude_gAMA == MagickFalse)
8461 png_set_gAMA(ping,ping_info,0.45455);
8465 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8468 if (ping_exclude_gAMA == MagickFalse &&
8469 (ping_exclude_sRGB == MagickFalse ||
8470 (image->gamma < .45 || image->gamma > .46)))
8472 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8476 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8478 if (logging != MagickFalse)
8479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8480 " Setting up gAMA chunk");
8482 png_set_gAMA(ping,ping_info,image->gamma);
8486 if (ping_exclude_cHRM == MagickFalse)
8488 if ((mng_info->have_write_global_chrm == 0) &&
8489 (image->chromaticity.red_primary.x != 0.0))
8492 Note image chromaticity.
8493 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8501 wp=image->chromaticity.white_point;
8502 rp=image->chromaticity.red_primary;
8503 gp=image->chromaticity.green_primary;
8504 bp=image->chromaticity.blue_primary;
8506 if (logging != MagickFalse)
8507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8508 " Setting up cHRM chunk");
8510 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
8516 ping_interlace_method=image_info->interlace != NoInterlace;
8518 if (mng_info->write_mng)
8519 png_set_sig_bytes(ping,8);
8521 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
8523 if (mng_info->write_png_colortype != 0)
8525 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
8526 if (ping_have_color != MagickFalse)
8528 ping_color_type = PNG_COLOR_TYPE_RGB;
8530 if (ping_bit_depth < 8)
8534 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
8535 if (ping_have_color != MagickFalse)
8536 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
8539 if (ping_need_colortype_warning != MagickFalse ||
8540 ((mng_info->write_png_depth &&
8541 (int) mng_info->write_png_depth != ping_bit_depth) ||
8542 (mng_info->write_png_colortype &&
8543 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
8544 mng_info->write_png_colortype != 7 &&
8545 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
8547 if (logging != MagickFalse)
8549 if (ping_need_colortype_warning != MagickFalse)
8551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8552 " Image has transparency but tRNS chunk was excluded");
8555 if (mng_info->write_png_depth)
8557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8558 " Defined PNG:bit-depth=%u, Computed depth=%u",
8559 mng_info->write_png_depth,
8563 if (mng_info->write_png_colortype)
8565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8566 " Defined PNG:color-type=%u, Computed color type=%u",
8567 mng_info->write_png_colortype-1,
8573 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
8576 if (image_matte != MagickFalse && image->matte == MagickFalse)
8578 /* Add an opaque matte channel */
8579 image->matte = MagickTrue;
8580 (void) SetImageOpacity(image,0);
8582 if (logging != MagickFalse)
8583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8584 " Added an opaque matte channel");
8587 if (number_transparent != 0 || number_semitransparent != 0)
8589 if (ping_color_type < 4)
8591 ping_have_tRNS=MagickTrue;
8592 if (logging != MagickFalse)
8593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8594 " Setting ping_have_tRNS=MagickTrue.");
8598 if (logging != MagickFalse)
8599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8600 " Writing PNG header chunks");
8602 png_set_IHDR(ping,ping_info,ping_width,ping_height,
8603 ping_bit_depth,ping_color_type,
8604 ping_interlace_method,ping_compression_method,
8605 ping_filter_method);
8607 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
8609 png_set_PLTE(ping,ping_info,palette,number_colors);
8611 if (logging != MagickFalse)
8613 for (i=0; i< (ssize_t) number_colors; i++)
8615 if (i < ping_num_trans)
8616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8617 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
8619 (int) palette[i].red,
8620 (int) palette[i].green,
8621 (int) palette[i].blue,
8623 (int) ping_trans_alpha[i]);
8625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8626 " PLTE[%d] = (%d,%d,%d)",
8628 (int) palette[i].red,
8629 (int) palette[i].green,
8630 (int) palette[i].blue);
8635 if (ping_exclude_bKGD == MagickFalse)
8637 if (ping_have_bKGD != MagickFalse)
8638 png_set_bKGD(ping,ping_info,&ping_background);
8641 if (ping_exclude_pHYs == MagickFalse)
8643 if (ping_have_pHYs != MagickFalse)
8645 png_set_pHYs(ping,ping_info,
8646 ping_pHYs_x_resolution,
8647 ping_pHYs_y_resolution,
8648 ping_pHYs_unit_type);
8652 #if defined(PNG_oFFs_SUPPORTED)
8653 if (ping_exclude_oFFs == MagickFalse)
8655 if (image->page.x || image->page.y)
8657 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
8658 (png_int_32) image->page.y, 0);
8660 if (logging != MagickFalse)
8661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8662 " Setting up oFFs chunk with x=%d, y=%d, units=0",
8663 (int) image->page.x, (int) image->page.y);
8668 png_write_info_before_PLTE(ping, ping_info);
8670 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
8672 if (logging != MagickFalse)
8674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8675 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
8678 if (ping_color_type == 3)
8679 (void) png_set_tRNS(ping, ping_info,
8686 (void) png_set_tRNS(ping, ping_info,
8691 if (logging != MagickFalse)
8693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8694 " tRNS color =(%d,%d,%d)",
8695 (int) ping_trans_color.red,
8696 (int) ping_trans_color.green,
8697 (int) ping_trans_color.blue);
8702 /* write any png-chunk-b profiles */
8703 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
8704 png_write_info(ping,ping_info);
8706 /* write any PNG-chunk-m profiles */
8707 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
8709 if (ping_exclude_vpAg == MagickFalse)
8711 if ((image->page.width != 0 && image->page.width != image->columns) ||
8712 (image->page.height != 0 && image->page.height != image->rows))
8717 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
8718 PNGType(chunk,mng_vpAg);
8719 LogPNGChunk(logging,mng_vpAg,9L);
8720 PNGLong(chunk+4,(png_uint_32) image->page.width);
8721 PNGLong(chunk+8,(png_uint_32) image->page.height);
8722 chunk[12]=0; /* unit = pixels */
8723 (void) WriteBlob(image,13,chunk);
8724 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8728 #if (PNG_LIBPNG_VER == 10206)
8729 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
8730 #define PNG_HAVE_IDAT 0x04
8731 ping->mode |= PNG_HAVE_IDAT;
8732 #undef PNG_HAVE_IDAT
8735 png_set_packing(ping);
8739 rowbytes=image->columns;
8740 if (image_depth > 8)
8742 switch (ping_color_type)
8744 case PNG_COLOR_TYPE_RGB:
8748 case PNG_COLOR_TYPE_GRAY_ALPHA:
8752 case PNG_COLOR_TYPE_RGBA:
8760 if (logging != MagickFalse)
8762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8763 " Writing PNG image data");
8765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8766 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
8768 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
8769 sizeof(*ping_pixels));
8771 if (ping_pixels == (unsigned char *) NULL)
8772 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8775 Initialize image scanlines.
8777 if (setjmp(png_jmpbuf(ping)))
8783 if (image_info->verbose)
8784 (void) printf("PNG write has failed.\n");
8786 png_destroy_write_struct(&ping,&ping_info);
8787 if (quantum_info != (QuantumInfo *) NULL)
8788 quantum_info=DestroyQuantumInfo(quantum_info);
8789 if (ping_pixels != (unsigned char *) NULL)
8790 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
8791 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8792 UnlockSemaphoreInfo(ping_semaphore);
8794 if (mng_info->need_blob != MagickFalse)
8795 (void) CloseBlob(image);
8796 image_info=DestroyImageInfo(image_info);
8797 image=DestroyImage(image);
8798 return(MagickFalse);
8800 quantum_info=AcquireQuantumInfo(image_info,image);
8801 if (quantum_info == (QuantumInfo *) NULL)
8802 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8803 quantum_info->format=UndefinedQuantumFormat;
8804 quantum_info->depth=image_depth;
8805 num_passes=png_set_interlace_handling(ping);
8807 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
8808 !mng_info->write_png32) &&
8809 (mng_info->IsPalette ||
8810 (image_info->type == BilevelType)) &&
8811 image_matte == MagickFalse &&
8812 ping_have_non_bw == MagickFalse)
8814 /* Palette, Bilevel, or Opaque Monochrome */
8815 register const PixelPacket
8818 quantum_info->depth=8;
8819 for (pass=0; pass < num_passes; pass++)
8822 Convert PseudoClass image to a PNG monochrome image.
8824 for (y=0; y < (ssize_t) image->rows; y++)
8826 if (logging != MagickFalse && y == 0)
8827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8828 " Writing row of pixels (0)");
8830 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
8832 if (p == (const PixelPacket *) NULL)
8835 if (mng_info->IsPalette)
8837 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8838 quantum_info,GrayQuantum,ping_pixels,&image->exception);
8839 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
8840 mng_info->write_png_depth &&
8841 mng_info->write_png_depth != old_bit_depth)
8843 /* Undo pixel scaling */
8844 for (i=0; i < (ssize_t) image->columns; i++)
8845 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
8846 >> (8-old_bit_depth));
8852 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8853 quantum_info,RedQuantum,ping_pixels,&image->exception);
8856 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
8857 for (i=0; i < (ssize_t) image->columns; i++)
8858 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
8861 if (logging != MagickFalse && y == 0)
8862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8863 " Writing row of pixels (1)");
8865 png_write_row(ping,ping_pixels);
8867 if (image->previous == (Image *) NULL)
8869 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
8870 if (status == MagickFalse)
8876 else /* Not Palette, Bilevel, or Opaque Monochrome */
8878 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
8879 !mng_info->write_png32) &&
8880 (image_matte != MagickFalse ||
8881 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
8882 (mng_info->IsPalette) && ping_have_color == MagickFalse)
8884 register const PixelPacket
8887 for (pass=0; pass < num_passes; pass++)
8890 for (y=0; y < (ssize_t) image->rows; y++)
8892 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
8894 if (p == (const PixelPacket *) NULL)
8897 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8899 if (mng_info->IsPalette)
8900 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8901 quantum_info,GrayQuantum,ping_pixels,&image->exception);
8904 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8905 quantum_info,RedQuantum,ping_pixels,&image->exception);
8907 if (logging != MagickFalse && y == 0)
8908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8909 " Writing GRAY PNG pixels (2)");
8912 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
8914 if (logging != MagickFalse && y == 0)
8915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8916 " Writing GRAY_ALPHA PNG pixels (2)");
8918 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8919 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
8922 if (logging != MagickFalse && y == 0)
8923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8924 " Writing row of pixels (2)");
8926 png_write_row(ping,ping_pixels);
8929 if (image->previous == (Image *) NULL)
8931 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
8932 if (status == MagickFalse)
8940 register const PixelPacket
8943 for (pass=0; pass < num_passes; pass++)
8945 if ((image_depth > 8) || (mng_info->write_png24 ||
8946 mng_info->write_png32 ||
8947 (!mng_info->write_png8 && !mng_info->IsPalette)))
8949 for (y=0; y < (ssize_t) image->rows; y++)
8951 p=GetVirtualPixels(image,0,y,image->columns,1,
8954 if (p == (const PixelPacket *) NULL)
8957 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8959 if (image->storage_class == DirectClass)
8960 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8961 quantum_info,RedQuantum,ping_pixels,&image->exception);
8964 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8965 quantum_info,GrayQuantum,ping_pixels,&image->exception);
8968 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
8970 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8971 quantum_info,GrayAlphaQuantum,ping_pixels,
8974 if (logging != MagickFalse && y == 0)
8975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8976 " Writing GRAY_ALPHA PNG pixels (3)");
8979 else if (image_matte != MagickFalse)
8980 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8981 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
8984 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8985 quantum_info,RGBQuantum,ping_pixels,&image->exception);
8987 if (logging != MagickFalse && y == 0)
8988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8989 " Writing row of pixels (3)");
8991 png_write_row(ping,ping_pixels);
8996 /* not ((image_depth > 8) || (mng_info->write_png24 ||
8997 mng_info->write_png32 ||
8998 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9000 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9001 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9003 if (logging != MagickFalse)
9004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9005 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9007 quantum_info->depth=8;
9011 for (y=0; y < (ssize_t) image->rows; y++)
9013 if (logging != MagickFalse && y == 0)
9014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9015 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9017 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9019 if (p == (const PixelPacket *) NULL)
9022 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9023 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9024 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9026 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9028 if (logging != MagickFalse && y == 0)
9029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9030 " Writing GRAY_ALPHA PNG pixels (4)");
9032 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9033 quantum_info,GrayAlphaQuantum,ping_pixels,
9041 * This is failing to account for 1, 2, 4-bit depths
9042 * The call to png_set_packing() above is supposed to
9043 * take care of those.
9046 /* GrayQuantum does not work here */
9047 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9048 quantum_info,IndexQuantum,ping_pixels,&image->exception);
9050 if (logging != MagickFalse && y <= 2)
9052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9053 " Writing row of pixels (4)");
9055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056 " ping_pixels[0]=%d,ping_pixels[1]=%d",
9057 (int)ping_pixels[0],(int)ping_pixels[1]);
9060 png_write_row(ping,ping_pixels);
9064 if (image->previous == (Image *) NULL)
9066 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9067 if (status == MagickFalse)
9074 if (quantum_info != (QuantumInfo *) NULL)
9075 quantum_info=DestroyQuantumInfo(quantum_info);
9077 if (logging != MagickFalse)
9079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9080 " Wrote PNG image data");
9082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9083 " Width: %.20g",(double) ping_width);
9085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9086 " Height: %.20g",(double) ping_height);
9088 if (mng_info->write_png_depth)
9090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9091 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9095 " PNG bit-depth written: %d",ping_bit_depth);
9097 if (mng_info->write_png_colortype)
9099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9100 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9104 " PNG color-type written: %d",ping_color_type);
9106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9107 " PNG Interlace method: %d",ping_interlace_method);
9110 Generate text chunks.
9112 if (ping_exclude_tEXt == MagickFalse && ping_exclude_zTXt == MagickFalse)
9114 ResetImagePropertyIterator(image);
9115 property=GetNextImageProperty(image);
9116 while (property != (const char *) NULL)
9121 value=GetImageProperty(image,property);
9122 if (value != (const char *) NULL)
9124 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9125 text[0].key=(char *) property;
9126 text[0].text=(char *) value;
9127 text[0].text_length=strlen(value);
9129 if (ping_exclude_tEXt != MagickFalse)
9130 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9132 else if (ping_exclude_zTXt != MagickFalse)
9133 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9137 text[0].compression=image_info->compression == NoCompression ||
9138 (image_info->compression == UndefinedCompression &&
9139 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9140 PNG_TEXT_COMPRESSION_zTXt ;
9143 if (logging != MagickFalse)
9145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9146 " Setting up text chunk");
9148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9149 " keyword: %s",text[0].key);
9152 png_set_text(ping,ping_info,text,1);
9153 png_free(ping,text);
9155 property=GetNextImageProperty(image);
9159 /* write any PNG-chunk-e profiles */
9160 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9162 if (logging != MagickFalse)
9163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9164 " Writing PNG end info");
9166 png_write_end(ping,ping_info);
9168 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9170 if (mng_info->page.x || mng_info->page.y ||
9171 (ping_width != mng_info->page.width) ||
9172 (ping_height != mng_info->page.height))
9178 Write FRAM 4 with clipping boundaries followed by FRAM 1.
9180 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
9181 PNGType(chunk,mng_FRAM);
9182 LogPNGChunk(logging,mng_FRAM,27L);
9184 chunk[5]=0; /* frame name separator (no name) */
9185 chunk[6]=1; /* flag for changing delay, for next frame only */
9186 chunk[7]=0; /* flag for changing frame timeout */
9187 chunk[8]=1; /* flag for changing frame clipping for next frame */
9188 chunk[9]=0; /* flag for changing frame sync_id */
9189 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9190 chunk[14]=0; /* clipping boundaries delta type */
9191 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9193 (png_uint_32) (mng_info->page.x + ping_width));
9194 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9196 (png_uint_32) (mng_info->page.y + ping_height));
9197 (void) WriteBlob(image,31,chunk);
9198 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9199 mng_info->old_framing_mode=4;
9200 mng_info->framing_mode=1;
9204 mng_info->framing_mode=3;
9206 if (mng_info->write_mng && !mng_info->need_fram &&
9207 ((int) image->dispose == 3))
9208 (void) ThrowMagickException(&image->exception,GetMagickModule(),
9209 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9210 "`%s'",image->filename);
9216 png_destroy_write_struct(&ping,&ping_info);
9218 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9220 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9221 UnlockSemaphoreInfo(ping_semaphore);
9224 if (mng_info->need_blob != MagickFalse)
9225 (void) CloseBlob(image);
9227 image_info=DestroyImageInfo(image_info);
9228 image=DestroyImage(image);
9230 /* Store bit depth actually written */
9231 s[0]=(char) ping_bit_depth;
9234 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9236 if (logging != MagickFalse)
9237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9238 " exit WriteOnePNGImage()");
9241 /* End write one PNG image */
9245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9249 % W r i t e P N G I m a g e %
9253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9255 % WritePNGImage() writes a Portable Network Graphics (PNG) or
9256 % Multiple-image Network Graphics (MNG) image file.
9258 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
9260 % The format of the WritePNGImage method is:
9262 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9264 % A description of each parameter follows:
9266 % o image_info: the image info.
9268 % o image: The image.
9270 % Returns MagickTrue on success, MagickFalse on failure.
9272 % Communicating with the PNG encoder:
9274 % While the datastream written is always in PNG format and normally would
9275 % be given the "png" file extension, this method also writes the following
9276 % pseudo-formats which are subsets of PNG:
9278 % o PNG8: An 8-bit indexed PNG datastream is written. If transparency
9279 % is present, the tRNS chunk must only have values 0 and 255
9280 % (i.e., transparency is binary: fully opaque or fully
9281 % transparent). The pixels contain 8-bit indices even if
9282 % they could be represented with 1, 2, or 4 bits. Note: grayscale
9283 % images will be written as indexed PNG files even though the
9284 % PNG grayscale type might be slightly more efficient.
9286 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
9287 % chunk can be present to convey binary transparency by naming
9288 % one of the colors as transparent.
9290 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
9291 % transparency is permitted, i.e., the alpha sample for
9292 % each pixel can have any value from 0 to 255. The alpha
9293 % channel is present even if the image is fully opaque.
9295 % o -define: For more precise control of the PNG output, you can use the
9296 % Image options "png:bit-depth" and "png:color-type". These
9297 % can be set from the commandline with "-define" and also
9298 % from the application programming interfaces. The options
9299 % are case-independent and are converted to lowercase before
9300 % being passed to this encoder.
9302 % png:color-type can be 0, 2, 3, 4, or 6.
9304 % When png:color-type is 0 (Grayscale), png:bit-depth can
9305 % be 1, 2, 4, 8, or 16.
9307 % When png:color-type is 2 (RGB), png:bit-depth can
9310 % When png:color-type is 3 (Indexed), png:bit-depth can
9311 % be 1, 2, 4, or 8. This refers to the number of bits
9312 % used to store the index. The color samples always have
9313 % bit-depth 8 in indexed PNG files.
9315 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9316 % png:bit-depth can be 8 or 16.
9318 % If the image cannot be written without loss in the requested PNG8, PNG24,
9319 % or PNG32 format or with the requested bit-depth and color-type without loss,
9320 % a PNG file will not be written, and the encoder will return MagickFalse.
9321 % Since image encoders should not be responsible for the "heavy lifting",
9322 % the user should make sure that ImageMagick has already reduced the
9323 % image depth and number of colors and limit transparency to binary
9324 % transparency prior to attempting to write the image in a format that
9325 % is subject to depth, color, or transparency limitations.
9327 % TODO: Enforce the previous paragraph.
9329 % Note that another definition, "png:bit-depth-written" exists, but it
9330 % is not intended for external use. It is only used internally by the
9331 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9333 % It is possible to request that the PNG encoder write previously-formatted
9334 % ancillary chunks in the output PNG file, using the "-profile" commandline
9335 % option as shown below or by setting the profile via a programming
9338 % -profile PNG-chunk-x:<file>
9340 % where x is a location flag and <file> is a file containing the chunk
9341 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9342 % This encoder will compute the chunk length and CRC, so those must not
9343 % be included in the file.
9345 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9346 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
9347 % of the same type, then add a short unique string after the "x" to prevent
9348 % subsequent profiles from overwriting the preceding ones, e.g.,
9350 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9352 % As of version 6.6.6 the following optimizations are always done:
9354 % o 32-bit depth is reduced to 16.
9355 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
9356 % high byte and low byte are identical.
9357 % o Palette is sorted to remove unused entries and to put a
9358 % transparent color first, if BUILD_PNG_PALETTE is defined.
9359 % o Opaque matte channel is removed when writing an indexed PNG.
9360 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
9361 % this can be done without loss and a larger bit depth N was not
9362 % requested via the "-define PNG:bit-depth=N" option.
9363 % o If matte channel is present but only one transparent color is
9364 % present, RGB+tRNS is written instead of RGBA
9365 % o Opaque matte channel is removed (or added, if color-type 4 or 6
9366 % was requested when converting an opaque image).
9368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9370 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9392 assert(image_info != (const ImageInfo *) NULL);
9393 assert(image_info->signature == MagickSignature);
9394 assert(image != (Image *) NULL);
9395 assert(image->signature == MagickSignature);
9396 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9397 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
9399 Allocate a MngInfo structure.
9401 have_mng_structure=MagickFalse;
9402 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9404 if (mng_info == (MngInfo *) NULL)
9405 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9408 Initialize members of the MngInfo structure.
9410 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9411 mng_info->image=image;
9412 mng_info->equal_backgrounds=MagickTrue;
9413 have_mng_structure=MagickTrue;
9415 /* See if user has requested a specific PNG subformat */
9417 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9418 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9419 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9421 if (mng_info->write_png8)
9423 mng_info->write_png_colortype = /* 3 */ 4;
9424 mng_info->write_png_depth = 8;
9428 if (mng_info->write_png24)
9430 mng_info->write_png_colortype = /* 2 */ 3;
9431 mng_info->write_png_depth = 8;
9434 if (image->matte == MagickTrue)
9435 (void) SetImageType(image,TrueColorMatteType);
9438 (void) SetImageType(image,TrueColorType);
9440 (void) SyncImage(image);
9443 if (mng_info->write_png32)
9445 mng_info->write_png_colortype = /* 6 */ 7;
9446 mng_info->write_png_depth = 8;
9449 if (image->matte == MagickTrue)
9450 (void) SetImageType(image,TrueColorMatteType);
9453 (void) SetImageType(image,TrueColorType);
9455 (void) SyncImage(image);
9458 value=GetImageOption(image_info,"png:bit-depth");
9460 if (value != (char *) NULL)
9462 if (LocaleCompare(value,"1") == 0)
9463 mng_info->write_png_depth = 1;
9465 else if (LocaleCompare(value,"2") == 0)
9466 mng_info->write_png_depth = 2;
9468 else if (LocaleCompare(value,"4") == 0)
9469 mng_info->write_png_depth = 4;
9471 else if (LocaleCompare(value,"8") == 0)
9472 mng_info->write_png_depth = 8;
9474 else if (LocaleCompare(value,"16") == 0)
9475 mng_info->write_png_depth = 16;
9478 (void) ThrowMagickException(&image->exception,
9479 GetMagickModule(),CoderWarning,
9480 "ignoring invalid defined png:bit-depth",
9483 if (logging != MagickFalse)
9484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9485 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
9488 value=GetImageOption(image_info,"png:color-type");
9490 if (value != (char *) NULL)
9492 /* We must store colortype+1 because 0 is a valid colortype */
9493 if (LocaleCompare(value,"0") == 0)
9494 mng_info->write_png_colortype = 1;
9496 else if (LocaleCompare(value,"2") == 0)
9497 mng_info->write_png_colortype = 3;
9499 else if (LocaleCompare(value,"3") == 0)
9500 mng_info->write_png_colortype = 4;
9502 else if (LocaleCompare(value,"4") == 0)
9503 mng_info->write_png_colortype = 5;
9505 else if (LocaleCompare(value,"6") == 0)
9506 mng_info->write_png_colortype = 7;
9509 (void) ThrowMagickException(&image->exception,
9510 GetMagickModule(),CoderWarning,
9511 "ignoring invalid defined png:color-type",
9514 if (logging != MagickFalse)
9515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9516 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
9519 /* Check for chunks to be excluded:
9521 * The default is to not exclude any known chunks except for any
9522 * listed in the "unused_chunks" array, above.
9524 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
9525 * define (in the image properties or in the image artifacts)
9526 * or via a mng_info member. For convenience, in addition
9527 * to or instead of a comma-separated list of chunks, the
9528 * "exclude-chunk" string can be simply "all" or "none".
9530 * The exclude-chunk define takes priority over the mng_info.
9532 * A "PNG:include-chunk" define takes priority over both the
9533 * mng_info and the "PNG:exclude-chunk" define. Like the
9534 * "exclude-chunk" string, it can define "all" or "none" as
9535 * well as a comma-separated list. Chunks that are unknown to
9536 * ImageMagick are always excluded, regardless of their "copy-safe"
9537 * status according to the PNG specification, and even if they
9538 * appear in the "include-chunk" list.
9540 * Finally, all chunks listed in the "unused_chunks" array are
9541 * automatically excluded, regardless of the other instructions
9544 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
9545 * will not be written and the gAMA chunk will only be written if it
9546 * is not between .45 and .46, or approximately (1.0/2.2).
9548 * If you exclude tRNS and the image has transparency, the colortype
9549 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
9551 * The -strip option causes StripImage() to set the png:include-chunk
9552 * artifact to "none,gama".
9555 mng_info->ping_exclude_bKGD=MagickFalse;
9556 mng_info->ping_exclude_cHRM=MagickFalse;
9557 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
9558 mng_info->ping_exclude_gAMA=MagickFalse;
9559 mng_info->ping_exclude_cHRM=MagickFalse;
9560 mng_info->ping_exclude_iCCP=MagickFalse;
9561 /* mng_info->ping_exclude_iTXt=MagickFalse; */
9562 mng_info->ping_exclude_oFFs=MagickFalse;
9563 mng_info->ping_exclude_pHYs=MagickFalse;
9564 mng_info->ping_exclude_sRGB=MagickFalse;
9565 mng_info->ping_exclude_tEXt=MagickFalse;
9566 mng_info->ping_exclude_tRNS=MagickFalse;
9567 mng_info->ping_exclude_vpAg=MagickFalse;
9568 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
9569 mng_info->ping_exclude_zTXt=MagickFalse;
9571 excluding=MagickFalse;
9573 for (source=0; source<1; source++)
9577 value=GetImageArtifact(image,"png:exclude-chunk");
9580 value=GetImageArtifact(image,"png:exclude-chunks");
9584 value=GetImageOption(image_info,"png:exclude-chunk");
9587 value=GetImageOption(image_info,"png:exclude-chunks");
9596 excluding=MagickTrue;
9598 if (logging != MagickFalse)
9601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9602 " png:exclude-chunk=%s found in image artifacts.\n", value);
9604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9605 " png:exclude-chunk=%s found in image properties.\n", value);
9610 for (i=0; i<(int) last; i+=5)
9613 if (LocaleNCompare(value+i,"all",3) == 0)
9615 mng_info->ping_exclude_bKGD=MagickTrue;
9616 mng_info->ping_exclude_cHRM=MagickTrue;
9617 mng_info->ping_exclude_EXIF=MagickTrue;
9618 mng_info->ping_exclude_gAMA=MagickTrue;
9619 mng_info->ping_exclude_iCCP=MagickTrue;
9620 /* mng_info->ping_exclude_iTXt=MagickTrue; */
9621 mng_info->ping_exclude_oFFs=MagickTrue;
9622 mng_info->ping_exclude_pHYs=MagickTrue;
9623 mng_info->ping_exclude_sRGB=MagickTrue;
9624 mng_info->ping_exclude_tEXt=MagickTrue;
9625 mng_info->ping_exclude_tRNS=MagickTrue;
9626 mng_info->ping_exclude_vpAg=MagickTrue;
9627 mng_info->ping_exclude_zCCP=MagickTrue;
9628 mng_info->ping_exclude_zTXt=MagickTrue;
9632 if (LocaleNCompare(value+i,"none",4) == 0)
9634 mng_info->ping_exclude_bKGD=MagickFalse;
9635 mng_info->ping_exclude_cHRM=MagickFalse;
9636 mng_info->ping_exclude_EXIF=MagickFalse;
9637 mng_info->ping_exclude_gAMA=MagickFalse;
9638 mng_info->ping_exclude_iCCP=MagickFalse;
9639 /* mng_info->ping_exclude_iTXt=MagickFalse; */
9640 mng_info->ping_exclude_oFFs=MagickFalse;
9641 mng_info->ping_exclude_pHYs=MagickFalse;
9642 mng_info->ping_exclude_sRGB=MagickFalse;
9643 mng_info->ping_exclude_tEXt=MagickFalse;
9644 mng_info->ping_exclude_tRNS=MagickFalse;
9645 mng_info->ping_exclude_vpAg=MagickFalse;
9646 mng_info->ping_exclude_zCCP=MagickFalse;
9647 mng_info->ping_exclude_zTXt=MagickFalse;
9650 if (LocaleNCompare(value+i,"bkgd",4) == 0)
9651 mng_info->ping_exclude_bKGD=MagickTrue;
9653 if (LocaleNCompare(value+i,"chrm",4) == 0)
9654 mng_info->ping_exclude_cHRM=MagickTrue;
9656 if (LocaleNCompare(value+i,"exif",4) == 0)
9657 mng_info->ping_exclude_EXIF=MagickTrue;
9659 if (LocaleNCompare(value+i,"gama",4) == 0)
9660 mng_info->ping_exclude_gAMA=MagickTrue;
9662 if (LocaleNCompare(value+i,"iccp",4) == 0)
9663 mng_info->ping_exclude_iCCP=MagickTrue;
9666 if (LocaleNCompare(value+i,"itxt",4) == 0)
9667 mng_info->ping_exclude_iTXt=MagickTrue;
9670 if (LocaleNCompare(value+i,"gama",4) == 0)
9671 mng_info->ping_exclude_gAMA=MagickTrue;
9673 if (LocaleNCompare(value+i,"offs",4) == 0)
9674 mng_info->ping_exclude_oFFs=MagickTrue;
9676 if (LocaleNCompare(value+i,"phys",4) == 0)
9677 mng_info->ping_exclude_pHYs=MagickTrue;
9679 if (LocaleNCompare(value+i,"srgb",4) == 0)
9680 mng_info->ping_exclude_sRGB=MagickTrue;
9682 if (LocaleNCompare(value+i,"text",4) == 0)
9683 mng_info->ping_exclude_tEXt=MagickTrue;
9685 if (LocaleNCompare(value+i,"trns",4) == 0)
9686 mng_info->ping_exclude_tRNS=MagickTrue;
9688 if (LocaleNCompare(value+i,"vpag",4) == 0)
9689 mng_info->ping_exclude_vpAg=MagickTrue;
9691 if (LocaleNCompare(value+i,"zccp",4) == 0)
9692 mng_info->ping_exclude_zCCP=MagickTrue;
9694 if (LocaleNCompare(value+i,"ztxt",4) == 0)
9695 mng_info->ping_exclude_zTXt=MagickTrue;
9701 for (source=0; source<1; source++)
9705 value=GetImageArtifact(image,"png:include-chunk");
9708 value=GetImageArtifact(image,"png:include-chunks");
9712 value=GetImageOption(image_info,"png:include-chunk");
9715 value=GetImageOption(image_info,"png:include-chunks");
9723 excluding=MagickTrue;
9725 if (logging != MagickFalse)
9728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9729 " png:include-chunk=%s found in image artifacts.\n", value);
9731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9732 " png:include-chunk=%s found in image properties.\n", value);
9737 for (i=0; i<(int) last; i+=5)
9739 if (LocaleNCompare(value+i,"all",3) == 0)
9741 mng_info->ping_exclude_bKGD=MagickFalse;
9742 mng_info->ping_exclude_cHRM=MagickFalse;
9743 mng_info->ping_exclude_EXIF=MagickFalse;
9744 mng_info->ping_exclude_gAMA=MagickFalse;
9745 mng_info->ping_exclude_iCCP=MagickFalse;
9746 /* mng_info->ping_exclude_iTXt=MagickFalse; */
9747 mng_info->ping_exclude_oFFs=MagickFalse;
9748 mng_info->ping_exclude_pHYs=MagickFalse;
9749 mng_info->ping_exclude_sRGB=MagickFalse;
9750 mng_info->ping_exclude_tEXt=MagickFalse;
9751 mng_info->ping_exclude_tRNS=MagickFalse;
9752 mng_info->ping_exclude_vpAg=MagickFalse;
9753 mng_info->ping_exclude_zCCP=MagickFalse;
9754 mng_info->ping_exclude_zTXt=MagickFalse;
9758 if (LocaleNCompare(value+i,"none",4) == 0)
9760 mng_info->ping_exclude_bKGD=MagickTrue;
9761 mng_info->ping_exclude_cHRM=MagickTrue;
9762 mng_info->ping_exclude_EXIF=MagickTrue;
9763 mng_info->ping_exclude_gAMA=MagickTrue;
9764 mng_info->ping_exclude_iCCP=MagickTrue;
9765 /* mng_info->ping_exclude_iTXt=MagickTrue; */
9766 mng_info->ping_exclude_oFFs=MagickTrue;
9767 mng_info->ping_exclude_pHYs=MagickTrue;
9768 mng_info->ping_exclude_sRGB=MagickTrue;
9769 mng_info->ping_exclude_tEXt=MagickTrue;
9770 mng_info->ping_exclude_tRNS=MagickTrue;
9771 mng_info->ping_exclude_vpAg=MagickTrue;
9772 mng_info->ping_exclude_zCCP=MagickTrue;
9773 mng_info->ping_exclude_zTXt=MagickTrue;
9776 if (LocaleNCompare(value+i,"bkgd",4) == 0)
9777 mng_info->ping_exclude_bKGD=MagickFalse;
9779 if (LocaleNCompare(value+i,"chrm",4) == 0)
9780 mng_info->ping_exclude_cHRM=MagickFalse;
9782 if (LocaleNCompare(value+i,"exif",4) == 0)
9783 mng_info->ping_exclude_EXIF=MagickFalse;
9785 if (LocaleNCompare(value+i,"gama",4) == 0)
9786 mng_info->ping_exclude_gAMA=MagickFalse;
9788 if (LocaleNCompare(value+i,"iccp",4) == 0)
9789 mng_info->ping_exclude_iCCP=MagickFalse;
9792 if (LocaleNCompare(value+i,"itxt",4) == 0)
9793 mng_info->ping_exclude_iTXt=MagickFalse;
9796 if (LocaleNCompare(value+i,"gama",4) == 0)
9797 mng_info->ping_exclude_gAMA=MagickFalse;
9799 if (LocaleNCompare(value+i,"offs",4) == 0)
9800 mng_info->ping_exclude_oFFs=MagickFalse;
9802 if (LocaleNCompare(value+i,"phys",4) == 0)
9803 mng_info->ping_exclude_pHYs=MagickFalse;
9805 if (LocaleNCompare(value+i,"srgb",4) == 0)
9806 mng_info->ping_exclude_sRGB=MagickFalse;
9808 if (LocaleNCompare(value+i,"text",4) == 0)
9809 mng_info->ping_exclude_tEXt=MagickFalse;
9811 if (LocaleNCompare(value+i,"trns",4) == 0)
9812 mng_info->ping_exclude_tRNS=MagickFalse;
9814 if (LocaleNCompare(value+i,"vpag",4) == 0)
9815 mng_info->ping_exclude_vpAg=MagickFalse;
9817 if (LocaleNCompare(value+i,"zccp",4) == 0)
9818 mng_info->ping_exclude_zCCP=MagickFalse;
9820 if (LocaleNCompare(value+i,"ztxt",4) == 0)
9821 mng_info->ping_exclude_zTXt=MagickFalse;
9827 if (excluding != MagickFalse && logging != MagickFalse)
9829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830 " Chunks to be excluded from the output PNG:");
9831 if (mng_info->ping_exclude_bKGD != MagickFalse)
9832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9834 if (mng_info->ping_exclude_cHRM != MagickFalse)
9835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 if (mng_info->ping_exclude_EXIF != MagickFalse)
9838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9840 if (mng_info->ping_exclude_gAMA != MagickFalse)
9841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9843 if (mng_info->ping_exclude_iCCP != MagickFalse)
9844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9847 if (mng_info->ping_exclude_iTXt != MagickFalse)
9848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9851 if (mng_info->ping_exclude_oFFs != MagickFalse)
9852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9854 if (mng_info->ping_exclude_pHYs != MagickFalse)
9855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9857 if (mng_info->ping_exclude_sRGB != MagickFalse)
9858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9860 if (mng_info->ping_exclude_tEXt != MagickFalse)
9861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9863 if (mng_info->ping_exclude_tRNS != MagickFalse)
9864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9866 if (mng_info->ping_exclude_vpAg != MagickFalse)
9867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9869 if (mng_info->ping_exclude_zCCP != MagickFalse)
9870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9872 if (mng_info->ping_exclude_zTXt != MagickFalse)
9873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9877 mng_info->need_blob = MagickTrue;
9879 status=WriteOnePNGImage(mng_info,image_info,image);
9881 MngInfoFreeStruct(mng_info,&have_mng_structure);
9883 if (logging != MagickFalse)
9884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
9889 #if defined(JNG_SUPPORTED)
9891 /* Write one JNG image */
9892 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
9893 const ImageInfo *image_info,Image *image)
9914 jng_alpha_compression_method,
9915 jng_alpha_sample_depth,
9922 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
9923 " enter WriteOneJNGImage()");
9925 blob=(unsigned char *) NULL;
9926 jpeg_image=(Image *) NULL;
9927 jpeg_image_info=(ImageInfo *) NULL;
9930 transparent=image_info->type==GrayscaleMatteType ||
9931 image_info->type==TrueColorMatteType;
9933 jng_alpha_sample_depth=0;
9934 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
9935 jng_alpha_compression_method=0;
9937 if (image->matte != MagickFalse)
9939 /* if any pixels are transparent */
9940 transparent=MagickTrue;
9941 if (image_info->compression==JPEGCompression)
9942 jng_alpha_compression_method=8;
9949 /* Create JPEG blob, image, and image_info */
9950 if (logging != MagickFalse)
9951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9952 " Creating jpeg_image_info for opacity.");
9954 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
9956 if (jpeg_image_info == (ImageInfo *) NULL)
9957 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9959 if (logging != MagickFalse)
9960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9961 " Creating jpeg_image.");
9963 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
9965 if (jpeg_image == (Image *) NULL)
9966 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9968 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
9969 status=SeparateImageChannel(jpeg_image,OpacityChannel);
9970 status=NegateImage(jpeg_image,MagickFalse);
9971 jpeg_image->matte=MagickFalse;
9973 if (jng_quality >= 1000)
9974 jpeg_image_info->quality=jng_quality/1000;
9977 jpeg_image_info->quality=jng_quality;
9979 jpeg_image_info->type=GrayscaleType;
9980 (void) SetImageType(jpeg_image,GrayscaleType);
9981 (void) AcquireUniqueFilename(jpeg_image->filename);
9982 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
9983 "%s",jpeg_image->filename);
9986 /* To do: check bit depth of PNG alpha channel */
9988 /* Check if image is grayscale. */
9989 if (image_info->type != TrueColorMatteType && image_info->type !=
9990 TrueColorType && ImageIsGray(image))
9995 if (jng_alpha_compression_method==0)
10000 /* Encode opacity as a grayscale PNG blob */
10001 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10002 &image->exception);
10003 if (logging != MagickFalse)
10004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10005 " Creating PNG blob.");
10008 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10009 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10010 jpeg_image_info->interlace=NoInterlace;
10012 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10013 &image->exception);
10015 /* Retrieve sample depth used */
10016 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10017 if (value != (char *) NULL)
10018 jng_alpha_sample_depth= (unsigned int) value[0];
10022 /* Encode opacity as a grayscale JPEG blob */
10024 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10025 &image->exception);
10027 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10028 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10029 jpeg_image_info->interlace=NoInterlace;
10030 if (logging != MagickFalse)
10031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10032 " Creating blob.");
10033 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10034 &image->exception);
10035 jng_alpha_sample_depth=8;
10037 if (logging != MagickFalse)
10038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10039 " Successfully read jpeg_image into a blob, length=%.20g.",
10043 /* Destroy JPEG image and image_info */
10044 jpeg_image=DestroyImage(jpeg_image);
10045 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10046 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10049 /* Write JHDR chunk */
10050 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
10051 PNGType(chunk,mng_JHDR);
10052 LogPNGChunk(logging,mng_JHDR,16L);
10053 PNGLong(chunk+4,(png_uint_32) image->columns);
10054 PNGLong(chunk+8,(png_uint_32) image->rows);
10055 chunk[12]=jng_color_type;
10056 chunk[13]=8; /* sample depth */
10057 chunk[14]=8; /*jng_image_compression_method */
10058 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10059 chunk[16]=jng_alpha_sample_depth;
10060 chunk[17]=jng_alpha_compression_method;
10061 chunk[18]=0; /*jng_alpha_filter_method */
10062 chunk[19]=0; /*jng_alpha_interlace_method */
10063 (void) WriteBlob(image,20,chunk);
10064 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10065 if (logging != MagickFalse)
10067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10068 " JNG width:%15lu",(unsigned long) image->columns);
10070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10071 " JNG height:%14lu",(unsigned long) image->rows);
10073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10074 " JNG color type:%10d",jng_color_type);
10076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10077 " JNG sample depth:%8d",8);
10079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10080 " JNG compression:%9d",8);
10082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10083 " JNG interlace:%11d",0);
10085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10086 " JNG alpha depth:%9d",jng_alpha_sample_depth);
10088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089 " JNG alpha compression:%3d",jng_alpha_compression_method);
10091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10092 " JNG alpha filter:%8d",0);
10094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10095 " JNG alpha interlace:%5d",0);
10098 /* Write any JNG-chunk-b profiles */
10099 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10102 Write leading ancillary chunks
10108 Write JNG bKGD chunk
10119 if (jng_color_type == 8 || jng_color_type == 12)
10123 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10124 PNGType(chunk,mng_bKGD);
10125 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10126 red=ScaleQuantumToChar(image->background_color.red);
10127 green=ScaleQuantumToChar(image->background_color.green);
10128 blue=ScaleQuantumToChar(image->background_color.blue);
10135 (void) WriteBlob(image,(size_t) num_bytes,chunk);
10136 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10139 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10142 Write JNG sRGB chunk
10144 (void) WriteBlobMSBULong(image,1L);
10145 PNGType(chunk,mng_sRGB);
10146 LogPNGChunk(logging,mng_sRGB,1L);
10148 if (image->rendering_intent != UndefinedIntent)
10149 chunk[4]=(unsigned char)
10150 Magick_RenderingIntent_to_PNG_RenderingIntent(
10151 (image->rendering_intent));
10154 chunk[4]=(unsigned char)
10155 Magick_RenderingIntent_to_PNG_RenderingIntent(
10156 (PerceptualIntent));
10158 (void) WriteBlob(image,5,chunk);
10159 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10163 if (image->gamma != 0.0)
10166 Write JNG gAMA chunk
10168 (void) WriteBlobMSBULong(image,4L);
10169 PNGType(chunk,mng_gAMA);
10170 LogPNGChunk(logging,mng_gAMA,4L);
10171 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10172 (void) WriteBlob(image,8,chunk);
10173 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10176 if ((mng_info->equal_chrms == MagickFalse) &&
10177 (image->chromaticity.red_primary.x != 0.0))
10183 Write JNG cHRM chunk
10185 (void) WriteBlobMSBULong(image,32L);
10186 PNGType(chunk,mng_cHRM);
10187 LogPNGChunk(logging,mng_cHRM,32L);
10188 primary=image->chromaticity.white_point;
10189 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10190 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10191 primary=image->chromaticity.red_primary;
10192 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10193 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10194 primary=image->chromaticity.green_primary;
10195 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10196 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10197 primary=image->chromaticity.blue_primary;
10198 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10199 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10200 (void) WriteBlob(image,36,chunk);
10201 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10205 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10208 Write JNG pHYs chunk
10210 (void) WriteBlobMSBULong(image,9L);
10211 PNGType(chunk,mng_pHYs);
10212 LogPNGChunk(logging,mng_pHYs,9L);
10213 if (image->units == PixelsPerInchResolution)
10215 PNGLong(chunk+4,(png_uint_32)
10216 (image->x_resolution*100.0/2.54+0.5));
10218 PNGLong(chunk+8,(png_uint_32)
10219 (image->y_resolution*100.0/2.54+0.5));
10226 if (image->units == PixelsPerCentimeterResolution)
10228 PNGLong(chunk+4,(png_uint_32)
10229 (image->x_resolution*100.0+0.5));
10231 PNGLong(chunk+8,(png_uint_32)
10232 (image->y_resolution*100.0+0.5));
10239 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10240 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10244 (void) WriteBlob(image,13,chunk);
10245 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10248 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10251 Write JNG oFFs chunk
10253 (void) WriteBlobMSBULong(image,9L);
10254 PNGType(chunk,mng_oFFs);
10255 LogPNGChunk(logging,mng_oFFs,9L);
10256 PNGsLong(chunk+4,(ssize_t) (image->page.x));
10257 PNGsLong(chunk+8,(ssize_t) (image->page.y));
10259 (void) WriteBlob(image,13,chunk);
10260 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10262 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10264 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10265 PNGType(chunk,mng_vpAg);
10266 LogPNGChunk(logging,mng_vpAg,9L);
10267 PNGLong(chunk+4,(png_uint_32) image->page.width);
10268 PNGLong(chunk+8,(png_uint_32) image->page.height);
10269 chunk[12]=0; /* unit = pixels */
10270 (void) WriteBlob(image,13,chunk);
10271 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10277 if (jng_alpha_compression_method==0)
10285 /* Write IDAT chunk header */
10286 if (logging != MagickFalse)
10287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10288 " Write IDAT chunks from blob, length=%.20g.",(double)
10291 /* Copy IDAT chunks */
10294 for (i=8; i<(ssize_t) length; i+=len+12)
10296 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10299 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10301 /* Found an IDAT chunk. */
10302 (void) WriteBlobMSBULong(image,(size_t) len);
10303 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10304 (void) WriteBlob(image,(size_t) len+4,p);
10305 (void) WriteBlobMSBULong(image,
10306 crc32(0,p,(uInt) len+4));
10311 if (logging != MagickFalse)
10312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10313 " Skipping %c%c%c%c chunk, length=%.20g.",
10314 *(p),*(p+1),*(p+2),*(p+3),(double) len);
10321 /* Write JDAA chunk header */
10322 if (logging != MagickFalse)
10323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10324 " Write JDAA chunk, length=%.20g.",(double) length);
10325 (void) WriteBlobMSBULong(image,(size_t) length);
10326 PNGType(chunk,mng_JDAA);
10327 LogPNGChunk(logging,mng_JDAA,length);
10328 /* Write JDAT chunk(s) data */
10329 (void) WriteBlob(image,4,chunk);
10330 (void) WriteBlob(image,length,blob);
10331 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10334 blob=(unsigned char *) RelinquishMagickMemory(blob);
10337 /* Encode image as a JPEG blob */
10338 if (logging != MagickFalse)
10339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10340 " Creating jpeg_image_info.");
10341 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10342 if (jpeg_image_info == (ImageInfo *) NULL)
10343 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10345 if (logging != MagickFalse)
10346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10347 " Creating jpeg_image.");
10349 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10350 if (jpeg_image == (Image *) NULL)
10351 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10352 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10354 (void) AcquireUniqueFilename(jpeg_image->filename);
10355 (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10356 jpeg_image->filename);
10358 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10359 &image->exception);
10361 if (logging != MagickFalse)
10362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10363 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10364 (double) jpeg_image->rows);
10366 if (jng_color_type == 8 || jng_color_type == 12)
10367 jpeg_image_info->type=GrayscaleType;
10369 jpeg_image_info->quality=jng_quality % 1000;
10370 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10371 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10373 if (logging != MagickFalse)
10374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10375 " Creating blob.");
10377 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10379 if (logging != MagickFalse)
10381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10382 " Successfully read jpeg_image into a blob, length=%.20g.",
10385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10386 " Write JDAT chunk, length=%.20g.",(double) length);
10389 /* Write JDAT chunk(s) */
10390 (void) WriteBlobMSBULong(image,(size_t) length);
10391 PNGType(chunk,mng_JDAT);
10392 LogPNGChunk(logging,mng_JDAT,length);
10393 (void) WriteBlob(image,4,chunk);
10394 (void) WriteBlob(image,length,blob);
10395 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10397 jpeg_image=DestroyImage(jpeg_image);
10398 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10399 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10400 blob=(unsigned char *) RelinquishMagickMemory(blob);
10402 /* Write any JNG-chunk-e profiles */
10403 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10405 /* Write IEND chunk */
10406 (void) WriteBlobMSBULong(image,0L);
10407 PNGType(chunk,mng_IEND);
10408 LogPNGChunk(logging,mng_IEND,0);
10409 (void) WriteBlob(image,4,chunk);
10410 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10412 if (logging != MagickFalse)
10413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10414 " exit WriteOneJNGImage()");
10421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10425 % W r i t e J N G I m a g e %
10429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10431 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10433 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
10435 % The format of the WriteJNGImage method is:
10437 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10439 % A description of each parameter follows:
10441 % o image_info: the image info.
10443 % o image: The image.
10445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10447 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10450 have_mng_structure,
10460 assert(image_info != (const ImageInfo *) NULL);
10461 assert(image_info->signature == MagickSignature);
10462 assert(image != (Image *) NULL);
10463 assert(image->signature == MagickSignature);
10464 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10465 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
10466 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
10467 if (status == MagickFalse)
10471 Allocate a MngInfo structure.
10473 have_mng_structure=MagickFalse;
10474 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10475 if (mng_info == (MngInfo *) NULL)
10476 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10478 Initialize members of the MngInfo structure.
10480 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10481 mng_info->image=image;
10482 have_mng_structure=MagickTrue;
10484 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
10486 status=WriteOneJNGImage(mng_info,image_info,image);
10487 (void) CloseBlob(image);
10489 (void) CatchImageException(image);
10490 MngInfoFreeStruct(mng_info,&have_mng_structure);
10491 if (logging != MagickFalse)
10492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
10499 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
10508 have_mng_structure,
10511 volatile MagickBooleanType
10523 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
10524 defined(PNG_MNG_FEATURES_SUPPORTED)
10527 all_images_are_gray,
10537 volatile unsigned int
10548 #if (PNG_LIBPNG_VER < 10200)
10549 if (image_info->verbose)
10550 printf("Your PNG library (libpng-%s) is rather old.\n",
10551 PNG_LIBPNG_VER_STRING);
10557 assert(image_info != (const ImageInfo *) NULL);
10558 assert(image_info->signature == MagickSignature);
10559 assert(image != (Image *) NULL);
10560 assert(image->signature == MagickSignature);
10561 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10562 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
10563 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
10564 if (status == MagickFalse)
10568 Allocate a MngInfo structure.
10570 have_mng_structure=MagickFalse;
10571 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10572 if (mng_info == (MngInfo *) NULL)
10573 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10575 Initialize members of the MngInfo structure.
10577 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10578 mng_info->image=image;
10579 have_mng_structure=MagickTrue;
10580 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
10583 * See if user has requested a specific PNG subformat to be used
10584 * for all of the PNGs in the MNG being written, e.g.,
10586 * convert *.png png8:animation.mng
10588 * To do: check -define png:bit_depth and png:color_type as well,
10589 * or perhaps use mng:bit_depth and mng:color_type instead for
10593 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10594 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10595 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10597 write_jng=MagickFalse;
10598 if (image_info->compression == JPEGCompression)
10599 write_jng=MagickTrue;
10601 mng_info->adjoin=image_info->adjoin &&
10602 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
10604 if (logging != MagickFalse)
10606 /* Log some info about the input */
10610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10611 " Checking input image(s)");
10613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10614 " Image_info depth: %.20g",(double) image_info->depth);
10616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10617 " Type: %d",image_info->type);
10620 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
10622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10623 " Scene: %.20g",(double) scene++);
10625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10626 " Image depth: %.20g",(double) p->depth);
10629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636 if (p->storage_class == PseudoClass)
10637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10638 " Storage class: PseudoClass");
10641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10642 " Storage class: DirectClass");
10645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10646 " Number of colors: %.20g",(double) p->colors);
10649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10650 " Number of colors: unspecified");
10652 if (mng_info->adjoin == MagickFalse)
10657 use_global_plte=MagickFalse;
10658 all_images_are_gray=MagickFalse;
10659 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
10660 need_local_plte=MagickTrue;
10662 need_defi=MagickFalse;
10663 need_matte=MagickFalse;
10664 mng_info->framing_mode=1;
10665 mng_info->old_framing_mode=1;
10668 if (image_info->page != (char *) NULL)
10671 Determine image bounding box.
10673 SetGeometry(image,&mng_info->page);
10674 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
10675 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
10687 mng_info->page=image->page;
10688 need_geom=MagickTrue;
10689 if (mng_info->page.width || mng_info->page.height)
10690 need_geom=MagickFalse;
10692 Check all the scenes.
10694 initial_delay=image->delay;
10695 need_iterations=MagickFalse;
10696 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
10697 mng_info->equal_physs=MagickTrue,
10698 mng_info->equal_gammas=MagickTrue;
10699 mng_info->equal_srgbs=MagickTrue;
10700 mng_info->equal_backgrounds=MagickTrue;
10702 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
10703 defined(PNG_MNG_FEATURES_SUPPORTED)
10704 all_images_are_gray=MagickTrue;
10705 mng_info->equal_palettes=MagickFalse;
10706 need_local_plte=MagickFalse;
10708 for (next_image=image; next_image != (Image *) NULL; )
10712 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
10713 mng_info->page.width=next_image->columns+next_image->page.x;
10715 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
10716 mng_info->page.height=next_image->rows+next_image->page.y;
10719 if (next_image->page.x || next_image->page.y)
10720 need_defi=MagickTrue;
10722 if (next_image->matte)
10723 need_matte=MagickTrue;
10725 if ((int) next_image->dispose >= BackgroundDispose)
10726 if (next_image->matte || next_image->page.x || next_image->page.y ||
10727 ((next_image->columns < mng_info->page.width) &&
10728 (next_image->rows < mng_info->page.height)))
10729 mng_info->need_fram=MagickTrue;
10731 if (next_image->iterations)
10732 need_iterations=MagickTrue;
10734 final_delay=next_image->delay;
10736 if (final_delay != initial_delay || final_delay > 1UL*
10737 next_image->ticks_per_second)
10738 mng_info->need_fram=1;
10740 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
10741 defined(PNG_MNG_FEATURES_SUPPORTED)
10743 check for global palette possibility.
10745 if (image->matte != MagickFalse)
10746 need_local_plte=MagickTrue;
10748 if (need_local_plte == 0)
10750 if (ImageIsGray(image) == MagickFalse)
10751 all_images_are_gray=MagickFalse;
10752 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
10753 if (use_global_plte == 0)
10754 use_global_plte=mng_info->equal_palettes;
10755 need_local_plte=!mng_info->equal_palettes;
10758 if (GetNextImageInList(next_image) != (Image *) NULL)
10760 if (next_image->background_color.red !=
10761 next_image->next->background_color.red ||
10762 next_image->background_color.green !=
10763 next_image->next->background_color.green ||
10764 next_image->background_color.blue !=
10765 next_image->next->background_color.blue)
10766 mng_info->equal_backgrounds=MagickFalse;
10768 if (next_image->gamma != next_image->next->gamma)
10769 mng_info->equal_gammas=MagickFalse;
10771 if (next_image->rendering_intent !=
10772 next_image->next->rendering_intent)
10773 mng_info->equal_srgbs=MagickFalse;
10775 if ((next_image->units != next_image->next->units) ||
10776 (next_image->x_resolution != next_image->next->x_resolution) ||
10777 (next_image->y_resolution != next_image->next->y_resolution))
10778 mng_info->equal_physs=MagickFalse;
10780 if (mng_info->equal_chrms)
10782 if (next_image->chromaticity.red_primary.x !=
10783 next_image->next->chromaticity.red_primary.x ||
10784 next_image->chromaticity.red_primary.y !=
10785 next_image->next->chromaticity.red_primary.y ||
10786 next_image->chromaticity.green_primary.x !=
10787 next_image->next->chromaticity.green_primary.x ||
10788 next_image->chromaticity.green_primary.y !=
10789 next_image->next->chromaticity.green_primary.y ||
10790 next_image->chromaticity.blue_primary.x !=
10791 next_image->next->chromaticity.blue_primary.x ||
10792 next_image->chromaticity.blue_primary.y !=
10793 next_image->next->chromaticity.blue_primary.y ||
10794 next_image->chromaticity.white_point.x !=
10795 next_image->next->chromaticity.white_point.x ||
10796 next_image->chromaticity.white_point.y !=
10797 next_image->next->chromaticity.white_point.y)
10798 mng_info->equal_chrms=MagickFalse;
10802 next_image=GetNextImageInList(next_image);
10804 if (image_count < 2)
10806 mng_info->equal_backgrounds=MagickFalse;
10807 mng_info->equal_chrms=MagickFalse;
10808 mng_info->equal_gammas=MagickFalse;
10809 mng_info->equal_srgbs=MagickFalse;
10810 mng_info->equal_physs=MagickFalse;
10811 use_global_plte=MagickFalse;
10812 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
10813 need_local_plte=MagickTrue;
10815 need_iterations=MagickFalse;
10818 if (mng_info->need_fram == MagickFalse)
10821 Only certain framing rates 100/n are exactly representable without
10822 the FRAM chunk but we'll allow some slop in VLC files
10824 if (final_delay == 0)
10826 if (need_iterations != MagickFalse)
10829 It's probably a GIF with loop; don't run it *too* fast.
10831 if (mng_info->adjoin)
10834 (void) ThrowMagickException(&image->exception,
10835 GetMagickModule(),CoderWarning,
10836 "input has zero delay between all frames; assuming",
10841 mng_info->ticks_per_second=0;
10843 if (final_delay != 0)
10844 mng_info->ticks_per_second=(png_uint_32)
10845 (image->ticks_per_second/final_delay);
10846 if (final_delay > 50)
10847 mng_info->ticks_per_second=2;
10849 if (final_delay > 75)
10850 mng_info->ticks_per_second=1;
10852 if (final_delay > 125)
10853 mng_info->need_fram=MagickTrue;
10855 if (need_defi && final_delay > 2 && (final_delay != 4) &&
10856 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
10857 (final_delay != 25) && (final_delay != 50) && (final_delay !=
10858 1UL*image->ticks_per_second))
10859 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
10862 if (mng_info->need_fram != MagickFalse)
10863 mng_info->ticks_per_second=1UL*image->ticks_per_second;
10865 If pseudocolor, we should also check to see if all the
10866 palettes are identical and write a global PLTE if they are.
10870 Write the MNG version 1.0 signature and MHDR chunk.
10872 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
10873 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
10874 PNGType(chunk,mng_MHDR);
10875 LogPNGChunk(logging,mng_MHDR,28L);
10876 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
10877 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
10878 PNGLong(chunk+12,mng_info->ticks_per_second);
10879 PNGLong(chunk+16,0L); /* layer count=unknown */
10880 PNGLong(chunk+20,0L); /* frame count=unknown */
10881 PNGLong(chunk+24,0L); /* play time=unknown */
10886 if (need_defi || mng_info->need_fram || use_global_plte)
10887 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
10890 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
10895 if (need_defi || mng_info->need_fram || use_global_plte)
10896 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
10899 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
10907 if (need_defi || mng_info->need_fram || use_global_plte)
10908 PNGLong(chunk+28,11L); /* simplicity=LC */
10911 PNGLong(chunk+28,9L); /* simplicity=VLC */
10916 if (need_defi || mng_info->need_fram || use_global_plte)
10917 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
10920 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
10923 (void) WriteBlob(image,32,chunk);
10924 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
10925 option=GetImageOption(image_info,"mng:need-cacheoff");
10926 if (option != (const char *) NULL)
10932 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
10934 PNGType(chunk,mng_nEED);
10935 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
10936 (void) WriteBlobMSBULong(image,(size_t) length);
10937 LogPNGChunk(logging,mng_nEED,(size_t) length);
10939 (void) WriteBlob(image,length,chunk);
10940 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
10942 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
10943 (GetNextImageInList(image) != (Image *) NULL) &&
10944 (image->iterations != 1))
10947 Write MNG TERM chunk
10949 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
10950 PNGType(chunk,mng_TERM);
10951 LogPNGChunk(logging,mng_TERM,10L);
10952 chunk[4]=3; /* repeat animation */
10953 chunk[5]=0; /* show last frame when done */
10954 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
10955 final_delay/MagickMax(image->ticks_per_second,1)));
10957 if (image->iterations == 0)
10958 PNGLong(chunk+10,PNG_UINT_31_MAX);
10961 PNGLong(chunk+10,(png_uint_32) image->iterations);
10963 if (logging != MagickFalse)
10965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10966 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
10967 final_delay/MagickMax(image->ticks_per_second,1)));
10969 if (image->iterations == 0)
10970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10971 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
10974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10975 " Image iterations: %.20g",(double) image->iterations);
10977 (void) WriteBlob(image,14,chunk);
10978 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
10981 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10983 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
10984 mng_info->equal_srgbs)
10987 Write MNG sRGB chunk
10989 (void) WriteBlobMSBULong(image,1L);
10990 PNGType(chunk,mng_sRGB);
10991 LogPNGChunk(logging,mng_sRGB,1L);
10993 if (image->rendering_intent != UndefinedIntent)
10994 chunk[4]=(unsigned char)
10995 Magick_RenderingIntent_to_PNG_RenderingIntent(
10996 (image->rendering_intent));
10999 chunk[4]=(unsigned char)
11000 Magick_RenderingIntent_to_PNG_RenderingIntent(
11001 (PerceptualIntent));
11003 (void) WriteBlob(image,5,chunk);
11004 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11005 mng_info->have_write_global_srgb=MagickTrue;
11010 if (image->gamma && mng_info->equal_gammas)
11013 Write MNG gAMA chunk
11015 (void) WriteBlobMSBULong(image,4L);
11016 PNGType(chunk,mng_gAMA);
11017 LogPNGChunk(logging,mng_gAMA,4L);
11018 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11019 (void) WriteBlob(image,8,chunk);
11020 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11021 mng_info->have_write_global_gama=MagickTrue;
11023 if (mng_info->equal_chrms)
11029 Write MNG cHRM chunk
11031 (void) WriteBlobMSBULong(image,32L);
11032 PNGType(chunk,mng_cHRM);
11033 LogPNGChunk(logging,mng_cHRM,32L);
11034 primary=image->chromaticity.white_point;
11035 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11036 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11037 primary=image->chromaticity.red_primary;
11038 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11039 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11040 primary=image->chromaticity.green_primary;
11041 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11042 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11043 primary=image->chromaticity.blue_primary;
11044 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11045 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11046 (void) WriteBlob(image,36,chunk);
11047 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11048 mng_info->have_write_global_chrm=MagickTrue;
11051 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11054 Write MNG pHYs chunk
11056 (void) WriteBlobMSBULong(image,9L);
11057 PNGType(chunk,mng_pHYs);
11058 LogPNGChunk(logging,mng_pHYs,9L);
11060 if (image->units == PixelsPerInchResolution)
11062 PNGLong(chunk+4,(png_uint_32)
11063 (image->x_resolution*100.0/2.54+0.5));
11065 PNGLong(chunk+8,(png_uint_32)
11066 (image->y_resolution*100.0/2.54+0.5));
11073 if (image->units == PixelsPerCentimeterResolution)
11075 PNGLong(chunk+4,(png_uint_32)
11076 (image->x_resolution*100.0+0.5));
11078 PNGLong(chunk+8,(png_uint_32)
11079 (image->y_resolution*100.0+0.5));
11086 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11087 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11091 (void) WriteBlob(image,13,chunk);
11092 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11095 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11096 or does not cover the entire frame.
11098 if (write_mng && (image->matte || image->page.x > 0 ||
11099 image->page.y > 0 || (image->page.width &&
11100 (image->page.width+image->page.x < mng_info->page.width))
11101 || (image->page.height && (image->page.height+image->page.y
11102 < mng_info->page.height))))
11104 (void) WriteBlobMSBULong(image,6L);
11105 PNGType(chunk,mng_BACK);
11106 LogPNGChunk(logging,mng_BACK,6L);
11107 red=ScaleQuantumToShort(image->background_color.red);
11108 green=ScaleQuantumToShort(image->background_color.green);
11109 blue=ScaleQuantumToShort(image->background_color.blue);
11110 PNGShort(chunk+4,red);
11111 PNGShort(chunk+6,green);
11112 PNGShort(chunk+8,blue);
11113 (void) WriteBlob(image,10,chunk);
11114 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11115 if (mng_info->equal_backgrounds)
11117 (void) WriteBlobMSBULong(image,6L);
11118 PNGType(chunk,mng_bKGD);
11119 LogPNGChunk(logging,mng_bKGD,6L);
11120 (void) WriteBlob(image,10,chunk);
11121 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11125 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11126 if ((need_local_plte == MagickFalse) &&
11127 (image->storage_class == PseudoClass) &&
11128 (all_images_are_gray == MagickFalse))
11134 Write MNG PLTE chunk
11136 data_length=3*image->colors;
11137 (void) WriteBlobMSBULong(image,data_length);
11138 PNGType(chunk,mng_PLTE);
11139 LogPNGChunk(logging,mng_PLTE,data_length);
11141 for (i=0; i < (ssize_t) image->colors; i++)
11143 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11144 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11145 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11148 (void) WriteBlob(image,data_length+4,chunk);
11149 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11150 mng_info->have_write_global_plte=MagickTrue;
11156 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11157 defined(PNG_MNG_FEATURES_SUPPORTED)
11158 mng_info->equal_palettes=MagickFalse;
11162 if (mng_info->adjoin)
11164 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11165 defined(PNG_MNG_FEATURES_SUPPORTED)
11167 If we aren't using a global palette for the entire MNG, check to
11168 see if we can use one for two or more consecutive images.
11170 if (need_local_plte && use_global_plte && !all_images_are_gray)
11172 if (mng_info->IsPalette)
11175 When equal_palettes is true, this image has the same palette
11176 as the previous PseudoClass image
11178 mng_info->have_write_global_plte=mng_info->equal_palettes;
11179 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11180 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11183 Write MNG PLTE chunk
11188 data_length=3*image->colors;
11189 (void) WriteBlobMSBULong(image,data_length);
11190 PNGType(chunk,mng_PLTE);
11191 LogPNGChunk(logging,mng_PLTE,data_length);
11193 for (i=0; i < (ssize_t) image->colors; i++)
11195 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11196 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11197 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11200 (void) WriteBlob(image,data_length+4,chunk);
11201 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11202 (uInt) (data_length+4)));
11203 mng_info->have_write_global_plte=MagickTrue;
11207 mng_info->have_write_global_plte=MagickFalse;
11218 previous_x=mng_info->page.x;
11219 previous_y=mng_info->page.y;
11226 mng_info->page=image->page;
11227 if ((mng_info->page.x != previous_x) ||
11228 (mng_info->page.y != previous_y))
11230 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
11231 PNGType(chunk,mng_DEFI);
11232 LogPNGChunk(logging,mng_DEFI,12L);
11233 chunk[4]=0; /* object 0 MSB */
11234 chunk[5]=0; /* object 0 LSB */
11235 chunk[6]=0; /* visible */
11236 chunk[7]=0; /* abstract */
11237 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11238 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11239 (void) WriteBlob(image,16,chunk);
11240 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11245 mng_info->write_mng=write_mng;
11247 if ((int) image->dispose >= 3)
11248 mng_info->framing_mode=3;
11250 if (mng_info->need_fram && mng_info->adjoin &&
11251 ((image->delay != mng_info->delay) ||
11252 (mng_info->framing_mode != mng_info->old_framing_mode)))
11254 if (image->delay == mng_info->delay)
11257 Write a MNG FRAM chunk with the new framing mode.
11259 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11260 PNGType(chunk,mng_FRAM);
11261 LogPNGChunk(logging,mng_FRAM,1L);
11262 chunk[4]=(unsigned char) mng_info->framing_mode;
11263 (void) WriteBlob(image,5,chunk);
11264 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11269 Write a MNG FRAM chunk with the delay.
11271 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
11272 PNGType(chunk,mng_FRAM);
11273 LogPNGChunk(logging,mng_FRAM,10L);
11274 chunk[4]=(unsigned char) mng_info->framing_mode;
11275 chunk[5]=0; /* frame name separator (no name) */
11276 chunk[6]=2; /* flag for changing default delay */
11277 chunk[7]=0; /* flag for changing frame timeout */
11278 chunk[8]=0; /* flag for changing frame clipping */
11279 chunk[9]=0; /* flag for changing frame sync_id */
11280 PNGLong(chunk+10,(png_uint_32)
11281 ((mng_info->ticks_per_second*
11282 image->delay)/MagickMax(image->ticks_per_second,1)));
11283 (void) WriteBlob(image,14,chunk);
11284 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11285 mng_info->delay=(png_uint_32) image->delay;
11287 mng_info->old_framing_mode=mng_info->framing_mode;
11290 #if defined(JNG_SUPPORTED)
11291 if (image_info->compression == JPEGCompression)
11296 if (logging != MagickFalse)
11297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11298 " Writing JNG object.");
11299 /* To do: specify the desired alpha compression method. */
11300 write_info=CloneImageInfo(image_info);
11301 write_info->compression=UndefinedCompression;
11302 status=WriteOneJNGImage(mng_info,write_info,image);
11303 write_info=DestroyImageInfo(write_info);
11308 if (logging != MagickFalse)
11309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11310 " Writing PNG object.");
11312 mng_info->need_blob = MagickFalse;
11314 /* We don't want any ancillary chunks written */
11315 mng_info->ping_exclude_bKGD=MagickTrue;
11316 mng_info->ping_exclude_cHRM=MagickTrue;
11317 mng_info->ping_exclude_EXIF=MagickTrue;
11318 mng_info->ping_exclude_gAMA=MagickTrue;
11319 mng_info->ping_exclude_cHRM=MagickTrue;
11320 mng_info->ping_exclude_iCCP=MagickTrue;
11321 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11322 mng_info->ping_exclude_oFFs=MagickTrue;
11323 mng_info->ping_exclude_pHYs=MagickTrue;
11324 mng_info->ping_exclude_sRGB=MagickTrue;
11325 mng_info->ping_exclude_tEXt=MagickTrue;
11326 mng_info->ping_exclude_tRNS=MagickTrue;
11327 mng_info->ping_exclude_vpAg=MagickTrue;
11328 mng_info->ping_exclude_zCCP=MagickTrue;
11329 mng_info->ping_exclude_zTXt=MagickTrue;
11331 status=WriteOnePNGImage(mng_info,image_info,image);
11334 if (status == MagickFalse)
11336 MngInfoFreeStruct(mng_info,&have_mng_structure);
11337 (void) CloseBlob(image);
11338 return(MagickFalse);
11340 (void) CatchImageException(image);
11341 if (GetNextImageInList(image) == (Image *) NULL)
11343 image=SyncNextImageInList(image);
11344 status=SetImageProgress(image,SaveImagesTag,scene++,
11345 GetImageListLength(image));
11347 if (status == MagickFalse)
11350 } while (mng_info->adjoin);
11354 while (GetPreviousImageInList(image) != (Image *) NULL)
11355 image=GetPreviousImageInList(image);
11357 Write the MEND chunk.
11359 (void) WriteBlobMSBULong(image,0x00000000L);
11360 PNGType(chunk,mng_MEND);
11361 LogPNGChunk(logging,mng_MEND,0L);
11362 (void) WriteBlob(image,4,chunk);
11363 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11366 Relinquish resources.
11368 (void) CloseBlob(image);
11369 MngInfoFreeStruct(mng_info,&have_mng_structure);
11371 if (logging != MagickFalse)
11372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11374 return(MagickTrue);
11376 #else /* PNG_LIBPNG_VER > 10011 */
11378 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11381 printf("Your PNG library is too old: You have libpng-%s\n",
11382 PNG_LIBPNG_VER_STRING);
11384 ThrowBinaryException(CoderError,"PNG library is too old",
11385 image_info->filename);
11388 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11390 return(WritePNGImage(image_info,image));
11392 #endif /* PNG_LIBPNG_VER > 10011 */