I have a little modified QScreen::grabWindow function. And on some computers, unfortunately I didn't find relationship between them, BitBlt freezes for even minutes! Why this can happen and what can I do?
QPixmap DetectionFlow::grabScreen(HWND h)
{
RECT rect;
GetClientRect(h, (LPRECT)&rect);
// get the height and width of the screen
int height = rect.bottom - rect.top;
int width = rect.right - rect.left;
// Create and setup bitmap
HDC display_dc = GetDC(0);
HDC bitmap_dc = CreateCompatibleDC(display_dc);
HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height);
HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
HDC window_dc = GetDC(h);
BitBlt(bitmap_dc, 0, 0, width, height, window_dc, 0, 0, SRCCOPY);
// clean up all but bitmap
ReleaseDC(h, window_dc);
SelectObject(bitmap_dc, null_bitmap);
DeleteDC(bitmap_dc);
const QPixmap pixmap = qt_pixmapFromWinHBITMAP(bitmap);
DeleteObject(bitmap);
ReleaseDC(0, display_dc);
return pixmap;
}
PS. What is interesting, on computers, where freezes, it freezes randomly. So usually there it works fast (a couple of ms) and then freeze.
Problem was in Aero. Computers that freezes with BitBlt has Win7 and Aero. Without Aero all ok.
Related
As the title says I'm unable to FillRect bitmap to be transparent. I know when creating the bitmap it is not monochrome as gray brush works fine but I have no way (that I'm aware of) to check if it is colored or grayscale. I also am aware that by default the bitmap is black hence why I'm trying to change it to transparent. I am also aware that I'm likely not cleaning up the dc's correctly however that is not the main issue. I'm trying to solve the black background by making it transparent.
#include<windows.h>
#include<iostream>
int main()
{
// Init DC
HWND Wnd = GetDesktopWindow();//GetConsoleWindow();
HDC ScreenDC = GetDC(Wnd);
// Init Rectangle
RECT ClientRect;
GetClientRect(Wnd, &ClientRect);
// Init Double Buffer
HDC MemDC = CreateCompatibleDC(ScreenDC);
HBITMAP MemBM = CreateCompatibleBitmap(ScreenDC, ClientRect.right - ClientRect.left, ClientRect.bottom - ClientRect.top);
HBITMAP OldBM = (HBITMAP)SelectObject(MemDC, MemBM);
// Create Brush and Pen
HPEN Pen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
HBRUSH ClearBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
// Set Brush and Pen
SelectObject(MemDC, Pen);
SelectObject(MemDC, ClearBrush);
POINT p;
while(!GetAsyncKeyState(VK_RETURN))
{
// Clear and Draw
GetCursorPos(&p);
FillRect(MemDC, &ClientRect, ClearBrush);
Rectangle(MemDC, p.x, p.y, p.x+20, p.y+20);
BitBlt(ScreenDC, 0, 0, ClientRect.right - ClientRect.left, ClientRect.bottom + ClientRect.left, MemDC, 0, 0, SRCCOPY);
}
SelectObject(MemDC, OldBM);
DeleteObject(ClearBrush);
DeleteObject(Pen);
DeleteObject(OldBM);
DeleteObject(MemBM);
DeleteDC(MemDC);
ReleaseDC(Wnd, ScreenDC);
return 0;
}
I've tried many different ways of setting transparent background to no avail. The end result is a rectangle appearing over the mouse and following it across the screen however the background shouldn't be black I should be able to see other windows.
I have a piece of code here which takes a "screenshot" of the display with HDC (with a high resolution aware program):
HDC hdc = GetDC(NULL);
HDC hDest = CreateCompatibleDC(hdc);
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
HBITMAP hbDesktop = CreateCompatibleBitmap( hdc, width, height);
SelectObject(hDest, hbDesktop);
BitBlt(hDest, 0,0, width, height, hdc, 0, 0, SRCCOPY);
Problem is, on higher resolutions displays such as mine (2736x1824), the bitmap image is very large coming in at around 14MB; and I certainly do not need that high of a resolution and would like to downscale it to a more reasonable size of around 1MB if possible as I want to send it over a TCP connection. I am very new to HDC so cut me some slack. Thanks!
You could try to use StretchBlt to resize the bitmap.
Refer to the Doc:Scaling an Image
I am no expert in anything GDI. But I was given some code YEARS ago which has served me decently well. But, it's getting old ... and with new windows10 dark theme, it's showing it's flaws.
I am rendering a menu (in an explorer menu plugin). Here's the snippet of code used to generate the bitmap.
My goal, to convert this code to generate a bitmap with transparency of the icon preserved.
(the result HBITMAP ends up in pItem->m_hBitmap )
HICON hIcon;
if ( (iIndex >= 0) && (ExtractIconEx(iconDLLPath, iIndex, NULL, &hIcon, 1) != 0) )
{
HDC hdc = CreateIC(L"DISPLAY", NULL, NULL, NULL);
HDC hdcMem = CreateCompatibleDC(hdc);
// XP demands 12x12, otherwise use 16x16
int cx = GetSystemMetrics((m_bUseSmallerIcons) ? SM_CXMENUCHECK : SM_CXSMICON);
int cy = GetSystemMetrics((m_bUseSmallerIcons) ? SM_CYMENUCHECK : SM_CYSMICON);
pItem->m_hBitmap = CreateCompatibleBitmap(hdc, cx, cy);
HBITMAP hBmOld = (HBITMAP) SelectObject(hdcMem, pItem->m_hBitmap);
// DC: paint entire mem dc COLOR_MENU so icon looks transparent
// when painted into context menu having this background color
HBRUSH hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = cx;
rect.bottom = cy;
FillRect(hdcMem, &rect, hBrush);
DeleteObject(hBrush);
// Draw icon transparently, on top of the background color. Transparent
// areas will be the background color.
DrawIconEx(hdcMem, 0, 0, hIcon, cx, cy, 0, 0, DI_NORMAL);
// Cleanup
SelectObject(hdcMem, hBmOld);
DeleteDC(hdc);
DeleteDC(hdcMem);
DestroyIcon(hIcon);
}
I should remove the which draws the white background, but how do I put down a transparent background? Everything I've tried yields a black background.
* just removing the white "fill"
* SetBkMode(TRANSPARENT)
* using the theme code to get the menu color...
How do I go about making a proper bitmap with transparency?
Ultimately, the answer is that I am doing nothing wrong. This menu is rendered as part of a menu constructed in a shell plugin using IContextMenu.
The answer - implement IContextMenu2. And in doing so, do an owner draw menu item.
I'm a little confused on how to double buffer this. I'm
not sure if I need to create another CreateCompatibleBitmap or CreateCompatibleDC and how to link it all.
This works as is but I don't think its double buffered right.
void __OnPaint(HWND hWnd, HDC _hdc = nullptr)
{
HDC hdc = _hdc;
PAINTSTRUCT paint;
RECT& rcClient = paint.rcPaint;
if (!_hdc)
hdc = BeginPaint(hWnd, &paint);
else
GetClientRect(hWnd, &rcClient);
if (hdc)
{
int width = rcClient.right - rcClient.left;
int height = rcClient.bottom - rcClient.top;
HDC hDCMem = CreateCompatibleDC(_hdc);
HBITMAP hBitmapMem = CreateCompatibleBitmap(hDCMem, width, height);
SelectObject(hDCMem, hBitmapMem);
Rectangle(hDCMem, 0, 0, width, height);
BLENDFUNCTION bfn;
bfn.BlendOp = AC_SRC_OVER;
bfn.BlendFlags = 0;
bfn.AlphaFormat = 0;
bfn.SourceConstantAlpha = 0x50;
AlphaBlend(hdc, 0, 0, width, height, hDCMem, 0, 0, width, height, bfn);
SetTextColor(hdc, RGB(255, 0, 0));
SetBkMode(hdc, TRANSPARENT);
DrawText(hdc, "Your text here", -1, &rcClient, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
//BitBlt(hdc, 0, 0, width, height, hDCMem, 0, 0, SRCCOPY);
DeleteDC(hDCMem);
DeleteObject(hBitmapMem);
}
if (!_hdc)
EndPaint(hWnd, &paint);
}
Also i found i have another problem with this.
i move my window in WM_TIMER, i call my __onpaint, problem im having is that it does not redraw it has something todo with the alphaBlend, it keeps what ever was under the window at the time of 1st draw, since it worked before i was using that
double buffering is to do all your printing and drawing on a temporary bitmap, that should be stored somewhere. the drawings on that bitmap can happen outside of WM_PAINT event (eg: on Adding items or selection change).
then on WM_PAINT event, the only thing you have to do is to project that bitmap to the window via BitBlt function or similar functions.
the way you are using AlphaBlend is wrong. AlphaBlend is used to draw images that have an AlphaChanel over an existing image as an overlay.
So, I'm very new to WinAPI and I've succeeded in loading a sprite which I can move with the arrow keys. My teacher told me to be very careful with 'memory leaks' and I'm not sure how to free memory the right way because C++ has done it for me when I was doing console programming. I ran the program and it froze my computer for a minute after moving the character for a while so I guess I've done something wrong. Can you tell me how to properly free memory (if I've done it wrong) and if there's anything bad with my bitmap method that needs optimising? Thanks! Here's the code.
void LoadAndBlitBitmap(LPCSTR szFileName, HWND hwnd, HDC winDC, int xPos, int yPos)
{
//DEFINITIONS
/*TransparentBlt(destDC, destXpos, destYpos, sizeX, sizeY, srcDC, 0, 0, sizeX, sizeY, RGB(a, b, c)) = TRANSPARENT BITMAP BLIT. Ex. RGB(255, 255, 255) if background is white*/
/*BitBlt(destDC, destXpos, destYpos, sizeX, sizeY, srcDC, 0, 0, SRCCOPY); = SOLID BITMAP BLIT WITH NO TRANSPARENCY*/
//END DEFINITIONS
//-----Get the size of the window---------
RECT rect;
int win_width = 0;
int win_height = 0;
if(GetWindowRect(hwnd, &rect))
{
win_width = rect.right - rect.left; //Subtracting the right coordinate with the left gives the width
win_height = rect.bottom - rect.top; //Subtracting the bottom coordinate with the top gives the height
}
//----------------------------------------
HDC hdcMem = CreateCompatibleDC(winDC); //Create the DC that will hold the off-screen printing. (Double Buffering "Anti-Flicker" Method)
HBITMAP hbmMem = CreateCompatibleBitmap(winDC, win_width, win_height); //Create the bitmap with the size of the window
HANDLE hOld = SelectObject(hdcMem, hbmMem); //Set the paintable bitmap to the off-screen DC
//Draw to the off-screen DC
HBITMAP bitmap = (HBITMAP)LoadImage(NULL, szFileName, IMAGE_BITMAP, 69, 69, LR_LOADFROMFILE); //Load the .bmp from a file
HDC blockDC = CreateCompatibleDC(NULL); //Create a DC to hold the .bmp
SelectObject(blockDC, bitmap); //Select the .bmp to the DC
TransparentBlt(hdcMem, xPos, yPos, 69, 69, blockDC, 0, 0, 69, 69, RGB(255, 255, 255)); //Blit the .bmp to the DC*/
//Transfer off-screen DC to the screen
BitBlt(winDC, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY);
// Uninitialize and deallocate resources
SelectObject(hdcMem, hOld);
DeleteDC(hdcMem);
SelectObject(blockDC, hOld);
DeleteDC(blockDC);
DeleteObject(bitmap);
}
Two things are wrong:
SelectObject(blockDC, hOld);
hOld did not come from blockDC, it came from hdcMem. You are not even saving the old bitmap from blockDC. Change to:
HBITMAP hOld2 = SelectObject(blockDC, bitmap);
// and
SelectObject(blockDC, hOld2);
Secondly, you are not deleting hbmMem anywhere. At the bottom, add:
DeleteObject(hbmMem);
Actually, a third thing is wrong too - you're not checking for failure in any of the API calls you make. You should check if things like CreateCompatibleDC return NULL, and abort and cleanup if so.