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)
13 // Copyright (C) 2013 Kristof Ralovich, changes are available under the terms of the MIT X11 license
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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();
55 internal IntPtr nativeObject = IntPtr.Zero;
56 // when using MS GDI+ and IStream we must ensure the stream stays alive for all the life of the Image
57 // 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
58 internal Stream stream;
66 internal Image (SerializationInfo info, StreamingContext context)
68 foreach (SerializationEntry serEnum in info) {
69 if (String.Compare(serEnum.Name, "Data", true) == 0) {
70 byte[] bytes = (byte[]) serEnum.Value;
73 MemoryStream ms = new MemoryStream (bytes);
74 nativeObject = InitFromStream (ms);
75 // under Win32 stream is owned by SD/GDI+ code
76 if (GDIPlus.RunningOnWindows ())
83 // FIXME - find out how metafiles (another decoder-only codec) are handled
84 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext context)
86 using (MemoryStream ms = new MemoryStream ()) {
87 // Icon is a decoder-only codec
88 if (RawFormat.Equals (ImageFormat.Icon)) {
89 Save (ms, ImageFormat.Png);
93 si.AddValue ("Data", ms.ToArray ());
99 public static Image FromFile(string filename)
101 return FromFile (filename, false);
104 public static Image FromFile(string filename, bool useEmbeddedColorManagement)
109 if (!File.Exists (filename))
110 throw new FileNotFoundException (filename);
112 if (useEmbeddedColorManagement)
113 st = GDIPlus.GdipLoadImageFromFileICM (filename, out imagePtr);
115 st = GDIPlus.GdipLoadImageFromFile (filename, out imagePtr);
116 GDIPlus.CheckStatus (st);
118 return CreateFromHandle (imagePtr);
121 public static Bitmap FromHbitmap(IntPtr hbitmap)
123 return FromHbitmap (hbitmap, IntPtr.Zero);
126 public static Bitmap FromHbitmap(IntPtr hbitmap, IntPtr hpalette)
131 st = GDIPlus.GdipCreateBitmapFromHBITMAP (hbitmap, hpalette, out imagePtr);
133 GDIPlus.CheckStatus (st);
134 return new Bitmap (imagePtr);
137 // note: FromStream can return either a Bitmap or Metafile instance
139 public static Image FromStream (Stream stream)
141 return LoadFromStream (stream, false);
144 [MonoLimitation ("useEmbeddedColorManagement isn't supported.")]
145 public static Image FromStream (Stream stream, bool useEmbeddedColorManagement)
147 return LoadFromStream (stream, false);
150 // See http://support.microsoft.com/default.aspx?scid=kb;en-us;831419 for performance discussion
151 [MonoLimitation ("useEmbeddedColorManagement and validateImageData aren't supported.")]
152 public static Image FromStream (Stream stream, bool useEmbeddedColorManagement, bool validateImageData)
154 return LoadFromStream (stream, false);
157 internal static Image LoadFromStream (Stream stream, bool keepAlive)
160 throw new ArgumentNullException ("stream");
162 Image img = CreateFromHandle (InitFromStream (stream));
164 // Under Windows, we may need to keep a reference on the stream as long as the image is alive
165 // (GDI+ seems to use a lazy loader)
166 if (keepAlive && GDIPlus.RunningOnWindows ())
172 internal static Image CreateFromHandle (IntPtr handle)
175 GDIPlus.CheckStatus (GDIPlus.GdipGetImageType (handle, out type));
177 case ImageType.Bitmap:
178 return new Bitmap (handle);
179 case ImageType.Metafile:
180 return new Metafile (handle);
182 throw new NotSupportedException (Locale.GetText ("Unknown image type."));
186 public static int GetPixelFormatSize(PixelFormat pixfmt)
190 case PixelFormat.Format16bppArgb1555:
191 case PixelFormat.Format16bppGrayScale:
192 case PixelFormat.Format16bppRgb555:
193 case PixelFormat.Format16bppRgb565:
196 case PixelFormat.Format1bppIndexed:
199 case PixelFormat.Format24bppRgb:
202 case PixelFormat.Format32bppArgb:
203 case PixelFormat.Format32bppPArgb:
204 case PixelFormat.Format32bppRgb:
207 case PixelFormat.Format48bppRgb:
210 case PixelFormat.Format4bppIndexed:
213 case PixelFormat.Format64bppArgb:
214 case PixelFormat.Format64bppPArgb:
217 case PixelFormat.Format8bppIndexed:
224 public static bool IsAlphaPixelFormat(PixelFormat pixfmt)
228 case PixelFormat.Format16bppArgb1555:
229 case PixelFormat.Format32bppArgb:
230 case PixelFormat.Format32bppPArgb:
231 case PixelFormat.Format64bppArgb:
232 case PixelFormat.Format64bppPArgb:
235 case PixelFormat.Format16bppGrayScale:
236 case PixelFormat.Format16bppRgb555:
237 case PixelFormat.Format16bppRgb565:
238 case PixelFormat.Format1bppIndexed:
239 case PixelFormat.Format24bppRgb:
240 case PixelFormat.Format32bppRgb:
241 case PixelFormat.Format48bppRgb:
242 case PixelFormat.Format4bppIndexed:
243 case PixelFormat.Format8bppIndexed:
250 public static bool IsCanonicalPixelFormat (PixelFormat pixfmt)
252 return ((pixfmt & PixelFormat.Canonical) != 0);
255 public static bool IsExtendedPixelFormat (PixelFormat pixfmt)
257 return ((pixfmt & PixelFormat.Extended) != 0);
260 internal static IntPtr InitFromStream (Stream stream)
263 throw new ArgumentException ("stream");
269 if (!stream.CanSeek) {
270 byte[] buffer = new byte[256];
275 if (buffer.Length < index + 256) {
276 byte[] newBuffer = new byte[buffer.Length * 2];
277 Array.Copy(buffer, newBuffer, buffer.Length);
280 count = stream.Read(buffer, index, 256);
285 stream = new MemoryStream(buffer, 0, index);
288 if (GDIPlus.RunningOnUnix ()) {
289 // Unix, with libgdiplus
290 // We use a custom API for this, because there's no easy way
291 // to get the Stream down to libgdiplus. So, we wrap the stream
292 // with a set of delegates.
293 GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream, true);
295 st = GDIPlus.GdipLoadImageFromDelegate_linux (sh.GetHeaderDelegate, sh.GetBytesDelegate,
296 sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, out imagePtr);
298 st = GDIPlus.GdipLoadImageFromStream (new ComIStreamWrapper (stream), out imagePtr);
301 return st == Status.Ok ? imagePtr : IntPtr.Zero;
305 public RectangleF GetBounds (ref GraphicsUnit pageUnit)
309 Status status = GDIPlus.GdipGetImageBounds (nativeObject, out source, ref pageUnit);
310 GDIPlus.CheckStatus (status);
315 public EncoderParameters GetEncoderParameterList(Guid encoder)
320 status = GDIPlus.GdipGetEncoderParameterListSize (nativeObject, ref encoder, out sz);
321 GDIPlus.CheckStatus (status);
323 IntPtr rawEPList = Marshal.AllocHGlobal ((int) sz);
324 EncoderParameters eps;
327 status = GDIPlus.GdipGetEncoderParameterList (nativeObject, ref encoder, sz, rawEPList);
328 eps = EncoderParameters.FromNativePtr (rawEPList);
329 GDIPlus.CheckStatus (status);
332 Marshal.FreeHGlobal (rawEPList);
338 public int GetFrameCount (FrameDimension dimension)
341 Guid guid = dimension.Guid;
343 Status status = GDIPlus.GdipImageGetFrameCount (nativeObject, ref guid, out count);
344 GDIPlus.CheckStatus (status);
349 public PropertyItem GetPropertyItem(int propid)
353 PropertyItem item = new PropertyItem ();
354 GdipPropertyItem gdipProperty = new GdipPropertyItem ();
357 status = GDIPlus.GdipGetPropertyItemSize (nativeObject, propid,
359 GDIPlus.CheckStatus (status);
361 /* Get PropertyItem */
362 property = Marshal.AllocHGlobal (propSize);
364 status = GDIPlus.GdipGetPropertyItem (nativeObject, propid, propSize, property);
365 GDIPlus.CheckStatus (status);
366 gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure (property,
367 typeof (GdipPropertyItem));
368 GdipPropertyItem.MarshalTo (gdipProperty, item);
371 Marshal.FreeHGlobal (property);
376 public Image GetThumbnailImage (int thumbWidth, int thumbHeight, Image.GetThumbnailImageAbort callback, IntPtr callbackData)
378 if ((thumbWidth <= 0) || (thumbHeight <= 0))
379 throw new OutOfMemoryException ("Invalid thumbnail size");
381 Image ThumbNail = new Bitmap (thumbWidth, thumbHeight);
383 using (Graphics g = Graphics.FromImage (ThumbNail)) {
384 Status status = GDIPlus.GdipDrawImageRectRectI (g.nativeObject, nativeObject,
385 0, 0, thumbWidth, thumbHeight,
386 0, 0, this.Width, this.Height,
387 GraphicsUnit.Pixel, IntPtr.Zero, null, IntPtr.Zero);
389 GDIPlus.CheckStatus (status);
396 public void RemovePropertyItem (int propid)
398 Status status = GDIPlus.GdipRemovePropertyItem (nativeObject, propid);
399 GDIPlus.CheckStatus (status);
402 public void RotateFlip (RotateFlipType rotateFlipType)
404 Status status = GDIPlus.GdipImageRotateFlip (nativeObject, rotateFlipType);
405 GDIPlus.CheckStatus (status);
408 internal ImageCodecInfo findEncoderForFormat (ImageFormat format)
410 ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
411 ImageCodecInfo encoder = null;
413 if (format.Guid.Equals (ImageFormat.MemoryBmp.Guid))
414 format = ImageFormat.Png;
416 /* Look for the right encoder for our format*/
417 for (int i = 0; i < encoders.Length; i++) {
418 if (encoders[i].FormatID.Equals (format.Guid)) {
419 encoder = encoders[i];
427 public void Save (string filename)
429 Save (filename, RawFormat);
432 public void Save(string filename, ImageFormat format)
434 ImageCodecInfo encoder = findEncoderForFormat (format);
435 if (encoder == null) {
437 encoder = findEncoderForFormat (RawFormat);
438 if (encoder == null) {
439 string msg = Locale.GetText ("No codec available for saving format '{0}'.", format.Guid);
440 throw new ArgumentException (msg, "format");
443 Save (filename, encoder, null);
446 public void Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
449 Guid guid = encoder.Clsid;
451 if (encoderParams == null) {
452 st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, IntPtr.Zero);
454 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
455 st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, nativeEncoderParams);
456 Marshal.FreeHGlobal (nativeEncoderParams);
459 GDIPlus.CheckStatus (st);
462 public void Save (Stream stream, ImageFormat format)
464 ImageCodecInfo encoder = findEncoderForFormat (format);
467 throw new ArgumentException ("No codec available for format:" + format.Guid);
469 Save (stream, encoder, null);
472 public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
475 IntPtr nativeEncoderParams;
476 Guid guid = encoder.Clsid;
478 if (encoderParams == null)
479 nativeEncoderParams = IntPtr.Zero;
481 nativeEncoderParams = encoderParams.ToNativePtr ();
484 if (GDIPlus.RunningOnUnix ()) {
485 GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream, false);
486 st = GDIPlus.GdipSaveImageToDelegate_linux (nativeObject, sh.GetBytesDelegate, sh.PutBytesDelegate,
487 sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, ref guid, nativeEncoderParams);
489 st = GDIPlus.GdipSaveImageToStream (new HandleRef (this, nativeObject),
490 new ComIStreamWrapper (stream), ref guid, new HandleRef (encoderParams, nativeEncoderParams));
494 if (nativeEncoderParams != IntPtr.Zero)
495 Marshal.FreeHGlobal (nativeEncoderParams);
498 GDIPlus.CheckStatus (st);
501 public void SaveAdd (EncoderParameters encoderParams)
505 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
506 st = GDIPlus.GdipSaveAdd (nativeObject, nativeEncoderParams);
507 Marshal.FreeHGlobal (nativeEncoderParams);
508 GDIPlus.CheckStatus (st);
511 public void SaveAdd (Image image, EncoderParameters encoderParams)
515 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
516 st = GDIPlus.GdipSaveAddImage (nativeObject, image.NativeObject, nativeEncoderParams);
517 Marshal.FreeHGlobal (nativeEncoderParams);
518 GDIPlus.CheckStatus (st);
521 public int SelectActiveFrame(FrameDimension dimension, int frameIndex)
523 Guid guid = dimension.Guid;
524 Status st = GDIPlus.GdipImageSelectActiveFrame (nativeObject, ref guid, frameIndex);
526 GDIPlus.CheckStatus (st);
531 public void SetPropertyItem(PropertyItem propitem)
533 if (propitem == null)
534 throw new ArgumentNullException ("propitem");
536 int nItemSize = Marshal.SizeOf (propitem.Value[0]);
537 int size = nItemSize * propitem.Value.Length;
538 IntPtr dest = Marshal.AllocHGlobal (size);
540 GdipPropertyItem pi = new GdipPropertyItem ();
542 pi.len = propitem.Len;
543 pi.type = propitem.Type;
545 Marshal.Copy (propitem.Value, 0, dest, size);
549 Status status = GDIPlus.GdipSetPropertyItem (nativeObject, &pi);
551 GDIPlus.CheckStatus (status);
555 Marshal.FreeHGlobal (dest);
565 Status status = GDIPlus.GdipGetImageFlags (nativeObject, out flags);
566 GDIPlus.CheckStatus (status);
572 public Guid[] FrameDimensionsList {
575 Status status = GDIPlus.GdipImageGetFrameDimensionsCount (nativeObject, out found);
576 GDIPlus.CheckStatus (status);
577 Guid [] guid = new Guid [found];
578 status = GDIPlus.GdipImageGetFrameDimensionsList (nativeObject, guid, found);
579 GDIPlus.CheckStatus (status);
584 [DefaultValue (false)]
586 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
590 Status status = GDIPlus.GdipGetImageHeight (nativeObject, out height);
591 GDIPlus.CheckStatus (status);
597 public float HorizontalResolution {
601 Status status = GDIPlus.GdipGetImageHorizontalResolution (nativeObject, out resolution);
602 GDIPlus.CheckStatus (status);
609 public ColorPalette Palette {
611 return retrieveGDIPalette();
614 storeGDIPalette(value);
618 internal ColorPalette retrieveGDIPalette()
621 ColorPalette ret = new ColorPalette ();
623 Status st = GDIPlus.GdipGetImagePaletteSize (nativeObject, out bytes);
624 GDIPlus.CheckStatus (st);
625 IntPtr palette_data = Marshal.AllocHGlobal (bytes);
627 st = GDIPlus.GdipGetImagePalette (nativeObject, palette_data, bytes);
628 GDIPlus.CheckStatus (st);
629 ret.setFromGDIPalette (palette_data);
634 Marshal.FreeHGlobal (palette_data);
638 internal void storeGDIPalette(ColorPalette palette)
640 if (palette == null) {
641 throw new ArgumentNullException("palette");
643 IntPtr palette_data = palette.getGDIPalette();
644 if (palette_data == IntPtr.Zero) {
649 Status st = GDIPlus.GdipSetImagePalette (nativeObject, palette_data);
650 GDIPlus.CheckStatus (st);
654 Marshal.FreeHGlobal(palette_data);
659 public SizeF PhysicalDimension {
662 Status status = GDIPlus.GdipGetImageDimension (nativeObject, out width, out height);
663 GDIPlus.CheckStatus (status);
665 return new SizeF (width, height);
669 public PixelFormat PixelFormat {
671 PixelFormat pixFormat;
672 Status status = GDIPlus.GdipGetImagePixelFormat (nativeObject, out pixFormat);
673 GDIPlus.CheckStatus (status);
680 public int[] PropertyIdList {
684 Status status = GDIPlus.GdipGetPropertyCount (nativeObject,
686 GDIPlus.CheckStatus (status);
688 int [] idList = new int [propNumbers];
689 status = GDIPlus.GdipGetPropertyIdList (nativeObject,
690 propNumbers, idList);
691 GDIPlus.CheckStatus (status);
698 public PropertyItem[] PropertyItems {
700 int propNums, propsSize, propSize;
701 IntPtr properties, propPtr;
702 PropertyItem[] items;
703 GdipPropertyItem gdipProperty = new GdipPropertyItem ();
706 status = GDIPlus.GdipGetPropertySize (nativeObject, out propsSize, out propNums);
707 GDIPlus.CheckStatus (status);
709 items = new PropertyItem [propNums];
714 /* Get PropertyItem list*/
715 properties = Marshal.AllocHGlobal (propsSize * propNums);
717 status = GDIPlus.GdipGetAllPropertyItems (nativeObject, propsSize,
718 propNums, properties);
719 GDIPlus.CheckStatus (status);
721 propSize = Marshal.SizeOf (gdipProperty);
722 propPtr = properties;
724 for (int i = 0; i < propNums; i++, propPtr = new IntPtr (propPtr.ToInt64 () + propSize)) {
725 gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure
726 (propPtr, typeof (GdipPropertyItem));
727 items [i] = new PropertyItem ();
728 GdipPropertyItem.MarshalTo (gdipProperty, items [i]);
732 Marshal.FreeHGlobal (properties);
738 public ImageFormat RawFormat {
741 Status st = GDIPlus.GdipGetImageRawFormat (nativeObject, out guid);
743 GDIPlus.CheckStatus (st);
744 return new ImageFormat (guid);
750 return new Size(Width, Height);
754 [DefaultValue (null)]
755 [LocalizableAttribute(false)]
756 [BindableAttribute(true)]
757 [TypeConverter (typeof (StringConverter))]
762 public float VerticalResolution {
766 Status status = GDIPlus.GdipGetImageVerticalResolution (nativeObject, out resolution);
767 GDIPlus.CheckStatus (status);
773 [DefaultValue (false)]
775 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
779 Status status = GDIPlus.GdipGetImageWidth (nativeObject, out width);
780 GDIPlus.CheckStatus (status);
786 internal IntPtr NativeObject{
791 nativeObject = value;
795 public void Dispose ()
798 GC.SuppressFinalize (this);
806 protected virtual void Dispose (bool disposing)
808 if (GDIPlus.GdiPlusToken != 0 && nativeObject != IntPtr.Zero) {
809 Status status = GDIPlus.GdipDisposeImage (nativeObject);
810 // dispose the stream (set under Win32 only if SD owns the stream) and ...
811 if (stream != null) {
815 // ... set nativeObject to null before (possibly) throwing an exception
816 nativeObject = IntPtr.Zero;
817 GDIPlus.CheckStatus (status);
821 public object Clone ()
823 if (GDIPlus.RunningOnWindows () && stream != null)
824 return CloneFromStream ();
826 IntPtr newimage = IntPtr.Zero;
827 Status status = GDIPlus.GdipCloneImage (NativeObject, out newimage);
828 GDIPlus.CheckStatus (status);
831 return new Bitmap (newimage);
833 return new Metafile (newimage);
836 // On win32, when cloning images that were originally created from a stream, we need to
837 // clone both the image and the stream to make sure the gc doesn't kill it
838 // (when using MS GDI+ and IStream we must ensure the stream stays alive for all the life of the Image)
839 object CloneFromStream ()
841 byte[] bytes = new byte [stream.Length];
842 MemoryStream ms = new MemoryStream (bytes);
843 int count = (stream.Length < 4096 ? (int) stream.Length : 4096);
844 byte[] buffer = new byte[count];
847 count = stream.Read (buffer, 0, count);
848 ms.Write (buffer, 0, count);
849 } while (count == 4096);
851 IntPtr newimage = IntPtr.Zero;
852 newimage = InitFromStream (ms);
855 return new Bitmap (newimage, ms);
857 return new Metafile (newimage, ms);