1. 程式人生 > >記錄一個MFC繼承自CStatic的繪圖bug

記錄一個MFC繼承自CStatic的繪圖bug

MFC想要動態載入圖片並不是一件容易的 事情,在主視窗中重繪圖形也很不划算,所以一般採用繼承控制元件,自定義繪畫的方式,定義一個結構體,結構體中存放HQ_Image picture;發現gdiplus中的drawImage返回11,即ValueOverflow。不知道什麼原因,但是結構體改為HQ_Image* picture;就沒問題。

HQ_Image*需要new ;採用的預設建構函式,HQ_Image不需要new,理論上是直接用預設建構函式初始化了的。但是不知道為什麼會導致gdiplus drawimage返回失敗

標頭檔案

#pragma once
#include <afxwin.h>
#include <atlimage.h>
#include <gdiplus.h>

using namespace Gdiplus;

void CreateStretchImage(CImage *pImage, CImage *ResultImage, int StretchHeight, int StretchWidth);

class HQ_Image : public CStatic
{
	DECLARE_DYNAMIC(HQ_Image)
public:
	int m_nFontsize;
	void SetText(CString text);
	void SetTextColor(COLORREF TextColor);
	void SetBkColor(COLORREF bkColor);
	void UnsetBkColor();
	void Update();
	void SetBkImg(const TCHAR* ptszPath, int status = 0);
	void turnOn();
	void turnOff();
	void setHoverStatus(BOOL bHoveStatus = TRUE);
	int getStatus();
	void setImagePadding(int iPadding);

public:
	HQ_Image();
	virtual ~HQ_Image();

protected:
	DECLARE_MESSAGE_MAP()

private:
	afx_msg void OnPaint();
	LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
	void Invalidate();

private:
	BOOL m_bTracking;
	BOOL m_bHoverSatus;
	CString m_text;
	int     m_padding;

	int m_status;//0:正常情況; 1:開啟狀態,用來切換圖片

	// status 0
	COLORREF m_TextColor;
	BOOL m_bSetBkColor;
	COLORREF m_bkColor;
	Bitmap *m_pBkBitmap;
	CString m_strBitmap1;

	// status 1
	Bitmap *m_pBkBitmap_2;
	CString m_strBitmap2;
};

原始檔

#include "HQ_Image.h"
#include "commonLog.h"//日誌檔案
using namespace ATL;

void CreateStretchImage(CImage *pImage, CImage *ResultImage, int StretchHeight, int StretchWidth)
{
	if (pImage->IsDIBSection())
	{
		// 取得 pImage 的 DC
		CDC* pImageDC1 = CDC::FromHandle(pImage->GetDC()); // Image 因為有自己的 DC, 所以必須使用 FromHandle 取得對應的 DC



		CBitmap *bitmap1 = pImageDC1->GetCurrentBitmap();
		BITMAP bmpInfo;
		bitmap1->GetBitmap(&bmpInfo);


		// 建立新的 CImage
		ResultImage->Create(StretchWidth, StretchHeight, bmpInfo.bmBitsPixel);
		CDC* ResultImageDC = CDC::FromHandle(ResultImage->GetDC());

		// 當 Destination 比較小的時候, 會根據 Destination DC 上的 Stretch Blt mode 決定是否要保留被刪除點的資訊
		ResultImageDC->SetStretchBltMode(HALFTONE); // 使用最高品質的方式
		::SetBrushOrgEx(ResultImageDC->m_hDC, 0, 0, NULL); // 調整 Brush 的起點


		// 把 pImage 畫到 ResultImage 上面
		StretchBlt(*ResultImageDC, 0, 0, StretchWidth, StretchHeight, *pImageDC1, 0, 0, pImage->GetWidth(), pImage->GetHeight(), SRCCOPY);
		// pImage->Draw(*ResultImageDC,0,0,StretchWidth,StretchHeight,0,0,pImage->GetWidth(),pImage->GetHeight());


		pImage->ReleaseDC();
		ResultImage->ReleaseDC();
	}
}

IMPLEMENT_DYNAMIC(HQ_Image, CStatic)
HQ_Image::HQ_Image()
{
	//TRACE("HQ_Image::HQ_Image()\n");  
	m_bSetBkColor = FALSE;
	m_pBkBitmap = NULL;
	m_pBkBitmap_2 = NULL;

	m_bTracking = FALSE;
	m_bHoverSatus = FALSE;
}

HQ_Image::~HQ_Image()
{
	if (NULL != m_pBkBitmap)
	{
		DeleteObject(m_pBkBitmap);
		m_pBkBitmap = NULL;
	}

	if (NULL != m_pBkBitmap_2)
	{
		DeleteObject(m_pBkBitmap_2);
		m_pBkBitmap_2 = NULL;
	}
	//TRACE("HQ_Image::~HQ_Image()\n");  
}


BEGIN_MESSAGE_MAP(HQ_Image, CStatic)
	ON_WM_PAINT()
END_MESSAGE_MAP()



// HQ_Image message handlers  

void HQ_Image::OnPaint()
{
	//TRACE("HQ_Image::OnPaint()\n");  
	CPaintDC dc(this); // device context for painting  

	// Where to draw text  
	CRect client_rect;
	GetClientRect(client_rect);

	//// Get the caption  
	//CString szText;
	//GetWindowText(szText);

	// Get the font  
	CFont *pFont, *pOldFont;
	pFont = GetFont();
	pOldFont = dc.SelectObject(pFont);


	// Map "Static Styles" to "Text Styles"  
#define MAP_STYLE(src, dest) if(dwStyle & (src)) dwText |= (dest)  
#define NMAP_STYLE(src, dest) if(!(dwStyle & (src))) dwText |= (dest)  

	DWORD dwStyle = GetStyle(), dwText = 0;

	MAP_STYLE(SS_RIGHT, DT_RIGHT);
	MAP_STYLE(SS_CENTER, DT_CENTER);
	MAP_STYLE(SS_CENTERIMAGE, DT_VCENTER | DT_SINGLELINE);
	MAP_STYLE(SS_NOPREFIX, DT_NOPREFIX);
	MAP_STYLE(SS_WORDELLIPSIS, DT_WORD_ELLIPSIS);
	MAP_STYLE(SS_ENDELLIPSIS, DT_END_ELLIPSIS);
	MAP_STYLE(SS_PATHELLIPSIS, DT_PATH_ELLIPSIS);

	NMAP_STYLE(SS_LEFTNOWORDWRAP |
		SS_CENTERIMAGE |
		SS_WORDELLIPSIS |
		SS_ENDELLIPSIS |
		SS_PATHELLIPSIS, DT_WORDBREAK);

	// Set transparent background
	dc.SetBkMode(TRANSPARENT);

	//解決重影(將對話方塊的背景圖片貼到控制元件上)  
	CClientDC clDC(GetParent()); //建立其父視窗的客戶區DC。由於此處控制元件的父視窗是對話方塊,因此就是獲取對話方塊的DC。  
								 //相當於GetDC  
	CRect rect;
	CRect rect1;

	GetClientRect(rect);//獲取本static控制元件的客戶區大小  
	//this->GetClientRect(rect); //省去一個this,this就代表觸發wm_paint訊息的控制元件,以下同  
	GetWindowRect(rect1);//獲取本static控制元件在視窗的位置和大小  
	GetParent()->ScreenToClient(rect1);//將本static控制元件的螢幕座標轉換為客戶區座標  
	
	if (m_bSetBkColor || NULL != m_pBkBitmap)
	{
		Graphics graphics(dc.GetSafeHdc());
		graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);

		Gdiplus::Rect rcDest(0, 0, rect.right, rect.bottom);

		if (m_bSetBkColor)
		{
			Gdiplus::SolidBrush brush(Color(255, GetRValue(m_bkColor), GetGValue(m_bkColor), GetBValue(m_bkColor)));
			graphics.FillRectangle(&brush, rcDest);
		}

		rcDest.X = m_padding;
		rcDest.Y = m_padding;
		rcDest.Width -= m_padding * 2;
		rcDest.Height -= m_padding * 2;

		if (1 == m_status && NULL != m_pBkBitmap_2)
		{
			graphics.DrawImage(m_pBkBitmap_2, rcDest, 0, 0,
				m_pBkBitmap->GetWidth(), m_pBkBitmap->GetHeight(), UnitPixel);
		}
		else
		{
			if (NULL != m_pBkBitmap)
			{
				graphics.DrawImage(m_pBkBitmap, rcDest, 0, 0,
					m_pBkBitmap->GetWidth(), m_pBkBitmap->GetHeight(), UnitPixel);
			}
		}

	}

	dc.SetTextColor(m_TextColor);//設定字型顏色  

	// Draw the text  
	dc.DrawText(m_text, client_rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);


	// Select old font  
	dc.SelectObject(pOldFont);
}

void HQ_Image::SetTextColor(COLORREF TextColor)
{
	//TRACE("HQ_Image::SetTextColor(COLORREF TextColor)\n");  
	m_TextColor = TextColor;
}

void HQ_Image::SetText(CString text)
{
	//TRACE("HQ_Image::SetTextColor(COLORREF TextColor)\n");  
	m_text = text;
}


//void HQ_Image::SetFont(int nSize)   
//{  
//  TRACE("HQ_Image::SetFont(int nSize)\n");  
//  m_nFontsize = nSize ;  
//  
//}  


void HQ_Image::SetBkColor(COLORREF bkColor)
{
	m_bSetBkColor = TRUE;
	m_bkColor = bkColor;

	Invalidate();
}

void HQ_Image::UnsetBkColor()
{
	m_bSetBkColor = FALSE;

	Invalidate();
}

void HQ_Image::Update()
{
	RECT rect;
	Status status;

	//大部分和onpaint中一樣,暫不加入到onpaint中,以後可能為了相容做其他處理
	if (m_bSetBkColor || NULL != m_pBkBitmap)
	{
		Invalidate();
		GetClientRect(&rect);//獲取本static控制元件的客戶區大小  

		CDC *pDC = GetDC();
		Graphics graphics(pDC->GetSafeHdc());
		graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);

		pDC->SetBkMode(TRANSPARENT);

		Gdiplus::Rect rcDest(0, 0, rect.right, rect.bottom);

		if (m_bSetBkColor)
		{
			Gdiplus::SolidBrush brush(Color(255, GetRValue(m_bkColor), GetGValue(m_bkColor), GetBValue(m_bkColor)));
			graphics.FillRectangle(&brush, rcDest);
		}

		rcDest.X = m_padding;
		rcDest.Y = m_padding;
		rcDest.Width -= m_padding * 2;
		rcDest.Height -= m_padding * 2;

		if (1 == m_status && NULL != m_pBkBitmap_2)
		{
			status = graphics.DrawImage(m_pBkBitmap_2, rcDest, 0, 0,
				m_pBkBitmap->GetWidth(), m_pBkBitmap->GetHeight(), UnitPixel);
		}
		else
		{
			if (NULL != m_pBkBitmap)
			{
				status = graphics.DrawImage(m_pBkBitmap, rcDest, 0, 0,
					m_pBkBitmap->GetWidth(), m_pBkBitmap->GetHeight(), UnitPixel);
			}
		}

		if (m_text.GetLength() > 0)
		{

			pDC->SetTextColor(m_TextColor);//設定字型顏色  

			// Draw the text  
			pDC->DrawText(m_text, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

		}


		//Invalidate();
		ReleaseDC(pDC);
	}
}

void HQ_Image::SetBkImg(const TCHAR* ptszPath, int status)
{
	if (0 == status)
	{
		if (NULL != m_pBkBitmap)
		{
			DeleteObject(m_pBkBitmap);
			m_pBkBitmap = NULL;
			//delete 會崩潰
			// delete m_pBkBitmap;
		}

		if (NULL == ptszPath || 0 == _tcslen(ptszPath))
		{
			m_pBkBitmap = NULL;

			Invalidate();

			return;
		}

		m_strBitmap1 = ptszPath;
		m_pBkBitmap = Bitmap::FromFile(ptszPath);
	}
	else
	{
		if (NULL != m_pBkBitmap_2)
		{
			DeleteObject(m_pBkBitmap_2);
			m_pBkBitmap_2 = NULL;
		}

		if (NULL == ptszPath || 0 == _tcslen(ptszPath))
		{
			m_pBkBitmap_2 = NULL;

			Invalidate();

			return;
		}

		m_strBitmap2 = ptszPath;
		m_pBkBitmap_2 = Bitmap::FromFile(ptszPath);
	}

	Update();
}

// 開啟狀態
void HQ_Image::turnOn()
{
	if (0 == m_status)
	{
		m_status = 1;
		Update();
	}
}

// 關閉狀態,兩種狀態主要實現圖片切換效果
void HQ_Image::turnOff()
{
	if (0 != m_status)
	{
		m_status = 0;
		Update();
	}
}

// 設定滑鼠懸停事件與turn on一樣
void HQ_Image::setHoverStatus(BOOL bHoveStatus)
{
	m_bHoverSatus = bHoveStatus;
}

int HQ_Image::getStatus()
{
	return m_status;
}

void HQ_Image::setImagePadding(int iPadding)
{
	if (iPadding < 1)
	{
		m_padding = 0;
		return;
	}

	CRect rect;

	GetClientRect(rect);//獲取本static控制元件的客戶區大小  

	if (iPadding >= rect.right / 2 || iPadding >= rect.bottom / 2)
	{
		return;
	}

	m_padding = iPadding;
}

void HQ_Image::Invalidate()
{
	RECT rect;

	POINT pt;

	GetWindowRect(&rect);
	pt.x = rect.left;
	pt.y = rect.top;
	::ScreenToClient(GetParent()->GetSafeHwnd(), &pt);

	GetClientRect(&rect);
	rect.left = pt.x;
	rect.top = pt.y;
	rect.right += rect.left;
	rect.bottom += rect.top;
	::InvalidateRect(GetParent()->GetSafeHwnd(), &rect, TRUE);
}

LRESULT HQ_Image::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_COMMAND:
		{
			break;
		}
		case WM_LBUTTONDOWN:
		{
			break;
		}
		case WM_MOUSEMOVE:
		{
			if (m_bHoverSatus && !m_bTracking)
			{
				TRACKMOUSEEVENT tme;
				tme.cbSize = sizeof(TRACKMOUSEEVENT);
				tme.dwFlags = TME_LEAVE | TME_HOVER;//要觸發的訊息
				tme.hwndTrack = GetSafeHwnd();
				tme.dwHoverTime = 10;// 若不設此引數,則無法觸發mouseHover

				if (::_TrackMouseEvent(&tme)) //MOUSELEAVE|MOUSEHOVER訊息由此函式觸發
				{
					m_bTracking = true;
				}
			}
			break;
		}
		case WM_MOUSEHOVER:
		{
			if (m_bHoverSatus && !m_status)
			{
				turnOn();
			}

			m_bTracking = FALSE;
			break;
		}
		case WM_MOUSELEAVE:
		{
			if (m_bHoverSatus && m_status)
			{
				turnOff();
			}

			m_bTracking = FALSE;
			break;
		}
		default:
		{
			break;
		}
	}

	return CStatic::WindowProc(message, wParam, lParam);
}