I'm trying to create a function which is equivalent to the windows API GetPixel() function, but I want to create a bitmap of my screen and then read that buffer.
This is what I've got (Mostly copy pasted from google searches), when I run it it only prints out 0's. I think I've got most of it right, and that my issue is that I don't know how to read the BYTE variable.
So my question is, what do I need to do in order to get it to print out some random colors (R,G or B) with my for loop?
#include <Windows.h>
#include <iostream>
#include <math.h>
#include <stdio.h>
using namespace std;
int main() {
HDC hdc,hdcMem;
hdc = GetDC(NULL);
hdcMem = CreateCompatibleDC(hdc);
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, 1680, 1050);
BITMAPINFO MyBMInfo = {0};
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
// Get the BITMAPINFO structure from the bitmap
if(0 == GetDIBits(hdcMem, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) {
cout << "error" << endl;
}
// create the bitmap buffer
BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
MyBMInfo.bmiHeader.biBitCount = 32;
MyBMInfo.bmiHeader.biCompression = BI_RGB;
MyBMInfo.bmiHeader.biHeight = abs(MyBMInfo.bmiHeader.biHeight);
// get the actual bitmap buffer
if(0 == GetDIBits(hdc, hBitmap, 0, MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS)) {
cout << "error2" << endl;
}
for(int i = 0; i < 100; i++) {
cout << (int)lpPixels[i] << endl;
}
return 0;
}
- Windows 7
- C::B 13.12 (Console Application)
- Compiler: mingw32-gcc
- Library gdi32 linked
As agreed, I'm adding a new answer with the working code snippet (I added the missing cleanup of lpPixels). See the discussions in my previous answer and the one made by @enhzflep.
#include <Windows.h>
#include <iostream>
#include <math.h>
#include <stdio.h>
using namespace std;
HBITMAP GetScreenBmp( HDC hdc) {
// Get screen dimensions
int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
// Create compatible DC, create a compatible bitmap and copy the screen using BitBlt()
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() {
HDC hdc = GetDC(0);
HBITMAP hBitmap = GetScreenBmp(hdc);
BITMAPINFO MyBMInfo = {0};
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
// Get the BITMAPINFO structure from the bitmap
if(0 == GetDIBits(hdc, hBitmap, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS)) {
cout << "error" << endl;
}
// create the bitmap buffer
BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];
// Better do this here - the original bitmap might have BI_BITFILEDS, which makes it
// necessary to read the color table - you might not want this.
MyBMInfo.bmiHeader.biCompression = BI_RGB;
// get the actual bitmap buffer
if(0 == GetDIBits(hdc, hBitmap, 0, MyBMInfo.bmiHeader.biHeight, (LPVOID)lpPixels, &MyBMInfo, DIB_RGB_COLORS)) {
cout << "error2" << endl;
}
for(int i = 0; i < 100; i++) {
cout << (int)lpPixels[i];
}
DeleteObject(hBitmap);
ReleaseDC(NULL, hdc);
delete[] lpPixels;
return 0;
}
Basically, you need to have drawn some pixels in order to get back a result other than 0.
At present, the 4th line of code in your main creates an empty (blank, 0-initialized) image. You then get information about the size of this image with your first call to GetDIBits
. You then get the actual (blank) pixels with your second call to GetDIBits
.
To fix, just load a bitmap file from disk into your hBitmap
and select this bitmap into your hdcMem
.
I.e, change
HBITMAP hBitmap = CreateCompatibleBitmap(hdc, 1680, 1050);
to something like this.
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, "xpButton.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE);
HBITMAP old = (HBITMAP) SelectObject(hdcMem, hBitmap);
(make sure you use a valid bmp file name. Mine exists in the same folder as the .cpp file, since this is the 'current' directory when you run via the IDE. If you wish to run via explorer, place another copy of the bmp in the same folder as your exe)
Here's the bmp I've used (which has been converted to a png after upload to SO):
And here's the first 10 iterations through the loop.
255
5
253
0
255
5
253
0
255
5
Note that the pixel at 0,0 has the colour of: rgb(253,5,255) and it's an 8bit image, so there's no alpha channel, hence it has the value 0. The pixels are stored as [BGRA], [BGRA], [BGRA], etc, etc.
I'll leave it to you to fix the (non-existant) clean-up section of your program. Windows will de-allocate the memory you've used here, but you absolutely should not get into the habit of not freeing any memory you've allocated. :)
Your code seems a bit confused. Too many snippets I guess :).
Still, you're quite close:
The first GetDIBits() call is in order to get the properties of the bitmap filled in, as the comment in your code suggests.
You are using an unnecessary MemDC for this - which is probably from a snippet that wants to do a BitBlt with the screen.
You then can use the filled in structure to get the actual bitmap pixels with the second GetDIBits() call, but what you're doing is replacing the properties with hard coded values again, making the first GetDIBits() call useless.
So: Drop the MemDC - you don't need it - and replace hdcMem with hdc in the first call to GetDIBits(), then remove all the statements that overwrite bmiHeader members after the first GetDIBits call and you should get your pixels.
Oh, and of course don't forget to call ReleaseDC()/DeleteObject() on the dc and bitmap and delete[] the buffer :)