I create a screenshot from the desktop and receive a vector with the pixel data, but I am having problems when trying to draw the pixel data on my screen.
CODE TO CREATE SCREENSHOT SNIPPET
std::vector<RGBQUAD> recvScreenSnippet(int x, int y, int width, int height) {
std::vector<RGBQUAD> v_screen_data(width*height);
HDC screen_dc = GetDC(NULL);
HDC mem_dc = CreateCompatibleDC(NULL);
HBITMAP hBmp = CreateCompatibleBitmap(screen_dc, width, height);
auto oldBmp = SelectObject(mem_dc, hBmp);
// 32 bit & Upside Down headers
BITMAPINFO bmi{};
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biHeight = height;
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
// Receive pixel data from hdc
BitBlt(mem_dc, 0, 0, width, height, screen_dc, x, y, SRCCOPY);
GetDIBits(mem_dc, hBmp, 0, height, &v_screen_data[0], &bmi, DIB_RGB_COLORS);
// Cleanup
SelectObject(mem_dc, oldBmp);
DeleteObject(hBmp);
DeleteDC(mem_dc);
ReleaseDC(0, screen_dc);
return v_screen_data;
}
CODE TO PAINT THE PIXEL DATA ON MY SCREEN:
void setScreenSnippet(int x, int y, int width, int height, std::vector<RGBQUAD> &v_pixel_data) {
HDC screen_dc = GetDC(0);
HDC mem_dc = CreateCompatibleDC(0);
// 32 bit & Upside Down headers
BITMAPINFO bmi{};
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biHeight = height;
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biSize = sizeof(BITMAPINFO);
// Create bitmap
HBITMAP bitmap = CreateCompatibleBitmap(mem_dc,width,height);
// Store pixel data in bitmap
SetDIBits(mem_dc, bitmap, 0, height, &v_pixel_data[0], &bmi, DIB_RGB_COLORS);
// Select bitmap data into mem_dc
SelectObject(mem_dc, bitmap);
while (true)
{
BitBlt(screen_dc, x, y, width, height, mem_dc, 0, 0, SRCCOPY);
Sleep(1);
}
// Cleanup
SelectObject(mem_dc, bitmap);
DeleteObject(bitmap);
ReleaseDC(0, screen_dc);
}
HOW I AM CALLING IT
int main()
{
Sleep(5000);
std::vector<RGBQUAD> v_pixel_data = recvScreenSnippet(600,600,400,400);
setScreenSnippet(100, 600, 400, 400, v_pixel_data);
system("pause"), exit(1);
}
The issue is that the painted screenshot is black on the top, which means that something is not working.
What am I doing wrong?
First of all, I have to apologize that I have used C++ only in bare metal projects where no standard library functions (std::...
) were used, so my knowledge of these functions is very limited.
However, let's look at the following code:
std::vector<int> example1, example2;
example1[20] = 1; /* Line 2 */
int * pExample = &example2[0]; /* Line 3 */
pExample[300] = 2; /* Line 4 */
Not knowing much about the std::vector
data type, I cannot tell you what "line 2" is doing; maybe the size of the vector
example1
is changed so it has at least 21 elements.
However, I can definitely tell you what happens in "lines 3 and 4":
In line 3, a pointer to the element [0]
of the vector
is generated.
In line 4, an operation on the pointer (but not on the vector
) is performed. This means that the compiler does not know that this operation accesses an element in the example2
vector
.
For this reason, the compiler definitely does not re-size the vector
in this line.
If the vector
example2
was already larger than 300 elements, "line 4" will write to example2[300]
; if it was smaller than 301 elements, this line of code will write to anywhere in the RAM and may crash your program because of overwriting important data (such as the return addresses on the stack).
For this reason, you must ensure that example2
is already larger than 300 elements before accessing pExample[300]
.
What does this have to do with your problem?
Windows API functions perform pointer operations, not vector
operations.
This means: GetDIBits()
internally works like this:
... GetDIBits(..., RGBQUAD * pExample, ...)
{
...
pExample[0] = ...;
pExample[1] = ...;
...
pExample[width * height - 1] = ...;
...
}
For this reason, you must ensure that the vector v_pixel_data
already has a size of at least width * height
elements before you call GetDIBits()
.
The issue is that the painted screenshot is black on the top, which means that something is not working.
Windows stores the bottom pixels first and the top pixels last.
Maybe, your vector
was large enough to hold a part of the image but not the full image.