Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'mcs/class/Managed.Windows.Forms/System.Windows.Forms/Cursor.cs')
-rw-r--r--mcs/class/Managed.Windows.Forms/System.Windows.Forms/Cursor.cs729
1 files changed, 729 insertions, 0 deletions
diff --git a/mcs/class/Managed.Windows.Forms/System.Windows.Forms/Cursor.cs b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/Cursor.cs
new file mode 100644
index 00000000000..08efb1401c6
--- /dev/null
+++ b/mcs/class/Managed.Windows.Forms/System.Windows.Forms/Cursor.cs
@@ -0,0 +1,729 @@
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Copyright (c) 2004 Novell, Inc.
+//
+// Authors:
+// Peter Bartok pbartok@novell.com
+//
+
+
+// NOT COMPLETE
+
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace System.Windows.Forms {
+ [Editor("System.Drawing.Design.CursorEditor, " + Consts.AssemblySystem_Drawing_Design, typeof(System.Drawing.Design.UITypeEditor))]
+ [Serializable]
+ [TypeConverter(typeof(CursorConverter))]
+ public sealed class Cursor : IDisposable, ISerializable {
+ #region Internal Structs
+ [StructLayout(LayoutKind.Sequential)]
+ private struct CursorDir {
+ internal ushort idReserved; // Reserved
+ internal ushort idType; // resource type (2 for cursors)
+ internal ushort idCount; // how many cursors
+ internal CursorEntry[] idEntries; // the entries for each cursor
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct CursorEntry {
+ internal byte width; // Width of cursor
+ internal byte height; // Height of cursor
+ internal byte colorCount; // colors in cursor
+ internal byte reserved; // Reserved
+ internal ushort xHotspot; // Hotspot X
+ internal ushort yHotspot; // Hotspot Y
+ internal ushort bitCount; // Bits per pixel
+ internal uint sizeInBytes; // size of (CursorInfoHeader + ANDBitmap + ORBitmap)
+ internal uint fileOffset; // position in file
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct CursorInfoHeader {
+ internal uint biSize;
+ internal int biWidth;
+ internal int biHeight;
+ internal ushort biPlanes;
+ internal ushort biBitCount;
+ internal uint biCompression;
+ internal uint biSizeImage;
+ internal int biXPelsPerMeter;
+ internal int biYPelsPerMeter;
+ internal uint biClrUsed;
+ internal uint biClrImportant;
+ };
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct CursorImage {
+ internal CursorInfoHeader cursorHeader; // image header
+ internal uint[] cursorColors; // colors table
+ internal byte[] cursorXOR; // bits for XOR mask
+ internal byte[] cursorAND; // bits for AND mask
+ };
+ #endregion // Internal structs
+
+ #region Local Variables
+ private static Cursor current;
+ private CursorDir cursor_dir;
+ private CursorImage[] cursor_data;
+ private int id;
+
+ internal IntPtr handle;
+ private Size size;
+ private Bitmap shape;
+ private Bitmap mask;
+ private Bitmap cursor;
+ internal string name;
+ #endregion // Local Variables
+
+ #region Public Constructors
+ private void CreateCursor(System.IO.Stream stream) {
+ InitFromStream(stream);
+ this.shape = ToBitmap(true, false);
+ this.mask = ToBitmap(false, false);
+ 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);
+ this.shape.Dispose();
+ this.shape = null;
+ this.mask.Dispose();
+ this.mask = null;
+
+ if (handle != IntPtr.Zero) {
+ this.cursor = ToBitmap(true, true);
+ }
+ }
+
+ private Cursor(SerializationInfo info, StreamingContext context) {
+ }
+
+ private Cursor() {
+ }
+
+ ~Cursor() {
+ Dispose();
+ }
+
+ // This is supposed to take a Win32 handle
+ public Cursor(IntPtr handle) {
+ this.handle = handle;
+ }
+
+ public Cursor(System.IO.Stream stream) {
+ CreateCursor(stream);
+ }
+
+ public Cursor(string fileName) : this (new FileStream (fileName, FileMode.Open)) {
+ }
+
+ public Cursor(Type type, string resource) {
+ using (Stream s = type.Assembly.GetManifestResourceStream (type, resource)) {
+ if (s == null) {
+ throw new FileNotFoundException ("Resource name was not found: `" + resource + "'");
+ }
+ CreateCursor(s);
+ }
+ }
+ #endregion // Public Constructors
+
+ #region Public Static Properties
+ public static Rectangle Clip {
+ get {
+ IntPtr handle;
+ bool confined;
+ Rectangle rect;
+ Size size;
+
+ XplatUI.GrabInfo(out handle, out confined, out rect);
+ if (handle != IntPtr.Zero) {
+ return rect;
+ }
+
+ XplatUI.GetDisplaySize(out size);
+ rect.X = 0;
+ rect.Y = 0;
+ rect.Width = size.Width;
+ rect.Height = size.Height;
+ return rect;
+ }
+
+ [MonoTODO("First need to add ability to set cursor clip rectangle to XplatUI drivers to implement this property")]
+ set {
+ ;
+ }
+ }
+
+ [MonoTODO("Implement setting a null cursor, and add XplatUI method to get current cursor")]
+ public static Cursor Current {
+ get {
+ if (current != null) {
+ return current;
+ }
+ return Cursors.Default;
+ }
+
+ set {
+ if (current != value) {
+ current = value;
+ if (value != null){
+ // FIXME - define and set empty cursor
+ current = null;
+ XplatUI.OverrideCursor(IntPtr.Zero);
+ } else
+ XplatUI.OverrideCursor(current.handle);
+ }
+ }
+ }
+
+ public static Point Position {
+ get {
+ int x;
+ int y;
+
+ XplatUI.GetCursorPos (IntPtr.Zero, out x, out y);
+ return new Point (x, y);
+ }
+
+ set {
+ XplatUI.SetCursorPos(IntPtr.Zero, value.X, value.Y);
+ }
+ }
+ #endregion // Public Static Properties
+
+ #region Public Instance Properties
+ public IntPtr Handle {
+ get {
+ return handle;
+ }
+ }
+
+ public Size Size {
+ get {
+ return size;
+ }
+ }
+ #endregion // Public Instance Properties
+
+ #region Public Static Methods
+ public static void Hide() {
+ XplatUI.ShowCursor(false);
+ }
+
+ public static void Show() {
+ XplatUI.ShowCursor(false);
+ }
+
+ public static bool operator !=(Cursor left, Cursor right) {
+ if ((object)left == (object)right) {
+ return false;
+ }
+
+ if ((object)left == null || (object)right == null) {
+ return true;
+ }
+
+ if (left.handle == right.handle) {
+ return false;
+ }
+ return true;
+ }
+
+
+ public static bool operator ==(Cursor left, Cursor right) {
+ if ((object)left == (object)right) {
+ return true;
+ }
+
+ if ((object)left == null || (object)right == null) {
+ return false;
+ }
+
+ if (left.handle == right.handle) {
+ return true;
+ }
+ return false;
+ }
+ #endregion // Public Static Methods
+
+ #region Public Instance Methods
+ public IntPtr CopyHandle() {
+ return handle;
+ }
+
+ public void Dispose() {
+ if (this.cursor != null) {
+ this.cursor.Dispose();
+ this.cursor = null;
+ }
+
+ if (this.shape != null) {
+ this.shape.Dispose();
+ this.shape = null;
+ }
+
+ if (this.mask != null) {
+ this.mask.Dispose();
+ this.mask = null;
+ }
+
+ GC.SuppressFinalize (this);
+ }
+
+ public void Draw(Graphics g, Rectangle targetRect) {
+ if (this.cursor != null) {
+ g.DrawImage(this.cursor, targetRect);
+ }
+ }
+
+ public void DrawStretched(Graphics g, Rectangle targetRect) {
+ if (this.cursor != null) {
+ g.DrawImage(this.cursor, targetRect, new Rectangle(0, 0, this.cursor.Width, this.cursor.Height), GraphicsUnit.Pixel);
+ }
+ }
+
+ public override bool Equals(object obj) {
+ if ( !(obj is Cursor)) {
+ return false;
+ }
+
+ if (((Cursor)obj).handle == this.handle) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public override int GetHashCode() {
+ return base.GetHashCode ();
+ }
+
+ public override string ToString() {
+ if (name != null) {
+ return "[Cursor:" + name + "]";
+ }
+
+ throw new FormatException("Cannot convert custom cursors to string.");
+ }
+
+ void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) {
+ MemoryStream ms;
+ BinaryWriter wr;
+ CursorImage ci;
+
+ ms = new MemoryStream();
+ wr = new BinaryWriter(ms);
+ ci = cursor_data[this.id];
+
+ // Build the headers, first the CursorDir
+ wr.Write((ushort)0); // Reserved
+ wr.Write((ushort)2); // Resource type
+ wr.Write((ushort)1); // Count
+
+ // Next the CursorEntry
+ wr.Write((byte)cursor_dir.idEntries[this.id].width);
+ wr.Write((byte)cursor_dir.idEntries[this.id].height);
+ wr.Write((byte)cursor_dir.idEntries[this.id].colorCount);
+ wr.Write((byte)cursor_dir.idEntries[this.id].reserved);
+ wr.Write((ushort)cursor_dir.idEntries[this.id].xHotspot);
+ wr.Write((ushort)cursor_dir.idEntries[this.id].yHotspot);
+ wr.Write((uint)(40 + (ci.cursorColors.Length * 4) + ci.cursorXOR.Length + ci.cursorAND.Length));
+ wr.Write((uint)(6 + 16)); // CursorDir + CursorEntry size
+
+ // Then the CursorInfoHeader
+ wr.Write(ci.cursorHeader.biSize);
+ wr.Write(ci.cursorHeader.biWidth);
+ wr.Write(ci.cursorHeader.biHeight);
+ wr.Write(ci.cursorHeader.biPlanes);
+ wr.Write(ci.cursorHeader.biBitCount);
+ wr.Write(ci.cursorHeader.biCompression);
+ wr.Write(ci.cursorHeader.biSizeImage);
+ wr.Write(ci.cursorHeader.biXPelsPerMeter);
+ wr.Write(ci.cursorHeader.biYPelsPerMeter);
+ wr.Write(ci.cursorHeader.biClrUsed);
+ wr.Write(ci.cursorHeader.biClrImportant);
+ for (int i = 0; i < ci.cursorColors.Length; i++) {
+ wr.Write(ci.cursorColors[i]);
+ }
+ wr.Write(ci.cursorXOR);
+ wr.Write(ci.cursorAND);
+ wr.Flush();
+
+ si.AddValue ("CursorData", ms.ToArray());
+ }
+ #endregion // Public Instance Methods
+
+ #region Private Methods w
+ private void InitFromStream(Stream stream) {
+ ushort entry_count;
+ CursorEntry ce;
+ uint largest;
+
+ //read the cursor header
+ if (stream == null || stream.Length == 0) {
+ throw new System.ArgumentException ("The argument 'stream' must be a picture that can be used as a cursor", "stream");
+ }
+
+ BinaryReader reader = new BinaryReader (stream);
+
+ cursor_dir = new CursorDir ();
+ cursor_dir.idReserved = reader.ReadUInt16();
+ cursor_dir.idType = reader.ReadUInt16();
+ if (cursor_dir.idReserved != 0 || !(cursor_dir.idType == 2 || cursor_dir.idType == 1))
+ throw new System.ArgumentException ("Invalid Argument, format error", "stream");
+
+ entry_count = reader.ReadUInt16();
+ cursor_dir.idCount = entry_count;
+ cursor_dir.idEntries = new CursorEntry[entry_count];
+ cursor_data = new CursorImage[entry_count];
+
+ //now read in the CursorEntry structures
+ for (int i=0; i < entry_count; i++){
+ ce = new CursorEntry();
+
+ ce.width = reader.ReadByte();
+ ce.height = reader.ReadByte();
+ ce.colorCount = reader.ReadByte();
+ ce.reserved = reader.ReadByte();
+ ce.xHotspot = reader.ReadUInt16();
+ ce.yHotspot = reader.ReadUInt16();
+ ce.sizeInBytes = reader.ReadUInt32();
+ ce.fileOffset = reader.ReadUInt32();
+
+ cursor_dir.idEntries[i] = ce;
+ }
+
+ // If we have more than one pick the largest cursor
+ largest = 0;
+ for (int j=0; j < entry_count; j++){
+ if (cursor_dir.idEntries[j].sizeInBytes >= largest) {
+ largest = cursor_dir.idEntries[j].sizeInBytes;
+ this.id = (ushort)j;
+ this.size.Height = cursor_dir.idEntries[j].height;
+ this.size.Width = cursor_dir.idEntries[j].width;
+ }
+ }
+
+ //now read in the cursor data
+ for (int j = 0; j < entry_count; j++) {
+ CursorImage curdata;
+ CursorInfoHeader cih;
+ byte[] buffer;
+ BinaryReader cih_reader;
+ int num_colors;
+ int cursor_height;
+ int bytes_per_line;
+ int xor_size;
+ int and_size;
+
+ curdata = new CursorImage();
+ cih = new CursorInfoHeader();
+
+ stream.Seek (cursor_dir.idEntries[j].fileOffset, SeekOrigin.Begin);
+ buffer = new byte [cursor_dir.idEntries[j].sizeInBytes];
+ stream.Read (buffer, 0, buffer.Length);
+
+ cih_reader = new BinaryReader(new MemoryStream(buffer));
+
+ cih.biSize = cih_reader.ReadUInt32 ();
+ if (cih.biSize != 40) {
+ throw new System.ArgumentException ("Invalid cursor file", "stream");
+ }
+ cih.biWidth = cih_reader.ReadInt32 ();
+ cih.biHeight = cih_reader.ReadInt32 ();
+ cih.biPlanes = cih_reader.ReadUInt16 ();
+ cih.biBitCount = cih_reader.ReadUInt16 ();
+ cih.biCompression = cih_reader.ReadUInt32 ();
+ cih.biSizeImage = cih_reader.ReadUInt32 ();
+ cih.biXPelsPerMeter = cih_reader.ReadInt32 ();
+ cih.biYPelsPerMeter = cih_reader.ReadInt32 ();
+ cih.biClrUsed = cih_reader.ReadUInt32 ();
+ cih.biClrImportant = cih_reader.ReadUInt32 ();
+
+ curdata.cursorHeader = cih;
+
+ //Read the number of colors used and corresponding memory occupied by
+ //color table. Fill this memory chunk into rgbquad[]
+ switch (cih.biBitCount){
+ case 1: num_colors = 2; break;
+ case 4: num_colors = 16; break;
+ case 8: num_colors = 256; break;
+ default: num_colors = 0; break;
+ }
+
+ curdata.cursorColors = new uint[num_colors];
+ for (int i = 0; i < num_colors; i++) {
+ curdata.cursorColors[i] = cih_reader.ReadUInt32 ();
+ }
+
+ //XOR mask is immediately after ColorTable and its size is
+ //icon height* no. of bytes per line
+
+ //cursor height is half of BITMAPINFOHEADER.biHeight, since it contains
+ //both XOR as well as AND mask bytes
+ cursor_height = cih.biHeight/2;
+
+ //bytes per line should should be uint aligned
+ bytes_per_line = ((((cih.biWidth * cih.biPlanes * cih.biBitCount)+ 31)>>5)<<2);
+
+ //Determine the XOR array Size
+ xor_size = bytes_per_line * cursor_height;
+ curdata.cursorXOR = new byte[xor_size];
+ for (int i = 0; i < xor_size; i++) {
+ curdata.cursorXOR[i] = cih_reader.ReadByte();
+ }
+
+ //Determine the AND array size
+ and_size = (int)(cih_reader.BaseStream.Length - cih_reader.BaseStream.Position);
+ curdata.cursorAND = new byte[and_size];
+ for (int i = 0; i < and_size; i++) {
+ curdata.cursorAND[i] = cih_reader.ReadByte();
+ }
+
+ cursor_data[j] = curdata;
+ cih_reader.Close();
+ }
+
+ reader.Close();
+ }
+
+ private Bitmap ToBitmapOld(bool xor, bool transparent) {
+ Bitmap bmp;
+
+ if (cursor_data != null) {
+ MemoryStream stream;
+ BinaryWriter writer;
+ CursorImage ci;
+ uint offset;
+ uint filesize;
+ ushort reserved12;
+ CursorInfoHeader cih;
+ int color_count;
+
+ stream = new MemoryStream();
+ writer = new BinaryWriter (stream);
+
+ ci = cursor_data[this.id];
+
+ try {
+ // write bitmap file header
+ writer.Write ('B');
+ writer.Write ('M');
+
+ // write the file size
+ // file size = bitmapfileheader + bitmapinfo + colorpalette + image bits
+ // sizeof bitmapfileheader = 14 bytes
+ // sizeof bitmapinfo = 40 bytes
+ if (xor) {
+ offset = (uint)(14 + 40 + ci.cursorColors.Length * 4);
+ filesize = (uint)(offset + ci.cursorXOR.Length);
+ } else {
+ offset = (uint)(14 + 40 + 8); // AND mask is always monochrome
+ filesize = (uint)(offset + ci.cursorAND.Length);
+ }
+ writer.Write(filesize);
+
+ // write reserved words
+ reserved12 = 0;
+ writer.Write(reserved12);
+ writer.Write(reserved12);
+
+ // write offset
+ writer.Write (offset);
+
+ // write bitmapfile header
+ cih = ci.cursorHeader;
+ writer.Write(cih.biSize);
+ writer.Write(cih.biWidth);
+ writer.Write(cih.biHeight/2);
+ writer.Write(cih.biPlanes);
+ if (xor) {
+ writer.Write(cih.biBitCount);
+ } else {
+ writer.Write((ushort)1);
+ }
+ writer.Write(cih.biCompression);
+ if (xor) {
+ writer.Write(ci.cursorXOR.Length);
+ } else {
+ writer.Write(ci.cursorAND.Length);
+ }
+ writer.Write(cih.biXPelsPerMeter);
+ writer.Write(cih.biYPelsPerMeter);
+
+ // write color table
+ if (xor) {
+ writer.Write(cih.biClrUsed);
+ writer.Write(cih.biClrImportant);
+ color_count = ci.cursorColors.Length;
+ for (int j = 0; j < color_count; j++) {
+ writer.Write (ci.cursorColors[j]);
+ }
+ } else {
+ // 2 used colors
+ writer.Write(2);
+ // 2 important colors
+ writer.Write(2);
+
+ writer.Write((uint)0x00000000);
+ writer.Write((uint)0x00ffffff);
+ }
+
+ // write image bits
+ if (xor)
+ writer.Write(ci.cursorXOR);
+ else
+ writer.Write(ci.cursorAND);
+
+ writer.Flush();
+
+ bmp = new Bitmap(stream);
+
+ if (transparent) {
+ bmp = new Bitmap(bmp); // This makes a 32bpp image out of an indexed one
+ // Apply the mask to make properly transparent
+ for (int y = 0; y < cih.biHeight/2; y++) {
+ for (int x = 0; x < cih.biWidth / 8; x++) {
+ for (int bit = 7; bit >= 0; bit--) {
+ if (((ci.cursorAND[y * cih.biWidth / 8 +x] >> bit) & 1) != 0) {
+ bmp.SetPixel(x*8 + 7-bit, cih.biHeight/2 - y - 1, Color.Transparent);
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw e;
+ } finally {
+ writer.Close();
+ }
+ } else {
+ bmp = new Bitmap (32, 32);
+ }
+
+ return bmp;
+ }
+
+ private Bitmap ToBitmap(bool xor, bool transparent) {
+ CursorImage ci;
+ CursorInfoHeader cih;
+ int ncolors;
+ Bitmap bmp;
+ BitmapData bits;
+ ColorPalette pal;
+ int biHeight;
+ int bytesPerLine;
+
+ if (cursor_data == null) {
+ return new Bitmap(32, 32);
+ }
+
+ ci = cursor_data[this.id];
+ cih = ci.cursorHeader;
+ biHeight = cih.biHeight / 2;
+
+ if (!xor) {
+ // The AND mask is 1bit - very straightforward
+ bmp = new Bitmap(cih.biWidth, biHeight, PixelFormat.Format1bppIndexed);
+ pal = bmp.Palette;
+ pal.Entries[0] = Color.FromArgb(0, 0, 0);
+ pal.Entries[1] = Color.FromArgb(unchecked((int)0xffffffffff));
+ bits = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
+
+ for (int y = 0; y < biHeight; y++) {
+ Marshal.Copy(ci.cursorAND, bits.Stride * y, (IntPtr)((int)bits.Scan0 + bits.Stride * (biHeight - 1 - y)), bits.Stride);
+ }
+
+ bmp.UnlockBits(bits);
+ } else {
+ ncolors = (int)cih.biClrUsed;
+ if (ncolors == 0) {
+ if (cih.biBitCount < 24) {
+ ncolors = (int)(1 << cih.biBitCount);
+ }
+ }
+
+ switch(cih.biBitCount) {
+ case 1: { // Monochrome
+ bmp = new Bitmap(cih.biWidth, biHeight, PixelFormat.Format1bppIndexed);
+ break;
+ }
+
+ case 4: { // 4bpp
+ bmp = new Bitmap(cih.biWidth, biHeight, PixelFormat.Format4bppIndexed);
+ break;
+ }
+
+ case 8: { // 8bpp
+ bmp = new Bitmap(cih.biWidth, biHeight, PixelFormat.Format8bppIndexed);
+ break;
+ }
+
+ case 24:
+ case 32: { // 32bpp
+ bmp = new Bitmap(cih.biWidth, biHeight, PixelFormat.Format32bppArgb);
+ break;
+ }
+
+ default: {
+ throw new Exception("Unexpected number of bits:" + cih.biBitCount.ToString());
+ }
+ }
+
+ if (cih.biBitCount < 24) {
+ pal = bmp.Palette; // Managed palette
+
+ for (int i = 0; i < ci.cursorColors.Length; i++) {
+ pal.Entries[i] = Color.FromArgb((int)ci.cursorColors[i] & unchecked((int)0xff000000));
+ }
+ }
+
+ bytesPerLine = (int)((((cih.biWidth * cih.biBitCount) + 31) & ~31) >> 3);
+ bits = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
+
+ for (int y = 0; y < biHeight; y++) {
+ Marshal.Copy(ci.cursorXOR, bytesPerLine * y, (IntPtr)((int)bits.Scan0 + bits.Stride * (biHeight - 1 - y)), bytesPerLine);
+ }
+
+ bmp.UnlockBits(bits);
+ }
+
+ if (transparent) {
+ bmp = new Bitmap(bmp); // This makes a 32bpp image out of an indexed one
+ // Apply the mask to make properly transparent
+ for (int y = 0; y < biHeight; y++) {
+ for (int x = 0; x < cih.biWidth / 8; x++) {
+ for (int bit = 7; bit >= 0; bit--) {
+ if (((ci.cursorAND[y * cih.biWidth / 8 +x] >> bit) & 1) != 0) {
+ bmp.SetPixel(x*8 + 7-bit, biHeight - y - 1, Color.Transparent);
+ }
+ }
+ }
+ }
+ }
+
+ return bmp;
+ }
+ #endregion // Private Methods
+ }
+}