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