65.9K
CodeProject 正在变化。 阅读更多。
Home

从屏幕捕获位图

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.98/5 (14投票s)

2006年2月7日

4分钟阅读

viewsIcon

81471

downloadIcon

1595

位图的结构。

Sample Image

引言

本文演示了如何从屏幕上的图像创建和保存位图,以及如何将一个位计数位图转换为另一个。该项目包含一个位图样本文件夹。不同大小的位图可供选择,名称从 Bitmap1.bmpBitmap32.bmpBitmapDemo.bmp 是测试图像,用于创建样本的位图是:fish*.bmplake.bmpstar.bmpfish 位图是 32 位的,lake 位图是 8 位的,star 是 4 位的。您会注意到 Bitmap8Bitmap32 具有良好的颜色属性,Bitmap4 因为只使用 8 种颜色而失去了一些颜色,而 Bitmap1 是 2 种颜色,黑色或白色。程序的典型用法是按 Ctrl+O 打开位图文件,按 Ctrl+C 创建图像,按 Ctrl+S 保存文件,或按 Ctrl+A 使用“另存为”将图像保存到位图文件。按 Ctrl+ICtrl+T 可在文本或位图图像之间切换。默认图像大小为 24。

背景

我于 1998 年 9 月开始使用 Microsoft Visual C++ 6.0。很快我开始使用位图,但从未理解它们是如何创建的或它们的文件的格式。这就是我编写这个程序的原因。它创建标准大小的位图:1、4、8、16、24 和 32 位。后两者,24 位和 32 位,是从图像数组创建的,我从中获取屏幕上的像素,将它们保存在一个 BYTE 数组中,创建位图,然后将其保存到文件中。这些的格式非常简单,文件包含一个 14 字节的 BITMAPFILEHEADER,后跟 BITMAPINFO 结构,它是一个 40 字节的 BITMAPINFOHEADER,然后是位图数据,这是 4 字节的 RGB 颜色信息,只使用前三个,第四个被忽略。每个像素使用一个 32 位值。24 位位图与 32 位位图相同,只是没有创建第四个字节,从而为每个像素数据节省了 1 字节。文件格式是小端序,低位字节在前。前三种类型,32、24、16 永不压缩且不使用调色板。16 位位图颜色字段是 5 位的三字节或三个 DWORD 颜色掩码,取决于 BITMAPINFOHEADERbiCompression DWORD 值;因此,我决定不为此编写代码,而是使用了 Charles Petzold"Programming Windows Fifth Edition" 中的 DIBBLE 位图和调色板程序。DIBBLE 是一个 "C" 程序,而我使用的是 "C++",这导致了一些问题(参见“趣味点”) 。

使用代码

代码最初是一个 Win32 应用程序,典型的“Hello World”应用程序。为了使代码编写更容易,我决定使用一些 "MFC" 类,例如 CMenuCBitmapCString 和通用对话框 CFileDialog。读者请注意:要更改为 "MFC" 应用程序,在 项目设置 菜单的 常规 选项卡中,将其更改为 "在共享 DLL 中使用 MFC",然后在 C\C++ 选项卡中,对于 代码生成,使用 "多线程 DLL"。这些更改必须同时为 DebugRelease 配置设置。WinMainMyRegisterClassInitInstanceWndProc 函数是由 Visual Studio C++ Wizard 创建的。其余代码是添加的。消息循环需要进行更改,我正在使用键盘加速器。为了使消息循环能够处理它们并打开相应的菜单项,需要进行以下更改

int APIENTRY WinMain(...)
{
    HACCEL hAccelTable;
    MSG msg;
    //
    // Main message loop
    //
    hAccelTable = LoadAccelerators (hInstance, szWindowClass);
    while (GetMessage (&msg, NULL, 0, 0))
    {
       if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg))
       {
           TranslateMessage(&msg);
           DispatchMessage(&msg);
       }
   }
   return msg.wParam;
}

这是从屏幕获取像素并创建图像数组的函数

bool CreateImageFromArray(HDC hdc) 
{
    long w = m_nWidth;
    long h = m_nHeight;
    BYTE c = m_nBitsPerPixel/8;  // bitsperpixel.
    
    BYTE* pArray = (BYTE*)malloc(w*h*c);
    int cxCursor = cxClient/2 - w/2;
    int cyCursor = cyClient/2 - h/2;
    int cxCapture = cxCursor;
    int cyCapture = cyCursor;
    
    //  ==========================================
    //  Get the device context of the desktop
    //  and from it get the color 
    //  of the pixel at the current position.
    //  ==========================================
    
    BYTE rVal, gVal, bVal;
    COLORREF m_color;
    
    int n, x, y;
    
    obTimer.Start();
    
    for (y = 0; y < h; y++)
    {
        for (x = 0; x < w; x++)
        {
            m_color = ::GetPixel(hdc, cxCapture, cyCapture);
            rVal = GetRValue(m_color);
            gVal = GetGValue(m_color);
            bVal = GetBValue(m_color);
            n = c* (x + w*y);
            if (c == 4)    // if bitsperpixel = 32.
                pArray[n + 3] = (BYTE)(0);
            pArray[n + 2] = (BYTE)(rVal);
            pArray[n + 1] = (BYTE)(gVal);
            pArray[n]     = (BYTE)(bVal);
            
            cxCapture = cxCursor + x;
            cyCapture = cyCursor + y;
        }
    }
    CreateFromArray(pArray, w, h, 8*c, c*w, true);
    
    free(pArray);

在使用非 MFC 生成的程序中的 Afx 支持时遇到的一个问题是,在 Debug 模式下,当步入 CFileDialog(...) 时,您会收到来自 AfxGetResourceHandle() 的异常。我在 MSDN 库中找到了一个解决方法

BOOL PromptForFileName(BOOL bOpenFileDialog)
{
    // Hack for AfxGetResourceHandle Exception
    HMODULE hMod;
    hMod = ::GetModuleHandle("BitmapFromScreen.exe");
    AfxSetResourceHandle(hMod);
    
    BOOL bRet;
    CString szFilter = "Windows Bitmap Files" 
                       " (*.BMP; *.DIB) |*.BMP; *.DIB||";
    LPSTR title;
    if (bOpenFileDialog)
    {
        title = "Open image file";
        CFileDialog dlg(TRUE, _T("bmp"), _T("*.bmp"),
            OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilter);
        dlg.m_ofn.lpstrTitle = title;
        CenterWindow(dlg.m_hWnd);
        if (dlg.DoModal() == IDOK)
        ...
        ...
}

关注点

DIBBLE 的源代码文件都是 "C" 语言。当转换为 "C++" 进行编译时,会出现几个差异,"C++" 不处理 VOID*,并且所有对 malloc 的调用都必须 强制转换为 适当的类型。所有变量都不会初始化为零。 "C" 例程 qsort 因为 Compare 回调中的 VOID* 而无法在 "C++" 中编译。我编写了一个 qsort 的替代品

// Sorts the array of structures
// in decending order, largest first.
VOID DibSortPal(BOXES* boxes, int iEntry)
{
    int nCmp;
    BOXES box;
    for (int i=0; i<iEntry; i++)
        for (int j=0; j<iEntry; j++)
        {
            nCmp = boxes[j].iBoxCount - boxes[j+1].iBoxCount;
            if (nCmp < 0)
            {
                box.iBoxCount  = boxes[j].iBoxCount;
                box.rgbBoxAv = boxes[j].rgbBoxAv;
                boxes[j].iBoxCount = boxes[j+1].iBoxCount;
                boxes[j].rgbBoxAv = boxes[j+1].rgbBoxAv;
                boxes[j+1].iBoxCount = box.iBoxCount;
                boxes[j+1].rgbBoxAv = box.rgbBoxAv;
            }
        }
}

下一个问题是 DIBHELP.C 作者的一个错误,他有这段代码

     int cEntries = 0 ;

     if (cColors != 0)
          cEntries = cColors ;
     else if (cBits <= 8)
          cEntries = 1 << cBits ;

     // here is the error, if cColors = 0 and cBits
     // is greater than 8, cEntries will = 0
     // the sizeof BITMAPINFOHEADER = 40 + 0 - 1 = - 1 * sizeof of RGBQUAD = 4
     // dwInfoSize will = 36, not 40
     dwInfoSize = sizeof (BITMAPINFOHEADER) + 
                  (cEntries - 1) * sizeof (RGBQUAD) ;
     // the fix
     dwInfoSize = sizeof (BITMAPINFOHEADER) + (cEntries - 1) * 
                  sizeof (RGBQUAD) + sizeof (RGBQUAD) ; // = 40
     // this call to malloc will alocate only 36 bytes.
     if (NULL == (pbmi = malloc (dwInfoSize)))
     {
          return NULL ;
     }
     // then:
     pbmi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER) ; // this is 40.
     ...
     ...
     free (pbmi)
     // Heap corruption, the previous
     // operation wrote past the end of the buffer.

我第一次执行这段代码时,cEntries 被定义为 int cEntries;,这是从 "C" 语言继承过来的,因此 cEntries 等于 0xcdcdcdcd。它试图将 300 多 MB 的数据写入页面文件。

致谢

  • Charles Petzold - "Programming Windows - Fifth Edition"。

历史

  • 2006 年 2 月 8 日 - 版本 1.0。
© . All rights reserved.