Brush.jvm.cs: fixed transform methods, createContext
[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                 bool destroyIcon = true;
99                         
100                 private Icon ()
101                 {
102                 }
103
104                 [MonoTODO ("Implement fully")]
105                 private Icon (IntPtr handle)
106                 {
107                         this.winHandle = handle;
108
109                         IconInfo ii;
110                         GDIPlus.GetIconInfo (winHandle, out ii);
111                         if (ii.IsIcon) {
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);
114                         }
115                         else {
116                                 throw new NotImplementedException ();
117                         }
118
119                         this.destroyIcon = false;
120                 }
121                 
122                 public Icon (Icon original, int width, int height) : this (original, new Size(width, height))
123                 {                       
124                 }
125
126                 public Icon (Icon original, Size size)
127                 {
128                         this.iconSize = size;
129                         this.winHandle = original.winHandle;
130                         this.iconDir = original.iconDir;
131                         this.imageData = original.imageData;
132                         
133                         int count = iconDir.idCount;
134                         bool sizeObtained = false;
135                         for (int i=0; i<count; i++){
136                                 IconDirEntry ide = iconDir.idEntries [i];
137                                 if (!sizeObtained)   
138                                         if (ide.height==size.Height && ide.width==size.Width) {
139                                                 this.id = (ushort) i;
140                                                 sizeObtained = true;
141                                                 this.iconSize.Height = ide.height;
142                                                 this.iconSize.Width = ide.width;
143                                                 break;
144                                         }
145                         }
146
147                         if (!sizeObtained){
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;
155                                         }
156                                 }
157                         }
158                 }
159
160                 public Icon (Stream stream) : this (stream, 32, 32) 
161                 {
162                 }
163
164                 public Icon (Stream stream, int width, int height)
165                 {
166                         InitFromStreamWithSize (stream, width, height);
167                 }
168
169                 public Icon (string fileName) : this (new FileStream (fileName, FileMode.Open))
170                 {                       
171                 }
172
173                 public Icon (Type type, string resource)
174                 {
175                         using (Stream s = type.Assembly.GetManifestResourceStream (type, resource)) {
176                                 if (s == null) {
177                                         throw new FileNotFoundException ("Resource name was not found: `" + resource + "'");
178                                 }
179                                 InitFromStreamWithSize (s, 32, 32);             // 32x32 is default
180                         }
181                 }
182
183                 private Icon (SerializationInfo info, StreamingContext context)
184                 {
185                         MemoryStream dataStream = null;
186                         int width=0;
187                         int height=0;
188                         foreach (SerializationEntry serEnum in info) {
189                                 if (String.Compare(serEnum.Name, "IconData", true) == 0) {
190                                         dataStream = new MemoryStream ((byte []) serEnum.Value);
191                                 }
192                                 if (String.Compare(serEnum.Name, "IconSize", true) == 0) {
193                                         Size iconSize = (Size) serEnum.Value;
194                                         width = iconSize.Width;
195                                         height = iconSize.Height;
196                                 }
197                         }
198                         if (dataStream != null && width != 0 && height != 0) {
199                                 dataStream.Seek (0, SeekOrigin.Begin);
200                                 InitFromStreamWithSize (dataStream, width, height);
201                         }
202                 }
203
204                 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
205                 {
206                         MemoryStream ms = new MemoryStream ();
207                         Save (ms);
208                         info.AddValue ("IconSize", this.Size, typeof (Size));
209                         info.AddValue ("IconData", ms.ToArray ());
210                 }
211
212                 public void Dispose ()
213                 {
214                         DisposeIcon ();
215                         GC.SuppressFinalize(this);
216                 }
217
218                 void DisposeIcon ()
219                 {
220                         if (winHandle ==IntPtr.Zero)
221                                 return;
222
223                         if (destroyIcon) {
224                                 //TODO: will have to call some win32 icon stuff
225                                 winHandle = IntPtr.Zero;
226                         }
227                 }
228
229                 public object Clone ()
230                 {
231                         return new Icon (this, this.Width, this.Height);
232                 }
233
234                 public static Icon FromHandle (IntPtr handle)
235                 {
236                         if (handle == IntPtr.Zero)
237                                 throw new ArgumentException ("handle");
238
239                         return new Icon (handle);
240                 }
241
242                 public void Save (Stream outputStream)
243                 {
244                         if (iconDir.idEntries!=null){
245                                 BinaryWriter bw = new BinaryWriter (outputStream);
246                                 //write icondir
247                                 bw.Write (iconDir.idReserved);
248                                 bw.Write (iconDir.idType);
249                                 ushort count = iconDir.idCount;
250                                 bw.Write (count);
251                                 
252                                 //now write iconDirEntries
253                                 for (int i=0; i<(int)count; i++){
254                                         IconDirEntry ide = iconDir.idEntries [i];
255                                         bw.Write (ide.width);
256                                         bw.Write (ide.height);
257                                         bw.Write (ide.colorCount);
258                                         bw.Write (ide.reserved);
259                                         bw.Write (ide.planes);
260                                         bw.Write (ide.bitCount);
261                                         bw.Write (ide.bytesInRes);
262                                         bw.Write (ide.imageOffset);                             
263                                 }
264                                 
265                                 //now write iconImage data
266                                 for (int i=0; i<(int)count; i++){
267                                         BitmapInfoHeader bih = imageData [i].iconHeader;
268                                         bw.Write (bih.biSize);
269                                         bw.Write (bih.biWidth);
270                                         bw.Write (bih.biHeight);
271                                         bw.Write (bih.biPlanes);
272                                         bw.Write (bih.biBitCount);
273                                         bw.Write (bih.biCompression);
274                                         bw.Write (bih.biSizeImage);
275                                         bw.Write (bih.biXPelsPerMeter);
276                                         bw.Write (bih.biYPelsPerMeter);
277                                         bw.Write (bih.biClrUsed);
278                                         bw.Write (bih.biClrImportant);
279
280                                         //now write color table
281                                         int colCount = imageData [i].iconColors.Length;
282                                         for (int j=0; j<colCount; j++)
283                                                 bw.Write (imageData [i].iconColors [j]);
284
285                                         //now write XOR Mask
286                                         bw.Write (imageData [i].iconXOR);
287                                         
288                                         //now write AND Mask
289                                         bw.Write (imageData [i].iconAND);
290                                 }
291                                 bw.Flush();                             
292                         }
293                 }
294
295                 public Bitmap ToBitmap ()
296                 {
297                         Bitmap bmp;
298
299                         if (imageData != null) {
300
301                                 // select active icon from the iconDirEntry
302                                 IconImage ii = imageData [this.id];
303                                 MemoryStream stream = new MemoryStream ();
304
305                                 BinaryWriter writer = new BinaryWriter (stream);
306
307                                 try {
308                                         // write bitmap file header
309                                         // start with writing signature
310                                         writer.Write ('B');
311                                         writer.Write ('M');
312
313                                         // write the file size
314                                         // file size = bitmapfileheader + bitmapinfo +
315                                         //               colorpalette + image bits
316                                         // sizeof bitmapfileheader = 14 bytes
317                                         // sizeof bitmapinfo = 40 bytes
318                                         uint offSet = (uint) (14 + 40 + ii.iconColors.Length * 4);
319                                         uint fileSize = (uint) (offSet + ii.iconXOR.Length);
320                                         writer.Write (fileSize);
321                                         
322                                         // write reserved words
323                                         ushort reserved12 = 0;
324                                         writer.Write (reserved12);
325                                         writer.Write (reserved12);
326
327                                         // write offset
328                                         writer.Write (offSet);
329
330                                         // write bitmapfile header
331                                         BitmapInfoHeader bih = ii.iconHeader;
332                                         writer.Write (bih.biSize);
333                                         writer.Write (bih.biWidth);
334                                         writer.Write (bih.biHeight/2);
335                                         writer.Write (bih.biPlanes);
336                                         writer.Write (bih.biBitCount);
337                                         writer.Write (bih.biCompression);
338                                         writer.Write (bih.biSizeImage);
339                                         writer.Write (bih.biXPelsPerMeter);
340                                         writer.Write (bih.biYPelsPerMeter);
341                                         writer.Write (bih.biClrUsed);
342                                         writer.Write (bih.biClrImportant);
343
344                                         // write color table
345                                         int colCount = ii.iconColors.Length;
346                                         for (int j = 0; j < colCount; j++)
347                                                 writer.Write (ii.iconColors [j]);
348
349                                         // write image bits
350                                         writer.Write (ii.iconXOR);
351
352                                         writer.Flush ();
353
354                                         // create bitmap from stream and return
355                                         if (colCount > 0) {
356                                                 Bitmap new_bmp;
357
358                                                 new_bmp = new Bitmap(stream);
359                                                 bmp = new Bitmap(new_bmp, bih.biWidth, bih.biHeight/2);
360                                                 new_bmp.Dispose();
361                                         } else {
362                                                 bmp = new Bitmap(stream);
363                                         }
364
365                                         // This hack is so ugly, it's embarassing. 
366                                         // But icons are small, so it should be ok for now
367                                         for (int y = 0; y < bih.biHeight/2; y++) {
368                                                 for (int x = 0; x < bih.biWidth / 8; x++) {
369                                                         for (int bit = 7; bit >= 0; bit--) {
370                                                                 if (((ii.iconAND[y * bih.biWidth / 8 +x] >> bit) & 1) != 0) {
371                                                                         bmp.SetPixel(x*8 + 7-bit, bih.biHeight/2 - y - 1, Color.Transparent);
372                                                                 }
373                                                         }
374                                                 }
375                                         }
376
377                                 } catch (Exception e) {
378                                         throw e;
379                                 } finally {
380                                         writer.Close (); // closes the underlying stream as well
381                                 }
382                         } else {
383                                 bmp = new Bitmap (32, 32);
384                         }
385
386                         return bmp;
387                 }
388
389                 public override string ToString ()
390                 {
391                         //is this correct, this is what returned by .Net
392                         return "<Icon>";                        
393                 }
394
395                 [Browsable (false)]
396                 public IntPtr Handle {
397                         get { 
398                                 return winHandle;
399                         }
400                 }
401
402                 [Browsable (false)]
403                 public int Height {
404                         get {
405                                 return iconSize.Height;
406                         }
407                 }
408
409                 public Size Size {
410                         get {
411                                 return iconSize;
412                         }
413                 }
414
415                 [Browsable (false)]
416                 public int Width {
417                         get {
418                                 return iconSize.Width;
419                         }
420                 }
421
422                 ~Icon ()
423                 {
424                         DisposeIcon ();
425                 }
426                         
427                 private void InitFromStreamWithSize (Stream stream, int width, int height)
428                 {
429                         //read the icon header
430                         if (stream == null || stream.Length == 0)
431                                 throw new System.ArgumentException ("The argument 'stream' must be a picture that can be used as a Icon", "stream");
432                         
433                         BinaryReader reader = new BinaryReader (stream);
434             
435                         //iconDir = new IconDir ();
436                         iconDir.idReserved = reader.ReadUInt16();
437                         if (iconDir.idReserved != 0) //must be 0
438                                 throw new System.ArgumentException ("Invalid Argument", "stream");
439                         
440                         iconDir.idType = reader.ReadUInt16();
441                         if (iconDir.idType != 1) //must be 1
442                                 throw new System.ArgumentException ("Invalid Argument", "stream");
443
444                         ushort dirEntryCount = reader.ReadUInt16();
445                         iconDir.idCount = dirEntryCount;
446                         iconDir.idEntries = new IconDirEntry [dirEntryCount];
447                         imageData = new IconImage [dirEntryCount];
448                         bool sizeObtained = false;
449                         //now read in the IconDirEntry structures
450                         for (int i=0; i<dirEntryCount; i++){
451                                 IconDirEntry ide;
452                                 ide.width = reader.ReadByte ();
453                                 ide.height = reader.ReadByte ();
454                                 ide.colorCount = reader.ReadByte ();
455                                 ide.reserved = reader.ReadByte ();
456                                 ide.planes = reader.ReadUInt16 ();
457                                 ide.bitCount = reader.ReadUInt16 ();
458                                 ide.bytesInRes = reader.ReadUInt32 ();
459                                 ide.imageOffset = reader.ReadUInt32 ();
460                                 iconDir.idEntries [i] = ide;
461                                 //is this is the best fit??
462                                 if (!sizeObtained)   
463                                         if (ide.height==height && ide.width==width) {
464                                                 this.id = (ushort) i;
465                                                 sizeObtained = true;
466                                                 this.iconSize.Height = ide.height;
467                                                 this.iconSize.Width = ide.width;
468                                         }                       
469                         }
470                         //if we havent found the best match, return the one with the
471                         //largest size. Is this approach correct??
472                         if (!sizeObtained){
473                                 uint largestSize = 0;
474                                 for (int j=0; j<dirEntryCount; j++){
475                                         if (iconDir.idEntries [j].bytesInRes >= largestSize)    {
476                                                 largestSize = iconDir.idEntries [j].bytesInRes;
477                                                 this.id = (ushort) j;
478                                                 this.iconSize.Height = iconDir.idEntries [j].height;
479                                                 this.iconSize.Width = iconDir.idEntries [j].width;
480                                         }
481                                 }
482                         }
483                         
484                         //now read in the icon data
485                         for (int j = 0; j<dirEntryCount; j++)
486                         {
487                                 IconImage iidata = new IconImage();
488                                 BitmapInfoHeader bih = new BitmapInfoHeader();
489                                 stream.Seek (iconDir.idEntries [j].imageOffset, SeekOrigin.Begin);
490                                 byte [] buffer = new byte [iconDir.idEntries [j].bytesInRes];
491                                 stream.Read (buffer, 0, buffer.Length);
492                                 BinaryReader bihReader = new BinaryReader (new MemoryStream(buffer));
493                                 bih.biSize = bihReader.ReadUInt32 ();
494                                 bih.biWidth = bihReader.ReadInt32 ();
495                                 bih.biHeight = bihReader.ReadInt32 ();
496                                 bih.biPlanes = bihReader.ReadUInt16 ();
497                                 bih.biBitCount = bihReader.ReadUInt16 ();
498                                 bih.biCompression = bihReader.ReadUInt32 ();
499                                 bih.biSizeImage = bihReader.ReadUInt32 ();
500                                 bih.biXPelsPerMeter = bihReader.ReadInt32 ();
501                                 bih.biYPelsPerMeter = bihReader.ReadInt32 ();
502                                 bih.biClrUsed = bihReader.ReadUInt32 ();
503                                 bih.biClrImportant = bihReader.ReadUInt32 ();
504
505                                 iidata.iconHeader = bih;
506                                 //Read the number of colors used and corresponding memory occupied by
507                                 //color table. Fill this memory chunk into rgbquad[]
508                                 int numColors;
509                                 switch (bih.biBitCount){
510                                         case 1: numColors = 2;
511                                                 break;
512                                         case 4: numColors = 16;
513                                                 break;
514                                         case 8: numColors = 256;
515                                                 break;
516                                         default: numColors = 0;
517                                                 break;
518                                 }
519                                 
520                                 iidata.iconColors = new uint [numColors];
521                                 for (int i=0; i<numColors; i++)
522                                         iidata.iconColors [i] = bihReader.ReadUInt32 ();
523
524                                 //XOR mask is immediately after ColorTable and its size is 
525                                 //icon height* no. of bytes per line
526                                 
527                                 //icon height is half of BITMAPINFOHEADER.biHeight, since it contains
528                                 //both XOR as well as AND mask bytes
529                                 int iconHeight = bih.biHeight/2;
530                                 
531                                 //bytes per line should should be uint aligned
532                                 int numBytesPerLine = ((((bih.biWidth * bih.biPlanes * bih.biBitCount)+ 31)>>5)<<2);
533                                 
534                                 //Determine the XOR array Size
535                                 int xorSize = numBytesPerLine * iconHeight;
536                                 iidata.iconXOR = new byte [xorSize];
537                                 for (int i=0; i<xorSize; i++)
538                                         iidata.iconXOR[i] = bihReader.ReadByte();               
539                                 
540                                 //Determine the AND array size
541                                 //For this i subtract the current position from the length.
542                                 //ugly hack...
543                                 int andSize = (int) (bihReader.BaseStream.Length - bihReader.BaseStream.Position);
544                                 iidata.iconAND = new byte [andSize];
545                                 for (int i=0; i<andSize; i++)
546                                         iidata.iconAND[i] = bihReader.ReadByte();               
547                                 
548                                 imageData [j] = iidata;
549                                 bihReader.Close();
550                         }                       
551
552                         reader.Close();
553                 }
554         }
555 }