The bitmap will not display when the left mouse button is clicked.
I'm using visual c++ express
HDC DC, memDC;
switch(message) {
case WM_CREATE:
hBit1 = LoadBitmap(hInst, "C:\New folder (2)\MyBP1.bmp");
break;
case WM_LBUTTONDOWN:
DC = GetDC(hwnd);
memDC = CreateCompatibleDC(DC);
SelectObject (memDC, hBit1);
BitBlt(DC, LOWORD(lParam), HIWORD(lParam), 27, 59, memDC, 0, 0, SRCCOPY);
ReleaseDC(hwnd, DC);
DeleteDC(memDC);
break;
I'm not sure exactly whats wrong with the code.
Theres no errors displayed in visual c++ express
If the code is all wrong would someone point me in the right direction
LoadBitmap is only for loading bitmaps that are stored as resources in your executable or a DLL.
To load a bitmap from a BMP file, you want to use LoadImage with the LR_LOADFROMFILE flag, something like this:
bmp = (HBITMAP)LoadImage(0, filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
As an aside, you probably don't really want to do the displaying directly in response to WM_LBUTTONDOWN. Instead, you normally want to record the position, possibly set a flag to indicate that the bitmap should be displayed, and call InvalidateRect to invalidate your client rectangle -- then do the actual drawing in response to WM_PAINT.
Related
It seems the flickering is generated by the CombineRgn function, but I really have no idea why this happens, since i've never used regions that much I'm possibly missing some knowledge on the matter.
Some events in the program triggers the addition of little rectangles to the main region, here's the code that handles that:
HRGN ActualRegion = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hwnd, ActualRegion);
HRGN AddedRect = CreateRectRgn(//long code that creates a rectangle)
CombineRgn(ActualRegion, ActualRegion, AddedRect, RGN_OR);
SetWindowRgn(hwnd, ActualRegion, FALSE);
InvalidateRect(hwnd, NULL, FALSE);
White Flickering appears only after the invalidation if new regions where combined to the main one.
Here's how I'm implementing double buffering in WM_PAINT:
PLEASE NOTE that on creation i'm enabling the DWM blur behind function with an invalid region (different from the Main one) which means that everything painted with BLACK_BRUSH will result in a 100% "invisible" portion of the program
RECT r; GetClientRect(hwnd, &r);
PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps);
HDC MemDc = CreateCompatibleDC(hdc);
HBITMAP hBmp = CreateCompatibleBitmap(hdc, r.right, r.bottom);
HBITMAP hOld = (HBITMAP)SelectObject(MemDc, hBmp);
//Making sure this dc is filled with "invisible" pixels to display
SelectObject(MemDc, GetStockObject(BLACK_BRUSH));
Rectangle(MemDc, //arbitrary values that matches the entire screen);
BitBlt(hdc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), MemDc, 0, 0, SRCCOPY);
//clean-up
SelectObject(MemDc, hOld);
DeleteObject(hBmp);
DeleteDC(MemDc);
EndPaint(hwnd, &ps);
WM_ERASEBKGND obviously returns TRUE without further handling, the WNDCLASSEX instance of the window has a default BLACK_BRUSH as the hbrBackground field.
I also tried to intercept and return TRUE from WM_NCPAINT message.
I'm doing everything necessary to avoid intermediate drawcalls, everything handled inside the WM_PAINT uses a backbuffer, also i'd like to mention i'm not working with images/bitmaps. Everything is drawn with gdi/gdi+, and in no place i'm actually issuing a "white" redraw that may possibly cause said flicker. I'm a bit lost here
Is it something that i'm possibly missing ? I can't really understand what may be causing white flickering in this scenario
The problem is not the CombineRgn function but the SetWindowRgn function which you call before the system draws the window for the first time. If you call SetWindowRgn after the first draw, no flicker. Unfortunatelly I don't know why. So, a way to counter that is to set the window region after the first draw (take the code that sets window region from WM_CREATE and leave only the DwmEnableBlurBehindWindow):
static int stc = 0;
//in WM_PAINT after the EndPaint(hwnd, &ps); add
HRESULT lr = DefWindowProc(hwnd, message, wParam, lParam);
if( stc == 0 ){
OnlyOnce();
stc++;
}
return lr;
and the OnlyOnce:
void OnlyOnce(void){
int X_Screen = GetSystemMetrics(SM_CXSCREEN);
int Y_Screen = GetSystemMetrics(SM_CYSCREEN);
HRGN ActualRegion = CreateRectRgn(X_Screen - 100, Y_Screen - 100, X_Screen - 100 + 40, Y_Screen - 100 + 40);
SetWindowRgn(hWnd, ActualRegion, true);
return;
}
I'm trying to write a program that grabs a screen shot of the current active window and then does some basic computer vision with the result. The current code I have is as follows:
RECT rc;
HWND hwnd = GetForegroundWindow(); //the window can't be min
if(hwnd == NULL) {
cerr << "it can't find any 'note' window" << endl;
system ("pause");
return 0;
}
GetClientRect(hwnd, &rc);
//create
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, rc.right - rc.left, rc.bottom - rc.top);
SelectObject(hdc, hbmp);
//Print to memory hdc
PrintWindow(hwnd, hdc, PW_CLIENTONLY);
//Get the bitmap
BITMAP bm;
GetObject (hbmp, sizeof(bm), &bm);
//release
DeleteDC(hdc);
DeleteObject(hbmp);
ReleaseDC(NULL, hdcScreen);
After everything's said an done, bm.bmBits is NULL. This is especially strange because all of bm's other fields are correct. Obviously I can't do very much without bm.bmBits. Could anyone tell me what I'm doing wrong?
This code is mostly copied from the answer to another question, but in that code handle to a bitmap was never transferred into a BITMAP struct, but instead was used to add the bitmap to the clipboard. Since I want to process the screenshot immediately, I'd rather extract the data within the handle immediately and process it.
bm.bmBits is NULL because you don't own the memory referenced by the bitmap. You need to have the kernel copy them for you, using GetDIBits or the like.
I'm trying to make a layered window with c++ win32 but I'm having a problem with the drawing or "collision" of it
For reference the picture that I'm trying to display.
This is the basic creation of the window
//window
DWORD exFlags = 0;
if(m_bTransparent)
exFlags |= WS_EX_LAYERED;
Create(WS_POPUP, exFlags);
std::wstring sPic(L"power-disconnected.png");
m_pAlertPic = m_pPowerMon->GetGPPicMan()->LoadPicture(sPic.c_str());
// make the window layered when using transparency
if(m_bTransparent && m_pAlertPic != nullptr)
{
HDC hdcScreen = GetDC(GetHandle());
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmpold = (HBITMAP)SelectObject(hdc, m_pAlertPic->GetBuffer());
POINT dcOffset = {0, 0};
SIZE size = {ww, wh};
BLENDFUNCTION bf = {AC_SRC_OVER, 0, (int) (2.55 * 100), AC_SRC_ALPHA}; // blend function combines opacity and pixel based transparency
UpdateLayeredWindow(GetHandle(), hdcScreen, NULL, &size, hdc, &dcOffset, RGB(255, 255, 255), &bf, ULW_ALPHA);
SelectObject(hdc, hbmpold);
DeleteDC(hdc);
ReleaseDC(GetHandle(), hdcScreen);
}
and the message loop
int WindowAlert::WndProc(Gnaq::WindowBase* pWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CLOSE:
Hide();
return 1;
case WM_PAINT:
// only draw when the widow is not transparent
// layered window redraw them self
if(!m_bTransparent)
m_pCanvas->Draw(m_pGraphics);
break;
case WM_LBUTTONUP:
pWnd->Hide();
m_bDismised = true;
break;
}
return DefWindowProcW(pWnd->GetHandle(), msg, wParam, lParam);
}
So this is the result I get
As you can see with this method I'm getting white borders where is should actually be fully transparent, but the semi transparent parts do work correctly.
here's what I've tried that gave me a "useful" change.
First I just tried to add the ULW_COLORKEY flag to hide the white color
UpdateLayeredWindow(GetHandle(), hdcScreen, NULL, &size, hdc, &dcOffset, RGB(255, 255, 255), &bf, ULW_ALPHA | ULW_COLORKEY);
And the result.
So this hides the white border but also all the white in the picture.
Next thing I've tried was using SetLayeredWindowAttributes in combination of UpdateLayeredWindow, without the ULW_COLORKEY flag
SetLayeredWindowAttributes(GetHandle(), 0xFFFFFF00, 255, LWA_COLORKEY);
Also in the window proc enable the paint, like this
case WM_PAINT:
m_pCanvas->Draw(m_pGraphics);
break;
This way I'm visually getting what I want like this
But the problem with his approach is that it the complete window is click able while with just using the UpdateLayeredWindow only the parts that should be fully transparent are click able like it should be. I also have the feeling with this last approach that it is more a "hack" than a decent approach.
So i hope that someone can tell me what I'm doing wrong.
The first way was the correct. The fault was in the bitmap, which didn't had premultiplied alpha
INTRODUCTION:
I have decided to make a test project in MS Visual Studio 2008, in C++ to test a small program in pure WIN32, regarding painting a bitmap as window's background.
PROBLEM:
Window should have gray brush and a bitmap picture stretched over its client area.
In my code for WM_PAINT, if I try to paint gray brush for window, without bitmap, everything seems to work well.
Same goes if I just try to put a bitmap as a background.
Yet, when I combine these two, so I can get a bitmap picture stretched, and gray background behind bitmap, this is what happens:
Bitmap picture appears to "stands still", but gray brush appears over entire window for a second, then disappears entirely, so only stretched bitmap is seen, and then appears again, and so on.
It seems as if it is drawn from top going to the bottom, and it seems as if application is doing it all over again.
This is how I see it when I start my program.
RELEVANT INFORMATION:
The program was made by choosing option File->New, and then choosing Win32 project from the options.
Window class was set automatically, and the following members were set like this:
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
I have added static global variable to store bitmap handle:
static HBITMAP bmp;
In the window procedure, made by the wizard, I have initialized it with following code:
case WM_CREATE:
bmp = LoadBitmap( hInst, MAKEINTRESOURCE(IDB_BITMAP1) );
return 0;
break;
In the window procedure, made by the wizard, I have added WM_PAINT handler, with following code:
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
RECT r;
GetClientRect( hWnd, &r );
// TODO: Add any drawing code here...
// fill client area with gray brush, just to test
FillRect( hdc, &r, (HBRUSH)GetStockObject( GRAY_BRUSH ) );
// memory DC for double buffering
HDC MemDC = CreateCompatibleDC( hdc );
// select our bitmap into memory DC
HBITMAP old = (HBITMAP)SelectObject( MemDC, bmp );
// get bitmap's width and height so we can stretch it
BITMAP b;
GetObject( bmp, sizeof(BITMAP), &b );
// stretch our bitmap
StretchBlt( hdc, 0, 0, r.right - r.left, r.bottom - r.top,
MemDC, 0, 0, b.bmWidth, b.bmHeight, SRCCOPY );
// perform proper cleanup
SelectObject( MemDC, old );
DeleteDC(MemDC);
EndPaint(hWnd, &ps);
}
return 0L;
break;
I have also invalidated client area when window is resized, or erasing of background happens, like this:
case WM_ERASEBKGND:
InvalidateRect( hWnd, NULL, TRUE );
return 1L;
break;
case WM_SIZE:
InvalidateRect( hWnd, NULL, TRUE );
return 0L;
Bitmap is destroyed like this:
case WM_DESTROY:
DeleteObject( bmp );
PostQuitMessage(0);
break;
IMPORTANT NOTE:
Even if I comment out handlers for WM_SIZE and WM_ERASEBKGND, the effect still occurs.
I do not have much experience with double buffering, but this is simple thing to do.
I just fail to see the mistake, so I ask more experienced and skillfull colleagues to help.
If additional source code is required, ask for it and I will post it, but until then I will omit it to keep the question brief.
You should not call InvalidateRect in WM_ERASEBKGND. That's just going to force an endless series of paint cycles.
The job of WM_ERASEBKGND is to paint the background, and that's all you should ever do. If your WM_PAINT is going to paint the entire window, then there's no need to paint any background. In which case, and I think this is your scenario, you should do nothing in WM_ERASEBKGND.
I have the following code in the WM_PAINT message handler of the main window :
void BossController::paint ( HWND hwnd, HBITMAP skin )
{
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint ( hwnd, &ps );
HDC dcSkin = CreateCompatibleDC ( hdc ); // memory dc for skin
HDC hMemDc = CreateCompatibleDC ( hdc ); // memory dc for painting
HBITMAP hmemBmp = CreateCompatibleBitmap ( hdc, width, height ); // Create bitmap to draw on
HBITMAP hOldMemBmp = (HBITMAP)SelectObject ( hMemDc, hmemBmp ); // select memory bitmap in memory dc
HBITMAP hOldSkinBmp = (HBITMAP)SelectObject ( dcSkin, skin ); //select skin bitmap in skin memory dc
BitBlt ( hMemDc, 0, 0, width, height, dcSkin, 0, 0, SRCCOPY ); // Paint Skin on Memory DC
BitBlt ( hdc, 0, 0, width, height, hMemDc, 0, 0, SRCCOPY ); // Paint Skin on Window DC
DeleteObject ( hOldSkinBmp );
DeleteObject ( hOldMemBmp );
DeleteObject( hmemBmp );
DeleteDC ( hMemDc );
DeleteDC ( dcSkin );
EndPaint ( hwnd, &ps );
};
I will be painting text on the skin aswell, that's why I am BitBlt ing on memory DC with a memory bitmap, I have tried with straight painting(directly to hdc) as well, but none worked, and I am not sure how to debug it. The only thing I could check was to check the skin against NULL in LoadBitmap function's return value and also in the void BossController::paint ( HWND hwnd, HBITMAP skin ). And BitBlt's return value.
It always shows a rectangle with the background color I chose while creating the window. (window is a custom skinned one so, no title bar etc is there.
Can someone point out the errors if any or the potential pitfalls or maybe how to debug it ?
It looks like nobody ever answered this.
First off, I don't know what framework you're using, but there are normally a few checks before one starts painting. A typical paint handler begins something like:
RECT r;
if (GetUpdateRect(&r) == 0)
{
// Nothing to paint, exit function.
return 0;
}
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
if (hdc == 0)
{
// No display device context available, exit function.
return 0;
}
Second, I don't know what 'skin' is (an HBITMAP, but I don't know anything about how it was created, where it come from, what its dimensions and bit depth are).
Third, your cleanup is incorrect. Always call SelectObject to restore the previous selected bitmap, pen, brush, etc. Then call DeleteObject on whatever you created.
As for how to debug, if something's broken in a paint handler, you should always strip it to the bare minimum, verify functionality, and start adding in things until it breaks. In this case, I would replace all of your existing code with a simple filled rectangle in a weird color and see if that works. Then I'd do a BitBlt directly from the 'skin' bitmap on top of the funky color, and see if that works.
The most common errors with GDI programming are caused by things like using the wrong coordinates (e.g. Window coordinates instead of Client coordinates, or giving the wrong offset into your source in a BitBlt call), failure to correctly create resources (e.g. CreateCompatibleDC, then calling CreateCompatibleBitmap using the new DC, which results in a monochrome bitmap), or failure to properly clean up (e.g. the way you didn't select the old resources before disposing of your newly-created ones). It's also common to not even initiate a repaint correctly, such that your WM_PAINT handler isn't even getting hit. The first step is always a breakpoint to make sure your code is even executing!