C++ 屏幕捕获 - 如何读取位图?

C++ screen capture - how to read the bitmap?

我想让程序(用c++写的)从屏幕上读取像素,但是我得到的响应似乎很乱,变量start表示老鼠。正确的做法是什么? 我使用了 GetPixel() 效果很好,但我需要一个完整的位图来提高效率。这是代码:

#include <Windows.h>
#include <iostream>
#include <math.h>
#include <stdio.h>
using namespace std;

int nScreenWidth;
int nScreenHeight;

HBITMAP GetScreenBmp(HDC hdc) {
    HDC hCaptureDC  = CreateCompatibleDC(hdc);
    HBITMAP hBitmap = CreateCompatibleBitmap(hdc, nScreenWidth, nScreenHeight);
    HGDIOBJ hOld = SelectObject(hCaptureDC, hBitmap);
    BOOL bOK = BitBlt(hCaptureDC,0,0,nScreenWidth, nScreenHeight, hdc,0,0,SRCCOPY|CAPTUREBLT);

    SelectObject(hCaptureDC, hOld); // always select the previously selected object once done
    DeleteDC(hCaptureDC);
    return hBitmap;
}

int main() {
    nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
    nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
    HBITMAP hBitmap;

    int times = 0;
    while (!GetAsyncKeyState(VK_SPACE) && times<1000)
    {
        times++;

        POINT p;
        GetCursorPos(&p);

        HDC hdc = GetDC(0);

        hBitmap = GetScreenBmp(hdc);

        BITMAPINFO MyBMInfo = {0};
        MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);

        if(0 == GetDIBits(hdc, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) {
            cout << "error" << endl;
        }

        BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];

        MyBMInfo.bmiHeader.biCompression = BI_RGB;

        if(0 == GetDIBits(hdc, hBitmap, 0, MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS)) {
            cout << "error2" << endl;
        }
        //**HERE** - position is wrong?
        int start = (p.y*nScreenWidth+p.x)*4;
        for(int i = start; i < start + 4; i+=4)
        {
            cout << "R:" << (int)lpPixels[i+2] << " G:" << (int)lpPixels[i+1] << " B:" << (int)lpPixels[i] << endl;
        }

        ReleaseDC(NULL, hdc);
        delete[] lpPixels;

        Sleep(1000);
    }
    DeleteObject(hBitmap);

    return 0;
}

您强制压缩为 BI_RGB,您不妨设置前 6 个值并仅调用一次 GetDIBits。由于位图高度从下到上开始,因此您必须为 BITMAPINFOHEADER 提供负高度,否则从下到上读取。

确保进程是 DPI 感知的。最简单的方法(但不是首选方法)是调用 SetProcessDPIAware()。对于每个 hBitmap 分配调用 DeleteObject(hBitmap)

int main()
{
    SetProcessDPIAware();
    int width = GetSystemMetrics(SM_CXSCREEN);
    int height = GetSystemMetrics(SM_CYSCREEN);
    while(!GetAsyncKeyState(VK_SPACE))
    {
        HDC hdc = GetDC(0);
        POINT p;
        GetCursorPos(&p);

        HDC memdc = CreateCompatibleDC(hdc);
        HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height);
        HGDIOBJ oldbmp = SelectObject(memdc, hbitmap);
        BitBlt(memdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY | CAPTUREBLT);
        SelectObject(memdc, oldbmp);
        DeleteDC(memdc);

        //use GetPixel for testing
        COLORREF c = GetPixel(hdc, p.x, p.y);
        printf("%02X%02X%02X\n", GetRValue(c), GetGValue(c), GetBValue(c));

        BITMAPINFO bi = { 0 };
        bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
        bi.bmiHeader.biWidth = width;
        bi.bmiHeader.biHeight = -height;
        bi.bmiHeader.biBitCount = 32; //32-bit bitmap
        bi.bmiHeader.biPlanes = 1;
        bi.bmiHeader.biCompression = BI_RGB;

        //allocate 4 bytes per pixel for 32-bit
        BYTE* lpPixels = new BYTE[height * width * 4]; 
        if(0 != GetDIBits(hdc, hbitmap, 0, height, lpPixels,
            &bi, DIB_RGB_COLORS))
        {
            int i = (p.y * width + p.x) * 4;
            printf("%02X%02X%02X\n\n", 
                        lpPixels[i + 2], lpPixels[i + 1], lpPixels[i + 0]);
        }
        DeleteObject(hbitmap);
        ReleaseDC(NULL, hdc);
        delete[] lpPixels;
        Sleep(1000);
    }
    return 0;
}