2 // System.Drawing.Icon.cs
5 // Gary Barnett (gary.barnett.mono@gmail.com)
6 // Dennis Hayes (dennish@Raytek.com)
7 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
8 // Sanjay Gupta (gsanjay@novell.com)
9 // Peter Dennis Bartok (pbartok@novell.com)
10 // Sebastien Pouliot <sebastien@ximian.com>
12 // Copyright (C) 2002 Ximian, Inc. http://www.ximian.com
13 // Copyright (C) 2004-2008 Novell, Inc (http://www.novell.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections;
36 using System.ComponentModel;
37 using System.Drawing.Imaging;
39 using System.Runtime.Serialization;
40 using System.Runtime.InteropServices;
41 using System.Security.Permissions;
43 namespace System.Drawing
50 [Editor ("System.Drawing.Design.IconEditor, " + Consts.AssemblySystem_Drawing_Design, typeof (System.Drawing.Design.UITypeEditor))]
52 [TypeConverter(typeof(IconConverter))]
54 public sealed class Icon : MarshalByRefObject, ISerializable, ICloneable, IDisposable
56 [StructLayout(LayoutKind.Sequential)]
57 internal struct IconDirEntry {
58 internal byte width; // Width of icon
59 internal byte height; // Height of icon
60 internal byte colorCount; // colors in icon
61 internal byte reserved; // Reserved
62 internal ushort planes; // Color Planes
63 internal ushort bitCount; // Bits per pixel
64 internal uint bytesInRes; // bytes in resource
65 internal uint imageOffset; // position in file
66 internal bool ignore; // for unsupported images (vista 256 png)
69 [StructLayout(LayoutKind.Sequential)]
70 internal struct IconDir {
71 internal ushort idReserved; // Reserved
72 internal ushort idType; // resource type (1 for icons)
73 internal ushort idCount; // how many images?
74 internal IconDirEntry [] idEntries; // the entries for each image
77 [StructLayout(LayoutKind.Sequential)]
78 internal struct BitmapInfoHeader {
81 internal int biHeight;
82 internal ushort biPlanes;
83 internal ushort biBitCount;
84 internal uint biCompression;
85 internal uint biSizeImage;
86 internal int biXPelsPerMeter;
87 internal int biYPelsPerMeter;
88 internal uint biClrUsed;
89 internal uint biClrImportant;
92 [StructLayout(LayoutKind.Sequential)] // added baseclass for non bmp image format support
93 internal abstract class ImageData {
96 [StructLayout(LayoutKind.Sequential)]
97 internal class IconImage : ImageData {
98 internal BitmapInfoHeader iconHeader; //image header
99 internal uint [] iconColors; //colors table
100 internal byte [] iconXOR; // bits for XOR mask
101 internal byte [] iconAND; //bits for AND mask
104 [StructLayout(LayoutKind.Sequential)]
105 internal class IconDump : ImageData {
106 internal byte [] data;
109 private Size iconSize;
110 private IntPtr handle = IntPtr.Zero;
111 private IconDir iconDir;
113 private ImageData [] imageData;
114 private bool undisposable;
115 private bool disposed;
116 private Bitmap bitmap;
123 private Icon (IntPtr handle)
125 this.handle = handle;
126 bitmap = Bitmap.FromHicon (handle);
127 iconSize = new Size (bitmap.Width, bitmap.Height);
128 if (GDIPlus.RunningOnUnix ()) {
129 bitmap = Bitmap.FromHicon (handle);
130 iconSize = new Size (bitmap.Width, bitmap.Height);
131 // FIXME: we need to convert the bitmap into an icon
134 GDIPlus.GetIconInfo (handle, out ii);
136 throw new NotImplementedException (Locale.GetText ("Handle doesn't represent an ICON."));
138 // If this structure defines an icon, the hot spot is always in the center of the icon
139 iconSize = new Size (ii.xHotspot * 2, ii.yHotspot * 2);
140 bitmap = (Bitmap) Image.FromHbitmap (ii.hbmColor);
146 public Icon (Icon original, int width, int height)
147 : this (original, new Size (width, height))
151 public Icon (Icon original, Size size)
153 if (original == null)
154 throw new ArgumentException ("original");
157 iconDir = original.iconDir;
159 int count = iconDir.idCount;
161 imageData = original.imageData;
162 id = UInt16.MaxValue;
164 for (ushort i=0; i < count; i++) {
165 IconDirEntry ide = iconDir.idEntries [i];
166 if (((ide.height == size.Height) || (ide.width == size.Width)) && !ide.ignore) {
172 // if a perfect match isn't found we look for the biggest icon *smaller* than specified
173 if (id == UInt16.MaxValue) {
174 int requested = Math.Min (size.Height, size.Width);
175 // previously best set to 1st image, as this might not be smallest changed loop to check all
176 IconDirEntry? best = null;
177 for (ushort i=0; i < count; i++) {
178 IconDirEntry ide = iconDir.idEntries [i];
179 if (((ide.height < requested) || (ide.width < requested)) && !ide.ignore) {
183 } else if ((ide.height > best.Value.height) || (ide.width > best.Value.width)) {
191 // last one, if nothing better can be found
192 if (id == UInt16.MaxValue) {
194 while (id == UInt16.MaxValue && i > 0) {
196 if (!iconDir.idEntries [i].ignore)
201 if (id == UInt16.MaxValue)
202 throw new ArgumentException ("Icon", "No valid icon image found");
204 iconSize.Height = iconDir.idEntries [id].height;
205 iconSize.Width = iconDir.idEntries [id].width;
207 iconSize.Height = size.Height;
208 iconSize.Width = size.Width;
211 if (original.bitmap != null)
212 bitmap = (Bitmap) original.bitmap.Clone ();
215 public Icon (Stream stream) : this (stream, 32, 32)
219 public Icon (Stream stream, int width, int height)
221 InitFromStreamWithSize (stream, width, height);
224 public Icon (string fileName)
226 using (FileStream fs = File.OpenRead (fileName)) {
227 InitFromStreamWithSize (fs, 32, 32);
231 public Icon (Type type, string resource)
233 if (resource == null)
234 throw new ArgumentException ("resource");
236 using (Stream s = type.Assembly.GetManifestResourceStream (type, resource)) {
238 string msg = Locale.GetText ("Resource '{0}' was not found.", resource);
239 throw new FileNotFoundException (msg);
241 InitFromStreamWithSize (s, 32, 32); // 32x32 is default
245 private Icon (SerializationInfo info, StreamingContext context)
247 MemoryStream dataStream = null;
250 foreach (SerializationEntry serEnum in info) {
251 if (String.Compare(serEnum.Name, "IconData", true) == 0) {
252 dataStream = new MemoryStream ((byte []) serEnum.Value);
254 if (String.Compare(serEnum.Name, "IconSize", true) == 0) {
255 Size iconSize = (Size) serEnum.Value;
256 width = iconSize.Width;
257 height = iconSize.Height;
260 if (dataStream != null) {
261 dataStream.Seek (0, SeekOrigin.Begin);
262 InitFromStreamWithSize (dataStream, width, height);
266 internal Icon (string resourceName, bool undisposable)
268 using (Stream s = typeof (Icon).Assembly.GetManifestResourceStream (resourceName)) {
270 string msg = Locale.GetText ("Resource '{0}' was not found.", resourceName);
271 throw new FileNotFoundException (msg);
273 InitFromStreamWithSize (s, 32, 32); // 32x32 is default
275 this.undisposable = true;
278 void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context)
280 MemoryStream ms = new MemoryStream ();
282 si.AddValue ("IconSize", this.Size, typeof (Size));
283 si.AddValue ("IconData", ms.ToArray ());
287 public Icon (Stream stream, Size size) :
288 this (stream, size.Width, size.Height)
292 public Icon (string fileName, int width, int height)
294 using (FileStream fs = File.OpenRead (fileName)) {
295 InitFromStreamWithSize (fs, width, height);
299 public Icon (string fileName, Size size)
301 using (FileStream fs = File.OpenRead (fileName)) {
302 InitFromStreamWithSize (fs, size.Width, size.Height);
306 [MonoLimitation ("The same icon, SystemIcons.WinLogo, is returned for all file types.")]
307 public static Icon ExtractAssociatedIcon (string filePath)
309 if (String.IsNullOrEmpty (filePath))
310 throw new ArgumentException (Locale.GetText ("Null or empty path."), "filePath");
311 if (!File.Exists (filePath))
312 throw new FileNotFoundException (Locale.GetText ("Couldn't find specified file."), filePath);
314 return SystemIcons.WinLogo;
318 public void Dispose ()
320 // SystemIcons requires this
326 if (GDIPlus.RunningOnWindows () && (handle != IntPtr.Zero)) {
327 GDIPlus.DestroyIcon (handle);
328 handle = IntPtr.Zero;
331 if (bitmap != null) {
335 GC.SuppressFinalize (this);
340 public object Clone ()
342 return new Icon (this, Size);
346 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode = true)]
347 public static Icon FromHandle (IntPtr handle)
349 if (handle == IntPtr.Zero)
350 throw new ArgumentException ("handle");
352 return new Icon (handle);
355 private void SaveIconImage (BinaryWriter writer, IconImage ii)
357 BitmapInfoHeader bih = ii.iconHeader;
358 writer.Write (bih.biSize);
359 writer.Write (bih.biWidth);
360 writer.Write (bih.biHeight);
361 writer.Write (bih.biPlanes);
362 writer.Write (bih.biBitCount);
363 writer.Write (bih.biCompression);
364 writer.Write (bih.biSizeImage);
365 writer.Write (bih.biXPelsPerMeter);
366 writer.Write (bih.biYPelsPerMeter);
367 writer.Write (bih.biClrUsed);
368 writer.Write (bih.biClrImportant);
370 //now write color table
371 int colCount = ii.iconColors.Length;
372 for (int j=0; j < colCount; j++)
373 writer.Write (ii.iconColors [j]);
376 writer.Write (ii.iconXOR);
379 writer.Write (ii.iconAND);
382 private void SaveIconDump (BinaryWriter writer, IconDump id)
384 writer.Write (id.data);
387 private void SaveIconDirEntry (BinaryWriter writer, IconDirEntry ide, uint offset)
389 writer.Write (ide.width);
390 writer.Write (ide.height);
391 writer.Write (ide.colorCount);
392 writer.Write (ide.reserved);
393 writer.Write (ide.planes);
394 writer.Write (ide.bitCount);
395 writer.Write (ide.bytesInRes);
396 writer.Write ((offset == UInt32.MaxValue) ? ide.imageOffset : offset);
399 private void SaveAll (BinaryWriter writer)
401 writer.Write (iconDir.idReserved);
402 writer.Write (iconDir.idType);
403 ushort count = iconDir.idCount;
404 writer.Write (count);
406 for (int i=0; i < (int)count; i++) {
407 SaveIconDirEntry (writer, iconDir.idEntries [i], UInt32.MaxValue);
410 for (int i=0; i < (int)count; i++) {
412 //FIXME: HACK: 1 (out of the 8) vista type icons had additional bytes (value:0)
413 //between images. This fixes the issue, but perhaps shouldnt include in production?
414 while (writer.BaseStream.Length < iconDir.idEntries[i].imageOffset)
415 writer.Write ((byte) 0);
417 if (imageData [i] is IconDump)
418 SaveIconDump (writer, (IconDump) imageData [i]);
420 SaveIconImage (writer, (IconImage) imageData [i]);
423 // TODO: check image not ignored (presently this method doesnt seem to be called unless width/height
425 private void SaveBestSingleIcon (BinaryWriter writer, int width, int height)
427 writer.Write (iconDir.idReserved);
428 writer.Write (iconDir.idType);
429 writer.Write ((ushort)1);
431 // find best entry and save it
434 for (int i=0; i < iconDir.idCount; i++) {
435 IconDirEntry ide = iconDir.idEntries [i];
436 if ((width == ide.width) && (height == ide.height)) {
437 if (ide.bitCount >= bitCount) {
438 bitCount = ide.bitCount;
444 SaveIconDirEntry (writer, iconDir.idEntries [best], 22);
445 SaveIconImage (writer, (IconImage) imageData [best]);
448 private void SaveBitmapAsIcon (BinaryWriter writer)
450 writer.Write ((ushort)0); // idReserved must be 0
451 writer.Write ((ushort)1); // idType must be 1
452 writer.Write ((ushort)1); // only one icon
454 // when transformed into a bitmap only a single image exists
455 IconDirEntry ide = new IconDirEntry ();
456 ide.width = (byte) bitmap.Width;
457 ide.height = (byte) bitmap.Height;
458 ide.colorCount = 0; // 32 bbp == 0, for palette size
459 ide.reserved = 0; // always 0
462 ide.imageOffset = 22; // 22 is the first icon position (for single icon files)
464 BitmapInfoHeader bih = new BitmapInfoHeader ();
465 bih.biSize = (uint) Marshal.SizeOf (typeof (BitmapInfoHeader));
466 bih.biWidth = bitmap.Width;
467 bih.biHeight = 2 * bitmap.Height; // include both XOR and AND images
470 bih.biCompression = 0;
472 bih.biXPelsPerMeter = 0;
473 bih.biYPelsPerMeter = 0;
475 bih.biClrImportant = 0;
477 IconImage ii = new IconImage ();
479 ii.iconColors = new uint [0]; // no palette
480 int xor_size = (((bih.biBitCount * bitmap.Width + 31) & ~31) >> 3) * bitmap.Height;
481 ii.iconXOR = new byte [xor_size];
483 for (int y = bitmap.Height - 1; y >=0; y--) {
484 for (int x = 0; x < bitmap.Width; x++) {
485 Color c = bitmap.GetPixel (x, y);
486 ii.iconXOR [p++] = c.B;
487 ii.iconXOR [p++] = c.G;
488 ii.iconXOR [p++] = c.R;
489 ii.iconXOR [p++] = c.A;
492 int and_line_size = (((Width + 31) & ~31) >> 3); // must be a multiple of 4 bytes
493 int and_size = and_line_size * bitmap.Height;
494 ii.iconAND = new byte [and_size];
496 ide.bytesInRes = (uint) (bih.biSize + xor_size + and_size);
498 SaveIconDirEntry (writer, ide, UInt32.MaxValue);
499 SaveIconImage (writer, ii);
502 private void Save (Stream outputStream, int width, int height)
504 BinaryWriter writer = new BinaryWriter (outputStream);
505 // if we have the icon information then save from this
506 if (iconDir.idEntries != null) {
507 if ((width == -1) && (height == -1))
510 SaveBestSingleIcon (writer, width, height);
511 } else if (bitmap != null) {
512 // if the icon was created from a bitmap then convert it
513 SaveBitmapAsIcon (writer);
518 public void Save (Stream outputStream)
520 if (outputStream == null)
521 throw new NullReferenceException ("outputStream");
523 // save every icons available
524 Save (outputStream, -1, -1);
527 internal Bitmap BuildBitmapOnWin32 ()
531 if (imageData == null)
532 return new Bitmap (32, 32);
534 IconImage ii = (IconImage) imageData [id];
535 BitmapInfoHeader bih = ii.iconHeader;
536 int biHeight = bih.biHeight / 2;
538 int ncolors = (int)bih.biClrUsed;
539 if ((ncolors == 0) && (bih.biBitCount < 24))
540 ncolors = (int)(1 << bih.biBitCount);
542 switch (bih.biBitCount) {
544 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format1bppIndexed);
547 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format4bppIndexed);
550 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format8bppIndexed);
553 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format24bppRgb);
556 bmp = new Bitmap (bih.biWidth, biHeight, PixelFormat.Format32bppArgb);
559 string msg = Locale.GetText ("Unexpected number of bits: {0}", bih.biBitCount);
560 throw new Exception (msg);
563 if (bih.biBitCount < 24) {
564 ColorPalette pal = bmp.Palette; // Managed palette
566 for (int i = 0; i < ii.iconColors.Length; i++) {
567 pal.Entries[i] = Color.FromArgb ((int)ii.iconColors[i] | unchecked((int)0xff000000));
572 int bytesPerLine = (int)((((bih.biWidth * bih.biBitCount) + 31) & ~31) >> 3);
573 BitmapData bits = bmp.LockBits (new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
575 for (int y = 0; y < biHeight; y++) {
576 Marshal.Copy (ii.iconXOR, bytesPerLine * y,
577 (IntPtr)(bits.Scan0.ToInt64() + bits.Stride * (biHeight - 1 - y)), bytesPerLine);
580 bmp.UnlockBits (bits);
582 bmp = new Bitmap (bmp); // This makes a 32bpp image out of an indexed one
584 // Apply the mask to make properly transparent
585 bytesPerLine = (int)((((bih.biWidth) + 31) & ~31) >> 3);
586 for (int y = 0; y < biHeight; y++) {
587 for (int x = 0; x < bih.biWidth / 8; x++) {
588 for (int bit = 7; bit >= 0; bit--) {
589 if (((ii.iconAND[y * bytesPerLine +x] >> bit) & 1) != 0) {
590 bmp.SetPixel (x*8 + 7-bit, biHeight - y - 1, Color.Transparent);
599 internal Bitmap GetInternalBitmap ()
601 if (bitmap == null) {
602 if (GDIPlus.RunningOnUnix ()) {
603 // Mono's libgdiplus doesn't require to keep the stream alive when loading images
604 using (MemoryStream ms = new MemoryStream ()) {
605 // save the current icon
606 Save (ms, Width, Height);
609 // libgdiplus can now decode icons
610 bitmap = (Bitmap) Image.LoadFromStream (ms, false);
613 // MS GDI+ ICO codec is more limited than the MS Icon class
614 // so we can't, reliably, get bitmap using it. We need to do this the "slow" way
615 bitmap = BuildBitmapOnWin32 ();
621 // note: all bitmaps are 32bits ARGB - no matter what the icon format (bitcount) was
622 public Bitmap ToBitmap ()
625 throw new ObjectDisposedException (Locale.GetText ("Icon instance was disposed."));
627 // note: we can't return the original image because
628 // (a) we have no control over the bitmap instance we return (i.e. it could be disposed)
629 // (b) the palette, flags won't match MS results. See MonoTests.System.Drawing.Imaging.IconCodecTest.
630 // Image16 for the differences
631 return new Bitmap (GetInternalBitmap ());
634 public override string ToString ()
636 //is this correct, this is what returned by .Net
642 public IntPtr Handle {
644 // note: this handle doesn't survive the lifespan of the icon instance
645 if (!disposed && (handle == IntPtr.Zero)) {
646 if (GDIPlus.RunningOnUnix ()) {
647 handle = GetInternalBitmap ().NativeObject;
649 // remember that this block executes only with MS GDI+
650 IconInfo ii = new IconInfo ();
652 ii.hbmColor = ToBitmap ().GetHbitmap ();
653 ii.hbmMask = ii.hbmColor;
654 handle = GDIPlus.CreateIconIndirect (ref ii);
664 return iconSize.Height;
677 return iconSize.Width;
686 private void InitFromStreamWithSize (Stream stream, int width, int height)
688 //read the icon header
689 if (stream == null || stream.Length == 0)
690 throw new System.ArgumentException ("The argument 'stream' must be a picture that can be used as a Icon", "stream");
692 BinaryReader reader = new BinaryReader (stream);
694 //iconDir = new IconDir ();
695 iconDir.idReserved = reader.ReadUInt16();
696 if (iconDir.idReserved != 0) //must be 0
697 throw new System.ArgumentException ("Invalid Argument", "stream");
699 iconDir.idType = reader.ReadUInt16();
700 if (iconDir.idType != 1) //must be 1
701 throw new System.ArgumentException ("Invalid Argument", "stream");
703 ushort dirEntryCount = reader.ReadUInt16();
704 imageData = new ImageData [dirEntryCount];
705 iconDir.idCount = dirEntryCount;
706 iconDir.idEntries = new IconDirEntry [dirEntryCount];
707 bool sizeObtained = false;
708 // now read in the IconDirEntry structures
709 for (int i = 0; i < dirEntryCount; i++) {
711 ide.width = reader.ReadByte ();
712 ide.height = reader.ReadByte ();
713 ide.colorCount = reader.ReadByte ();
714 ide.reserved = reader.ReadByte ();
715 ide.planes = reader.ReadUInt16 ();
716 ide.bitCount = reader.ReadUInt16 ();
717 ide.bytesInRes = reader.ReadUInt32 ();
718 ide.imageOffset = reader.ReadUInt32 ();
720 Console.WriteLine ("Entry: {0}", i);
721 Console.WriteLine ("\tide.width: {0}", ide.width);
722 Console.WriteLine ("\tide.height: {0}", ide.height);
723 Console.WriteLine ("\tide.colorCount: {0}", ide.colorCount);
724 Console.WriteLine ("\tide.reserved: {0}", ide.reserved);
725 Console.WriteLine ("\tide.planes: {0}", ide.planes);
726 Console.WriteLine ("\tide.bitCount: {0}", ide.bitCount);
727 Console.WriteLine ("\tide.bytesInRes: {0}", ide.bytesInRes);
728 Console.WriteLine ("\tide.imageOffset: {0}", ide.imageOffset);
730 // Vista 256x256 icons points directly to a PNG bitmap
731 // 256x256 icons are decoded as 0x0 (width and height are encoded as BYTE)
732 // and we ignore them just like MS does (at least up to fx 2.0)
733 // Added: storing data so it can be saved back
734 if ((ide.width == 0) && (ide.height == 0))
739 iconDir.idEntries [i] = ide;
741 //is this is the best fit??
743 if (((ide.height == height) || (ide.width == width)) && !ide.ignore) {
744 this.id = (ushort) i;
746 this.iconSize.Height = ide.height;
747 this.iconSize.Width = ide.width;
752 // throw error if no valid entries found
754 for (int i = 0; i < dirEntryCount; i++) {
755 if (!(iconDir.idEntries [i].ignore))
760 throw new Win32Exception (0, "No valid icon entry were found.");
762 // if we havent found the best match, return the one with the
763 // largest size. Is this approach correct??
765 uint largestSize = 0;
766 for (int j=0; j<dirEntryCount; j++){
767 if (iconDir.idEntries [j].bytesInRes >= largestSize && !iconDir.idEntries [j].ignore) {
768 largestSize = iconDir.idEntries [j].bytesInRes;
769 this.id = (ushort) j;
770 this.iconSize.Height = iconDir.idEntries [j].height;
771 this.iconSize.Width = iconDir.idEntries [j].width;
776 //now read in the icon data
777 for (int j = 0; j<dirEntryCount; j++)
779 // process ignored into IconDump
780 if (iconDir.idEntries [j].ignore) {
781 IconDump id = new IconDump ();
782 stream.Seek (iconDir.idEntries [j].imageOffset, SeekOrigin.Begin);
783 id.data = new byte [iconDir.idEntries [j].bytesInRes];
784 stream.Read (id.data, 0, id.data.Length);
789 IconImage iidata = new IconImage();
790 BitmapInfoHeader bih = new BitmapInfoHeader();
791 stream.Seek (iconDir.idEntries [j].imageOffset, SeekOrigin.Begin);
792 byte [] buffer = new byte [iconDir.idEntries [j].bytesInRes];
793 stream.Read (buffer, 0, buffer.Length);
794 BinaryReader bihReader = new BinaryReader (new MemoryStream(buffer));
795 bih.biSize = bihReader.ReadUInt32 ();
796 bih.biWidth = bihReader.ReadInt32 ();
797 bih.biHeight = bihReader.ReadInt32 ();
798 bih.biPlanes = bihReader.ReadUInt16 ();
799 bih.biBitCount = bihReader.ReadUInt16 ();
800 bih.biCompression = bihReader.ReadUInt32 ();
801 bih.biSizeImage = bihReader.ReadUInt32 ();
802 bih.biXPelsPerMeter = bihReader.ReadInt32 ();
803 bih.biYPelsPerMeter = bihReader.ReadInt32 ();
804 bih.biClrUsed = bihReader.ReadUInt32 ();
805 bih.biClrImportant = bihReader.ReadUInt32 ();
807 Console.WriteLine ("Entry: {0}", j);
808 Console.WriteLine ("\tbih.biSize: {0}", bih.biSize);
809 Console.WriteLine ("\tbih.biWidth: {0}", bih.biWidth);
810 Console.WriteLine ("\tbih.biHeight: {0}", bih.biHeight);
811 Console.WriteLine ("\tbih.biPlanes: {0}", bih.biPlanes);
812 Console.WriteLine ("\tbih.biBitCount: {0}", bih.biBitCount);
813 Console.WriteLine ("\tbih.biCompression: {0}", bih.biCompression);
814 Console.WriteLine ("\tbih.biSizeImage: {0}", bih.biSizeImage);
815 Console.WriteLine ("\tbih.biXPelsPerMeter: {0}", bih.biXPelsPerMeter);
816 Console.WriteLine ("\tbih.biYPelsPerMeter: {0}", bih.biYPelsPerMeter);
817 Console.WriteLine ("\tbih.biClrUsed: {0}", bih.biClrUsed);
818 Console.WriteLine ("\tbih.biClrImportant: {0}", bih.biClrImportant);
820 iidata.iconHeader = bih;
821 //Read the number of colors used and corresponding memory occupied by
822 //color table. Fill this memory chunk into rgbquad[]
824 switch (bih.biBitCount){
825 case 1: numColors = 2;
827 case 4: numColors = 16;
829 case 8: numColors = 256;
831 default: numColors = 0;
835 iidata.iconColors = new uint [numColors];
836 for (int i=0; i<numColors; i++)
837 iidata.iconColors [i] = bihReader.ReadUInt32 ();
839 //XOR mask is immediately after ColorTable and its size is
840 //icon height* no. of bytes per line
842 //icon height is half of BITMAPINFOHEADER.biHeight, since it contains
843 //both XOR as well as AND mask bytes
844 int iconHeight = bih.biHeight/2;
846 //bytes per line should should be uint aligned
847 int numBytesPerLine = ((((bih.biWidth * bih.biPlanes * bih.biBitCount)+ 31)>>5)<<2);
849 //Determine the XOR array Size
850 int xorSize = numBytesPerLine * iconHeight;
851 iidata.iconXOR = new byte [xorSize];
852 int nread = bihReader.Read (iidata.iconXOR, 0, xorSize);
853 if (nread != xorSize) {
854 string msg = Locale.GetText ("{0} data length expected {1}, read {2}", "XOR", xorSize, nread);
855 throw new ArgumentException (msg, "stream");
858 //Determine the AND array size
859 numBytesPerLine = (int)((((bih.biWidth) + 31) & ~31) >> 3);
860 int andSize = numBytesPerLine * iconHeight;
861 iidata.iconAND = new byte [andSize];
862 nread = bihReader.Read (iidata.iconAND, 0, andSize);
863 if (nread != andSize) {
864 string msg = Locale.GetText ("{0} data length expected {1}, read {2}", "AND", andSize, nread);
865 throw new ArgumentException (msg, "stream");
868 imageData [j] = iidata;