Wrap always_inline and noinline attributes in compiler checks and use MSVC equivalent.
[mono.git] / mcs / class / System.Drawing / System.Drawing / Icon.cs
1 //
2 // System.Drawing.Icon.cs
3 //
4 // Authors:
5 //   Dennis Hayes (dennish@Raytek.com)
6 //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 //   Sanjay Gupta (gsanjay@novell.com)
8 //   Peter Dennis Bartok (pbartok@novell.com)
9 //   Sebastien Pouliot  <sebastien@ximian.com>
10 //
11 // Copyright (C) 2002 Ximian, Inc. http://www.ximian.com
12 // Copyright (C) 2004-2008 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.Collections;
35 using System.ComponentModel;
36 using System.Drawing.Imaging;
37 using System.IO;
38 using System.Runtime.Serialization;
39 using System.Runtime.InteropServices;
40 using System.Security.Permissions;
41
42 namespace System.Drawing
43 {
44 #if !NET_2_0
45         [ComVisible (false)] 
46 #endif 
47         [Serializable]  
48 #if !MONOTOUCH
49         [Editor ("System.Drawing.Design.IconEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))]
50 #endif
51         [TypeConverter(typeof(IconConverter))]
52
53         public sealed class Icon : MarshalByRefObject, ISerializable, ICloneable, IDisposable
54         {
55                 [StructLayout(LayoutKind.Sequential)]
56                 internal struct IconDirEntry {
57                         internal byte   width;          // Width of icon
58                         internal byte   height;         // Height of icon
59                         internal byte   colorCount;     // colors in icon 
60                         internal byte   reserved;       // Reserved
61                         internal ushort planes;         // Color Planes
62                         internal ushort bitCount;       // Bits per pixel
63                         internal uint   bytesInRes;     // bytes in resource
64                         internal uint   imageOffset;    // position in file 
65                 }; 
66
67                 [StructLayout(LayoutKind.Sequential)]
68                 internal struct IconDir {
69                         internal ushort                 idReserved;   // Reserved
70                         internal ushort                 idType;       // resource type (1 for icons)
71                         internal ushort                 idCount;      // how many images?
72                         internal IconDirEntry []        idEntries;    // the entries for each image
73                 };
74                 
75                 [StructLayout(LayoutKind.Sequential)]
76                 internal struct BitmapInfoHeader {
77                         internal uint   biSize; 
78                         internal int    biWidth; 
79                         internal int    biHeight; 
80                         internal ushort biPlanes; 
81                         internal ushort biBitCount; 
82                         internal uint   biCompression; 
83                         internal uint   biSizeImage; 
84                         internal int    biXPelsPerMeter; 
85                         internal int    biYPelsPerMeter; 
86                         internal uint   biClrUsed; 
87                         internal uint   biClrImportant; 
88                 };
89
90                 [StructLayout(LayoutKind.Sequential)]
91                 internal struct IconImage {
92                         internal BitmapInfoHeader       iconHeader;     //image header
93                         internal uint []                iconColors;     //colors table
94                         internal byte []                iconXOR;        // bits for XOR mask
95                         internal byte []                iconAND;        //bits for AND mask
96                 };      
97
98                 private Size iconSize;
99                 private IntPtr handle = IntPtr.Zero;
100                 private IconDir iconDir;
101                 private ushort id;
102                 private IconImage [] imageData;
103                 private bool undisposable;
104                 private bool disposed;
105                 private Bitmap bitmap;
106
107                 private Icon ()
108                 {
109                 }
110
111 #if !MONOTOUCH
112                 private Icon (IntPtr handle)
113                 {
114                         this.handle = handle;
115                         bitmap = Bitmap.FromHicon (handle);
116                         iconSize = new Size (bitmap.Width, bitmap.Height);
117                         if (GDIPlus.RunningOnUnix ()) {
118                                 bitmap = Bitmap.FromHicon (handle);
119                                 iconSize = new Size (bitmap.Width, bitmap.Height);
120                                 // FIXME: we need to convert the bitmap into an icon
121                         } else {
122                                 IconInfo ii;
123                                 GDIPlus.GetIconInfo (handle, out ii);
124                                 if (!ii.IsIcon)
125                                         throw new NotImplementedException (Locale.GetText ("Handle doesn't represent an ICON."));
126
127                                 // If this structure defines an icon, the hot spot is always in the center of the icon
128                                 iconSize = new Size (ii.xHotspot * 2, ii.yHotspot * 2);
129                                 bitmap = (Bitmap) Image.FromHbitmap (ii.hbmColor);
130                         }
131                         undisposable = true;
132                 }
133 #endif
134
135                 public Icon (Icon original, int width, int height)
136                         : this (original, new Size (width, height))
137                 {
138                 }
139
140                 public Icon (Icon original, Size size)
141                 {
142                         if (original == null)
143                                 throw new ArgumentException ("original");
144
145                         iconSize = size;
146                         iconDir = original.iconDir;
147                         
148                         int count = iconDir.idCount;
149                         if (count > 0) {
150                                 imageData = original.imageData;
151                                 id = UInt16.MaxValue;
152
153                                 for (ushort i=0; i < count; i++) {
154                                         IconDirEntry ide = iconDir.idEntries [i];
155                                         if ((ide.height == size.Height) || (ide.width == size.Width)) {
156                                                 id = i;
157                                                 break;
158                                         }
159                                 }
160
161                                 // if a perfect match isn't found we look for the biggest icon *smaller* than specified
162                                 if (id == UInt16.MaxValue) {
163                                         int requested = Math.Min (size.Height, size.Width);
164                                         IconDirEntry best = iconDir.idEntries [0];
165                                         for (ushort i=1; i < count; i++) {
166                                                 IconDirEntry ide = iconDir.idEntries [i];
167                                                 if ((ide.height < requested) || (ide.width < requested)) {
168                                                         if ((ide.height > best.height) || (ide.width > best.width))
169                                                                 id = i;
170                                                 }
171                                         }
172                                 }
173
174                                 // last one, if nothing better can be found
175                                 if (id == UInt16.MaxValue)
176                                         id = (ushort) (count - 1);
177
178                                 iconSize.Height = iconDir.idEntries [id].height;
179                                 iconSize.Width = iconDir.idEntries [id].width;
180                         } else {
181                                 iconSize.Height = size.Height;
182                                 iconSize.Width = size.Width;
183                         }
184
185                         if (original.bitmap != null)
186                                 bitmap = (Bitmap) original.bitmap.Clone ();
187                 }
188
189                 public Icon (Stream stream) : this (stream, 32, 32) 
190                 {
191                 }
192
193                 public Icon (Stream stream, int width, int height)
194                 {
195                         InitFromStreamWithSize (stream, width, height);
196                 }
197
198                 public Icon (string fileName)
199                 {
200                         using (FileStream fs = File.OpenRead (fileName)) {
201                                 InitFromStreamWithSize (fs, 32, 32);
202                         }
203                 }
204
205                 public Icon (Type type, string resource)
206                 {
207                         if (resource == null)
208                                 throw new ArgumentException ("resource");
209
210                         using (Stream s = type.Assembly.GetManifestResourceStream (type, resource)) {
211                                 if (s == null) {
212                                         string msg = Locale.GetText ("Resource '{0}' was not found.", resource);
213                                         throw new FileNotFoundException (msg);
214                                 }
215                                 InitFromStreamWithSize (s, 32, 32);             // 32x32 is default
216                         }
217                 }
218
219                 private Icon (SerializationInfo info, StreamingContext context)
220                 {
221                         MemoryStream dataStream = null;
222                         int width=0;
223                         int height=0;
224                         foreach (SerializationEntry serEnum in info) {
225                                 if (String.Compare(serEnum.Name, "IconData", true) == 0) {
226                                         dataStream = new MemoryStream ((byte []) serEnum.Value);
227                                 }
228                                 if (String.Compare(serEnum.Name, "IconSize", true) == 0) {
229                                         Size iconSize = (Size) serEnum.Value;
230                                         width = iconSize.Width;
231                                         height = iconSize.Height;
232                                 }
233                         }
234                         if (dataStream != null) {
235                                 dataStream.Seek (0, SeekOrigin.Begin);
236                                 InitFromStreamWithSize (dataStream, width, height);
237                         }
238                 }
239
240                 internal Icon (string resourceName, bool undisposable)
241                 {
242                         using (Stream s = typeof (Icon).Assembly.GetManifestResourceStream (resourceName)) {
243                                 if (s == null) {
244                                         string msg = Locale.GetText ("Resource '{0}' was not found.", resourceName);
245                                         throw new FileNotFoundException (msg);
246                                 }
247                                 InitFromStreamWithSize (s, 32, 32);             // 32x32 is default
248                         }
249                         this.undisposable = true;
250                 }
251
252                 void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context)
253                 {
254                         MemoryStream ms = new MemoryStream ();
255                         Save (ms);
256                         si.AddValue ("IconSize", this.Size, typeof (Size));
257                         si.AddValue ("IconData", ms.ToArray ());
258                 }
259
260 #if NET_2_0
261                 public Icon (Stream stream, Size size) : 
262                         this (stream, size.Width, size.Height)
263                 {
264                 }
265                 
266                 public Icon (string fileName, int width, int height)
267                 {
268                         using (FileStream fs = File.OpenRead (fileName)) {
269                                 InitFromStreamWithSize (fs, width, height);
270                         }
271                 }
272         
273                 public Icon (string fileName, Size size)
274                 {
275                         using (FileStream fs = File.OpenRead (fileName)) {
276                                 InitFromStreamWithSize (fs, size.Width, size.Height);
277                         }
278                 }
279
280                 [MonoLimitation ("The same icon, SystemIcons.WinLogo, is returned for all file types.")]
281                 public static Icon ExtractAssociatedIcon (string filePath)
282                 {
283                         if (String.IsNullOrEmpty (filePath))
284                                 throw new ArgumentException (Locale.GetText ("Null or empty path."), "filePath");
285                         if (!File.Exists (filePath))
286                                 throw new FileNotFoundException (Locale.GetText ("Couldn't find specified file."), filePath);
287
288                         return SystemIcons.WinLogo;
289                 }       
290 #endif
291
292                 public void Dispose ()
293                 {
294                         // SystemIcons requires this
295                         if (undisposable)
296                                 return;
297                         
298                         if (!disposed) {
299 #if !MONOTOUCH
300                                 if (GDIPlus.RunningOnWindows () && (handle != IntPtr.Zero)) {
301                                         GDIPlus.DestroyIcon (handle);
302                                         handle = IntPtr.Zero;
303                                 }
304 #endif
305                                 if (bitmap != null) {
306                                         bitmap.Dispose ();
307                                         bitmap = null;
308                                 }
309                                 GC.SuppressFinalize (this);
310                         }
311                         disposed = true;
312                 }
313
314                 public object Clone ()
315                 {
316                         return new Icon (this, Size);
317                 }
318                 
319 #if !MONOTOUCH
320                 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
321                 public static Icon FromHandle (IntPtr handle)
322                 {
323                         if (handle == IntPtr.Zero)
324                                 throw new ArgumentException ("handle");
325
326                         return new Icon (handle);
327                 }
328 #endif
329                 private void SaveIconImage (BinaryWriter writer, IconImage ii)
330                 {
331                         BitmapInfoHeader bih = ii.iconHeader;
332                         writer.Write (bih.biSize);
333                         writer.Write (bih.biWidth);
334                         writer.Write (bih.biHeight);
335                         writer.Write (bih.biPlanes);
336                         writer.Write (bih.biBitCount);
337                         writer.Write (bih.biCompression);
338                         writer.Write (bih.biSizeImage);
339                         writer.Write (bih.biXPelsPerMeter);
340                         writer.Write (bih.biYPelsPerMeter);
341                         writer.Write (bih.biClrUsed);
342                         writer.Write (bih.biClrImportant);
343
344                         //now write color table
345                         int colCount = ii.iconColors.Length;
346                         for (int j=0; j < colCount; j++)
347                                 writer.Write (ii.iconColors [j]);
348
349                         //now write XOR Mask
350                         writer.Write (ii.iconXOR);
351
352                         //now write AND Mask
353                         writer.Write (ii.iconAND);
354                 }
355
356                 private void SaveIconDirEntry (BinaryWriter writer, IconDirEntry ide, uint offset)
357                 {
358                         writer.Write (ide.width);
359                         writer.Write (ide.height);
360                         writer.Write (ide.colorCount);
361                         writer.Write (ide.reserved);
362                         writer.Write (ide.planes);
363                         writer.Write (ide.bitCount);
364                         writer.Write (ide.bytesInRes);
365                         writer.Write ((offset == UInt32.MaxValue) ? ide.imageOffset : offset);
366                 }
367
368                 private void SaveAll (BinaryWriter writer)
369                 {
370                         writer.Write (iconDir.idReserved);
371                         writer.Write (iconDir.idType);
372                         ushort count = iconDir.idCount;
373                         writer.Write (count);
374
375                         for (int i=0; i < (int)count; i++) {
376                                 SaveIconDirEntry (writer, iconDir.idEntries [i], UInt32.MaxValue);
377                         }
378
379                         for (int i=0; i < (int)count; i++) {
380                                 SaveIconImage (writer, imageData [i]);
381                         }
382                 }
383
384                 private void SaveBestSingleIcon (BinaryWriter writer, int width, int height)
385                 {
386                         writer.Write (iconDir.idReserved);
387                         writer.Write (iconDir.idType);
388                         writer.Write ((ushort)1);
389
390                         // find best entry and save it
391                         int best = 0;
392                         int bitCount = 0;
393                         for (int i=0; i < iconDir.idCount; i++) {
394                                 IconDirEntry ide = iconDir.idEntries [i];
395                                 if ((width == ide.width) && (height == ide.height)) {
396                                         if (ide.bitCount >= bitCount) {
397                                                 bitCount = ide.bitCount;
398                                                 best = i;
399                                         }
400                                 }
401                         }
402
403                         SaveIconDirEntry (writer, iconDir.idEntries [best], 22);
404                         SaveIconImage (writer, imageData [best]);
405                 }
406
407                 private void SaveBitmapAsIcon (BinaryWriter writer)
408                 {
409                         writer.Write ((ushort)0);       // idReserved must be 0
410                         writer.Write ((ushort)1);       // idType must be 1
411                         writer.Write ((ushort)1);       // only one icon
412
413                         // when transformed into a bitmap only a single image exists
414                         IconDirEntry ide = new IconDirEntry ();
415                         ide.width = (byte) bitmap.Width;
416                         ide.height = (byte) bitmap.Height;
417                         ide.colorCount = 0;     // 32 bbp == 0, for palette size
418                         ide.reserved = 0;       // always 0
419                         ide.planes = 0;
420                         ide.bitCount = 32;
421                         ide.imageOffset = 22;   // 22 is the first icon position (for single icon files)
422
423                         BitmapInfoHeader bih = new BitmapInfoHeader ();
424                         bih.biSize = (uint) Marshal.SizeOf (typeof (BitmapInfoHeader));
425                         bih.biWidth = bitmap.Width;
426                         bih.biHeight = 2 * bitmap.Height; // include both XOR and AND images
427                         bih.biPlanes = 1;
428                         bih.biBitCount = 32;
429                         bih.biCompression = 0;
430                         bih.biSizeImage = 0;
431                         bih.biXPelsPerMeter = 0;
432                         bih.biYPelsPerMeter = 0;
433                         bih.biClrUsed = 0;
434                         bih.biClrImportant = 0;
435
436                         IconImage ii = new IconImage ();
437                         ii.iconHeader = bih;
438                         ii.iconColors = new uint [0];   // no palette
439                         int xor_size = (((bih.biBitCount * bitmap.Width + 31) & ~31) >> 3) * bitmap.Height;
440                         ii.iconXOR = new byte [xor_size];
441                         int p = 0;
442                         for (int y = bitmap.Height - 1; y >=0; y--) {
443                                 for (int x = 0; x < bitmap.Width; x++) {
444                                         Color c = bitmap.GetPixel (x, y);
445                                         ii.iconXOR [p++] = c.B;
446                                         ii.iconXOR [p++] = c.G;
447                                         ii.iconXOR [p++] = c.R;
448                                         ii.iconXOR [p++] = c.A;
449                                 }
450                         }
451                         int and_line_size = (((Width + 31) & ~31) >> 3);        // must be a multiple of 4 bytes
452                         int and_size = and_line_size * bitmap.Height;
453                         ii.iconAND = new byte [and_size];
454
455                         ide.bytesInRes = (uint) (bih.biSize + xor_size + and_size);
456
457                         SaveIconDirEntry (writer, ide, UInt32.MaxValue);
458                         SaveIconImage (writer, ii);
459                 }
460
461                 private void Save (Stream outputStream, int width, int height)
462                 {
463                         BinaryWriter writer = new BinaryWriter (outputStream);
464                         // if we have the icon information then save from this
465                         if (iconDir.idEntries != null) {
466                                 if ((width == -1) && (height == -1))
467                                         SaveAll (writer);
468                                 else
469                                         SaveBestSingleIcon (writer, width, height);
470                         } else if (bitmap != null) {
471                                 // if the icon was created from a bitmap then convert it
472                                 SaveBitmapAsIcon (writer);
473                         }
474                         writer.Flush ();
475                 }
476
477                 public void Save (Stream outputStream)
478                 {
479                         if (outputStream == null)
480                                 throw new NullReferenceException ("outputStream");
481
482                         // save every icons available
483                         Save (outputStream, -1, -1);
484                 }
485 #if !MONOTOUCH
486                 internal Bitmap BuildBitmapOnWin32 ()
487                 {
488                         Bitmap bmp;
489
490                         if (imageData == null)
491                                 return new Bitmap (32, 32);
492
493                         IconImage ii = imageData [id];
494                         BitmapInfoHeader bih = ii.iconHeader;
495                         int biHeight = bih.biHeight / 2;
496
497                         int ncolors = (int)bih.biClrUsed;
498                         if ((ncolors == 0) && (bih.biBitCount < 24))
499                                 ncolors = (int)(1 << bih.biBitCount);
500
501                         switch (bih.biBitCount) {
502                         case 1:
503                                 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format1bppIndexed);
504                                 break;
505                         case 4:
506                                 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format4bppIndexed);
507                                 break;
508                         case 8:
509                                 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format8bppIndexed);
510                                 break;
511                         case 24:
512                                 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format24bppRgb);
513                                 break;
514                         case 32:
515                                 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format32bppArgb);
516                                 break;
517                         default:
518                                 string msg = Locale.GetText ("Unexpected number of bits: {0}", bih.biBitCount);
519                                 throw new Exception (msg);
520                         }
521
522                         if (bih.biBitCount < 24) {
523                                 ColorPalette pal = bmp.Palette; // Managed palette
524
525                                 for (int i = 0; i < ii.iconColors.Length; i++) {
526                                         pal.Entries[i] = Color.FromArgb ((int)ii.iconColors[i] | unchecked((int)0xff000000));
527                                 }
528                                 bmp.Palette = pal;
529                         }
530
531                         int bytesPerLine = (int)((((bih.biWidth * bih.biBitCount) + 31) & ~31) >> 3);
532                         BitmapData bits = bmp.LockBits (new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
533
534                         for (int y = 0; y < biHeight; y++) {
535                                 Marshal.Copy (ii.iconXOR, bytesPerLine * y, 
536                                         (IntPtr)(bits.Scan0.ToInt64() + bits.Stride * (biHeight - 1 - y)), bytesPerLine);
537                         }
538                         
539                         bmp.UnlockBits (bits);
540
541                         bmp = new Bitmap (bmp); // This makes a 32bpp image out of an indexed one
542
543                         // Apply the mask to make properly transparent
544                         bytesPerLine = (int)((((bih.biWidth) + 31) & ~31) >> 3);
545                         for (int y = 0; y < biHeight; y++) {
546                                 for (int x = 0; x < bih.biWidth / 8; x++) {
547                                         for (int bit = 7; bit >= 0; bit--) {
548                                                 if (((ii.iconAND[y * bytesPerLine +x] >> bit) & 1) != 0) {
549                                                         bmp.SetPixel (x*8 + 7-bit, biHeight - y - 1, Color.Transparent);
550                                                 }
551                                         }
552                                 }
553                         }
554
555                         return bmp;
556                 }
557
558                 internal Bitmap GetInternalBitmap ()
559                 {
560                         if (bitmap == null) {
561                                 if (GDIPlus.RunningOnUnix ()) {
562                                         // Mono's libgdiplus doesn't require to keep the stream alive when loading images
563                                         using (MemoryStream ms = new MemoryStream ()) {
564                                                 // save the current icon
565                                                 Save (ms, Width, Height);
566                                                 ms.Position = 0;
567
568                                                 // libgdiplus can now decode icons
569                                                 bitmap = (Bitmap) Image.LoadFromStream (ms, false);
570                                         }
571                                 } else {
572                                         // MS GDI+ ICO codec is more limited than the MS Icon class
573                                         // so we can't, reliably, get bitmap using it. We need to do this the "slow" way
574                                         bitmap = BuildBitmapOnWin32 ();
575                                 }
576                         }
577
578                         return bitmap;
579                 }
580
581                 // note: all bitmaps are 32bits ARGB - no matter what the icon format (bitcount) was
582                 public Bitmap ToBitmap ()
583                 {
584                         if (disposed)
585                                 throw new ObjectDisposedException (Locale.GetText ("Icon instance was disposed."));
586
587                         // note: we can't return the original image because
588                         // (a) we have no control over the bitmap instance we return (i.e. it could be disposed)
589                         // (b) the palette, flags won't match MS results. See MonoTests.System.Drawing.Imaging.IconCodecTest.
590                         //     Image16 for the differences
591                         return new Bitmap (GetInternalBitmap ());
592                 }
593 #endif
594                 public override string ToString ()
595                 {
596                         //is this correct, this is what returned by .Net
597                         return "<Icon>";                        
598                 }
599                 
600 #if !MONOTOUCH
601                 [Browsable (false)]
602                 public IntPtr Handle {
603                         get {
604                                 // note: this handle doesn't survive the lifespan of the icon instance
605                                 if (!disposed && (handle == IntPtr.Zero)) {
606                                         if (GDIPlus.RunningOnUnix ()) {
607                                                 handle = GetInternalBitmap ().NativeObject;
608                                         } else {
609                                                 // remember that this block executes only with MS GDI+
610                                                 IconInfo ii = new IconInfo ();
611                                                 ii.IsIcon = true;
612                                                 ii.hbmColor = ToBitmap ().GetHbitmap ();
613                                                 ii.hbmMask = ii.hbmColor;
614                                                 handle = GDIPlus.CreateIconIndirect (ref ii);
615                                         }
616                                 }
617                                 return handle;
618                         }
619                 }
620 #endif
621                 [Browsable (false)]
622                 public int Height {
623                         get {
624                                 return iconSize.Height;
625                         }
626                 }
627
628                 public Size Size {
629                         get {
630                                 return iconSize;
631                         }
632                 }
633
634                 [Browsable (false)]
635                 public int Width {
636                         get {
637                                 return iconSize.Width;
638                         }
639                 }
640
641                 ~Icon ()
642                 {
643                         Dispose ();
644                 }
645                         
646                 private void InitFromStreamWithSize (Stream stream, int width, int height)
647                 {
648                         //read the icon header
649                         if (stream == null || stream.Length == 0)
650                                 throw new System.ArgumentException ("The argument 'stream' must be a picture that can be used as a Icon", "stream");
651                         
652                         BinaryReader reader = new BinaryReader (stream);
653
654                         //iconDir = new IconDir ();
655                         iconDir.idReserved = reader.ReadUInt16();
656                         if (iconDir.idReserved != 0) //must be 0
657                                 throw new System.ArgumentException ("Invalid Argument", "stream");
658                         
659                         iconDir.idType = reader.ReadUInt16();
660                         if (iconDir.idType != 1) //must be 1
661                                 throw new System.ArgumentException ("Invalid Argument", "stream");
662
663                         ushort dirEntryCount = reader.ReadUInt16();
664                         ArrayList entries = new ArrayList (dirEntryCount);
665                         bool sizeObtained = false;
666                         // now read in the IconDirEntry structures
667                         for (int i = 0; i < dirEntryCount; i++) {
668                                 IconDirEntry ide;
669                                 ide.width = reader.ReadByte ();
670                                 ide.height = reader.ReadByte ();
671                                 ide.colorCount = reader.ReadByte ();
672                                 ide.reserved = reader.ReadByte ();
673                                 ide.planes = reader.ReadUInt16 ();
674                                 ide.bitCount = reader.ReadUInt16 ();
675                                 ide.bytesInRes = reader.ReadUInt32 ();
676                                 ide.imageOffset = reader.ReadUInt32 ();
677 #if false
678 Console.WriteLine ("Entry: {0}", i);
679 Console.WriteLine ("\tide.width: {0}", ide.width);
680 Console.WriteLine ("\tide.height: {0}", ide.height);
681 Console.WriteLine ("\tide.colorCount: {0}", ide.colorCount);
682 Console.WriteLine ("\tide.reserved: {0}", ide.reserved);
683 Console.WriteLine ("\tide.planes: {0}", ide.planes);
684 Console.WriteLine ("\tide.bitCount: {0}", ide.bitCount);
685 Console.WriteLine ("\tide.bytesInRes: {0}", ide.bytesInRes);
686 Console.WriteLine ("\tide.imageOffset: {0}", ide.imageOffset);
687 #endif
688
689                                 // 256x256 icons are decoded as 0x0 (width and height are encoded as BYTE)
690                                 // and we ignore them just like MS does (at least up to fx 2.0)
691                                 if ((ide.width == 0) && (ide.height == 0))
692                                         continue;
693
694                                 int index = entries.Add (ide);
695
696                                 //is this is the best fit??
697                                 if (!sizeObtained) {
698                                         if ((ide.height == height) || (ide.width == width)) {
699                                                 this.id = (ushort) index;
700                                                 sizeObtained = true;
701                                                 this.iconSize.Height = ide.height;
702                                                 this.iconSize.Width = ide.width;
703                                         }
704                                 }
705                         }
706
707                         // Vista 256x256 icons points directly to a PNG bitmap
708                         dirEntryCount = (ushort) entries.Count;
709                         if (dirEntryCount == 0)
710                                 throw new Win32Exception (0, "No valid icon entry were found.");
711
712                         iconDir.idCount = dirEntryCount;
713                         imageData = new IconImage [dirEntryCount];
714                         iconDir.idEntries = new IconDirEntry [dirEntryCount];
715                         entries.CopyTo (iconDir.idEntries);
716
717                         //if we havent found the best match, return the one with the
718                         //largest size. Is this approach correct??
719                         if (!sizeObtained){
720                                 uint largestSize = 0;
721                                 for (int j=0; j<dirEntryCount; j++){
722                                         if (iconDir.idEntries [j].bytesInRes >= largestSize)    {
723                                                 largestSize = iconDir.idEntries [j].bytesInRes;
724                                                 this.id = (ushort) j;
725                                                 this.iconSize.Height = iconDir.idEntries [j].height;
726                                                 this.iconSize.Width = iconDir.idEntries [j].width;
727                                         }
728                                 }
729                         }
730                         
731                         //now read in the icon data
732                         for (int j = 0; j<dirEntryCount; j++)
733                         {
734                                 IconImage iidata = new IconImage();
735                                 BitmapInfoHeader bih = new BitmapInfoHeader();
736                                 stream.Seek (iconDir.idEntries [j].imageOffset, SeekOrigin.Begin);
737                                 byte [] buffer = new byte [iconDir.idEntries [j].bytesInRes];
738                                 stream.Read (buffer, 0, buffer.Length);
739                                 BinaryReader bihReader = new BinaryReader (new MemoryStream(buffer));
740                                 bih.biSize = bihReader.ReadUInt32 ();
741                                 bih.biWidth = bihReader.ReadInt32 ();
742                                 bih.biHeight = bihReader.ReadInt32 ();
743                                 bih.biPlanes = bihReader.ReadUInt16 ();
744                                 bih.biBitCount = bihReader.ReadUInt16 ();
745                                 bih.biCompression = bihReader.ReadUInt32 ();
746                                 bih.biSizeImage = bihReader.ReadUInt32 ();
747                                 bih.biXPelsPerMeter = bihReader.ReadInt32 ();
748                                 bih.biYPelsPerMeter = bihReader.ReadInt32 ();
749                                 bih.biClrUsed = bihReader.ReadUInt32 ();
750                                 bih.biClrImportant = bihReader.ReadUInt32 ();
751 #if false
752 Console.WriteLine ("Entry: {0}", j);
753 Console.WriteLine ("\tbih.biSize: {0}", bih.biSize);
754 Console.WriteLine ("\tbih.biWidth: {0}", bih.biWidth);
755 Console.WriteLine ("\tbih.biHeight: {0}", bih.biHeight);
756 Console.WriteLine ("\tbih.biPlanes: {0}", bih.biPlanes);
757 Console.WriteLine ("\tbih.biBitCount: {0}", bih.biBitCount);
758 Console.WriteLine ("\tbih.biCompression: {0}", bih.biCompression);
759 Console.WriteLine ("\tbih.biSizeImage: {0}", bih.biSizeImage);
760 Console.WriteLine ("\tbih.biXPelsPerMeter: {0}", bih.biXPelsPerMeter);
761 Console.WriteLine ("\tbih.biYPelsPerMeter: {0}", bih.biYPelsPerMeter);
762 Console.WriteLine ("\tbih.biClrUsed: {0}", bih.biClrUsed);
763 Console.WriteLine ("\tbih.biClrImportant: {0}", bih.biClrImportant);
764 #endif
765                                 iidata.iconHeader = bih;
766                                 //Read the number of colors used and corresponding memory occupied by
767                                 //color table. Fill this memory chunk into rgbquad[]
768                                 int numColors;
769                                 switch (bih.biBitCount){
770                                         case 1: numColors = 2;
771                                                 break;
772                                         case 4: numColors = 16;
773                                                 break;
774                                         case 8: numColors = 256;
775                                                 break;
776                                         default: numColors = 0;
777                                                 break;
778                                 }
779                                 
780                                 iidata.iconColors = new uint [numColors];
781                                 for (int i=0; i<numColors; i++)
782                                         iidata.iconColors [i] = bihReader.ReadUInt32 ();
783
784                                 //XOR mask is immediately after ColorTable and its size is 
785                                 //icon height* no. of bytes per line
786                                 
787                                 //icon height is half of BITMAPINFOHEADER.biHeight, since it contains
788                                 //both XOR as well as AND mask bytes
789                                 int iconHeight = bih.biHeight/2;
790                                 
791                                 //bytes per line should should be uint aligned
792                                 int numBytesPerLine = ((((bih.biWidth * bih.biPlanes * bih.biBitCount)+ 31)>>5)<<2);
793                                 
794                                 //Determine the XOR array Size
795                                 int xorSize = numBytesPerLine * iconHeight;
796                                 iidata.iconXOR = new byte [xorSize];
797                                 int nread = bihReader.Read (iidata.iconXOR, 0, xorSize);
798                                 if (nread != xorSize) {
799                                         string msg = Locale.GetText ("{0} data length expected {1}, read {2}", "XOR", xorSize, nread);
800                                         throw new ArgumentException (msg, "stream");
801                                 }
802                                 
803                                 //Determine the AND array size
804                                 numBytesPerLine = (int)((((bih.biWidth) + 31) & ~31) >> 3);
805                                 int andSize = numBytesPerLine * iconHeight;
806                                 iidata.iconAND = new byte [andSize];
807                                 nread = bihReader.Read (iidata.iconAND, 0, andSize);
808                                 if (nread != andSize) {
809                                         string msg = Locale.GetText ("{0} data length expected {1}, read {2}", "AND", andSize, nread);
810                                         throw new ArgumentException (msg, "stream");
811                                 }
812                                 
813                                 imageData [j] = iidata;
814                                 bihReader.Close();
815                         }                       
816
817                         reader.Close();
818                 }
819         }
820 }