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;
103 public Icon (Icon original, int width, int height) : this (original, new Size(width, height))
107 public Icon (Icon original, Size size)
109 this.iconSize = size;
110 this.winHandle = original.winHandle;
111 this.iconDir = original.iconDir;
112 this.imageData = original.imageData;
114 int count = iconDir.idCount;
115 bool sizeObtained = false;
116 for (int i=0; i<count; i++){
117 IconDirEntry ide = iconDir.idEntries [i];
119 if (ide.height==size.Height && ide.width==size.Width) {
120 this.id = (ushort) i;
122 this.iconSize.Height = ide.height;
123 this.iconSize.Width = ide.width;
129 uint largestSize = 0;
130 for (int j=0; j<count; j++){
131 if (iconDir.idEntries [j].bytesInRes >= largestSize){
132 largestSize = iconDir.idEntries [j].bytesInRes;
133 this.id = (ushort) j;
134 this.iconSize.Height = iconDir.idEntries [j].height;
135 this.iconSize.Width = iconDir.idEntries [j].width;
141 public Icon (Stream stream) : this (stream, 32, 32)
145 public Icon (Stream stream, int width, int height)
147 //read the icon header
148 if (stream == null || stream.Length == 0)
149 throw new System.ArgumentException ("The argument 'stream' must be a picture that can be used as a Icon", "stream");
151 BinaryReader reader = new BinaryReader (stream);
153 //iconDir = new IconDir ();
154 iconDir.idReserved = reader.ReadUInt16();
155 if (iconDir.idReserved != 0) //must be 0
156 throw new System.ArgumentException ("Invalid Argument", "stream");
158 iconDir.idType = reader.ReadUInt16();
159 if (iconDir.idType != 1) //must be 1
160 throw new System.ArgumentException ("Invalid Argument", "stream");
162 ushort dirEntryCount = reader.ReadUInt16();
163 iconDir.idCount = dirEntryCount;
164 iconDir.idEntries = new IconDirEntry [dirEntryCount];
165 imageData = new IconImage [dirEntryCount];
166 bool sizeObtained = false;
167 //now read in the IconDirEntry structures
168 for (int i=0; i<dirEntryCount; i++){
170 ide.width = reader.ReadByte ();
171 ide.height = reader.ReadByte ();
172 ide.colorCount = reader.ReadByte ();
173 ide.reserved = reader.ReadByte ();
174 ide.planes = reader.ReadUInt16 ();
175 ide.bitCount = reader.ReadUInt16 ();
176 ide.bytesInRes = reader.ReadUInt32 ();
177 ide.imageOffset = reader.ReadUInt32 ();
178 iconDir.idEntries [i] = ide;
179 //is this is the best fit??
181 if (ide.height==height && ide.width==width) {
182 this.id = (ushort) i;
184 this.iconSize.Height = ide.height;
185 this.iconSize.Width = ide.width;
188 //if we havent found the best match, return the one with the
189 //largest size. Is this approach correct??
191 uint largestSize = 0;
192 for (int j=0; j<dirEntryCount; j++){
193 if (iconDir.idEntries [j].bytesInRes >= largestSize) {
194 largestSize = iconDir.idEntries [j].bytesInRes;
195 this.id = (ushort) j;
196 this.iconSize.Height = iconDir.idEntries [j].height;
197 this.iconSize.Width = iconDir.idEntries [j].width;
202 //now read in the icon data
203 for (int j = 0; j<dirEntryCount; j++)
205 IconImage iidata = new IconImage();
206 BitmapInfoHeader bih = new BitmapInfoHeader();
207 stream.Seek (iconDir.idEntries [j].imageOffset, SeekOrigin.Begin);
208 byte [] buffer = new byte [iconDir.idEntries [j].bytesInRes];
209 stream.Read (buffer, 0, buffer.Length);
210 BinaryReader bihReader = new BinaryReader (new MemoryStream(buffer));
211 bih.biSize = bihReader.ReadUInt32 ();
212 bih.biWidth = bihReader.ReadInt32 ();
213 bih.biHeight = bihReader.ReadInt32 ();
214 bih.biPlanes = bihReader.ReadUInt16 ();
215 bih.biBitCount = bihReader.ReadUInt16 ();
216 bih.biCompression = bihReader.ReadUInt32 ();
217 bih.biSizeImage = bihReader.ReadUInt32 ();
218 bih.biXPelsPerMeter = bihReader.ReadInt32 ();
219 bih.biYPelsPerMeter = bihReader.ReadInt32 ();
220 bih.biClrUsed = bihReader.ReadUInt32 ();
221 bih.biClrImportant = bihReader.ReadUInt32 ();
223 iidata.iconHeader = bih;
224 //Read the number of colors used and corresponding memory occupied by
225 //color table. Fill this memory chunk into rgbquad[]
227 switch (bih.biBitCount){
228 case 1: numColors = 2;
230 case 4: numColors = 16;
232 case 8: numColors = 256;
234 default: numColors = 0;
238 iidata.iconColors = new uint [numColors];
239 for (int i=0; i<numColors; i++)
240 iidata.iconColors [i] = bihReader.ReadUInt32 ();
242 //XOR mask is immediately after ColorTable and its size is
243 //icon height* no. of bytes per line
245 //icon height is half of BITMAPINFOHEADER.biHeight, since it contains
246 //both XOR as well as AND mask bytes
247 int iconHeight = bih.biHeight/2;
249 //bytes per line should should be uint aligned
250 int numBytesPerLine = ((((bih.biWidth * bih.biPlanes * bih.biBitCount)+ 31)>>5)<<2);
252 //Determine the XOR array Size
253 int xorSize = numBytesPerLine * iconHeight;
254 iidata.iconXOR = new byte [xorSize];
255 for (int i=0; i<xorSize; i++)
256 iidata.iconXOR[i] = bihReader.ReadByte();
258 //Determine the AND array size
259 //For this i subtract the current position from the length.
261 int andSize = (int) (bihReader.BaseStream.Length - bihReader.BaseStream.Position);
262 iidata.iconAND = new byte [andSize];
263 for (int i=0; i<andSize; i++)
264 iidata.iconAND[i] = bihReader.ReadByte();
266 imageData [j] = iidata;
273 public Icon (string fileName) : this (new FileStream (fileName, FileMode.Open))
277 [MonoTODO ("Implement")]
278 public Icon (Type type, string resource)
280 using (Stream s = type.Assembly.GetManifestResourceStream (resource)) {
282 throw new FileNotFoundException ("Resource name was not found: `" + resource + "'");
284 throw new NotImplementedException ();
287 [MonoTODO ("Implement")]
288 private Icon (SerializationInfo info, StreamingContext context)
290 //FIXME, need to check how MS stores Icon structure
291 //Will serialized form help
292 throw new NotImplementedException ();
295 [MonoTODO ("Implement")]
296 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
298 throw new NotImplementedException ();
301 public void Dispose ()
303 //FIXME What needs to be called to free memory pointed by handle
304 if (winHandle!=IntPtr.Zero)
305 winHandle = IntPtr.Zero;
308 public object Clone ()
310 return new Icon (this, this.Width, this.Height);
313 [MonoTODO ("Implement")]
314 public static Icon FromHandle (IntPtr handle)
316 throw new NotImplementedException ();
319 public void Save (Stream outputStream)
321 if (iconDir.idEntries!=null){
322 BinaryWriter bw = new BinaryWriter (outputStream);
324 bw.Write (iconDir.idReserved);
325 bw.Write (iconDir.idType);
326 ushort count = iconDir.idCount;
329 //now write iconDirEntries
330 for (int i=0; i<(int)count; i++){
331 IconDirEntry ide = iconDir.idEntries [i];
332 bw.Write (ide.width);
333 bw.Write (ide.height);
334 bw.Write (ide.colorCount);
335 bw.Write (ide.reserved);
336 bw.Write (ide.planes);
337 bw.Write (ide.bitCount);
338 bw.Write (ide.bytesInRes);
339 bw.Write (ide.imageOffset);
342 //now write iconImage data
343 for (int i=0; i<(int)count; i++){
344 BitmapInfoHeader bih = imageData [i].iconHeader;
345 bw.Write (bih.biSize);
346 bw.Write (bih.biWidth);
347 bw.Write (bih.biHeight);
348 bw.Write (bih.biPlanes);
349 bw.Write (bih.biBitCount);
350 bw.Write (bih.biCompression);
351 bw.Write (bih.biSizeImage);
352 bw.Write (bih.biXPelsPerMeter);
353 bw.Write (bih.biYPelsPerMeter);
354 bw.Write (bih.biClrUsed);
355 bw.Write (bih.biClrImportant);
357 //now write color table
358 int colCount = imageData [i].iconColors.Length;
359 for (int j=0; j<colCount; j++)
360 bw.Write (imageData [i].iconColors [j]);
363 bw.Write (imageData [i].iconXOR);
366 bw.Write (imageData [i].iconAND);
372 public Bitmap ToBitmap ()
375 if (imageData!=null){
376 FileStream stream=null;
377 //select active icon from the iconDirEntry
378 IconImage ii = imageData [this.id];
379 //MemoryStream stream = new MemoryStream ();
380 //UGLY HACK.....Hate to create a FileStream and then do writing and reading back
381 //but no other option as using MemoryStream causes the program to fail
382 //The Constructor for Bitmap which takes a memory stream some how
383 //fails everytime and i m not able to get the reason for that.
385 stream = new FileStream ("yajnas_temp.bmp", FileMode.CreateNew);
386 BinaryWriter writer = new BinaryWriter (stream);
388 //write bitmap file header
389 //start with writing signature
393 //now write file size
394 //file size is bitmapfileheader + bitmapinfo + colorpalette + image bits
395 //size of bitmapfileheader is 14 bytes, bitmapinfo is 40 bytes
396 uint offSet = (uint)(14+40+ ii.iconColors.Length* 4);
397 uint fileSize = (uint) (offSet + ii.iconXOR.Length);
398 writer.Write (fileSize);
400 //write reserved words
401 ushort reserved12 = 0;
402 writer.Write (reserved12);
403 writer.Write (reserved12);
406 writer.Write (offSet);
408 //now write bitmapfile header
409 BitmapInfoHeader bih = ii.iconHeader;
410 writer.Write (bih.biSize);
411 writer.Write (bih.biWidth);
412 writer.Write (bih.biHeight/2);
413 writer.Write (bih.biPlanes);
414 writer.Write (bih.biBitCount);
415 writer.Write (bih.biCompression);
416 writer.Write (bih.biSizeImage);
417 writer.Write (bih.biXPelsPerMeter);
418 writer.Write (bih.biYPelsPerMeter);
419 writer.Write (bih.biClrUsed);
420 writer.Write (bih.biClrImportant);
422 //now write color table
423 int colCount = ii.iconColors.Length;
424 for (int j=0; j<colCount; j++)
425 writer.Write (ii.iconColors [j]);
427 //now write image bits
428 writer.Write (ii.iconXOR);
433 //create bitmap from stream and return
434 bmp = new Bitmap("yajnas_temp.bmp");
435 File.Delete("yajnas_temp.bmp");
436 }catch (Exception e){
440 File.Delete("yajnas_temp.bmp");
443 bmp = new Bitmap (32, 32);
449 public override string ToString ()
451 //is this correct, this is what returned by .Net
456 public IntPtr Handle {
465 return iconSize.Height;
478 return iconSize.Width;