I'm currently screenshotting windows using the following lines of code:
UpdateWindow(hwnd);
HDC window_dc = GetDC(hwnd);
HDC res = CreateCompatibleDC(window_dc);
RECT r;
GetClientRect(hwnd, &r);
HBITMAP bmp = CreateCompatibleBitmap(window_dc, r.right - r.left, r.bottom - r.top);
SelectObject(res, bmp);
PrintWindow(hwnd, res, PW_CLIENTONLY);
DeleteObject(bmp);
ReleaseDC(hwnd, window_dc);
Now is there a way, to only screenshot a specific area of my HWND? I realized, that on some applications, the screenshotting takes siginificantly longer when the window is only a little bit larger. So I figured if I was able to only screenshot the important frame, I could really improve my speed.
EDIT: It needs to work for windows which are in the background, eg. overlapped by other windows.
You could try it like this:
bool ScreenShot(HWND hwnd, int x, int y, int w, int h, LPCSTR file){
HDC source = GetDC(hwnd);
HDC memory = CreateCompatibleDC(source);
HBITMAP bitmap = CreateCompatibleBitmap(source, w, h);
HBITMAP bitmapOld = (HBITMAP)SelectObject(memory, hBitmap);
BitBlt(memory, 0, 0, w, h, source, x, y, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(memory, bitmapOld);
DeleteDC(source);
DeleteDC(memory);
HPALETTE pal = NULL;
if(saveBitmap(file, bitmap, pal)) return true;
return false;
}
Related
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've been trying to rescale a bitmap without it printing the original and reprinting the rescaled image. I'm trying to use StretchBlt(), based on the MSDN Microsoft rescaling images function:
https://msdn.microsoft.com/en-us/library/windows/desktop/dd162950(v=vs.85).aspx
but that requires a secondary hdc tied to the Source, which stretching can't be done without printing the HBITMAP first. Is there a way to convert HBITMAP's into HDC's? I have been able to get HANDLE's out of HBITMAP's, which might provide a more direct route. The other thing i could do is create a resized bitmap in allocated memory (not saved) out of the standard bitmap and print that.
The standard way i print bitmaps is:
HBITMAP hBitmap;
static HANDLE hDIB = NULL;
CHAR szFileName[MAX_PATH] = "fileName.bmp";
hDIB = OpenDIB((LPSTR)szFileName);
hBitmap = BitmapFromDIB(hDIB, NULL);
DrawBitmap(hdc, x, y, hBitmap, SRCCOPY);
Another option i could try is to look into another means of displaying the bmp. I'm pretty new to win32, so I don't know any other means of accomplishing this task. Any insight into how i can rescale the BITMAP without printing it in the first place.
The link you posted (Scaling an Image) already contains code, that renders a bitmap. All you need to do is replace the call to BitBlt with StretchBlt:
BOOL DrawBitmap (HDC hDC, INT x, INT y, INT width, INT height, HBITMAP hBitmap, DWORD dwROP)
{
HDC hDCBits;
BITMAP Bitmap;
BOOL bResult;
if (!hDC || !hBitmap)
return FALSE;
hDCBits = CreateCompatibleDC(hDC);
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
SelectObject(hDCBits, hBitmap);
// Replace with StretchBlt call
//bResult = BitBlt(hDC, x, y, Bitmap.bmWidth, Bitmap.bmHeight, hDCBits, 0, 0, dwROP);
bResult = StretchBlt(hDC, x, y, width, height,
hDCBits, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, dwROP);
DeleteDC(hDCBits);
return bResult;
}
You can call this from your WM_PAINT message handler, for example:
case WM_PAINT:
{
PAINTSTRUCT ps = { 0 };
HDC hDC = ::BeginPaint( hWnd, &ps );
RECT rc = { 0 };
::GetClientRect( hWnd, &rc );
DrawBitmap( hDC, 0, 0, rc.right, rc.bottom, hBitmap, SRCCOPY );
::EndPaint( hWnd, &ps );
}
break;
I need to get a screen view of a specific window as .bmp format. In other words I need to have the functionality of alt+printscreen and it needs to work even the window is at back or minimized. So I wrote a function below which returns a HBITMAP type in order to save the view as .bmp file with another function later.
HBITMAP CaptureWindowBitmap(HWND MyHWND)
{
HDC hWindowDC = GetWindowDC(MyHWND);
HDC hMemoryDC = CreateCompatibleDC(hWindowDC);
int x = GetDeviceCaps(hWindowDC, HORZRES);
int y = GetDeviceCaps(hWindowDC, VERTRES);
HBITMAP hBitmap = CreateCompatibleBitmap(hWindowDC, x, y);
HBITMAP hBitmapOld = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
BitBlt(hMemoryDC, 0, 0, x, y, hWindowDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmapOld);
DeleteDC(hMemoryDC);
DeleteDC(hWindowDC);
return hBitmap;
}
However my function gets the image of the whole screen. How can I fix this?
Here is the solution i found from elsewhere:
HBITMAP CaptureWindowBitmap(HWND MyHWND)
{
RECT rc;
GetClientRect(MyHWND, &rc);
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen, rc.right - rc.left, rc.bottom - rc.top);
SelectObject(hdc, hbmp);
PrintWindow(MyHWND, hdc, PW_CLIENTONLY);
DeleteDC(hdc);
ReleaseDC(NULL, hdcScreen);
return hbmp;
}
Window is captured by the function below:
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
char buffer[128];
int written = GetWindowTextA(hwnd, buffer, 128);
if (written && strstr(buffer,WindowName) ) {
*(HWND*)lParam = hwnd;
return FALSE;
}
return TRUE;
}
HWND GetHwnd()
{
HWND hWnd = NULL;
EnumWindows(EnumWindowsProc, (LPARAM)&hWnd);
return hWnd;
}
where LPCTSTR WindowName is a global variable containing the name or partial name of the window.
Is there a way to receive the colour values for each pixel in the client area of a window, with gdi?
As noted in comment by #JerryCoffin. Here's a simple example
hDC = GetDC(hwnd);
hBitmap = CreateCompatibleBitmap(hDC, width, height);
hMemDC = CreateCompatibleDC(hDC);
hOld = SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, width, height, hDC, x, y, SRCCOPY);
// Clean up
DeleteDC(hMemDC);
ReleaseDC(hwnd, hDC);
You should have a bitmap object selected into memory DC for which you can use GetPixel GDI function and then you can also extract the color values using GetRValue() , GetGValue() , and GetBValue() macros.
How can I take a screenshot of the current screen using Win32?
HDC hScreenDC = GetDC(nullptr); // CreateDC("DISPLAY",nullptr,nullptr,nullptr);
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int width = GetDeviceCaps(hScreenDC,HORZRES);
int height = GetDeviceCaps(hScreenDC,VERTRES);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC,width,height);
HBITMAP hOldBitmap = static_cast<HBITMAP>(SelectObject(hMemoryDC,hBitmap));
BitBlt(hMemoryDC,0,0,width,height,hScreenDC,0,0,SRCCOPY);
hBitmap = static_cast<HBITMAP>(SelectObject(hMemoryDC,hOldBitmap));
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);
Use GetDC(NULL); to get a DC for the entire screen.
Use CreateCompatibleDC to create a DC compatible with the screen DC.
Use CreateCompatibleBitmap to create a bitmap compatible with the screen DC to hold the result.
Use SelectObject to select the compatible bitmap into the compatible DC.
Use BitBlt to copy from the screen DC to the compatible DC.
Use SelectObject to deselect the compatible bitmap from the compatible DC.
Use DeleteDC to delete the compatible DC.
When you create the compatible bitmap, you want it compatible with the screen DC, not the compatible DC.
For example:
HDC dcScreen = GetDC(0);
HDC dcTarget = CreateCompatibleDC(dcScreen);
HBITMAP bmpTarget = CreateCompatibleBitmap(dcScreen);
HGDIOBJ oldBmp = SelectObject(dcTarget, bmpTarget);
BitBlt(dcTarget, 0, 0, cx, cy, dcDesktop, x, y, SRCCOPY | CAPTUREBLT);
SelectObject(dcTarget, oldBmp);
DeleteDC(dcTarget);
ReleaseDC(dcScreen);
The other important part is to get the size, and location, of the entire virtual screen:
int x = GetSystemMetrics(SM_XVIRTUALSCREEN); //left (e.g. -1024)
int y = GetSystemMetrics(SM_YVIRTUALSCREEN); //top (e.g. -34)
int cx = GetSystemMetrics(SM_CXVIRTUALSCREEN); //entire width (e.g. 2704)
int cy = GetSystemMetrics(SM_CYVIRTUALSCREEN); //entire height (e.g. 1050)
void GetScreenShot(void)
{
int x1, y1, x2, y2, w, h;
// get screen dimensions
x1 = GetSystemMetrics(SM_XVIRTUALSCREEN);
y1 = GetSystemMetrics(SM_YVIRTUALSCREEN);
x2 = GetSystemMetrics(SM_CXVIRTUALSCREEN);
y2 = GetSystemMetrics(SM_CYVIRTUALSCREEN);
w = x2 - x1;
h = y2 - y1;
// copy screen to bitmap
HDC hScreen = GetDC(NULL);
HDC hDC = CreateCompatibleDC(hScreen);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, w, h);
HGDIOBJ old_obj = SelectObject(hDC, hBitmap);
BOOL bRet = BitBlt(hDC, 0, 0, w, h, hScreen, x1, y1, SRCCOPY);
// save bitmap to clipboard
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, hBitmap);
CloseClipboard();
// clean up
SelectObject(hDC, old_obj);
DeleteDC(hDC);
ReleaseDC(NULL, hScreen);
DeleteObject(hBitmap);
}
Full code for saving a raw 24-bit lossless bitmap of all monitors at the current window station using Windows API:
BOOL WINAPI SaveBitmap(WCHAR *wPath)
{
BITMAPFILEHEADER bfHeader;
BITMAPINFOHEADER biHeader;
BITMAPINFO bInfo;
HGDIOBJ hTempBitmap;
HBITMAP hBitmap;
BITMAP bAllDesktops;
HDC hDC, hMemDC;
LONG lWidth, lHeight;
BYTE *bBits = NULL;
HANDLE hHeap = GetProcessHeap();
DWORD cbBits, dwWritten = 0;
HANDLE hFile;
INT x = GetSystemMetrics(SM_XVIRTUALSCREEN);
INT y = GetSystemMetrics(SM_YVIRTUALSCREEN);
ZeroMemory(&bfHeader, sizeof(BITMAPFILEHEADER));
ZeroMemory(&biHeader, sizeof(BITMAPINFOHEADER));
ZeroMemory(&bInfo, sizeof(BITMAPINFO));
ZeroMemory(&bAllDesktops, sizeof(BITMAP));
hDC = GetDC(NULL);
hTempBitmap = GetCurrentObject(hDC, OBJ_BITMAP);
GetObjectW(hTempBitmap, sizeof(BITMAP), &bAllDesktops);
lWidth = bAllDesktops.bmWidth;
lHeight = bAllDesktops.bmHeight;
DeleteObject(hTempBitmap);
bfHeader.bfType = (WORD)('B' | ('M' << 8));
bfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
biHeader.biSize = sizeof(BITMAPINFOHEADER);
biHeader.biBitCount = 24;
biHeader.biCompression = BI_RGB;
biHeader.biPlanes = 1;
biHeader.biWidth = lWidth;
biHeader.biHeight = lHeight;
bInfo.bmiHeader = biHeader;
cbBits = (((24 * lWidth + 31)&~31) / 8) * lHeight;
hMemDC = CreateCompatibleDC(hDC);
hBitmap = CreateDIBSection(hDC, &bInfo, DIB_RGB_COLORS, (VOID **)&bBits, NULL, 0);
SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, lWidth, lHeight, hDC, x, y, SRCCOPY);
hFile = CreateFileW(wPath, GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
WriteFile(hFile, &bfHeader, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
WriteFile(hFile, &biHeader, sizeof(BITMAPINFOHEADER), &dwWritten, NULL);
WriteFile(hFile, bBits, cbBits, &dwWritten, NULL);
CloseHandle(hFile);
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
DeleteObject(hBitmap);
return TRUE;
}
There is a MSDN sample, Capturing an Image, for capturing an arbitrary HWND to a DC (you could try passing the output from GetDesktopWindow to this). But how well this will work under the new desktop compositor on Vista/Windows 7, I don't know.