記錄一個MFC繼承自CStatic的繪圖bug
阿新 • • 發佈:2018-11-09
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); }