This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / class / System.Drawing / System.Drawing / Icon.cs
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 //
9 // Copyright (C) 2002 Ximian, Inc. http://www.ximian.com
10 // Copyright (C) 2004 Novell, Inc. http://www.novell.com
11 //
12
13 //
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 //
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:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
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.
34 //
35
36 using System;
37 using System.IO;
38 using System.Runtime.Serialization;
39 using System.Runtime.InteropServices;
40 using System.ComponentModel;
41
42 namespace System.Drawing
43 {
44         [Serializable]
45         [ComVisible (false)]
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
49         {
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 
60                 }; 
61
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
68                 };
69                 
70                 [StructLayout(LayoutKind.Sequential)]
71                 internal struct BitmapInfoHeader {
72                         internal uint   biSize; 
73                         internal int    biWidth; 
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; 
83                 };
84
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
91                 };      
92
93                 private Size iconSize;
94                 private IntPtr winHandle = IntPtr.Zero;
95                 private IconDir iconDir;
96                 private ushort id;
97                 private IconImage [] imageData;
98                         
99                 private Icon ()
100                 {
101                 }
102                 
103                 public Icon (Icon original, int width, int height) : this (original, new Size(width, height))
104                 {                       
105                 }
106
107                 public Icon (Icon original, Size size)
108                 {
109                         this.iconSize = size;
110                         this.winHandle = original.winHandle;
111                         this.iconDir = original.iconDir;
112                         this.imageData = original.imageData;
113                         
114                         int count = iconDir.idCount;
115                         bool sizeObtained = false;
116                         for (int i=0; i<count; i++){
117                                 IconDirEntry ide = iconDir.idEntries [i];
118                                 if (!sizeObtained)   
119                                         if (ide.height==size.Height && ide.width==size.Width) {
120                                                 this.id = (ushort) i;
121                                                 sizeObtained = true;
122                                                 this.iconSize.Height = ide.height;
123                                                 this.iconSize.Width = ide.width;
124                                                 break;
125                                         }
126                         }
127
128                         if (!sizeObtained){
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;
136                                         }
137                                 }
138                         }
139                 }
140
141                 public Icon (Stream stream) : this (stream, 32, 32) 
142                 {
143                 }
144
145                 public Icon (Stream stream, int width, int height)
146                 {
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");
150                         
151                         BinaryReader reader = new BinaryReader (stream);
152             
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");
157                         
158                         iconDir.idType = reader.ReadUInt16();
159                         if (iconDir.idType != 1) //must be 1
160                                 throw new System.ArgumentException ("Invalid Argument", "stream");
161
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++){
169                                 IconDirEntry ide;
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??
180                                 if (!sizeObtained)   
181                                         if (ide.height==height && ide.width==width) {
182                                                 this.id = (ushort) i;
183                                                 sizeObtained = true;
184                                                 this.iconSize.Height = ide.height;
185                                                 this.iconSize.Width = ide.width;
186                                         }                       
187                         }
188                         //if we havent found the best match, return the one with the
189                         //largest size. Is this approach correct??
190                         if (!sizeObtained){
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;
198                                         }
199                                 }
200                         }
201                         
202                         //now read in the icon data
203                         for (int j = 0; j<dirEntryCount; j++)
204                         {
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 ();
222
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[]
226                                 int numColors;
227                                 switch (bih.biBitCount){
228                                         case 1: numColors = 2;
229                                                 break;
230                                         case 4: numColors = 16;
231                                                 break;
232                                         case 8: numColors = 256;
233                                                 break;
234                                         default: numColors = 0;
235                                                 break;
236                                 }
237                                 
238                                 iidata.iconColors = new uint [numColors];
239                                 for (int i=0; i<numColors; i++)
240                                         iidata.iconColors [i] = bihReader.ReadUInt32 ();
241
242                                 //XOR mask is immediately after ColorTable and its size is 
243                                 //icon height* no. of bytes per line
244                                 
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;
248                                 
249                                 //bytes per line should should be uint aligned
250                                 int numBytesPerLine = ((((bih.biWidth * bih.biPlanes * bih.biBitCount)+ 31)>>5)<<2);
251                                 
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();               
257                                 
258                                 //Determine the AND array size
259                                 //For this i subtract the current position from the length.
260                                 //ugly hack...
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();               
265                                 
266                                 imageData [j] = iidata;
267                                 bihReader.Close();
268                         }                       
269
270                         reader.Close();
271                 }
272
273                 public Icon (string fileName) : this (new FileStream (fileName, FileMode.Open))
274                 {                       
275                 }
276
277                 [MonoTODO ("Implement")]
278                 public Icon (Type type, string resource)
279                 {
280                         using (Stream s = type.Assembly.GetManifestResourceStream (resource)) {
281                                 if (s == null)
282                                         throw new FileNotFoundException ("Resource name was not found: `" + resource + "'");                            
283                         }
284                         throw new NotImplementedException ();
285                 }
286
287                 [MonoTODO ("Implement")]
288                 private Icon (SerializationInfo info, StreamingContext context)
289                 {
290                         //FIXME, need to check how MS stores Icon structure
291                         //Will serialized form help
292                         throw new NotImplementedException ();
293                 }
294
295                 [MonoTODO ("Implement")]
296                 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
297                 {
298                         throw new NotImplementedException ();
299                 }
300
301                 public void Dispose ()
302                 {
303                         //FIXME What needs to be called to free memory pointed by handle
304                         if (winHandle!=IntPtr.Zero)
305                                 winHandle = IntPtr.Zero;
306                 }
307
308                 public object Clone ()
309                 {
310                         return new Icon (this, this.Width, this.Height);
311                 }
312
313                 [MonoTODO ("Implement")]
314                 public static Icon FromHandle (IntPtr handle)
315                 {
316                         throw new NotImplementedException ();
317                 }
318
319                 public void Save (Stream outputStream)
320                 {
321                         if (iconDir.idEntries!=null){
322                                 BinaryWriter bw = new BinaryWriter (outputStream);
323                                 //write icondir
324                                 bw.Write (iconDir.idReserved);
325                                 bw.Write (iconDir.idType);
326                                 ushort count = iconDir.idCount;
327                                 bw.Write (count);
328                                 
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);                             
340                                 }
341                                 
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);
356
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]);
361
362                                         //now write XOR Mask
363                                         bw.Write (imageData [i].iconXOR);
364                                         
365                                         //now write AND Mask
366                                         bw.Write (imageData [i].iconAND);
367                                 }
368                                 bw.Flush();                             
369                         }
370                 }
371
372                 public Bitmap ToBitmap ()
373                 {
374                         Bitmap bmp;
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.
384                                 try     {
385                                         stream = new FileStream ("yajnas_temp.bmp", FileMode.CreateNew);
386                                         BinaryWriter writer = new BinaryWriter (stream);
387                                 
388                                         //write bitmap file header
389                                         //start with writing signature
390                                         writer.Write ('B');
391                                         writer.Write ('M');
392                                 
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);
399                                         
400                                         //write reserved words
401                                         ushort reserved12 = 0;
402                                         writer.Write (reserved12);
403                                         writer.Write (reserved12);
404                                         
405                                         //write offset
406                                         writer.Write (offSet);
407                                         
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);
421                                         
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]);
426
427                                         //now write image bits
428                                         writer.Write (ii.iconXOR);
429
430                                         writer.Flush();
431                                         writer.Close();
432                                         stream.Close();
433                                         //create bitmap from stream and return
434                                         bmp = new Bitmap("yajnas_temp.bmp");
435                                         File.Delete("yajnas_temp.bmp");
436                                 }catch (Exception e){
437                                         throw e;
438                                 }finally {
439                                         stream.Close();
440                                         File.Delete("yajnas_temp.bmp");
441                                 }
442                         } else {
443                                 bmp = new Bitmap (32, 32);
444                         }
445
446                         return bmp;
447                 }
448
449                 public override string ToString ()
450                 {
451                         //is this correct, this is what returned by .Net
452                         return "<Icon>";                        
453                 }
454
455                 [Browsable (false)]
456                 public IntPtr Handle {
457                         get { 
458                                 return winHandle;
459                         }
460                 }
461
462                 [Browsable (false)]
463                 public int Height {
464                         get {
465                                 return iconSize.Height;
466                         }
467                 }
468
469                 public Size Size {
470                         get {
471                                 return iconSize;
472                         }
473                 }
474
475                 [Browsable (false)]
476                 public int Width {
477                         get {
478                                 return iconSize.Width;
479                         }
480                 }
481
482                 ~Icon ()
483                 {
484                         Dispose ();
485                 }
486                         
487         }
488 }