GetDIBits and loop through pixels using X, Y
Apart from the good answers already given, here's an example of how to get a simple array structure to walk on. (You can use e.g. Goz' code for the iteration.)
GetDIBits reference @ MSDN
You have to select DIB_RGB_COLORS
as flag for uUsage
and set up the BITMAPINFO
structure and the BITMAPINFOHEADER
structure it contains. When you set biClrUsed
and biClrImportant
to zero, there is "no" color table, so you can read the pixels of the bitmap you get from GetDIBits
as a sequence of RGB values. Using 32
as bit count (biBitCount
) sets up the data structure according to MSDN:
The bitmap has a maximum of 2^32 colors. If the
biCompression
member of theBITMAPINFOHEADER
isBI_RGB
, thebmiColors
member ofBITMAPINFO
isNULL
. EachDWORD
in the bitmap array represents the relative intensities of blue, green, and red, respectively, for a pixel. The high byte in eachDWORD
is not used.
Since a MS LONG
is exactly 32 bit long (the size of a DWORD
), you do not have to pay attention to padding (as described in the Remarks section).
Code:
HDC hdcSource = NULL; // the source device context
HBITMAP hSource = NULL; // the bitmap selected into the device context
BITMAPINFO MyBMInfo = {0};
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
// Get the BITMAPINFO structure from the bitmap
if(0 == GetDIBits(hdcSource, hSource, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS))
{
// error handling
}
// create the pixel buffer
BYTE* lpPixels = new BYTE[MyBMInfo.bmiHeader.biSizeImage];
// We'll change the received BITMAPINFOHEADER to request the data in a
// 32 bit RGB format (and not upside-down) so that we can iterate over
// the pixels easily.
// requesting a 32 bit image means that no stride/padding will be necessary,
// although it always contains an (possibly unused) alpha channel
MyBMInfo.bmiHeader.biBitCount = 32;
MyBMInfo.bmiHeader.biCompression = BI_RGB; // no compression -> easier to use
// correct the bottom-up ordering of lines (abs is in cstdblib and stdlib.h)
MyBMInfo.bmiHeader.biHeight = abs(MyBMInfo.bmiHeader.biHeight);
// Call GetDIBits a second time, this time to (format and) store the actual
// bitmap data (the "pixels") in the buffer lpPixels
if(0 == GetDIBits(hdcSource, hSource, 0, MyBMInfo.bmiHeader.biHeight,
lpPixels, &MyBMInfo, DIB_RGB_COLORS))
{
// error handling
}
// clean up: deselect bitmap from device context, close handles, delete buffer
GetDIBits: Where's that pixel? (x, y coordinates)
The problem was indeed that I was not taking the stride into account. Here is the working loop:
stride = ((((width * 24) + 31) & ~31) >> 3);
totalpx = stride*height;
for(i = 0; i < totalpx; i++)
{
//int x = i % width;
//int y = ((i-x)/width);
if(RGB(
(ScreenData[(3*i)+2]),
(ScreenData[(3*i)+1]),
(ScreenData[(3*i)+0]))==target)
{
int x = i % stride;
int y = ((i-x)/width);
SetCursorPos(rect.left+x,rect.top+y);
DeleteObject(hBitmap);
DeleteDC(memDC);
free(ScreenData);
ReleaseDC(hWnd, hDC);
return;
}
}
c++ read pixels with GetDIBits()
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;
}
GetDIBits returns array with all values 0
For screen data without transparency, you can simply set the fourth byte (Alpha) to 255, which means opaque.
The color format of the bitmap data obtained by GetDIBits
is written in BGR (lpPixels
parameter). If we want to assign data to a byte array using RGB format, we need to perform some conversion.
e.p.
for (size_t i = 0; i < myWidth * myHeight; i++)
{
size_t tempIndex = (i * 4);
// The colors are in BGR-format (windows format)
// Hence, the order is reversed
rgbPixels[tempIndex] = bgrPixels[tempIndex + 2]; // r
rgbPixels[tempIndex + 1] = bgrPixels[tempIndex + 1]; // g
rgbPixels[tempIndex + 2] = bgrPixels[tempIndex]; // b
rgbPixels[tempIndex + 3] = 255; // a (always 255)
}
Updated:
HDC hdc, hdcTemp;
RECT rect;
BYTE* bitPointer;
int x, y;
int red, green, blue, alpha;
hdc = GetDC(HWND_DESKTOP);
GetWindowRect(hWND_Desktop, &rect);
int MAX_WIDTH = rect.right;
int MAX_HEIGHT = rect.bottom;
hdcTemp = CreateCompatibleDC(hdc);
BITMAPINFO bitmap;
bitmap.bmiHeader.biSize = sizeof(bitmap.bmiHeader);
bitmap.bmiHeader.biWidth = MAX_WIDTH;
bitmap.bmiHeader.biHeight = MAX_HEIGHT;
bitmap.bmiHeader.biPlanes = 1;
bitmap.bmiHeader.biBitCount = 32;
bitmap.bmiHeader.biCompression = BI_RGB;
bitmap.bmiHeader.biSizeImage = MAX_WIDTH * 4 * MAX_HEIGHT;
bitmap.bmiHeader.biClrUsed = 0;
bitmap.bmiHeader.biClrImportant = 0;
HBITMAP hBitmap2 = CreateDIBSection(hdcTemp, &bitmap, DIB_RGB_COLORS, (void**)(&bitPointer), NULL, NULL);
SelectObject(hdcTemp, hBitmap2);
BitBlt(hdcTemp, 0, 0, MAX_WIDTH, MAX_HEIGHT, hdc, 0, 0, SRCCOPY);
for (int i=0; i<(MAX_WIDTH * 4 * MAX_HEIGHT); i+=4)
{
red = (int)bitPointer[i];
green = (int)bitPointer[i+1];
blue = (int)bitPointer[i+2];
alpha = (int)bitPointer[i+3];
}
Getting individual pixels of another window with BitBlt
I believe you need to select a bitmap into your device context.
"A bitmap must be selected within the device context, otherwise, CLR_INVALID is returned on all pixels." - GetPixel()
bool Gameboard::Refresh()
{
bool ret = false;
HDC context, localContext;
HGDIOBJ origHandle;
context = GetWindowDC(m_window);
if (context != NULL)
{
localContext = CreateCompatibleDC(context);
origHandle = SelectObject(localcontext,CreateCompatibleBitmap(context, GameboardInfo::BoardWidth, GameboardInfo::BoardHeight));
if (localContext != NULL)
{
if (BitBlt(localContext, 0, 0, GameboardInfo::BoardWidth, GameboardInfo::BoardHeight,
context, GameboardInfo::TopLeft.x, GameboardInfo::TopLeft.y, SRCCOPY))
{
ret = true;
// several calls to GetPixel which all return CLR_INVALID
}
SelectObject(localcontext, origHandle);
DeleteDC(localContext);
}
ReleaseDC(m_window, context);
}
return ret;
}
Getting RGB-values of a specific pixel from a HBITMAP
Windows bitmaps store their pixel starting at the lower left with Y increasing upwards. Try using ((height - 1) - PixelY)
instead of just PixelY
and see if that helps.
c++ GetDIBits not working
Your first function call fails because you did not initialise MyBMInfo.bmiHeader.biSize
. You need to do this:
...
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))
....
Once you fix that, the rest of the code will work as intended.
Related Topics
C++, Sort One Vector Based on Another One
Parameter Pack Must Be at the End of the Parameter List... When and Why
What Is the C++ Compiler Required to Do with Ill-Formed Programs According to the Standard
Operator< Comparing Multiple Fields
Where Are Temporary Object Stored
Constexpr Static Member Before/After C++17
C++ Fastest Way to Read Only Last Line of Text File
How to Get a Copy of the File Libstdc++.So.6.0.15
Linux Equivalent for Conio.H Getch()
Gdb Shows Incorrect Arguments of Functions for Stack Frames
Why Should the Assignment Operator Return a Reference to the Object
Does the Gotw #101 "Solution" Actually Solve Anything