1、说明
最近在使用MFC时发现,Picture Control控件中的图像若被其他窗口遮盖、窗口移出屏幕外、最小化等操作时,都会导致图像消失。其原因在于onPaint()函数。
原因:MFC的机制是当窗口大小改变、遮挡、最小化等操作时,会使窗口中部分/全部内容“失效”,当恢复窗口时,会自动产生WM_PAINT消息发送给视图刷新窗口,该消息会触发onPaint()函数进行窗口绘制。也就是说当窗口每经过一次改变(存在“无效”区),都会触发onPaint()函数。
若Picture Control控件中的图像显示操作是一个自定义函数,并未在onPaint()函数中实现或调用,那么每次调用onPaint()函数时都不会刷新Picture Control控件中的内容,这也就是为什么图像会消失的原因。
百度百科对WM_PAINT的解释:https://baike.baidu.com/item/WM_PAINT%E6%B6%88%E6%81%AF
2、解决方案
解决这个问题的方法就是在onPaint函数中添加对应的图像显示代码,也可以在该函数中直接调用自定义的图像显示函数。
(1)自定义图像显示函数:
void ShowMatImgToWnd(CWnd* pWnd, Mat show_image)
{
if (show_image.empty())
{
return;
}
CRect drect;
pWnd->GetClientRect(&drect);
CClientDC dc(pWnd);
HDC hDC = dc.GetSafeHdc();
resize(show_image, show_image, Size(drect.Width(), drect.Height()));
//Copy image data from memory to the screen
BYTE *bitBuffer = NULL;
BITMAPINFO *bitMapinfo = NULL;
int ichannels = show_image.channels();
if (ichannels == 1)
{
bitBuffer = new BYTE[40 + 4 * 256];
}
else if (ichannels == 3)
{
bitBuffer = new BYTE[sizeof(BITMAPINFO)];
}
else
{
return;
}
if (bitBuffer == NULL)
{
return;
}
bitMapinfo = (BITMAPINFO *)bitBuffer;
bitMapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
//If the height is positive, the starting position of the bitmap is in the lower-left corner.
//If the height is negative, the starting position is in the upper-left corner.
bitMapinfo->bmiHeader.biHeight = -show_image.rows;
bitMapinfo->bmiHeader.biWidth = show_image.cols;
//The level of the target device must be 1
bitMapinfo->bmiHeader.biPlanes = 1;
//The number of digits required per pixel must be one of 1 (two colors), 4 (16 colors), 8 (256 colors), or 24 (True color)
bitMapinfo->bmiHeader.biBitCount = ichannels * 8;
//Bitmap compression type, must be one of 0 (uncompressed), 1 (bi_rle8 compression type), or 2 (bi_rle4 compression type)
bitMapinfo->bmiHeader.biCompression = BI_RGB;
//The size of the bitmap, in bytes
bitMapinfo->bmiHeader.biSizeImage = 0;
//Bitmap horizontal resolution, prime number per meter image
bitMapinfo->bmiHeader.biXPelsPerMeter = 0;
//Bitmap vertical resolution, prime number per meter image
bitMapinfo->bmiHeader.biYPelsPerMeter = 0;
//Number of colors in the color table actually used by the bitmap
bitMapinfo->bmiHeader.biClrUsed = 0;
//Number of important colors in the bitmap display process
bitMapinfo->bmiHeader.biClrImportant = 0;
if (ichannels == 1)
{
for (int i = 0; i < 256; i++)
{
//Range of color values (0-255)
bitMapinfo->bmiColors[i].rgbBlue = bitMapinfo->bmiColors[i].rgbGreen = bitMapinfo->bmiColors[i].rgbRed = (BYTE)i;
}
//Number of colors in the color table actually used by the bitmap
bitMapinfo->bmiHeader.biClrUsed = 256;
}
SetStretchBltMode(hDC, COLORONCOLOR);
StretchDIBits(hDC,
0,
0,
drect.right, //Display window width
drect.bottom, //Display window height
0,
0,
show_image.cols, //Image width
show_image.rows, //Image Height
show_image.data,
bitMapinfo,
DIB_RGB_COLORS,
SRCCOPY
);
delete[]bitBuffer;
}
(2)onPaint函数:
在onPaint函数中添加图像显示函数即可,首先添加全局布尔变量,用来表示Picture Control控件中是否有图像,若存在图像,则刷新时调用,若没有图像,则无需调用。
发现问题:添加完成测试时发现,窗口被遮盖或移出屏幕外恢复时都可以正常显示图像,但是最小化恢复以后图像还是会消失。单步调试发现,其实onPaint函数正常会调用图像显示函数,且图像会正常显示。但当onPaint函数结束后,窗口又被重新绘制了一次,导致显示的图像再次被刷新掉。(暂时没找到是什么原因导致的。。。)
解决方法:在图像显示前调用UpdateWindow()进行窗口刷新可解决最小化后图像依然消失的问题。
void CGigeCameraDemoDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM)dc.GetSafeHdc(), 0);
// Center icon in client rectangle
INT32 cxIcon = GetSystemMetrics(SM_CXICON);
INT32 cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
INT32 x = (rect.Width() - cxIcon + 1) / 2;
INT32 y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
//当重绘标志为真时,若窗口中有内容无效,则重新显示图像
if(repaint_flag)
{
UpdateWindow();
CWnd *target_wnd = NULL;
target_wnd = this->GetDlgItem(IDC_DST_STATIC); //IDC_DST_STATIC为picture control控件ID
ShowMatImgToWnd(target_wnd, paint_result_image); //调用图像显示函数,paint_result_image为全局Mat
}
}