CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 11 of 11
  1. #1
    Join Date
    Apr 2008
    Posts
    26

    Problem with GetDIBits function

    I'm not sure where the problem is but my function (FindColor) is not returning the correct result. The goal of this function is to find a color in a window by using FindDIBits to create an array of every pixel, and then loop through the array until the pixel is found. Here is my code inside a simple Console App I was using for testing. I'm using Dev-C++ btw.
    Code:
    #include <cstdlib>
    #include <iostream>
    #include <windows.h>
    
    using namespace std;
    
    void FindColor (HWND hWnd, COLORREF Color, POINT * Result)
    {
        HDC hDC = GetDC (hWnd);
        
        RECT WinRECT;
        GetClientRect (hWnd, &WinRECT);
        
        HBITMAP hBMP = CreateCompatibleBitmap (hDC, WinRECT.right, WinRECT.bottom);
        
        BITMAPINFO BMPInfo;
        BMPInfo.bmiHeader.biSize = sizeof (BITMAPINFO);
        BMPInfo.bmiHeader.biWidth = WinRECT.right;
        BMPInfo.bmiHeader.biHeight = -WinRECT.bottom;
        BMPInfo.bmiHeader.biPlanes = 1;
        BMPInfo.bmiHeader.biBitCount = 32;
        BMPInfo.bmiHeader.biCompression = BI_RGB;
        
        RGBQUAD * BitArray;
        BitArray = new RGBQUAD[(WinRECT.right - 1) * (WinRECT.bottom - 1)];
        if (0 != GetDIBits (hDC, hBMP, 0, WinRECT.bottom, &BitArray, &BMPInfo, DIB_RGB_COLORS))
        {
            for (int iCountX; iCountX < WinRECT.right; iCountX++)
            {
                for (int iCountY; iCountY < WinRECT.bottom; iCountY++)
                {
                    if (Color = RGB(BitArray[iCountX + (iCountY * (WinRECT.bottom - 1))].rgbRed, BitArray[iCountX + (iCountY * (WinRECT.bottom - 1))].rgbGreen, BitArray[iCountX + (iCountY * (WinRECT.bottom - 1))].rgbBlue))
                    {
                        Result->x = iCountX;
                        Result->y = iCountY;
                        delete [] BitArray;
                        return;
                    }
                }
            }
        }
        delete [] BitArray;
    }
    
    int main(int argc, char *argv[])
    {
        POINT Location;
        FindColor (0, 0xFFFFFF, &Location);
        cout << Location.x << ',' << Location.y << '\n';
        system("PAUSE");
        return EXIT_SUCCESS;
    }
    Does anyone know what I've been doing wrong?
    Last edited by Brownhead; April 13th, 2008 at 12:29 PM.

  2. #2
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    Re: Problem with GetDIBits function

    I'm not sure where the problem is but my function (FindColor) is not returning the correct result.
    It could be anything, so you need to tell us more. Do some debugging. Is GetDiBits() returning correct results? If not, why not (probably not enough room in BitArray, which should be winRect+1, not -1)? If yes, then what's happening inside the loop that looks for the pixel?

    Single-step through the code and give us more symptoms.

    Mike

  3. #3
    Join Date
    Apr 2008
    Posts
    26

    Re: Problem with GetDIBits function

    Your right, I was assuming too much, there were a couple of problems but I'm still stuck on the second one. The call to GetDIBits is failing. It is returning 0 as a result, here is the altered code
    Code:
    #include <cstdlib>
    #include <iostream>
    #include <windows.h>
    
    #define BITARRAY iCountX + (iCountY * (WinRECT.bottom))
    
    using namespace std;
    
    void FindColor (HWND hWnd, COLORREF Color, POINT * Result)
    {
        HDC hDC = GetDC (hWnd);
        
        RECT WinRECT;
        if (hWnd != 0)
        {
            if (!GetClientRect (hWnd, &WinRECT))
            {
                cout << "Call to GetClientRect Failed" << '\n';
                return;
            }
        } else {
            WinRECT.right = GetSystemMetrics(SM_CXSCREEN);
            WinRECT.bottom = GetSystemMetrics(SM_CYSCREEN);
        }
        HBITMAP hBMP = CreateCompatibleBitmap (hDC, WinRECT.right, WinRECT.bottom);
        if (!hBMP) cout << "Call to CreateCompatibleBitmap Failed";
        
        BITMAPINFO BMPInfo;
        BMPInfo.bmiHeader.biSize = sizeof (BITMAPINFO);
        BMPInfo.bmiHeader.biWidth = WinRECT.right;
        BMPInfo.bmiHeader.biHeight = -WinRECT.bottom;
        BMPInfo.bmiHeader.biPlanes = 1;
        BMPInfo.bmiHeader.biBitCount = 32;
        BMPInfo.bmiHeader.biCompression = BI_RGB;
        
        RGBQUAD * BitArray;
        BitArray = new RGBQUAD[(WinRECT.right+1) * (WinRECT.bottom+1)];
        if (0 != GetDIBits (hDC, hBMP, 0, WinRECT.bottom, &BitArray, &BMPInfo, DIB_RGB_COLORS))
        {
            for (int iCountX; iCountX < WinRECT.right; iCountX++)
            {
                for (int iCountY; iCountY < WinRECT.bottom; iCountY++)
                {
                    if (Color = RGB(BitArray[BITARRAY].rgbRed, BitArray[BITARRAY].rgbGreen, BitArray[BITARRAY].rgbBlue))
                    {
                        Result->x = iCountX;
                        Result->y = iCountY;
                        delete [] BitArray;
                        return;
                    }
                }
            }
        } else {
            cout << "Call to GetDIBits Failed" << '\n';
        }
        delete [] BitArray;
    }
    
    int main(int argc, char *argv[])
    {
        POINT Location;
        FindColor (0, 0xFFFFFF, &Location);
        cout << Location.x << ',' << Location.y << '\n';
        system("PAUSE");
        return EXIT_SUCCESS;
    }
    Thanks for the help !

    I've tried changing the size of the array slightly but it still didn't work, I'm not sure if I just have a major problem with the sizing or if the problem is somewhere else. I've never used GetDIBits before.
    Last edited by Brownhead; April 13th, 2008 at 03:00 PM.

  4. #4
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    Re: Problem with GetDIBits function

    Re-read the documentation of GetDIBits(). It must be called twice. In the first call, set lpvBits to NULL. If lpvBits is NULL, then GetDIBits() will populate the lpbi structure for you. Use the structure as populated by the first call in the second call.

  5. #5

    Re: Problem with GetDIBits function

    Quote Originally Posted by Brownhead
    Your right, I was assuming too much, there were a couple of problems but I'm still stuck on the second one. The call to GetDIBits is failing. It is returning 0 as a result, here is the altered code
    Code:
    #include <cstdlib>
    #include <iostream>
    #include <windows.h>
    
    #define BITARRAY iCountX + (iCountY * (WinRECT.bottom))
    
    using namespace std;
    
    void FindColor (HWND hWnd, COLORREF Color, POINT * Result)
    {
        HDC hDC = GetDC (hWnd);
        
        RECT WinRECT;
        if (hWnd != 0)
        {
            if (!GetClientRect (hWnd, &WinRECT))
            {
                cout << "Call to GetClientRect Failed" << '\n';
                return;
            }
        } else {
            WinRECT.right = GetSystemMetrics(SM_CXSCREEN);
            WinRECT.bottom = GetSystemMetrics(SM_CYSCREEN);
        }
        HBITMAP hBMP = CreateCompatibleBitmap (hDC, WinRECT.right, WinRECT.bottom);
        if (!hBMP) cout << "Call to CreateCompatibleBitmap Failed";
        
        BITMAPINFO BMPInfo;
        BMPInfo.bmiHeader.biSize = sizeof (BITMAPINFO);
        BMPInfo.bmiHeader.biWidth = WinRECT.right;
        BMPInfo.bmiHeader.biHeight = -WinRECT.bottom;
        BMPInfo.bmiHeader.biPlanes = 1;
        BMPInfo.bmiHeader.biBitCount = 32;
        BMPInfo.bmiHeader.biCompression = BI_RGB;
        
        RGBQUAD * BitArray;
        BitArray = new RGBQUAD[(WinRECT.right+1) * (WinRECT.bottom+1)];
        if (0 != GetDIBits (hDC, hBMP, 0, WinRECT.bottom, &BitArray, &BMPInfo, DIB_RGB_COLORS))
        {
            for (int iCountX; iCountX < WinRECT.right; iCountX++)
            {
                for (int iCountY; iCountY < WinRECT.bottom; iCountY++)
                {
                    if (Color = RGB(BitArray[BITARRAY].rgbRed, BitArray[BITARRAY].rgbGreen, BitArray[BITARRAY].rgbBlue))
                    {
                        Result->x = iCountX;
                        Result->y = iCountY;
                        delete [] BitArray;
                        return;
                    }
                }
            }
        } else {
            cout << "Call to GetDIBits Failed" << '\n';
        }
        delete [] BitArray;
    }
    
    int main(int argc, char *argv[])
    {
        POINT Location;
        FindColor (0, 0xFFFFFF, &Location);
        cout << Location.x << ',' << Location.y << '\n';
        system("PAUSE");
        return EXIT_SUCCESS;
    }
    Thanks for the help !

    I've tried changing the size of the array slightly but it still didn't work, I'm not sure if I just have a major problem with the sizing or if the problem is somewhere else. I've never used GetDIBits before.
    Check the highlighted code. This is a common mistake, if you want a compare, it should be ==.

  6. #6
    Join Date
    Apr 2008
    Posts
    26

    Re: Problem with GetDIBits function

    *Slaps Head*
    ****, can't believe I missed that, thankyou :-), I'll see if that works.

  7. #7
    Join Date
    Apr 2008
    Posts
    26

    Re: Problem with GetDIBits function

    I'm sure that was going to be a large problem but GetDIBits still fails which is the current problem at hand.. I think the problem may be the array size but increasing it doesn't work so I'm not sure.. I'm prolly giving it the wrong information for one of the arguments but I can't figure out which one, and as I compare it with other examples it looks fine..

  8. #8

    Re: Problem with GetDIBits function

    You should be setting the size of the bmiHeader.biSize to sizeof(BITMAPINFOHEADER) and not sizeof(BITMAPINFO). You are setting the size of the bmiHeader variable, which is of type BITMAPINFOHEADER.

    That's another thing that's wrong. Another thing, index calculation for a bitmap is done like this.

    If it's bottom up (you have specified -height, so that would mean it's a top down bitmap) it's calculated like this:

    Code:
    int nIndex = ((nBitmapHeight - posY - 1) * nBitmapWidth) + posX; // for bottom up
    
    // top down follows
    int nIndex = (posY * nBitmapWidth) + posX;
    Your code is all wrong, you are doing a multiplication on height!

    Code:
    #define BITARRAY iCountX + (iCountY * (WinRECT.bottom))
    The #define above is WRONG FOR SURE.
    Last edited by JamesSchumacher; April 15th, 2008 at 12:45 AM.

  9. #9
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    Re: Problem with GetDIBits function

    If you are still having difficulty with the GetDIBits() function, then try downloading the code for WinCap in this MSDN KB article: "WINCAP Captures Screens Using DIB API" at http://support.microsoft.com/kb/q97193/

    The article and code show many different DDB and DIB techniques, but the one you are most interested in is the function to capture any window into a device-independent bitmap.
    Code:
    HDIB FAR BitmapToDIB(HBITMAP hBitmap, HPALETTE hPal)
    {
       BITMAP bm;                   // bitmap structure
       BITMAPINFOHEADER bi;         // bitmap header
       BITMAPINFOHEADER FAR *lpbi;  // pointer to BITMAPINFOHEADER
       DWORD dwLen;                 // size of memory block
       HANDLE hDIB, h;              // handle to DIB, temp handle
       HDC hDC;                     // handle to DC
       WORD biBits;                 // bits per pixel
    
       /* check if bitmap handle is valid */
    
       if (!hBitmap)
          return NULL;
    
       /* fill in BITMAP structure, return NULL if it didn't work */
       if (!GetObject(hBitmap, sizeof(bm), (LPSTR)&bm))
          return NULL;
    
       /* if no palette is specified, use default palette */
       if (hPal == NULL)
          hPal = GetStockObject(DEFAULT_PALETTE);
    
       /* calculate bits per pixel */
       biBits = bm.bmPlanes * bm.bmBitsPixel;
    
       /* make sure bits per pixel is valid */
       if (biBits <= 1)
          biBits = 1;
       else if (biBits <= 4)
          biBits = 4;
       else if (biBits <= 8)
          biBits = 8;
       else /* if greater than 8-bit, force to 24-bit */
          biBits = 24;
    
       /* initialize BITMAPINFOHEADER */
       bi.biSize = sizeof(BITMAPINFOHEADER);
       bi.biWidth = bm.bmWidth;
       bi.biHeight = bm.bmHeight;
       bi.biPlanes = 1;
       bi.biBitCount = biBits;
       bi.biCompression = BI_RGB;
       bi.biSizeImage = 0;
       bi.biXPelsPerMeter = 0;
       bi.biYPelsPerMeter = 0;
       bi.biClrUsed = 0;
       bi.biClrImportant = 0;
    
       /* calculate size of memory block required to store BITMAPINFO */
       dwLen = bi.biSize + PaletteSize((LPSTR)&bi);
    
       /* get a DC */
       hDC = GetDC(NULL);
    
       /* select and realize our palette */
       hPal = SelectPalette(hDC, hPal, FALSE);
       RealizePalette(hDC);
    
       /* alloc memory block to store our bitmap */
       hDIB = GlobalAlloc(GHND, dwLen);
    
       /* if we couldn't get memory block */
       if (!hDIB)
       {
          /* clean up and return NULL */
          SelectPalette(hDC, hPal, TRUE);
          RealizePalette(hDC);
          ReleaseDC(NULL, hDC);
          return NULL;
       }
    
       /* lock memory and get pointer to it */
       lpbi = (VOID FAR *)GlobalLock(hDIB);
    
       /* use our bitmap info. to fill BITMAPINFOHEADER */
       *lpbi = bi;
    
       /*  call GetDIBits with a NULL lpBits param, so it will calculate the
        *  biSizeImage field for us
        */
       GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, NULL, (LPBITMAPINFO)lpbi,
             DIB_RGB_COLORS);
    
       /* get the info. returned by GetDIBits and unlock memory block */
       bi = *lpbi;
       GlobalUnlock(hDIB);
    
       /* if the driver did not fill in the biSizeImage field, make one up */
       if (bi.biSizeImage == 0)
          bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight;
    
       /* realloc the buffer big enough to hold all the bits */
       dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + bi.biSizeImage;
       if (h = GlobalReAlloc(hDIB, dwLen, 0))
          hDIB = h;
       else
       {
          /* clean up and return NULL */
          GlobalFree(hDIB);
          hDIB = NULL;
          SelectPalette(hDC, hPal, TRUE);
          RealizePalette(hDC);
          ReleaseDC(NULL, hDC);
          return NULL;
       }
    
       /* lock memory block and get pointer to it */
       lpbi = (VOID FAR *)GlobalLock(hDIB);
    
       /*  call GetDIBits with a NON-NULL lpBits param, and actualy get the
        *  bits this time
        */
       if (GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, (LPSTR)lpbi + (WORD)lpbi
             ->biSize + PaletteSize((LPSTR)lpbi), (LPBITMAPINFO)lpbi,
             DIB_RGB_COLORS) == 0)
       {
          /* clean up and return NULL */
          GlobalUnlock(hDIB);
          hDIB = NULL;
          SelectPalette(hDC, hPal, TRUE);
          RealizePalette(hDC);
          ReleaseDC(NULL, hDC);
          return NULL;
       }
       bi = *lpbi;
    
       /* clean up */
       GlobalUnlock(hDIB);
       SelectPalette(hDC, hPal, TRUE);
       RealizePalette(hDC);
       ReleaseDC(NULL, hDC);
    
       /* return handle to the DIB */
       return hDIB;
    }
    Mike

  10. #10

    Re: Problem with GetDIBits function

    Quote Originally Posted by Brownhead
    Code:
        RGBQUAD * BitArray;
        BitArray = new RGBQUAD[(WinRECT.right - 1) * (WinRECT.bottom - 1)];
    You're allocation is wrong as well. You will have a buffer overrun, because the width is right - left, and in the situation of left being 0, the width is the right member. (Same goes for height calculations)

    Think if the rect has the following data.

    Code:
    RECT rectData;
    
    rectData.left = 0;
    rectData.right = 1;
    rectData.top = 0;
    rectData.bottom = 2;
    
    RGBQUAD * pData = new RGBQUAD[(rectData.right - 1) * (rectData.bottom - 1)]; // 0 * 1 == 0
    This is the reason why right and bottom are NOT inclusive to the rectangle's contents. They represent the edge.

    Left and top coordinates are inclusive due to the fact that '0 index is still an index into the array, it's the first'.
    Last edited by JamesSchumacher; April 15th, 2008 at 02:10 PM.

  11. #11
    Join Date
    Apr 2008
    Posts
    26

    Re: Problem with GetDIBits function

    Thankyou for the responses everyone :-)! I know nothing about how GetDIBits works or how to use it and was just trying to go off the examples I've looked at. I'll look into what everyone has said and see if I can't find the problem finally . Thanks again everyone!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured