
Figure 1:
Transition of a color
bitmap to a
cursor
Environment: Win32
This
article
focuses on creating
a color cursor from a
HBITMAP.
First, it explains the steps that Windows
performs to display
a cursor on the screen and how
can we create
the necessary information that Windows needs
to create
our cursor.
Then, it explains the steps needed to
convert a color
HBITMAP to an
HCURSOR.
Finally, it shows a utility class that
converts HBITMAP
to HCURSOR.
How Does
Windows Display a
Cursor?
In Windows,
transparency of the
cursor is achieved by the use
of two masks. One is called the AND mask and
the other is called the XOR mask. To
display
a cursor
in the screen, the system first performs a
logical AND operation on the screen with the
AND mask. In this process, the pixels in the
screen corresponding to the 1 bits in the
AND mask remain unchanged and the pixels
corresponding to the 0 bits in the AND mask
become modified. Then, the system will
perform a logical XOR operation on the
screen with the XOR mask. In this process,
the pixels on the screen corresponding to
the 0 bits in the XOR mask remain unchanged
and the pixels corresponding to the non-0
bits get modified.

Figure 2: A
sample color bitmap to be
converted
as a cursor
Now, let's
try to realize the above cursor to its AND/XOR
masks so that the system can
display
the cursor
using these masks. First, let us create the
AND mask. The above
cursor contains a red colored
rectangle in the center. So, all the other
pixels should be transparent. Assuming that
the size of the cursor is 8*8 and the size
of the rectangle is 4*4, we shall define the
AND mask as shown below.
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 0 0 0 0 1 1
1 1 0 0 0 0 1 1
1 1 0 0 0 0 1 1
1 1 0 0 0 0 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
Figure 3:
AND mask for the sample color bitmap in
Figure 2
In the
above AND mask, the bits corresponding to
the red rectangle are 0 and the rest of the
bits are 1. This is because we need only the
red rectangle to be displayed as the cursor
and the rest of the area should be
transparent. When the system performs a
logical AND operation of this mask to the
screen, the pixels in the screen
corresponding to the red rectangle become
modified and the rest remain unchanged.
Now, let us
create the XOR mask for our
cursor.
Because we need to display the red rectangle
as a cursor
on the screen and the rest as transparent,
we need to make the bits in the XOR mask
correspond to the red rectangle as Red (RGB
(255,0,0)) and the rest as 0.
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 R R R R 0 0
0 0 R R R R 0 0
0 0 R R R R 0 0
0 0 R R R R 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
Figure 4:
XOR mask for the sample color bitmap in
Figure 2
The R in
the above XOR mask represents RGB (255,0,0).
In other words, it's the red color. When the
system performs a logical XOR of this XOR
mask to the screen, the R pixels are updated
on the screen and the pixels corresponding
to the 0 bits remain unchanged.
So,
finally, after performing the logical AND of
the AND mask, followed by the logical XOR of
the XOR mask to the screen, the screen under
our cursor region looks like what is shown
below.
S S S S S S S S
S S S S S S S S
S S R R R R S S
S S R R R R S S
S S R R R R S S
S S R R R R S S
S S S S S S S S
S S S S S S S S
Figure 5:
State of the screen area under the cursor
after applying AND and XOR masks
The S
represents the original screen pixels and
the R represents the red color
pixels.
Converting
HBITMAP to HCURSOR
Now, let us
try to create these AND/XOR masks from a
HBITMAP. The following code fragment will do
it for you.
void CColorCursor::GetMaskBitmaps(HBITMAP hSourceBitmap,
COLORREF clrTransparent,
HBITMAP &hAndMaskBitmap,
HBITMAP &hXorMaskBitmap)
{
HDC hDC = ::GetDC(NULL);
HDC hMainDC = ::CreateCompatibleDC(hDC);
HDC hAndMaskDC = ::CreateCompatibleDC(hDC);
HDC hXorMaskDC = ::CreateCompatibleDC(hDC);
BITMAP bm;
::GetObject(hSourceBitmap,sizeof(BITMAP),&bm);
hAndMaskBitmap = ::CreateCompatibleBitmap(hDC,bm.bmWidth,
bm.bmHeight);
hXorMaskBitmap = ::CreateCompatibleBitmap(hDC,bm.bmWidth,
bm.bmHeight);
HBITMAP hOldMainBitmap = (HBITMAP)::
SelectObject(hMainDC,hSourceBitmap);
HBITMAP hOldAndMaskBitmap = (HBITMAP)::
SelectObject(hAndMaskDC,
hAndMaskBitmap);
HBITMAP hOldXorMaskBitmap = (HBITMAP)::
SelectObject(hXorMaskDC,
hXorMaskBitmap);
COLORREF MainBitPixel;
for(int x=0;x<bm.bmWidth;++x)
{
for(int y=0;y<bm.bmHeight;++y)
{
MainBitPixel = ::GetPixel(hMainDC,x,y);
if(MainBitPixel == clrTransparent)
{
::SetPixel(hAndMaskDC,x,y,RGB(255,255,255));
::SetPixel(hXorMaskDC,x,y,RGB(0,0,0));
}
else
{
::SetPixel(hAndMaskDC,x,y,RGB(0,0,0));
::SetPixel(hXorMaskDC,x,y,MainBitPixel);
}
}
}
::SelectObject(hMainDC,hOldMainBitmap);
::SelectObject(hAndMaskDC,hOldAndMaskBitmap);
::SelectObject(hXorMaskDC,hOldXorMaskBitmap);
::DeleteDC(hXorMaskDC);
::DeleteDC(hAndMaskDC);
::DeleteDC(hMainDC);
::ReleaseDC(NULL,hDC);
}
The above
code creates two memory DC and two memory
bitmaps for the AND/XOR masks. Then, it
examines the source bitmap pixels and
creates the masks as we have explained in
the theory part.
Now, what
we need is to use these masks and create a
cursor using the well-known
CreateIconIndirect()
SDK call as shown below.
ICONINFO iconinfo = {0};
iconinfo.fIcon = FALSE;
iconinfo.xHotspot = 0;
iconinfo.yHotspot = 0;
iconinfo.hbmMask = hAndMask;
iconinfo.hbmColor = hXorMask;
HCURSOR hCursor = ::CreateIconIndirect(&iconinfo);
That's it.
We have successfully created a color cursor
from a bitmap.
Using the
Code
It is
always better to create a utility class for
doing these things for us. So, I created one
called CColorCursor. It has the following
interfaces.
static void GetMaskBitmaps(HBITMAP hSourceBitmap,
COLORREF clrTransparent,
HBITMAP &hAndMaskBitmap,
HBITMAP &hXorMaskBitmap);
static HCURSOR CreateCursorFromBitmap(HBITMAP hSourceBitmap,
COLORREF clrTransparent,
DWORD xHotspot,
DWORD yHotspot);
The first
interface is called from the second one to
create the masks. The first one is also made
public because we can use it to get an idea
of what is happening inside. I used the
first interface in my test application to
display the AND/XOR mask as shown in the
first figure and the second one to create a
cursor directly.
Now, we are
approaching towards the end of this
article.
I will finish it by showing the usage of
this utility class.
#include "ColorCursor.h"
....
HBITMAP hSourceBitmap = c.
HCURSOR hCursor = CColorCursor::
CreateCursorFromBitmap(hSourceBitmap,RGB(0,0,0),
0,0);
A Word of
Caution
The utility
class explained above will try to
create
the cursor
in the same size of the input source bitmap.
But in Windows, there are some limitations
for the size of the
cursor. So, it is always safer
to pass bitmaps having a standard size.
Otherwise, the result may be unpredictable.
Downloads
Download demo
project - 37 Kb
Download
source - 2 Kb