使用方法在頭文件的最下方用註釋的形式給出來了。
//[6/26/2016 yufuxiang]
//[5/23/2018 autumoon]
#pragma once
#include <GdiPlus.h>
#pragma comment(lib,"gdiplus.lib")
using namespace Gdiplus;
#define BK_COLOR (Color(234,234,235))
#define BACK_GROUND_VALUE 220
#define MIN_SCALE (0.01)
#define MAX_SCALE (8)
#define MIN_SCALE_RESLOVING (120/0.125)
//pMemDC爲使用外部雙緩存的時候的入口,如果爲nullptr則使用gdi內部雙緩存
class CGDIShow
{
public:
CGDIShow(CWnd* pWnd, CString imgPath, BOOL bIsDlg = FALSE, CDC* pDC = nullptr, CDC* pMemDC = nullptr);
CGDIShow(CWnd* pWnd, unsigned char* pData, int nWidth, int nHeigth, int nBandNum, BOOL bIsDlg = FALSE, CDC* pDC = nullptr, CDC* pMemDC = nullptr);
~CGDIShow(void);
public:
bool m_bIsLBtnDown;
//注意m_pDC不應當反覆通過GetDC得到
//在運行週期內應當是一個固定值
CDC* m_pDC;
void ShowImage(BOOL bDirectDC = FALSE);
public:
void OnLButtonDown(POINT point);
void OnLButtonUp(POINT point);
void OnMouseMove(POINT point);
void OnMouseWheel(short zDelta, CPoint pt);
public:
double getScale() { return m_dScale; }
REAL getCurrentX(int x);
REAL getCurrentY(int y);
private:
CWnd* m_pWnd;
CString m_pImgPath;
POINT m_pMouDownPnt;
REAL m_pXoffset;
REAL m_pYoffset;
REAL m_pXstart;
REAL m_pYstart;
int m_nImgWidth;
int m_nImgHeight;
int m_nX;
int m_nY;
int m_nShowWidth;
int m_nShowHeight;
double m_dScale;
protected:
bool m_bIsDlg;
ULONG_PTR m_gdiplusToken;
Image* pTmpImg;
//使用外部雙緩存可以防止閃爍
CDC* m_pMemDC;
void ResetOffset();
};
//對話框內部雙緩存
/************************************************************************
//Dlg中定義指針
CGDIShow* m_pGDIShow;
//構造函數中初始化
m_pGDIShow = nullptr;
//析構函數中釋放
if (m_pGDIShow)
{
delete m_pGDIShow;
m_pGDIShow = nullptr;
}
//添加static控件或者picturectrl控件
//動作按鈕中添加如下內容
CWnd* m_pWnd =GetDlgItem(IDC_STATIC_PIC);
if (m_pGDIShow)
{
delete m_pGDIShow;
}
m_pGDIShow = new CGDIShow(m_pWnd, CMfcStrFile::OpenFile(), TRUE, m_pWnd->GetDC());
m_pGDIShow->ShowImage(TRUE);
//PreTranslateMessage(MSG* pMsg)
if (pMsg->message == WM_LBUTTONDOWN && m_pGDIShow)
{
m_pGDIShow->OnLButtonDown(pMsg->pt);
}
if (pMsg->message == WM_LBUTTONUP && m_pGDIShow)
{
m_pGDIShow->OnLButtonUp(pMsg->pt);
}
if (pMsg->message == WM_MOUSEMOVE && m_pGDIShow)
{
m_pGDIShow->OnMouseMove(pMsg->pt);
}
//OnMouseWheel()
if (m_pGDIShow)
{
m_pGDIShow->OnMouseWheel(zDelta, pt);
}
/************************************************************************/
//視圖中雙緩存參考
/***********************************************************************
//View類中定義
CGDIShow* m_pGDIShow;
CBitmap m_bmp;
CDC m_memDC;
HBITMAP m_hBmp;
HBITMAP m_hOldBmp;
int m_nDCHeight;
int m_nDCWidth;
//構造函數中初始化
m_pGDIShow = nullptr;
m_nDCWidth = -1;
m_nDCHeight = -1;
//OnCreate()
CDC *pDC = GetDC();
m_memDC.CreateCompatibleDC(pDC);
ReleaseDC(pDC);
//OnSize()
if (cx < 1 || cy < 1)
{
return;
}
m_nDCHeight = cy;
m_nDCWidth = cx;
m_nDCWidth = ((m_nDCWidth + 3) / 4) * 4;
size_t nDCMemSize = m_nDCWidth * m_nDCHeight * 3;
BITMAPINFO bmpInfo;
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = m_nDCWidth;
bmpInfo.bmiHeader.biHeight = m_nDCHeight;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 24;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biSizeImage = 0;
bmpInfo.bmiHeader.biXPelsPerMeter = 0;
bmpInfo.bmiHeader.biYPelsPerMeter = 0;
bmpInfo.bmiHeader.biClrUsed = 0;
bmpInfo.bmiHeader.biClrImportant = 0;
if (m_hBmp)
{
SelectObject(m_memDC.m_hDC, m_hOldBmp);
m_hOldBmp = NULL;
DeleteObject(m_hBmp);
m_hBmp = NULL;
}
BYTE* m_pFrontBuffer = nullptr;
m_hBmp = CreateDIBSection(m_memDC.m_hDC, &bmpInfo, DIB_RGB_COLORS, (void **)&m_pFrontBuffer, NULL, 0);
m_hOldBmp = (HBITMAP)::SelectObject(m_memDC.m_hDC, m_hBmp);
//PreTranslateMessage(MSG* pMsg)
if (pMsg->message == WM_LBUTTONDOWN && m_pGDIShow)
{
m_pGDIShow->OnLButtonDown(pMsg->pt);
}
if (pMsg->message == WM_LBUTTONUP && m_pGDIShow)
{
m_pGDIShow->OnLButtonUp(pMsg->pt);
}
if (pMsg->message == WM_MOUSEMOVE && m_pGDIShow)
{
m_pGDIShow->OnMouseMove(pMsg->pt);
}
//OnMouseWheel()
if (m_pGDIShow)
{
m_pGDIShow->OnMouseWheel(zDelta, pt);
}
//OnDraw()
if (m_pGDIShow)
{
#ifdef INNER_DOUBLEBUFFER
m_pGDIShow->m_pDC = pDC;
m_pGDIShow->ShowImage();
#else
BitBlt(pDC->m_hDC, 0, 0, m_nDCWidth, m_nDCHeight, m_memDC.m_hDC, 0, 0, SRCCOPY);
#endif // INNER_DOUBLEBUFFER
}
//動作函數中
if (m_pGDIShow)
{
delete m_pGDIShow;
}
#ifdef INNER_DOUBLEBUFFER
m_pGDIShow = new CGDIShow(FromHandle(m_hWnd), strFilePath.c_str());
#else
m_pGDIShow = new CGDIShow(FromHandle(m_hWnd), strFilePath.c_str(), FALSE, &m_memDC);
m_pGDIShow->ShowImage(TRUE);
#endif // INNER_DOUBLEBUFFER
/************************************************************************/
接着是cpp文件。
#include "..\StdAfx.h"
#include "GDIShow.h"
#include "afxdialogex.h"
//[6/26/2016 yufuxiang]
//[5/23/2018 autumoon]
CGDIShow::CGDIShow(CWnd* pWnd,CString imgPath, BOOL bIsDlg /*= FALSE*/, CDC* pDC /*= nullptr*/, CDC* pMemDC /*= nullptr*/)
{
// 初始化GDI+
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup( &m_gdiplusToken, &gdiplusStartupInput, NULL );
m_pWnd=pWnd;
m_pImgPath=imgPath;
m_bIsDlg = bIsDlg == TRUE;
m_bIsLBtnDown = false;
m_pMouDownPnt.x=0;
m_pMouDownPnt.y=0;
m_pXoffset=0.0;
m_pYoffset=0.0;
m_pXstart=0.0;
m_pYstart=0.0;
pTmpImg = new Image(imgPath);
m_nImgWidth = pTmpImg->GetWidth();
m_nImgHeight = pTmpImg->GetHeight();
m_pDC = pDC;
m_pMemDC = pMemDC;
ResetOffset();
}
CGDIShow::CGDIShow(CWnd* pWnd, unsigned char* pData, int nWidth, int nHeigth, int nBandNum, BOOL bIsDlg /*= FALSE*/, CDC* pDC /*= nullptr*/, CDC* pMemDC /*= nullptr*/)
{
// 初始化GDI+
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup( &m_gdiplusToken, &gdiplusStartupInput, NULL );
m_pWnd=pWnd;
m_bIsDlg = bIsDlg == TRUE;
m_bIsLBtnDown = false;
m_pMouDownPnt.x=0;
m_pMouDownPnt.y=0;
m_pXoffset=0.0;
m_pYoffset=0.0;
m_pXstart=0.0;
m_pYstart=0.0;
//注意本構造需要滿足寬度爲4的倍數,外部提前處理
pTmpImg = new Bitmap(nWidth, nHeigth, nWidth * nBandNum, PixelFormat24bppRGB, pData);
m_nImgWidth = pTmpImg->GetWidth();
m_nImgHeight = pTmpImg->GetHeight();
m_pDC = pDC;
m_pMemDC = pMemDC;
ResetOffset();
}
CGDIShow::~CGDIShow(void)
{
if (pTmpImg)
{
delete pTmpImg;
pTmpImg = nullptr;
}
Gdiplus::GdiplusShutdown( m_gdiplusToken );
}
void CGDIShow::OnLButtonDown(POINT point)
{
CRect rect;
m_pWnd->GetWindowRect(&rect);
if (!m_bIsDlg)
{
SetCapture(m_pWnd->GetSafeHwnd());
}
if (rect.PtInRect(point))
{
m_bIsLBtnDown=true;
m_pMouDownPnt=point;
}
}
void CGDIShow::OnLButtonUp(POINT point)
{
m_bIsLBtnDown=false;
m_pMouDownPnt=point;
m_pXstart+=m_pXoffset;
m_pYstart+=m_pYoffset;
m_pXoffset=0.0;
m_pYoffset=0.0;
if (!m_bIsDlg)
{
ReleaseCapture();
}
}
void CGDIShow::OnMouseMove(POINT point)
{
if (m_bIsLBtnDown)
{
CRect rect;
m_pWnd->GetWindowRect(&rect);
if (rect.PtInRect(point))
{
m_nX = m_pXstart + m_pXoffset;
m_nY = m_pYstart + m_pYoffset;
m_nShowWidth = m_nImgWidth*m_dScale;
m_nShowHeight = m_nImgHeight*m_dScale;
m_pXoffset = point.x - m_pMouDownPnt.x;
m_pYoffset = point.y - m_pMouDownPnt.y;
ShowImage();
}
}
}
//
void CGDIShow::OnMouseWheel(short zDelta, CPoint pt)
{
if(zDelta>120) zDelta=120;
if(zDelta<-120) zDelta=-120;
double tmpOffsetX=0;double tmpOffsetY=0;
CRect rect;
m_pWnd->GetWindowRect(&rect);
POINT point;
point.x=pt.x;
point.y=pt.y;
if (rect.PtInRect(point))
{
if(m_dScale > MIN_SCALE && zDelta < 0 || m_dScale < MAX_SCALE && zDelta > 0)
{
double tmpscale = zDelta/(double)MIN_SCALE_RESLOVING;
tmpscale *= m_dScale;
tmpOffsetX=m_nImgWidth*tmpscale*((double)(point.x-rect.left-m_pXstart)/(double)(m_nImgWidth*(m_dScale)));
tmpOffsetY=m_nImgHeight*tmpscale*((double)(point.y-rect.top-m_pYstart)/(double)(m_nImgHeight*(m_dScale)));
int nX = m_pXstart-tmpOffsetX;
int nY = m_pYstart-tmpOffsetY;
int nShowWidth = m_nImgWidth*(m_dScale+tmpscale);
int nShowHeight = m_nImgHeight*(m_dScale+tmpscale);
if (nShowWidth > 0 && nShowHeight > 0)
{
//採用此次縮放的結果
m_nX = nX;
m_nY = nY;
m_nShowWidth = nShowWidth;
m_nShowHeight = nShowHeight;
ShowImage();
m_dScale += tmpscale;
m_pXstart -= tmpOffsetX;
m_pYstart -= tmpOffsetY;
}
}
}
}
//
void CGDIShow::ResetOffset()
{
CRect rect;
m_pWnd->GetWindowRect(&rect);
//計算比例
const int m_nDCWidth = rect.Width();
const int m_nDCHeight = rect.Height();
double dWidthProp = (double)m_nDCWidth / m_nImgWidth;
double dHeightProp = (double)m_nDCHeight / m_nImgHeight;
m_dScale = min(dWidthProp, dHeightProp);
m_dScale = min(1.0, m_dScale);
double dNeedWidth = m_nImgWidth * m_dScale;
double dNeedHeight = m_nImgHeight * m_dScale;
//計算可能需要的偏移
if (dNeedWidth < m_nDCWidth)
{
//此時需要顯示的圖像比較小
m_pXstart = (m_nDCWidth - dNeedWidth) / 2;
}
if (dNeedHeight < m_nDCHeight)
{
//此時需要顯示的圖像比較小
m_pYstart = (m_nDCHeight - dNeedHeight) / 2;
}
m_nX = m_pXstart;
m_nY = m_pYstart;
m_nShowWidth = m_nImgWidth*m_dScale;
m_nShowHeight = m_nImgHeight*m_dScale;
}
void CGDIShow::ShowImage(BOOL bDirectDC /*= false*/)
{
HDC m_pHDC = nullptr;
//特別說明,因爲ShowImg反覆調用部分代碼不能合併
if (m_pMemDC)
{
//使用外部雙緩存,寫到外部內存DC
m_pHDC = m_pMemDC->m_hDC;
Graphics graphics(m_pHDC);
graphics.Clear(BK_COLOR);
graphics.SetInterpolationMode(InterpolationModeNearestNeighbor);
graphics.DrawImage(pTmpImg, m_nX, m_nY, m_nShowWidth, m_nShowHeight);
}
else
{
if (m_pDC)
{
if (bDirectDC)
{
m_pHDC = m_pDC->m_hDC;
Graphics graphics(m_pHDC);
graphics.Clear(BK_COLOR);
graphics.SetInterpolationMode(InterpolationModeNearestNeighbor);
graphics.DrawImage(pTmpImg, m_nX, m_nY, m_nShowWidth, m_nShowHeight);
}
else
{
//使用GDI內部雙緩存
CMemDC memDC(*m_pDC, m_pWnd);
CDC* pDC = &memDC.GetDC();
m_pHDC = pDC->m_hDC;
Graphics graphics(m_pHDC);
graphics.Clear(BK_COLOR);
graphics.SetInterpolationMode(InterpolationModeNearestNeighbor);
graphics.DrawImage(pTmpImg, m_nX, m_nY, m_nShowWidth, m_nShowHeight);
}
}
}
if (!m_bIsDlg)
{
m_pWnd->Invalidate(FALSE);
}
}
REAL CGDIShow::getCurrentX(int x)
{
CRect rect;
m_pWnd->GetWindowRect(&rect);
return (x-rect.left-m_pXstart)/m_dScale;
}
//
REAL CGDIShow::getCurrentY(int y)
{
CRect rect;
m_pWnd->GetWindowRect(&rect);
return (y-rect.top-m_pYstart)/m_dScale;
}
此代碼是多人合作的結果,最初的代碼由yufuxiang分享,我經過修改和完善,實現自己需要的功能,需要自取。
更多的交流,歡迎留言。