Create a bitmap in memory and use it - c

My goal is to create a bitmap in memory and use its handle as a parameter of the BM_SETIMAGE message (a message that set a button's bitmap).
The following is my code:
........
HDC hdc = GetDC(hwnd);
HDC memDC = CreateCompatibleDC(hdc);
HBITMAP hMemBmp = CreateCompatibleBitmap(hdc, 100, 100);
HBITMAP hOldBmp = (HBITMAP)SelectObject(memDC, hMemBmp);
Rectangle(memDC, 0, 0, 100, 100);
HBRUSH brush = CreateSolidBrush(RGB(0xff, 0xff, 0x00));
RECT rc;
rc.left = 0;
rc.top = 0;
rc.right = 100;
rc.bottom = 100;
FillRect(memDC, &rc, brush);
SendMessage(GetDlgItem(hDlg, IDC_SET_START_PAGE_BG), BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hMemBmp);
........
but it doesn't work, I just got black color on my button.
Questions:
Does FillRect(memDC, &rc, brush) modify the content of hMemBmp? If not, what SelectObject(memDC, hMemBmp) did for these 2 objects?
Should I use CreateBitmap instead of CreateCompatibleBitmap to do my task?

You should select the old bitmap back into the memory DC before you send the message. According to MSDN a bitmap can be selected into only a single DC at a time.

Related

Why is window creation slow for many windows? Can it be faster?

For an experiment I want to create lots of small windows. I mean a lot, like a thousand or so.
The windows are small, containing some labels only (AB):
I created a hundred of them as an experiment, but their display is not instantaneous, it is visible as they are put on the screen. Why is that?
I expected for a C/C++ program to be very fast, so that I don't see the windows put on the screen at all. Is it a wrong expectation? Or should I use some kind of lighter window type (I'm no Windows-programmer, so I'm just guessing) which can be put up much faster?
Here's the relevant part of the code:
HWND parent = 0;
for (int i = 0; i < 100; ++i)
{
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_BORDER,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
if (parent == 0)
parent = hWnd;
else
SetWindowLong(hWnd, GWL_HWNDPARENT, (long)parent);
SetWindowLong(hWnd, GWL_STYLE, 0);
SetMenu(hWnd, NULL);
SetWindowPos(hWnd, HWND_TOP, 100 + (i * 20), 100, 20, 20, 0);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
}
...
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
SetBkColor(hdc, RGB(0,255,0));
TextOut(hdc, 1, 1, TEXT("AB"), strlen("AB"));
EndPaint(hWnd, &ps);
}
break;
SetWindowLong, SetMenu, SetWindowPos and ShowWindow can all be removed by giving the same information in the CreateWindowW arguments.
Then you can also remove the call to UpdateWindow.
Here its not the speed of C/C++ that matter, but the Win32 API calls which send windows messages.

WIN32 C Properly using SetWindowLongPtr and GetWindowLongPtr

Yes this is a homework assignment and I'm completely stumped.
So I've made a struct and two windows:
typedef struct thingy {
int count;
TCHAR* MSG;
COLORREF colour; };
The windows have:
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = sizeof(thingy*);
wndclass.cbWndExtra = sizeof(thingy*);
I need one window to display 0 and the next to display 1 using this struct stored in the clsextra using SetWindowLongPtr and GetWindowLongPtr/SetClassLongPtr and GetClassLongPtr
Count of course has to be initialized to 0 for the FIRST window but not for the second and I have no idea how to do this. Only one WndProc can be used to do this.
static thingy* mythingy = (thingy*)GetWindowLongPtr(hwnd, 0);
char buf[128];
int num = GetClassLongPtr(hwnd, 0);
static boolean set = false;
case WM_CREATE:
if (!set) {
mythingy = (thingy*)malloc(sizeof(thingy));
mythingy->count = 0;
mythingy->colour = RGB(0, 0, 0);
mythingy->MSG = TEXT("Hello Windows!");
set = true;
}
if (lParam != NULL) {
SetClassLongPtr(hwnd, 0, (LONG)mythingy->count);
}
mythingy->count++;
SetWindowLongPtr(hwnd, 0, (LONG)mythingy);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, mythingy->MSG, -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
sprintf_s(buf, "%d", num);
TextOut(hdc, 0, 0, LPCWSTR(buf), 1);
EndPaint(hwnd, &ps);
return 0;
Right now both windows display 1 and I'm struggling to see why it isn't doing what I want as I can't find anything on Google about how to use these two functions or when I need to be calling them.
Window: 0x000a0528
count = 0
Add to class data
Window: 0x001f099a
count = 1
Add to class data
From the paint method I get the data and both of them are 1.
A Window Class
... is a set of attributes that the system uses as a template to create a window. Every window is a member of a window class.
Since the Windows API is exposed as a flat C interface, there is no inheritance at the language level. The phrase "is a member of" is implemented by sharing the class memory across window instances of that class. Consequently, every call to GetClassLongPtr accesses the same shared memory.
In contrast, each window can reserve cbWndExtra bytes of memory, that are attributed to the specific window instance. This memory is private to each window, and can store per-window data.
To implement your requirements you need to store the common information (current count of windows) in the window class' extra memory (cbClsExtra), and keep the per-window data (index, message, and color) in the window instance's extra memory (cbWndExtra).
Apply the following changes to your code:
// Total count of windows stored as an integer:
wndclass.cbClsExtra = sizeof(int);
In the WM_CREATE-handler, set the per-window data, increment the total count, and store it away:
case WM_CREATE:
{
int count = (int)GetClassLongPtr(hwnd, 0);
// Allocate new per-window data object:
thingy* mythingy = (thingy*)malloc(sizeof(thingy));
mythingy->count = count;
mythingy->colour = RGB(0, 0, 0);
mythingy->MSG = TEXT("Hello Windows!");
// Store the per-window data:
SetWindowLongPtr(hwnd, 0, (LONG_PTR)mythingy);
// Increment total count and store it in the class extra memory:
++count;
SetClassLongPtr(hwnd, 0, (LONG_PTR)count);
}
return DefWindowProc(hwnd, msg, wParam, lParam);
In the WM_PAINT-handler, access the per-window data:
case WM_PAINT:
{
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
RECT rect;
GetClientRect(hwnd, &rect);
// Retrieve per-window data:
thingy* mythingy = (thingy*)GetWindowLongPtr(hwnd, 0);
DrawText(hdc, mythingy->MSG, -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
char buf[128];
sprintf_s(buf, "%d", mythingy->count);
TextOutA(hdc, 0, 0, buf, 1);
EndPaint(hwnd, &ps);
return 0;
}
Note: All error handling has been elided for brevity. Character-encoding issues have not really been addressed either (char vs. wchar_t). Likewise, resource management is missing. You'd probably want to deallocate memory in a WM_NCDESTROY-handler. The code assumes, that only windows of a single window class are created.

WinAPI text output in C

I'm fairly new to WINAPI, and I need some help doing text output. I have an array of pixels that I write to with functions and then periodically blit onto the screen using the following functions:
DWORD WINAPI tickThreadProc(HANDLE handle) {
ShowWindow( hwnd, SW_SHOW );
HDC hdc = GetDC( hwnd );
hdcMem = CreateCompatibleDC( hdc );
HBITMAP hbmOld = (HBITMAP)SelectObject( hdcMem, hbmp );
int delay = 1000 / fps;
InitPhys();
LoadIMGs();
for ( ;; ) {
onFrame( pixels );
BitBlt( hdc, gLeft, gTop, width, height, hdcMem, 0, 0, SRCCOPY );
// Wait
Sleep( delay );
// Physics
SimPhys();
}
SelectObject( hdcMem, hbmOld );
DeleteDC( hdc );
return 0;
}
void MakeSurface(HWND hwnd) {
BITMAPINFO bmi;
bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height; // Order pixels from top to bottom
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; // last byte not used, 32 bit for alignment
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
bmi.bmiHeader.biXPelsPerMeter = 0;
bmi.bmiHeader.biYPelsPerMeter = 0;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biClrImportant = 0;
bmi.bmiColors[0].rgbBlue = 0;
bmi.bmiColors[0].rgbGreen = 0;
bmi.bmiColors[0].rgbRed = 0;
bmi.bmiColors[0].rgbReserved = 0;
HDC hdc = GetDC( hwnd );
// Create DIB section to always give direct access to pixels
hbmp = CreateDIBSection( hdc, &bmi, DIB_RGB_COLORS, (void**)&pixels, NULL, 0 );
DeleteDC( hdc );
// Create a new thread to use as a timer
hTickThread = CreateThread( NULL, 0, &tickThreadProc, NULL,0, NULL );
}
This is modified off some code I found on the internet. The pixel struct has 4 ints for r, g, b, and a.
I need to do text output and loading a picture for text is impractical. Any help?
First of all, if you use GetDC to get a handle to device context, you must use ReleaseDC when you're done with it. DeleteDC is only for device contexts that you created.
To draw text to this window, you can use functions like TextOut or DrawText using that DC (before you release it).
PAINTSTRUCT is for handling WM_PAINT messages (which is the more common way to draw to a Window). It looks like you're instead trying to draw directly from another thread on a regular basis. GDI isn't very good at dealing with multiple threads, so you might have some problems with this approach. But if your BitBlts are working, then a TextOut should work as well.

How can I reuse a HBITMAP handle?

I have to draw a bitmap multiple times. It's loaded from file. I can reload it every time I have to use it in SelectObject the following way:
void drawBitmap(HWND hWnd, int xPos, int yPos) {
HBITMAP hBmp = (HBITMAP) LoadImage(NULL, "image.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HDC hDC = GetDC(hWnd);
HDC hdcMem = CreateCompatibleDC(hDC);
SelectObject(hdcMem, hBmp);
BitBlt(hDC, xPos, yPos, 7, 7, hdcMem, 0, 0, SRCCOPY);
}
drawBitmap(hMainWnd, 0, 0);
drawBitmap(hMainWnd, 14, 0);
drawBitmap(hMainWnd, 28, 0);
But is it also possible to do something like this?
HBITMAP hBmp = (HBITMAP) LoadImage(NULL, "image.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
void drawBitmap(HWND hWnd, int xPos, int yPos) {
HBITMAP hBmp2 = hBmp;
HDC hDC = GetDC(hWnd);
HDC hdcMem = CreateCompatibleDC(hDC);
SelectObject(hdcMem, hBmp2);
BitBlt(hDC, xPos, yPos, 7, 7, hdcMem, 0, 0, SRCCOPY);
}
drawBitmap(hMainWnd, 0, 0);
drawBitmap(hMainWnd, 14, 0);
drawBitmap(hMainWnd, 28, 0);
But this only draws one bitmap...
MSDN says:
The SelectObject function selects an
object into the specified device
context (DC). The new object replaces
the previous object of the same type.
So maybe my hBmp is wasted after SelectObject is called. But I copied it into hBmp2 first, then what's the problem?
You are not deleting the memory DC when you are done with it. That means the DC is leaked, and the bitmap is still selected in that leaked DC. And according to the SelectObject documentation: "An application cannot select a single bitmap into more than one DC at a time."
So the second SelectObject fails because the bitmap is still selected in the first HDC.
Clean up after yourself by calling DeleteDC at the end of the drawBitmap function (and also call DeleteObject on the hBmp when you are done with it).
Additionally, the HBITMAP hBmp2 = hBmp; line accomplishes nothing. You're just assigning the handle to a different variable. It's still the same handle to the same bitmap.

c - win32 api rectangle is not drawn if hdc passed to method

I have been playing with C and win32 recently and the following has me balked:
case WM_PAINT:
g_crntRect = (RECT*) malloc(sizeof(RECT));
GetWindowRect(hwnd, g_crntRect);
hpen = CreatePen(PS_SOLID, 1, RGB(255,25,5));
hdc = BeginPaint (hwnd, &ps) ;
oldPen = SelectObject(hdc, hpen);
drawRects(hwnd, hdc);
//Rectangle(hdc, 0, 0, 840, 525);
SelectObject(hdc,oldPen);
DeleteObject(hpen);
EndPaint (hwnd, &ps) ;
return 0 ;
So, if I call my own method above for drawing rectangles, it draws nothing, however the call to draw the rectangle in the WM_PAINT that I have commented above succeeds without a problem.
Here is my method:
BOOL drawRects(HWND hwnd, HDC hdc)
{
char buffer[50];
BOOL res = FALSE;
RECT tempRect = {0};
char quadStr[6] = "";
int i = 0;
quadStr[i]='*';
OutputDebugString("Going to draw");
for (i = 1; i <= 4; i++)
{
//get rect for each quadrent from the parent
OutputDebugString("inside for");
getRect(g_crntRect, &tempRect, i);
OutputDebugString("got rectr");;
res = Rectangle(hdc, tempRect.right, tempRect.top, tempRect.right, tempRect.bottom);
if (res == FALSE)
{
OutputDebugString("false");;
sprintf(buffer, "Error: %ld", GetLastError());
OutputDebugString(buffer);
}
else
{
OutputDebugString("drew");;
}
quadStr[i]='*';
printRect(quadStr, &tempRect);
}
return TRUE;
}
Looking at debug output, everything seems fine. Proper values are being passed into the Rectangle method. However, I wonder if I am not passing HDC correctly?
Any ideas?
Looks like a simple typo. In your method you have:
res = Rectangle(hdc, tempRect.right, tempRect.top, tempRect.right, tempRect.bottom);
The second parameter should be tempRect.left not tempRect.right. You're trying to draw a zero-width rectangle.
Change
BOOL drawRects(HWND hwnd, HDC* hdc)
to
BOOL drawRects(HWND hwnd, HDC hdc)
A Windows handle is actually a pointer, so there is no need to pass it by reference. But if you do, you would need to call your function as drawRects(hwnd, &hdc);
In general you don't paint outside the WM_PAINT handler. If you want your window to be updated, just call InvalidateRect on the area you want to be redrawn. That will trigger the WM_PAINT call which will then repaint the window.
Is there a reason you need to paint the window outside the paint handler?

Resources