I have a problem - I need to draw two png files, one on the other. When I do it usual way, there is a "blinking" effect (first image overdraws the second one for small time period). I use GDI+ library and my WM_PAINT handling looks like this:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint( hwnd, & ps );
displayImage(firstImage, hwnd);
displayImage(secondImage, hwnd);
EndPaint( hwnd, & ps );
break;
}
displayImage function:
void displayImage(HBITMAP mBmp, HWND mHwnd)
{
RECT myRect;
BITMAP bm;
HDC screenDC, memDC;
HBITMAP oldBmp;
BLENDFUNCTION bf;
GetObject(mBmp, sizeof(bm), &bm);
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xff;
bf.AlphaFormat = AC_SRC_ALPHA;
screenDC = GetDC(mHwnd);
GetClientRect(mHwnd, &myRect);
if (mBmp == NULL)
FillRect(screenDC, &myRect, WHITE_BRUSH);
else
{
memDC = CreateCompatibleDC(screenDC);
oldBmp = (HBITMAP)SelectObject(memDC, mBmp);
AlphaBlend (screenDC, 0, 0, myRect.right,myRect.bottom, memDC, 0, 0, bm.bmWidth,bm.bmHeight, bf);
SelectObject(memDC, oldBmp);
DeleteDC(memDC);
ReleaseDC(mHwnd, screenDC);
}
}
Loading files to variables:
HBITMAP mLoadImg(WCHAR *szFilename)
{
HBITMAP result=NULL;
Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(szFilename,false);
bitmap->GetHBITMAP(NULL, &result);
delete bitmap;
return result;
}
firstImage = mLoadImg(L"data\\img\\screen.png");
secondImage = mLoadImg(L"data\\img\\screen2.png");
I've heard that I should do a off-screen drawing. How should that look like?
You don't need all of that. You can use GDI+ directly:
static Gdiplus::Image *firstImage;
static Gdiplus::Image *secondImage;
case WM_CREATE: // or WM_INITDIALOG if it's dialog
{
firstImage = new Gdiplus::Image(L"data\\img\\screen.png");
secondImage = new Gdiplus::Image(L"data\\img\\screen2.png");
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
Gdiplus::Graphics gr(hdc);
gr.DrawImage(firstImage, 0, 0);
gr.DrawImage(secondImage, 0, 0);//<== this will draw transparently
EndPaint(hwnd, &ps);
return 0;
}
However, this code is still drawing 2 images back to back with possible flicker (like your original code). Use double-buffering in WM_PAINT so that only one BltBlt is done. Simply change to:
if (msg == WM_PAINT)
{
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
HGDIOBJ oldbmp = SelectObject(memdc, hbitmap);
FillRect(memdc, &rc, WHITE_BRUSH);
Gdiplus::Graphics gr(memdc);
gr.DrawImage(firstImage, 0, 0);
gr.DrawImage(secondImage, 0, 0);
BitBlt(hdc, 0, 0, rc.right, rc.bottom, memdc, 0, 0, SRCCOPY);
SelectObject(memdc, oldbmp);
DeleteObject(hbitmap);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
return 0;
}
As for the original code:
void displayImage(HBITMAP mBmp, HWND mHwnd)
{
HDC hdc = GetDC(mHwnd);
...
}
You should change the function declaration to void displayImage(HBITMAP mBmp, HWND mHwnd, HDC hdc) then you can pass the hdc directly from WM_PAINT
First, change displayImage to take the HDC and RECT from the caller instead of the HWND.
void displayImage(HBITMAP mBmp, HDC hdc, const RECT &myRect)
{
if (mBmp == NULL)
FillRect(screenDC, &myRect, WHITE_BRUSH);
else
{
BITMAP bm;
GetObject(mBmp, sizeof(bm), &bm);
HDC memDC = CreateCompatibleDC(screenDC);
HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, mBmp);
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xff;
bf.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(hdc, 0, 0, myRect.right, myRect.bottom, memDC, 0, 0, bm.bmWidth, bm.bmHeight, bf);
SelectObject(memDC, oldBmp);
DeleteDC(memDC);
}
}
Then, in the caller create a compatible DC and bitmap. These are your off-screen space for doing the compositing. Make the calls to displayImage with this new DC. This will compose the PNGs offscreen. Finally, blit the composed result to the actual window DC in one go.
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT myRect;
GetClientRect(hwnd, &myRect);
// Create an off-screen DC for composing the images.
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmpMem = CreateCompatibleBitmap(hdc, myRect.right, myRect.bottom);
HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpMem);
// Compose the images to the offscreen bitmap.
displayImage(firstImage, hdcMem, myRect);
displayImage(secondImage, hdcMem, myRect);
// Blit the resulting composition to the window DC.
BitBlt(hdc, 0, 0, myRect.right, myRect.bottom,
hdcMem, 0, 0, SRCCOPY);
// Clean up the offscreen stuff.
SelectObject(hdcMem, hbmpOld);
DeleteObject(hbmpMem);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
break;
}
Finally, if you're still seeing a flash of the background color, see Pavan Chandaka's answer.
Handle "WM_ERASEBKGND" message by your self.
Actually before loading the second image two things happen.
WM_ERASEBKGND is triggered first to fill the image area with, whatever current windows background color is.
WM_PAINT to render action.
Documentation says to avoid blink/Flickr, provide a default handler for "WM_ERASEBKGND".
Below is the link, go to "A Control That Doesn't Flicker". You have an example too.
https://msdn.microsoft.com/en-us/library/ms969905.aspx
Related
I encounter a problem with GDI objects increasing. How can I solve this problem?
The DeleteObject() function doesn`t help.
// Other Events
GetClientRect(hAnimationStars, &Dimensions);
AnimationStarsDC = BeginPaint(hAnimationStars, &ps);
MemoryDC = CreateCompatibleDC(AnimationStarsDC);
HBITMAP Bitmap = CreateCompatibleBitmap(AnimationStarsDC, Dimensions.right, Dimensions.bottom);
SelectObject(MemoryDC, Bitmap);
SetBkMode(MemoryDC, TRANSPARENT);
FillRect(MemoryDC, &Dimensions, CreateSolidBrush(BackgroundColor));
// Draw Operations
BitBlt(AnimationStarsDC, 0, 0, Dimensions.right, Dimensions.bottom, MemoryDC, 0, 0, SRCCOPY);
while (!DeleteDC(MemoryDC));
while (!DeleteObject(Bitmap));
EndPaint(hAnimationStars, &ps);
// Other Events
You need to restore any object you replace with SelectObject() before destroying the HDC. You also need to destroy the HBRUSH you are creating.
GetClientRect(hAnimationStars, &Dimensions);
HDC AnimationStarsDC = BeginPaint(hAnimationStars, &ps);
HDC MemoryDC = CreateCompatibleDC(AnimationStarsDC);
HBITMAP Bitmap = CreateCompatibleBitmap(AnimationStarsDC, Dimensions.right, Dimensions.bottom);
HBITMAP oldBmp = (HBITMAP) SelectObject(MemoryDC, Bitmap); // <-- REMEMBER THE OLD BITMAP!
SetBkMode(MemoryDC, TRANSPARENT);
HBRUSH Brush = CreateSolidBrush(BackgroundColor); // <-- REMEMBER THE BRUSH YOU CREATE!
FillRect(MemoryDC, &Dimensions, Brush);
DeleteObject(CreateSolidBrush); // <-- DESTROY THE BRUSH!
// Draw Operations
BitBlt(AnimationStarsDC, 0, 0, Dimensions.right, Dimensions.bottom, MemoryDC, 0, 0, SRCCOPY);
SelectObject(MemoryDC, oldBmp); // <-- RESTORE THE OLD BITMAP!
DeleteObject(Bitmap);
DeleteDC(MemoryDC);
EndPaint(hAnimationStars, &ps);
I'm trying to paint an Icon onto my window with the win32 api. Here is where I load the image.
case WM_CREATE: {
HANDLE image = (HICON)LoadImage(NULL, TEXT("Button.ico"), IMAGE_ICON, 16, 16, LR_LOADFROMFILE | LR_LOADTRANSPARENT);
break;
}
Here is where I try to paint the icon onto the screen.
case WM_NCPAINT: {
PAINTSTRUCT ps;
BITMAP bm;
HDC hdc = BeginPaint(ParentHwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = SelectObject(hdcMem, image);
GetObject(image, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(ParentHwnd, &ps);
break;
}
For some reason, I get this error on SelectObject();
E0144 a value of type "HGDIOBJ" cannot be used to initialize an entity of type "HBITMAP"
I'm using Visual Studio community 2019. I've looked all over the place for an answer. Thank you in advance for your efforts to help.
One, you are not supposed to use BeginPaint() in a WM_NCPAINT handler, only in a WM_PAINT handler. Per the WM_NCPAINT documentation, use GetDCEx() instead.
Two, you likely have STRICT Type Checking turned on (which is a good thing), that is why you are getting the compiler error. Under STRICT, an HGDIOBJ (aka void*) cannot be assigned to an HBITMAP (aka struct HBITMAP__*), so you would need to explicitly type-cast the return value of SelectObject(). However, you are loading an HICON, which you can't select as-is into an HDC, so you will have to either:
load a BMP file instead of an ICO file.
convert the HICON data to an actual HBITMAP.
use DrawIcon() or DrawIconEx().
Three, in your WM_CREATE handler, your image variable is local to that message handler, so whatever image you are accessing in the WM_NCPAINT handler is not the same variable.
Try this instead:
HBITMAP image;
...
case WM_CREATE: {
HICON icon = (HICON) LoadImage(NULL, TEXT("Button.ico"), IMAGE_ICON, 16, 16, LR_LOADFROMFILE | LR_LOADTRANSPARENT);
// convert icon to image as needed...
DestroyIcon(icon);
break;
}
case WM_NCPAINT: {
BITMAP bm;
HDC hdc = GetDCEx(hwnd, (HRGN)wParam, DCX_WINDOW | DCX_INTERSECTRGN);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, image);
GetObject(image, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
ReleaseDC(hwnd, hdc);
break;
}
Or this:
HICON image;
...
case WM_CREATE: {
image = (HICON) LoadImage(NULL, TEXT("Button.ico"), IMAGE_ICON, 16, 16, LR_LOADFROMFILE | LR_LOADTRANSPARENT);
break;
}
case WM_DESTROY: {
DestroyIcon(image);
break;
}
case WM_NCPAINT: {
HDC hdc = GetDCEx(hwnd, (HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN);
DrawIcon(hdc, 0, 0, image);
ReleaseDC(hwnd, hdc);
break;
}
I am trying to load some bitmaps when starting up the application. I am trying to load them in WM_CREATE but only the last bitmap i load will stay loaded.
I can load the pictures in WM_PAINT but i have been told it's better to load them in WM_CREATE.
//before switch statement
static HBITMAP bitmap1, bitmap2;
case WM_CREATE: {
HINSTANCE hInstance = GetModuleHandle(NULL);
bitmap1 = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(IDB_BITMAP1),
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
bitmap2 = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(IDB_BITMAP2),
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
if (!bitmap1 || !bitmap2) MessageBox(NULL, _T("Error while loading images"), _T("Error!"), MB_ICONEXCLAMATION | MB_OK);
case WM_PAINT:{
//Draw bitmap...
}
I expected that both bitmaps got loaded as they do when loaded inside WM_PAINT
EDIT:
The problem weren't were i thought. The problem is it draws behind the filled area i had made. I still don't know how to fix.
//Draw bitmap function
bool DrawBitmap(HBITMAP hBitmap, int posX, int posY, int sizeX, int sizeY)
{
BITMAP bmp;
HWND hWnd = FindWindow(windowClassName, NULL);
if (!hWnd) return false;
HDC hdc = GetDC(hWnd);
if (!hdc) return false;
HDC hBitmapDC = CreateCompatibleDC(hdc);
if (!hBitmapDC) return false;
GetObject(hBitmap, sizeof(bmp), &bmp);
SelectObject(hBitmapDC, hBitmap);
BitBlt(hdc, posX, posY, sizeX, sizeY, hBitmapDC, 0, 0, SRCCOPY);
DeleteObject(hBitmap);
ReleaseDC(hWnd, hBitmapDC);
ReleaseDC(hWnd, hdc);
return true;
}
//Before switch
RECT recRect;
STATIC HBITMAP bitmap1;
//case WM_CREATE:
case WM_CREATE: {
HINSTANCE hInstance = GetModuleHandle(NULL);
bitmap1 = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(IDB_BITMAP1),
IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
if (!bitmap) MessageBox(NULL, _T("Error while loading images"), _T("Error!"), MB_ICONEXCLAMATION | MB_OK);
return 0;
break;
//case WM_PAINT
case WM_PAINT:
DefWindowProc(hwnd, msg, wParam, lParam);
hdc = GetWindowDC(hwnd);
recRect = { -1, -1, 50 + 1, 30 + 1 };
FillRect(hdc, &recRect, (HBRUSH)CreateSolidBrush(RGB(30, 30, 30)));
if (DrawBitmap(bitmap1, 5, 5, 10, 10) == false) MessageBox(NULL, _T("Error while drawing images"), _T("Error!"), MB_ICONEXCLAMATION | MB_OK);
return DefWindowProc(hwnd, msg, wParam, lParam);
return 0;
break;
The problem does not occur when i declare the bitmap in WM_PAINT
GetWindowDC returns the dc for the whole window, including the non-client area. You usually need GetDC instead.
Always use BeginPaint/EndPaint when responding to WM_PAINT and return 0.
CreateSolidBrush is a GDI resource leak. After creating brush or other GDI objects, you have to destroy those objects, otherwise you have resource leak, the program will crash after 10,000 GDI leaks.
In your draw function you have DeleteObject(hBitmap) This will destroy the bitmap immediately. But you probably want to keep the bitmap handle and destroy it only after the program is finished.
Suggestion:
bool DrawBitmap(HDC hdc, HBITMAP hBitmap, int posX, int posY, int sizeX, int sizeY)
{
HDC memdc = CreateCompatibleDC(hdc);
HGDIOBJ oldbmp = SelectObject(memdc, hBitmap);
BitBlt(hdc, posX, posY, sizeX, sizeY, memdc, 0, 0, SRCCOPY);
SelectObject(memdc, oldbmp);
DeleteDC(memdc);
return true;
}
...
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HBRUSH hbrush = CreateSolidBrush(RGB(255, 0, 0));
FillRect(hdc, &ps.rcPaint, hbrush);
DeleteObject(hbrush);
BITMAP bm1;
GetObject(bitmap1, sizeof(bm1), &bm1);
DrawBitmap(hdc, bitmap1, 0, 0, bm1.bmWidth, bm1.bmHeight);
int x = bm1.bmWidth;
int y = bm1.bmHeight;
BITMAP bm2;
GetObject(bitmap2, sizeof(bm2), &bm2);
DrawBitmap(hdc, bitmap2, x, y, bm2.bmWidth, bm2.bmHeight);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
DeleteObject(bitmap1);
DeleteObject(bitmap2);
PostQuitMessage(0);
return 0;
How can I stop displaying the bitmap in my Win32 Project. I have a method that I'm calling in WM_PAINT called LoadBitmap. It's code looks like this:
bool LoadBitmap(LPTSTR szfilename, HDC winhdc, int x, int y) {
HBITMAP bitmap;
bitmap = (HBITMAP)LoadImage(NULL, szfilename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (bitmap == NULL) {
::MessageBox(NULL, _T("Bitmap failed"), NULL, MB_OK);
return false;
}
HDC hdc;
hdc = ::CreateCompatibleDC(winhdc);
if (hdc == NULL)
{
::MessageBox(NULL, _T("HDC FAILED"), NULL, MB_OK);
return false;
}
BITMAP qbitmap;
int ireturn = GetObject(reinterpret_cast<HGDIOBJ>(bitmap), sizeof(BITMAP), reinterpret_cast<LPVOID>(&qbitmap));
if (!ireturn) {
::MessageBox(NULL, _T("RETURN FAILED"), NULL, MB_OK);
return false;
}
HBITMAP holdbitmap = (HBITMAP)::SelectObject(hdc, bitmap);
if (holdbitmap == NULL) {
::MessageBox(NULL, _T("HOLD FAILED"), NULL, MB_OK);
return false;
}
BOOL qRetBlit = ::BitBlt(winhdc, x, y, qbitmap.bmWidth, qbitmap.bmHeight, hdc, 0, 0, SRCCOPY);
if (!qRetBlit)
{
::MessageBox(NULL, _T("BLIT FAILED"), NULL, MB_OK);
return false;
}
::SelectObject(hdc, holdbitmap);
::DeleteDC(hdc);
::DeleteObject(bitmap);
return true;
}
NOTE x and y change continuously.
When x and y change however, the previous instance of them stays behind.
How can I stop displaying a bitmap once it has been painted in a new position?
If there is background brush then the old bitmap is erased with each paint call. If there is no background then erase it manually for example with FillRect Here is example with double buffering, assumes there is no background brush.
case WM_PAINT:
{
...
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP membitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
HGDIOBJ oldbitmap = SelectObject(memdc, membitmap);
//double-buffer ready, do custom paintings here:
FillRect(memdc, &rc, GetSysColorBrush(COLOR_3DFACE));
LoadBitmap(filename, memdc, x, y);
//BitBlt to hdc
BitBlt(hdc, 0, 0, rc.right, rc.bottom, memdc, 0, 0, SRCCOPY);
//cleanup:
SelectObject(hdc, oldbitmap);
DeleteObject(membitmap);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
return 0;
}
Edit *************
It will be faster to load the bitmap file only once. For each paint request, we need only to draw the bitmap, instead of LoadImage every time + draw.
You can declare HBITMAP handles as static values in window's procedure. Set it up once in WM_CREATE, and clean it up in WM_NCDESTROY. Now we can use them anywhere in side window procedure:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
static HBITMAP hbitmap_background = NULL;
static HBITMAP hbitmap_sprite = NULL;
switch (msg)
{
case WM_CREATE:
{
hbitmap_background = (HBITMAP)LoadImage(NULL,
L"background.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (!hbitmap_background)
OutputDebugStringW(L"!hbitmap_background\n");
hbitmap_sprite = (HBITMAP)LoadImage(NULL,
L"sprite.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (!hbitmap_sprite)
OutputDebugStringW(L"!hbitmap_sprite\n");
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
//setup double-buffering:
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP membitmap = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
HGDIOBJ oldbitmap = SelectObject(memdc, membitmap);
//double-buffer ready, do custom paintings here:
FillRect(memdc, &rc, GetSysColorBrush(COLOR_3DFACE));
DrawBitmap(hbitmap_background, memdc, 0, 0);
DrawBitmap(hbitmap_sprite, memdc, 0, 0);
//BitBlt to hdc
BitBlt(hdc, 0, 0, rc.right, rc.bottom, memdc, 0, 0, SRCCOPY);
//cleanup:
SelectObject(hdc, oldbitmap);
DeleteObject(membitmap);
DeleteDC(memdc);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_NCDESTROY:
{
//cleapup bitmap handles
if (hbitmap_background)
DeleteObject(hbitmap_background);
if (hbitmap_sprite)
DeleteObject(hbitmap_sprite);
}
}
return DefWindowProc(hwnd, msg, wp, lp);
}
We can change DrawBitmap so it only needs HBITMAP handle
void DrawBitmap(HBITMAP hbitmap, HDC hdc, int x, int y)
{
if (!hbitmap)
{
OutputDebugStringW(L"error\n");
return;
}
BITMAP bm;
GetObject(hbitmap, sizeof(BITMAP), &bm);
HDC memdc = CreateCompatibleDC(hdc);
HGDIOBJ oldbitmap = SelectObject(memdc, hbitmap);
BitBlt(hdc, x, y, bm.bmWidth, bm.bmHeight, memdc, 0, 0, SRCCOPY);
SelectObject(memdc, oldbitmap);
DeleteDC(memdc);
}
Note, in this example I assume background.bmp is very large, and sprite.bmp is very small. If it is painted the other way around then background will hide the sprite.
I'm trying to create a Gradient Brush in windows mobile as follows:
HBITMAP hBitmap = CreateBitmap(16, 16, 1, 16, NULL);
HDC hDC = CreateCompatibleDC(NULL);
HBITMAP hPrevious = SelectObject(hDC, hBitmap);
TRIVERTEX vert[2];
GRADIENT_RECT gRect;
//... fill in vert and gRect
GradientFill(hDC, vert, 2, &gRect, 1, GRADIENT_FILL_RECT_V);
SelectObject(hDC, hPrevious);
Delete(hDC);
HBRUSH hPatternBrush = CreatePatternBrush(hBitmap);
HDC hDC = BeginPaint(hWnd, &ps);
SelectObject(hDC, hPatternBrush);
RoundRect(hDC, ...);
EndPaint(hWND, &ps);
Draw the bitmap without pattern brush
HDC hdcSrc = CreateCompatibleDC(NULL);
HGDIOBJ hbmOld = SelectObject(hdcSrc, hBitmap);
BitBlt(hDC, ..., hdcSrc, ..., SRCCOPY);
SelectObject(hdcSrc, hbmOld);
DeleteDC(hdcSrc);
This code create a round rect with a black background, not the pattern brush. I can draw the hBitmap which is used to create the brush and it draws the gradient. Anyone got a solution?