Merge pull request #1275 from ranma42/fix-lib64
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / Cursor.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004-2010 Novell, Inc.
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25
26 using System;
27 using System.Drawing;
28 using System.Drawing.Imaging;
29 using System.IO;
30 using System.ComponentModel;
31 using System.Runtime.InteropServices;
32 using System.Runtime.Serialization;
33 using System.Reflection;
34
35 namespace System.Windows.Forms {
36         [Editor("System.Drawing.Design.CursorEditor, " + Consts.AssemblySystem_Drawing_Design, typeof(System.Drawing.Design.UITypeEditor))]
37         [Serializable]
38         [TypeConverter(typeof(CursorConverter))]
39         public sealed class Cursor : IDisposable, ISerializable {
40                 #region Internal Structs
41                 [StructLayout (LayoutKind.Sequential)]
42                 private  struct CursorDir {
43                         internal ushort         idReserved;     // Reserved
44                         internal ushort         idType;         // resource type (2 for cursors)
45                         internal ushort         idCount;        // how many cursors
46                         internal CursorEntry[]  idEntries;      // the entries for each cursor
47                 };
48                 
49                 [StructLayout (LayoutKind.Sequential)]
50                 private  struct CursorEntry {
51                         internal byte           width;          // Width of cursor
52                         internal byte           height;         // Height of cursor
53                         internal byte           colorCount;     // colors in cursor
54                         internal byte           reserved;       // Reserved
55                         internal ushort         xHotspot;       // Hotspot X
56                         internal ushort         yHotspot;       // Hotspot Y
57                         internal ushort         bitCount;       // Bits per pixel
58                         internal uint           sizeInBytes;    // size of (CursorInfoHeader + ANDBitmap + ORBitmap)
59                         internal uint           fileOffset;     // position in file 
60                 }; 
61
62                 [StructLayout(LayoutKind.Sequential)]
63                 private  struct CursorInfoHeader {
64                         internal uint           biSize; 
65                         internal int            biWidth; 
66                         internal int            biHeight; 
67                         internal ushort         biPlanes; 
68                         internal ushort         biBitCount; 
69                         internal uint           biCompression; 
70                         internal uint           biSizeImage; 
71                         internal int            biXPelsPerMeter; 
72                         internal int            biYPelsPerMeter; 
73                         internal uint           biClrUsed; 
74                         internal uint           biClrImportant; 
75                 };
76
77                 [StructLayout(LayoutKind.Sequential)]
78                 private struct CursorImage {
79                         internal CursorInfoHeader       cursorHeader;   // image header
80                         internal uint[]                 cursorColors;   // colors table
81                         internal byte[]                 cursorXOR;      // bits for XOR mask
82                         internal byte[]                 cursorAND;      // bits for AND mask
83                 };
84                 #endregion      // Internal structs
85
86                 #region Local Variables
87                 private static Cursor   current;
88                 private CursorDir       cursor_dir;
89                 private CursorImage[]   cursor_data;
90                 private int             id;
91
92                 internal IntPtr         handle;
93                 private Size            size;
94                 private Bitmap          shape;
95                 private Bitmap          mask;
96                 private Bitmap          cursor;
97                 internal string         name;
98                 private StdCursor       std_cursor = (StdCursor) (-1);
99
100                 private object tag;
101
102                 #endregion      // Local Variables
103
104                 #region Public Constructors
105                 private void CreateCursor (Stream stream)
106                 {
107                         InitFromStream(stream);
108                         this.shape = ToBitmap(true, false);
109                         this.mask = ToBitmap(false, false);
110                         handle = XplatUI.DefineCursor(shape, mask, Color.FromArgb(255, 255, 255), Color.FromArgb(255, 255, 255), cursor_dir.idEntries[id].xHotspot, cursor_dir.idEntries[id].yHotspot);
111                         this.shape.Dispose();
112                         this.shape = null;
113                         this.mask.Dispose();
114                         this.mask = null;
115
116                         if (handle != IntPtr.Zero) {
117                                 this.cursor = ToBitmap(true, true);
118                         }
119                 }
120         
121                 internal Cursor (StdCursor cursor) : this (XplatUI.DefineStdCursor (cursor))
122                 {
123                         std_cursor = cursor;
124                 }
125                 
126                 private Cursor(SerializationInfo info, StreamingContext context)
127                 {
128                 }
129
130                 private Cursor()
131                 {
132                 }
133
134                 ~Cursor()
135                 {
136                         Dispose();
137                 }
138
139                 // This is supposed to take a Win32 handle
140                 public Cursor (IntPtr handle) 
141                 {
142                         this.handle = handle;
143                 }
144
145                 public Cursor (Stream stream)
146                 {
147                         CreateCursor(stream);
148                 }
149
150                 public Cursor (string fileName)
151                 {
152                         using (FileStream fs = File.OpenRead (fileName)) {
153                                 CreateCursor (fs);
154                         }
155                 }
156
157                 public Cursor(Type type, string resource) {
158                         using (Stream s = type.Assembly.GetManifestResourceStream (type, resource)) {
159                                 if (s != null) {
160                                         CreateCursor (s);
161                                         return;
162                                 }
163                         }
164
165                         // Try a different way, previous failed
166                         using (Stream s = Assembly.GetExecutingAssembly ().GetManifestResourceStream (resource)) {
167                                 if (s != null) {
168                                         CreateCursor (s);
169                                         return;
170                                 }
171                         }
172                         throw new FileNotFoundException ("Resource name was not found: `" + resource + "'");
173                 }
174                 #endregion      // Public Constructors
175
176                 #region Public Static Properties
177                 public static Rectangle Clip {
178                         get {
179                                 IntPtr          handle;
180                                 bool            confined;
181                                 Rectangle       rect;
182                                 Size            size;
183
184                                 XplatUI.GrabInfo (out handle, out confined, out rect);
185                                 if (handle != IntPtr.Zero) {
186                                         return rect;
187                                 }
188
189                                 XplatUI.GetDisplaySize (out size);
190                                 rect.X = 0;
191                                 rect.Y = 0;
192                                 rect.Width = size.Width;
193                                 rect.Height = size.Height;
194                                 return rect;
195                         }
196
197                         [MonoTODO ("Stub, does nothing")]
198                         [MonoInternalNote ("First need to add ability to set cursor clip rectangle to XplatUI drivers to implement this property")]
199                         set {
200                                 ;
201                         }
202                 }
203
204                 public static Cursor Current {
205                         get {
206                                 if (current != null) 
207                                         return current;
208                                 return Cursors.Default;
209                         }
210
211                         set {
212                                 if (current == value)
213                                         return;
214                                 
215                                 current = value;
216                                 if (current == null){
217                                         // FIXME - define and set empty cursor
218                                         XplatUI.OverrideCursor(IntPtr.Zero);
219                                 } else
220                                         XplatUI.OverrideCursor(current.handle);
221                         }
222                 }
223
224                 public static Point Position {
225                         get {
226                                 int x;
227                                 int y;
228
229                                 XplatUI.GetCursorPos (IntPtr.Zero, out x, out y);
230                                 return new Point (x, y);
231                         }
232
233                         set {
234                                 XplatUI.SetCursorPos(IntPtr.Zero, value.X, value.Y);
235                         }
236                 }
237                 #endregion      // Public Static Properties
238
239                 #region Public Instance Properties
240                 public IntPtr Handle {
241                         get {
242                                 return handle;
243                         }
244                 }
245
246                 [MonoTODO ("Implemented for Win32, X11 always returns 0,0")]
247                 public Point HotSpot {
248                         get {
249                                 int cursor_w, cursor_h, hot_x, hot_y;
250                                 XplatUI.GetCursorInfo (Handle, out cursor_w, out cursor_h, out hot_x, out hot_y);
251
252                                 return new Point (hot_x, hot_y);
253                         }
254                 }
255
256                 public Size Size {
257                         get {
258                                 return size;
259                         }
260                 }
261                 
262                 [Localizable (false)]
263                 [Bindable (true)]
264                 [TypeConverter (typeof (StringConverter))]
265                 [DefaultValue (null)]
266                 [MWFCategory ("Data")]
267                 public object Tag {
268                         get { return this.tag; }
269                         set { this.tag = value; }
270                 }
271
272                 #endregion      // Public Instance Properties
273
274                 #region Public Static Methods
275                 public static void Hide ()
276                 {
277                         XplatUI.ShowCursor(false);
278                 }
279
280                 public static void Show ()
281                 {
282                         XplatUI.ShowCursor(true);
283                 }
284
285                 public static bool operator != (Cursor left, Cursor right) {
286                         if ((object)left == (object)right)
287                                 return false;
288
289                         if ((object)left == null || (object)right == null) 
290                                 return true;
291
292                         if (left.handle == right.handle) 
293                                 return false;
294                         return true;
295                 }
296
297
298                 public static bool operator ==(Cursor left, Cursor right)
299                 {
300                         if ((object)left == (object)right) 
301                                 return true;
302
303                         if ((object)left == null || (object)right == null)
304                                 return false;
305
306                         if (left.handle == right.handle)
307                                 return true;
308
309                         return false;
310                 }
311                 #endregion      // Public Static Methods
312
313                 #region Public Instance Methods
314                 public IntPtr CopyHandle() {
315                         return handle;
316                 }
317
318                 public void Dispose ()
319                 {
320                         if (cursor != null) {
321                                 cursor.Dispose ();
322                                 cursor = null;
323                         }
324
325                         if (shape != null) {
326                                 shape.Dispose ();
327                                 shape = null;
328                         }
329
330                         if (mask != null) {
331                                 mask.Dispose ();
332                                 mask = null;
333                         }
334
335                         GC.SuppressFinalize (this);
336                 }
337
338                 public void Draw (Graphics g, Rectangle targetRect)
339                 {
340                         if (cursor == null && std_cursor != (StdCursor) (-1)) 
341                                 cursor = XplatUI.DefineStdCursorBitmap (std_cursor);
342
343                         if (cursor != null) {
344                                 // Size of the targetRect is not considered at all
345                                 g.DrawImage (cursor, targetRect.X, targetRect.Y);
346                         }
347                 }
348
349                 public void DrawStretched (Graphics g, Rectangle targetRect)
350                 {
351                         if (cursor == null && std_cursor != (StdCursor)(-1)) 
352                                 cursor = XplatUI.DefineStdCursorBitmap (std_cursor);
353
354                         if (cursor != null) {
355                                 g.DrawImage (cursor, targetRect, new Rectangle(0, 0, cursor.Width, cursor.Height), GraphicsUnit.Pixel);
356                         }
357                 }
358
359                 public override bool Equals (object obj)
360                 {
361                         if (!(obj is Cursor)) 
362                                 return false;
363
364                         if (((Cursor)obj).handle == handle)
365                                 return true;
366
367                         return false;
368                 }
369
370                 public override int GetHashCode()
371                 {
372                         return base.GetHashCode ();
373                 }
374
375                 public override string ToString()
376                 {
377                         if (name != null) {
378                                 return "[Cursor:" + name + "]";
379                         }
380
381                         throw new FormatException("Cannot convert custom cursors to string.");
382                 }
383
384                 void ISerializable.GetObjectData (SerializationInfo si, StreamingContext context)
385                 {
386                         MemoryStream    ms;
387                         BinaryWriter    wr;
388                         CursorImage     ci;
389
390                         ms = new MemoryStream ();
391                         wr = new BinaryWriter (ms);
392                         ci = cursor_data [this.id];
393
394                         // Build the headers, first the CursorDir
395                         wr.Write ((ushort) 0);  // Reserved
396                         wr.Write ((ushort) 2);  // Resource type
397                         wr.Write ((ushort) 1);  // Count
398
399                         // Next the CursorEntry
400                         wr.Write ((byte)cursor_dir.idEntries [this.id].width);
401                         wr.Write ((byte)cursor_dir.idEntries [this.id].height);
402                         wr.Write ((byte)cursor_dir.idEntries [this.id].colorCount);
403                         wr.Write ((byte)cursor_dir.idEntries [this.id].reserved);
404                         wr.Write ((ushort)cursor_dir.idEntries [this.id].xHotspot);
405                         wr.Write ((ushort)cursor_dir.idEntries [this.id].yHotspot);
406                         wr.Write ((uint)(40 + (ci.cursorColors.Length * 4) + ci.cursorXOR.Length + ci.cursorAND.Length));
407                         wr.Write ((uint)(6 + 16));      // CursorDir + CursorEntry size
408
409                         // Then the CursorInfoHeader
410                         wr.Write (ci.cursorHeader.biSize);
411                         wr.Write (ci.cursorHeader.biWidth);
412                         wr.Write (ci.cursorHeader.biHeight);
413                         wr.Write (ci.cursorHeader.biPlanes);
414                         wr.Write (ci.cursorHeader.biBitCount);
415                         wr.Write (ci.cursorHeader.biCompression);
416                         wr.Write (ci.cursorHeader.biSizeImage);
417                         wr.Write (ci.cursorHeader.biXPelsPerMeter);
418                         wr.Write (ci.cursorHeader.biYPelsPerMeter);
419                         wr.Write (ci.cursorHeader.biClrUsed);
420                         wr.Write (ci.cursorHeader.biClrImportant);
421                         
422                         for (int i = 0; i < ci.cursorColors.Length; i++) 
423                                 wr.Write(ci.cursorColors[i]);
424
425                         wr.Write (ci.cursorXOR);
426                         wr.Write (ci.cursorAND);
427                         wr.Flush ();
428
429                         si.AddValue ("CursorData", ms.ToArray ());
430                 }
431                 #endregion      // Public Instance Methods
432
433                 #region Private Methods
434                 private void InitFromStream (Stream stream)
435                 {
436                         ushort          entry_count;
437                         CursorEntry     ce;
438                         uint            largest;
439
440                         //read the cursor header
441                         if (stream == null || stream.Length == 0) 
442                                 throw new ArgumentException ("The argument 'stream' must be a picture that can be used as a cursor", "stream");
443                         
444                         BinaryReader reader = new BinaryReader (stream);
445             
446                         cursor_dir = new CursorDir ();
447                         cursor_dir.idReserved = reader.ReadUInt16();
448                         cursor_dir.idType = reader.ReadUInt16();
449                         if (cursor_dir.idReserved != 0 || !(cursor_dir.idType == 2 || cursor_dir.idType == 1))
450                                 throw new ArgumentException ("Invalid Argument, format error", "stream");
451
452                         entry_count = reader.ReadUInt16();
453                         cursor_dir.idCount = entry_count;
454                         cursor_dir.idEntries = new CursorEntry[entry_count];
455                         cursor_data = new CursorImage[entry_count];
456
457                         //now read in the CursorEntry structures
458                         for (int i=0; i < entry_count; i++){
459                                 ce = new CursorEntry();
460
461                                 ce.width = reader.ReadByte();
462                                 ce.height = reader.ReadByte();
463                                 ce.colorCount = reader.ReadByte();
464                                 ce.reserved = reader.ReadByte();
465                                 ce.xHotspot = reader.ReadUInt16();
466                                 ce.yHotspot = reader.ReadUInt16();
467                                 if (cursor_dir.idType == 1) {
468                                         ce.xHotspot = (ushort)(ce.width / 2);
469                                         ce.yHotspot = (ushort)(ce.height / 2);
470                                 }
471                                 ce.sizeInBytes = reader.ReadUInt32();
472                                 ce.fileOffset = reader.ReadUInt32();
473
474                                 cursor_dir.idEntries[i] = ce;
475                         }
476
477                         // If we have more than one pick the largest cursor
478                         largest = 0;
479                         for (int j = 0; j < entry_count; j++){
480                                 if (cursor_dir.idEntries[j].sizeInBytes >= largest)     {
481                                         largest = cursor_dir.idEntries[j].sizeInBytes;
482                                         this.id = (ushort)j;
483                                         this.size.Height = cursor_dir.idEntries[j].height;
484                                         this.size.Width = cursor_dir.idEntries[j].width;
485                                 }
486                         }
487
488                         //now read in the cursor data
489                         for (int j = 0; j < entry_count; j++) {
490                                 CursorImage             curdata;
491                                 CursorInfoHeader        cih;
492                                 byte[]                  buffer;
493                                 BinaryReader            cih_reader;
494                                 int                     num_colors;
495                                 int                     cursor_height;
496                                 int                     bytes_per_line;
497                                 int                     xor_size;
498                                 int                     and_size;
499
500                                 curdata = new CursorImage();
501                                 cih = new CursorInfoHeader();
502                                 
503                                 stream.Seek (cursor_dir.idEntries[j].fileOffset, SeekOrigin.Begin);
504                                 buffer = new byte [cursor_dir.idEntries[j].sizeInBytes];
505                                 stream.Read (buffer, 0, buffer.Length);
506
507                                 cih_reader = new BinaryReader(new MemoryStream(buffer));
508
509                                 cih.biSize = cih_reader.ReadUInt32 ();
510                                 if (cih.biSize != 40) {
511                                         throw new ArgumentException ("Invalid cursor file", "stream");
512                                 }
513                                 cih.biWidth = cih_reader.ReadInt32 ();
514                                 cih.biHeight = cih_reader.ReadInt32 ();
515                                 cih.biPlanes = cih_reader.ReadUInt16 ();
516                                 cih.biBitCount = cih_reader.ReadUInt16 ();
517                                 cih.biCompression = cih_reader.ReadUInt32 ();
518                                 cih.biSizeImage = cih_reader.ReadUInt32 ();
519                                 cih.biXPelsPerMeter = cih_reader.ReadInt32 ();
520                                 cih.biYPelsPerMeter = cih_reader.ReadInt32 ();
521                                 cih.biClrUsed = cih_reader.ReadUInt32 ();
522                                 cih.biClrImportant = cih_reader.ReadUInt32 ();
523
524                                 curdata.cursorHeader = cih;
525
526                                 //Read the number of colors used and corresponding memory occupied by
527                                 //color table. Fill this memory chunk into rgbquad[]
528                                 switch (cih.biBitCount){
529                                         case 1: num_colors = 2; break;
530                                         case 4: num_colors = 16; break;
531                                         case 8: num_colors = 256; break;
532                                         default: num_colors = 0; break;
533                                 }
534                                 
535                                 curdata.cursorColors = new uint[num_colors];
536                                 for (int i = 0; i < num_colors; i++) {
537                                         curdata.cursorColors[i] = cih_reader.ReadUInt32 ();
538                                 }
539
540                                 //XOR mask is immediately after ColorTable and its size is 
541                                 //icon height* no. of bytes per line
542                                 
543                                 //cursor height is half of BITMAPINFOHEADER.biHeight, since it contains
544                                 //both XOR as well as AND mask bytes
545                                 cursor_height = cih.biHeight/2;
546                                 
547                                 //bytes per line should should be uint aligned
548                                 bytes_per_line = ((((cih.biWidth * cih.biPlanes * cih.biBitCount)+ 31)>>5)<<2);
549                                 
550                                 //Determine the XOR array Size
551                                 xor_size = bytes_per_line * cursor_height;
552                                 curdata.cursorXOR = new byte[xor_size];
553                                 for (int i = 0; i < xor_size; i++) {
554                                         curdata.cursorXOR[i] = cih_reader.ReadByte();
555                                 }
556                                 
557                                 //Determine the AND array size
558                                 and_size = (int)(cih_reader.BaseStream.Length - cih_reader.BaseStream.Position);
559                                 curdata.cursorAND = new byte[and_size];
560                                 for (int i = 0; i < and_size; i++) {
561                                         curdata.cursorAND[i] = cih_reader.ReadByte();
562                                 }
563                                 
564                                 cursor_data[j] = curdata;
565                                 cih_reader.Close();
566                         }                       
567
568                         reader.Close();
569                 }
570
571                 private Bitmap ToBitmap(bool xor, bool transparent)
572                 {
573                         CursorImage             ci;
574                         CursorInfoHeader        cih;
575                         int                     ncolors;
576                         Bitmap                  bmp;
577                         BitmapData              bits;
578                         ColorPalette            pal;
579                         int                     biHeight;
580                         int                     bytesPerLine;
581
582                         if (cursor_data == null)
583                                 return new Bitmap(32, 32);
584
585                         ci = cursor_data[this.id];
586                         cih = ci.cursorHeader;
587                         biHeight = cih.biHeight / 2;
588
589                         if (!xor) {
590                                 // The AND mask is 1bit - very straightforward
591                                 bmp = new Bitmap(cih.biWidth, biHeight, PixelFormat.Format1bppIndexed);
592                                 pal = bmp.Palette;
593                                 pal.Entries[0] = Color.FromArgb(0, 0, 0);
594                                 pal.Entries[1] = Color.FromArgb(unchecked((int)0xffffffffff));
595                                 bits = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
596
597                                 for (int y = 0; y < biHeight; y++) {
598                                         Marshal.Copy(ci.cursorAND, bits.Stride * y, (IntPtr)(bits.Scan0.ToInt64() + bits.Stride * (biHeight - 1 - y)), bits.Stride);
599                                 }
600
601                                 bmp.UnlockBits(bits);
602                         } else {
603                                 ncolors = (int)cih.biClrUsed;
604                                 if (ncolors == 0) {
605                                         if (cih.biBitCount < 24) {
606                                                 ncolors = (int)(1 << cih.biBitCount);
607                                         }
608                                 }
609
610                                 switch(cih.biBitCount) {
611                                 case 1: {       // Monochrome
612                                         bmp = new Bitmap (cih.biWidth, biHeight, PixelFormat.Format1bppIndexed);
613                                         break;
614                                 }
615                                         
616                                 case 4: {       // 4bpp
617                                         bmp = new Bitmap (cih.biWidth, biHeight, PixelFormat.Format4bppIndexed);
618                                         break;
619                                 }
620                                         
621                                 case 8: {       // 8bpp
622                                         bmp = new Bitmap (cih.biWidth, biHeight, PixelFormat.Format8bppIndexed);
623                                         break;
624                                 }
625                                         
626                                 case 24:
627                                 case 32: {      // 32bpp
628                                         bmp = new Bitmap (cih.biWidth, biHeight, PixelFormat.Format32bppArgb);
629                                         break;
630                                 }
631                                         
632                                 default: 
633                                         throw new Exception("Unexpected number of bits:" + cih.biBitCount.ToString());
634                                 }
635                                 
636                                 if (cih.biBitCount < 24) {
637                                         pal = bmp.Palette;                              // Managed palette
638                                         for (int i = 0; i < ci.cursorColors.Length; i++) 
639                                                 pal.Entries[i] = Color.FromArgb((int)ci.cursorColors[i] | unchecked((int)0xff000000));
640                                         bmp.Palette = pal;
641                                 }
642
643                                 bytesPerLine = (int)((((cih.biWidth * cih.biBitCount) + 31) & ~31) >> 3);
644                                 bits = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
645
646                                 for (int y = 0; y < biHeight; y++) 
647                                         Marshal.Copy(ci.cursorXOR, bytesPerLine * y, (IntPtr)(bits.Scan0.ToInt64() + bits.Stride * (biHeight - 1 - y)), bytesPerLine);
648                                 
649                                 bmp.UnlockBits(bits);
650                         }
651
652                         if (transparent) {
653                                 bmp = new Bitmap(bmp);  // This makes a 32bpp image out of an indexed one
654                                 // Apply the mask to make properly transparent
655                                 for (int y = 0; y < biHeight; y++) {
656                                         for (int x = 0; x < cih.biWidth / 8; x++) {
657                                                 for (int bit = 7; bit >= 0; bit--) {
658                                                         if (((ci.cursorAND[y * cih.biWidth / 8 +x] >> bit) & 1) != 0) 
659                                                                 bmp.SetPixel(x*8 + 7-bit, biHeight - y - 1, Color.Transparent);
660                                                 }
661                                         }
662                                 }
663                         }
664
665                         return bmp;
666                 }
667                 #endregion      // Private Methods
668         }
669 }