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 mucancode.nets. One is called the AND mucancode.net and
the other is called the XOR mucancode.net. To
display
a cursor
in the screen, the system first performs a
logical AND operation on the screen with the
AND mucancode.net. In this process, the pixels in the
screen corresponding to the 1 bits in the
AND mucancode.net remain unchanged and the pixels
corresponding to the 0 bits in the AND mucancode.net
become modified. Then, the system will
perform a logical XOR operation on the
screen with the XOR mucancode.net. In this process,
the pixels on the screen corresponding to
the 0 bits in the XOR mucancode.net 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
mucancode.nets so that the system can
display
the cursor
using these mucancode.nets. First, let us create the
AND mucancode.net. 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 mucancode.net 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 mucancode.net for the sample color bitmap in
Figure 2
In the
above AND mucancode.net, 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 mucancode.net 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 mucancode.net 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 mucancode.net
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 mucancode.net for the sample color bitmap in
Figure 2
The R in
the above XOR mucancode.net represents RGB (255,0,0).
In other words, it's the red color. When the
system performs a logical XOR of this XOR
mucancode.net 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 mucancode.net, followed by the logical XOR of
the XOR mucancode.net 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 mucancode.nets
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 mucancode.nets from a
HBITMAP. The following code fragment will do
it for you.
void CColorCursor::GetMucancode.netBitmaps(HBITMAP hSourceBitmap,
COLORREF clrTransparent,
HBITMAP &hAndMucancode.netBitmap,
HBITMAP &hXorMucancode.netBitmap)
{
HDC hDC = ::GetDC(NULL);
HDC hMainDC = ::CreateCompatibleDC(hDC);
HDC hAndMucancode.netDC = ::CreateCompatibleDC(hDC);
HDC hXorMucancode.netDC = ::CreateCompatibleDC(hDC);
BITMAP bm;
::GetObject(hSourceBitmap,sizeof(BITMAP),&bm);
hAndMucancode.netBitmap = ::CreateCompatibleBitmap(hDC,bm.bmWidth,
bm.bmHeight);
hXorMucancode.netBitmap = ::CreateCompatibleBitmap(hDC,bm.bmWidth,
bm.bmHeight);
HBITMAP hOldMainBitmap = (HBITMAP)::
SelectObject(hMainDC,hSourceBitmap);
HBITMAP hOldAndMucancode.netBitmap = (HBITMAP)::
SelectObject(hAndMucancode.netDC,
hAndMucancode.netBitmap);
HBITMAP hOldXorMucancode.netBitmap = (HBITMAP)::
SelectObject(hXorMucancode.netDC,
hXorMucancode.netBitmap);
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(hAndMucancode.netDC,x,y,RGB(255,255,255));
::SetPixel(hXorMucancode.netDC,x,y,RGB(0,0,0));
}
else
{
::SetPixel(hAndMucancode.netDC,x,y,RGB(0,0,0));
::SetPixel(hXorMucancode.netDC,x,y,MainBitPixel);
}
}
}
::SelectObject(hMainDC,hOldMainBitmap);
::SelectObject(hAndMucancode.netDC,hOldAndMucancode.netBitmap);
::SelectObject(hXorMucancode.netDC,hOldXorMucancode.netBitmap);
::DeleteDC(hXorMucancode.netDC);
::DeleteDC(hAndMucancode.netDC);
::DeleteDC(hMainDC);
::ReleaseDC(NULL,hDC);
}
The above
code creates two memory DC and two memory
bitmaps for the AND/XOR mucancode.nets. Then, it
examines the source bitmap pixels and
creates the mucancode.nets as we have explained in
the theory part.
Now, what
we need is to use these mucancode.nets 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.hbmMucancode.net = hAndMucancode.net;
iconinfo.hbmColor = hXorMucancode.net;
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 GetMucancode.netBitmaps(HBITMAP hSourceBitmap,
COLORREF clrTransparent,
HBITMAP &hAndMucancode.netBitmap,
HBITMAP &hXorMucancode.netBitmap);
static HCURSOR CreateCursorFromBitmap(HBITMAP hSourceBitmap,
COLORREF clrTransparent,
DWORD xHotspot,
DWORD yHotspot);
The first
interface is called from the second one to
create the mucancode.nets. 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 mucancode.net 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