2 // System.Drawing.Icon.cs
5 // Dennis Hayes (dennish@Raytek.com)
6 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
7 // Sanjay Gupta (gsanjay@novell.com)
9 // Copyright (C) 2002 Ximian, Inc. http://www.ximian.com
10 // Copyright (C) 2004 Novell, Inc. http://www.novell.com
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 using System
.Runtime
.Serialization
;
39 using System
.Runtime
.InteropServices
;
40 using System
.ComponentModel
;
42 namespace System
.Drawing
46 [Editor ("System.Drawing.Design.IconEditor, " + Consts
.AssemblySystem_Drawing_Design
, typeof (System
.Drawing
.Design
.UITypeEditor
))]
47 [TypeConverter(typeof(IconConverter
))]
48 public sealed class Icon
: MarshalByRefObject
, ISerializable
, ICloneable
, IDisposable
50 [StructLayout(LayoutKind
.Sequential
)]
51 internal struct IconDirEntry
{
52 internal byte width
; // Width of icon
53 internal byte height
; // Height of icon
54 internal byte colorCount
; // colors in icon
55 internal byte reserved
; // Reserved
56 internal ushort planes
; // Color Planes
57 internal ushort bitCount
; // Bits per pixel
58 internal uint bytesInRes
; // bytes in resource
59 internal uint imageOffset
; // position in file
62 [StructLayout(LayoutKind
.Sequential
)]
63 internal struct IconDir
{
64 internal ushort idReserved
; // Reserved
65 internal ushort idType
; // resource type (1 for icons)
66 internal ushort idCount
; // how many images?
67 internal IconDirEntry
[] idEntries
; // the entries for each image
70 [StructLayout(LayoutKind
.Sequential
)]
71 internal struct BitmapInfoHeader
{
74 internal int biHeight
;
75 internal ushort biPlanes
;
76 internal ushort biBitCount
;
77 internal uint biCompression
;
78 internal uint biSizeImage
;
79 internal int biXPelsPerMeter
;
80 internal int biYPelsPerMeter
;
81 internal uint biClrUsed
;
82 internal uint biClrImportant
;
85 [StructLayout(LayoutKind
.Sequential
)]
86 internal struct IconImage
{
87 internal BitmapInfoHeader iconHeader
; //image header
88 internal uint [] iconColors
; //colors table
89 internal byte [] iconXOR
; // bits for XOR mask
90 internal byte [] iconAND
; //bits for AND mask
93 private Size iconSize
;
94 private IntPtr winHandle
= IntPtr
.Zero
;
95 private IconDir iconDir
;
97 private IconImage
[] imageData
;
98 bool destroyIcon
= true;
104 [MonoTODO ("Implement fully")]
105 private Icon (IntPtr handle
)
107 this.winHandle
= handle
;
110 GDIPlus
.GetIconInfo (winHandle
, out ii
);
112 // If this structure defines an icon, the hot spot is always in the center of the icon
113 iconSize
= new Size (ii
.xHotspot
* 2, ii
.yHotspot
* 2);
116 throw new NotImplementedException ();
119 this.destroyIcon
= false;
122 public Icon (Icon original
, int width
, int height
) : this (original
, new Size(width
, height
))
126 public Icon (Icon original
, Size size
)
128 this.iconSize
= size
;
129 this.winHandle
= original
.winHandle
;
130 this.iconDir
= original
.iconDir
;
131 this.imageData
= original
.imageData
;
133 int count
= iconDir
.idCount
;
134 bool sizeObtained
= false;
135 for (int i
=0; i
<count
; i
++){
136 IconDirEntry ide
= iconDir
.idEntries
[i
];
138 if (ide
.height
==size
.Height
&& ide
.width
==size
.Width
) {
139 this.id
= (ushort) i
;
141 this.iconSize
.Height
= ide
.height
;
142 this.iconSize
.Width
= ide
.width
;
148 uint largestSize
= 0;
149 for (int j
=0; j
<count
; j
++){
150 if (iconDir
.idEntries
[j
].bytesInRes
>= largestSize
){
151 largestSize
= iconDir
.idEntries
[j
].bytesInRes
;
152 this.id
= (ushort) j
;
153 this.iconSize
.Height
= iconDir
.idEntries
[j
].height
;
154 this.iconSize
.Width
= iconDir
.idEntries
[j
].width
;
160 public Icon (Stream stream
) : this (stream
, 32, 32)
164 public Icon (Stream stream
, int width
, int height
)
166 InitFromStreamWithSize (stream
, width
, height
);
169 public Icon (string fileName
) : this (new FileStream (fileName
, FileMode
.Open
))
173 public Icon (Type type
, string resource
)
175 using (Stream s
= type
.Assembly
.GetManifestResourceStream (type
, resource
)) {
177 throw new FileNotFoundException ("Resource name was not found: `" + resource
+ "'");
179 InitFromStreamWithSize (s
, 32, 32); // 32x32 is default
183 private Icon (SerializationInfo info
, StreamingContext context
)
185 MemoryStream dataStream
= null;
188 foreach (SerializationEntry serEnum
in info
) {
189 if (String
.Compare(serEnum
.Name
, "IconData", true) == 0) {
190 dataStream
= new MemoryStream ((byte []) serEnum
.Value
);
192 if (String
.Compare(serEnum
.Name
, "IconSize", true) == 0) {
193 Size iconSize
= (Size
) serEnum
.Value
;
194 width
= iconSize
.Width
;
195 height
= iconSize
.Height
;
198 if (dataStream
!= null && width
!= 0 && height
!= 0) {
199 dataStream
.Seek (0, SeekOrigin
.Begin
);
200 InitFromStreamWithSize (dataStream
, width
, height
);
204 void ISerializable
.GetObjectData(SerializationInfo info
, StreamingContext context
)
206 MemoryStream ms
= new MemoryStream ();
208 info
.AddValue ("IconSize", this.Size
, typeof (Size
));
209 info
.AddValue ("IconData", ms
.ToArray ());
212 public void Dispose ()
216 GC
.SuppressFinalize(this);
222 if (winHandle
==IntPtr
.Zero
)
226 //TODO: will have to call some win32 icon stuff
227 winHandle
= IntPtr
.Zero
;
232 public object Clone ()
234 return new Icon (this, this.Width
, this.Height
);
237 public static Icon
FromHandle (IntPtr handle
)
239 if (handle
== IntPtr
.Zero
)
240 throw new ArgumentException ("handle");
242 return new Icon (handle
);
245 public void Save (Stream outputStream
)
247 if (iconDir
.idEntries
!=null){
248 BinaryWriter bw
= new BinaryWriter (outputStream
);
250 bw
.Write (iconDir
.idReserved
);
251 bw
.Write (iconDir
.idType
);
252 ushort count
= iconDir
.idCount
;
255 //now write iconDirEntries
256 for (int i
=0; i
<(int)count
; i
++){
257 IconDirEntry ide
= iconDir
.idEntries
[i
];
258 bw
.Write (ide
.width
);
259 bw
.Write (ide
.height
);
260 bw
.Write (ide
.colorCount
);
261 bw
.Write (ide
.reserved
);
262 bw
.Write (ide
.planes
);
263 bw
.Write (ide
.bitCount
);
264 bw
.Write (ide
.bytesInRes
);
265 bw
.Write (ide
.imageOffset
);
268 //now write iconImage data
269 for (int i
=0; i
<(int)count
; i
++){
270 BitmapInfoHeader bih
= imageData
[i
].iconHeader
;
271 bw
.Write (bih
.biSize
);
272 bw
.Write (bih
.biWidth
);
273 bw
.Write (bih
.biHeight
);
274 bw
.Write (bih
.biPlanes
);
275 bw
.Write (bih
.biBitCount
);
276 bw
.Write (bih
.biCompression
);
277 bw
.Write (bih
.biSizeImage
);
278 bw
.Write (bih
.biXPelsPerMeter
);
279 bw
.Write (bih
.biYPelsPerMeter
);
280 bw
.Write (bih
.biClrUsed
);
281 bw
.Write (bih
.biClrImportant
);
283 //now write color table
284 int colCount
= imageData
[i
].iconColors
.Length
;
285 for (int j
=0; j
<colCount
; j
++)
286 bw
.Write (imageData
[i
].iconColors
[j
]);
289 bw
.Write (imageData
[i
].iconXOR
);
292 bw
.Write (imageData
[i
].iconAND
);
298 public Bitmap
ToBitmap ()
302 if (imageData
!= null) {
304 // select active icon from the iconDirEntry
305 IconImage ii
= imageData
[this.id
];
306 MemoryStream stream
= new MemoryStream ();
308 BinaryWriter writer
= new BinaryWriter (stream
);
311 // write bitmap file header
312 // start with writing signature
316 // write the file size
317 // file size = bitmapfileheader + bitmapinfo +
318 // colorpalette + image bits
319 // sizeof bitmapfileheader = 14 bytes
320 // sizeof bitmapinfo = 40 bytes
321 uint offSet
= (uint) (14 + 40 + ii
.iconColors
.Length
* 4);
322 uint fileSize
= (uint) (offSet
+ ii
.iconXOR
.Length
);
323 writer
.Write (fileSize
);
325 // write reserved words
326 ushort reserved12
= 0;
327 writer
.Write (reserved12
);
328 writer
.Write (reserved12
);
331 writer
.Write (offSet
);
333 // write bitmapfile header
334 BitmapInfoHeader bih
= ii
.iconHeader
;
335 writer
.Write (bih
.biSize
);
336 writer
.Write (bih
.biWidth
);
337 writer
.Write (bih
.biHeight
/2);
338 writer
.Write (bih
.biPlanes
);
339 writer
.Write (bih
.biBitCount
);
340 writer
.Write (bih
.biCompression
);
341 writer
.Write (bih
.biSizeImage
);
342 writer
.Write (bih
.biXPelsPerMeter
);
343 writer
.Write (bih
.biYPelsPerMeter
);
344 writer
.Write (bih
.biClrUsed
);
345 writer
.Write (bih
.biClrImportant
);
348 int colCount
= ii
.iconColors
.Length
;
349 for (int j
= 0; j
< colCount
; j
++)
350 writer
.Write (ii
.iconColors
[j
]);
353 writer
.Write (ii
.iconXOR
);
359 // create bitmap from stream and return
363 new_bmp
= new Bitmap(stream
);
364 bmp
= new Bitmap(new_bmp
, bih
.biWidth
, bih
.biHeight
/2);
367 bmp
= new Bitmap(stream
);
370 // This hack is so ugly, it's embarassing.
371 // But icons are small, so it should be ok for now
372 for (int y
= 0; y
< bih
.biHeight
/2; y
++) {
373 for (int x
= 0; x
< bih
.biWidth
/ 8; x
++) {
374 for (int bit
= 7; bit
>= 0; bit
--) {
375 if (((ii
.iconAND
[y
* bih
.biWidth
/ 8 +x
] >> bit
) & 1) != 0) {
376 bmp
.SetPixel(x
*8 + 7-bit
, bih
.biHeight
/2 - y
- 1, Color
.Transparent
);
382 } catch (Exception e
) {
385 writer
.Close (); // closes the underlying stream as well
388 bmp
= new Bitmap (32, 32);
394 public override string ToString ()
396 //is this correct, this is what returned by .Net
401 public IntPtr Handle
{
410 return iconSize
.Height
;
423 return iconSize
.Width
;
434 private void InitFromStreamWithSize (Stream stream
, int width
, int height
)
436 //read the icon header
437 if (stream
== null || stream
.Length
== 0)
438 throw new System
.ArgumentException ("The argument 'stream' must be a picture that can be used as a Icon", "stream");
440 BinaryReader reader
= new BinaryReader (stream
);
442 //iconDir = new IconDir ();
443 iconDir
.idReserved
= reader
.ReadUInt16();
444 if (iconDir
.idReserved
!= 0) //must be 0
445 throw new System
.ArgumentException ("Invalid Argument", "stream");
447 iconDir
.idType
= reader
.ReadUInt16();
448 if (iconDir
.idType
!= 1) //must be 1
449 throw new System
.ArgumentException ("Invalid Argument", "stream");
451 ushort dirEntryCount
= reader
.ReadUInt16();
452 iconDir
.idCount
= dirEntryCount
;
453 iconDir
.idEntries
= new IconDirEntry
[dirEntryCount
];
454 imageData
= new IconImage
[dirEntryCount
];
455 bool sizeObtained
= false;
456 //now read in the IconDirEntry structures
457 for (int i
=0; i
<dirEntryCount
; i
++){
459 ide
.width
= reader
.ReadByte ();
460 ide
.height
= reader
.ReadByte ();
461 ide
.colorCount
= reader
.ReadByte ();
462 ide
.reserved
= reader
.ReadByte ();
463 ide
.planes
= reader
.ReadUInt16 ();
464 ide
.bitCount
= reader
.ReadUInt16 ();
465 ide
.bytesInRes
= reader
.ReadUInt32 ();
466 ide
.imageOffset
= reader
.ReadUInt32 ();
467 iconDir
.idEntries
[i
] = ide
;
468 //is this is the best fit??
470 if (ide
.height
==height
&& ide
.width
==width
) {
471 this.id
= (ushort) i
;
473 this.iconSize
.Height
= ide
.height
;
474 this.iconSize
.Width
= ide
.width
;
477 //if we havent found the best match, return the one with the
478 //largest size. Is this approach correct??
480 uint largestSize
= 0;
481 for (int j
=0; j
<dirEntryCount
; j
++){
482 if (iconDir
.idEntries
[j
].bytesInRes
>= largestSize
) {
483 largestSize
= iconDir
.idEntries
[j
].bytesInRes
;
484 this.id
= (ushort) j
;
485 this.iconSize
.Height
= iconDir
.idEntries
[j
].height
;
486 this.iconSize
.Width
= iconDir
.idEntries
[j
].width
;
491 //now read in the icon data
492 for (int j
= 0; j
<dirEntryCount
; j
++)
494 IconImage iidata
= new IconImage();
495 BitmapInfoHeader bih
= new BitmapInfoHeader();
496 stream
.Seek (iconDir
.idEntries
[j
].imageOffset
, SeekOrigin
.Begin
);
497 byte [] buffer
= new byte [iconDir
.idEntries
[j
].bytesInRes
];
498 stream
.Read (buffer
, 0, buffer
.Length
);
499 BinaryReader bihReader
= new BinaryReader (new MemoryStream(buffer
));
500 bih
.biSize
= bihReader
.ReadUInt32 ();
501 bih
.biWidth
= bihReader
.ReadInt32 ();
502 bih
.biHeight
= bihReader
.ReadInt32 ();
503 bih
.biPlanes
= bihReader
.ReadUInt16 ();
504 bih
.biBitCount
= bihReader
.ReadUInt16 ();
505 bih
.biCompression
= bihReader
.ReadUInt32 ();
506 bih
.biSizeImage
= bihReader
.ReadUInt32 ();
507 bih
.biXPelsPerMeter
= bihReader
.ReadInt32 ();
508 bih
.biYPelsPerMeter
= bihReader
.ReadInt32 ();
509 bih
.biClrUsed
= bihReader
.ReadUInt32 ();
510 bih
.biClrImportant
= bihReader
.ReadUInt32 ();
512 iidata
.iconHeader
= bih
;
513 //Read the number of colors used and corresponding memory occupied by
514 //color table. Fill this memory chunk into rgbquad[]
516 switch (bih
.biBitCount
){
517 case 1: numColors
= 2;
519 case 4: numColors
= 16;
521 case 8: numColors
= 256;
523 default: numColors
= 0;
527 iidata
.iconColors
= new uint [numColors
];
528 for (int i
=0; i
<numColors
; i
++)
529 iidata
.iconColors
[i
] = bihReader
.ReadUInt32 ();
531 //XOR mask is immediately after ColorTable and its size is
532 //icon height* no. of bytes per line
534 //icon height is half of BITMAPINFOHEADER.biHeight, since it contains
535 //both XOR as well as AND mask bytes
536 int iconHeight
= bih
.biHeight
/2;
538 //bytes per line should should be uint aligned
539 int numBytesPerLine
= ((((bih
.biWidth
* bih
.biPlanes
* bih
.biBitCount
)+ 31)>>5)<<2);
541 //Determine the XOR array Size
542 int xorSize
= numBytesPerLine
* iconHeight
;
543 iidata
.iconXOR
= new byte [xorSize
];
544 for (int i
=0; i
<xorSize
; i
++)
545 iidata
.iconXOR
[i
] = bihReader
.ReadByte();
547 //Determine the AND array size
548 //For this i subtract the current position from the length.
550 int andSize
= (int) (bihReader
.BaseStream
.Length
- bihReader
.BaseStream
.Position
);
551 iidata
.iconAND
= new byte [andSize
];
552 for (int i
=0; i
<andSize
; i
++)
553 iidata
.iconAND
[i
] = bihReader
.ReadByte();
555 imageData
[j
] = iidata
;