c++ - 比较两个位图(设备上下文 - 文件)

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

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

我在 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 的第一个测试条件(在 Compare 方法中)。它在我们比较两个位图的位的最后一个 memcmp() 上失败。

在调试器中,两个位图的结构相同,两个 pBitmapInfoLeft\pBitmapInfoRight->bmiColors 字段之间只有很小的区别。

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

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

  • 乙肝

最佳答案

虽然有一个错误。

您使用了 BITMAPINFO 结构,这实际上是一个伪造的,并非设计为按原样使用。

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

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

BITMAPINFO 结构由BITMAPINFO 结构(bmiHeader)和bmiColors 组成,bmiColors 始终具有< strong>一个条目。 从来没有是这种情况。

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

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

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

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

关于c++ - 比较两个位图(设备上下文 - 文件),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9865438/

相关文章:

c++ - std::pow 不返回预期的 int 值

C++:如何使用 new 来查找函数返回值的存储?

c++ - 使用 C++ 删除目录中的所有 .txt

c++ - cv::findcontours 导致程序崩溃

c++ - WinSock 非阻塞 I/O

c++ - var_ptr==NULL 和 NULL==var_ptr 在各种arch中的区别

c++ - 允许 IFileOpenDialog 选择一个尚不存在的文件夹

java - 使用来自 java 的 windows "net use"命令和 win32 api

c++ - STL lower_bound 不符合规范

c++ - 多阵列动态内存分配错误