Reputation: 103
I wanted the program (written in c++) to read the pixels from the screen, but the response I get seems to be messy, the variable start indicates the position of the mouse. What is the correct way to do it? I used the GetPixel() which worked fine, but I need a whole bitmap for the efficiency. Here is the code:
#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;
}
Upvotes: 2
Views: 1964
Reputation: 31599
You are forcing the compression to be BI_RGB
, you might as well set the first 6 values and call GetDIBits
only once. And since the bitmap height starts from bottom to top you have to supply negative height for BITMAPINFOHEADER
otherwise read from bottom to top.
Make sure the process is DPI aware. Easiest way (but not the preferred way) is to call SetProcessDPIAware()
. For each hBitmap
allocation call 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;
}
Upvotes: 3