Image.cs: Made GetThumbnailImage throw OutOfMemoryException when image size is invali...
[mono.git] / mcs / class / System.Drawing / System.Drawing / Image.cs
1 //
2 // System.Drawing.Image.cs
3 //
4 // Copyright (C) 2002 Ximian, Inc.  http://www.ximian.com
5 // Copyright (C) 2004 Novell, Inc.  http://www.novell.com
6 //
7 // Author:      Christian Meyer (Christian.Meyer@cs.tum.edu)
8 //              Alexandre Pigolkine (pigolkine@gmx.de)
9 //              Jordi Mas i Hernandez (jordi@ximian.com)
10 //              Sanjay Gupta (gsanjay@novell.com)
11 //              Ravindra (rkumar@novell.com)
12 //
13
14 //
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 // 
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 // 
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36
37 using System;
38 using System.Runtime.Remoting;
39 using System.Runtime.Serialization;
40 using System.Runtime.InteropServices;
41 using System.ComponentModel;
42 using System.Drawing.Imaging;
43 using System.IO;
44 using System.Reflection;
45
46 namespace System.Drawing
47 {
48 [Serializable]
49 [ComVisible (true)]
50 [Editor ("System.Drawing.Design.ImageEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))]
51 [TypeConverter (typeof(ImageConverter))]
52 [ImmutableObject (true)]
53 public abstract class Image : MarshalByRefObject, IDisposable , ICloneable, ISerializable 
54 {
55         public delegate bool GetThumbnailImageAbort();
56         private object tag;
57         
58         internal IntPtr nativeObject = IntPtr.Zero;     
59         
60         
61         // constructor
62         internal  Image()
63         {       
64                 
65         }
66         
67         private Image (SerializationInfo info, StreamingContext context)
68         {
69                 foreach (SerializationEntry serEnum in info) {
70                         if (String.Compare(serEnum.Name, "Data", true) == 0) {
71                                 byte[] bytes = (byte[]) serEnum.Value;
72
73                                 if (bytes != null) {
74                                         InitFromStream(new MemoryStream(bytes));
75                                 }
76                         }
77                 }
78         }
79
80         private static bool IsIndexedPixelFormat(PixelFormat pixfmt)
81         {
82                 return ((pixfmt & PixelFormat.Indexed) != 0);
83         }
84
85         
86         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
87         {
88                 MemoryStream ms = new MemoryStream ();
89                 this.Save (ms, RawFormat);
90                 info.AddValue ("Data", ms.ToArray ());
91         }
92     
93         // public methods
94         // static
95         public static Image FromFile(string filename)
96         {
97                 return FromFile (filename, false);
98         }
99         
100         public static Image FromFile(string filename, bool useEmbeddedColorManagement)
101         {
102                 IntPtr imagePtr;
103                 Status st;
104
105                 if (!File.Exists (filename))
106                         throw new FileNotFoundException (filename);
107
108                 if (useEmbeddedColorManagement)
109                         st = GDIPlus.GdipLoadImageFromFileICM (filename, out imagePtr);
110                 else
111                         st = GDIPlus.GdipLoadImageFromFile (filename, out imagePtr);
112
113                 GDIPlus.CheckStatus (st);
114                 return new Bitmap (imagePtr);
115         }
116
117         public static Bitmap FromHbitmap(IntPtr hbitmap)
118         {
119                 return FromHbitmap (hbitmap, IntPtr.Zero);
120         }
121
122         public static Bitmap FromHbitmap(IntPtr hbitmap, IntPtr hpalette)
123         {               
124                 IntPtr imagePtr;
125                 Status st;
126
127                 st = GDIPlus.GdipCreateBitmapFromHBITMAP (hbitmap, hpalette, out imagePtr);
128
129                 GDIPlus.CheckStatus (st);
130                 return new Bitmap (imagePtr);
131         }
132
133         public static Image FromStream (Stream stream)
134         {
135                 return new Bitmap (stream);
136         }
137         
138         public static Image FromStream (Stream stream, bool useECM)
139         {
140                 return new Bitmap (stream, useECM);
141         }
142
143         // See http://support.microsoft.com/default.aspx?scid=kb;en-us;831419 for performance discussion        
144         public static Image FromStream (Stream stream, bool useECM, bool validateImageData)
145         {
146                 return new Bitmap (stream, useECM);
147         }
148
149         public static int GetPixelFormatSize(PixelFormat pixfmt)
150         {
151                 int result = 0;
152                 switch (pixfmt) {
153                         case PixelFormat.Format16bppArgb1555:
154                         case PixelFormat.Format16bppGrayScale:
155                         case PixelFormat.Format16bppRgb555:
156                         case PixelFormat.Format16bppRgb565:
157                                 result = 16;
158                                 break;
159                         case PixelFormat.Format1bppIndexed:
160                                 result = 1;
161                                 break;
162                         case PixelFormat.Format24bppRgb:
163                                 result = 24;
164                                 break;
165                         case PixelFormat.Format32bppArgb:
166                         case PixelFormat.Format32bppPArgb:
167                         case PixelFormat.Format32bppRgb:
168                                 result = 32;
169                                 break;
170                         case PixelFormat.Format48bppRgb:
171                                 result = 48;
172                                 break;
173                         case PixelFormat.Format4bppIndexed:
174                                 result = 4;
175                                 break;
176                         case PixelFormat.Format64bppArgb:
177                         case PixelFormat.Format64bppPArgb:
178                                 result = 64;
179                                 break;
180                         case PixelFormat.Format8bppIndexed:
181                                 result = 8;
182                                 break;
183                 }
184                 return result;
185         }
186
187         public static bool IsAlphaPixelFormat(PixelFormat pixfmt)
188         {
189                 bool result = false;
190                 switch (pixfmt) {
191                         case PixelFormat.Format16bppArgb1555:
192                         case PixelFormat.Format32bppArgb:
193                         case PixelFormat.Format32bppPArgb:
194                         case PixelFormat.Format64bppArgb:
195                         case PixelFormat.Format64bppPArgb:
196                                 result = true;
197                                 break;
198                         case PixelFormat.Format16bppGrayScale:
199                         case PixelFormat.Format16bppRgb555:
200                         case PixelFormat.Format16bppRgb565:
201                         case PixelFormat.Format1bppIndexed:
202                         case PixelFormat.Format24bppRgb:
203                         case PixelFormat.Format32bppRgb:
204                         case PixelFormat.Format48bppRgb:
205                         case PixelFormat.Format4bppIndexed:
206                         case PixelFormat.Format8bppIndexed:
207                                 result = false;
208                                 break;
209                 }
210                 return result;
211         }
212         
213         public static bool IsCanonicalPixelFormat (PixelFormat pixfmt)
214         {
215                 return ((pixfmt & PixelFormat.Canonical) != 0);
216         }
217         
218         public static bool IsExtendedPixelFormat (PixelFormat pixfmt)
219         {
220                 return ((pixfmt & PixelFormat.Extended) != 0);
221         }
222
223         internal void InitFromStream (Stream stream)
224         {
225                 IntPtr imagePtr;
226                 Status st;
227                 
228                 // Seeking required
229                 if (!stream.CanSeek) {
230                         byte[] buffer = new byte[256];
231                         int index = 0;
232                         int count;
233
234                         do {
235                                 if (buffer.Length < index + 256) {
236                                         byte[] newBuffer = new byte[buffer.Length * 2];
237                                         Array.Copy(buffer, newBuffer, buffer.Length);
238                                         buffer = newBuffer;
239                                 }
240                                 count = stream.Read(buffer, index, 256);
241                                 index += count;
242                         }
243                         while (count != 0);
244
245                         stream = new MemoryStream(buffer, 0, index);
246                 }
247
248                 // check for Unix platforms - see FAQ for more details
249                 // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F
250                 int platform = (int) Environment.OSVersion.Platform;
251                 if ((platform == 4) || (platform == 128)) {
252                         // Unix, with libgdiplus
253                         // We use a custom API for this, because there's no easy way
254                         // to get the Stream down to libgdiplus.  So, we wrap the stream
255                         // with a set of delegates.
256                         GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream);
257
258                         st = GDIPlus.GdipLoadImageFromDelegate_linux (sh.GetHeaderDelegate, sh.GetBytesDelegate,
259                                 sh.PutBytesDelegate, sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, out imagePtr);
260                 } else {
261                         // this is MS-land
262                         st = GDIPlus.GdipLoadImageFromStream(new ComIStreamWrapper(stream), out imagePtr);
263                 }
264
265                 GDIPlus.CheckStatus (st);
266                 nativeObject = imagePtr;
267         }
268
269         // non-static   
270         public RectangleF GetBounds (ref GraphicsUnit pageUnit)
271         {       
272                 RectangleF source;                      
273                 
274                 Status status = GDIPlus.GdipGetImageBounds (nativeObject, out source, ref pageUnit);
275                 GDIPlus.CheckStatus (status);           
276                 
277                 return source;
278         }
279         
280         public EncoderParameters GetEncoderParameterList(Guid format)
281         {
282                 Status status;
283                 uint sz;
284
285                 status = GDIPlus.GdipGetEncoderParameterListSize (nativeObject, ref format, out sz);
286                 GDIPlus.CheckStatus (status);
287
288                 IntPtr rawEPList = Marshal.AllocHGlobal ((int) sz);
289                 EncoderParameters eps;
290
291                 try {
292                         status = GDIPlus.GdipGetEncoderParameterList (nativeObject, ref format, sz, rawEPList);
293                         eps = EncoderParameters.FromNativePtr (rawEPList);
294                         GDIPlus.CheckStatus (status);
295                 }
296                 finally {
297                         Marshal.FreeHGlobal (rawEPList);
298                 }
299
300                 return eps;
301         }
302         
303         public int GetFrameCount(FrameDimension dimension)
304         {
305                 int count;
306                 Guid guid = dimension.Guid;
307                 Status status = GDIPlus.GdipImageGetFrameCount (nativeObject, ref guid, out  count); 
308
309                 GDIPlus.CheckStatus (status);           
310                 
311                 return count;
312                 
313         }
314         
315         public PropertyItem GetPropertyItem(int propid)
316         {
317                 int propSize;
318                 IntPtr property;
319                 PropertyItem item = new PropertyItem ();
320                 GdipPropertyItem gdipProperty = new GdipPropertyItem ();
321                 Status status;
322                         
323                 status = GDIPlus.GdipGetPropertyItemSize (nativeObject, propid, 
324                                                                         out propSize);
325                 GDIPlus.CheckStatus (status);
326
327                 /* Get PropertyItem */
328                 property = Marshal.AllocHGlobal (propSize);
329                 try {
330                         status = GDIPlus.GdipGetPropertyItem (nativeObject, propid, propSize, property);
331                         GDIPlus.CheckStatus (status);
332                         gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure (property, 
333                                                                 typeof (GdipPropertyItem));                                             
334                         GdipPropertyItem.MarshalTo (gdipProperty, item);
335                 }
336                 finally {
337                         Marshal.FreeHGlobal (property);
338                 }
339                 return item;
340         }
341         
342         public Image GetThumbnailImage (int thumbWidth, int thumbHeight, Image.GetThumbnailImageAbort callback, IntPtr callbackData)
343         {
344                 if ((thumbWidth <= 0) || (thumbHeight <= 0))
345                         throw new OutOfMemoryException ("Invalid thumbnail size");
346
347                 Image ThumbNail = new Bitmap (thumbWidth, thumbHeight);
348
349                 using (Graphics g = Graphics.FromImage (ThumbNail)) {
350                         Status status = GDIPlus.GdipDrawImageRectRectI (g.nativeObject, nativeObject,
351                                 0, 0, thumbWidth, thumbHeight,
352                                 0, 0, this.Width, this.Height,
353                                 GraphicsUnit.Pixel, IntPtr.Zero, null, IntPtr.Zero);
354
355                         GDIPlus.CheckStatus (status);
356                 }
357
358                 return ThumbNail;
359         }
360         
361         
362         public void RemovePropertyItem (int propid)
363         {               
364                 Status status = GDIPlus.GdipRemovePropertyItem (nativeObject, propid);
365                 GDIPlus.CheckStatus (status);                                   
366         }       
367         
368         public void RotateFlip (RotateFlipType rotateFlipType)
369         {                       
370                 Status status = GDIPlus.GdipImageRotateFlip (nativeObject, rotateFlipType);
371                 GDIPlus.CheckStatus (status);                           
372         }
373
374         internal ImageCodecInfo findEncoderForFormat (ImageFormat format)
375         {
376                 ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();                  
377                 ImageCodecInfo encoder = null;
378                 
379                 if (format.Guid.Equals (ImageFormat.MemoryBmp.Guid))
380                         format = ImageFormat.Png;
381         
382                 /* Look for the right encoder for our format*/
383                 for (int i = 0; i < encoders.Length; i++) {
384                         if (encoders[i].FormatID.Equals (format.Guid)) {
385                                 encoder = encoders[i];
386                                 break;
387                         }                       
388                 }
389
390                 return encoder;
391         }
392
393         public void Save (string filename)
394         {
395                 Save (filename, RawFormat);
396         }
397
398         public void Save(string filename, ImageFormat format) 
399         {
400                 ImageCodecInfo encoder = findEncoderForFormat (format);
401
402                 if (encoder == null)
403                         throw new ArgumentException ("No codec available for format:" + format.Guid);
404
405                 Save (filename, encoder, null);
406         }
407
408         public void Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
409         {
410                 Status st;
411                 Guid guid = encoder.Clsid;
412
413                 if (encoderParams == null) {
414                         st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, IntPtr.Zero);
415                 } else {
416                         IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
417                         st = GDIPlus.GdipSaveImageToFile (nativeObject, filename, ref guid, nativeEncoderParams);
418                         Marshal.FreeHGlobal (nativeEncoderParams);
419                 }
420
421                 GDIPlus.CheckStatus (st);
422         }
423
424         public void Save (Stream stream, ImageFormat format)
425         {
426                 ImageCodecInfo encoder = findEncoderForFormat (format);
427
428                 if (encoder == null)
429                         throw new ArgumentException ("No codec available for format:" + format.Guid);
430
431                 Save (stream, encoder, null);
432         }
433
434         public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams)
435         {
436                 Status st;
437                 IntPtr nativeEncoderParams;
438                 Guid guid = encoder.Clsid;
439
440                 if (encoderParams == null)
441                         nativeEncoderParams = IntPtr.Zero;
442                 else
443                         nativeEncoderParams = encoderParams.ToNativePtr ();
444
445                 try {
446                         // check for Unix platforms - see FAQ for more details
447                         // http://www.mono-project.com/FAQ:_Technical#How_to_detect_the_execution_platform_.3F
448                         int platform = (int) Environment.OSVersion.Platform;
449                         if ((platform == 4) || (platform == 128)) {
450                                 GDIPlus.GdiPlusStreamHelper sh = new GDIPlus.GdiPlusStreamHelper (stream);
451                                 st = GDIPlus.GdipSaveImageToDelegate_linux (nativeObject, sh.GetBytesDelegate, sh.PutBytesDelegate,
452                                         sh.SeekDelegate, sh.CloseDelegate, sh.SizeDelegate, ref guid, nativeEncoderParams);
453                         }
454                         else
455                                 st = GDIPlus.GdipSaveImageToStream(new HandleRef(this, nativeObject), new ComIStreamWrapper(stream), ref guid, new HandleRef(encoderParams, nativeEncoderParams));
456                 }
457                 finally {
458                         if (nativeEncoderParams != IntPtr.Zero)
459                                 Marshal.FreeHGlobal (nativeEncoderParams);
460                 }
461                 
462                 GDIPlus.CheckStatus (st);               
463         }
464         
465         public void SaveAdd (EncoderParameters encoderParams)
466         {
467                 Status st;
468                 
469                 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
470                 st = GDIPlus.GdipSaveAdd (nativeObject, nativeEncoderParams);
471                 Marshal.FreeHGlobal (nativeEncoderParams);
472                 GDIPlus.CheckStatus (st);
473         }
474                 
475         public void SaveAdd (Image image, EncoderParameters encoderParams)
476         {
477                 Status st;
478                 
479                 IntPtr nativeEncoderParams = encoderParams.ToNativePtr ();
480                 st = GDIPlus.GdipSaveAddImage (nativeObject, image.NativeObject, nativeEncoderParams);
481                 Marshal.FreeHGlobal (nativeEncoderParams);
482                 GDIPlus.CheckStatus (st);
483         }
484                 
485         public int SelectActiveFrame(FrameDimension dimension, int frameIndex)
486         {
487                 Guid guid = dimension.Guid;             
488                 Status st = GDIPlus.GdipImageSelectActiveFrame (nativeObject, ref guid, frameIndex);
489                 
490                 GDIPlus.CheckStatus (st);                       
491                 
492                 return frameIndex;              
493         }
494         
495         public void SetPropertyItem(PropertyItem propitem)
496         {
497                 IntPtr property;
498                 int size = Marshal.SizeOf (typeof(GdipPropertyItem));
499                 property = Marshal.AllocHGlobal (size);
500
501                 Marshal.StructureToPtr (propitem, property, true);
502                 Status status = GDIPlus.GdipSetPropertyItem (nativeObject, property);
503                 GDIPlus.CheckStatus (status);
504                 // FIXME: GdipSetPropertyItem isn't implemented in libgdiplus (but returns Ok)
505                 // so who's freeing "property" ? GDI+ ?
506         }
507
508         // properties   
509         [Browsable (false)]
510         public int Flags {
511                 get {
512                         int flags;
513                         
514                         Status status = GDIPlus.GdipGetImageFlags (nativeObject, out flags);                    
515                         GDIPlus.CheckStatus (status);                                           
516                         return flags;                   
517                 }
518         }
519         
520         [Browsable (false)]
521         public Guid[] FrameDimensionsList {
522                 get {
523                         uint found;
524                         Status status = GDIPlus.GdipImageGetFrameDimensionsCount (nativeObject, out found);
525                         GDIPlus.CheckStatus (status);
526                         Guid [] guid = new Guid [found];
527                         status = GDIPlus.GdipImageGetFrameDimensionsList (nativeObject, guid, found);
528                         GDIPlus.CheckStatus (status);  
529                         return guid;
530                 }
531         }
532
533         [DefaultValue (false)]
534         [Browsable (false)]
535         [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
536         public int Height {
537                 get {
538                         int height;                     
539                         Status status = GDIPlus.GdipGetImageHeight (nativeObject, out height);          
540                         GDIPlus.CheckStatus (status);                   
541                         
542                         return height;
543                 }
544         }
545         
546         public float HorizontalResolution {
547                 get {
548                         float resolution;
549                         
550                         Status status = GDIPlus.GdipGetImageHorizontalResolution (nativeObject, out resolution);                        
551                         GDIPlus.CheckStatus (status);                   
552                         
553                         return resolution;
554                 }
555         }
556         
557         [Browsable (false)]
558         public ColorPalette Palette {
559                 get {                                                   
560                         return retrieveGDIPalette();
561                 }
562                 set {
563                         storeGDIPalette(value);
564                 }
565         }
566
567         internal ColorPalette retrieveGDIPalette()
568         {
569                 ColorPalette ret = new ColorPalette();
570                 if (!IsIndexedPixelFormat (PixelFormat)) {
571                         return ret;
572                 }
573                 Status st;
574                 int bytes;
575
576                 st = GDIPlus.GdipGetImagePaletteSize (nativeObject, out bytes);
577                 GDIPlus.CheckStatus (st);
578                 IntPtr palette_data = Marshal.AllocHGlobal (bytes);
579                 try {
580                         st = GDIPlus.GdipGetImagePalette (nativeObject, palette_data, bytes);
581                         GDIPlus.CheckStatus (st);
582                         ret.setFromGDIPalette (palette_data);
583                         return ret;
584                 }
585
586                 finally {
587                         Marshal.FreeHGlobal (palette_data);
588                 }
589         }
590
591         internal void storeGDIPalette(ColorPalette palette)
592         {
593                 if (palette == null) {
594                         throw new ArgumentNullException("palette");
595                 }
596                 IntPtr palette_data = palette.getGDIPalette();
597                 if (palette_data == IntPtr.Zero) {
598                         return;
599                 }
600
601                 try {
602                         Status st = GDIPlus.GdipSetImagePalette (nativeObject, palette_data);
603                         GDIPlus.CheckStatus (st);
604                 }
605
606                 finally {
607                         Marshal.FreeHGlobal(palette_data);
608                 }
609         }
610
611                 
612         public SizeF PhysicalDimension {
613                 get {
614                         float width,  height;
615                         Status status = GDIPlus.GdipGetImageDimension (nativeObject, out width, out height);            
616                         GDIPlus.CheckStatus (status);                   
617                         
618                         return new SizeF (width, height);
619                 }
620         }
621         
622         public PixelFormat PixelFormat {
623                 get {                   
624                         PixelFormat pixFormat;                          
625                         Status status = GDIPlus.GdipGetImagePixelFormat (nativeObject, out pixFormat);          
626                         GDIPlus.CheckStatus (status);                   
627                         
628                         return pixFormat;
629                 }               
630         }
631         
632         [Browsable (false)]
633         public int[] PropertyIdList {
634                 get {
635                         uint propNumbers;
636                         
637                         Status status = GDIPlus.GdipGetPropertyCount (nativeObject, 
638                                                                         out propNumbers);                       
639                         GDIPlus.CheckStatus (status);
640                         
641                         int [] idList = new int [propNumbers];
642                         status = GDIPlus.GdipGetPropertyIdList (nativeObject, 
643                                                                 propNumbers, idList);
644                         GDIPlus.CheckStatus (status);
645                         
646                         return idList;
647                 }
648         }
649         
650         [Browsable (false)]
651         public PropertyItem[] PropertyItems {
652                 get {
653                         int propNums, propsSize, propSize;
654                         IntPtr properties, propPtr;
655                         PropertyItem[] items;
656                         GdipPropertyItem gdipProperty = new GdipPropertyItem ();
657                         Status status;
658                         
659                         status = GDIPlus.GdipGetPropertySize (nativeObject, out propsSize, out propNums);
660                         GDIPlus.CheckStatus (status);
661
662                         items =  new PropertyItem [propNums];
663                         
664                         if (propNums == 0)
665                                 return items;                   
666                                         
667                         /* Get PropertyItem list*/
668                         properties = Marshal.AllocHGlobal (propsSize * propNums);
669                         try {
670                                 status = GDIPlus.GdipGetAllPropertyItems (nativeObject, propsSize, 
671                                                                 propNums, properties);
672                                 GDIPlus.CheckStatus (status);
673
674                                 propSize = Marshal.SizeOf (gdipProperty);                       
675                                 propPtr = properties;
676                         
677                                 for (int i = 0; i < propNums; i++, propPtr = new IntPtr (propPtr.ToInt64 () + propSize)) {
678                                         gdipProperty = (GdipPropertyItem) Marshal.PtrToStructure 
679                                                 (propPtr, typeof (GdipPropertyItem));                                           
680                                         items [i] = new PropertyItem ();
681                                         GdipPropertyItem.MarshalTo (gdipProperty, items [i]);                                                           
682                                 }
683                         }
684                         finally {
685                                 Marshal.FreeHGlobal (properties);
686                         }
687                         return items;
688                 }
689         }
690
691         public ImageFormat RawFormat {
692                 get {
693                         Guid guid;
694                         Status st = GDIPlus.GdipGetImageRawFormat (nativeObject, out guid);
695                         
696                         GDIPlus.CheckStatus (st);
697                         return new ImageFormat (guid);                  
698                 }
699         }
700         
701         public Size Size {
702                 get {
703                         return new Size(Width, Height);
704                 }
705         }
706
707 #if NET_2_0
708         [DefaultValue (null)]
709         [LocalizableAttribute(false)] 
710         [BindableAttribute(true)]       
711         [TypeConverter (typeof (StringConverter))]
712         public object Tag { 
713                 get { return tag; }
714                 set { tag = value; }
715         }
716 #endif  
717         public float VerticalResolution {
718                 get {
719                         float resolution;
720                         
721                         Status status = GDIPlus.GdipGetImageVerticalResolution (nativeObject, out resolution);
722                         GDIPlus.CheckStatus (status);
723
724                         return resolution;
725                 }
726         }
727
728         [DefaultValue (false)]
729         [Browsable (false)]
730         [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
731         public int Width {
732                 get {
733                         int width;                      
734                         Status status = GDIPlus.GdipGetImageWidth (nativeObject, out width);            
735                         GDIPlus.CheckStatus (status);                   
736                         
737                         return width;
738                 }
739         }
740         
741         internal IntPtr NativeObject{
742                 get{
743                         return nativeObject;
744                 }
745                 set     {
746                         nativeObject = value;
747                 }
748         }
749         
750         public void Dispose ()
751         {
752                 Dispose (true);
753                 System.GC.SuppressFinalize (this);
754         }
755
756         ~Image ()
757         {
758                 Dispose (false);
759         }
760
761         private void DisposeResources ()
762         {
763                 Status status = GDIPlus.GdipDisposeImage (nativeObject);
764                 GDIPlus.CheckStatus (status);           
765         }
766         
767         protected virtual void Dispose (bool disposing)
768         {
769                 if (nativeObject != IntPtr.Zero){
770                         DisposeResources ();
771                         nativeObject = IntPtr.Zero;
772                 }
773         }
774         
775         public object Clone()
776         {                               
777
778                 IntPtr newimage = IntPtr.Zero;
779                 
780                 if (!(this is Bitmap)) 
781                         throw new NotImplementedException (); 
782                 
783                 Status status = GDIPlus.GdipCloneImage (NativeObject, out newimage);                    
784                 GDIPlus.CheckStatus (status);                   
785
786                 if (this is Bitmap){
787                         return new Bitmap (newimage);
788                 }
789                 
790                 throw new NotImplementedException ();           
791         }
792
793 }
794
795 }