0

How to I get one specific pixels RGB-values from a HBITMAP? I tried reading similar posts at StackOverflow but none really fit this problem. The code below seems to get an RGB value for another position (not the wanted one) in the HBITMAP.

    int width = rc.right - rc.left;
    int height = rc.bottom - rc.top;

    HDC hdcSource = hdc; // the source device context
    HBITMAP hSource = hbmp; // 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* Pixels = 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,
                      Pixels, &MyBMInfo, DIB_RGB_COLORS))
    {
        // error handling
    }

    int PixelX = 221;
    int PixelY = 14;

    cout << "R: " << (int)Pixels[4*((PixelX-1)+(PixelY-1)*width)] << " | G: " << (int)Pixels[4*((PixelX-1)+(PixelY-1)*width)+1] << " | B: " << (int)Pixels[4*((PixelX-1)+(PixelY-1)*width)+2] << endl;

EDIT (with user1118321's solution)

height-1 because y starts at 0 and not 1.

cout << "R: " << (int)Pixels[4*((PixelX)+(height-1-(PixelY))*width)] << " | G: " << (int)Pixels[4*((PixelX)+(height-1-(PixelY))*width)+1] << " | B: " << (int)Pixels[4*((PixelX)+(height-1-(PixelY))*width)+2] << endl;
  • 1
    Are you looking for [`GetPixel`](http://msdn.microsoft.com/en-us/library/dd144909.aspx)? – Igor Tandetnik Dec 28 '14 at 15:42
  • GetPixel is very slow which is why I am using HBITMAP to get all the pixels into an array. – Adam Lindberg Dec 28 '14 at 17:03
  • I'm confused. Is it "one specific pixel" or "all pixels" that you want? Your main question asks for the former, and now in the comment you are asking for the latter. Which is it? – Igor Tandetnik Dec 28 '14 at 17:21
  • `We'll change the received BITMAPINFOHEADER to request the data in a 32 bit RGB format` When you change the format, the size of the pixel data also changes, of course. The buffer you've allocated, based on the original format, may be too small for the new format (especially the 32bpp one). – Igor Tandetnik Dec 28 '14 at 17:27
  • 1
    Your math on `PixelX` and `PixelY` seems to assume one-based coordinates. Is that what you want? Are they really not zero-based? You probably don't want to subtract one from them. – Igor Tandetnik Dec 28 '14 at 17:30
  • What's `rc`? How does it relate to the size of the bitmap? In your offset math, you use `width`, which is derived from `rc`. I would have expected you to use `MyBMInfo.bmiHeader.biWidth` instead - just as you use `MyBMInfo.bmiHeader.biHeight` and not `height`. – Igor Tandetnik Dec 28 '14 at 17:32
  • I want access to all the pixels to later get rgb-values from many of them but in this case I just want one pixel from the array of all the pixels @IgorTandetnik – Adam Lindberg Dec 28 '14 at 18:43
  • You are right 1 should not be subtracted. Still get the wrong value though :( @IgorTandetnik – Adam Lindberg Dec 28 '14 at 18:47
  • It might have something to do with changing the format. I'm not sure how it works though. @IgorTandetnik – Adam Lindberg Dec 28 '14 at 18:51
  • width is the same as MyBMInfo.bmiHeader.biWidth. Tested with cout just to make sure. @IgorTandetnik – Adam Lindberg Dec 28 '14 at 18:52
  • Don't know the direction this went in, but Get/ Set Pixel are slow. Copying each pixel or reading each pixel is almost always a no no. If you want some of the pixels say at (x, y)'s; then Get/ Set Pixel is the proper tool. If you want to copy entire parts of a bitmap between DC's, you will use BitBlt();. – Evan Carslake Dec 28 '14 at 19:00
  • Are Windows bitmaps increasing Y-up or Y-down? You might be getting the wrong pixel because your assumption is wrong. (Or maybe I'm wrong and that only applies to .bmp files?) Try using `((height - 1) - PixelY)` and see if that helps. – user1118321 Dec 28 '14 at 19:20
  • That was the problem! Thanks. Any way I can cred you for it? @user1118321 – Adam Lindberg Dec 28 '14 at 22:28
  • I'll add an answer below. – user1118321 Dec 28 '14 at 22:41

1 Answers1

1

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.

user1118321
  • 25,567
  • 4
  • 55
  • 86