Winform中最常用的显示图像和视频的控件就是PictureBox,而常见的显示方式是直接对PictureBox的Image属性进行赋值,但该方式有个明显的缺点:若需要在主线程外的其他线程对PictureBox赋值,则需要将赋值需求Invoke到主线程,但过多的Invoke会影响主线程的界面更新,甚至有可能出现画面在流畅更新,但界面的其他更新不被响应的情况(用大恒相机回调连续图像采集时遇到过,不清楚什么原因,但是是这个现象)。
相比之下用StretchDIBits更为底层,更省性能。
之前在网上没找到有人这样用,就自己摸索着写了一下用StretchDIBits显示Bitmap和用StretchDIBits显示Mat的功能,没想到真能用,嘿嘿。
-
前置条件
//与图像显示相关的成员
int m_nWidth; //图像宽度
int m_nHeigh; //图像高度
CWin32Bitmaps.BITMAPINFO m_objBitmapInfo = new CWin32Bitmaps.BITMAPINFO(); //位图信息头数据
IntPtr m_pBitmapInfo = IntPtr.Zero; //位图信息头指针
const uint DIB_RGB_COLORS = 0; //使StretchDIBits函数处理24位位图
const uint SRCCOPY = 0x00CC0020; //光栅操作模式代码,这个是“直接将源拷贝到目标区域”
byte[] m_byColorBuffer = null; //图像数据
IntPtr m_pHDC = IntPtr.Zero; //目标设备上下文指针,这里指向picturebox
const int COLORONCOLOR = 3; //StretchDIBits拉伸图像的模式,这里是“删除像素。 此模式会删除所有已消除的像素线,而不会尝试保留其信息。”
初始化参数
//初始化bitmapInfo参数
BitmapInfoInit();
Marshal.StructureToPtr(m_objBitmapInfo, m_pBitmapInfo, false);
m_pHDC = m_pictureBox.CreateGraphics().GetHdc();
m_byColorBuffer = new byte[3 * m_nWidth * m_nHeigh];
用到的函数
/// <summary>
/// 初始化Bitmap文件头
/// </summary>
private void BitmapInfoInit()
{
m_objBitmapInfo.bmiHeader.biSize = (uint)Marshal.SizeOf(typeof(CWin32Bitmaps.BITMAPINFOHEADER));
m_objBitmapInfo.bmiHeader.biWidth = m_nWidth;
m_objBitmapInfo.bmiHeader.biHeight = m_nHeigh;
m_objBitmapInfo.bmiHeader.biPlanes = 1;
m_objBitmapInfo.bmiHeader.biBitCount = 24;
m_objBitmapInfo.bmiHeader.biCompression = 0;
m_objBitmapInfo.bmiHeader.biSizeImage = 0;
m_objBitmapInfo.bmiHeader.biXPelsPerMeter = 0;
m_objBitmapInfo.bmiHeader.biYPelsPerMeter = 0;
m_objBitmapInfo.bmiHeader.biClrUsed = 0;
m_objBitmapInfo.bmiHeader.biClrImportant = 0;
m_pBitmapInfo = Marshal.AllocHGlobal(2048);
}
最终结果1:使用StretchDIBits显示Bitmap图像
/// <summary>
/// 最终的画面显示函数
/// </summary>
/// <param name="_imgBmp"></param>
/// <param name="pictureBox"></param>
private void DispBitmap(Bitmap _imgBmp, Control pictureBox)
{
BitmapData bmpData = _imgBmp.LockBits(new Rectangle(0, 0, _imgBmp.Width, _imgBmp.Height), ImageLockMode.ReadWrite, _imgBmp.PixelFormat);
IntPtr pBufferColor = bmpData.Scan0;
_imgBmp.UnlockBits(bmpData);
m_pHDC = pictureBox.CreateGraphics().GetHdc();
m_objBitmapInfo.bmiHeader.biWidth = _imgBmp.Width;
m_objBitmapInfo.bmiHeader.biHeight = _imgBmp.Height;
Marshal.StructureToPtr(m_objBitmapInfo, m_pBitmapInfo, false);
Marshal.Copy(pBufferColor, m_byColorBuffer, 0, 3 * _imgBmp.Width * _imgBmp.Height);
CWin32Bitmaps.SetStretchBltMode(m_pHDC, COLORONCOLOR);
CWin32Bitmaps.StretchDIBits(
m_pHDC,
0,
0,
pictureBox.Width,
pictureBox.Height,
0,
0,
_imgBmp.Width,
_imgBmp.Height,
m_byColorBuffer,
m_pBitmapInfo,
DIB_RGB_COLORS,
SRCCOPY);
}
最终结果2:使用StretchDIBits显示Mat表示的图像
private void DispMat(Mat mat, Control pictureBox)
{
m_pHDC = pictureBox.CreateGraphics().GetHdc();
m_objBitmapInfo.bmiHeader.biWidth = mat.Width;
m_objBitmapInfo.bmiHeader.biHeight = mat.Height;
Marshal.StructureToPtr(m_objBitmapInfo, m_pBitmapInfo, false);
Marshal.Copy(mat.Data, m_byColorBuffer, 0, 3 * mat.Width * mat.Height);
CWin32Bitmaps.SetStretchBltMode(m_pHDC, COLORONCOLOR);
CWin32Bitmaps.StretchDIBits(
m_pHDC,
0,
0,
pictureBox.Width,
pictureBox.Height,
0,
0,
mat.Width,
mat.Height,
m_byColorBuffer,
m_pBitmapInfo,
DIB_RGB_COLORS,
SRCCOPY);
}