0

I need to find some image on screen. I decided to make a simple comparing loop.

I found this answer that seems to be helpfull and wrote the next code:

void Capt()
{
  HDC hdcSource = GetDC(GetDesktopWindow()); // the source device context
  HDC hdc = CreateCompatibleDC(hdcSource);
  HBITMAP hSource = CreateCompatibleBitmap(hdcSource, xw, yw); // the bitmap selected into the device context

  SelectObject(hdc, hSource);

  BITMAPINFO MyBMInfo = { 0 };
  MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);

  // Get the BITMAPINFO structure from the bitmap
  if (0 == GetDIBits(hdc, hSource, 0, 0, NULL, &MyBMInfo, DIB_RGB_COLORS))
  {
    mb("error1");
    IA(GetLastError());
  }

  // 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(hdc, hSource, 0, MyBMInfo.bmiHeader.biHeight,
    lpPixels, &MyBMInfo, DIB_RGB_COLORS))
  {
    mb("error2");
    IA(GetLastError());
  }

  DeleteObject(hSource);
  ReleaseDC(NULL, hdcSource);


  for (int i = 0, j=0; i < MyBMInfo.bmiHeader.biSizeImage&&j<100; i++)
  {
    if (lpPixels[i] != 0)
    {
      char buf[1024] = {};
      _itoa_s(lpPixels[i], buf, 10);
  
      //output
    }
  }
}

Two problems:

  1. My screen resolution is 1280x800=1 024 000 px, MyBMInfo.bmiHeader.biSizeImage is equal to 4 096 000. Is it rgba or what?
  2. The main issue: although, there is no errors, and the value I mentioned in previous problem is not zero, but in the loop at the bottom of code, all values of lpPixels are zero, I do not get any output. Why?
  • 2
    Maybe a dumb question ... but where do you copy the screen's pixel values into your hdc? Shouldn't there be a `BitBlt` or some such between `hdcSource` (the screen) and `hdc` (your local DC/bitmap)? – Adrian Mole Mar 10 '21 at 18:48
  • 2
    You're pulling data from a brand-new bitmap. Try using BitBlt to copy some data from `hdcSource` to `hdc`. – Ben Voigt Mar 10 '21 at 18:48
  • As well as above comments, you don't need the intermediate compatible bitmap. You can use `CreateDIBSection` to create a DIB and blit directly from the screen to that. – Jonathan Potter Mar 10 '21 at 18:49
  • @AdrianMole @BenVoight I try `BitBlt` for a minute – Thawbkisavv Mar 10 '21 at 18:53
  • @BenVoigt added `BitBlt(hdc, 0, 0, xw, yw, hdcSource, 0, 0, SRCCOPY);` and nothing is changed. `xw` and `yw` are window width and height. – Thawbkisavv Mar 10 '21 at 18:55
  • @JonathanPotter can You please specify what should I pass to `CreateDIBSection`, because in my case it all should work the fastest as possible. Should I call it twice as `GetDIBits`? – Thawbkisavv Mar 10 '21 at 18:58
  • @BenVoigt, oh, my fault, `BitBlt` in fact worked. This seems to solve my problem – Thawbkisavv Mar 10 '21 at 20:11
  • @BenVoigt but now I am confused of how to work with array. I am totally confused. I think the reason of pixel array size is getting 4 times bigger is related to the bit count — 32, i.e. 4 byte. But... so what?). First element of the pixel array is red value of first pixel; second value is green value of first pixel; third is red; what is fourth then? – Thawbkisavv Mar 10 '21 at 21:12
  • @Thawbkisavv: Well, it depends on the other fields in the BITMAPINFOHEADER. Specifically you want to look at the one "number of color planes". If it is 4, then there is a fourth color plane (alpha). If it is 3, then the fourth byte is just meaningless padding. Either way you probably want to just cast to (RGBQUAD*)lpPixels` and away you go. – Ben Voigt Mar 10 '21 at 22:39

1 Answers1

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];

}
Strive Sun
  • 5,988
  • 1
  • 9
  • 26