MFC 透明内存DC

7 篇文章 0 订阅

在MFC中绘制比较复杂图形,通常采用双缓冲技术来绘图,的确可以大大加快绘制速度和减少闪烁,但是有些情况也不尽然。

我最近遇到了一个问题,采用的也是双缓冲来加快绘图,但是绘制效果还是不尽人意。A对象里大约有几百个可以绘画的对象,每个对象都没有填充背景,他们的背景是另一对象B。A和B在一个窗口中可能有N个,绘画时,先绘制B然后在绘制A,只有2、3个A对象的时候,绘画已经比较慢了,DEBUG下可以明显感觉到延迟,原因是我可能只改变了对象B或一个A对象,但是需要把所有的对象重新绘画一边,效率非常低,即使是用上双缓冲也不行,PS大家都用过,它里面有一个叫做透明层的概念,在透明层上画任何东西不影响下面的层,那么我们能不能将A对象绘画在一个"透明层"1上,将B对象绘画在另一"透明层"2上。改变A对象时只需要重新在"透明层"1重新绘制A对象,而"透明层"2不需要重新绘制,最后先画透明层1然后再画透明层2,这样效率就可以大大提高了。

问题的关键之处是创建一个透明位图,然后在这个透明的位图上绘制图形。

注意:演示代码使用了GDI+,因为GDI没有使用ARGB,不会改写Alpha的值,即使画了也显示不出来。

 1、首先写一个CPngMem类。

class CPngMemDC
{
public:
	CPngMemDC() : m_hBmp(NULL)
	{
	}
	~CPngMemDC()
	{
		if (m_hBmp)
			::DeleteObject(m_hBmp);
	}

	//创建内存DC
	void CreateMemDC(CDC *pDC)
	{
		ASSERT(pDC);

		if (m_MemDC.GetSafeHdc())
			m_MemDC.DeleteDC();
		m_MemDC.CreateCompatibleDC(pDC);
	}

	//创建位图,并将位图选进内存DC
	void CreateBitmap(int nWidth, int nHeight)
	{
		BITMAPINFO bi;
		bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bi.bmiHeader.biBitCount = 32;
		bi.bmiHeader.biHeight = nHeight;
		bi.bmiHeader.biWidth  = nWidth;
		bi.bmiHeader.biPlanes = 1;
		bi.bmiHeader.biCompression = BI_RGB;
		bi.bmiHeader.biXPelsPerMeter = 0;
		bi.bmiHeader.biYPelsPerMeter = 0;
		bi.bmiHeader.biClrUsed = 0;
		bi.bmiHeader.biSizeImage = 0;
		bi.bmiHeader.biSizeImage = nWidth * nHeight * bi.bmiHeader.biBitCount / 8;

		if (m_hBmp)
			::DeleteObject(m_hBmp);
		m_hBmp = ::CreateDIBSection(m_MemDC, &bi, 0, NULL, 0, 0);//创建32位位图

		m_MemDC.SelectObject(m_hBmp);

		m_nWidth = nWidth;
		m_nHeight = nHeight;
	}

	void Draw(CDC *pDC)
	{
		BLENDFUNCTION bf;
		bf.AlphaFormat = AC_SRC_ALPHA;
		bf.BlendFlags = 0;
		bf.BlendOp = AC_SRC_OVER;
		bf.SourceConstantAlpha = 255;
		
		BOOL bRet = pDC->AlphaBlend(0, 0, m_nWidth, m_nHeight,
&m_MemDC, 0, 0, m_nWidth, m_nHeight, bf);
		VERIFY(bRet);
	}

	operator HDC()//重载HDC类型转换
	{
		return m_MemDC.GetSafeHdc();
	}

private:
	CDC m_MemDC;
	HBITMAP m_hBmp;

	int m_nWidth;
	int m_nHeight;
};

1、在对话框类中添加两个成员变量:

private:
	CPngMemDC m_pngMem1;
	CPngMemDC m_pngMen2;

2、在OnInitDialog()函数中创建内存DC和位图:

	CClientDC dc(this);
	CRect rcClient;
	GetClientRect(rcClient);

	m_pngMem1.CreateMemDC(&dc);
	m_pngMem1.CreateBitmap(rcClient.Width(), rcClient.Height());

	m_pngMen2.CreateMemDC(&dc);
	m_pngMen2.CreateBitmap(rcClient.Width(), rcClient.Height());

3、添加OnBnClickedOk()按钮响应函数:

void CDlg::OnBnClickedOk()
{
	Graphics g1(m_pngMem1);
	Pen pen1(Color(255, 255, 0, 0), 5);//红色
	g1.DrawLine(&pen1, Point(100, 0), Point(100, 300));

	Graphics g2(m_pngMen2);
	Pen pen2(Color(0, 255, 0), 5);//绿色
	g2.DrawLine(&pen2, Point(0, 150), Point(300, 150));

	CClientDC dc(this);
	
	m_pngMem1.Draw(&dc);
	m_pngMen2.Draw(&dc);
}

最后显示结果如下:

MFC 中实现紫色透明,可以通过以下步骤: 1. 将图片加载到程序中。 2. 创建一个与图片相同大小的内存 DC。 3. 将图片绘制到内存 DC 中。 4. 遍历内存 DC 中的每个像素,如果该像素的颜色为紫色,则将其 Alpha 值设为 0,否则不变。 5. 将内存 DC 中的图片绘制到屏幕 DC 中,即可实现紫色透明的效果。 下面是一个示例代码: ``` // 加载图片 CBitmap bmp; bmp.LoadBitmap(IDB_BITMAP1); // 获取图片大小 BITMAP bm; bmp.GetBitmap(&bm); // 创建内存 DC CDC memDC; memDC.CreateCompatibleDC(NULL); // 创建内存位图 CBitmap memBmp; memBmp.CreateCompatibleBitmap(pDC, bm.bmWidth, bm.bmHeight); CBitmap* oldBmp = memDC.SelectObject(&memBmp); // 绘制图片到内存 DC memDC.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &pDC, 0, 0, SRCCOPY); // 遍历像素,将紫色设为透明 for (int y = 0; y < bm.bmHeight; y++) { for (int x = 0; x < bm.bmWidth; x++) { COLORREF color = memDC.GetPixel(x, y); if (GetBValue(color) == 255 && GetRValue(color) == 255 && GetGValue(color) == 0) { memDC.SetPixel(x, y, RGB(0, 0, 0)); } else { memDC.SetPixel(x, y, color | 0xff000000); } } } // 绘制图片到屏幕 DC pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &memDC, 0, 0, SRCCOPY); // 清理资源 memDC.SelectObject(oldBmp); memBmp.DeleteObject(); bmp.DeleteObject(); ``` 在上述代码中,我们首先加载了一张图片(这里假设图片中紫色为 RGB(255, 0, 255)),然后创建了一个内存 DC,并将图片绘制到内存 DC 中。接着,我们遍历了内存 DC 中的每个像素,如果该像素的颜色为紫色,则将其 Alpha 值设为 0,否则保持不变。最后,我们将内存 DC 中的图片绘制到屏幕 DC 中,即可实现紫色透明的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值