如何以16/24/32位颜色正确捕获屏幕位图

时间:2013-06-27 09:59:20

标签: winapi bitmap win32gui

我在位图中捕获桌面窗口的特定部分并尝试打印 BGR像素颜色值。桌面窗口的捕获部分完全填充了16,0,16种颜色。

当我的窗口颜色深度为32时捕获并打印数据时,一切都是正确的, 但如果我的窗口处于24/16位彩色模式,那么我得到的是不同的像素值,而不是16,0,16。

我正在捕捉屏幕左= 150,顶部= 150,右= 200,底部= 200。 * ** * ** * ** 从桌面捕获图像 * ** * ** * **

    iLeft = 150;
    iTop = 150;
    iRight = iLeft + 50;
    iBottom = iTop + 50;



        /*

    HDC hdcScreen;
    HDC hdcWindow;
    HDC hdcMemDC = NULL;
    HBITMAP hbmScreen = NULL;
    BITMAP bmpScreen;

    // Retrieve the handle to a display device context for the client 
    // area of the window. 
    hdcScreen = GetDC(NULL);
    hdcWindow = GetDC(hWnd);

    // Create a compatible DC which is used in a BitBlt from the window DC
    hdcMemDC = CreateCompatibleDC(hdcWindow); 

    if(!hdcMemDC)
    {
        MessageBox(hWnd, L"CreateCompatibleDC has failed",L"Failed", MB_OK);
        goto done;
    }

    // Get the client area for size calculation
    RECT rcClient;
    GetClientRect(hWnd, &rcClient);

    //This is the best stretch mode
    SetStretchBltMode(hdcWindow,HALFTONE);

    //The source DC is the entire screen and the destination DC is the current window (HWND)
    if(!StretchBlt(hdcWindow, 
               0,0, 
               rcClient.right, rcClient.bottom, 
               hdcScreen, 
               0,0,
               GetSystemMetrics (SM_CXSCREEN),
               GetSystemMetrics (SM_CYSCREEN),
               SRCCOPY))
    {
        MessageBox(hWnd, L"StretchBlt has failed",L"Failed", MB_OK);
        goto done;
    }

    // Create a compatible bitmap from the Window DC
    hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);

    if(!hbmScreen)
    {
        MessageBox(hWnd, L"CreateCompatibleBitmap Failed",L"Failed", MB_OK);
        goto done;
    }

    // Select the compatible bitmap into the compatible memory DC.
    SelectObject(hdcMemDC,hbmScreen);

    // Bit block transfer into our compatible memory DC.
    if(!BitBlt(hdcMemDC, 
               0,0, 
               rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, 
               hdcWindow, 
               0,0,
               SRCCOPY))
    {
        MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
        goto done;
    }

    // Get the BITMAP from the HBITMAP
    GetObject(hbmScreen,sizeof(BITMAP),&bmpScreen);

    BITMAPFILEHEADER   bmfHeader;    
    BITMAPINFOHEADER   bi;

    bi.biSize = sizeof(BITMAPINFOHEADER);    
    bi.biWidth = bmpScreen.bmWidth;    
    bi.biHeight = bmpScreen.bmHeight;  
    bi.biPlanes = 1;    
    bi.biBitCount = 32;    
    bi.biCompression = BI_RGB;    
    bi.biSizeImage = 0;  
    bi.biXPelsPerMeter = 0;    
    bi.biYPelsPerMeter = 0;    
    bi.biClrUsed = 0;    
    bi.biClrImportant = 0;

    DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

    // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that 
    // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc 
    // have greater overhead than HeapAlloc.
    HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize); 
    char *lpbitmap = (char *)GlobalLock(hDIB);    


    // Gets the "bits" from the bitmap and copies them into a buffer 
    // which is pointed to by lpbitmap.
    GetDIBits(hdcWindow, hbmScreen, 0,
        (UINT)bmpScreen.bmHeight,
        lpbitmap,
        (BITMAPINFO *)&bi, DIB_RGB_COLORS);

    // A file is created, this is where we will save the screen capture.
    HANDLE hFile = CreateFile(L"captureqwsx.bmp",
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, NULL);   

    // Add the size of the headers to the size of the bitmap to get the total file size
    DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    //Offset to where the actual bitmap bits start.
    bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); 

    //Size of the file
    bmfHeader.bfSize = dwSizeofDIB; 

    //bfType must always be BM for Bitmaps
    bmfHeader.bfType = 0x4D42; //BM   

    DWORD dwBytesWritten = 0;
    WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);

    //Unlock and Free the DIB from the heap
    GlobalUnlock(hDIB);    
    GlobalFree(hDIB);

    //Close the handle for the file that was created
    CloseHandle(hFile);


    //Clean up
done:
    DeleteObject(hbmScreen);
    DeleteObject(hdcMemDC);
    ReleaseDC(NULL,hdcScreen);
    ReleaseDC(hWnd,hdcWindow);

    return 0;
*/

//#if 0

    HDC hdcScreen;
   // HDC hdcWindow;
    HDC hdcMemDC = NULL;
    HBITMAP hbmScreen = NULL;
    HRGN rgn = NULL;

    BITMAP bmpScreen;

    // Retrieve the handle to a display device context for the client 
    // area of the window. 
    hdcScreen = GetDC(NULL);
    //hdcWindow = GetDC(hWnd);

    //if(g_hdcMemDC == NULL)
    {
        // Create a compatible DC which is used in a BitBlt from the window DC
       hdcMemDC = CreateCompatibleDC(hdcScreen); 
       if(!hdcMemDC)
        {
            //MessageBox(hWnd, L"CreateCompatibleDC has failed",L"Failed", MB_OK);
            goto done;
        }

        // Get the client area for size calculation
        //RECT rcClient;
        //GetClientRect(hWnd, &rcClient);

        //This is the best stretch mode
        SetStretchBltMode(hdcMemDC,HALFTONE);
          // Create a compatible bitmap from the Window DC
            hbmScreen = CreateCompatibleBitmap(hdcScreen, iRight - iLeft, iBottom - iTop);

            if(!hbmScreen)
            {
                //MessageBox(hWnd, L"CreateCompatibleBitmap Failed",L"Failed", MB_OK);
                goto done;
            }

            // Select the compatible bitmap into the compatible memory DC.
            SelectObject(hdcMemDC,hbmScreen);

    }


    //The source DC is the entire screen and the destination DC is the current window (HWND)
  /*  if(!StretchBlt(hdcWindow, 
               0,0, 
               rcClient.right, rcClient.bottom, 
               hdcScreen, 
               0,0,
               GetSystemMetrics (SM_CXSCREEN),
               GetSystemMetrics (SM_CYSCREEN),
               SRCCOPY))
    {
        MessageBox(hWnd, L"StretchBlt has failed",L"Failed", MB_OK);
        goto done;
    }
    */


    // Bit block transfer into our compatible memory DC.
     if(!StretchBlt(hdcMemDC, 
               0,0, 
               iRight - iLeft,
               iBottom - iTop,
               hdcScreen, 
               iLeft,iTop,
               iRight - iLeft,
               iBottom - iTop,
               SRCCOPY))
    {
       // MessageBox(hWnd, L"StretchBlt has failed",L"Failed", MB_OK);
        goto done;
    }
  }
*******************FUNCTION FOR GETTING THE BITMAP RAW DATA POINTER***********
BYTE* Get24BitPixels(HDC dcDesktop, HBITMAP pBitmap, WORD *pwWidth, WORD *pwHeight, WORD * pReminderWidth)
{


  // a bitmap object just to get bitmap width and height
    BITMAP bmpBmp;

  // pointer to original bitmap info
    LPBITMAPINFO pbmiInfo;

  // bitmap info will hold the new 24bit bitmap info
  BITMAPINFO bmiInfo;

  // width and height of the bitmap
  WORD wBmpWidth ; WORD wBmpHeight;

  // ---------------------------------------------------------
  // get some info from the bitmap
  // ---------------------------------------------------------
  GetObject(pBitmap, sizeof(bmpBmp),&bmpBmp);
  pbmiInfo   = (LPBITMAPINFO)&bmpBmp;

   //get width and height
    wBmpWidth  = (WORD)pbmiInfo->bmiHeader.biWidth;
    int iReminderWidth = (wBmpWidth%4);                    
    //wBmpWidth -= (wBmpWidth%4);                       // width is 4 byte boundary aligned.
    wBmpHeight = (WORD)pbmiInfo->bmiHeader.biHeight;

  // copy to caller width and height parms
  *pwWidth  = wBmpWidth;
  *pwHeight = wBmpHeight;

  wBmpWidth += (4 - iReminderWidth);  // width is 4 byte boundary aligned, thereby increasing the width 
//so that it will be fully divible by four , it will cause some extra bytes to be filled in with garbage value
//beyond the actual width of the bitmap, we will be discrading this extra padding pixels data while processign each pixel.


*pReminderWidth = 4  - iReminderWidth;
  // ---------------------------------------------------------

    // allocate width * height * 24bits pixels
   BYTE * pPixels = new BYTE[wBmpWidth*wBmpHeight*3];
    if (!pPixels) return NULL;


  // get user desktop device context to get pixels from
    //HDC hDC = GetWindowDC(NULL);




  // fill desired structure
    bmiInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmiInfo.bmiHeader.biWidth = wBmpWidth;
    bmiInfo.bmiHeader.biHeight = -wBmpHeight;
    bmiInfo.bmiHeader.biPlanes = 1;
    bmiInfo.bmiHeader.biBitCount = 24;
    bmiInfo.bmiHeader.biCompression = BI_RGB;
    bmiInfo.bmiHeader.biSizeImage = wBmpWidth*wBmpHeight*3;
    bmiInfo.bmiHeader.biXPelsPerMeter = 0;
    bmiInfo.bmiHeader.biYPelsPerMeter = 0;
    bmiInfo.bmiHeader.biClrUsed = 0;
    bmiInfo.bmiHeader.biClrImportant = 0;



  // get pixels from the original bitmap converted to 24bits

    int iRes = GetDIBits(dcDesktop,pBitmap,0,wBmpHeight,(LPVOID)pPixels,&bmiInfo,DIB_RGB_COLORS);

  // release the device context
    //ReleaseDC(NULL,hDC);

  // if failed, cancel the operation.
    if (!iRes)
  {
    delete [] pPixels;
    pPixels = NULL;
    return NULL;
  };

  // return the pixel array
    return pPixels;
} 

1 个答案:

答案 0 :(得分:0)

幸运的是,我在StackOverflow上收到了以下提及的帖子。 非常感谢Vodemki发布了这个答案。

Get Pixel color fastest way?