MFC单文档实现滚轮图片缩放显示

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/Teddygogogo/article/details/98106721

目标

实现滚轮的缩放:滚轮向上,以鼠标当前在绘图区的位置进行放大;滚轮向下,以绘图去的中心进行缩小。

核心

1、OnDraw函数

2、CImage

3、CImage::StretchBlt方法【缩放原理】

4、滚轮的消息响应函数

5、工作区Client坐标、屏幕Screen坐标

核心概述

1、OnDraw函数

  • 应用窗口的工作区Client进行绘图的代码都必须写在这个函数当中

2、CImage

  • 首先需要用一个CImage的对象去加载一个地址的图片,也就是我们要显示的图片
  • 如果之前有图片对象,则销毁对象,重新创建
  • 然后获取相关的图片参数:宽、高
//判断是否加载过图片
if (!cimgImage.IsNull())
{
    //加载过,销毁
    cimgImage.Destroy();
}
//加载图片
cimgImage.Load(cstrLoadPathName);
//放大倍数清零
m_fMultiple = 0;
//原图的宽度
m_nWidthSrc = cimgImage.GetWidth();
//原图的高度
m_nHeightSrc = cimgImage.GetHeight();
//源的原点清零
 m_OriginSrcPoint = (0, 0);
//使当前的窗口无效:让Windows知道这个窗口现在该重新绘制一下了
Invalidate();

3、CImage::StretchBlt()方法【缩放原理】

  • 显示图片的核心方法
  • 主要是通过改变源Src的坐标原点xSrc、ySrc以及宽度nSrcWidth和高度nSrcHeight进行缩放的操作
  • 坐标原点影响缩放的位置
  • 宽度nSrcWidth、高度nSrcHeight影响缩放的大小(小于原图的宽高:相当于放大原图的某个区域)
BOOL StretchBlt( 
   //目标
   HDC hDestDC,  //对目标设备上下文的句柄。【pDC->m_hDC】pDC是画布的对象指针,m_hDC是对象的成员,画布的句柄
   int xDest,    //x坐标,在逻辑单位,目标矩形的左上角。
   int yDest,    //y坐标,在逻辑单位,目标矩形的左上角。
   int nDestWidth,   //宽度,在逻辑单位,目标矩形。
   int nDestHeight,  //高度,在逻辑单位,目标矩形。
   //源:不需要源的DC句柄,加载的时候已经获取
   int xSrc,     //x坐标,在逻辑单位,源矩形的左上角。
   int ySrc,     //y坐标,在逻辑单位,源矩形的左上角。
   int nSrcWidth,  //宽度,在逻辑单位,源矩形。
   int nSrcHeight, //高度,在逻辑单位,源矩形。
   DWORD dwROP = SRCCOPY  
) const throw( ); 

4、滚轮的消息响应函数

  • 类视图》CxxxView类》右键》属性》消息》WM_MOUSEWHEEL:添加
  • 主要有滚轮上/下的标识为zDelta,屏幕坐标系的坐标点pt
  • BOOL CMyRipView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)

5、工作区Client坐标、屏幕Screen坐标

  • 屏幕坐标系转工作区坐标系:ScreenToClient(&pt)

代码实现

1、View.h定义相关的变量+初始化

//缩放功能
//放大的倍数
float m_fMultiple;
//原图坐标原点
CPoint m_OriginSrcPoint;
//原图的宽度
int m_nWidthSrc;
//原图的高度
int	m_nHeightSrc;
//滚轮滚动时工作区坐标点
CPoint m_ptRollPointClient;
//滚轮滚动时屏幕坐标点
CPoint m_ptRollPointScreen;
//绘图区的矩形区域
CRect m_rectDraw;
CMyRipView::CMyRipView() noexcept
{
	// TODO: 在此处添加构造代码
	//窗口类指针初始化
	m_pDitherDlg = NULL;
	m_pICCDlg = NULL;

    //放大倍数初始化
    m_fMultiple = 0.0;
    //原图坐标原点
    m_OriginSrcPoint = (0, 0);
    //原图的宽度
    m_nWidthSrc = 0;
    //原图的高度
    m_nWidthSrc = 0;
    //滚轮滚动时工作区坐标点
    m_ptRollPointClient = (0, 0);
    //滚轮滚动时屏幕坐标点
    m_ptRollPointScreen = (0, 0);
}

2、添加滚轮消息响应函数

  • 类视图》CMyRipView》右键》属性》消息》WM_MOUSEWHEEL
  • 放大:以鼠标的位置为中心
  • 缩小:以绘图区为中心
BOOL CMyRipView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	//pt是Screen显示器坐标系的坐标
	//将OnMouseWheel的左边传到当前位置坐标变量当中
	m_ptRollPointScreen = pt;
	//将pt从屏幕坐标系转换工作区坐标系
	//ScreenToClient传入的是坐标点的指针
	m_ptRollPointClient = pt;
	ScreenToClient(&m_ptRollPointClient);


	//如果存在加载的图像,改变原点和宽、高
	if (!cimgImage.IsNull())
	{
		//设立一个tmp暂存正常变量
		//保存上一次的结果
		CPoint ptTmp = m_OriginSrcPoint;
		int nTmpWidth = m_nWidthSrc;
		int nTmpHeight = m_nHeightSrc;

		//放大操作
		if (zDelta > 0)
		{
			//判断滚轮坐标是否在绘图区当中:在左侧的右边,在右侧的左边
			if (m_ptRollPointClient.x < m_rectDraw.TopLeft().x || m_ptRollPointClient.x > m_rectDraw.BottomRight().x)
			{
				return CView::OnMouseWheel(nFlags, zDelta, pt);
			}
			//防止缩放过度
			//如果宽、高<50,则缩放过度
			else if (m_nWidthSrc < 50 || m_nHeightSrc < 50)
			{
				return CView::OnMouseWheel(nFlags, zDelta, pt);
			}
			//设置放大倍数
			m_fMultiple = 0.1;
			//改变原图的宽、高:
			//实现放大的过程其实是获取原图某个【区域】的过程
			//这个【区域】对比【源图区域】是小的
			m_nWidthSrc = m_nWidthSrc * (1 - m_fMultiple);
			m_nHeightSrc = m_nHeightSrc * (1 - m_fMultiple);

			/*(计算缩放前在Rect的宽和高的比例)、然后用比例乘以Delta(缩放前后宽、高的差值)*/
			float nDeltaX = m_ptRollPointClient.x - float(m_rectDraw.TopLeft().x);
			float fPosXRatio = nDeltaX / float(m_rectDraw.Width());
			float nDeltaY = m_ptRollPointClient.y - float(m_rectDraw.TopLeft().y);
			float fPosYRatio = nDeltaY / float(m_rectDraw.Height());

			//改变显示区域的原点位置
			//改变原点的位置,往右、下移动
			//1、以鼠标为中心的缩放(放大)
			m_OriginSrcPoint.x += (nTmpWidth - m_nWidthSrc) * fPosXRatio;
			m_OriginSrcPoint.y += (nTmpHeight - m_nHeightSrc) * fPosYRatio;

		}
		//缩小操作
		if (zDelta < 0)
		{
			//判断滚轮坐标是否在绘图区当中
			if (m_ptRollPointClient.x < m_rectDraw.TopLeft().x || m_ptRollPointClient.x > m_rectDraw.BottomRight().x)
			{
				return CView::OnMouseWheel(nFlags, zDelta, pt);
			}
			//如果宽、高到为3000,则缩小过度
			else if (m_nWidthSrc > 3000 || m_nHeightSrc > 3000)
			{
				return CView::OnMouseWheel(nFlags, zDelta, pt);
			}
			//设置缩小倍数
			m_fMultiple = -0.1;		

			//改变原图的宽、高:
			//实现放大的过程其实是获取原图某个【区域】的过程
			//这个【区域】对比【源图区域】是小的
			m_nWidthSrc = m_nWidthSrc * (1 - m_fMultiple);
			m_nHeightSrc = m_nHeightSrc * (1 - m_fMultiple);

			//改变显示区域的原点位置
			//改变原点的位置,往右、下移动
			//2、以工作区的中心进行缩放(缩小)fPosXRatio = fPosYRatio = 0.5
			m_OriginSrcPoint.x += (nTmpWidth - m_nWidthSrc) * 0.5;
			m_OriginSrcPoint.y += (nTmpHeight - m_nHeightSrc) * 0.5;
		}

		//使当前的窗口无效:让Windows知道这个窗口现在该重新绘制一下了
		Invalidate();
		
	}
	return CView::OnMouseWheel(nFlags, zDelta, pt);
}

3、修改OnDraw函数

void CMyRipView::OnDraw(CDC* pDC)
{
	CMyRipDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码
	//如果CImage对象不为空
	if (!cimgImage.IsNull())
	{
		//矩形对象rectControl:客户区的矩形区域
		CRect rectClient;
		//获取工作区Client的矩形区域
		GetClientRect(rectClient);

		//每次显示前先将原来的界面刷新称白色
		CBrush br(0xffffff);//刷成白色(255,255,255)
		pDC->FillRect(rectClient, &br);

		//获取图片的宽、高
		double dWidthOrigin = cimgImage.GetWidth();
		double dHeightOrigin = cimgImage.GetHeight();
		//定义拉伸后图片的宽、高
		double dWidthStrectch;
		double dHeightStrectch;

		//显示方法二:使原图最大地显示在客户区当中
		if (dWidthOrigin > dHeightOrigin)
		{
			//原图的宽比高大:处理后的宽度,让它和显示框等宽
			dWidthStrectch = rectClient.Width();
			//处理后的高度
			dHeightStrectch = dWidthStrectch / dWidthOrigin * dHeightOrigin;
		}
		else
		{
			//原图的高比宽大:处理后的高度,让它和显示框等高
			dHeightStrectch = rectClient.Height();
			dWidthStrectch = dHeightStrectch / dHeightOrigin * dWidthOrigin;
		}

		//绘图区域在客户区Client的位置
		m_rectDraw = CRect(CPoint((rectClient.Width() - dWidthStrectch) / 2, (rectClient.Height() - dHeightStrectch) / 2), CSize(dWidthStrectch, dHeightStrectch));

		//绘制图片到控件表示的区域:CImage::StretchBlt
		//句柄+尺寸参数
		//SRCCOPY:将源矩形区直接拷贝到目标矩形区域pDC
		//handle to destination DC:pDC->m_hDC
		/*方法1*/
		//cimgImage.StretchBlt(pDC->m_hDC, rectDraw, SRCCOPY);
		/*方法2*/
		//目标区域Dst
		int xDest = m_rectDraw.TopLeft().x;
		int yDest = m_rectDraw.TopLeft().y;
		int nDestWidth = m_rectDraw.Width();
		int nDestHeight = m_rectDraw.Height();
		//源图片Src
		int xSrc = m_OriginSrcPoint.x;
		int ySrc = m_OriginSrcPoint.y;
		int nSrcWidth = m_nWidthSrc;
		int nSrcHeight = m_nHeightSrc;
		//光栅操作
		DWORD dwROP = SRCCOPY;
		//缩放操作
		cimgImage.StretchBlt(pDC->m_hDC, xDest, yDest, nDestWidth, dHeightStrectch, xSrc, ySrc, nSrcWidth, nSrcHeight,dwROP);
		//image.Draw(pDC->GetSafeHdc(), 0, 0);
	}

	//在工作区Client中显示相关的变量
	//在(0,0)的位置中输出加载地址
	CString cstr_text1 = _T("Load Path:");
	cstr_text1 += cstrLoadPathName;
	pDC->TextOut(0, 0, cstr_text1);
	//输出显示区域的坐标原点
	CString cstr_OriginPoint;
	cstr_OriginPoint.Format(_T("Origin Point:(%d,%d)"), m_OriginSrcPoint.x, m_OriginSrcPoint.y);
	pDC->TextOut(0, 20, cstr_OriginPoint);
	//输出屏幕坐标:
	CString cstr_PointScreen;
	cstr_PointScreen.Format(_T("Screen Point:(%d,%d)"), m_ptRollPointScreen.x, m_ptRollPointScreen.y);
	pDC->TextOut(0, 40, cstr_PointScreen);
	//输出滚轮坐标位置
	CString cstr_PointClient;
	cstr_PointClient.Format(_T("Client Point:(%d,%d)"), m_ptRollPointClient.x, m_ptRollPointClient.y);
	pDC->TextOut(0, 60, cstr_PointClient);
}

4、修改加载图片的函数

void CMyRipView::OnLoadButton()
{
	// TODO: 在此添加命令处理程序代码
	//设置打开文件类型的滤波器
	TCHAR szFilter[] = _T("Tiff文件(*.tif)|*.tif|位图文件(*.bmp)|*.bmp|所有文件(*.*)|*.*||");
	//构造加载文件的文件对话框
	CFileDialog fileDlg(TRUE, _T("*.*"), NULL, 0, szFilter, this);

	//打开对话框
	if (IDOK == fileDlg.DoModal())	//.DoModal()显示一个模态对话框,成功返回IDOK
	{
		//成功显示打开加载图片的对话框	
		//获取加载的相关信息:路径名字、名字、路径
		cstrLoadPathName = fileDlg.GetPathName();
		//获取格式化的图片的名字
		cstrLoadName = fileDlg.GetFileName();
		//去除路径当中的名字:
		//找到路径名字从左往右是第几个索引值
		int index = cstrLoadPathName.Find(cstrLoadName);
		cstrLoadPath = cstrLoadPathName.Left(index);

		//判断是否加载过图片
		if (!cimgImage.IsNull())
		{
			//加载过,销毁
			cimgImage.Destroy();
		}
		//加载图片
		cimgImage.Load(cstrLoadPathName);
		//放大倍数清零
		m_fMultiple = 0;
		//原图的宽度
		m_nWidthSrc = cimgImage.GetWidth();
		//原图的高度
		m_nHeightSrc = cimgImage.GetHeight();
		//源的原点清零
		m_OriginSrcPoint = (0, 0);
		//使当前的窗口无效:让Windows知道这个窗口现在该重新绘制一下了
		Invalidate();
	}
	else if (IDCANCEL == fileDlg.DoModal())
	{
		MessageBox(_T("没有选择加载图片"));
		return;
	}
}

猜你喜欢

转载自blog.csdn.net/Teddygogogo/article/details/98106721
今日推荐