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?
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);
is CreateDIBitmap everytime in WM_PAINT is necessary? I thought if I give it apointer to my buffer
it will be updated - but it looks it copies the data. The CreateDIBitmap - takes a lot of performance. CreateBitmap a little less.. The code below works - is it possible to put CreateDIBitmap outside of the loop (MY_BUFFER is updated every frame) - I tried to put it outside but I didn't see the content of MY_BUFFER anymore, just the DrawText with FPS..
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HDC hdc_mem = CreateCompatibleDC(hdc);
HBITMAP hbm_mem = CreateDIBitmap(hdc, &bitmapheader, CBM_INIT, &MY_BUFFER, &bitmapinfo, DIB_RGB_COLORS); // < --- killing performance !!!
SelectObject(hdc_mem, hbm_mem);
// draw FPS
TCHAR tmp_fps_buff[16];
ZeroMemory(&tmp_fps_buff, sizeof(tmp_fps_buff));
swprintf_s(tmp_fps_buff, L"fps: %.3f", APP_fps);
SetTextColor(hdc_mem, RGB(255, 255, 255));
SetBkColor(hdc_mem, RGB(0, 0, 0));
SetBkMode(hdc_mem, OPAQUE);
DrawText(hdc_mem, tmp_fps_buff, -1, &APP_fps_rect, DT_SINGLELINE | DT_NOCLIP);
// ---
BitBlt(hdc, 0, 0, APP_requested_width, APP_requested_height, hdc_mem, 0, 0, SRCCOPY);
DeleteObject(hbm_mem);
DeleteDC(hdc_mem);
EndPaint(hWnd, &ps);
}
break;
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'm creating a program using the Win32 API, and I need to create a new bitmap filled with one color and with given dimensions.
Here is my code:
m_hBitmap =( HBITMAP ) CreateCompatibleBitmap(hDC, iWidth, iHeight);
HDC hDCn = CreateCompatibleDC( hDC );
SelectObject( hDCn, m_hBitmap );
ExtFloodFill(hDCn, 0, 0, crColor, FLOODFILLSURFACE);
DeleteDC( hDCn );
The bitmap dimensions match, but the bitmap is always black regardless of the crColor parameter.
Use FillRect() instead of ExtFloodFill().
Also, you need to de-select the bitmap before you delete the DC, otherwise the original bitmap created and selected into the DC by CreateCompatibleDC() will be leaked.
Try this:
m_hBitmap = CreateCompatibleBitmap(hDC, iWidth, iHeight);
HDC hDCn = CreateCompatibleDC(hDC);
HBITMAP hOld = (HBITMAP) SelectObject(hDCn, m_hBitmap); // <-- SAVE OLD BITMAP!
//ExtFloodFill(hDCn, 0, 0, crColor, FLOODFILLSURFACE);
RECT r;
r.left = r.top = 0;
r.right = iWidth;
r.bottom = iHeight;
HBRUSH hBrush = CreateSolidBrush(crColor);
FillRect(hDCn, &r, hBrush);
DeleteObject(hBrush);
SelectObject(hDCn, hOld); // <-- RESTORE OLD BITMAP
DeleteDC(hDCn);
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