daniel
daniel

Reputation: 221

BITMAP to mat/2d array

my goal is to have a program which screen captures a game and then to read the picture from code.

i'm realy new to the win api + never used bitmaps, so im having a hard time, im a complete noob.

the code i have to screen capture is (found it on stack overflow):

    HDC hScreenDC = GetDC(NULL);
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

int width = GetDeviceCaps(hScreenDC, HORZRES);
int height = GetDeviceCaps(hScreenDC, VERTRES);

// maybe worth checking these are positive values
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);

// get a new bitmap
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);

BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);

// clean up
DeleteDC(hMemoryDC);
DeleteDC(hScreenDC);

my idea of how that would be possible is converting "hBitmap" to a mat/2d array. i searched the internet and found the next solution:

  1. Load image into a HBITMAP handle.

  2. Get BITMAP object from HBITMAP.

  3. Create a DWORD array to load bmBits form BITMAP.

  4. Save the DWORD array into a RGB structure matrix.

step 1 already did, i have the var "hBitmap".

for step 2, i added the next code:

//Get BITMAP object from HBITMAP
BITMAP bitmap;
GetObject(hBitmap, sizeof(BITMAP), &bitmap);

before the delete dc.

i don't know how to do step 3 and 4. would be glad if someone showed me as i did not manage to find any simple guide on the subject. if there are other better solutions i would be happy to hear about them as well.

edit: tried to save it as .ppm with "Barmak Shemirani" answer but it just showed me 4 big squares?

note: i +=4, i know its rgba and not rgb, but the a is always 255 code:

using namespace std;
int main()
{
    cout << "Hello World!\n";
    HDC hScreenDC = GetDC(NULL);
    // and a device context to put it in
    HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

int width = GetDeviceCaps(hScreenDC, HORZRES);
int height = GetDeviceCaps(hScreenDC, VERTRES);

// maybe worth checking these are positive values
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, width, height);

// get a new bitmap
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);

BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);


WORD bpp = 32; //32-bit bitmap
cout << "width=" << width << endl;
cout << "height=" << height << endl;
DWORD size = ((width * bpp + 31) / 32) * 4 * height;
BITMAPINFOHEADER bi = { sizeof(bi) };
bi.biWidth = width;
bi.biHeight = height;
bi.biPlanes = 1;
bi.biBitCount = bpp;

unsigned char* bits = new unsigned char[size];
int result = GetDIBits(hScreenDC, hBitmap, 0, height, bits, (BITMAPINFO*)&bi, DIB_RGB_COLORS);

ofstream img(".ppm");
img << "P3" << endl;
img << 1536 << " " << 864 << endl;
img << "255" << endl;
for (int i = 0; i <= 864; i++) {
    for (int j = 0; j < 1536; j+=4) {
        int r = bits[i];
        int g = bits[i + 1];
        int b = bits[i + 2];
        img << r << " " << g << " " << b << endl;
    }
}

cout << "finished" << endl;

delete[]bits;

// clean up
    SelectObject(hMemoryDC, hOldBitmap);//now we can destroy hMemoryDC & hBitmap
    DeleteObject(hBitmap);
    DeleteDC(hMemoryDC);
    ReleaseDC(0, hScreenDC);

}

Upvotes: 1

Views: 530

Answers (1)

Barmak Shemirani
Barmak Shemirani

Reputation: 31599

GetObject(hBitmap...) will fill the bm.bmBits member for DIBs only. But here we don't have a DIB. Use GetDIBits instead. The example below reads the pixels in to bits for a 32-bit bitmap.

WORD bpp = 32; //32-bit bitmap
DWORD size = ((width * bpp + 31) / 32) * 4 * height;
BITMAPINFOHEADER bi = { sizeof(bi) };
bi.biWidth = width;
bi.biHeight = height;
bi.biPlanes = 1;
bi.biBitCount = bpp;

unsigned char* bits = new unsigned char[size];
int result = GetDIBits(hdc, hBitmap, 0, height, bits, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
...
delete[]bits;

Aside, your code also requires ReleaseDC(0, hScreenDC) for cleanup, instead of DeleteDC(hScreenDC), because DC was acquired by GetDC.

//cleanup:
SelectObject(hMemoryDC, hOldBitmap);//now we can destroy hMemoryDC & hBitmap
DeleteObject(hBitmap);
DeleteDC(hMemoryDC);
ReleaseDC(0, hScreenDC);

Saving in ppm/non-binary:

ofstream img("filename.ppm");
img << "P3\n";
img << width << " " << height << "\n";
img << "255\n";
for(int y = height - 1; y >= 0; y--) 
{
    for(int x = 0; x < width; x ++)
    {
        int i = (y * width + x) * 4;
        int r = bits[i + 2];
        int g = bits[i + 1];
        int b = bits[i + 0];
        img << r << " " << g << " " << b << " ";
    }
    img << "\n";
}

Upvotes: 1

Related Questions