Hello. I have a cursor conundrum. I am developing a custom control and one feature I want this custom control to have is the ability to be able to rearrange its appearance by dragging and dropping one part of it to another. The way I decided I would do this would be to:
1) Convert the selected area into a bitmap
2) Use the bitmap from 1) as the cursor until the drop point is selected.
3) Redraw the control in the new arrangement.
I am not worried about step 3). That should be trivial - it won't involve anything I haven't achieved already in my code concerning bitmaps or device contexts. However, I have a conundrum concerning step 2). I have achieved what I want to achieve but only if I save the bitmap to a file first then reload it as a cursor! What I am looking for here is a way of obtaining the bitmap data and converting it directly into a cursor. Clearly my problem stems from having cobbled together samples of code from various sources without properly understanding what is going on -
So I call the image capuring function on clicking the mouse...
void CScrollBarEx::OnLButtonDown(UINT inFlags, CPoint inPoint) {
...
CaptureAnImage(sliderVector[m_currentSlider-1].sliderRect);
...
}
and then you can see where I have edited out the code where I try and use the HBITMAP used to subsequently make a .bmp file to make the cursor - doing this always produces a black rectangle as the cursor. However- when I save that HBITMAP to .bmp file, then reload it into memory and use that to make a cursor - it works perfectly!
int CScrollBarEx::CaptureAnImage(RECT sliderRect)
{
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
HBITMAP m_hAndMask;
HBITMAP m_hXorMask;
hdcWindow = (GetDC())->GetSafeHdc();
hdcMemDC = CreateCompatibleDC(hdcWindow);
if(!hdcMemDC)
{
goto done;
}
RECT rcClient;
GetClientRect(&rcClient);
hbmScreen = CreateCompatibleBitmap(hdcWindow, sliderRect.right-sliderRect.left, sliderRect.bottom-sliderRect.top);
if(!hbmScreen)
{
goto done;
}
SelectObject(hdcMemDC,hbmScreen);
if(!BitBlt(hdcMemDC,
0,0,
sliderRect.right-sliderRect.left, sliderRect.bottom-sliderRect.top,
hdcWindow,
sliderRect.left,sliderRect.top,
SRCCOPY))
{
goto done;
}
GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);
BITMAPFILEHEADER bmfHeader;
BITMAPINFOHEADER bi;
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = bmpScreen.bmWidth;
bi.biHeight = bmpScreen.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;
HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize);
char *lpbitmap = (char *)GlobalLock(hDIB);
GetDIBits(hdcWindow, hbmScreen, 0,
(UINT)bmpScreen.bmHeight,
lpbitmap,
(BITMAPINFO *)&bi, DIB_RGB_COLORS);
HANDLE hFile = CreateFile(L"capture.bmp",
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);
bmfHeader.bfSize = dwSizeofDIB;
bmfHeader.bfType = 0x4D42;
DWORD dwBytesWritten = 0;
WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);
GlobalUnlock(hDIB);
GlobalFree(hDIB);
CloseHandle(hFile);
HBITMAP hBMP;
HPALETTE palette;
LoadBitmapFromBMPFile(L"capture.bmp", &hBMP, &palette);
m_hCursor = CColorCursor::CreateCursorFromBitmap(hBMP,RGB(0,0,0),0,0);
::SetCursor(m_hCursor);
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
DeleteObject(hdcWindow);
return 0;
}
and
HCURSOR CColorCursor::CreateCursorFromBitmap(HBITMAP hSourceBitmap,
COLORREF clrTransparent,
DWORD xHotspot,DWORD yHotspot)
{
HCURSOR hRetCursor = NULL;
do
{
if(NULL == hSourceBitmap)
{
break;
}
HBITMAP hAndMask = NULL;
HBITMAP hXorMask = NULL;
GetMaskBitmaps(hSourceBitmap,clrTransparent,hAndMask,hXorMask);
if(NULL == hAndMask || NULL == hXorMask)
{
break;
}
ICONINFO iconinfo = {0};
iconinfo.fIcon = FALSE;
iconinfo.xHotspot = xHotspot;
iconinfo.yHotspot = yHotspot;
iconinfo.hbmMask = hAndMask;
iconinfo.hbmColor = hXorMask;
hRetCursor = ::CreateIconIndirect(&iconinfo);
}
while(0);
return hRetCursor;
}
I have used code from the following sources...
HOWTO: How To Use LoadImage() to Read a BMP File
at http://support.microsoft.com/kb/158898
and
Creating a color cursor from a bitmap
at Creating a color cursor from a bitmap
In the last article the author warns against making a cursor outside of the standard cursor sizes but this doesn't explain why the code works when loading a cursor from file but not when using the bitmap directly!
modified 21-Feb-13 4:05am.
|