Hmm thanks for your post, but how would you release the memory used by GetWindowDC? I get the feeling it is ReleaseDC because it works similarly to GetDC, I'll be on MSDN checkin' it out. Also how would you adjust the offset to your window?
Thanks.
Printable View
Hmm thanks for your post, but how would you release the memory used by GetWindowDC? I get the feeling it is ReleaseDC because it works similarly to GetDC, I'll be on MSDN checkin' it out. Also how would you adjust the offset to your window?
Thanks.
i wouldn't adjust the offset if i could help it - that is why i used a DC that was in the right place.
i have done what i think you wanted on your code... you are lucky i was bored! :)
Code:#include <windows.h>
const char *ClsName = "BasicApp";
const char *WndName = "A Simple Window";
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam);
HINSTANCE hInstance;
// **ADDED THIS
HDC g_MemDC;
HBITMAP g_MemBM;
HBITMAP g_OldMemBM;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG Msg;
HWND hWnd;
WNDCLASSEX WndClsEx;
// Create the application window
WndClsEx.cbSize = sizeof(WNDCLASSEX);
WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
WndClsEx.lpfnWndProc = WndProc;
WndClsEx.cbClsExtra = 0;
WndClsEx.cbWndExtra = 0;
WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndClsEx.lpszMenuName = NULL;
WndClsEx.lpszClassName = ClsName;
WndClsEx.hInstance = hInstance;
WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// Register the application
RegisterClassEx(&WndClsEx);
// Create the window object
hWnd = CreateWindow(ClsName,
WndName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
// Find out if the window was created
if( !hWnd ) // If the window was not created,
return 0; // stop the application
// Display the window to the user
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
// Decode and treat the messages
// as long as the application is running
while( GetMessage(&Msg, NULL, 0, 0) )
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
static bool Draw = false;
static POINT StartingCP, CurrentCP, EndingCP;
switch (Msg)
{
// **ADDED THIS
case WM_CREATE :
{
RECT rect;
GetWindowRect(hWnd, &rect);
int height = rect.bottom-rect.top;
int width = rect.right-rect.left;
//Get a Copy of the Main Window DC, so we can create a compatible one
//but keep the handle so it can be released again
HDC MainDC = GetWindowDC(hWnd);
g_MemDC = CreateCompatibleDC(MainDC);
DeleteObject(MainDC);
// YOU MUST DO THIS BIT SINCE THE DC ONLY HAS A 1x1 BITMAP IN IT
g_MemBM = CreateCompatibleBitmap(GetWindowDC(hWnd), width, height);
g_OldMemBM = (HBITMAP)SelectObject(g_MemDC, g_MemBM);
// THIS BIT IS OPTIONAL BUT THE BITMAP IS CREATED ALL BLACK I THINK, SO BEST TO MAKE IT WHITE :)
// Create Bitmap Structure to fill the background of that window
LPBITMAPINFO lpbi = (LPBITMAPINFO)new BYTE[sizeof(BITMAPINFOHEADER)];
lpbi->bmiHeader.biBitCount = 32;
lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
lpbi->bmiHeader.biWidth = width;
lpbi->bmiHeader.biHeight = height;
lpbi->bmiHeader.biPlanes = 1;
lpbi->bmiHeader.biCompression = BI_RGB;
lpbi->bmiHeader.biSizeImage = 0;
lpbi->bmiHeader.biXPelsPerMeter = 0;
lpbi->bmiHeader.biYPelsPerMeter = 0;
lpbi->bmiHeader.biClrUsed = 0;
lpbi->bmiHeader.biClrImportant = 0;
// Create Bit Array for the Bitmap
BYTE *bArray = new BYTE[4*height*width];
for(int i=0; i<height; i++)
{
for(int j=0; j<width; j++)
{
bArray[((height-1-i)*4*width)+(4*j)+0] = 0xff; //blue
bArray[((height-1-i)*4*width)+(4*j)+1] = 0xff; //green
bArray[((height-1-i)*4*width)+(4*j)+2] = 0xff; //red
bArray[((height-1-i)*4*width)+(4*j)+3] = 0x00; //junk
}
}
// Set Bitmap Bits in Memory DC
SetDIBits(g_MemDC, g_MemBM, 0, height, bArray, lpbi, DIB_RGB_COLORS);
delete[] lpbi;
delete[] bArray;
}
break;
case WM_DESTROY :
//**ADDED THIS
SelectObject(g_MemDC, g_OldMemBM);
DeleteObject(g_MemDC);
DeleteObject(g_MemBM);
DeleteObject(g_OldMemBM);
PostQuitMessage(WM_QUIT);
break;
case WM_CLOSE :
DestroyWindow(hWnd);
break;
case WM_LBUTTONDOWN :
Draw = true;
GetCursorPos(&StartingCP);
ScreenToClient(hWnd, &StartingCP);
break;
case WM_MOUSEMOVE :
if (Draw == true)
{
GetCursorPos(&CurrentCP);
ScreenToClient(hWnd, &CurrentCP);
//**ADDED THIS
MoveToEx(g_MemDC, StartingCP.x, StartingCP.y, NULL);
LineTo(g_MemDC, CurrentCP.x, CurrentCP.y);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE);
}
break;
case WM_LBUTTONUP :
Draw = false;
GetCursorPos(&EndingCP);
ScreenToClient(hWnd, &EndingCP);
//**ADDED THIS
MoveToEx(g_MemDC, StartingCP.x, StartingCP.y, NULL);
LineTo(g_MemDC, CurrentCP.x, CurrentCP.y);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE);
break;
case WM_PAINT :
{
PAINTSTRUCT PS;
HDC DC = BeginPaint(hWnd, &PS);
BitBlt(PS.hdc, PS.rcPaint.left, PS.rcPaint.top,
PS.rcPaint.right - PS.rcPaint.left,
PS.rcPaint.bottom - PS.rcPaint.top,
g_MemDC, // Global Mem DC
PS.rcPaint.left, PS.rcPaint.top, SRCCOPY);
EndPaint(hWnd, &PS);
}
break;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
Thanks for another post man!
Unfortunately my brain can't seem to comprehend it(you probably don't know, but I'm 14) :(
Is that code you supplied completely skeletal? By this I mean that does it have only the 'features/code' that are REQUIRED and NOT OPTIONAL?
Does it really need to have ALL that bitmap info? Can't you just remove it and hope for the best :p?
Thanks.
i tried to comment in the code, but yes you do need all of that bitmap stuff in order to set the bits programaticaly :(Quote:
Originally Posted by .pcbrainbuster
however, the good news is you can just copy and paste it whenever you need it. you would only have to change it if you wanted to change the colour depth or something.
the other option is to store a white bitmap file somewhere (it can be small) and then resize it to fit the window you want to create a bitmap for.
something like:
all the bitmap structure info is hidden in the bitmap file header, but it means you have got a file hanging around that your program depends on, so i prefer to create one from scratch in the program.Code:hbmDisplay = (HBITMAP)LoadImage(GetModuleHandle(NULL),"FileName.bmp",
IMAGE_BITMAP, required_width, required_height, LR_LOADFROMFILE);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, hbmDisplay);
What do you recommend I do :(?
Thanks.
try both and see what the differences are :)Quote:
Originally Posted by .pcbrainbuster
to make it clear:
is equivalent to:Code:case WM_CREATE :
{
RECT rect;
GetWindowRect(hWnd, &rect);
int height = rect.bottom-rect.top;
int width = rect.right-rect.left;
//Get a Copy of the Main Window DC, so we can create a compatible one
//but keep the handle so it can be released again
HDC MainDC = GetWindowDC(hWnd);
g_MemDC = CreateCompatibleDC(MainDC);
DeleteObject(MainDC);
// YOU MUST DO THIS BIT SINCE THE DC ONLY HAS A 1x1 BITMAP IN IT
g_MemBM = CreateCompatibleBitmap(GetWindowDC(hWnd), width, height);
g_OldMemBM = (HBITMAP)SelectObject(g_MemDC, g_MemBM);
// THIS BIT IS OPTIONAL BUT THE BITMAP IS CREATED ALL BLACK I THINK, SO BEST TO MAKE IT WHITE :)
// Create Bitmap Structure to fill the background of that window
LPBITMAPINFO lpbi = (LPBITMAPINFO)new BYTE[sizeof(BITMAPINFOHEADER)];
lpbi->bmiHeader.biBitCount = 32;
lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
lpbi->bmiHeader.biWidth = width;
lpbi->bmiHeader.biHeight = height;
lpbi->bmiHeader.biPlanes = 1;
lpbi->bmiHeader.biCompression = BI_RGB;
lpbi->bmiHeader.biSizeImage = 0;
lpbi->bmiHeader.biXPelsPerMeter = 0;
lpbi->bmiHeader.biYPelsPerMeter = 0;
lpbi->bmiHeader.biClrUsed = 0;
lpbi->bmiHeader.biClrImportant = 0;
// Create Bit Array for the Bitmap
BYTE *bArray = new BYTE[4*height*width];
for(int i=0; i<height; i++)
{
for(int j=0; j<width; j++)
{
bArray[((height-1-i)*4*width)+(4*j)+0] = 0xff; //blue
bArray[((height-1-i)*4*width)+(4*j)+1] = 0xff; //green
bArray[((height-1-i)*4*width)+(4*j)+2] = 0xff; //red
bArray[((height-1-i)*4*width)+(4*j)+3] = 0x00; //junk
}
}
// Set Bitmap Bits in Memory DC
SetDIBits(g_MemDC, g_MemBM, 0, height, bArray, lpbi, DIB_RGB_COLORS);
delete[] lpbi;
delete[] bArray;
}
break;
All the code with the BITMAPINFO and SetDIBits() is not required, but you do need to make a file "FileName.bmp" that is the colour of the background you want (e.g. white) in photoshop or whatever and put it in the folder.Code:case WM_CREATE :
{
RECT rect;
GetWindowRect(hWnd, &rect);
int height = rect.bottom-rect.top;
int width = rect.right-rect.left;
//Get a Copy of the Main Window DC, so we can create a compatible one
//but keep the handle so it can be released again
HDC MainDC = GetWindowDC(hWnd);
g_MemDC = CreateCompatibleDC(MainDC);
DeleteObject(MainDC);
// this replaces all the stuff in the previous example
g_MemBM = (HBITMAP)LoadImage(hInstance, "FileName.bmp",
IMAGE_BITMAP, width, height, LR_LOADFROMFILE);
g_OldMemBM = (HBITMAP)SelectObject(g_MemDC, g_MemBM);
}
break;
try it out with proper bitmaps and you can have different images as the background :) you will sometimes want to display images from files anyway!
edit: actually, if you do display a real bitmap, then you can select regions of it and invert the pixels as was your original request! :D
Hmm, thanks :)!
I'll soon be testing what you said.
Just to get it clear all I have to do is copy and paste that into my WM_CREATE message right?
Thanks.
yep, you can use either of the above two in my modified version of your code that i posted above.Quote:
Originally Posted by .pcbrainbuster
just get any old bitmap file and put it in the folder with your source code and change file name in the code where it says "filename.bmp" to the name of that file.
the only thing is that the image will be stretched to the original size of the window, and it will not resize when you resize the window. (to do that you would have to re-load the bitmap when the window is resized.)
you will also see the horrible flicker (talked about on the other thread) as you resize the window, since windows is erasing the background before WM_PAINT each time. but that is a something else to find out about :)
also, you will still be able to draw lines on it as before.
Oh man!
It still doesn't work :( Aren't I supposed to update the Memory DC in WM_PAINT and WM_LBUTTONUP? If so, how?
Thanks.
what doesn't work? copy and paste this as the whole file and compile it.Quote:
Originally Posted by .pcbrainbuster
i have also attached the bitmap with the same name :)
soon i will make you do it your self or you will never learn, lol :p :pCode:#include <windows.h>
const char *ClsName = "BasicApp";
const char *WndName = "A Simple Window";
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam);
HINSTANCE hInstance;
// **ADDED THIS
HDC g_MemDC;
HBITMAP g_MemBM;
HBITMAP g_OldMemBM;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG Msg;
HWND hWnd;
WNDCLASSEX WndClsEx;
// Create the application window
WndClsEx.cbSize = sizeof(WNDCLASSEX);
WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
WndClsEx.lpfnWndProc = WndProc;
WndClsEx.cbClsExtra = 0;
WndClsEx.cbWndExtra = 0;
WndClsEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
WndClsEx.lpszMenuName = NULL;
WndClsEx.lpszClassName = ClsName;
WndClsEx.hInstance = hInstance;
WndClsEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// Register the application
RegisterClassEx(&WndClsEx);
// Create the window object
hWnd = CreateWindow(ClsName,
WndName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
// Find out if the window was created
if( !hWnd ) // If the window was not created,
return 0; // stop the application
// Display the window to the user
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
// Decode and treat the messages
// as long as the application is running
while( GetMessage(&Msg, NULL, 0, 0) )
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
static bool Draw = false;
static POINT StartingCP, CurrentCP, EndingCP;
switch (Msg)
{
// **ADDED THIS
case WM_CREATE :
{
RECT rect;
GetWindowRect(hWnd, &rect);
int height = rect.bottom-rect.top;
int width = rect.right-rect.left;
//Get a Copy of the Main Window DC, so we can create a compatible one
//but keep the handle so it can be released again
HDC MainDC = GetWindowDC(hWnd);
g_MemDC = CreateCompatibleDC(MainDC);
DeleteObject(MainDC);
// this replaces all the stuff in the previous example
g_MemBM = (HBITMAP)LoadImage(hInstance,
"rose.bmp", IMAGE_BITMAP, width, height, LR_LOADFROMFILE);
g_OldMemBM = (HBITMAP)SelectObject(g_MemDC, g_MemBM);
}
break;
case WM_DESTROY :
//**ADDED THIS
SelectObject(g_MemDC, g_OldMemBM);
DeleteObject(g_MemDC);
DeleteObject(g_MemBM);
DeleteObject(g_OldMemBM);
PostQuitMessage(WM_QUIT);
break;
case WM_CLOSE :
DestroyWindow(hWnd);
break;
case WM_LBUTTONDOWN :
Draw = true;
GetCursorPos(&StartingCP);
ScreenToClient(hWnd, &StartingCP);
break;
case WM_MOUSEMOVE :
if (Draw == true)
{
GetCursorPos(&CurrentCP);
ScreenToClient(hWnd, &CurrentCP);
//**ADDED THIS
MoveToEx(g_MemDC, StartingCP.x, StartingCP.y, NULL);
LineTo(g_MemDC, CurrentCP.x, CurrentCP.y);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE);
}
break;
case WM_LBUTTONUP :
Draw = false;
GetCursorPos(&EndingCP);
ScreenToClient(hWnd, &EndingCP);
//**ADDED THIS
MoveToEx(g_MemDC, StartingCP.x, StartingCP.y, NULL);
LineTo(g_MemDC, CurrentCP.x, CurrentCP.y);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE);
break;
case WM_PAINT :
{
PAINTSTRUCT PS;
HDC DC = BeginPaint(hWnd, &PS);
BitBlt(PS.hdc, PS.rcPaint.left, PS.rcPaint.top,
PS.rcPaint.right - PS.rcPaint.left,
PS.rcPaint.bottom - PS.rcPaint.top,
g_MemDC, // Global Mem DC
PS.rcPaint.left, PS.rcPaint.top, SRCCOPY);
EndPaint(hWnd, &PS);
}
break;
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
Oh I just realized what I did wrong, I'll test it out again after half an hour :)