Drawing on a DIB doesn't work - c++

I'm working on a GUI class and I created a method which paints a bitmap on a window from an array of pixels.
I'd like to draw on that bitmap in the same method. Also, I use an off-screen DC to avoid flickering.
Here's my code :
int width(m_rect.right - m_rect.left), height(m_rect.bottom - m_rect.top); // m_rect is the RECT of the bitmap, initialized beforehand
BITMAPINFOHEADER bih = { 0 };
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biCompression = BI_RGB;
bih.biBitCount = 32;
bih.biPlanes = 1;
bih.biWidth = width; // = 100, for instance
bih.biHeight = height; // = 100, same here
HDC dc = CreateCompatibleDC(hdc); // "hdc" is the DC of my window
HBITMAP bmp = CreateDIBitmap(hdc, &bih, CBM_INIT, m_data, &m_bmpInfo, DIB_RGB_COLORS); // creates a 32-bit device-independent bitmap
HGDIOBJ oldObj = SelectObject(dc, bmp);
RECT r = { m_rect.left + 10, m_rect.top + 10, m_rect.right - 10, m_rect.bottom - 10 };
HBRUSH brush = CreateSolidBrush(0xff);
FillRect(dc, &r, brush); // this line doesn't work!
DeleteObject(brush);
BitBlt(hdc, m_rect.left, m_rect.top, width, height, dc, 0, 0, SRCCOPY);
SelectObject(dc, oldObj);
DeleteObject(bmp);
DeleteDC(dc);
The problem is that I can't draw anything on the bitmap. It is properly drawn on the screen, but I just can't draw on it. Same with other drawing functions : Rectangle, RoundRect, etc. Also, performance matters to me. The faster this code is, the happier I'll get. So if you have any advice on performance improvements please let me know.
Any help would be greatly appreciated.
Thank you in advance.

CreateDIBitmap function
CreateDIBitmap function creates a compatible bitmap (DDB)

Related

unable to FillRect with transparent brush when double buffering

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.

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

Acquiring frames from Chrome (simple game) and displaying them with OpenCV

I've searched for ways to display a real-time simple game in a small window, and got to understand that OpenCV can handle it for me. But to accomplish that I need to acquire the frames in each iteration, just before showing it in the desired window.
This is the current solution that I am using:
HDC hwindowDC;
HDC hwindowCompatibleDC; // Memory for given DC
HBITMAP hbwindow;
int height, width;
// handles information about DIB (device-independent bitmap)
// https://learn.microsoft.com/en-us/windows/desktop/gdi/device-independent-bitmaps
BITMAPINFOHEADER bi;
// retrieces a handle to DC for the client area for entire screen
hwindowDC = GetDC(NULL);
// creates memory DC for the entire screen
hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
// enables strecn mode which has low costs - because of the grayscale game
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
// size of the game window - according to the inspect of the game within chrome
height = 150;
width = 600;
// creates matrix with four color channels and values in the range 0 to 255
// - https://stackoverflow.com/questions/39543580/what-is-the-purpose-of-cvtype-cv-8uc4
frame.create(height, width, CV_8UC4);
// create a bitmap for the window
hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = -height; //the minus sign makes it draw upside down or not
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
// attach the bitmap with the memory DC for our window
SelectObject(hwindowCompatibleDC, hbwindow);
// copy from the window device context to the bitmap device context
StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, 650, 130, 620, 150, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
// Gets the "bits" from the bitmap and copies them into src.data
GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, frame.data, (BITMAPINFO *)&bi, DIB_RGB_COLORS); //copy from hwindowCompatibleDC to hbwindow
// avoid memory leak // avoid memory leak
DeleteObject(hbwindow);
DeleteDC(hwindowCompatibleDC); // for any DC that was created by calling the CreateDC function
ReleaseDC(hwnd, hwindowDC); // releasing the DC for use of other applications (MUST)
img2 = frame;
//img1 = frame;
return frame;
After trying to figure out how to make the game running smoother, I've tried to implement screen recorder with the DirectX API, but without any success. This code results in a black screen.
IDirect3DSurface9* pSurface;
d3ddev->CreateOffscreenPlainSurface(100, 100,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL);
d3ddev->GetFrontBufferData(NULL , pSurface);
/*D3DLOCKED_RECT lockedRect;
ZeroMemory(&lockedRect, sizeof(D3DLOCKED_RECT));
pSurface->LockRect(&lockedRect, 0, D3DLOCK_READONLY);
*/
LPD3DXBUFFER buffer;
D3DXSaveSurfaceToFileInMemory(&buffer, D3DXIFF_BMP, pSurface, NULL, NULL);
DWORD imSize = buffer->GetBufferSize();
void* imgBuffer = buffer->GetBufferPointer();
//**** OpenCV
Mat D3DSurface(100, 100, CV_8UC4, imgBuffer);
imshow("D3DSurface", D3DSurface);
pSurface->Release();
return D3DSurface;
Can anyone can help me figure out how to accomplish this task in a better way?
I just think that there is something that I'm not quite understanding in this process, mirroring a live moving game to a desired window with minimal lags.
My purpose is to make a bot for an online game.

Create transparent bitmap with Gdiplus in C++

In C++, I want to create a simple transparent image with Gdiplus and save it as a png. I have the following code:
// These variables are class members and got initialized and are used elsewhere
BITMAPINFO bmi;
HDC hdc;
void* pvBits;
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = some_width;
bmi.bmiHeader.biHeight = some_height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = ((((bmi.bmiHeader.biWidth * bmi.bmiHeader.biBitCount) + 31) & ~31) >> 3) * bmi.bmiHeader.biHeight;
HBITMAP hBM = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0);
FillMemory(pvBits, bmi.bmiHeader.biSizeImage, 255);
HGDIOBJ oldObj = SelectObject(hDC, hBM);
ReleaseDC(NULL, hDC);
GdiFlush();
GdiPlusBitmap* bitmap = new Gdiplus::Bitmap(&bmi, pvBits);
When I save the image to png, I can see that there is an alpha channel, but its set to 0, so nothing transparent (RGB is all set). I also tried changing the 255 to 0, but that only gives me a black image with no transpirancy. Why is the Fillmemory call not filling the alpha channel, or am I missing something else?
FillMemory(pvBits, bmi.bmiHeader.biSizeImage, 255) will fill the memory with solid white color. If you write over the image with GDI functions, the alpha values remain unchanged. You won't see any transparency even if the file supports it.
To create 32-bit image you only need Gdiplus::Bitmap(w, h, PixelFormat32bppARGB). There is no need for BITMAPINFO and CreateDIBSection
If you mix GDI+ with GDI functions, you may want to reset alpha after writing with GDI functions. For example:
void test()
{
int w = 100;
int h = 100;
int bitcount = 32;
int size = ((((w * bitcount) + 31) & ~31) >> 3) * h;
BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = w;
bmi.bmiHeader.biHeight = -h;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = bitcount;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = size;
HDC hdc = GetDC(0);
BYTE* pvBits = NULL;
HBITMAP hbitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS,
(void**)&pvBits, NULL, 0x0);
FillMemory(pvBits, size, 0);
auto memdc = CreateCompatibleDC(hdc);
auto oldbmp = SelectObject(memdc, hbitmap);
SetBkColor(memdc, RGB(255, 0, 0));
TextOut(memdc, 0, 0, L"123", 3);
//GDI cleanup, don't delete hbitmap yet
SelectObject(memdc, oldbmp);
DeleteDC(memdc);
ReleaseDC(0, hdc);
//make the non-zero colors transparent:
for(int i = 0; i < size; i += 4)
{
int n = *(int*)(pvBits + i);
if (n != 0)
pvBits[i + 3] = 255;
}
CLSID clsid_png;
CLSIDFromString(L"{557cf406-1a04-11d3-9a73-0000f81ef32e}", &clsid_png);
Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(w, h, PixelFormat32bppARGB);
Gdiplus::BitmapData data;
bitmap->LockBits(&Gdiplus::Rect(0, 0, w, h),
Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &data);
memcpy(data.Scan0, pvBits, size);
bitmap->UnlockBits(&data);
//safe to delete hbitmap
DeleteObject(hbitmap);
bitmap->Save(L"test.png", &clsid_png);
delete bitmap;
}
Your code suffers from the same hdc problems as in previous question. A filling image with 0 should give a completely transparent black image as expected.
But I think it would be simpler to create bitmap directly specifying dimensions and pixel format and set content later:
Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(width, height, PixelFormat32bppARGB);
In transparent images, the value 0 means "full transparent" and 255 means
"full opaque".

C++ Window capture output isn't the same size as said window

I have a short program in C++ to capture the pixels from this BlueStacks emulator window, and then I'll be manipulating the pixels using OpenCV and then sending mouse input (win32api) based on some decisions here and there. I'm working in Visual Studio Enterprise 2017.
Only problem is, the function seems to be capturing a smaller region of pixels compared to the full window.
Here's an example image. Original window is on the left, mirrored output is on the right.
How can I fix this? I've already enabled High DPI awareness in my project settings, not sure what other steps to take other than adding random magic numbers into the function.
I've done this program already in Python but I'd like to redo this in C++ for the performance boost.
Here's a video (warning: noise). And another one.
Here's my current code:
#include "stdafx.h"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <Windows.h>
#include <iostream>
using namespace std;
using namespace cv;
Mat hwnd2mat(HWND hwnd);
int main(int argc, char **argv)
{
printf("Hello, world!\n");
Sleep(1000);
HWND hwndDesktop;
hwndDesktop = GetForegroundWindow();
// namedWindow("output", WINDOW_NORMAL);
int key = 0;
Mat src;
while (key != 27)
{
src = hwnd2mat(hwndDesktop);
// you can do some image processing here
imshow("output", src);
key = waitKey(1); // you can change wait time
}
}
Mat hwnd2mat(HWND hwnd)
{
HDC hwindowDC, hwindowCompatibleDC;
int height, width, srcheight, srcwidth;
HBITMAP hbwindow;
Mat src;
BITMAPINFOHEADER bi;
hwindowDC = GetDC(hwnd);
hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
RECT windowsize; // get the height and width of the screen
GetClientRect(hwnd, &windowsize);
srcheight = windowsize.bottom;
srcwidth = windowsize.right;
height = windowsize.bottom / 1; //change this to whatever size you want to resize to
width = windowsize.right / 1;
src.create(height, width, CV_8UC4);
// create a bitmap
hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
bi.biSize = sizeof(BITMAPINFOHEADER); //http://msdn.microsoft.com/en-us/library/windows/window/dd183402%28v=vs.85%29.aspx
bi.biWidth = width;
bi.biHeight = -height; //this is the line that makes it draw upside down or not
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
// use the previously created device context with the bitmap
SelectObject(hwindowCompatibleDC, hbwindow);
// copy from the window device context to the bitmap device context
StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, 0, 0, srcwidth, srcheight, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO *)&bi, DIB_RGB_COLORS); //copy from hwindowCompatibleDC to hbwindow
// avoid memory leak
DeleteObject(hbwindow);
DeleteDC(hwindowCompatibleDC);
ReleaseDC(hwnd, hwindowDC);
return src;
}
The stretching is due to DPI scaling. Your own program is not DPI aware, the other program seems to be DPI aware. The easiest way to make your program DPI aware is by calling SetProcessDPIAware(); at the start of the program.
Aside note, HBITMAP handle should not be selected in a device context when calling GetDIBits. You can rewrite your code as
SetProcessDPIAware();
...
Mat hwnd2mat(HWND hwnd)
{
RECT rc;
GetClientRect(hwnd, &rc);
int width = rc.right;
int height = rc.bottom;
Mat src;
src.create(height, width, CV_8UC4);
HDC hdc = GetDC(hwnd);
HDC memdc = CreateCompatibleDC(hdc);
HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height);
HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
BitBlt(memdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
SelectObject(memdc, oldbmp);
BITMAPINFOHEADER bi = { sizeof(BITMAPINFOHEADER), width, -height, 1, 32, BI_RGB };
GetDIBits(hdc, hbitmap, 0, height, src.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(hwnd, hdc);
return src;
}
This code will only work for native Win32 programs. For other programs like Chrome, WPF, Qt apps, this will show blank screen. You would need to take screen shot of desktop window instead.
I think you want GetActiveWindow() instead of GetForegroundWindow()
See this SO answer