使用GDI在對話框和單文檔上顯示、縮放和平移圖片

使用方法在頭文件的最下方用註釋的形式給出來了。

//[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分享,我經過修改和完善,實現自己需要的功能,需要自取。

更多的交流,歡迎留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章