MFC案例:使用键盘按键放大、缩小窗口图像的实验

当在对话框窗口居中显示一幅图像时,图像的尺寸可能大于或小于窗口的尺寸,这时我们希望通过使用按键"+"进行图像放大显示,使用按键"-"进行缩小显示(当然也可以使用其它按键)。下面开始使用MFC进行这个实验,编译环境是VS2022。

思路:首先,把一幅图像按原尺寸(不做拉伸)居中显示到窗口中,使我们能看到图像原始的样子;然后,进行程序核心部分,使用键盘消息处理函数接收键盘消息,当接收到"+"或"-"按键按下消息时,相应的显示图像的某一区域。原理上,在窗口尺寸既定的情况下,图像选定区域越小则显示出来越大,反之亦然。

实验的具体步骤如下:

一、建立一个基于对话框的MFC项目

打开VS2022->创建新项目->MFC应用->下一步->项目名称填写:picEnlgReduTest->创建->应用程序类型选择:基于对话框->完成(其余项目均按缺省)。

二、在桌面上放一张jpg图片,我这里的路径是:C:\Users\Administrator\Desktop\2222.jpg。(内容是我自己拍摄的月季花)

三、把图像文件读入内存并获得这幅图像的尺寸以及窗口尺寸等基本数据,这项工作我们放到对话框的初始化程序中进行。具体是找到picEnlgReduTestDlg.cpp中的OnInitDialog()函数,在TODO:行下面添加下列代码:

cpp 复制代码
	img.Load(L"C:\\Users\\Administrator\\Desktop\\1111.jpg"); //载入图像文件
	imgWidth = img.GetWidth();// 返回图像的宽度(以像素为单位)
	imgHeight = img.GetHeight();// 返回图像的高度
	CRect DCrect; //定义矩形对象(用于接收对话框客户区数据)
	GetClientRect(DCrect); //获得窗口客户区坐标、宽高等数据
	DCwidth = DCrect.Width();//客户区宽度
	DCheight = DCrect.Height();//客户区高度

在上面的代码中使用的img、imgWidth、imgHeight、DCwidth、DCheight几个变量,在对话框类的其它函数中可能还会用到,因此这几个变量要放在picEnlgReduTestDlg.h中定义,定义方式是public:,具体如下:

cpp 复制代码
    CImage img; //声明用于载入图像文件的CImage对象
	int imgWidth, imgHeight; //载入到img中的图像的宽、高(像素)
	int DCwidth, DCheight;//窗口客户区宽、高(像素)

四、在鼠标移动消息处理程序中把图像按照原始大小显示到窗口

这一步骤在OnInitDialog()函数中是不能进行的,因为此时对话框还没有完成初始化。现在我们把这一工作放到鼠标移动消息处理函数中进行,因为程序开始运行窗口出现后,只要鼠标出现在窗口范围内,第一时间就可以将图像显示到对话框中。但存在一个问题,后边随着鼠标的经常性移动,会反复将原始大小图像显示到对话框窗口,如此会影响到后续的操作。为此,设置一个逻辑变量isFirst,让这个显示过程仅执行一次。这个isFirst也放到picEnlgReduTestDlg.h中去定义,代码就一句:

cpp 复制代码
BOOL isFirst=1; // 等于1时显示图像,显示后立即置为0

下面,我们进入类向导,类名选择CpicEnlgReduTestDlg,其余按缺省,然后点开消息,找到WM_MOUSEMOVE,点击"添加处理程序"。这时在picEnlgReduTestDlg.cpp中可以看到OnMouseMove()函数,在这个函数的TODO:行下添加下列代码:

cpp 复制代码
   if (isFirst == 1) {
     CDC* pDC; //声明设备上下文指针
	 pDC = GetDC(); //获得当前设备上下文指针(屏幕)
	 img.Draw(
	   pDC->m_hDC, //设备上下文对象(pDC->m_hDC通过指针获得对象)
	   (DCwidth - imgWidth) / 2, (DCheight - imgHeight) / 2 
		 //图像左上角在窗口中的坐标,上面计算的坐标可以保证图像中心与窗口中心重合
	 );
     pDC->DeleteDC(); //清理设备上下文指针
	 isFirst = 0; //将其置为0,使上面的显示代码仅执行一次
   }

说明:上面的这段代码,功能是将图像居中显示到窗口上,程序运行时只要鼠标在窗口范围内,这个程序即会运行起来。这段代码的核心语句是调用CImage::Draw()函数。这里Draw函数仅使用了前三个参数,忽略后边参数意义是表示图像是以原始大小(分辨率)显示在窗口上的,当图像尺寸大于窗口尺寸时,部分图像内容会显示不出来,但分辨率是原来的。这个函数是本程序使用的主要函数,后边还会继续使用,函数中参数含义如下:

参1:CDC类对象,也就是图像显示目标

参2、参3:图像左上角显示在窗口上的坐标。这个坐标允许选择在窗口外面,不报错。

五、在键盘按下消息处理程序中实现显示图像的缩放

还是通过类向导,类名仍选择CpicEnlgreduTestDlg,其余按缺省,然后点击消息,找到WM_KEYDOWN,点击"添加处理程序",这时在picEnlgReduTestDlg.cpp中会增加一个函数OnKeyDown()。这个函数处理按键被按下去了的消息,当然具体到是哪个按键被按下,还需要我们根据这个函数返回的参数值判断。OnKeyDown函数带着三个参数,头一个参数nChar是被按下键的键值,通过这个键值可以确定是哪个键被按下。在OnKeyDown函数的TODO:行下面,添加下列代码:

cpp 复制代码
  if (nChar == 107 || nChar==109) { //当+、-号被按下时
	int imgShowX = (imgWidth / 2 - DCwidth / 2) + 10 * countPlus - 10 * countMinus;
	int imgShowY = (imgHeight / 2 - DCheight / 2) + 10 * countPlus - 10 * countMinus;
	  //imgShowX、imgShowY图像拟显示区域左上角坐标;实际选取的图像区域小于窗口尺寸是放大显示,反之是缩小显示
	  //选取的图像区域增大是缩小显示。
	int imgShowWidth = DCwidth - 20 * countPlus + 20 * countMinus;
	int imgShowHeight = DCheight - 20 * countPlus + 20 * countMinus;
	  //imgShowWidth、imgShowHeight图像中拟显示区域的宽高(原理同上)
	if (nChar == 107 && imgShowWidth > 20 && imgShowHeight>20)	countPlus++;
	  //+号累计按下次数记录,当图像选取区域宽高小于20个像素时,按+号功能失效
        if (nChar == 109 && imgShowWidth < 5*DCwidth )countMinus++;
	  //当图像选取区域宽度达到窗口宽度的5倍时,按-号功能失效
	CDC* pDC; //声明设备上下文指针
	pDC = GetDC(); //获得当前设备上下文指针(指屏幕)
	SetStretchBltMode(pDC->m_hDC, COLORONCOLOR); //图像拉伸变形时会出现失真,做此设置则不会失真
	if (nChar == 109 && (imgShowX < 0 || imgShowY < 0)) pDC->FillSolidRect(0, 0, DCwidth, DCheight, RGB(255, 255, 255)); 
	   //当缩小显示且显示内容填不满整个窗口时,重绘窗口背景
	img.Draw(pDC->m_hDC, //设备上下文
	         0,0,//窗口左上角坐标
	    	 DCwidth,DCheight,//窗口宽、高(像素)
		     imgShowX,imgShowY, 
	          //图像拟显示区域做上角坐标
		     imgShowWidth,imgShowHeight
		      //图像拟显示区域的宽、高
                 );
	pDC->DeleteDC(); //清理设备上下文指针

说明:上面这段代码的功能是在接收到键盘按下"+"或"-"键消息时,对图像放大或缩小显示,同时保证图像的中心点不变,始终与窗口的中心重合。当图像放大到超过窗口的尺寸时,超出部分不被显示;当图像缩小到小于窗口的尺寸时,空出的区域需要用白色填充。放大、缩小都设置了停止点,以防程序崩溃,具体见代码。核心函数仍是CImage::Draw()(当然是重载的),这里使用的参数要多一些,共有9个:

参1:CDC对象

参2参3:窗口显示区域的左上角坐标

参4参5:窗口显示区域的宽、高

参6参7:图像中拟显示区域的左上角坐标

参8参9:图像中拟显示区域的宽高

注意,上面涉及两个坐标,一个是窗口的,一个是图像的不要混淆。

六、到此,实验相关的代码部分已经写好,运行后效果尚可;存在一点小问题:即缩小显示进行到小于窗口需重绘背景时,有些闪烁,如要解决可使用缓冲技术,这里不展开。

运行效果截图:

鼠标移动消息效果图:

放大效果图:

缩小效果图:

相关推荐
微网兔子5 分钟前
伺服器用什么语言开发呢?做什么用什么?
服务器·c++·后端·游戏
YuforiaCode7 分钟前
第十三届蓝桥杯 2022 C/C++组 修剪灌木
c语言·c++·蓝桥杯
YOULANSHENGMENG22 分钟前
linux 下python 调用c++的动态库的方法
c++·python
CodeWithMe38 分钟前
【C++】STL之deque
开发语言·c++
炯哈哈1 小时前
【上位机——MFC】运行时类信息机制
开发语言·c++·mfc·上位机
rigidwill6662 小时前
LeetCode hot 100—最长有效括号
数据结构·c++·算法·leetcode·职场和发展
阳光_你好3 小时前
C++/Qt中QActionGroup类用法
c++·qt
菜鸟射手3 小时前
QT creater和vs2017文件路径问题
linux·c++·windows·qt
wuqingshun3141593 小时前
蓝桥杯17. 机器人塔
c++·算法·职场和发展·蓝桥杯·深度优先
simple_whu4 小时前
解决编译pcl时报错‘chrono_literals‘: is not a member of ‘std‘
c++·windows·visual studio