MFC贴图双缓冲技术

 MFC图像贴图双缓冲解决闪烁自定义图像内存加载类的封装:
支持从内部资源/外部文件加载,支持bmp/jpg/gif/png图像文件;
支持创建空白位图(黑色,指定宽高);
文件显示透明/收缩透明位图;
支持根据背景颜色生成RGN裁剪区。
使用时,将此头文件在"stdafx.h"中添加 #include "MemoryDC.h"

// 2019.03.10
// 自定义图像内存加载类的封装:
// 支持从内部资源/外部文件加载,支持bmp/jpg/gif/png图像文件;
// 支持创建空白位图(黑色,指定宽高);
// 文件显示透明/收缩透明位图;
// 支持根据背景颜色生成RGN裁剪区。
// 使用时,将此头文件在"stdafx.h"中添加 #include "MemoryDC.h"

#include "stdafx.h"
//#include <functional>

#pragma once

class CMemoryDC : public CDC
{
public:
	// 构造 与 析构
	CMemoryDC():m_size(0, 0)
	{

	}
	CMemoryDC(UINT nBitmapID, CDC* pDC = nullptr) :m_size(0, 0)
	{
		this->LoadBitmap(nBitmapID, pDC);
	}
	CMemoryDC(LPCTSTR szBitmap, CDC* pDC = nullptr) :m_size(0, 0)
	{
		this->LoadBitmap(szBitmap, pDC);
	}
	CMemoryDC(int cx, int cy, CDC* pDC = nullptr) :m_size(0, 0)
	{
		this->Create(cx, cy, pDC);
	}
	virtual ~CMemoryDC()
	{
		this->DeleteObject();
	}

	//创建一张空白内存画布
	BOOL Create(int cx, int cy, CDC* pDC = nullptr)
	{
		BOOL result;
		CBitmap bitmap;
		if (pDC)
			result = bitmap.CreateCompatibleBitmap(pDC, cx, cy);
		else
			result = bitmap.CreateCompatibleBitmap(&CClientDC(NULL), cx, cy);

		//这种方式语法没错,但为什么不成功哎?
		//auto fcompatile = std::bind(&CBitmap::CreateCompatibleBitmap, bitmap, 
		//	std::placeholders::_1, cx, cy);
		//result = pDC ? fcompatile(pDC) : fcompatile(&CClientDC(NULL));

		m_size.SetSize(cx, cy);
		this->CreateCompatibleDC(pDC);
		this->SelectObject(bitmap);
		return result;
	}
	// 释放
	BOOL DeleteObject()
	{
		if (!GetSafeHdc())
			return FALSE;
		CBitmap* pBitmap = GetCurrentBitmap();
		if (pBitmap)
			pBitmap->DeleteObject();
		return CDC::DeleteDC();
	}

	// 从内部资源加载位图 bmp
	BOOL LoadBitmap(UINT nBitmapID, CDC* pDC = nullptr)
	{
		CBitmap bitmap;
		if (!bitmap.LoadBitmap(nBitmapID))			//加载图像,只能通过资源ID
			return FALSE;

		BITMAP bmp;
		bitmap.GetBitmap(&bmp);						//获取图像参数
		m_size.SetSize(bmp.bmWidth, bmp.bmHeight);	//保存图像尺寸
		this->CreateCompatibleDC(pDC);				//创建兼容设备
		this->SelectObject(bitmap);					//选择图像
		
		bitmap.DeleteObject();
		return TRUE;
	}
	// 从外部文件加载 bmp / jpg / gif
	BOOL LoadBitmap(LPCTSTR szBitmap, CDC* pDC = nullptr)
	{
		HBITMAP hBitmap = (HBITMAP)::LoadImage(nullptr, szBitmap, IMAGE_BITMAP, 0, 0, 
			LR_DEFAULTCOLOR | LR_DEFAULTSIZE | LR_LOADFROMFILE);
		if (!hBitmap)
			return FALSE;

		BITMAP bmp;
		::GetObject(hBitmap, sizeof(bmp), &bmp);
		m_size.SetSize(bmp.bmWidth, bmp.bmHeight);
		this->CreateCompatibleDC(pDC);
		this->SelectObject(hBitmap);

		::DeleteObject(hBitmap);
		return TRUE;
	}
	// 从外部文件加载 jpg / gif / png -> bmp
	// 要在OnInitDialog()中进行资源加载!
	HRESULT Load(LPCTSTR pszFileName, CDC* pDC = nullptr)
	{
		CImage image;
		HRESULT result = image.Load(pszFileName);
		CBitmap* pBitmap = CBitmap::FromHandle(image.Detach());

		BITMAP bmp;
		pBitmap->GetBitmap(&bmp);
		m_size.SetSize(bmp.bmWidth, bmp.bmHeight);
		this->CreateCompatibleDC(pDC);
		this->SelectObject(pBitmap);

		image.Destroy();
		pBitmap->DeleteObject();

		return result;
	}

	// 图像透明化. pDC: 目标DC,贴图至此处.
	// CPaintDC dc(this); dc.CDC::BitBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC,int xSrc, int ySrc, DWORD dwRop);
	// 则将pSrcDC贴图至dc上.
	void BitTrans(
		int nXDest,		// 目标起点X
		int nYDest,		// 目标起点Y
		int nWidthDest,	// 目标宽度
		int nHeightDest,// 目标高度
		CDC* pDC,		// 目标DC
		int nXSrc,		// 来源起点X
		int nYSrc,		// 来源起点Y
		COLORREF crTrans// 透明色
	)
	{
		CMemoryDC dcImage(nWidthDest, nHeightDest, pDC);				//临时DC
		CBitmap bmpMask;
		bmpMask.CreateBitmap(nWidthDest, nHeightDest, 1, 1, NULL);		// 创建单色掩码位图
		CDC dcMask;														//掩码DC 
		dcMask.CreateCompatibleDC(pDC);
		dcMask.SelectObject(bmpMask);
		//将载入位图的内存DC中的位图,拷贝到临时DC中
		dcImage.BitBlt(0, 0, nWidthDest, nHeightDest, this, nXSrc, nYSrc, SRCCOPY);

		// 设置临时DC的透明色
		dcImage.SetBkColor(crTrans);
		//掩码DC的透明区域为白色其它区域为黑色
		dcMask.BitBlt(0, 0, nWidthDest, nHeightDest, &dcImage, 0, 0, SRCCOPY);

		//临时DC透明区域为黑色,其它区域保持不变
		dcImage.SetBkColor(RGB(0, 0, 0));
		dcImage.SetTextColor(RGB(255, 255, 255));
		dcImage.BitBlt(0, 0, nWidthDest, nHeightDest, &dcMask, 0, 0, SRCAND);

		// 目标DC透明部分保持屏幕不变,其它部分变成黑色
		pDC->SetBkColor(RGB(255, 255, 255));
		pDC->SetTextColor(RGB(0, 0, 0));
		pDC->BitBlt(nXDest, nYDest, nWidthDest, nHeightDest, &dcMask, 0, 0, SRCAND);
		pDC->BitBlt(nXDest, nYDest, nWidthDest, nHeightDest, &dcImage, 0, 0, SRCPAINT);
	}
	// 图像收缩透明化. pDC: 目标DC,贴图至此处.
	// CPaintDC dc(this); dc.CDC::StretchBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC,int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop);
	// 则将pSrcDC贴图至dc上.
	void StretchTrans(
		int nXDest,			// 目标起点X
		int nYDest,			// 目标起点Y
		int nWidthDest,     // 目标宽度
		int nHeightDest,    // 目标高度
		CDC* pDC,			// 目标DC
		int nXSrc,			// 来源起点X
		int nYSrc,			// 来源起点Y
		int nWidthSrc,		// 来源宽度
		int nHeightSrc,		// 来源高度
		COLORREF crTrans	// 透明色
	)
	{
		CMemoryDC dcImage(nWidthDest, nHeightDest, pDC);//临时DC
		CBitmap bmpMask;
		// 创建单色掩码位图
		bmpMask.CreateBitmap(nWidthDest, nHeightDest, 1, 1, NULL);
		CDC dcMask;
		dcMask.CreateCompatibleDC(pDC);
		dcMask.SelectObject(bmpMask);

		// 将载入位图的内存DC中的位图,拷贝到临时DC中
		if (nWidthDest == nWidthSrc && nHeightDest == nHeightSrc)
			dcImage.BitBlt(0, 0, nWidthDest, nHeightDest, this, nXSrc, nYSrc, SRCCOPY);
		else
			dcImage.StretchBlt(0, 0, nWidthDest, nHeightDest,
				this, nXSrc, nYSrc, nWidthSrc, nHeightSrc, SRCCOPY);

		// 设置临时DC的透明色
		dcImage.SetBkColor(crTrans);
		//掩码DC的透明区域为白色其它区域为黑色
		dcMask.BitBlt(0, 0, nWidthDest, nHeightDest, &dcImage, 0, 0, SRCCOPY);

		//临时DC透明区域为黑色,其它区域保持不变
		dcImage.SetBkColor(RGB(0, 0, 0));
		dcImage.SetTextColor(RGB(255, 255, 255));
		dcImage.BitBlt(0, 0, nWidthDest, nHeightDest, &dcMask, 0, 0, SRCAND);

		// 目标DC透明部分保持屏幕不变,其它部分变成黑色
		pDC->SetBkColor(RGB(255, 255, 255));
		pDC->SetTextColor(RGB(0, 0, 0));
		pDC->BitBlt(nXDest, nYDest, nWidthDest, nHeightDest, &dcMask, 0, 0, SRCAND);
		pDC->BitBlt(nXDest, nYDest, nWidthDest, nHeightDest, &dcImage, 0, 0, SRCPAINT);
	}

	// 创建RGN裁剪区.
	// 使用时要先创建再重新拷贝以免破坏裁剪区域.
	// BOOL CRgn::CreateRectRgn(int x1, int y1, int x2, int y2); int CRgn::CopyRgn(const CRgn* pRgnSrc);
	void MakeRgn(CRgn& rgn, COLORREF trans)
	{
		if (rgn.GetSafeHandle())
			rgn.DeleteObject();
		rgn.CreateRectRgn(0, 0, 0, 0);						//空的矩形RGN

		int cx = m_size.cx;
		int cy = m_size.cy;
		int x, y;
		for(x = 0; x < cx; x++)
			for (y = 0; y < cy; y++)
			{
				if (GetPixel(x, y) != trans)				//需要处理为透明的像素
				{
					CRgn r;
					r.CreateRectRgn(x, y, x + 1, y + 1);	//创建面积为1*1的RGN
					rgn.CombineRgn(&rgn, &r, RGN_OR);		//RGN组合
				}
			}
	}

	int GetWidth() const { return m_size.cx; }
	int GetHeight() const { return m_size.cy; }

private:
	CSize m_size;	//图像大小
};

/***
//pDC: 若该句柄为NULL,该函数创建一个与应用程序的当前显示器兼容的内存设备上下文环境
BOOL CDC::CreateCompatibleDC(CDC* pDC);

//加载 icon / cursor / bitmap,从内部资源或外部文件均可
//e.g. (LPCTSTR)IDB_BITMAP / MAKEINTRESOURCE(IDB_BITMAP) or _T("./res/image_bmp.bmp")
HANDLE WINAPI LoadImageW(
	_In_opt_ HINSTANCE hInst,
	_In_ LPCWSTR name,
	_In_ UINT type,
	_In_ int cx,
	_In_ int cy,
	_In_ UINT fuLoad);

#define IMAGE_BITMAP        0
#define IMAGE_ICON          1
#define IMAGE_CURSOR        2

#define LR_DEFAULTCOLOR     0x00000000
#define LR_MONOCHROME       0x00000001
#define LR_COLOR            0x00000002
#define LR_COPYRETURNORG    0x00000004
#define LR_COPYDELETEORG    0x00000008
#define LR_LOADFROMFILE     0x00000010
#define LR_LOADTRANSPARENT  0x00000020
#define LR_DEFAULTSIZE      0x00000040
#define LR_VGACOLOR         0x00000080
#define LR_LOADMAP3DCOLORS  0x00001000
#define LR_CREATEDIBSECTION 0x00002000
#define LR_COPYFROMRESOURCE 0x00004000
#define LR_SHARED           0x00008000

// Region Flags
#define ERROR               0
#define NULLREGION          1
#define SIMPLEREGION        2
#define COMPLEXREGION       3
#define RGN_ERROR			ERROR

// CombineRgn() Styles
#define RGN_AND             1
#define RGN_OR              2
#define RGN_XOR             3
#define RGN_DIFF            4
#define RGN_COPY            5
#define RGN_MIN             RGN_AND
#define RGN_MAX             RGN_COPY

// StretchBlt() Modes
#define BLACKONWHITE                 1
#define WHITEONBLACK                 2
#define COLORONCOLOR                 3
#define HALFTONE                     4
#define MAXSTRETCHBLTMODE            4

***/

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值