MFC中实现缩放画图的另一种方式

4 篇文章 1 订阅
在前面的一篇文章中,作者详细介绍了利用GDI+的API实现缩放图像的方法。下面介绍另外一种可以实现图形缩放的方法。
在windows的绘图中,绘图的逻辑单位和视图的物理单位之间的对应关系是可以改变的,通过 CDC::SetMapMode可以改变这种关系。而本次实现图形缩放的功能原理就是通过改变这种映射关系来实现。
CDC::SetMapMode在MSDN中这样解释:
CDC::SetMapMode 
virtual int SetMapMode( int nMapMode );
Return Value
The previous mapping mode.
Parameters
nMapMode
Specifies the new mapping mode. It can be any one of the following values:
MM_ANISOTROPIC   Logical units are converted to arbitrary units with arbitrarily scaled axes. Setting the mapping mode to MM_ANISOTROPIC does not change the current window or viewport settings. To change the units, orientation, and scaling, call the SetWindowExt and SetViewportExt member functions.
MM_HIENGLISH   Each logical unit is converted to 0.001 inch. Positive x is to the right; positive y is up.
MM_HIMETRIC   Each logical unit is converted to 0.01 millimeter. Positive x is to the right; positive y is up.
MM_ISOTROPIC   Logical units are converted to arbitrary units with equally scaled axes; that is, 1 unit along the x-axis is equal to 1 unit along the y-axis. Use the SetWindowExt and SetViewportExt member functions to specify the desired units and the orientation of the axes. GDI makes adjustments as necessary to ensure that the x and y units remain the same size.
MM_LOENGLISH   Each logical unit is converted to 0.01 inch. Positive x is to the right; positive y is up.
MM_LOMETRIC   Each logical unit is converted to 0.1 millimeter. Positive x is to the right; positive y is up.
MM_TEXT   Each logical unit is converted to 1 device pixel. Positive x is to the right; positive y is down.
MM_TWIPS   Each logical unit is converted to 1/20 of a point. (Because a point is 1/72 inch, a twip is 1/1440 inch.) Positive x is to the right; positive y is up.
Remarks
Sets the mapping mode. The mapping mode defines the unit of measure used to convert logical units to device units; it also defines the orientation of the device’s x- and y-axes. GDI uses the mapping mode to convert logical coordinates into the appropriate device coordinates. The MM_TEXT mode allows applications to work in device pixels, where 1 unit is equal to 1 pixel. The physical size of a pixel varies from device to device.
The MM_HIENGLISH, MM_HIMETRIC, MM_LOENGLISH, MM_LOMETRIC, and MM_TWIPS modes are useful for applications that must draw in physically meaningful units (such as inches or millimeters). The MM_ISOTROPIC mode ensures a 1:1 aspect ratio, which is useful when it is important to preserve the exact shape of an image. The MM_ANISOTROPIC mode allows the x- and y-coordinates to be adjusted independently.
这里主要介绍MM_ANISOTROPIC的映射模式,MSDN的介绍翻译成中文:
逻辑单位可以在任意坐标系中设置成任意的单位。设置该映射模式不能该表现有的窗口和视口设置。想要改变单位、方向、或者缩放比例,利用SetWindowExt和SetViewportExt功能。
那现在再看下MSDN中对SetWindowExt和SetViewportExt的解释,这里不再引用MSDN,只介绍下其中文解释:
对于SetWindowExt,MSDN中描述其功能:
设置窗口的相对于物理绘图设备x和y轴长度,窗口以及物理绘图设备决定了GDI+绘图中逻辑设备坐标和物理设备坐标的映射关系。
对于SetViewportExt,MSDN中描述其功能:
设置物理绘图设备的视口的x和y轴长度,视口以及绘图设备中的窗口决定了GDI+绘图中逻辑设备坐标和物理设备坐标的映射关系。换句话说,就是它们决定了逻辑单位和物理单位之间的映射关系。
下面,就是利用CDC::SetMapMode以及SetWindowExt、SetViewportExt实现图形的缩放功能。

首先,建立一个单文档MFC工程,名字就叫GDITest,在GDITestApp中添加GDI+的令牌作为成员变量用于初始化GDI+绘图环境。

private:
	ULONG_PTR m_gdiplusToken;
分别在GDITestApp的InitInstance和ExitInstance中添加GDI+初始化和释放的代码:
BOOL CGDITestApp::InitInstance()
{
	…
	//初始化GDI+库
	Gdiplus::GdiplusStartupInput gdiplusStartupInput;
	Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
               …
}
int CGDITestApp::ExitInstance()
{
	…
              //关闭GDI+库
	Gdiplus::GdiplusShutdown(m_gdiplusToken);
	…
}

然后,在CGDITestView的OnDraw函数中,进行图形的绘制,这里面我们先做图形绘制的准备工作,具体绘制图形的任务,交给CGDITestDoc这个文档类完成。所以先在CGDITestDoc中添加两个成员函数,分别用于绘制绘图背景,和绘制图形,如下所示:

void CGDITestDoc::DrawBK(CDC* pDc, CRect& rect)
{
	//定义背景画刷
	CBrush brushOut(RGB(100,100,100));
	//绘制背景色
	pDc->FillRect(rect,&brushOut);
}

void CGDITestDoc::DrawShapes(CDC* pDc,CRect& rect)
{
	Gdiplus::Graphics graphi(pDc->m_hDC);
	Gdiplus::Pen pen(Gdiplus::Color(255,0,0,255));
	graphi.DrawLine(&pen,0,0,200,100);
}


我们先在CGDITestView中添加一个double型的成员变量m_dbZoom作为控制画面缩放的缩放系数,并赋初值1.0,在CGDITestView的OnDraw函数中进行绘图的准备工作同时调用文档类的绘图函数进行绘图:
void CGDITestView::OnDraw(CDC* pDC)
{
	CGDITestDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;
	//获取视图窗口大小
	CRect clent;
	this->GetClientRect(&clent);
	//创建内存DC
	CDC Memdc;
	Memdc.CreateCompatibleDC(NULL);
	//创建内存Bitmap
	CBitmap bitmap;
	CBitmap* pOldbitmap;
	bitmap.CreateCompatibleBitmap(pDC,clent.Width(),clent.Height());
	//将内存DC关联到内存Bitmap
	pOldbitmap= Memdc.SelectObject(&bitmap);
	//设置内存DC背景
	Memdc.SetBkColor(TRANSPARENT);
	//绘图
	//1.设置缩放系数
              CSize szViewPort;
	pDC->SetMapMode(MM_ANISOTROPIC);
	szViewPort.cx = m_dbZoom*1000;
	szViewPort.cy = m_szViewPort.cx;
	pDC->SetViewportExt(szViewPort);
	pDC->SetWindowExt(1000,1000);
	//1.绘制背景色
	pDoc->DrawBK(&Memdc,clent);
	//2.绘制图形
	pDoc->DrawShapes(&Memdc,clent);
	//从内存DC将内容拷贝的视图DC
	pDC->BitBlt(0,0,clent.Width(),clent.Height(),&Memdc,0,0,SRCCOPY);
	//还原内存DC的bitmap
	Memdc.SelectObject(pOldbitmap);
}

最后重载BOOL CGDITestView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)函数,这样就能通过鼠标滚轴控制图形的缩放。
BOOL CGDITestView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	if (zDelta>0)
	{
		m_dbZoom*=1.2;
	}
	else
	{
		m_dbZoom/=1.2;
	}
	Invalidate(TRUE);
	return CView::OnMouseWheel(nFlags, zDelta, pt);
}

以上的缩放是以原点为中心进行的缩放,若想要实现以鼠标所在点为中心进行缩放,要怎么实现?
其实原理和上述博客中所讲绘制的原理差不多,只不过这里需要先介绍下两个函数:
CDC::SetViewportOrg设置物理绘图设备的起始点;
CDC::SetWindowOrg设置窗口绘图设备的起始点;
我们就是利用SetViewportOrg以及缩放后的偏移距离,来设置开始绘图的坐标,以达到以鼠标为中心缩放的目的。
首先,计算偏移距离,在CGDITestView增加CSize型的成员变量m_csZoom.。修改 BOOL CGDITestView::OnMouseWheel函数如下:
BOOL CGDITestView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	double PreZoom=m_dbZoom;
	if (zDelta>0)
		m_dbZoom*=1.2;
	else
		m_dbZoom/=1.2;
	//获取视图所在的范围
	CRect rect;
	GetWindowRect(&rect);
	//获取当前鼠标相对于视图所在的位置
	CPoint point;
	point.x=pt.x-rect.left;
	point.y=pt.y-rect.top;
	//计算偏移距离
	m_csZoom.cx=(point.x * m_dbZoom) - (point.x * PreZoom);
	m_csZoom.cy=(point.y * m_dbZoom) - (point.y * PreZoom);
	Invalidate(TRUE);
	return CView::OnMouseWheel(nFlags, zDelta, pt);
}

根据偏移距离,在OnDraw函数中重置绘图坐标原点,如下所示:

void CGDITestView::OnDraw(CDC* pDC)
{
	CGDITestDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;
	//获取视图范围
	CRect clent;
	this->GetClientRect(&clent);
	//创建内存DC
	CDC Memdc;
	Memdc.CreateCompatibleDC(NULL);
	//创建内存bitmap
	CBitmap bitmap;
	CBitmap* pOldbitmap;
	bitmap.CreateCompatibleBitmap(pDC,clent.Width(),clent.Height());
	//将内存DC关联到bitmap
	pOldbitmap= Memdc.SelectObject(&bitmap);
	//设置内存DC背景
	Memdc.SetBkColor(TRANSPARENT);
	//绘图
	//获取缩放距离
	pDC->SetMapMode(MM_ANISOTROPIC);
	m_szViewPort.cx = m_dbZoom*1000;
	m_szViewPort.cy = m_szViewPort.cx;
	pDC->SetViewportExt(m_szViewPort);
	pDC->SetWindowExt(1000,1000);
	//重新设置绘图原点
	m_ViewOrgPoint.x-=m_csZoom.cx;
	m_ViewOrgPoint.y-=m_csZoom.cy;
	pDC->SetViewportOrg(m_ViewOrgPoint.x,m_ViewOrgPoint.y);
	pDC->SetWindowOrg(0,0);
	//1.绘制背景
	pDoc->DrawBK(&Memdc,clent);
	//2.绘制形状
	pDoc->DrawShapes(&Memdc,m_csZoom);
	//将内存DC拷贝到视图DC
	pDC->BitBlt(0,0,clent.Width(),clent.Height(),&Memdc,0,0,SRCCOPY);
	//还原内存bitmap
	Memdc.SelectObject(pOldbitmap);
}



  • 2
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值