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