2005-10-04 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / System.Drawing / System.Drawing / Image.cs
index 7db505207a3fb5d14fca89d0edd2aaf9efc91d13..83edd59dcfa76697df0ceb623c27775a8ef1eed1 100644 (file)
@@ -41,6 +41,7 @@ using System.Runtime.InteropServices;
 using System.ComponentModel;
 using System.Drawing.Imaging;
 using System.IO;
+using System.Reflection;
 
 namespace System.Drawing
 {
@@ -54,13 +55,12 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
        public delegate bool GetThumbnailImageAbort();
        
        internal IntPtr nativeObject = IntPtr.Zero;     
-       ColorPalette colorPalette;
        
        
        // constructor
        internal  Image()
-       {               
-               colorPalette = new ColorPalette();
+       {       
+               
        }
        
        private Image (SerializationInfo info, StreamingContext context)
@@ -75,35 +75,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, ImageFormat.Bmp);
+               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)
@@ -192,28 +215,48 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
 
        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.PutBytesDelegate,
-                                                                       sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate,
-                                                                                    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   
@@ -264,10 +307,29 @@ 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);
+               status = GDIPlus.GdipGetPropertyItem (nativeObject, propid, propSize,  
+                                                                               property);
+               GDIPlus.CheckStatus (status);
+               gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure ((IntPtr)property, 
+                                                               typeof (GdipPropertyItem));                                             
+               GdipPropertyItem.MarshalTo (gdipProperty, item);                                                                
+               
+               Marshal.FreeHGlobal (property);
+               return item;
        }
        
        public Image GetThumbnailImage(int thumbWidth, int thumbHeight, Image.GetThumbnailImageAbort callback, IntPtr callbackData)
@@ -365,23 +427,32 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
        public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
        {
                Status st;
+               IntPtr nativeEncoderParams;
                Guid guid = encoder.Clsid;
 
-               if (Environment.OSVersion.Platform == (PlatformID) 128) {
-                       GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream);
-                       if (encoderParams == null) {
-                               st = GDIPlus.GdipSaveImageToDelegate_linux (nativeObject, sh.GetBytesDelegate, sh.PutBytesDelegate,
-                                               sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, ref guid, IntPtr.Zero);
-                       } else {
-                               IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
+               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);
-                               Marshal.FreeHGlobal (nativeEncoderParams);
+                                       sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, ref guid, nativeEncoderParams);
                        }
-               } else {
-                       throw new NotImplementedException ("Image.Save(Stream) (win32)");
+                       else
+                               st = GDIPlus.GdipSaveImageToStream(new HandleRef(this, nativeObject), new ComIStreamWrapper(stream), ref guid, new HandleRef(encoderParams, nativeEncoderParams));
                }
-               GDIPlus.CheckStatus (st);
+               finally {
+                       if (nativeEncoderParams != IntPtr.Zero)
+                               Marshal.FreeHGlobal (nativeEncoderParams);
+               }
+               
+               GDIPlus.CheckStatus (st);               
        }
        
        public void SaveAdd (EncoderParameters encoderParams)
@@ -391,6 +462,7 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
                st = GDIPlus.GdipSaveAdd (nativeObject, nativeEncoderParams);
                Marshal.FreeHGlobal (nativeEncoderParams);
+               GDIPlus.CheckStatus (st);
        }
                
        public void SaveAdd (Image image, EncoderParameters encoderParams)
@@ -400,6 +472,7 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
                IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
                st = GDIPlus.GdipSaveAddImage (nativeObject, image.NativeObject, nativeEncoderParams);
                Marshal.FreeHGlobal (nativeEncoderParams);
+               GDIPlus.CheckStatus (st);
        }
                
        public int SelectActiveFrame(FrameDimension dimension, int frameIndex)
@@ -412,10 +485,15 @@ 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);
        }
 
        // properties   
@@ -470,13 +548,57 @@ 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 {
@@ -501,45 +623,57 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
        [Browsable (false)]
        public int[] PropertyIdList {
                get {
-                       PropertyItem [] propItems = this.PropertyItems ;
-                       int length = propItems.Length;
-                       int [] ids = new int [length];
-                       for (int i = 0; i < length; i++)
-                               ids [i] = propItems [i].Id;
-
-                       return ids;
+                       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;
                }
        }
        
        [Browsable (false)]
        public PropertyItem[] PropertyItems {
                get {
-                       int prop_ptr, prop_size;
-                       uint bufferSize, numProperties;
-                        
-                       Status st = GDIPlus.GdipGetPropertySize (nativeObject, 
-                                                       out bufferSize, out numProperties);
-                       GDIPlus.CheckStatus (st);
-
-                       IntPtr dest = Marshal.AllocHGlobal ( (int)bufferSize);           
+                       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);
 
-                       st = GDIPlus.GdipGetAllPropertyItems (nativeObject, bufferSize, 
-                                                               numProperties, out dest);
-                       GDIPlus.CheckStatus (st);
+                       items =  new PropertyItem [propNums];
+                       
+                       if (propNums == 0)
+                               return items;                   
+                                       
+                       /* Get PropertyItem list*/
+                       properties = Marshal.AllocHGlobal (propsSize);
+                       status = GDIPlus.GdipGetAllPropertyItems (nativeObject, propsSize, 
+                                                               propNums, properties);
+                       GDIPlus.CheckStatus (status);
 
-                       GdipPropertyItem gdipProp = new GdipPropertyItem();
-                       PropertyItem [] propItems = new PropertyItem [numProperties] ;
-                       prop_size = Marshal.SizeOf (gdipProp);                  
-                       prop_ptr = dest.ToInt32();
+                       propSize = Marshal.SizeOf (gdipProperty);                       
+                       propPtr = properties;
                        
-                       for (int i = 0; i < numProperties; i++, prop_ptr += prop_size) {
-                               gdipProp = (GdipPropertyItem) Marshal.PtrToStructure 
-                                               ((IntPtr)prop_ptr, typeof (GdipPropertyItem));                                          
-                               GdipPropertyItem.MarshalTo (gdipProp, propItems [i]);                           
+                       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]);                                                           
                        }
                        
-                       Marshal.FreeHGlobal (dest);
-                       return propItems;
+                       Marshal.FreeHGlobal (properties);
+                       return items;
                }
        }
 
@@ -595,6 +729,7 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
        public void Dispose ()
        {
                Dispose (true);
+               System.GC.SuppressFinalize (this);
        }
 
        ~Image ()
@@ -604,39 +739,34 @@ public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISer
 
        private void DisposeResources ()
        {
-               lock (this)
-               {
-                       Status status = GDIPlus.GdipDisposeImage (nativeObject);
-                       GDIPlus.CheckStatus (status);
-               }
+               Status status = GDIPlus.GdipDisposeImage (nativeObject);
+               GDIPlus.CheckStatus (status);           
        }
        
        protected virtual void Dispose (bool disposing)
        {
-               if (nativeObject != (IntPtr) 0){
+               if (nativeObject != IntPtr.Zero){
                        DisposeResources ();
-                       nativeObject=IntPtr.Zero;
+                       nativeObject = IntPtr.Zero;
                }
        }
        
        public virtual object Clone()
        {                               
-               lock (this)
-               {
-                       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);
-                       
+
+               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 ();           
        }
 
 }