2 // System.Drawing.Icon.cs
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>
11 // Copyright (C) 2002 Ximian, Inc. http://www.ximian.com
12 // Copyright (C) 2004-2008 Novell, Inc (http://www.novell.com)
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:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
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.
34 using System
.Collections
;
35 using System
.ComponentModel
;
36 using System
.Drawing
.Imaging
;
38 using System
.Runtime
.Serialization
;
39 using System
.Runtime
.InteropServices
;
40 using System
.Security
.Permissions
;
42 namespace System
.Drawing
48 [Editor ("System.Drawing.Design.IconEditor, " + Consts
.AssemblySystem_Drawing_Design
, typeof (System
.Drawing
.Design
.UITypeEditor
))]
49 [TypeConverter(typeof(IconConverter
))]
50 public sealed class Icon
: MarshalByRefObject
, ISerializable
, ICloneable
, IDisposable
52 [StructLayout(LayoutKind
.Sequential
)]
53 internal struct IconDirEntry
{
54 internal byte width
; // Width of icon
55 internal byte height
; // Height of icon
56 internal byte colorCount
; // colors in icon
57 internal byte reserved
; // Reserved
58 internal ushort planes
; // Color Planes
59 internal ushort bitCount
; // Bits per pixel
60 internal uint bytesInRes
; // bytes in resource
61 internal uint imageOffset
; // position in file
64 [StructLayout(LayoutKind
.Sequential
)]
65 internal struct IconDir
{
66 internal ushort idReserved
; // Reserved
67 internal ushort idType
; // resource type (1 for icons)
68 internal ushort idCount
; // how many images?
69 internal IconDirEntry
[] idEntries
; // the entries for each image
72 [StructLayout(LayoutKind
.Sequential
)]
73 internal struct BitmapInfoHeader
{
76 internal int biHeight
;
77 internal ushort biPlanes
;
78 internal ushort biBitCount
;
79 internal uint biCompression
;
80 internal uint biSizeImage
;
81 internal int biXPelsPerMeter
;
82 internal int biYPelsPerMeter
;
83 internal uint biClrUsed
;
84 internal uint biClrImportant
;
87 [StructLayout(LayoutKind
.Sequential
)]
88 internal struct IconImage
{
89 internal BitmapInfoHeader iconHeader
; //image header
90 internal uint [] iconColors
; //colors table
91 internal byte [] iconXOR
; // bits for XOR mask
92 internal byte [] iconAND
; //bits for AND mask
95 private Size iconSize
;
96 private IntPtr handle
= IntPtr
.Zero
;
97 private IconDir iconDir
;
99 private IconImage
[] imageData
;
100 private bool undisposable
;
101 private bool disposed
;
102 private Bitmap bitmap
;
108 private Icon (IntPtr handle
)
110 this.handle
= handle
;
111 if (GDIPlus
.RunningOnUnix ()) {
112 bitmap
= Bitmap
.FromHicon (handle
);
113 iconSize
= new Size (bitmap
.Width
, bitmap
.Height
);
114 // FIXME: we need to convert the bitmap into an icon
117 GDIPlus
.GetIconInfo (handle
, out ii
);
119 throw new NotImplementedException (Locale
.GetText ("Handle doesn't represent an ICON."));
121 // If this structure defines an icon, the hot spot is always in the center of the icon
122 iconSize
= new Size (ii
.xHotspot
* 2, ii
.yHotspot
* 2);
123 bitmap
= (Bitmap
) Image
.FromHbitmap (ii
.hbmColor
);
128 public Icon (Icon original
, int width
, int height
)
129 : this (original
, new Size (width
, height
))
133 public Icon (Icon original
, Size size
)
135 if (original
== null)
136 throw new ArgumentException ("original");
139 iconDir
= original
.iconDir
;
141 int count
= iconDir
.idCount
;
143 imageData
= original
.imageData
;
144 id
= UInt16
.MaxValue
;
146 for (ushort i
=0; i
< count
; i
++) {
147 IconDirEntry ide
= iconDir
.idEntries
[i
];
148 if ((ide
.height
== size
.Height
) || (ide
.width
== size
.Width
)) {
154 // if a perfect match isn't found we look for the biggest icon *smaller* than specified
155 if (id
== UInt16
.MaxValue
) {
156 int requested
= Math
.Min (size
.Height
, size
.Width
);
157 IconDirEntry best
= iconDir
.idEntries
[0];
158 for (ushort i
=1; i
< count
; i
++) {
159 IconDirEntry ide
= iconDir
.idEntries
[i
];
160 if ((ide
.height
< requested
) || (ide
.width
< requested
)) {
161 if ((ide
.height
> best
.height
) || (ide
.width
> best
.width
))
167 // last one, if nothing better can be found
168 if (id
== UInt16
.MaxValue
)
169 id
= (ushort) (count
- 1);
171 iconSize
.Height
= iconDir
.idEntries
[id
].height
;
172 iconSize
.Width
= iconDir
.idEntries
[id
].width
;
174 iconSize
.Height
= size
.Height
;
175 iconSize
.Width
= size
.Width
;
178 if (original
.bitmap
!= null)
179 bitmap
= (Bitmap
) original
.bitmap
.Clone ();
182 public Icon (Stream stream
) : this (stream
, 32, 32)
186 public Icon (Stream stream
, int width
, int height
)
188 InitFromStreamWithSize (stream
, width
, height
);
191 public Icon (string fileName
)
193 using (FileStream fs
= File
.OpenRead (fileName
)) {
194 InitFromStreamWithSize (fs
, 32, 32);
198 public Icon (Type type
, string resource
)
200 if (resource
== null)
201 throw new ArgumentException ("resource");
203 using (Stream s
= type
.Assembly
.GetManifestResourceStream (type
, resource
)) {
205 string msg
= Locale
.GetText ("Resource '{0}' was not found.", resource
);
206 throw new FileNotFoundException (msg
);
208 InitFromStreamWithSize (s
, 32, 32); // 32x32 is default
212 private Icon (SerializationInfo info
, StreamingContext context
)
214 MemoryStream dataStream
= null;
217 foreach (SerializationEntry serEnum
in info
) {
218 if (String
.Compare(serEnum
.Name
, "IconData", true) == 0) {
219 dataStream
= new MemoryStream ((byte []) serEnum
.Value
);
221 if (String
.Compare(serEnum
.Name
, "IconSize", true) == 0) {
222 Size iconSize
= (Size
) serEnum
.Value
;
223 width
= iconSize
.Width
;
224 height
= iconSize
.Height
;
227 if ((dataStream
!= null) && (width
== height
)) {
228 dataStream
.Seek (0, SeekOrigin
.Begin
);
229 InitFromStreamWithSize (dataStream
, width
, height
);
233 internal Icon (string resourceName
, bool undisposable
)
235 using (Stream s
= typeof (Icon
).Assembly
.GetManifestResourceStream (resourceName
)) {
237 string msg
= Locale
.GetText ("Resource '{0}' was not found.", resourceName
);
238 throw new FileNotFoundException (msg
);
240 InitFromStreamWithSize (s
, 32, 32); // 32x32 is default
242 this.undisposable
= true;
245 void ISerializable
.GetObjectData(SerializationInfo si
, StreamingContext context
)
247 MemoryStream ms
= new MemoryStream ();
249 si
.AddValue ("IconSize", this.Size
, typeof (Size
));
250 si
.AddValue ("IconData", ms
.ToArray ());
254 public Icon (Stream stream
, Size size
) :
255 this (stream
, size
.Width
, size
.Height
)
259 public Icon (string fileName
, int width
, int height
)
261 using (FileStream fs
= File
.OpenRead (fileName
)) {
262 InitFromStreamWithSize (fs
, width
, height
);
266 public Icon (string fileName
, Size size
)
268 using (FileStream fs
= File
.OpenRead (fileName
)) {
269 InitFromStreamWithSize (fs
, size
.Width
, size
.Height
);
273 [MonoLimitation ("The same icon, SystemIcons.WinLogo, is returned for all file types.")]
274 public static Icon
ExtractAssociatedIcon (string filePath
)
276 if (String
.IsNullOrEmpty (filePath
))
277 throw new ArgumentException (Locale
.GetText ("Null or empty path."), "filePath");
278 if (!File
.Exists (filePath
))
279 throw new FileNotFoundException (Locale
.GetText ("Couldn't find specified file."), filePath
);
281 return SystemIcons
.WinLogo
;
285 public void Dispose ()
287 // SystemIcons requires this
292 if (GDIPlus
.RunningOnWindows () && (handle
!= IntPtr
.Zero
)) {
293 GDIPlus
.DestroyIcon (handle
);
294 handle
= IntPtr
.Zero
;
296 if (bitmap
!= null) {
300 GC
.SuppressFinalize (this);
305 public object Clone ()
307 return new Icon (this, Size
);
310 [SecurityPermission (SecurityAction
.LinkDemand
, UnmanagedCode
= true)]
311 public static Icon
FromHandle (IntPtr handle
)
313 if (handle
== IntPtr
.Zero
)
314 throw new ArgumentException ("handle");
316 return new Icon (handle
);
319 private void SaveIconImage (BinaryWriter writer
, IconImage ii
)
321 BitmapInfoHeader bih
= ii
.iconHeader
;
322 writer
.Write (bih
.biSize
);
323 writer
.Write (bih
.biWidth
);
324 writer
.Write (bih
.biHeight
);
325 writer
.Write (bih
.biPlanes
);
326 writer
.Write (bih
.biBitCount
);
327 writer
.Write (bih
.biCompression
);
328 writer
.Write (bih
.biSizeImage
);
329 writer
.Write (bih
.biXPelsPerMeter
);
330 writer
.Write (bih
.biYPelsPerMeter
);
331 writer
.Write (bih
.biClrUsed
);
332 writer
.Write (bih
.biClrImportant
);
334 //now write color table
335 int colCount
= ii
.iconColors
.Length
;
336 for (int j
=0; j
< colCount
; j
++)
337 writer
.Write (ii
.iconColors
[j
]);
340 writer
.Write (ii
.iconXOR
);
343 writer
.Write (ii
.iconAND
);
346 private void SaveIconDirEntry (BinaryWriter writer
, IconDirEntry ide
, uint offset
)
348 writer
.Write (ide
.width
);
349 writer
.Write (ide
.height
);
350 writer
.Write (ide
.colorCount
);
351 writer
.Write (ide
.reserved
);
352 writer
.Write (ide
.planes
);
353 writer
.Write (ide
.bitCount
);
354 writer
.Write (ide
.bytesInRes
);
355 writer
.Write ((offset
== UInt32
.MaxValue
) ? ide
.imageOffset
: offset
);
358 private void SaveAll (BinaryWriter writer
)
360 writer
.Write (iconDir
.idReserved
);
361 writer
.Write (iconDir
.idType
);
362 ushort count
= iconDir
.idCount
;
363 writer
.Write (count
);
365 for (int i
=0; i
< (int)count
; i
++) {
366 SaveIconDirEntry (writer
, iconDir
.idEntries
[i
], UInt32
.MaxValue
);
369 for (int i
=0; i
< (int)count
; i
++) {
370 SaveIconImage (writer
, imageData
[i
]);
374 private void SaveBestSingleIcon (BinaryWriter writer
, int width
, int height
)
376 writer
.Write (iconDir
.idReserved
);
377 writer
.Write (iconDir
.idType
);
378 writer
.Write ((ushort)1);
380 // find best entry and save it
383 for (int i
=0; i
< iconDir
.idCount
; i
++) {
384 IconDirEntry ide
= iconDir
.idEntries
[i
];
385 if ((width
== ide
.width
) && (height
== ide
.height
)) {
386 if (ide
.bitCount
>= bitCount
) {
387 bitCount
= ide
.bitCount
;
393 SaveIconDirEntry (writer
, iconDir
.idEntries
[best
], 22);
394 SaveIconImage (writer
, imageData
[best
]);
397 private void SaveBitmapAsIcon (BinaryWriter writer
)
399 writer
.Write ((ushort)0); // idReserved must be 0
400 writer
.Write ((ushort)1); // idType must be 1
401 writer
.Write ((ushort)1); // only one icon
403 // when transformed into a bitmap only a single image exists
404 IconDirEntry ide
= new IconDirEntry ();
405 ide
.width
= (byte) bitmap
.Width
;
406 ide
.height
= (byte) bitmap
.Height
;
407 ide
.colorCount
= 0; // 32 bbp == 0, for palette size
408 ide
.reserved
= 0; // always 0
411 ide
.imageOffset
= 22; // 22 is the first icon position (for single icon files)
413 BitmapInfoHeader bih
= new BitmapInfoHeader ();
414 bih
.biSize
= (uint) Marshal
.SizeOf (typeof (BitmapInfoHeader
));
415 bih
.biWidth
= bitmap
.Width
;
416 bih
.biHeight
= 2 * bitmap
.Height
; // include both XOR and AND images
419 bih
.biCompression
= 0;
421 bih
.biXPelsPerMeter
= 0;
422 bih
.biYPelsPerMeter
= 0;
424 bih
.biClrImportant
= 0;
426 IconImage ii
= new IconImage ();
428 ii
.iconColors
= new uint [0]; // no palette
429 int xor_size
= (((bih
.biBitCount
* bitmap
.Width
+ 31) & ~
31) >> 3) * bitmap
.Height
;
430 ii
.iconXOR
= new byte [xor_size
];
432 for (int y
= bitmap
.Height
- 1; y
>=0; y
--) {
433 for (int x
= 0; x
< bitmap
.Width
; x
++) {
434 Color c
= bitmap
.GetPixel (x
, y
);
435 ii
.iconXOR
[p
++] = c
.B
;
436 ii
.iconXOR
[p
++] = c
.G
;
437 ii
.iconXOR
[p
++] = c
.R
;
438 ii
.iconXOR
[p
++] = c
.A
;
441 int and_line_size
= (((Width
+ 31) & ~
31) >> 3); // must be a multiple of 4 bytes
442 int and_size
= and_line_size
* bitmap
.Height
;
443 ii
.iconAND
= new byte [and_size
];
445 ide
.bytesInRes
= (uint) (bih
.biSize
+ xor_size
+ and_size
);
447 SaveIconDirEntry (writer
, ide
, UInt32
.MaxValue
);
448 SaveIconImage (writer
, ii
);
451 private void Save (Stream outputStream
, int width
, int height
)
453 BinaryWriter writer
= new BinaryWriter (outputStream
);
454 // if we have the icon information then save from this
455 if (iconDir
.idEntries
!= null) {
456 if ((width
== -1) && (height
== -1))
459 SaveBestSingleIcon (writer
, width
, height
);
460 } else if (bitmap
!= null) {
461 // if the icon was created from a bitmap then convert it
462 SaveBitmapAsIcon (writer
);
467 public void Save (Stream outputStream
)
469 if (outputStream
== null)
470 throw new NullReferenceException ("outputStream");
472 // save every icons available
473 Save (outputStream
, -1, -1);
476 internal Bitmap
BuildBitmapOnWin32 ()
480 if (imageData
== null)
481 return new Bitmap (32, 32);
483 IconImage ii
= imageData
[id
];
484 BitmapInfoHeader bih
= ii
.iconHeader
;
485 int biHeight
= bih
.biHeight
/ 2;
487 int ncolors
= (int)bih
.biClrUsed
;
488 if ((ncolors
== 0) && (bih
.biBitCount
< 24))
489 ncolors
= (int)(1 << bih
.biBitCount
);
491 switch (bih
.biBitCount
) {
493 bmp
= new Bitmap (bih
.biWidth
, biHeight
, PixelFormat
.Format1bppIndexed
);
496 bmp
= new Bitmap (bih
.biWidth
, biHeight
, PixelFormat
.Format4bppIndexed
);
499 bmp
= new Bitmap (bih
.biWidth
, biHeight
, PixelFormat
.Format8bppIndexed
);
502 bmp
= new Bitmap (bih
.biWidth
, biHeight
, PixelFormat
.Format24bppRgb
);
505 bmp
= new Bitmap (bih
.biWidth
, biHeight
, PixelFormat
.Format32bppArgb
);
508 string msg
= Locale
.GetText ("Unexpected number of bits: {0}", bih
.biBitCount
);
509 throw new Exception (msg
);
512 if (bih
.biBitCount
< 24) {
513 ColorPalette pal
= bmp
.Palette
; // Managed palette
515 for (int i
= 0; i
< ii
.iconColors
.Length
; i
++) {
516 pal
.Entries
[i
] = Color
.FromArgb ((int)ii
.iconColors
[i
] | unchecked((int)0xff000000));
521 int bytesPerLine
= (int)((((bih
.biWidth
* bih
.biBitCount
) + 31) & ~
31) >> 3);
522 BitmapData bits
= bmp
.LockBits (new Rectangle(0, 0, bmp
.Width
, bmp
.Height
), ImageLockMode
.WriteOnly
, bmp
.PixelFormat
);
524 for (int y
= 0; y
< biHeight
; y
++) {
525 Marshal
.Copy (ii
.iconXOR
, bytesPerLine
* y
,
526 (IntPtr
)(bits
.Scan0
.ToInt64() + bits
.Stride
* (biHeight
- 1 - y
)), bytesPerLine
);
529 bmp
.UnlockBits (bits
);
531 bmp
= new Bitmap (bmp
); // This makes a 32bpp image out of an indexed one
533 // Apply the mask to make properly transparent
534 bytesPerLine
= (int)((((bih
.biWidth
) + 31) & ~
31) >> 3);
535 for (int y
= 0; y
< biHeight
; y
++) {
536 for (int x
= 0; x
< bih
.biWidth
/ 8; x
++) {
537 for (int bit
= 7; bit
>= 0; bit
--) {
538 if (((ii
.iconAND
[y
* bytesPerLine
+x
] >> bit
) & 1) != 0) {
539 bmp
.SetPixel (x
*8 + 7-bit
, biHeight
- y
- 1, Color
.Transparent
);
548 internal Bitmap
GetInternalBitmap ()
550 if (bitmap
== null) {
551 if (GDIPlus
.RunningOnUnix ()) {
552 // Mono's libgdiplus doesn't require to keep the stream alive when loading images
553 using (MemoryStream ms
= new MemoryStream ()) {
554 // save the current icon
555 Save (ms
, Width
, Height
);
558 // libgdiplus can now decode icons
559 bitmap
= (Bitmap
) Image
.LoadFromStream (ms
, false);
562 // MS GDI+ ICO codec is more limited than the MS Icon class
563 // so we can't, reliably, get bitmap using it. We need to do this the "slow" way
564 bitmap
= BuildBitmapOnWin32 ();
571 // note: all bitmaps are 32bits ARGB - no matter what the icon format (bitcount) was
572 public Bitmap
ToBitmap ()
575 throw new ObjectDisposedException (Locale
.GetText ("Icon instance was disposed."));
577 // note: we can't return the original image because
578 // (a) we have no control over the bitmap instance we return (i.e. it could be disposed)
579 // (b) the palette, flags won't match MS results. See MonoTests.System.Drawing.Imaging.IconCodecTest.
580 // Image16 for the differences
581 return new Bitmap (GetInternalBitmap ());
584 public override string ToString ()
586 //is this correct, this is what returned by .Net
591 public IntPtr Handle
{
593 // note: this handle doesn't survive the lifespan of the icon instance
594 if (!disposed
&& (handle
== IntPtr
.Zero
)) {
595 if (GDIPlus
.RunningOnUnix ()) {
596 handle
= GetInternalBitmap ().NativeObject
;
598 // remember that this block executes only with MS GDI+
599 IconInfo ii
= new IconInfo ();
601 ii
.hbmColor
= ToBitmap ().GetHbitmap ();
602 ii
.hbmMask
= ii
.hbmColor
;
603 handle
= GDIPlus
.CreateIconIndirect (ref ii
);
613 return iconSize
.Height
;
626 return iconSize
.Width
;
635 private void InitFromStreamWithSize (Stream stream
, int width
, int height
)
637 //read the icon header
638 if (stream
== null || stream
.Length
== 0)
639 throw new System
.ArgumentException ("The argument 'stream' must be a picture that can be used as a Icon", "stream");
641 BinaryReader reader
= new BinaryReader (stream
);
643 //iconDir = new IconDir ();
644 iconDir
.idReserved
= reader
.ReadUInt16();
645 if (iconDir
.idReserved
!= 0) //must be 0
646 throw new System
.ArgumentException ("Invalid Argument", "stream");
648 iconDir
.idType
= reader
.ReadUInt16();
649 if (iconDir
.idType
!= 1) //must be 1
650 throw new System
.ArgumentException ("Invalid Argument", "stream");
652 ushort dirEntryCount
= reader
.ReadUInt16();
653 ArrayList entries
= new ArrayList (dirEntryCount
);
654 bool sizeObtained
= false;
655 // now read in the IconDirEntry structures
656 for (int i
= 0; i
< dirEntryCount
; i
++) {
658 ide
.width
= reader
.ReadByte ();
659 ide
.height
= reader
.ReadByte ();
660 ide
.colorCount
= reader
.ReadByte ();
661 ide
.reserved
= reader
.ReadByte ();
662 ide
.planes
= reader
.ReadUInt16 ();
663 ide
.bitCount
= reader
.ReadUInt16 ();
664 ide
.bytesInRes
= reader
.ReadUInt32 ();
665 ide
.imageOffset
= reader
.ReadUInt32 ();
667 Console
.WriteLine ("Entry: {0}", i
);
668 Console
.WriteLine ("\tide.width: {0}", ide
.width
);
669 Console
.WriteLine ("\tide.height: {0}", ide
.height
);
670 Console
.WriteLine ("\tide.colorCount: {0}", ide
.colorCount
);
671 Console
.WriteLine ("\tide.reserved: {0}", ide
.reserved
);
672 Console
.WriteLine ("\tide.planes: {0}", ide
.planes
);
673 Console
.WriteLine ("\tide.bitCount: {0}", ide
.bitCount
);
674 Console
.WriteLine ("\tide.bytesInRes: {0}", ide
.bytesInRes
);
675 Console
.WriteLine ("\tide.imageOffset: {0}", ide
.imageOffset
);
678 // 256x256 icons are decoded as 0x0 (width and height are encoded as BYTE)
679 // and we ignore them just like MS does (at least up to fx 2.0)
680 if ((ide
.width
== 0) && (ide
.height
== 0))
683 int index
= entries
.Add (ide
);
685 //is this is the best fit??
687 if ((ide
.height
== height
) || (ide
.width
== width
)) {
688 this.id
= (ushort) index
;
690 this.iconSize
.Height
= ide
.height
;
691 this.iconSize
.Width
= ide
.width
;
696 // Vista 256x256 icons points directly to a PNG bitmap
697 dirEntryCount
= (ushort) entries
.Count
;
698 if (dirEntryCount
== 0)
699 throw new Win32Exception (0, "No valid icon entry were found.");
701 iconDir
.idCount
= dirEntryCount
;
702 imageData
= new IconImage
[dirEntryCount
];
703 iconDir
.idEntries
= new IconDirEntry
[dirEntryCount
];
704 entries
.CopyTo (iconDir
.idEntries
);
706 //if we havent found the best match, return the one with the
707 //largest size. Is this approach correct??
709 uint largestSize
= 0;
710 for (int j
=0; j
<dirEntryCount
; j
++){
711 if (iconDir
.idEntries
[j
].bytesInRes
>= largestSize
) {
712 largestSize
= iconDir
.idEntries
[j
].bytesInRes
;
713 this.id
= (ushort) j
;
714 this.iconSize
.Height
= iconDir
.idEntries
[j
].height
;
715 this.iconSize
.Width
= iconDir
.idEntries
[j
].width
;
720 //now read in the icon data
721 for (int j
= 0; j
<dirEntryCount
; j
++)
723 IconImage iidata
= new IconImage();
724 BitmapInfoHeader bih
= new BitmapInfoHeader();
725 stream
.Seek (iconDir
.idEntries
[j
].imageOffset
, SeekOrigin
.Begin
);
726 byte [] buffer
= new byte [iconDir
.idEntries
[j
].bytesInRes
];
727 stream
.Read (buffer
, 0, buffer
.Length
);
728 BinaryReader bihReader
= new BinaryReader (new MemoryStream(buffer
));
729 bih
.biSize
= bihReader
.ReadUInt32 ();
730 bih
.biWidth
= bihReader
.ReadInt32 ();
731 bih
.biHeight
= bihReader
.ReadInt32 ();
732 bih
.biPlanes
= bihReader
.ReadUInt16 ();
733 bih
.biBitCount
= bihReader
.ReadUInt16 ();
734 bih
.biCompression
= bihReader
.ReadUInt32 ();
735 bih
.biSizeImage
= bihReader
.ReadUInt32 ();
736 bih
.biXPelsPerMeter
= bihReader
.ReadInt32 ();
737 bih
.biYPelsPerMeter
= bihReader
.ReadInt32 ();
738 bih
.biClrUsed
= bihReader
.ReadUInt32 ();
739 bih
.biClrImportant
= bihReader
.ReadUInt32 ();
741 Console
.WriteLine ("Entry: {0}", j
);
742 Console
.WriteLine ("\tbih.biSize: {0}", bih
.biSize
);
743 Console
.WriteLine ("\tbih.biWidth: {0}", bih
.biWidth
);
744 Console
.WriteLine ("\tbih.biHeight: {0}", bih
.biHeight
);
745 Console
.WriteLine ("\tbih.biPlanes: {0}", bih
.biPlanes
);
746 Console
.WriteLine ("\tbih.biBitCount: {0}", bih
.biBitCount
);
747 Console
.WriteLine ("\tbih.biCompression: {0}", bih
.biCompression
);
748 Console
.WriteLine ("\tbih.biSizeImage: {0}", bih
.biSizeImage
);
749 Console
.WriteLine ("\tbih.biXPelsPerMeter: {0}", bih
.biXPelsPerMeter
);
750 Console
.WriteLine ("\tbih.biYPelsPerMeter: {0}", bih
.biYPelsPerMeter
);
751 Console
.WriteLine ("\tbih.biClrUsed: {0}", bih
.biClrUsed
);
752 Console
.WriteLine ("\tbih.biClrImportant: {0}", bih
.biClrImportant
);
754 iidata
.iconHeader
= bih
;
755 //Read the number of colors used and corresponding memory occupied by
756 //color table. Fill this memory chunk into rgbquad[]
758 switch (bih
.biBitCount
){
759 case 1: numColors
= 2;
761 case 4: numColors
= 16;
763 case 8: numColors
= 256;
765 default: numColors
= 0;
769 iidata
.iconColors
= new uint [numColors
];
770 for (int i
=0; i
<numColors
; i
++)
771 iidata
.iconColors
[i
] = bihReader
.ReadUInt32 ();
773 //XOR mask is immediately after ColorTable and its size is
774 //icon height* no. of bytes per line
776 //icon height is half of BITMAPINFOHEADER.biHeight, since it contains
777 //both XOR as well as AND mask bytes
778 int iconHeight
= bih
.biHeight
/2;
780 //bytes per line should should be uint aligned
781 int numBytesPerLine
= ((((bih
.biWidth
* bih
.biPlanes
* bih
.biBitCount
)+ 31)>>5)<<2);
783 //Determine the XOR array Size
784 int xorSize
= numBytesPerLine
* iconHeight
;
785 iidata
.iconXOR
= new byte [xorSize
];
786 int nread
= bihReader
.Read (iidata
.iconXOR
, 0, xorSize
);
787 if (nread
!= xorSize
) {
788 string msg
= Locale
.GetText ("{0} data length expected {1}, read {2}", "XOR", xorSize
, nread
);
789 throw new ArgumentException (msg
, "stream");
792 //Determine the AND array size
793 numBytesPerLine
= (int)((((bih
.biWidth
) + 31) & ~
31) >> 3);
794 int andSize
= numBytesPerLine
* iconHeight
;
795 iidata
.iconAND
= new byte [andSize
];
796 nread
= bihReader
.Read (iidata
.iconAND
, 0, andSize
);
797 if (nread
!= andSize
) {
798 string msg
= Locale
.GetText ("{0} data length expected {1}, read {2}", "AND", andSize
, nread
);
799 throw new ArgumentException (msg
, "stream");
802 imageData
[j
] = iidata
;