-
drawing on bitmaps
Hi all.
I’ve been reading Petzold about DIB’s and been playing about with some ideas. One problem I’m having trouble with at the moment is drawing on the same bitmap where an image has been loaded.
The code below (mostly pulled in from various parts of Petzolds book) loads a bitmap using a DIB section. Drawing on top of this bitmap with the cursor and then resizing the screen results in the sketch being erased on a repaint.
How do I draw directly onto the memory set aside via the DIB section?
Code:
#include <windows.h>
#define IDM_FILE_OPEN 40001
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
TCHAR szAppName[] = TEXT ("DibSect") ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = szAppName ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("DIB Section Display"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
HBITMAP CreateDibSectionFromDibFile (PTSTR szFileName)
{
BITMAPFILEHEADER bmfh ;
BITMAPINFO * pbmi ;
BYTE * pBits ;
BOOL bSuccess ;
DWORD dwInfoSize, dwBytesRead ;
HANDLE hFile ;
HBITMAP hBitmap = NULL;
// Open the file: read access, prohibit write access
hFile = CreateFile (szFileName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL) ;
if (hFile == INVALID_HANDLE_VALUE)
return NULL ;
// Read in the BITMAPFILEHEADER
bSuccess = ReadFile (hFile, &bmfh, sizeof (BITMAPFILEHEADER),
&dwBytesRead, NULL) ;
if (!bSuccess || (dwBytesRead != sizeof (BITMAPFILEHEADER))
|| (bmfh.bfType != * (WORD *) "BM"))
{
CloseHandle (hFile) ;
return NULL ;
}
// Allocate memory for the BITMAPINFO structure & read it in
dwInfoSize = bmfh.bfOffBits - sizeof (BITMAPFILEHEADER) ;
pbmi = malloc (dwInfoSize) ;
bSuccess = ReadFile (hFile, pbmi, dwInfoSize, &dwBytesRead, NULL) ;
if (!bSuccess || (dwBytesRead != dwInfoSize))
{
free (pbmi) ;
CloseHandle (hFile) ;
return NULL ;
}
// Create the DIB Section
hBitmap = CreateDIBSection (NULL, pbmi, DIB_RGB_COLORS, (VOID *)&pBits, NULL, 0) ;
if (hBitmap == NULL)
{
free (pbmi) ;
CloseHandle (hFile) ;
return NULL ;
}
// Read in the bitmap bits
ReadFile (hFile, pBits, bmfh.bfSize - bmfh.bfOffBits, &dwBytesRead, NULL) ;
free (pbmi) ;
CloseHandle (hFile) ;
return hBitmap ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HBITMAP hBitmap;
static BOOL fLeftButtonDown, fRightButtonDown ;
static int cxClient, cyClient, xMouse, yMouse ;
static OPENFILENAME ofn ;
static TCHAR szFileName [MAX_PATH], szTitleName [MAX_PATH] ;
static TCHAR szFilter[] = TEXT ("Bitmap Files (*.BMP)\0*.bmp\0")
TEXT ("All Files (*.*)\0*.*\0\0") ;
BITMAP bitmap ;
HDC hdc, hdcMem ;
PAINTSTRUCT ps ;
switch (message)
{
case WM_CREATE:
ZeroMemory(&ofn, sizeof(OPENFILENAME));
ofn.lStructSize = sizeof (OPENFILENAME) ;
ofn.hwndOwner = hwnd ;
ofn.lpstrFilter = szFilter ;
ofn.lpstrFile = szFileName ;
ofn.nMaxFile = MAX_PATH ;
ofn.lpstrFileTitle = szTitleName ;
ofn.nMaxFileTitle = MAX_PATH ;
ofn.lpstrDefExt = TEXT ("bmp") ;
return 0 ;
case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0 ;
case WM_COMMAND:
switch (LOWORD (wParam))
{
case IDM_FILE_OPEN:
// Show the File Open dialog box
if (!GetOpenFileName (&ofn))
return 0 ;
// If there's an existing bitmap, delete it
if (hBitmap)
{
DeleteObject (hBitmap);
hBitmap = NULL ;
}
// Create the DIB Section from the DIB file
SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
ShowCursor (TRUE) ;
hBitmap = CreateDibSectionFromDibFile (szFileName) ;
ShowCursor (FALSE) ;
SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
// Invalidate the client area for later update
InvalidateRect (hwnd, NULL, TRUE) ;
if (hBitmap == NULL)
{
MessageBox (hwnd, TEXT ("Cannot load DIB file"),
szAppName, MB_OK | MB_ICONEXCLAMATION) ;
}
return 0 ;
}
break ;
case WM_LBUTTONDOWN:
if (!fRightButtonDown)
SetCapture (hwnd) ;
xMouse = LOWORD (lParam) ;
yMouse = HIWORD (lParam) ;
fLeftButtonDown = TRUE ;
return 0 ;
case WM_LBUTTONUP:
if (fLeftButtonDown)
SetCapture (NULL) ;
fLeftButtonDown = FALSE ;
return 0 ;
case WM_RBUTTONDOWN:
if (!fLeftButtonDown)
SetCapture (hwnd) ;
xMouse = LOWORD (lParam) ;
yMouse = HIWORD (lParam) ;
fRightButtonDown = TRUE ;
return 0 ;
case WM_RBUTTONUP:
if (fRightButtonDown)
SetCapture (NULL) ;
fRightButtonDown = FALSE ;
return 0 ;
case WM_MOUSEMOVE:
if (!fLeftButtonDown && !fRightButtonDown)
return 0 ;
hdc = GetDC (hwnd) ;
SelectObject (hdcMem, hBitmap);
SelectObject (hdc, hBitmap);
SelectObject (hdc,
GetStockObject (fLeftButtonDown ? BLACK_PEN : WHITE_PEN)) ;
SelectObject (hdcMem,
GetStockObject (fLeftButtonDown ? BLACK_PEN : WHITE_PEN)) ;
MoveToEx (hdc, xMouse, yMouse, NULL) ;
MoveToEx (hdcMem, xMouse, yMouse, NULL) ;
xMouse = (short) LOWORD (lParam) ;
yMouse = (short) HIWORD (lParam) ;
LineTo (hdc, xMouse, yMouse) ;
LineTo (hdcMem, xMouse, yMouse) ;
ReleaseDC (hwnd, hdc) ;
return 0 ;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
if (hBitmap)
{
GetObject (hBitmap, sizeof (BITMAP), &bitmap) ;
hdcMem = CreateCompatibleDC (hdc) ;
SelectObject (hdcMem, hBitmap) ;
BitBlt (hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight,
hdcMem, 0, 0, SRCCOPY) ;
DeleteDC (hdcMem) ;
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
if (hBitmap)
DeleteObject (hBitmap) ;
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
Code:
#define IDM_FILE_OPEN 40001
DIBSECT MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open", IDM_FILE_OPEN
END
END
-
The first thing you've got to understand is that you can't select the same bitmap into more than one DC. Selecting into your memory DC and then the window's DC means that it's not in the memory DC anymore.
Also, you should create your memory DC once, in your WM_CREATE handler, then delete it in WM_DESTROY (like your bitmap there). Forget about using GetObject to grab your bitmap's dimensions in WM_PAINT, they're not important. Instead use the update rectangle provided for you in PAINTSTRUCT by BeginPaint:-
Code:
BitBlt (ps.hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left,
ps.rcPaint.bottom - ps.rcPaint.top, hdcMem, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY) ;
You don't need to select the bitmap into the window DC in your WM_MOUSEMOVE handler, it'll already be visible thanks to WM_PAINT. All you want it to do is draw your lines, by doing them to hdcMem at the same time with your bitmap selected, the changes will be "saved".
P.S. Make sure that you keep the handle returned by SelectObject when you first select your bitmap into your memory DC! A 1x1 bitmap is created for every DC you created, select it back into the DC before you delete it to prevent memory leakage!
-
Thanks for the clarification, all is working now :)