How to create IBuffer from HBITMAP in C++? - c++

I have code, that making screenshot, and saving it to HBITMAP
GetClientRect(hwnd, &rc);
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
HBITMAP hbmp = CreateCompatibleBitmap(hdcScreen,width, height);
SelectObject(hdc, hbmp);
PrintWindow(hwnd, hdc, PW_CLIENTONLY);
Then, i want to create new Buffer (windows.storage.streams.buffer) class instance from my hbmp object
Buffer obj = new Buffer(hbmp); // HOW TO DO SOMETHING LIKE THIS ???
SoftwareBitmap^ softwareBitmap = SoftwareBitmap::CreateCopyFromBuffer(obj ,
BitmapPixelFormat::Bgra8, width, height);
The main goal - save screenshot of window to SoftwareBitmap object

Related

Can I call the DC of the hidden child dialog?

There are three child dialogs attached to the bottom of the main dialog.
When dialog1 is in the SW_SHOW state and dialog2 is in SW_HIDE, I want to bring dialog2 DC, screen and capture it.
The entire screen is captured, but if the child dialog changes, the changed dialog is captured.
Any help would be appreciated.
MainTrendGraphDlg *pDlg = (MainTrendGraphDlg*)::AfxGetMainWnd();
// MainTrendGraphDlg is dialog2
CClientDC hdcSys(pDlg);
int x_size = ::GetDeviceCaps(hdcSys, HORZRES);
int y_size = ::GetDeviceCaps(hdcSys, VERTRES);
HBITMAP hBitmap; // <-- The image represented by hBitmap
// Initialize DCs
HDC hdcMem = CreateCompatibleDC(hdcSys); // Create compatible DC
void *ptrBitmapPixels; // <-- Pointer variable that will contain the potinter for the pixels
// Create hBitmap with Pointer to the pixels of the Bitmap
BITMAPINFO bi; HDC hdc;
ZeroMemory(&bi, sizeof(BITMAPINFO));
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = x_size;
bi.bmiHeader.biHeight = -y_size; //negative so (0,0) is at top left
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 32;
hdc = GetDC(NULL);
hBitmap = CreateDIBSection(hdc, &bi, DIB_RGB_COLORS, &ptrBitmapPixels, NULL, 0);
// ^^ The output: hBitmap & ptrBitmapPixels
// Set hBitmap in the hdcMem
SelectObject(hdcMem, hBitmap);
// Set matBitmap to point to the pixels of the hBitmap
matDlgCap = Mat(y_size, x_size, CV_8UC4, ptrBitmapPixels, 0);
// ^^ note: first it is y, then it is x. very confusing
// Now update the pixels using BitBlt
BitBlt(hdcMem, 0, 0, x_size, y_size, hdcSys, 0, 0, SRCCOPY);
imwrite("ScreenShot.jpg", matDlgCap, JPEG_QUALITY);

how to mirror a HBITMAP

how to flip a HBITMAP horizontally?As an option, I thought to get an array of colors from BITMAP and write them to another BITMAP, but somehow it's too busy. Are there built-in functions or other options to do this?
You can use StretchBlt with a negative dimension as below:
HBITMAP FlipBitmapHorizontally(HBITMAP hbm) {
BITMAP bm;
GetObject(hbm, sizeof(BITMAP), &bm);
int wd = bm.bmWidth;
int hgt = bm.bmHeight;
HDC hdcScr = GetDC(NULL);
HDC hdcFlipped = CreateCompatibleDC(hdcScr);
HBITMAP hbmFlipped = CreateCompatibleBitmap(hdcScr, wd, hgt);
HGDIOBJ oldFlipped = SelectObject(hdcFlipped, hbmFlipped);
HDC hdcSrc = CreateCompatibleDC(hdcScr);
HGDIOBJ oldSrc = SelectObject(hdcSrc, hbm);
StretchBlt(hdcFlipped, wd, 0, -wd, hgt, hdcSrc, 0, 0, wd, hgt, SRCCOPY);
SelectObject(hdcSrc, oldSrc);
DeleteDC(hdcSrc);
SelectObject(hdcFlipped, oldFlipped);
DeleteDC(hdcFlipped);
ReleaseDC(NULL, hdcScr);
return hbmFlipped;
}

Creating a new bitmap with given dimensions filled with given color

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);

Off-screen drawing GDI+

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

Saving screenshot of a specific window as bmp in window

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.