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.Serialization;
36 using System.Runtime.InteropServices;
37 using System.ComponentModel;
38 using System.Drawing.Imaging;
40 using System.Reflection;
42 namespace System.Drawing
46 [Editor ("System.Drawing.Design.ImageEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))]
47 [TypeConverter (typeof(ImageConverter))]
48 [ImmutableObject (true)]
49 public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISerializable
51 public delegate bool GetThumbnailImageAbort();
54 internal IntPtr nativeObject = IntPtr.Zero;
55 // when using MS GDI+ and IStream we must ensure the stream stays alive for all the life of the Image
56 // 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
57 internal Stream stream;
65 internal Image (SerializationInfo info, StreamingContext context)
67 foreach (SerializationEntry serEnum in info) {
68 if (String.Compare(serEnum.Name, "Data", true) == 0) {
69 byte[] bytes = (byte[]) serEnum.Value;
72 MemoryStream ms = new MemoryStream (bytes);
73 nativeObject = InitFromStream (ms);
74 // under Win32 stream is owned by SD/GDI+ code
75 if (GDIPlus.RunningOnWindows ())
82 // FIXME - find out how metafiles (another decoder-only codec) are handled
83 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext context)
85 using (MemoryStream ms = new MemoryStream ()) {
86 // Icon is a decoder-only codec
87 if (RawFormat.Equals (ImageFormat.Icon)) {
88 Save (ms, ImageFormat.Png);
92 si.AddValue ("Data", ms.ToArray ());
98 public static Image FromFile(string filename)
100 return FromFile (filename, false);
103 public static Image FromFile(string filename, bool useEmbeddedColorManagement)
108 if (!File.Exists (filename))
109 throw new FileNotFoundException (filename);
111 if (useEmbeddedColorManagement)
112 st = GDIPlus.GdipLoadImageFromFileICM (filename, out imagePtr);
114 st = GDIPlus.GdipLoadImageFromFile (filename, out imagePtr);
115 GDIPlus.CheckStatus (st);
117 return CreateFromHandle (imagePtr);
120 public static Bitmap FromHbitmap(IntPtr hbitmap)
122 return FromHbitmap (hbitmap, IntPtr.Zero);
125 public static Bitmap FromHbitmap(IntPtr hbitmap, IntPtr hpalette)
130 st = GDIPlus.GdipCreateBitmapFromHBITMAP (hbitmap, hpalette, out imagePtr);
132 GDIPlus.CheckStatus (st);
133 return new Bitmap (imagePtr);
136 // note: FromStream can return either a Bitmap or Metafile instance
138 public static Image FromStream (Stream stream)
140 return LoadFromStream (stream, false);
143 [MonoLimitation ("useEmbeddedColorManagement isn't supported.")]
144 public static Image FromStream (Stream stream, bool useEmbeddedColorManagement)
146 return LoadFromStream (stream, false);
149 // See http://support.microsoft.com/default.aspx?scid=kb;en-us;831419 for performance discussion
150 [MonoLimitation ("useEmbeddedColorManagement and validateImageData aren't supported.")]
151 public static Image FromStream (Stream stream, bool useEmbeddedColorManagement, bool validateImageData)
153 return LoadFromStream (stream, false);
156 internal static Image LoadFromStream (Stream stream, bool keepAlive)
159 throw new ArgumentNullException ("stream");
161 Image img = CreateFromHandle (InitFromStream (stream));
163 // Under Windows, we may need to keep a reference on the stream as long as the image is alive
164 // (GDI+ seems to use a lazy loader)
165 if (keepAlive && GDIPlus.RunningOnWindows ())
171 internal static Image CreateFromHandle (IntPtr handle)
174 GDIPlus.CheckStatus (GDIPlus.GdipGetImageType (handle, out type));
176 case ImageType.Bitmap:
177 return new Bitmap (handle);
178 case ImageType.Metafile:
179 return new Metafile (handle);
181 throw new NotSupportedException (Locale.GetText ("Unknown image type."));
185 public static int GetPixelFormatSize(PixelFormat pixfmt)
189 case PixelFormat.Format16bppArgb1555:
190 case PixelFormat.Format16bppGrayScale:
191 case PixelFormat.Format16bppRgb555:
192 case PixelFormat.Format16bppRgb565:
195 case PixelFormat.Format1bppIndexed:
198 case PixelFormat.Format24bppRgb:
201 case PixelFormat.Format32bppArgb:
202 case PixelFormat.Format32bppPArgb:
203 case PixelFormat.Format32bppRgb:
206 case PixelFormat.Format48bppRgb:
209 case PixelFormat.Format4bppIndexed:
212 case PixelFormat.Format64bppArgb:
213 case PixelFormat.Format64bppPArgb:
216 case PixelFormat.Format8bppIndexed:
223 public static bool IsAlphaPixelFormat(PixelFormat pixfmt)
227 case PixelFormat.Format16bppArgb1555:
228 case PixelFormat.Format32bppArgb:
229 case PixelFormat.Format32bppPArgb:
230 case PixelFormat.Format64bppArgb:
231 case PixelFormat.Format64bppPArgb:
234 case PixelFormat.Format16bppGrayScale:
235 case PixelFormat.Format16bppRgb555:
236 case PixelFormat.Format16bppRgb565:
237 case PixelFormat.Format1bppIndexed:
238 case PixelFormat.Format24bppRgb:
239 case PixelFormat.Format32bppRgb:
240 case PixelFormat.Format48bppRgb:
241 case PixelFormat.Format4bppIndexed:
242 case PixelFormat.Format8bppIndexed:
249 public static bool IsCanonicalPixelFormat (PixelFormat pixfmt)
251 return ((pixfmt & PixelFormat.Canonical) != 0);
254 public static bool IsExtendedPixelFormat (PixelFormat pixfmt)
256 return ((pixfmt & PixelFormat.Extended) != 0);
259 internal static IntPtr InitFromStream (Stream stream)
262 throw new ArgumentException ("stream");
268 if (!stream.CanSeek) {
269 byte[] buffer = new byte[256];
274 if (buffer.Length < index + 256) {
275 byte[] newBuffer = new byte[buffer.Length * 2];
276 Array.Copy(buffer, newBuffer, buffer.Length);
279 count = stream.Read(buffer, index, 256);
284 stream = new MemoryStream(buffer, 0, index);
287 if (GDIPlus.RunningOnUnix ()) {
288 // Unix, with libgdiplus
289 // We use a custom API for this, because there's no easy way
290 // to get the Stream down to libgdiplus. So, we wrap the stream
291 // with a set of delegates.
292 GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream, true);
294 st = GDIPlus.GdipLoadImageFromDelegate_linux (sh.GetHeaderDelegate, sh.GetBytesDelegate,
295 sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, out imagePtr);
297 st = GDIPlus.GdipLoadImageFromStream (new ComIStreamWrapper (stream), out imagePtr);
300 return st == Status.Ok ? imagePtr : IntPtr.Zero;
304 public RectangleF GetBounds (ref GraphicsUnit pageUnit)
308 Status status = GDIPlus.GdipGetImageBounds (nativeObject, out source, ref pageUnit);
309 GDIPlus.CheckStatus (status);
314 public EncoderParameters GetEncoderParameterList(Guid encoder)
319 status = GDIPlus.GdipGetEncoderParameterListSize (nativeObject, ref encoder, out sz);
320 GDIPlus.CheckStatus (status);
322 IntPtr rawEPList = Marshal.AllocHGlobal ((int) sz);
323 EncoderParameters eps;
326 status = GDIPlus.GdipGetEncoderParameterList (nativeObject, ref encoder, sz, rawEPList);
327 eps = EncoderParameters.FromNativePtr (rawEPList);
328 GDIPlus.CheckStatus (status);
331 Marshal.FreeHGlobal (rawEPList);
337 public int GetFrameCount (FrameDimension dimension)
340 Guid guid = dimension.Guid;
342 Status status = GDIPlus.GdipImageGetFrameCount (nativeObject, ref guid, out count);
343 GDIPlus.CheckStatus (status);
348 public PropertyItem GetPropertyItem(int propid)
352 PropertyItem item = new PropertyItem ();
353 GdipPropertyItem gdipProperty = new GdipPropertyItem ();
356 status = GDIPlus.GdipGetPropertyItemSize (nativeObject, propid,
358 GDIPlus.CheckStatus (status);
360 /* Get PropertyItem */
361 property = Marshal.AllocHGlobal (propSize);
363 status = GDIPlus.GdipGetPropertyItem (nativeObject, propid, propSize, property);
364 GDIPlus.CheckStatus (status);
365 gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure (property,
366 typeof (GdipPropertyItem));
367 GdipPropertyItem.MarshalTo (gdipProperty, item);
370 Marshal.FreeHGlobal (property);
375 public Image GetThumbnailImage (int thumbWidth, int thumbHeight, Image.GetThumbnailImageAbort callback, IntPtr callbackData)
377 if ((thumbWidth <= 0) || (thumbHeight <= 0))
378 throw new OutOfMemoryException ("Invalid thumbnail size");
380 Image ThumbNail = new Bitmap (thumbWidth, thumbHeight);
382 using (Graphics g = Graphics.FromImage (ThumbNail)) {
383 Status status = GDIPlus.GdipDrawImageRectRectI (g.nativeObject, nativeObject,
384 0, 0, thumbWidth, thumbHeight,
385 0, 0, this.Width, this.Height,
386 GraphicsUnit.Pixel, IntPtr.Zero, null, IntPtr.Zero);
388 GDIPlus.CheckStatus (status);
395 public void RemovePropertyItem (int propid)
397 Status status = GDIPlus.GdipRemovePropertyItem (nativeObject, propid);
398 GDIPlus.CheckStatus (status);
401 public void RotateFlip (RotateFlipType rotateFlipType)
403 Status status = GDIPlus.GdipImageRotateFlip (nativeObject, rotateFlipType);
404 GDIPlus.CheckStatus (status);
407 internal ImageCodecInfo findEncoderForFormat (ImageFormat format)
409 ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
410 ImageCodecInfo encoder = null;
412 if (format.Guid.Equals (ImageFormat.MemoryBmp.Guid))
413 format = ImageFormat.Png;
415 /* Look for the right encoder for our format*/
416 for (int i = 0; i < encoders.Length; i++) {
417 if (encoders[i].FormatID.Equals (format.Guid)) {
418 encoder = encoders[i];
426 public void Save (string filename)
428 Save (filename, RawFormat);
431 public void Save(string filename, ImageFormat format)
433 ImageCodecInfo encoder = findEncoderForFormat (format);
434 if (encoder == null) {
436 encoder = findEncoderForFormat (RawFormat);
437 if (encoder == null) {
438 string msg = Locale.GetText ("No codec available for saving format '{0}'.", format.Guid);
439 throw new ArgumentException (msg, "format");
442 Save (filename, encoder, null);
445 public void Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
448 Guid guid = encoder.Clsid;
450 if (encoderParams == null) {
451 st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, IntPtr.Zero);
453 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
454 st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, nativeEncoderParams);
455 Marshal.FreeHGlobal (nativeEncoderParams);
458 GDIPlus.CheckStatus (st);
461 public void Save (Stream stream, ImageFormat format)
463 ImageCodecInfo encoder = findEncoderForFormat (format);
466 throw new ArgumentException ("No codec available for format:" + format.Guid);
468 Save (stream, encoder, null);
471 public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
474 IntPtr nativeEncoderParams;
475 Guid guid = encoder.Clsid;
477 if (encoderParams == null)
478 nativeEncoderParams = IntPtr.Zero;
480 nativeEncoderParams = encoderParams.ToNativePtr ();
483 if (GDIPlus.RunningOnUnix ()) {
484 GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream, false);
485 st = GDIPlus.GdipSaveImageToDelegate_linux (nativeObject, sh.GetBytesDelegate, sh.PutBytesDelegate,
486 sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, ref guid, nativeEncoderParams);
488 st = GDIPlus.GdipSaveImageToStream (new HandleRef (this, nativeObject),
489 new ComIStreamWrapper (stream), ref guid, new HandleRef (encoderParams, nativeEncoderParams));
493 if (nativeEncoderParams != IntPtr.Zero)
494 Marshal.FreeHGlobal (nativeEncoderParams);
497 GDIPlus.CheckStatus (st);
500 public void SaveAdd (EncoderParameters encoderParams)
504 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
505 st = GDIPlus.GdipSaveAdd (nativeObject, nativeEncoderParams);
506 Marshal.FreeHGlobal (nativeEncoderParams);
507 GDIPlus.CheckStatus (st);
510 public void SaveAdd (Image image, EncoderParameters encoderParams)
514 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
515 st = GDIPlus.GdipSaveAddImage (nativeObject, image.NativeObject, nativeEncoderParams);
516 Marshal.FreeHGlobal (nativeEncoderParams);
517 GDIPlus.CheckStatus (st);
520 public int SelectActiveFrame(FrameDimension dimension, int frameIndex)
522 Guid guid = dimension.Guid;
523 Status st = GDIPlus.GdipImageSelectActiveFrame (nativeObject, ref guid, frameIndex);
525 GDIPlus.CheckStatus (st);
530 public void SetPropertyItem(PropertyItem propitem)
532 throw new NotImplementedException ();
534 GdipPropertyItem pi = new GdipPropertyItem ();
535 GdipPropertyItem.MarshalTo (pi, propitem);
537 Status status = GDIPlus.GdipSetPropertyItem (nativeObject, &pi);
539 GDIPlus.CheckStatus (status);
550 Status status = GDIPlus.GdipGetImageFlags (nativeObject, out flags);
551 GDIPlus.CheckStatus (status);
557 public Guid[] FrameDimensionsList {
560 Status status = GDIPlus.GdipImageGetFrameDimensionsCount (nativeObject, out found);
561 GDIPlus.CheckStatus (status);
562 Guid [] guid = new Guid [found];
563 status = GDIPlus.GdipImageGetFrameDimensionsList (nativeObject, guid, found);
564 GDIPlus.CheckStatus (status);
569 [DefaultValue (false)]
571 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
575 Status status = GDIPlus.GdipGetImageHeight (nativeObject, out height);
576 GDIPlus.CheckStatus (status);
582 public float HorizontalResolution {
586 Status status = GDIPlus.GdipGetImageHorizontalResolution (nativeObject, out resolution);
587 GDIPlus.CheckStatus (status);
594 public ColorPalette Palette {
596 return retrieveGDIPalette();
599 storeGDIPalette(value);
603 internal ColorPalette retrieveGDIPalette()
606 ColorPalette ret = new ColorPalette ();
608 Status st = GDIPlus.GdipGetImagePaletteSize (nativeObject, out bytes);
609 GDIPlus.CheckStatus (st);
610 IntPtr palette_data = Marshal.AllocHGlobal (bytes);
612 st = GDIPlus.GdipGetImagePalette (nativeObject, palette_data, bytes);
613 GDIPlus.CheckStatus (st);
614 ret.setFromGDIPalette (palette_data);
619 Marshal.FreeHGlobal (palette_data);
623 internal void storeGDIPalette(ColorPalette palette)
625 if (palette == null) {
626 throw new ArgumentNullException("palette");
628 IntPtr palette_data = palette.getGDIPalette();
629 if (palette_data == IntPtr.Zero) {
634 Status st = GDIPlus.GdipSetImagePalette (nativeObject, palette_data);
635 GDIPlus.CheckStatus (st);
639 Marshal.FreeHGlobal(palette_data);
644 public SizeF PhysicalDimension {
647 Status status = GDIPlus.GdipGetImageDimension (nativeObject, out width, out height);
648 GDIPlus.CheckStatus (status);
650 return new SizeF (width, height);
654 public PixelFormat PixelFormat {
656 PixelFormat pixFormat;
657 Status status = GDIPlus.GdipGetImagePixelFormat (nativeObject, out pixFormat);
658 GDIPlus.CheckStatus (status);
665 public int[] PropertyIdList {
669 Status status = GDIPlus.GdipGetPropertyCount (nativeObject,
671 GDIPlus.CheckStatus (status);
673 int [] idList = new int [propNumbers];
674 status = GDIPlus.GdipGetPropertyIdList (nativeObject,
675 propNumbers, idList);
676 GDIPlus.CheckStatus (status);
683 public PropertyItem[] PropertyItems {
685 int propNums, propsSize, propSize;
686 IntPtr properties, propPtr;
687 PropertyItem[] items;
688 GdipPropertyItem gdipProperty = new GdipPropertyItem ();
691 status = GDIPlus.GdipGetPropertySize (nativeObject, out propsSize, out propNums);
692 GDIPlus.CheckStatus (status);
694 items = new PropertyItem [propNums];
699 /* Get PropertyItem list*/
700 properties = Marshal.AllocHGlobal (propsSize * propNums);
702 status = GDIPlus.GdipGetAllPropertyItems (nativeObject, propsSize,
703 propNums, properties);
704 GDIPlus.CheckStatus (status);
706 propSize = Marshal.SizeOf (gdipProperty);
707 propPtr = properties;
709 for (int i = 0; i < propNums; i++, propPtr = new IntPtr (propPtr.ToInt64 () + propSize)) {
710 gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure
711 (propPtr, typeof (GdipPropertyItem));
712 items [i] = new PropertyItem ();
713 GdipPropertyItem.MarshalTo (gdipProperty, items [i]);
717 Marshal.FreeHGlobal (properties);
723 public ImageFormat RawFormat {
726 Status st = GDIPlus.GdipGetImageRawFormat (nativeObject, out guid);
728 GDIPlus.CheckStatus (st);
729 return new ImageFormat (guid);
735 return new Size(Width, Height);
739 [DefaultValue (null)]
740 [LocalizableAttribute(false)]
741 [BindableAttribute(true)]
742 [TypeConverter (typeof (StringConverter))]
747 public float VerticalResolution {
751 Status status = GDIPlus.GdipGetImageVerticalResolution (nativeObject, out resolution);
752 GDIPlus.CheckStatus (status);
758 [DefaultValue (false)]
760 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
764 Status status = GDIPlus.GdipGetImageWidth (nativeObject, out width);
765 GDIPlus.CheckStatus (status);
771 internal IntPtr NativeObject{
776 nativeObject = value;
780 public void Dispose ()
783 GC.SuppressFinalize (this);
791 protected virtual void Dispose (bool disposing)
793 if (GDIPlus.GdiPlusToken != 0 && nativeObject != IntPtr.Zero) {
794 Status status = GDIPlus.GdipDisposeImage (nativeObject);
795 // dispose the stream (set under Win32 only if SD owns the stream) and ...
796 if (stream != null) {
800 // ... set nativeObject to null before (possibly) throwing an exception
801 nativeObject = IntPtr.Zero;
802 GDIPlus.CheckStatus (status);
806 public object Clone ()
808 if (GDIPlus.RunningOnWindows () && stream != null)
809 return CloneFromStream ();
811 IntPtr newimage = IntPtr.Zero;
812 Status status = GDIPlus.GdipCloneImage (NativeObject, out newimage);
813 GDIPlus.CheckStatus (status);
816 return new Bitmap (newimage);
818 return new Metafile (newimage);
821 // On win32, when cloning images that were originally created from a stream, we need to
822 // clone both the image and the stream to make sure the gc doesn't kill it
823 // (when using MS GDI+ and IStream we must ensure the stream stays alive for all the life of the Image)
824 object CloneFromStream ()
826 byte[] bytes = new byte [stream.Length];
827 MemoryStream ms = new MemoryStream (bytes);
828 int count = (stream.Length < 4096 ? (int) stream.Length : 4096);
829 byte[] buffer = new byte[count];
832 count = stream.Read (buffer, 0, count);
833 ms.Write (buffer, 0, count);
834 } while (count == 4096);
836 IntPtr newimage = IntPtr.Zero;
837 newimage = InitFromStream (ms);
840 return new Bitmap (newimage, ms);
842 return new Metafile (newimage, ms);