Sign in to follow this  

GetDIBits and CreateCompatibleBitmap woes

This topic is 3039 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm working on a screen scraper app in Lazarus (like Delphi only open source) and working directly with Windows API/GDI to create a screen shot that is then processed by my app. After quite a bit of reading I discovered that I wanted to use GetDIBits to retrieve a pixel array of all of the bits within the screen rect that I needed, I also found posts about padding the scanline size to account for 4 byte alignment. A bit more digging and I ended up with the code below. Problem is, when I run this on a small section (say 5px x 4px) I get back what appears to be a palette, run it at 100px by 100px and I get back a perfect image (inverted of course, but I already knew about that), run it at 201px x 100px get a staggered thing back (hmm... might have something off in my scanline width calc), run it at 500px x 500px get back a black screen (all 0's). So, I'm guessing that my problem is with using CreateCompatibleBitmap. I'm guessing that it is creating a bitmap that is paletable instead of RGB only. I've tried using CreateDIBsection but it always AV'd on me, same results with CreateDIBitmap. The code below is in Pascal (as said above) but I can read/write C/C++ easily enough and could easily translate if need be :). Any help would be greatly appreciated. PS: Considering that this is going to be used to scan a known section of the screen I'd also be interested in ways that I didn't have to re-/create the DC every time, could I simply move the init and cleanup stuff out into constructors/destructors and then call BitBlt/GetDIBits each scan?
procedure TCapInfo.CapRect(R: TRect);
var
  src, dst : HDC;
  pad : integer;
  bmp, bmold: HBITMAP;
  bmi : BITMAPINFO;
  bptr : pointer;
begin
  SetAll(24, R.Right-R.Left, R.Bottom-R.Top); // Set BPP, Width, Height
  src := GetDC(0); // Get the screen DC
  dst := CreateCompatibleDC(src); // Create a working DC that is the same as the screen
  bmi.bmiHeader.biSize := sizeof(BITMAPINFOHEADER); // header size
  bmi.bmiHeader.biSizeImage := FHeight * BPR; // height * Scanline size (BPR or Bytes Per Row)
  bmi.bmiHeader.biPlanes:=1; // 1 working plane (not sure why)
  bmi.bmiHeader.biBitCount:=24; // 24 bits per pixel
  bmi.bmiHeader.biCompression:= BI_RGB; // No compression 0 would work too
  bmi.bmiHeader.biWidth := FWidth; // Width in pixels
  bmi.bmiHeader.biHeight := FHeight; // Height in pixels
  bmi.bmiHeader.biClrUsed:=0; // no idea, but seems it needs to be 0
  bmi.bmiHeader.biClrImportant:=0; // no idea, but seems it needs to be 0
// I'm positive the line below is my problem child,
// can't figure out a proper way to create the Bitmap
// tried CreateDIBSection, CreateDIBitmap and neither worked
  bmp :=  CreateCompatibleBitmap(src, FWidth, FHeight); // Create a bitmap that is the same as the screen, this is my problem I think
  bmold := SelectObject(dst, bmp); // select the dest DC into the bitmap
  BitBlt(dst, 0, 0, Width, Height, src, R.Left, R.Top, SRCCOPY); // Blit from screen to dest
  GetDIBits(dst, bmp, 0, bmi.bmiHeader.biHeight, Data, bmi, DIB_RGB_COLORS); // Pull the pixel data from the dest DC using bmi (created above) and data pointer
  // cleanup stuff
  SelectObject(dst, bmold);
  DeleteObject(bmp);
  ReleaseDC(0, src);
  DeleteDC(dst);
end;      

Thanks again, - Jeremy [Edited by - jdarling on August 17, 2009 5:59:39 PM]

Share this post


Link to post
Share on other sites
Quick-look:
If this is your actual code, maybe height should be height?

bmi.bmiHeader.biWidth := FWidth; // Width in pixels
bmi.bmiHeader.biHeight := FWidth; // Height in pixels


Also, take a look at the FAQ about using [ source] tags. Makes your posts a bit more readable for longer sequences of code.

Share this post


Link to post
Share on other sites
Quote:
Original post by Buckeye
Quick-look:
If this is your actual code, maybe height should be height?

bmi.bmiHeader.biWidth := FWidth; // Width in pixels
bmi.bmiHeader.biHeight := FWidth; // Height in pixels


Also, take a look at the FAQ about using [ source] tags. Makes your posts a bit more readable for longer sequences of code.


Well, don't I feel stupid (it was bad copy/paste). Double checked the actual code and all is ok with it (was trying to make readable comments while pasting). Fixed code above and switched from CODE to SOURCE tags.

Just in case, here is my code to calculate Bytes Per Row (IE: Scanline Width)
function TCapInfo.BPR: Cardinal;
var
tBPP : byte;
begin
tBPP := wbpp + (wBPP mod 4); // wbpp is BPP (bits) div 8 - Or Working Bytes PP
result := tBPP * FWidth;
end;

Share this post


Link to post
Share on other sites
First - creating the bitmap and the destination dc both from the screen is correct.

However, per the docs on GetDIBits(hdc, hbmp,..):
Quote:
The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function
Emphasis mine. So it would appear that you should select the old bitmap back to dst before you make the GetDIBits call.

Also, you don't have to set biSizeImage for BI_RGB bitmaps. (Many years ago, don't ask) I think I had problems when I did set that parameter.

EDIT: Do you allocate memory for the Data pointer somewhere? If Data==NULL, then all GetDIBits does is fill bmi.

EDIT2: Yeah, it'd be nice to get and hold the dc's, but they're global resources and should be released when you're done with them. Remember, other apps might be running on the machine at the same time.

Share this post


Link to post
Share on other sites

This topic is 3039 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this