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;
}
我想让程序(用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;
}