【问题标题】:Compare two bitmap (device context - file)比较两个位图(设备上下文 - 文件)
【发布时间】:2012-03-26 01:02:51
【问题描述】:

我需要比较两个位图。一个位图是从文件加载的,第二个是来自设备上下文的位图。文件位图由同一程序生成,用于测试目的。

我在vc10/win7上编程

我故意不处理错误以保持帖子上的代码清晰。

第一步,我制作了一个rgb24位图文件并保存为“test.bmp”:

void GetBitmap24FromDcToFile(HDC winDC, int x, int y, int w, int h)
{
int imgsize;

if((3 * w) % 4 > 0)
    imgsize = ((3 * w) / 4 + 1) * 4 * h;
else if((3 * w) % 4 == 0)
    imgsize = 3 * w * h;

BITMAPINFO bi;
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = w;
bi.bmiHeader.biHeight = h;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = 24;
bi.bmiHeader.biCompression = BI_RGB;
bi.bmiHeader.biSizeImage = imgsize;
bi.bmiHeader.biXPelsPerMeter = 0;
bi.bmiHeader.biYPelsPerMeter = 0;
bi.bmiHeader.biClrUsed = 0;
bi.bmiHeader.biClrImportant = 0;

void *pvBits = NULL;
HBITMAP hbmp = ::CreateDIBSection(winDC, &bi, DIB_RGB_COLORS, &pvBits, NULL, 0);
HDC hdc = ::CreateCompatibleDC(winDC);
HBITMAP holdbmp = (HBITMAP)::SelectObject(hdc, hbmp);
::BitBlt(hdc, 0, 0, w, h, winDC, x, y, SRCCOPY);

HANDLE hFile = ::CreateFile(_T("test.bmp"), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

DWORD dwCnt;
BITMAPFILEHEADER bmfh;

ZeroMemory(&bmfh, sizeof(BITMAPFILEHEADER));
bmfh.bfType = 0x4d42;
bmfh.bfSize = imgsize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

WriteFile(hFile, (char*)&bmfh, sizeof(BITMAPFILEHEADER), &dwCnt, NULL);
WriteFile(hFile, (char*)&bi.bmiHeader, sizeof(BITMAPINFOHEADER), &dwCnt, NULL);
WriteFile(hFile, (char*)pvBits, imgsize, &dwCnt, NULL);
CloseHandle(hFile);

::SelectObject(hdc, holdbmp);
::DeleteDC(hdc);
::DeleteObject(hbmp);
}

第二步,我从设备上下文制作位图:

HBITMAP GetBitmap24FromDC(HDC winDC, int x, int y, int w, int h)
{
HDC  hMemDC = ::CreateCompatibleDC( winDC );
HBITMAP hbmp; // = ::CreateCompatibleBitmap( winDC, w, h);

BITMAPINFOHEADER infoHeader; 
infoHeader.biSize          = sizeof(infoHeader); 
infoHeader.biWidth         = (LONG)w; 
infoHeader.biHeight        = (LONG)h; 
infoHeader.biPlanes        = 1; 
infoHeader.biBitCount      = 24; 
infoHeader.biCompression   = BI_RGB; 
infoHeader.biSizeImage     = 0; 
infoHeader.biXPelsPerMeter = 0; 
infoHeader.biYPelsPerMeter = 0; 
infoHeader.biClrUsed       = 0; 
infoHeader.biClrImportant  = 0; 

BITMAPINFO info; 
info.bmiHeader = infoHeader; 

unsigned char *mem;
hbmp = CreateDIBSection(winDC, &info, DIB_RGB_COLORS, (void**)&mem, 0, 0);
HBITMAP holdbmp = (HBITMAP) ::SelectObject(hMemDC, hbmp); 
::BitBlt(hMemDC, 0, 0, w, h, winDC, x, y, SRCCOPY);
    ::SelectObject(hMemDC, holdbmp);
::DeleteDC(hMemDC);

return hbmp;
}

我使用这种方法进行比较:

// Author: PJ Arends - codeproject
bool CompareBitmaps(HBITMAP HBitmapLeft, HBITMAP HBitmapRight)
{
if (HBitmapLeft == HBitmapRight)
{
    return true;
}

if (NULL == HBitmapLeft || NULL == HBitmapRight)
{
    return false;
}

bool bSame = false;

HDC hdc = GetDC(NULL);
BITMAPINFO BitmapInfoLeft = {0};
BITMAPINFO BitmapInfoRight = {0};

BitmapInfoLeft.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfoRight.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

if (0 != GetDIBits(hdc, HBitmapLeft, 0, 0, NULL, &BitmapInfoLeft, DIB_RGB_COLORS) &&
    0 != GetDIBits(hdc, HBitmapRight, 0, 0, NULL, &BitmapInfoRight, DIB_RGB_COLORS))
{
    // Compare the BITMAPINFOHEADERs of the two bitmaps

    if (0 == memcmp(&BitmapInfoLeft.bmiHeader, &BitmapInfoRight.bmiHeader, 
        sizeof(BITMAPINFOHEADER)))
    {
        // The BITMAPINFOHEADERs are the same so now compare the actual bitmap bits

        BYTE *pLeftBits = (BYTE*)malloc(sizeof(BYTE) * BitmapInfoLeft.bmiHeader.biSizeImage);
        BYTE *pRightBits = (BYTE*)malloc(sizeof(BYTE) * BitmapInfoRight.bmiHeader.biSizeImage);
        BYTE *pByteLeft = NULL;
        BYTE *pByteRight = NULL;

        PBITMAPINFO pBitmapInfoLeft = &BitmapInfoLeft;
        PBITMAPINFO pBitmapInfoRight = &BitmapInfoRight;

        // calculate the size in BYTEs of the additional

        // memory needed for the bmiColor table

        int AdditionalMemory = 0;
        switch (BitmapInfoLeft.bmiHeader.biBitCount)
        {
        case 1:
            AdditionalMemory = 1 * sizeof(RGBQUAD);
            break;
        case 4:
            AdditionalMemory = 15 * sizeof(RGBQUAD);
            break;
        case 8:
            AdditionalMemory = 255 * sizeof(RGBQUAD);
            break;
        case 16:
        case 32:
            AdditionalMemory = 2 * sizeof(RGBQUAD);
        }

        if (AdditionalMemory)
        {
            // we have to allocate room for the bmiColor table that will be

            // attached to our BITMAPINFO variables

            pByteLeft = new BYTE[sizeof(BITMAPINFO) + AdditionalMemory];
            if (pByteLeft)
            {
                memset(pByteLeft, 0, sizeof(BITMAPINFO) + AdditionalMemory);
                memcpy(pByteLeft, pBitmapInfoLeft, sizeof(BITMAPINFO));
                pBitmapInfoLeft = (PBITMAPINFO)pByteLeft;
            }

            pByteRight = new BYTE[sizeof(BITMAPINFO) + AdditionalMemory];
            if (pByteRight)
            {
                memset(pByteRight, 0, sizeof(BITMAPINFO) + AdditionalMemory);
                memcpy(pByteRight, pBitmapInfoRight, sizeof(BITMAPINFO));
                pBitmapInfoRight = (PBITMAPINFO)pByteRight;
            }
        }

        if (pLeftBits && pRightBits && pBitmapInfoLeft && pBitmapInfoRight)
        {
            // zero out the bitmap bit buffers

            memset(pLeftBits, 0, BitmapInfoLeft.bmiHeader.biSizeImage);
            memset(pRightBits, 0, BitmapInfoRight.bmiHeader.biSizeImage);

            // fill the bit buffers with the actual bitmap bits

            if (0 != GetDIBits(hdc, HBitmapLeft, 0, 
                pBitmapInfoLeft->bmiHeader.biHeight, pLeftBits, pBitmapInfoLeft, 
                DIB_RGB_COLORS) && 0 != GetDIBits(hdc, HBitmapRight, 0, 
                pBitmapInfoRight->bmiHeader.biHeight, pRightBits, pBitmapInfoRight, 
                DIB_RGB_COLORS))
            {
                // compare the actual bitmap bits of the two bitmaps

                bSame = 0 == memcmp(pLeftBits, pRightBits, 
                    pBitmapInfoLeft->bmiHeader.biSizeImage);
            }
        }

        // clean up
        free(pLeftBits);
        free(pRightBits);
        free(pByteLeft);
        free(pByteRight);
    }
}

ReleaseDC(NULL, hdc);

return bSame;
}

所以,在我的主要代码中我有类似的东西:

   (...)
   HWND capture = ::FindWindow(_T("the_window_class"), NULL);
   HDC winDC = ::GetDC(capture);
   GetBitmap24FromDcToFile(winDC, 0, 0, 200, 200); // generate bitmap file "test.bmp"
   HBITMAP bmpFile = (HBITMAP)LoadImage( NULL, _T("test.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION ); 
   HBITMAP bmpMem  = GetBitmap24FromDC(winDC, 0, 0, 200, 200); // get bitmap from DC

   bool isEqual = CompareBitmaps(bmpFile, bmpMem); // test both bitmaps
   if(isEqual)
      AfxMessageBox(_T("Success"));
   (...)

两个文件之间的比较返回true;来自 dc 的两个位图返回 true; 位图文件和 dc 位图的比较总是返回 false。

调试后,它通过了我们检查 BITMAPINFOHEADER 的第一个测试条件(在比较方法中)。它在我们比较两个位图的位的最后一个 memcmp() 上失败。

在调试器中,两个位图的结构是相同的,我只有两个 pBitmapInfoLeft\pBitmapInfoRight->bmiColors 字段之间的细微差别。

检查两个位图标头中的位是否相同(pLeftBits\pRightBits)。

一个想法,一个替代方案,一个例子?让我知道!谢谢!

  • JE

【问题讨论】:

    标签: c++ winapi visual-c++ graphics bitmap


    【解决方案1】:

    不过有一个错误。

    您使用BITMAPINFO 结构,它实际上是一个假的,不是设计为按原样使用的。

    实际的位图标头由一个固定的BITMAPINFOHEADER 结构和一个可变大小 数组RGBQUAD 结构组成,而这个数组的大小取决于BITMAPINFOHEADER 中的数据.根据位图位数,此数组应具有以下长度:

    • 1/4/8:数组大小应为 2^bitness。 IE。 2/16/256 分别。位图被认为是索引,这个数组中的值定义了实际的颜色。
    • 16:像素值使用所谓的位域转换为颜色。数组大小取决于biCompression 成员:
      • BI_RGB: 数组应该是empty。使用默认位域 5-5-5。
      • BI_BITFIELDS: 数组应该有 3 个条目。为 R/G/B 通道定义适当的位掩码。
    • 32:像素值或者直接对应于颜色,或者如果biCompression 设置为BI_BITFIELDS,则使用位域进行转换。与 16 位的情况一样,该数组应为 empty 或具有 3 个条目。

    BITMAPINFO 结构由BITMAPINFO 结构(bmiHeader) 和bmiColors 组成,bmiColors 总是有一个 条目。 从来没有是这样的。

    这就是为什么BITMAPINFO 实际上是一个假结构。为了创建位图标头,应首先为BITMAPINFOHEADER 和所需的数组分配所需的内存量,然后将其强制转换BITMAPINFO

    简单来说:比较BITMAPINFO 结构(即使用sizeof(BITMAPINFO))没有意义。 bmiColors 要么包含未初始化的数据,要么无法访问,或者实际上具有更大的大小。

    附:顺便说一句,整个位图比较有点脏恕我直言。将位图保存到文件中,只是为了比较 - 看起来很疯狂。此外,您实际上不需要为整个位图分配内存,可以逐行比较。

    此外,如果其中一个位图是 DIB,您可以直接获取指向其位的指针,因此不需要分配额外的内存和复制。

    【讨论】:

      【解决方案2】:

      我相信您可以使用SoIL Library(实际上是除 WinApi 之外的任何其他工具)来加载和操作位图文件。它免费且轻量级,可将您的代码缩短约 90%。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-08-25
        • 1970-01-01
        • 2022-01-04
        相关资源
        最近更新 更多