2006-08-07 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / System.Drawing / System.Drawing / Image.cs
index bdd9a91adc9bcb894504d3d693b2f515457c796b..a2371aa8159395eaee69506ada4912ab9dfcec83 100644 (file)
 //             Ravindra (rkumar@novell.com)
 //
 
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.Runtime.Remoting;
 using System.Runtime.Serialization;
@@ -18,6 +41,7 @@ using System.Runtime.InteropServices;
 using System.ComponentModel;
 using System.Drawing.Imaging;
 using System.IO;
+using System.Reflection;
 
 namespace System.Drawing
 {
@@ -29,15 +53,15 @@ namespace System.Drawing
 public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISerializable 
 {
        public delegate bool GetThumbnailImageAbort();
+       private object tag;
        
        internal IntPtr nativeObject = IntPtr.Zero;     
-       protected ColorPalette colorPalette;
        
        
        // constructor
        internal  Image()
-       {               
-               colorPalette = new ColorPalette();
+       {       
+               
        }
        
        private Image (SerializationInfo info, StreamingContext context)
@@ -52,35 +76,58 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                        }
                }
        }
+
+       private static bool IsIndexedPixelFormat(PixelFormat pixfmt)
+       {
+               return ((pixfmt & PixelFormat.Indexed) != 0);
+       }
+
        
-       [MonoTODO]      
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
-               throw new NotImplementedException();
+               MemoryStream ms = new MemoryStream ();
+               this.Save (ms, RawFormat);
+               info.AddValue ("Data", ms.ToArray ());
        }
     
        // public methods
        // static
        public static Image FromFile(string filename)
        {
-               return new Bitmap (filename);
+               return FromFile (filename, false);
        }
        
        public static Image FromFile(string filename, bool useEmbeddedColorManagement)
        {
-               return new Bitmap (filename, useEmbeddedColorManagement);
+               IntPtr imagePtr;
+               Status st;
+
+               if (!File.Exists (filename))
+                       throw new FileNotFoundException (filename);
+
+               if (useEmbeddedColorManagement)
+                       st = GDIPlus.GdipLoadImageFromFileICM (filename, out imagePtr);
+               else
+                       st = GDIPlus.GdipLoadImageFromFile (filename, out imagePtr);
+
+               GDIPlus.CheckStatus (st);
+               return new Bitmap (imagePtr);
        }
 
-       [MonoTODO]      
        public static Bitmap FromHbitmap(IntPtr hbitmap)
-       {               
-               throw new NotImplementedException ();
+       {
+               return FromHbitmap (hbitmap, IntPtr.Zero);
        }
 
-       [MonoTODO]      
        public static Bitmap FromHbitmap(IntPtr hbitmap, IntPtr hpalette)
        {               
-               throw new NotImplementedException ();
+               IntPtr imagePtr;
+               Status st;
+
+               st = GDIPlus.GdipCreateBitmapFromHBITMAP (hbitmap, hpalette, out imagePtr);
+
+               GDIPlus.CheckStatus (st);
+               return new Bitmap (imagePtr);
        }
 
        public static Image FromStream (Stream stream)
@@ -93,6 +140,12 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                return new Bitmap (stream, useECM);
        }
 
+       // See http://support.microsoft.com/default.aspx?scid=kb;en-us;831419 for performance discussion        
+       public static Image FromStream (Stream stream, bool useECM, bool validateImageData)
+       {
+               return new Bitmap (stream, useECM);
+       }
+
        public static int GetPixelFormatSize(PixelFormat pixfmt)
        {
                int result = 0;
@@ -167,30 +220,50 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                return ((pixfmt & PixelFormat.Extended) != 0);
        }
 
-       internal protected void InitFromStream (Stream stream)
+       internal void InitFromStream (Stream stream)
        {
-               if (Environment.OSVersion.Platform == (PlatformID) 128) {
+               IntPtr imagePtr;
+               Status st;
+               
+               // Seeking required
+               if (!stream.CanSeek) {
+                       byte[] buffer = new byte[256];
+                       int index = 0;
+                       int count;
+
+                       do {
+                               if (buffer.Length < index + 256) {
+                                       byte[] newBuffer = new byte[buffer.Length * 2];
+                                       Array.Copy(buffer, newBuffer, buffer.Length);
+                                       buffer = newBuffer;
+                               }
+                               count = stream.Read(buffer, index, 256);
+                               index += count;
+                       }
+                       while (count != 0);
+
+                       stream = new MemoryStream(buffer, 0, index);
+               }
+
+               // check for Unix platforms - see FAQ for more details
+               // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F
+               int platform = (int) Environment.OSVersion.Platform;
+               if ((platform == 4) || (platform == 128)) {
                        // Unix, with libgdiplus
                        // We use a custom API for this, because there's no easy way
                        // to get the Stream down to libgdiplus.  So, we wrap the stream
                        // with a set of delegates.
                        GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream);
-                       IntPtr imagePtr;
 
-                       Status st = GDIPlus.GdipLoadImageFromDelegate_linux (sh.GetBytesDelegate,
-                                                                                    sh.SeekDelegate,
-                                                                                    out imagePtr);
-                       GDIPlus.CheckStatus (st);
-                       nativeObject = imagePtr;
+                       st = GDIPlus.GdipLoadImageFromDelegate_linux (sh.GetHeaderDelegate, sh.GetBytesDelegate,
+                               sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, out imagePtr);
                } else {
                        // this is MS-land
-                       // FIXME
-                       // We can't call the native gdip functions here, because they expect
-                       // a COM IStream interface.  So, a hack is to create a tmp file, read
-                       // the stream, and then load from the tmp file.
-                       // This is an ugly hack.
-                       throw new NotImplementedException ("Bitmap.InitFromStream (win32)");
+                       st = GDIPlus.GdipLoadImageFromStream(new ComIStreamWrapper(stream), out imagePtr);
                }
+
+               GDIPlus.CheckStatus (st);
+               nativeObject = imagePtr;
        }
 
        // non-static   
@@ -204,10 +277,27 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                return source;
        }
        
-       [MonoTODO]      
-       public EncoderParameters GetEncoderParameterList(Guid encoder)
+       public EncoderParameters GetEncoderParameterList(Guid format)
        {
-               throw new NotImplementedException ();
+               Status status;
+               uint sz;
+
+               status = GDIPlus.GdipGetEncoderParameterListSize (nativeObject, ref format, out sz);
+               GDIPlus.CheckStatus (status);
+
+               IntPtr rawEPList = Marshal.AllocHGlobal ((int) sz);
+               EncoderParameters eps;
+
+               try {
+                       status = GDIPlus.GdipGetEncoderParameterList (nativeObject, ref format, sz, rawEPList);
+                       eps = EncoderParameters.FromNativePtr (rawEPList);
+                       GDIPlus.CheckStatus (status);
+               }
+               finally {
+                       Marshal.FreeHGlobal (rawEPList);
+               }
+
+               return eps;
        }
        
        public int GetFrameCount(FrameDimension dimension)
@@ -222,16 +312,50 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                
        }
        
-       [MonoTODO]      
        public PropertyItem GetPropertyItem(int propid)
        {
-               throw new NotImplementedException ();
+               int propSize;
+               IntPtr property;
+               PropertyItem item = new PropertyItem ();
+               GdipPropertyItem gdipProperty = new GdipPropertyItem ();
+               Status status;
+                       
+               status = GDIPlus.GdipGetPropertyItemSize (nativeObject, propid, 
+                                                                       out propSize);
+               GDIPlus.CheckStatus (status);
+
+               /* Get PropertyItem */
+               property = Marshal.AllocHGlobal (propSize);
+               try {
+                       status = GDIPlus.GdipGetPropertyItem (nativeObject, propid, propSize, property);
+                       GDIPlus.CheckStatus (status);
+                       gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure (property, 
+                                                               typeof (GdipPropertyItem));                                             
+                       GdipPropertyItem.MarshalTo (gdipProperty, item);
+               }
+               finally {
+                       Marshal.FreeHGlobal (property);
+               }
+               return item;
        }
        
-       [MonoTODO]      
-       public Image GetThumbnailImage(int thumbWidth, int thumbHeight, Image.GetThumbnailImageAbort callback, IntPtr callbackData)
+       public Image GetThumbnailImage (int thumbWidth, int thumbHeight, Image.GetThumbnailImageAbort callback, IntPtr callbackData)
        {
-               throw new NotImplementedException ();                           
+               if ((thumbWidth <= 0) || (thumbHeight <= 0))
+                       throw new OutOfMemoryException ("Invalid thumbnail size");
+
+               Image ThumbNail = new Bitmap (thumbWidth, thumbHeight);
+
+               using (Graphics g = Graphics.FromImage (ThumbNail)) {
+                       Status status = GDIPlus.GdipDrawImageRectRectI (g.nativeObject, nativeObject,
+                               0, 0, thumbWidth, thumbHeight,
+                               0, 0, this.Width, this.Height,
+                               GraphicsUnit.Pixel, IntPtr.Zero, null, IntPtr.Zero);
+
+                       GDIPlus.CheckStatus (status);
+               }
+
+               return ThumbNail;
        }
        
        
@@ -247,34 +371,13 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                GDIPlus.CheckStatus (status);                           
        }
 
-       public void Save (string filename)
-       {
-               Save (filename, RawFormat);
-       }
-
-       public void Save (Stream stream, ImageFormat format)
+       internal ImageCodecInfo findEncoderForFormat (ImageFormat format)
        {
-               Status st;
-               if (Environment.OSVersion.Platform == (PlatformID) 128) {
-                       byte[] g = format.Guid.ToByteArray();
-                       GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream);
-                       st = GDIPlus.GdipSaveImageToDelegate_linux (nativeObject, sh.PutBytesDelegate, g, IntPtr.Zero);
-
-               } else {
-                       throw new NotImplementedException ("Image.Save(Stream) (win32)");
-               }
-               GDIPlus.CheckStatus (st);
-       }
-
-       public void Save(string filename, ImageFormat format) 
-       {
-               Status st;              
-               ImageCodecInfo[] encoders =  ImageCodecInfo.GetImageEncoders();                 
+               ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();                  
                ImageCodecInfo encoder = null;
-               Guid guid;
                
                if (format.Guid.Equals (ImageFormat.MemoryBmp.Guid))
-                       format = ImageFormat.Bmp;
+                       format = ImageFormat.Png;
        
                /* Look for the right encoder for our format*/
                for (int i = 0; i < encoders.Length; i++) {
@@ -283,51 +386,102 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                                break;
                        }                       
                }
-               
+
+               return encoder;
+       }
+
+       public void Save (string filename)
+       {
+               Save (filename, RawFormat);
+       }
+
+       public void Save(string filename, ImageFormat format) 
+       {
+               ImageCodecInfo encoder = findEncoderForFormat (format);
+
                if (encoder == null)
-                       throw new ArgumentException ("No coded available for format:" + format.Guid);           
-               
-               guid = encoder.Clsid;
-               st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, IntPtr.Zero);                               
-               GDIPlus.CheckStatus (st);
+                       throw new ArgumentException ("No codec available for format:" + format.Guid);
+
+               Save (filename, encoder, null);
        }
-       
-       internal void setGDIPalette() 
+
+       public void Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
        {
-               IntPtr gdipalette;
+               Status st;
+               Guid guid = encoder.Clsid;
 
-               gdipalette = colorPalette.getGDIPalette ();
-               Status st = GDIPlus.GdipSetImagePalette (NativeObject, gdipalette);
-               Marshal.FreeHGlobal (gdipalette);
+               if (encoderParams == null) {
+                       st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, IntPtr.Zero);
+               } else {
+                       IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
+                       st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, nativeEncoderParams);
+                       Marshal.FreeHGlobal (nativeEncoderParams);
+               }
 
                GDIPlus.CheckStatus (st);
        }
 
-       [MonoTODO ("Ignoring EncoderParameters")]
-       public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
+       public void Save (Stream stream, ImageFormat format)
        {
-               Save (stream, new ImageFormat (encoder.FormatID));
+               ImageCodecInfo encoder = findEncoderForFormat (format);
+
+               if (encoder == null)
+                       throw new ArgumentException ("No codec available for format:" + format.Guid);
+
+               Save (stream, encoder, null);
        }
-       
-       [MonoTODO ("Ignoring EncoderParameters")]       
-       public void Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
+
+       public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
        {
-               Save (filename, new ImageFormat (encoder.FormatID));
+               Status st;
+               IntPtr nativeEncoderParams;
+               Guid guid = encoder.Clsid;
+
+               if (encoderParams == null)
+                       nativeEncoderParams = IntPtr.Zero;
+               else
+                       nativeEncoderParams = encoderParams.ToNativePtr ();
+
+               try {
+                       // check for Unix platforms - see FAQ for more details
+                       // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F
+                       int platform = (int) Environment.OSVersion.Platform;
+                       if ((platform == 4) || (platform == 128)) {
+                               GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream);
+                               st = GDIPlus.GdipSaveImageToDelegate_linux (nativeObject, sh.GetBytesDelegate, sh.PutBytesDelegate,
+                                       sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, ref guid, nativeEncoderParams);
+                       }
+                       else
+                               st = GDIPlus.GdipSaveImageToStream(new HandleRef(this, nativeObject), new ComIStreamWrapper(stream), ref guid, new HandleRef(encoderParams, nativeEncoderParams));
+               }
+               finally {
+                       if (nativeEncoderParams != IntPtr.Zero)
+                               Marshal.FreeHGlobal (nativeEncoderParams);
+               }
+               
+               GDIPlus.CheckStatus (st);               
        }
        
-       [MonoTODO]      
-       public void SaveAdd(EncoderParameters encoderParams)
+       public void SaveAdd (EncoderParameters encoderParams)
        {
-               throw new NotImplementedException ();
+               Status st;
+               
+               IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
+               st = GDIPlus.GdipSaveAdd (nativeObject, nativeEncoderParams);
+               Marshal.FreeHGlobal (nativeEncoderParams);
+               GDIPlus.CheckStatus (st);
        }
-       
-       [MonoTODO]      
-       public void SaveAdd(Image image, EncoderParameters encoderParams)
+               
+       public void SaveAdd (Image image, EncoderParameters encoderParams)
        {
-               throw new NotImplementedException ();
+               Status st;
+               
+               IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
+               st = GDIPlus.GdipSaveAddImage (nativeObject, image.NativeObject, nativeEncoderParams);
+               Marshal.FreeHGlobal (nativeEncoderParams);
+               GDIPlus.CheckStatus (st);
        }
-       
-       
+               
        public int SelectActiveFrame(FrameDimension dimension, int frameIndex)
        {
                Guid guid = dimension.Guid;             
@@ -338,10 +492,17 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                return frameIndex;              
        }
        
-       [MonoTODO]      
        public void SetPropertyItem(PropertyItem propitem)
        {
-               throw new NotImplementedException ();
+               IntPtr property;
+               int size = Marshal.SizeOf (typeof(GdipPropertyItem));
+               property = Marshal.AllocHGlobal (size);
+
+               Marshal.StructureToPtr (propitem, property, true);
+               Status status = GDIPlus.GdipSetPropertyItem (nativeObject, property);
+               GDIPlus.CheckStatus (status);
+               // FIXME: GdipSetPropertyItem isn't implemented in libgdiplus (but returns Ok)
+               // so who's freeing "property" ? GDI+ ?
        }
 
        // properties   
@@ -362,7 +523,7 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                        uint found;
                        Status status = GDIPlus.GdipImageGetFrameDimensionsCount (nativeObject, out found);
                        GDIPlus.CheckStatus (status);
-                       Guid [] guid = new Guid [found ];
+                       Guid [] guid = new Guid [found];
                        status = GDIPlus.GdipImageGetFrameDimensionsList (nativeObject, guid, found);
                        GDIPlus.CheckStatus (status);  
                        return guid;
@@ -396,15 +557,58 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
        [Browsable (false)]
        public ColorPalette Palette {
                get {                                                   
-                       
-                       return colorPalette;
+                       return retrieveGDIPalette();
                }
                set {
-                       colorPalette = value;                                   
+                       storeGDIPalette(value);
                }
        }
-       
-       
+
+       internal ColorPalette retrieveGDIPalette()
+       {
+               ColorPalette ret = new ColorPalette();
+               if (!IsIndexedPixelFormat (PixelFormat)) {
+                       return ret;
+               }
+               Status st;
+               int bytes;
+
+               st = GDIPlus.GdipGetImagePaletteSize (nativeObject, out bytes);
+               GDIPlus.CheckStatus (st);
+               IntPtr palette_data = Marshal.AllocHGlobal (bytes);
+               try {
+                       st = GDIPlus.GdipGetImagePalette (nativeObject, palette_data, bytes);
+                       GDIPlus.CheckStatus (st);
+                       ret.setFromGDIPalette (palette_data);
+                       return ret;
+               }
+
+               finally {
+                       Marshal.FreeHGlobal (palette_data);
+               }
+       }
+
+       internal void storeGDIPalette(ColorPalette palette)
+       {
+               if (palette == null) {
+                       throw new ArgumentNullException("palette");
+               }
+               IntPtr palette_data = palette.getGDIPalette();
+               if (palette_data == IntPtr.Zero) {
+                       return;
+               }
+
+               try {
+                       Status st = GDIPlus.GdipSetImagePalette (nativeObject, palette_data);
+                       GDIPlus.CheckStatus (st);
+               }
+
+               finally {
+                       Marshal.FreeHGlobal(palette_data);
+               }
+       }
+
+               
        public SizeF PhysicalDimension {
                get {
                        float width,  height;
@@ -425,20 +629,62 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                }               
        }
        
-       
-       [MonoTODO]
        [Browsable (false)]
        public int[] PropertyIdList {
                get {
-                       throw new NotImplementedException ();
+                       uint propNumbers;
+                       
+                       Status status = GDIPlus.GdipGetPropertyCount (nativeObject, 
+                                                                       out propNumbers);                       
+                       GDIPlus.CheckStatus (status);
+                       
+                       int [] idList = new int [propNumbers];
+                       status = GDIPlus.GdipGetPropertyIdList (nativeObject, 
+                                                               propNumbers, idList);
+                       GDIPlus.CheckStatus (status);
+                       
+                       return idList;
                }
        }
        
-       [MonoTODO]
        [Browsable (false)]
        public PropertyItem[] PropertyItems {
                get {
-                       throw new NotImplementedException ();
+                       int propNums, propsSize, propSize;
+                       IntPtr properties, propPtr;
+                       PropertyItem[] items;
+                       GdipPropertyItem gdipProperty = new GdipPropertyItem ();
+                       Status status;
+                       
+                       status = GDIPlus.GdipGetPropertySize (nativeObject, out propsSize, out propNums);
+                       GDIPlus.CheckStatus (status);
+
+                       items =  new PropertyItem [propNums];
+                       
+                       if (propNums == 0)
+                               return items;                   
+                                       
+                       /* Get PropertyItem list*/
+                       properties = Marshal.AllocHGlobal (propsSize * propNums);
+                       try {
+                               status = GDIPlus.GdipGetAllPropertyItems (nativeObject, propsSize, 
+                                                               propNums, properties);
+                               GDIPlus.CheckStatus (status);
+
+                               propSize = Marshal.SizeOf (gdipProperty);                       
+                               propPtr = properties;
+                       
+                               for (int i = 0; i < propNums; i++, propPtr = new IntPtr (propPtr.ToInt64 () + propSize)) {
+                                       gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure 
+                                               (propPtr, typeof (GdipPropertyItem));                                           
+                                       items [i] = new PropertyItem ();
+                                       GdipPropertyItem.MarshalTo (gdipProperty, items [i]);                                                           
+                               }
+                       }
+                       finally {
+                               Marshal.FreeHGlobal (properties);
+                       }
+                       return items;
                }
        }
 
@@ -457,7 +703,17 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                        return new Size(Width, Height);
                }
        }
-       
+
+#if NET_2_0
+       [DefaultValue (null)]
+       [LocalizableAttribute(false)] 
+       [BindableAttribute(true)]       
+       [TypeConverter (typeof (StringConverter))]
+       public object Tag { 
+               get { return tag; }
+               set { tag = value; }
+       }
+#endif 
        public float VerticalResolution {
                get {
                        float resolution;
@@ -494,6 +750,7 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
        public void Dispose ()
        {
                Dispose (true);
+               System.GC.SuppressFinalize (this);
        }
 
        ~Image ()
@@ -501,38 +758,28 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                Dispose (false);
        }
 
-       protected virtual void DisposeResources ()
-       {
-               Status status = GDIPlus.GdipDisposeImage (nativeObject);
-               GDIPlus.CheckStatus (status);
-       }
-       
        protected virtual void Dispose (bool disposing)
        {
-               if (nativeObject != (IntPtr) 0){
-                       DisposeResources ();
-                       nativeObject=IntPtr.Zero;
+               if (nativeObject != IntPtr.Zero){
+                       Status status = GDIPlus.GdipDisposeImage (nativeObject);
+                       // set nativeObject to null before throwing an exception
+                       nativeObject = IntPtr.Zero;
+                       GDIPlus.CheckStatus (status);           
                }
        }
        
-       
-       public virtual object Clone()
-       {                               
+       public object Clone ()
+       {
+               Bitmap b = (this as Bitmap);
+               if (b == null)
+                       throw new NotImplementedException ("This Image instance isn't a Bitmap instance."); 
+
                IntPtr newimage = IntPtr.Zero;
-               
-               if (!(this is Bitmap)) 
-                       throw new NotImplementedException (); 
-               
                Status status = GDIPlus.GdipCloneImage (NativeObject, out newimage);                    
-               
                GDIPlus.CheckStatus (status);                   
-               
-               if (this is Bitmap)
-                       return new Bitmap (newimage);
-               
-               throw new NotImplementedException (); 
-       }
 
+               return new Bitmap (newimage);
+       }
 }
 
 }