implement Image.SetPropertyItem() method v4 (#1167)
[mono.git] / mcs / class / System.Drawing / System.Drawing / Image.cs
1 //
2 // System.Drawing.Image.cs
3 //
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>
10 //
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
14 //
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:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
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.
33 //
34
35 using System;
36 using System.Runtime.Serialization;
37 using System.Runtime.InteropServices;
38 using System.ComponentModel;
39 using System.Drawing.Imaging;
40 using System.IO;
41 using System.Reflection;
42
43 namespace System.Drawing
44 {
45 [Serializable]
46 [ComVisible (true)]
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 
51 {
52         public delegate bool GetThumbnailImageAbort();
53         private object tag;
54         
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;
59         
60         
61         // constructor
62         internal  Image()
63         {       
64         }
65         
66         internal Image (SerializationInfo info, StreamingContext context)
67         {
68                 foreach (SerializationEntry serEnum in info) {
69                         if (String.Compare(serEnum.Name, "Data", true) == 0) {
70                                 byte[] bytes = (byte[]) serEnum.Value;
71
72                                 if (bytes != null) {
73                                         MemoryStream ms = new MemoryStream (bytes);
74                                         nativeObject = InitFromStream (ms);
75                                         // under Win32 stream is owned by SD/GDI+ code
76                                         if (GDIPlus.RunningOnWindows ())
77                                                 stream = ms;
78                                 }
79                         }
80                 }
81         }
82
83         // FIXME - find out how metafiles (another decoder-only codec) are handled
84         void ISerializable.GetObjectData (SerializationInfo si, StreamingContext context)
85         {
86                 using (MemoryStream ms = new MemoryStream ()) {
87                         // Icon is a decoder-only codec
88                         if (RawFormat.Equals (ImageFormat.Icon)) {
89                                 Save (ms, ImageFormat.Png);
90                         } else {
91                                 Save (ms, RawFormat);
92                         }
93                         si.AddValue ("Data", ms.ToArray ());
94                 }
95         }
96     
97         // public methods
98         // static
99         public static Image FromFile(string filename)
100         {
101                 return FromFile (filename, false);
102         }
103         
104         public static Image FromFile(string filename, bool useEmbeddedColorManagement)
105         {
106                 IntPtr imagePtr;
107                 Status st;
108
109                 if (!File.Exists (filename))
110                         throw new FileNotFoundException (filename);
111
112                 if (useEmbeddedColorManagement)
113                         st = GDIPlus.GdipLoadImageFromFileICM (filename, out imagePtr);
114                 else
115                         st = GDIPlus.GdipLoadImageFromFile (filename, out imagePtr);
116                 GDIPlus.CheckStatus (st);
117
118                 return CreateFromHandle (imagePtr);
119         }
120
121         public static Bitmap FromHbitmap(IntPtr hbitmap)
122         {
123                 return FromHbitmap (hbitmap, IntPtr.Zero);
124         }
125
126         public static Bitmap FromHbitmap(IntPtr hbitmap, IntPtr hpalette)
127         {               
128                 IntPtr imagePtr;
129                 Status st;
130
131                 st = GDIPlus.GdipCreateBitmapFromHBITMAP (hbitmap, hpalette, out imagePtr);
132
133                 GDIPlus.CheckStatus (st);
134                 return new Bitmap (imagePtr);
135         }
136
137         // note: FromStream can return either a Bitmap or Metafile instance
138
139         public static Image FromStream (Stream stream)
140         {
141                 return LoadFromStream (stream, false);
142         }
143
144         [MonoLimitation ("useEmbeddedColorManagement  isn't supported.")]
145         public static Image FromStream (Stream stream, bool useEmbeddedColorManagement)
146         {
147                 return LoadFromStream (stream, false);
148         }
149
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)
153         {
154                 return LoadFromStream (stream, false);
155         }
156
157         internal static Image LoadFromStream (Stream stream, bool keepAlive)
158         {
159                 if (stream == null)
160                         throw new ArgumentNullException ("stream");
161
162                 Image img = CreateFromHandle (InitFromStream (stream));
163
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 ())
167                         img.stream = stream;
168
169                 return img;
170         }
171
172         internal static Image CreateFromHandle (IntPtr handle)
173         {
174                 ImageType type;
175                 GDIPlus.CheckStatus (GDIPlus.GdipGetImageType (handle, out type));
176                 switch (type) {
177                 case ImageType.Bitmap:
178                         return new Bitmap (handle);
179                 case ImageType.Metafile:
180                         return new Metafile (handle);
181                 default:
182                         throw new NotSupportedException (Locale.GetText ("Unknown image type."));
183                 }
184         }
185
186         public static int GetPixelFormatSize(PixelFormat pixfmt)
187         {
188                 int result = 0;
189                 switch (pixfmt) {
190                         case PixelFormat.Format16bppArgb1555:
191                         case PixelFormat.Format16bppGrayScale:
192                         case PixelFormat.Format16bppRgb555:
193                         case PixelFormat.Format16bppRgb565:
194                                 result = 16;
195                                 break;
196                         case PixelFormat.Format1bppIndexed:
197                                 result = 1;
198                                 break;
199                         case PixelFormat.Format24bppRgb:
200                                 result = 24;
201                                 break;
202                         case PixelFormat.Format32bppArgb:
203                         case PixelFormat.Format32bppPArgb:
204                         case PixelFormat.Format32bppRgb:
205                                 result = 32;
206                                 break;
207                         case PixelFormat.Format48bppRgb:
208                                 result = 48;
209                                 break;
210                         case PixelFormat.Format4bppIndexed:
211                                 result = 4;
212                                 break;
213                         case PixelFormat.Format64bppArgb:
214                         case PixelFormat.Format64bppPArgb:
215                                 result = 64;
216                                 break;
217                         case PixelFormat.Format8bppIndexed:
218                                 result = 8;
219                                 break;
220                 }
221                 return result;
222         }
223
224         public static bool IsAlphaPixelFormat(PixelFormat pixfmt)
225         {
226                 bool result = false;
227                 switch (pixfmt) {
228                         case PixelFormat.Format16bppArgb1555:
229                         case PixelFormat.Format32bppArgb:
230                         case PixelFormat.Format32bppPArgb:
231                         case PixelFormat.Format64bppArgb:
232                         case PixelFormat.Format64bppPArgb:
233                                 result = true;
234                                 break;
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:
244                                 result = false;
245                                 break;
246                 }
247                 return result;
248         }
249         
250         public static bool IsCanonicalPixelFormat (PixelFormat pixfmt)
251         {
252                 return ((pixfmt & PixelFormat.Canonical) != 0);
253         }
254         
255         public static bool IsExtendedPixelFormat (PixelFormat pixfmt)
256         {
257                 return ((pixfmt & PixelFormat.Extended) != 0);
258         }
259
260         internal static IntPtr InitFromStream (Stream stream)
261         {
262                 if (stream == null)
263                         throw new ArgumentException ("stream");
264
265                 IntPtr imagePtr;
266                 Status st;
267                 
268                 // Seeking required
269                 if (!stream.CanSeek) {
270                         byte[] buffer = new byte[256];
271                         int index = 0;
272                         int count;
273
274                         do {
275                                 if (buffer.Length < index + 256) {
276                                         byte[] newBuffer = new byte[buffer.Length * 2];
277                                         Array.Copy(buffer, newBuffer, buffer.Length);
278                                         buffer = newBuffer;
279                                 }
280                                 count = stream.Read(buffer, index, 256);
281                                 index += count;
282                         }
283                         while (count != 0);
284
285                         stream = new MemoryStream(buffer, 0, index);
286                 }
287
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);
294
295                         st = GDIPlus.GdipLoadImageFromDelegate_linux (sh.GetHeaderDelegate, sh.GetBytesDelegate,
296                                 sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, out imagePtr);
297                 } else {
298                         st = GDIPlus.GdipLoadImageFromStream (new ComIStreamWrapper (stream), out imagePtr);
299                 }
300
301                 return st == Status.Ok ? imagePtr : IntPtr.Zero;
302         }
303
304         // non-static   
305         public RectangleF GetBounds (ref GraphicsUnit pageUnit)
306         {       
307                 RectangleF source;                      
308                 
309                 Status status = GDIPlus.GdipGetImageBounds (nativeObject, out source, ref pageUnit);
310                 GDIPlus.CheckStatus (status);           
311                 
312                 return source;
313         }
314         
315         public EncoderParameters GetEncoderParameterList(Guid encoder)
316         {
317                 Status status;
318                 uint sz;
319
320                 status = GDIPlus.GdipGetEncoderParameterListSize (nativeObject, ref encoder, out sz);
321                 GDIPlus.CheckStatus (status);
322
323                 IntPtr rawEPList = Marshal.AllocHGlobal ((int) sz);
324                 EncoderParameters eps;
325
326                 try {
327                         status = GDIPlus.GdipGetEncoderParameterList (nativeObject, ref encoder, sz, rawEPList);
328                         eps = EncoderParameters.FromNativePtr (rawEPList);
329                         GDIPlus.CheckStatus (status);
330                 }
331                 finally {
332                         Marshal.FreeHGlobal (rawEPList);
333                 }
334
335                 return eps;
336         }
337         
338         public int GetFrameCount (FrameDimension dimension)
339         {
340                 uint count;
341                 Guid guid = dimension.Guid;
342
343                 Status status = GDIPlus.GdipImageGetFrameCount (nativeObject, ref guid, out count); 
344                 GDIPlus.CheckStatus (status);
345                 
346                 return (int) count;
347         }
348         
349         public PropertyItem GetPropertyItem(int propid)
350         {
351                 int propSize;
352                 IntPtr property;
353                 PropertyItem item = new PropertyItem ();
354                 GdipPropertyItem gdipProperty = new GdipPropertyItem ();
355                 Status status;
356                         
357                 status = GDIPlus.GdipGetPropertyItemSize (nativeObject, propid, 
358                                                                         out propSize);
359                 GDIPlus.CheckStatus (status);
360
361                 /* Get PropertyItem */
362                 property = Marshal.AllocHGlobal (propSize);
363                 try {
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);
369                 }
370                 finally {
371                         Marshal.FreeHGlobal (property);
372                 }
373                 return item;
374         }
375         
376         public Image GetThumbnailImage (int thumbWidth, int thumbHeight, Image.GetThumbnailImageAbort callback, IntPtr callbackData)
377         {
378                 if ((thumbWidth <= 0) || (thumbHeight <= 0))
379                         throw new OutOfMemoryException ("Invalid thumbnail size");
380
381                 Image ThumbNail = new Bitmap (thumbWidth, thumbHeight);
382
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);
388
389                         GDIPlus.CheckStatus (status);
390                 }
391
392                 return ThumbNail;
393         }
394         
395         
396         public void RemovePropertyItem (int propid)
397         {               
398                 Status status = GDIPlus.GdipRemovePropertyItem (nativeObject, propid);
399                 GDIPlus.CheckStatus (status);                                   
400         }       
401         
402         public void RotateFlip (RotateFlipType rotateFlipType)
403         {                       
404                 Status status = GDIPlus.GdipImageRotateFlip (nativeObject, rotateFlipType);
405                 GDIPlus.CheckStatus (status);                           
406         }
407
408         internal ImageCodecInfo findEncoderForFormat (ImageFormat format)
409         {
410                 ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();                  
411                 ImageCodecInfo encoder = null;
412                 
413                 if (format.Guid.Equals (ImageFormat.MemoryBmp.Guid))
414                         format = ImageFormat.Png;
415         
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];
420                                 break;
421                         }                       
422                 }
423
424                 return encoder;
425         }
426
427         public void Save (string filename)
428         {
429                 Save (filename, RawFormat);
430         }
431
432         public void Save(string filename, ImageFormat format) 
433         {
434                 ImageCodecInfo encoder = findEncoderForFormat (format);
435                 if (encoder == null) {
436                         // second chance
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");
441                         }
442                 }
443                 Save (filename, encoder, null);
444         }
445
446         public void Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
447         {
448                 Status st;
449                 Guid guid = encoder.Clsid;
450
451                 if (encoderParams == null) {
452                         st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, IntPtr.Zero);
453                 } else {
454                         IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
455                         st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, nativeEncoderParams);
456                         Marshal.FreeHGlobal (nativeEncoderParams);
457                 }
458
459                 GDIPlus.CheckStatus (st);
460         }
461
462         public void Save (Stream stream, ImageFormat format)
463         {
464                 ImageCodecInfo encoder = findEncoderForFormat (format);
465
466                 if (encoder == null)
467                         throw new ArgumentException ("No codec available for format:" + format.Guid);
468
469                 Save (stream, encoder, null);
470         }
471
472         public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
473         {
474                 Status st;
475                 IntPtr nativeEncoderParams;
476                 Guid guid = encoder.Clsid;
477
478                 if (encoderParams == null)
479                         nativeEncoderParams = IntPtr.Zero;
480                 else
481                         nativeEncoderParams = encoderParams.ToNativePtr ();
482
483                 try {
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);
488                         } else {
489                                 st = GDIPlus.GdipSaveImageToStream (new HandleRef (this, nativeObject), 
490                                         new ComIStreamWrapper (stream), ref guid, new HandleRef (encoderParams, nativeEncoderParams));
491                         }
492                 }
493                 finally {
494                         if (nativeEncoderParams != IntPtr.Zero)
495                                 Marshal.FreeHGlobal (nativeEncoderParams);
496                 }
497                 
498                 GDIPlus.CheckStatus (st);               
499         }
500         
501         public void SaveAdd (EncoderParameters encoderParams)
502         {
503                 Status st;
504                 
505                 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
506                 st = GDIPlus.GdipSaveAdd (nativeObject, nativeEncoderParams);
507                 Marshal.FreeHGlobal (nativeEncoderParams);
508                 GDIPlus.CheckStatus (st);
509         }
510                 
511         public void SaveAdd (Image image, EncoderParameters encoderParams)
512         {
513                 Status st;
514                 
515                 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
516                 st = GDIPlus.GdipSaveAddImage (nativeObject, image.NativeObject, nativeEncoderParams);
517                 Marshal.FreeHGlobal (nativeEncoderParams);
518                 GDIPlus.CheckStatus (st);
519         }
520                 
521         public int SelectActiveFrame(FrameDimension dimension, int frameIndex)
522         {
523                 Guid guid = dimension.Guid;             
524                 Status st = GDIPlus.GdipImageSelectActiveFrame (nativeObject, ref guid, frameIndex);
525                 
526                 GDIPlus.CheckStatus (st);                       
527                 
528                 return frameIndex;              
529         }
530
531         public void SetPropertyItem(PropertyItem propitem)
532         {
533                 if (propitem == null)
534                         throw new ArgumentNullException ("propitem");
535
536                 int nItemSize =  Marshal.SizeOf (propitem.Value[0]);
537                 int size = nItemSize * propitem.Value.Length;
538                 IntPtr dest = Marshal.AllocHGlobal (size);
539                 try {
540                         GdipPropertyItem pi = new GdipPropertyItem ();
541                         pi.id    = propitem.Id;
542                         pi.len   = propitem.Len;
543                         pi.type  = propitem.Type;
544
545                         Marshal.Copy (propitem.Value, 0, dest, size);
546                         pi.value = dest;
547
548                         unsafe {
549                                 Status status = GDIPlus.GdipSetPropertyItem (nativeObject, &pi);
550                         
551                                 GDIPlus.CheckStatus (status);
552                         }
553                 }
554                 finally {
555                         Marshal.FreeHGlobal (dest);
556                 }
557         }
558
559         // properties   
560         [Browsable (false)]
561         public int Flags {
562                 get {
563                         int flags;
564                         
565                         Status status = GDIPlus.GdipGetImageFlags (nativeObject, out flags);                    
566                         GDIPlus.CheckStatus (status);                                           
567                         return flags;                   
568                 }
569         }
570         
571         [Browsable (false)]
572         public Guid[] FrameDimensionsList {
573                 get {
574                         uint found;
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);  
580                         return guid;
581                 }
582         }
583
584         [DefaultValue (false)]
585         [Browsable (false)]
586         [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
587         public int Height {
588                 get {
589                         uint height;                    
590                         Status status = GDIPlus.GdipGetImageHeight (nativeObject, out height);          
591                         GDIPlus.CheckStatus (status);                   
592                         
593                         return (int)height;
594                 }
595         }
596         
597         public float HorizontalResolution {
598                 get {
599                         float resolution;
600                         
601                         Status status = GDIPlus.GdipGetImageHorizontalResolution (nativeObject, out resolution);                        
602                         GDIPlus.CheckStatus (status);                   
603                         
604                         return resolution;
605                 }
606         }
607         
608         [Browsable (false)]
609         public ColorPalette Palette {
610                 get {                                                   
611                         return retrieveGDIPalette();
612                 }
613                 set {
614                         storeGDIPalette(value);
615                 }
616         }
617
618         internal ColorPalette retrieveGDIPalette()
619         {
620                 int bytes;
621                 ColorPalette ret = new ColorPalette ();
622
623                 Status st = GDIPlus.GdipGetImagePaletteSize (nativeObject, out bytes);
624                 GDIPlus.CheckStatus (st);
625                 IntPtr palette_data = Marshal.AllocHGlobal (bytes);
626                 try {
627                         st = GDIPlus.GdipGetImagePalette (nativeObject, palette_data, bytes);
628                         GDIPlus.CheckStatus (st);
629                         ret.setFromGDIPalette (palette_data);
630                         return ret;
631                 }
632
633                 finally {
634                         Marshal.FreeHGlobal (palette_data);
635                 }
636         }
637
638         internal void storeGDIPalette(ColorPalette palette)
639         {
640                 if (palette == null) {
641                         throw new ArgumentNullException("palette");
642                 }
643                 IntPtr palette_data = palette.getGDIPalette();
644                 if (palette_data == IntPtr.Zero) {
645                         return;
646                 }
647
648                 try {
649                         Status st = GDIPlus.GdipSetImagePalette (nativeObject, palette_data);
650                         GDIPlus.CheckStatus (st);
651                 }
652
653                 finally {
654                         Marshal.FreeHGlobal(palette_data);
655                 }
656         }
657
658                 
659         public SizeF PhysicalDimension {
660                 get {
661                         float width,  height;
662                         Status status = GDIPlus.GdipGetImageDimension (nativeObject, out width, out height);            
663                         GDIPlus.CheckStatus (status);                   
664                         
665                         return new SizeF (width, height);
666                 }
667         }
668         
669         public PixelFormat PixelFormat {
670                 get {                   
671                         PixelFormat pixFormat;                          
672                         Status status = GDIPlus.GdipGetImagePixelFormat (nativeObject, out pixFormat);          
673                         GDIPlus.CheckStatus (status);                   
674                         
675                         return pixFormat;
676                 }               
677         }
678         
679         [Browsable (false)]
680         public int[] PropertyIdList {
681                 get {
682                         uint propNumbers;
683                         
684                         Status status = GDIPlus.GdipGetPropertyCount (nativeObject, 
685                                                                         out propNumbers);                       
686                         GDIPlus.CheckStatus (status);
687                         
688                         int [] idList = new int [propNumbers];
689                         status = GDIPlus.GdipGetPropertyIdList (nativeObject, 
690                                                                 propNumbers, idList);
691                         GDIPlus.CheckStatus (status);
692                         
693                         return idList;
694                 }
695         }
696         
697         [Browsable (false)]
698         public PropertyItem[] PropertyItems {
699                 get {
700                         int propNums, propsSize, propSize;
701                         IntPtr properties, propPtr;
702                         PropertyItem[] items;
703                         GdipPropertyItem gdipProperty = new GdipPropertyItem ();
704                         Status status;
705                         
706                         status = GDIPlus.GdipGetPropertySize (nativeObject, out propsSize, out propNums);
707                         GDIPlus.CheckStatus (status);
708
709                         items =  new PropertyItem [propNums];
710                         
711                         if (propNums == 0)
712                                 return items;                   
713                                         
714                         /* Get PropertyItem list*/
715                         properties = Marshal.AllocHGlobal (propsSize * propNums);
716                         try {
717                                 status = GDIPlus.GdipGetAllPropertyItems (nativeObject, propsSize, 
718                                                                 propNums, properties);
719                                 GDIPlus.CheckStatus (status);
720
721                                 propSize = Marshal.SizeOf (gdipProperty);                       
722                                 propPtr = properties;
723                         
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]);                                                           
729                                 }
730                         }
731                         finally {
732                                 Marshal.FreeHGlobal (properties);
733                         }
734                         return items;
735                 }
736         }
737
738         public ImageFormat RawFormat {
739                 get {
740                         Guid guid;
741                         Status st = GDIPlus.GdipGetImageRawFormat (nativeObject, out guid);
742                         
743                         GDIPlus.CheckStatus (st);
744                         return new ImageFormat (guid);                  
745                 }
746         }
747         
748         public Size Size {
749                 get {
750                         return new Size(Width, Height);
751                 }
752         }
753
754         [DefaultValue (null)]
755         [LocalizableAttribute(false)] 
756         [BindableAttribute(true)]       
757         [TypeConverter (typeof (StringConverter))]
758         public object Tag { 
759                 get { return tag; }
760                 set { tag = value; }
761         }
762         public float VerticalResolution {
763                 get {
764                         float resolution;
765                         
766                         Status status = GDIPlus.GdipGetImageVerticalResolution (nativeObject, out resolution);
767                         GDIPlus.CheckStatus (status);
768
769                         return resolution;
770                 }
771         }
772
773         [DefaultValue (false)]
774         [Browsable (false)]
775         [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
776         public int Width {
777                 get {
778                         uint width;                     
779                         Status status = GDIPlus.GdipGetImageWidth (nativeObject, out width);            
780                         GDIPlus.CheckStatus (status);                   
781                         
782                         return (int)width;
783                 }
784         }
785         
786         internal IntPtr NativeObject{
787                 get{
788                         return nativeObject;
789                 }
790                 set     {
791                         nativeObject = value;
792                 }
793         }
794         
795         public void Dispose ()
796         {
797                 Dispose (true);
798                 GC.SuppressFinalize (this);
799         }
800
801         ~Image ()
802         {
803                 Dispose (false);
804         }
805
806         protected virtual void Dispose (bool disposing)
807         {
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) {
812                                 stream.Dispose ();
813                                 stream = null;
814                         }
815                         // ... set nativeObject to null before (possibly) throwing an exception
816                         nativeObject = IntPtr.Zero;
817                         GDIPlus.CheckStatus (status);           
818                 }
819         }
820         
821         public object Clone ()
822         {
823                 if (GDIPlus.RunningOnWindows () && stream != null)
824                         return CloneFromStream ();
825
826                 IntPtr newimage = IntPtr.Zero;
827                 Status status = GDIPlus.GdipCloneImage (NativeObject, out newimage);
828                 GDIPlus.CheckStatus (status);
829
830                 if (this is Bitmap)
831                         return new Bitmap (newimage);
832                 else
833                         return new Metafile (newimage);
834         }
835
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 ()
840         {
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];
845                 stream.Position = 0;
846                 do {
847                         count = stream.Read (buffer, 0, count);
848                         ms.Write (buffer, 0, count);
849                 } while (count == 4096);
850
851                 IntPtr newimage = IntPtr.Zero;
852                 newimage = InitFromStream (ms);
853
854                 if (this is Bitmap)
855                         return new Bitmap (newimage, ms);
856                 else
857                         return new Metafile (newimage, ms);
858         }
859
860 }
861
862 }