我需要比较两个位图。一个位图是从文件加载的,第二个是从设备上下文加载的位图。文件位图由同一程序生成,用于测试目的。
我在 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
和所需的数组分配所需的内存量,然后将其cast 到 BITMAPINFO
.
简单来说:比较BITMAPINFO
结构(即使用sizeof(BITMAPINFO)
)没有意义。 bmiColors
将包含未初始化的数据,或者无法访问,或者实际上具有更大的大小。
附言顺便说一句,恕我直言,整个位图比较有点脏。将位图保存到文件中,只是为了比较 - 看起来很疯狂。而且你实际上不需要为整个位图分配内存,它可以逐行比较。
此外,如果其中一个位图是 DIB,您可以直接获得指向其位的指针,因此不需要分配额外的内存和复制。
关于c++ - 比较两个位图(设备上下文 - 文件),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9865438/