GetDIBits

Hello,

Below is a member function in an image class I've created - the purpose of this function is just to get the bytes from the screen and put them in an array of raw data.

The problem is that I get only a black image when I use this function, and I can't for the life of me figure out what I'm missing. I think I may be staring at this too hard and in my frustration I've missed something simple, which is why I figured posting it would help point out the problem. Any help is greatly appreciated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
void Image::LoadImage(HWND hwndSource, RECT rect)
{  
    //Free the current memory used by scanlines array
    delete[] scanlines;  // scanlines is a member char array, which may or may not already be filled    
    scanlines = NULL;

    width = rect.right - rect.left;
    height = rect.bottom - rect.top;
    
    //Get DC from window
    HDC hdcSource = GetDC(hwndSource);
    
    //Create DC into which to select output bitmap
    HDC hdcDest = CreateCompatibleDC(hdcSource);

    //Create the bitmap
    HBITMAP hbmOutput = CreateCompatibleBitmap(hdcSource,width,height);
    
    //Select bitmap into the compatible device context
    HBITMAP hbmOld = (HBITMAP)SelectObject(hdcDest,hbmOutput);

    //Block-transfer the image data from screen dc into output dc
    BitBlt(hdcDest,0,0,width,height,hdcSource,rect.left,rect.top,SRCCOPY);

    //Determine padding for use in allocating new memory
    int padding = 0;
    while ( (width * 3 + padding) % 4 != 0) padding++;
    
    //Create and fill BITMAPINFO structure to pass to GetDIBits
    BITMAPINFO bmi;
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = width;
    bmi.bmiHeader.biHeight = height;
    bmi.bmiHeader.biPlanes = 1; 
    bmi.bmiHeader.biBitCount = 24; 
    bmi.bmiHeader.biCompression = 0; 
    bmi.bmiHeader.biSizeImage = height * (width * 3 + padding); 
    bmi.bmiHeader.biXPelsPerMeter = 0; 
    bmi.bmiHeader.biYPelsPerMeter = 0; 
    bmi.bmiHeader.biClrUsed = 0; 
    bmi.bmiHeader.biClrImportant = 0; 
     
    //Allocate memory
    scanlines = new unsigned char[height*(width*3+padding)];   
    
    //Acquire bytes from bitmap
    GetDIBits(hdcDest,hbmOutput,0,height,scanlines,&bmi,DIB_RGB_COLORS);
    
    //Free output bitmap
    SelectObject(hdcDest,hbmOld);
    DeleteObject(hbmOutput);
    
    //Free DCs
    ReleaseDC(hwndSource,hdcSource);
    DeleteDC(hdcDest);
}
Last edited on
first, make you probably already know this, but be extremely careful deleting your 'scanlines' unless you are certain that there was already memory allocated beforehand!

also, i'm not positive, but i think char's are by default unsigned, so i don't think that's necessary when calling new to allocate the memory.

ok, so reading MSDN on the BITMAPINFOHEADER structure ( http://msdn.microsoft.com/en-us/library/ms532290(VS.85).aspx ), there were a couple of things i noticed. the first is under 'biBitCount' where it has 24, it says that the 'bmiColors' member of BITMAPINFO should be NULL. next; you may have already looked in the header files and know this, but for the compression, I don't know what the actual value of BI_RGB( uncompressed ) is defined as, so i'd just use that define instead of '0'. if you keep reading, you'll see that for uncompressed images, the 'biSizeImage' member can be '0'. and finally, but the 'biX(Y)PelsPerMeter' members specify resolutions, so by putting them at '0', it seems to me like you are telling the function that there are no pixels to get, because the resolution is 0 pixels per meter. I am not sure, you may be able to leave them at 0, but you should likewise be able to use GetDeviceCaps() to find the millimeters for each dimension, and the pixels for each dimension, then it's just a simple conversion from there. Hopefully something above will be useful, good luck!
1) The class constructor sets scanlines to NULL. Therefore it either contains data or is NULL. Since it IS safe to delete a NULL pointer, there is nothing wrong with that part.
2) Char is not by default unsigned on every architecture, so even if it's redundant, it's the safe thing to do.
3) There is no need to fill the XPelsPerMeters or YPelsPerMeter with anything other than zero.
4) BI_RGB is already defined as zero anyway.
5) biSizeImage can be zero, but doesn't have to be.

So, all in all, none of that solves the problem (thanks anyway though - sometimes I miss the obvious stuff). Am I somehow misusing GetBIBits? I have another function in the class that uses SetDIBits, and it works just fine. Since GetDIBits is essentially just the reverse, I can't see what the problem is here.
Topic archived. No new replies allowed.