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