Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a color cursor from a bitmap

0.00/5 (No votes)
14 Oct 2003 1  
An article explaining how to convert a color HBITMAP to HCURSOR

Figure 1: Transition of a color bitmap to a cursor

Introduction

This article focuses on creating a color cursor from a HBITMAP. First it explains the steps performed by windows to display a cursor on the screen and how we can create the necessary information needed by windows to create our cursor. Then it explains the steps needed to convert a color HBITMAP to an HCURSOR. Finally it shows a utility class, which converts HBITMAP to HCURSOR.

How Windows displays 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. In order 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 remains unchanged and the pixels corresponding to the 0 bits in the AND mask becomes modified. Then the system will perform a logical XOR operation on the screen with the XOR mask. In this process, the pixels in screen corresponding to the 0 bits in the XOR mask remains unchanged and the pixels corresponding to the non 0 bits gets modified.


Figure 2: A sample color bitmap to be converted as a cursor

Now lets 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 becomes modified and the rest remains unchanged.

Now let us create the XOR mask for our cursor. As we need to display the red rectangle as cursor on the screen and the rest as transparent, we need to make the bits in the XOR mask corresponding 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). I.e., red color. When the system performs logical XOR of this XOR mask to the screen, the R pixels are updated in the screen and the pixels corresponding to the 0 bits remains unchanged.

So finally, after performing logical AND of the AND mask followed by logical XOR of the XOR mask to the screen, the screen under our cursor region look like as 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

Where 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 an 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); 

  //Get the dimensions of the source bitmap

  BITMAP bm;
  ::GetObject(hSourceBitmap,sizeof(BITMAP),&bm);

  
  hAndMaskBitmap  = ::CreateCompatibleBitmap(hDC,bm.bmWidth,bm.bmHeight);
  hXorMaskBitmap  = ::CreateCompatibleBitmap(hDC,bm.bmWidth,bm.bmHeight);

  //Select the bitmaps to DC

  HBITMAP hOldMainBitmap = (HBITMAP)::SelectObject(hMainDC,hSourceBitmap);
  HBITMAP hOldAndMaskBitmap  = (HBITMAP)::SelectObject(hAndMaskDC,
    hAndMaskBitmap);
  HBITMAP hOldXorMaskBitmap  = (HBITMAP)::SelectObject(hXorMaskDC,
    hXorMaskBitmap);

  //Scan each pixel of the souce bitmap and create the masks

  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 having 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 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 safe to pass bitmaps having standard size. Otherwise the result may be unpredictable.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here