2 // System.Drawing.Image.cs
4 // Authors: Christian Meyer (Christian.Meyer@cs.tum.edu)
5 // Alexandre Pigolkine (pigolkine@gmx.de)
6 // Jordi Mas i Hernandez (jordi@ximian.com)
7 // Sanjay Gupta (gsanjay@novell.com)
8 // Ravindra (rkumar@novell.com)
9 // Sebastien Pouliot <sebastien@ximian.com>
11 // Copyright (C) 2002 Ximian, Inc. http://www.ximian.com
12 // Copyright (C) 2004, 2007 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Runtime.Remoting;
36 using System.Runtime.Serialization;
37 using System.Runtime.InteropServices;
38 using System.ComponentModel;
39 using System.Drawing.Imaging;
41 using System.Reflection;
43 namespace System.Drawing
47 [Editor ("System.Drawing.Design.ImageEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))]
48 [TypeConverter (typeof(ImageConverter))]
49 [ImmutableObject (true)]
50 public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISerializable
52 public delegate bool GetThumbnailImageAbort();
57 internal IntPtr nativeObject = IntPtr.Zero;
58 // when using MS GDI+ and IStream we must ensure the stream stays alive for all the life of the Image
59 // http://groups.google.com/group/microsoft.public.win32.programmer.gdi/browse_thread/thread/4967097db1469a27/4d36385b83532126?lnk=st&q=IStream+gdi&rnum=3&hl=en#4d36385b83532126
60 internal Stream stream;
68 internal Image (SerializationInfo info, StreamingContext context)
70 foreach (SerializationEntry serEnum in info) {
71 if (String.Compare(serEnum.Name, "Data", true) == 0) {
72 byte[] bytes = (byte[]) serEnum.Value;
75 MemoryStream ms = new MemoryStream (bytes);
76 nativeObject = InitFromStream (ms);
77 // under Win32 stream is owned by SD/GDI+ code
78 if (GDIPlus.RunningOnWindows ())
85 // FIXME - find out how metafiles (another decoder-only codec) are handled
86 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext context)
88 using (MemoryStream ms = new MemoryStream ()) {
89 // Icon is a decoder-only codec
90 if (RawFormat.Equals (ImageFormat.Icon)) {
91 Save (ms, ImageFormat.Png);
95 si.AddValue ("Data", ms.ToArray ());
101 public static Image FromFile(string filename)
103 return FromFile (filename, false);
106 public static Image FromFile(string filename, bool useEmbeddedColorManagement)
111 if (!File.Exists (filename))
112 throw new FileNotFoundException (filename);
114 if (useEmbeddedColorManagement)
115 st = GDIPlus.GdipLoadImageFromFileICM (filename, out imagePtr);
117 st = GDIPlus.GdipLoadImageFromFile (filename, out imagePtr);
118 GDIPlus.CheckStatus (st);
120 return CreateFromHandle (imagePtr);
123 public static Bitmap FromHbitmap(IntPtr hbitmap)
125 return FromHbitmap (hbitmap, IntPtr.Zero);
128 public static Bitmap FromHbitmap(IntPtr hbitmap, IntPtr hpalette)
133 st = GDIPlus.GdipCreateBitmapFromHBITMAP (hbitmap, hpalette, out imagePtr);
135 GDIPlus.CheckStatus (st);
136 return new Bitmap (imagePtr);
139 // note: FromStream can return either a Bitmap or Metafile instance
141 public static Image FromStream (Stream stream)
143 return LoadFromStream (stream, false);
146 [MonoLimitation ("useEmbeddedColorManagement isn't supported.")]
147 public static Image FromStream (Stream stream, bool useEmbeddedColorManagement)
149 return LoadFromStream (stream, false);
152 // See http://support.microsoft.com/default.aspx?scid=kb;en-us;831419 for performance discussion
153 [MonoLimitation ("useEmbeddedColorManagement and validateImageData aren't supported.")]
154 public static Image FromStream (Stream stream, bool useEmbeddedColorManagement, bool validateImageData)
156 return LoadFromStream (stream, false);
159 internal static Image LoadFromStream (Stream stream, bool keepAlive)
162 throw new ArgumentNullException ("stream");
164 Image img = CreateFromHandle (InitFromStream (stream));
166 // Under Windows, we may need to keep a reference on the stream as long as the image is alive
167 // (GDI+ seems to use a lazy loader)
168 if (keepAlive && GDIPlus.RunningOnWindows ())
174 internal static Image CreateFromHandle (IntPtr handle)
177 GDIPlus.CheckStatus (GDIPlus.GdipGetImageType (handle, out type));
179 case ImageType.Bitmap:
180 return new Bitmap (handle);
181 case ImageType.Metafile:
182 return new Metafile (handle);
184 throw new NotSupportedException (Locale.GetText ("Unknown image type."));
188 public static int GetPixelFormatSize(PixelFormat pixfmt)
192 case PixelFormat.Format16bppArgb1555:
193 case PixelFormat.Format16bppGrayScale:
194 case PixelFormat.Format16bppRgb555:
195 case PixelFormat.Format16bppRgb565:
198 case PixelFormat.Format1bppIndexed:
201 case PixelFormat.Format24bppRgb:
204 case PixelFormat.Format32bppArgb:
205 case PixelFormat.Format32bppPArgb:
206 case PixelFormat.Format32bppRgb:
209 case PixelFormat.Format48bppRgb:
212 case PixelFormat.Format4bppIndexed:
215 case PixelFormat.Format64bppArgb:
216 case PixelFormat.Format64bppPArgb:
219 case PixelFormat.Format8bppIndexed:
226 public static bool IsAlphaPixelFormat(PixelFormat pixfmt)
230 case PixelFormat.Format16bppArgb1555:
231 case PixelFormat.Format32bppArgb:
232 case PixelFormat.Format32bppPArgb:
233 case PixelFormat.Format64bppArgb:
234 case PixelFormat.Format64bppPArgb:
237 case PixelFormat.Format16bppGrayScale:
238 case PixelFormat.Format16bppRgb555:
239 case PixelFormat.Format16bppRgb565:
240 case PixelFormat.Format1bppIndexed:
241 case PixelFormat.Format24bppRgb:
242 case PixelFormat.Format32bppRgb:
243 case PixelFormat.Format48bppRgb:
244 case PixelFormat.Format4bppIndexed:
245 case PixelFormat.Format8bppIndexed:
252 public static bool IsCanonicalPixelFormat (PixelFormat pixfmt)
254 return ((pixfmt & PixelFormat.Canonical) != 0);
257 public static bool IsExtendedPixelFormat (PixelFormat pixfmt)
259 return ((pixfmt & PixelFormat.Extended) != 0);
262 internal static IntPtr InitFromStream (Stream stream)
265 throw new ArgumentException ("stream");
271 if (!stream.CanSeek) {
272 byte[] buffer = new byte[256];
277 if (buffer.Length < index + 256) {
278 byte[] newBuffer = new byte[buffer.Length * 2];
279 Array.Copy(buffer, newBuffer, buffer.Length);
282 count = stream.Read(buffer, index, 256);
287 stream = new MemoryStream(buffer, 0, index);
290 if (GDIPlus.RunningOnUnix ()) {
291 // Unix, with libgdiplus
292 // We use a custom API for this, because there's no easy way
293 // to get the Stream down to libgdiplus. So, we wrap the stream
294 // with a set of delegates.
295 GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream, true);
297 st = GDIPlus.GdipLoadImageFromDelegate_linux (sh.GetHeaderDelegate, sh.GetBytesDelegate,
298 sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, out imagePtr);
300 st = GDIPlus.GdipLoadImageFromStream (new ComIStreamWrapper (stream), out imagePtr);
303 return st == Status.Ok ? imagePtr : IntPtr.Zero;
307 public RectangleF GetBounds (ref GraphicsUnit pageUnit)
311 Status status = GDIPlus.GdipGetImageBounds (nativeObject, out source, ref pageUnit);
312 GDIPlus.CheckStatus (status);
317 public EncoderParameters GetEncoderParameterList(Guid encoder)
322 status = GDIPlus.GdipGetEncoderParameterListSize (nativeObject, ref encoder, out sz);
323 GDIPlus.CheckStatus (status);
325 IntPtr rawEPList = Marshal.AllocHGlobal ((int) sz);
326 EncoderParameters eps;
329 status = GDIPlus.GdipGetEncoderParameterList (nativeObject, ref encoder, sz, rawEPList);
330 eps = EncoderParameters.FromNativePtr (rawEPList);
331 GDIPlus.CheckStatus (status);
334 Marshal.FreeHGlobal (rawEPList);
340 public int GetFrameCount (FrameDimension dimension)
343 Guid guid = dimension.Guid;
345 Status status = GDIPlus.GdipImageGetFrameCount (nativeObject, ref guid, out count);
346 GDIPlus.CheckStatus (status);
351 public PropertyItem GetPropertyItem(int propid)
355 PropertyItem item = new PropertyItem ();
356 GdipPropertyItem gdipProperty = new GdipPropertyItem ();
359 status = GDIPlus.GdipGetPropertyItemSize (nativeObject, propid,
361 GDIPlus.CheckStatus (status);
363 /* Get PropertyItem */
364 property = Marshal.AllocHGlobal (propSize);
366 status = GDIPlus.GdipGetPropertyItem (nativeObject, propid, propSize, property);
367 GDIPlus.CheckStatus (status);
368 gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure (property,
369 typeof (GdipPropertyItem));
370 GdipPropertyItem.MarshalTo (gdipProperty, item);
373 Marshal.FreeHGlobal (property);
378 public Image GetThumbnailImage (int thumbWidth, int thumbHeight, Image.GetThumbnailImageAbort callback, IntPtr callbackData)
380 if ((thumbWidth <= 0) || (thumbHeight <= 0))
381 throw new OutOfMemoryException ("Invalid thumbnail size");
383 Image ThumbNail = new Bitmap (thumbWidth, thumbHeight);
385 using (Graphics g = Graphics.FromImage (ThumbNail)) {
386 Status status = GDIPlus.GdipDrawImageRectRectI (g.nativeObject, nativeObject,
387 0, 0, thumbWidth, thumbHeight,
388 0, 0, this.Width, this.Height,
389 GraphicsUnit.Pixel, IntPtr.Zero, null, IntPtr.Zero);
391 GDIPlus.CheckStatus (status);
398 public void RemovePropertyItem (int propid)
400 Status status = GDIPlus.GdipRemovePropertyItem (nativeObject, propid);
401 GDIPlus.CheckStatus (status);
404 public void RotateFlip (RotateFlipType rotateFlipType)
406 Status status = GDIPlus.GdipImageRotateFlip (nativeObject, rotateFlipType);
407 GDIPlus.CheckStatus (status);
410 internal ImageCodecInfo findEncoderForFormat (ImageFormat format)
412 ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
413 ImageCodecInfo encoder = null;
415 if (format.Guid.Equals (ImageFormat.MemoryBmp.Guid))
416 format = ImageFormat.Png;
418 /* Look for the right encoder for our format*/
419 for (int i = 0; i < encoders.Length; i++) {
420 if (encoders[i].FormatID.Equals (format.Guid)) {
421 encoder = encoders[i];
429 public void Save (string filename)
431 Save (filename, RawFormat);
434 public void Save(string filename, ImageFormat format)
436 ImageCodecInfo encoder = findEncoderForFormat (format);
437 if (encoder == null) {
439 encoder = findEncoderForFormat (RawFormat);
440 if (encoder == null) {
441 string msg = Locale.GetText ("No codec available for saving format '{0}'.", format.Guid);
442 throw new ArgumentException (msg, "format");
445 Save (filename, encoder, null);
448 public void Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
451 Guid guid = encoder.Clsid;
453 if (encoderParams == null) {
454 st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, IntPtr.Zero);
456 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
457 st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, nativeEncoderParams);
458 Marshal.FreeHGlobal (nativeEncoderParams);
461 GDIPlus.CheckStatus (st);
464 public void Save (Stream stream, ImageFormat format)
466 ImageCodecInfo encoder = findEncoderForFormat (format);
469 throw new ArgumentException ("No codec available for format:" + format.Guid);
471 Save (stream, encoder, null);
474 public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
477 IntPtr nativeEncoderParams;
478 Guid guid = encoder.Clsid;
480 if (encoderParams == null)
481 nativeEncoderParams = IntPtr.Zero;
483 nativeEncoderParams = encoderParams.ToNativePtr ();
486 if (GDIPlus.RunningOnUnix ()) {
487 GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream, false);
488 st = GDIPlus.GdipSaveImageToDelegate_linux (nativeObject, sh.GetBytesDelegate, sh.PutBytesDelegate,
489 sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, ref guid, nativeEncoderParams);
491 st = GDIPlus.GdipSaveImageToStream (new HandleRef (this, nativeObject),
492 new ComIStreamWrapper (stream), ref guid, new HandleRef (encoderParams, nativeEncoderParams));
496 if (nativeEncoderParams != IntPtr.Zero)
497 Marshal.FreeHGlobal (nativeEncoderParams);
500 GDIPlus.CheckStatus (st);
503 public void SaveAdd (EncoderParameters encoderParams)
507 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
508 st = GDIPlus.GdipSaveAdd (nativeObject, nativeEncoderParams);
509 Marshal.FreeHGlobal (nativeEncoderParams);
510 GDIPlus.CheckStatus (st);
513 public void SaveAdd (Image image, EncoderParameters encoderParams)
517 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
518 st = GDIPlus.GdipSaveAddImage (nativeObject, image.NativeObject, nativeEncoderParams);
519 Marshal.FreeHGlobal (nativeEncoderParams);
520 GDIPlus.CheckStatus (st);
523 public int SelectActiveFrame(FrameDimension dimension, int frameIndex)
525 Guid guid = dimension.Guid;
526 Status st = GDIPlus.GdipImageSelectActiveFrame (nativeObject, ref guid, frameIndex);
528 GDIPlus.CheckStatus (st);
533 public void SetPropertyItem(PropertyItem propitem)
535 throw new NotImplementedException ();
537 GdipPropertyItem pi = new GdipPropertyItem ();
538 GdipPropertyItem.MarshalTo (pi, propitem);
540 Status status = GDIPlus.GdipSetPropertyItem (nativeObject, &pi);
542 GDIPlus.CheckStatus (status);
553 Status status = GDIPlus.GdipGetImageFlags (nativeObject, out flags);
554 GDIPlus.CheckStatus (status);
560 public Guid[] FrameDimensionsList {
563 Status status = GDIPlus.GdipImageGetFrameDimensionsCount (nativeObject, out found);
564 GDIPlus.CheckStatus (status);
565 Guid [] guid = new Guid [found];
566 status = GDIPlus.GdipImageGetFrameDimensionsList (nativeObject, guid, found);
567 GDIPlus.CheckStatus (status);
572 [DefaultValue (false)]
574 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
578 Status status = GDIPlus.GdipGetImageHeight (nativeObject, out height);
579 GDIPlus.CheckStatus (status);
585 public float HorizontalResolution {
589 Status status = GDIPlus.GdipGetImageHorizontalResolution (nativeObject, out resolution);
590 GDIPlus.CheckStatus (status);
597 public ColorPalette Palette {
599 return retrieveGDIPalette();
602 storeGDIPalette(value);
606 internal ColorPalette retrieveGDIPalette()
609 ColorPalette ret = new ColorPalette ();
611 Status st = GDIPlus.GdipGetImagePaletteSize (nativeObject, out bytes);
612 GDIPlus.CheckStatus (st);
613 IntPtr palette_data = Marshal.AllocHGlobal (bytes);
615 st = GDIPlus.GdipGetImagePalette (nativeObject, palette_data, bytes);
616 GDIPlus.CheckStatus (st);
617 ret.setFromGDIPalette (palette_data);
622 Marshal.FreeHGlobal (palette_data);
626 internal void storeGDIPalette(ColorPalette palette)
628 if (palette == null) {
629 throw new ArgumentNullException("palette");
631 IntPtr palette_data = palette.getGDIPalette();
632 if (palette_data == IntPtr.Zero) {
637 Status st = GDIPlus.GdipSetImagePalette (nativeObject, palette_data);
638 GDIPlus.CheckStatus (st);
642 Marshal.FreeHGlobal(palette_data);
647 public SizeF PhysicalDimension {
650 Status status = GDIPlus.GdipGetImageDimension (nativeObject, out width, out height);
651 GDIPlus.CheckStatus (status);
653 return new SizeF (width, height);
657 public PixelFormat PixelFormat {
659 PixelFormat pixFormat;
660 Status status = GDIPlus.GdipGetImagePixelFormat (nativeObject, out pixFormat);
661 GDIPlus.CheckStatus (status);
668 public int[] PropertyIdList {
672 Status status = GDIPlus.GdipGetPropertyCount (nativeObject,
674 GDIPlus.CheckStatus (status);
676 int [] idList = new int [propNumbers];
677 status = GDIPlus.GdipGetPropertyIdList (nativeObject,
678 propNumbers, idList);
679 GDIPlus.CheckStatus (status);
686 public PropertyItem[] PropertyItems {
688 int propNums, propsSize, propSize;
689 IntPtr properties, propPtr;
690 PropertyItem[] items;
691 GdipPropertyItem gdipProperty = new GdipPropertyItem ();
694 status = GDIPlus.GdipGetPropertySize (nativeObject, out propsSize, out propNums);
695 GDIPlus.CheckStatus (status);
697 items = new PropertyItem [propNums];
702 /* Get PropertyItem list*/
703 properties = Marshal.AllocHGlobal (propsSize * propNums);
705 status = GDIPlus.GdipGetAllPropertyItems (nativeObject, propsSize,
706 propNums, properties);
707 GDIPlus.CheckStatus (status);
709 propSize = Marshal.SizeOf (gdipProperty);
710 propPtr = properties;
712 for (int i = 0; i < propNums; i++, propPtr = new IntPtr (propPtr.ToInt64 () + propSize)) {
713 gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure
714 (propPtr, typeof (GdipPropertyItem));
715 items [i] = new PropertyItem ();
716 GdipPropertyItem.MarshalTo (gdipProperty, items [i]);
720 Marshal.FreeHGlobal (properties);
726 public ImageFormat RawFormat {
729 Status st = GDIPlus.GdipGetImageRawFormat (nativeObject, out guid);
731 GDIPlus.CheckStatus (st);
732 return new ImageFormat (guid);
738 return new Size(Width, Height);
743 [DefaultValue (null)]
744 [LocalizableAttribute(false)]
745 [BindableAttribute(true)]
746 [TypeConverter (typeof (StringConverter))]
752 public float VerticalResolution {
756 Status status = GDIPlus.GdipGetImageVerticalResolution (nativeObject, out resolution);
757 GDIPlus.CheckStatus (status);
763 [DefaultValue (false)]
765 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
769 Status status = GDIPlus.GdipGetImageWidth (nativeObject, out width);
770 GDIPlus.CheckStatus (status);
776 internal IntPtr NativeObject{
781 nativeObject = value;
785 public void Dispose ()
788 GC.SuppressFinalize (this);
796 protected virtual void Dispose (bool disposing)
798 if (GDIPlus.GdiPlusToken != 0 && nativeObject != IntPtr.Zero) {
799 Status status = GDIPlus.GdipDisposeImage (nativeObject);
800 // dispose the stream (set under Win32 only if SD owns the stream) and ...
801 if (stream != null) {
805 // ... set nativeObject to null before (possibly) throwing an exception
806 nativeObject = IntPtr.Zero;
807 GDIPlus.CheckStatus (status);
811 public object Clone ()
813 if (GDIPlus.RunningOnWindows () && stream != null)
814 return CloneFromStream ();
816 IntPtr newimage = IntPtr.Zero;
817 Status status = GDIPlus.GdipCloneImage (NativeObject, out newimage);
818 GDIPlus.CheckStatus (status);
821 return new Bitmap (newimage);
823 return new Metafile (newimage);
826 // On win32, when cloning images that were originally created from a stream, we need to
827 // clone both the image and the stream to make sure the gc doesn't kill it
828 // (when using MS GDI+ and IStream we must ensure the stream stays alive for all the life of the Image)
829 object CloneFromStream ()
831 byte[] bytes = new byte [stream.Length];
832 MemoryStream ms = new MemoryStream (bytes);
833 int count = (stream.Length < 4096 ? (int) stream.Length : 4096);
834 byte[] buffer = new byte[count];
837 count = stream.Read (buffer, 0, count);
838 ms.Write (buffer, 0, count);
839 } while (count == 4096);
841 IntPtr newimage = IntPtr.Zero;
842 newimage = InitFromStream (ms);
845 return new Bitmap (newimage, ms);
847 return new Metafile (newimage, ms);