我们发现在 Windows XP 下,使用位图作为按钮背景的自绘制按钮会偶尔出现显示位图错误的问题。如果同一窗口中包含多个使用相同位图文件作为按钮背景的按钮,则大多数按钮将正确显示,但某些情况下可能会有一个或两个按钮将显示缩小后的位图背景。
如果退出应用程序然后重新启动它,您可能会看到相同的按钮图标显示不正确的行为,但可能与之前的按钮不同。这种图标显示不正确的行为并不总是出现。有时会出现,有时则不会。由于我们只需加载一次按钮图标,因此一旦按钮显示不正确,它将始终显示不正确。
通过调试器,我们最终发现,在调用 GetObject() 函数时,返回的位图大小数据有时会不正确。例如,在某个情况下,位图大小是 75x75 像素,而 GetObject() 返回的大小却是 13x13。由于此大小是位图绘制的一部分,因此显示的背景变成了按钮窗口上的小装饰品。
实际源代码如下。
使用调试器,我们可以看到
该变量在类头文件中定义。
作为研究的一部分,我找到了这个栈溢出问题,GetObject返回奇怪的大小,我想知道这里是否存在某种对齐问题。
在调用
如果退出应用程序然后重新启动它,您可能会看到相同的按钮图标显示不正确的行为,但可能与之前的按钮不同。这种图标显示不正确的行为并不总是出现。有时会出现,有时则不会。由于我们只需加载一次按钮图标,因此一旦按钮显示不正确,它将始终显示不正确。
通过调试器,我们最终发现,在调用 GetObject() 函数时,返回的位图大小数据有时会不正确。例如,在某个情况下,位图大小是 75x75 像素,而 GetObject() 返回的大小却是 13x13。由于此大小是位图绘制的一部分,因此显示的背景变成了按钮窗口上的小装饰品。
实际源代码如下。
if (!hBitmapFocus) {
CString iconPath;
iconPath.Format(ICON_FILES_DIR_FORMAT, m_Icon);
hBitmapFocus = (HBITMAP)LoadImage(NULL, iconPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
}
if (hBitmapFocus) {
BITMAP bitmap;
int iNoBytes = GetObject(hBitmapFocus, sizeof(BITMAP), &bitmap);
if (iNoBytes < 1) {
char xBuff[128];
sprintf (xBuff, "GetObject() failed. GetLastError = %d", GetLastError ());
NHPOS_ASSERT_TEXT((iNoBytes > 0), xBuff);
}
cxSource = bitmap.bmWidth;
cySource = bitmap.bmHeight;
//Bitmaps cannot be drawn directly to the screen so a
//compatible memory DC is created to draw to, then the image is
//transfered to the screen
CDC hdcMem;
hdcMem.CreateCompatibleDC(pDC);
HGDIOBJ hpOldObject = hdcMem.SelectObject(hBitmapFocus);
int xPos;
int yPos;
//The Horizontal and Vertical Alignment
//For Images
//Are set in the Layout Manager
//the proper attribute will have to be checked against
//for now the Image is centered on the button
//Horizontal Alignment
if(btnAttributes.horIconAlignment == IconAlignmentHLeft){//Image to left
xPos = 2;
}else if(btnAttributes.horIconAlignment == IconAlignmentHRight){//Image to right
xPos = myRect.right - cxSource - 5;
}else {//Horizontal center
xPos = ((myRect.right - cxSource) / 2) - 1;
}
//Vertical Alignment
if(btnAttributes.vertIconAlignment == IconAlignmentVTop){//Image to top
yPos = 2;
}else if(btnAttributes.vertIconAlignment == IconAlignmentVBottom){//Image to bottom
yPos = myRect.bottom - cySource - 5;
}else{//Vertical Center
yPos = ((myRect.bottom - cySource) / 2) - 1;
}
pDC->BitBlt(xPos, yPos, cxSource, cySource, &hdcMem, 0, 0, SRCCOPY);
hdcMem.SelectObject(hpOldObject);
}
使用调试器,我们可以看到
iconPath
字符串是正确的,并且位图已加载,因为hBitmapFocus
不为NULL。接下来,我们可以看到调用GetObject()
,返回iNoBytes
的值等于24。对于那些正确显示的按钮,bitmap.bmWidth
和bitmap.bmHeight
中的值是正确的,但对于那些不正确的按钮,这些值要小得多,导致在绘制位图时尺寸不正确。该变量在类头文件中定义。
HBITMAP hBitmapFocus;
作为研究的一部分,我找到了这个栈溢出问题,GetObject返回奇怪的大小,我想知道这里是否存在某种对齐问题。
在调用
GetObject()
时,bitmap
变量是否需要处于某种对齐边界上?尽管我们对一些数据使用紧凑格式,但我们正在使用pragma
指令来仅指定包含需要在一个字节边界上打包的特定结构体的代码的特定部分,这些结构体在包含文件中。
BitBlt
在绘制时不会缩放图像-传递较小的大小将剪切图像而不是缩放它。你是否真的意味着图像被剪裁,还是确实看到完整的图像缩小了?如果是后者,则问题在于实际加载的图像而不是渲染。 - Jonathan Potter