天天看點

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

***/
           

繼續閱讀