gpt4 book ai didi

c++ - Windows GetDIBits 不返回预期值

转载 作者:可可西里 更新时间:2023-11-01 11:23:23 25 4
gpt4 key购买 nike

我目前正在编写一个扫描屏幕并查找像素的小程序。我的问题是 GetDIBits 函数似乎没有返回正确的屏幕截图。

将位图复制到剪贴板会将正确的屏幕图像放入剪贴板。我决定将函数的输出打印到一个 BMP 文件,以了解发生了什么,这显然不是我所期望的。

我还会提到我有 3 个显示器,以防它可以解释为什么它的行为不像预期的那样。

class Test {
int screenWidth;
int screenHeight;
HWND targetWindow;
HDC targetDC;
HDC captureDC;
RGBQUAD *pixels;
HBITMAP captureBitmap;


bool TakeScreenshot() {
ZeroMemory(pixels, screenHeight*screenWidth);
screenWidth = GetSystemMetrics(SM_CXSCREEN);
screenHeight = GetSystemMetrics(SM_CYSCREEN);

targetWindow = GetDesktopWindow();
targetDC = GetDC(NULL);


captureDC = CreateCompatibleDC(targetDC);

captureBitmap = CreateCompatibleBitmap(targetDC, screenWidth, screenHeight);
HGDIOBJ old = SelectObject(captureDC, captureBitmap);
if (!old)
printf("Error selecting object\n");

OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_BITMAP, captureBitmap);
CloseClipboard();

if (BitBlt(captureDC, 0, 0, screenWidth, screenHeight, targetDC, 0, 0, SRCCOPY)) {
BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = screenWidth;
bmi.bmiHeader.biHeight = -screenHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;

if (!SelectObject(captureDC, old))
printf("Error unselecting object\n");
if (!GetDIBits(captureDC,
captureBitmap,
0,
screenHeight,
pixels,
&bmi,
DIB_RGB_COLORS
)) {
printf("%s: GetDIBits failed\n", __FUNCTION__);
return false;
}

}
else {
printf("%s: BitBlt failed\n", __FUNCTION__);
return false;
}
return true;
}
// This is from somewhere on stackoverflow - can't find where.
void MakePicture() {
typedef struct /**** BMP file header structure ****/
{
unsigned int bfSize; /* Size of file */
unsigned short bfReserved1; /* Reserved */
unsigned short bfReserved2; /* ... */
unsigned int bfOffBits; /* Offset to bitmap data */
} BITMAPFILEHEADER;

BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;

unsigned short bfType = 0x4d42;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfSize = 2 + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 2560 * 1440 * 3;
bfh.bfOffBits = 0x36;

bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = screenWidth;
bih.biHeight = screenHeight;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 5000;
bih.biYPelsPerMeter = 5000;
bih.biClrUsed = 0;
bih.biClrImportant = 0;

FILE *file;
fopen_s(&file, "test.bmp", "wb");
if (!file)
{
printf("Could not write file\n");
return;
}

/*Write headers*/
fwrite(&bfType, 1, sizeof(bfType), file);
fwrite(&bfh, 1, sizeof(bfh), file);
fwrite(&bih, 1, sizeof(bih), file);

/*Write bitmap*/
for (int y = 0; y < screenHeight; y++)
{
for (int x = 0; x < screenWidth; x++)
{
unsigned char r = pixels[x + y].rgbRed;
unsigned char g = pixels[x + y].rgbGreen;
unsigned char b = pixels[x + y].rgbBlue;
fwrite(&b, 1, 1, file);
fwrite(&g, 1, 1, file);
fwrite(&r, 1, 1, file);
}
}
fclose(file);
}
Test() {
screenWidth = GetSystemMetrics(SM_CXSCREEN);
screenHeight = GetSystemMetrics(SM_CYSCREEN);
pixels = new RGBQUAD[screenWidth * screenHeight];
}
~Test() {
//cleanup
}
};

这是代码给出的结果(而不是屏幕截图): enter image description here

它似乎从我的屏幕顶部提取了几个像素并将它们拉伸(stretch)成图像。屏幕截图来自正在打开的 Visual Studio(橙色部分是通知)。

如果我在屏幕上放置一个巨大的红色方 block (255, 0, 0),如果它的高度不为 0,则像素数组将不包含单个红色像素。

最佳答案

BitBlt 执行实际的复制。剪贴板函数应该在 BitBlt

之后调用

另请注意,在多显示器设置中,SM_CXSCREEN/Y... 给出主显示器的尺寸。对整个屏幕使用 SM_XVIRTUALSCREEN/XV...SM_XVIRTUALSCREEN/Y 将给出 X/Y 坐标(通常为零)

完成后一定要释放所有句柄并删除使用的对象。事实上,没有必要将targetDC等声明为类成员。

如果应用程序不支持 DPI,则位图可能看起来较小,具体取决于 DPI 设置。在程序开始时调用 SetProcessDPIAware() 进行快速修复,或设置 list 。

如评论中所述,使用 SetClipboardData(CF_BITMAP, captureBitmap); 系统接管 captureBitmap。避免调用此函数或复制位图以传递到剪贴板。

int main()
{
int screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int screenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int screen_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
int screen_y = GetSystemMetrics(SM_YVIRTUALSCREEN);

screenWidth = GetSystemMetrics(SM_CXSCREEN);
screenHeight = GetSystemMetrics(SM_CYSCREEN);
screen_x = 0;
screen_y = 0;

RGBQUAD* pixels = new RGBQUAD[screenWidth * screenHeight];

DWORD size = screenWidth * screenHeight * 4;

ZeroMemory(pixels, size);

HDC targetDC = GetDC(NULL);
HDC captureDC = CreateCompatibleDC(targetDC);
HBITMAP captureBitmap = CreateCompatibleBitmap(targetDC, screenWidth, screenHeight);
HGDIOBJ old = SelectObject(captureDC, captureBitmap);

if(!BitBlt(captureDC, 0, 0, screenWidth, screenHeight, targetDC,
screen_x, screen_y, SRCCOPY))
printf("BitBlt error\n");

SelectObject(captureDC, old);

BITMAPINFO bmi = { 0 };
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = screenWidth;
bmi.bmiHeader.biHeight = -screenHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;

if(OpenClipboard(NULL))
{
EmptyClipboard();
SetClipboardData(CF_BITMAP,
CopyImage(captureBitmap, IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE));
CloseClipboard();
}

if(!GetDIBits(targetDC,
captureBitmap,
0,
screenHeight,
pixels,
&bmi,
DIB_RGB_COLORS
))
printf("%s: GetDIBits failed\n", __FUNCTION__);

BITMAPFILEHEADER filehdr = { 'MB', 54 + size, 0, 0, 54 };
std::ofstream f("test.bmp", std::ios::binary);
f.write((char*)&filehdr, sizeof(filehdr));
f.write((char*)&bmi, sizeof(bmi));
f.write((char*)pixels, size);

//cleanup:
SelectObject(captureDC, old);
DeleteObject(captureBitmap);
DeleteDC(captureDC);
ReleaseDC(0, targetDC);
}

关于c++ - Windows GetDIBits 不返回预期值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54394853/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com