2010-06-03 Jb Evain <jbevain@novell.com>
[mcs.git] / class / System.Drawing / System.Drawing / Icon.cs
blob151a6a7566edadfc13237a9a6fb6e173d4a6dc4f
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>
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:
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.
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;
42 namespace System.Drawing
44 #if !NET_2_0
45 [ComVisible (false)]
46 #endif
47 [Serializable]
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
62 };
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 {
74 internal uint biSize;
75 internal int biWidth;
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
93 };
95 private Size iconSize;
96 private IntPtr handle = IntPtr.Zero;
97 private IconDir iconDir;
98 private ushort id;
99 private IconImage [] imageData;
100 private bool undisposable;
101 private bool disposed;
102 private Bitmap bitmap;
104 private Icon ()
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
115 } else {
116 IconInfo ii;
117 GDIPlus.GetIconInfo (handle, out ii);
118 if (!ii.IsIcon)
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);
125 undisposable = true;
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");
138 iconSize = size;
139 iconDir = original.iconDir;
141 int count = iconDir.idCount;
142 if (count > 0) {
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)) {
149 id = i;
150 break;
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))
162 id = i;
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;
173 } else {
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)) {
204 if (s == null) {
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;
215 int width=0;
216 int height=0;
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)) {
236 if (s == null) {
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 ();
248 Save (ms);
249 si.AddValue ("IconSize", this.Size, typeof (Size));
250 si.AddValue ("IconData", ms.ToArray ());
253 #if NET_2_0
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;
283 #endif
285 public void Dispose ()
287 // SystemIcons requires this
288 if (undisposable)
289 return;
291 if (!disposed) {
292 if (GDIPlus.RunningOnWindows () && (handle != IntPtr.Zero)) {
293 GDIPlus.DestroyIcon (handle);
294 handle = IntPtr.Zero;
296 if (bitmap != null) {
297 bitmap.Dispose ();
298 bitmap = null;
300 GC.SuppressFinalize (this);
302 disposed = true;
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]);
339 //now write XOR Mask
340 writer.Write (ii.iconXOR);
342 //now write AND Mask
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
381 int best = 0;
382 int bitCount = 0;
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;
388 best = i;
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
409 ide.planes = 0;
410 ide.bitCount = 32;
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
417 bih.biPlanes = 1;
418 bih.biBitCount = 32;
419 bih.biCompression = 0;
420 bih.biSizeImage = 0;
421 bih.biXPelsPerMeter = 0;
422 bih.biYPelsPerMeter = 0;
423 bih.biClrUsed = 0;
424 bih.biClrImportant = 0;
426 IconImage ii = new IconImage ();
427 ii.iconHeader = bih;
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];
431 int p = 0;
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))
457 SaveAll (writer);
458 else
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);
464 writer.Flush ();
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 ()
478 Bitmap bmp;
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) {
492 case 1:
493 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format1bppIndexed);
494 break;
495 case 4:
496 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format4bppIndexed);
497 break;
498 case 8:
499 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format8bppIndexed);
500 break;
501 case 24:
502 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format24bppRgb);
503 break;
504 case 32:
505 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format32bppArgb);
506 break;
507 default:
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));
518 bmp.Palette = pal;
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);
545 return bmp;
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);
556 ms.Position = 0;
558 // libgdiplus can now decode icons
559 bitmap = (Bitmap) Image.LoadFromStream (ms, false);
561 } else {
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 ();
568 return bitmap;
571 // note: all bitmaps are 32bits ARGB - no matter what the icon format (bitcount) was
572 public Bitmap ToBitmap ()
574 if (disposed)
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
587 return "<Icon>";
590 [Browsable (false)]
591 public IntPtr Handle {
592 get {
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;
597 } else {
598 // remember that this block executes only with MS GDI+
599 IconInfo ii = new IconInfo ();
600 ii.IsIcon = true;
601 ii.hbmColor = ToBitmap ().GetHbitmap ();
602 ii.hbmMask = ii.hbmColor;
603 handle = GDIPlus.CreateIconIndirect (ref ii);
606 return handle;
610 [Browsable (false)]
611 public int Height {
612 get {
613 return iconSize.Height;
617 public Size Size {
618 get {
619 return iconSize;
623 [Browsable (false)]
624 public int Width {
625 get {
626 return iconSize.Width;
630 ~Icon ()
632 Dispose ();
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++) {
657 IconDirEntry ide;
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 ();
666 #if false
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);
676 #endif
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))
681 continue;
683 int index = entries.Add (ide);
685 //is this is the best fit??
686 if (!sizeObtained) {
687 if ((ide.height == height) || (ide.width == width)) {
688 this.id = (ushort) index;
689 sizeObtained = true;
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??
708 if (!sizeObtained){
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 ();
740 #if false
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);
753 #endif
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[]
757 int numColors;
758 switch (bih.biBitCount){
759 case 1: numColors = 2;
760 break;
761 case 4: numColors = 16;
762 break;
763 case 8: numColors = 256;
764 break;
765 default: numColors = 0;
766 break;
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;
803 bihReader.Close();
806 reader.Close();