MFC对话框程序之算法实现图片缩放

MFC综合运用

计算机思维与算法分析课程结课作业

  • 工程说明

平台:Visual Studio C++ MFC对话框

功能:结合使用对话框的Button、Static TEXT 以及Edit Control 控件,使用课上学习的最近邻算法,实现对图片的导入,放大,缩小,左右移动,上下移动以及对图像的旋转功能。还实现了对话框的背景设置,对话框的标题栏重命名。(背景设置没有在下面的内容提到)

  • 此工程中的图片缩放是使用最近邻算法实现,除算法以外还可以使用第三方库函数
  • 工程实现效果
    实现效果

一、创建工程

请参考 :如何创建与使用MFC对话框工程

使用对话框编辑器中的工具,在窗口中创建对应的Button、Edit Control、Static Text控件,注意创建时将控件摆放到相对应得位置,不然需要另外写代码设置摆放位置。
控件

二、生成对应控件函数

如果需要编辑对应控件的效果,直接再上述窗口中双击该控件,会自动生成并跳转到对应的函数中。

1.位图框位置设置

因为我的图片控件使用的是Edit Control,而不是静态的Picture Control,所以需要自己根据需求对两个图片框设置位置。

需要在初始话的函数OnInitDialog()中添加如下代码

//功能相关
	m_bmp.ModifyStyle(0xf, SS_BITMAP | SS_CENTERIMAGE);//设置位图风格,控件将显示位图,并且位图将居中显示
	m_bmp2.ModifyStyle(0xf, SS_BITMAP | SS_CENTERIMAGE);//设置位图风格
	int  width = 350;//框的宽度
	int  height = 450;//框的高度
	//MoveWindow(0, 0, 3 * width + 50, height + 100);//移动和调整对话框的大小,00为左上角坐标
	//设置适合位图的大小
	m_bmp.MoveWindow(70, 80, width, height);
	m_bmp2.MoveWindow(1.7 * width, 80, width, height);
	int pos = width * 3 / 2 - 70;

2.导入图片按钮

双击导入按钮后,会自动跳转到相关函数

void Cwork1Dlg::OnBnClickedimport()//下面函数是导入按钮实现功能的函数,局限:只能导入单张图片—--目标—>导入多张
{
	// TODO: 在此添加控件通知处理程序代码
	CFileDialog fileDlg(TRUE, _T("png"), NULL, 0, _T("image Files(*.bmp; *.jpg;*.png)|*.JPG;*.PNG;*.BMP|All Files (*.*) |*.*|"), this);
	fileDlg.DoModal();

	strFilePath = fileDlg.GetPathName();		//文件路径
	strFileName = fileDlg.GetFileName();	//文件名
	if (strFilePath == _T(""))
	{
		return;
	}
	CImage image;
	size++;
	pos = size;
	times = 1;//倍数
	image.Load(strFilePath);				//加载位图
	CRect rectControl;                        //控件矩形对象
	m_bmp.GetClientRect(rectControl);		//左边的框
	CDC* pDc = m_bmp.GetDC();			 //设备上下文对象的类
	rectControl = CRect(rectControl.TopLeft(), CSize((int)rectControl.Width(), (int)rectControl.Height()));
	m_bmp.SetBitmap(NULL);				//清空picture
	//image.Draw(pDc->m_hDC, rectControl);    //将图片绘制到控件表示的矩形区域
	//image.Destroy();

	HBITMAP a = image.Detach();//将一个 CImage 对象转换为 HBITMAP 类型
	//下面是存储位图以及显示当前位图的操作
	bmparray[pos - 1] = a;
	bmp[pos - 1].Attach(a);
	m_bmp.SetBitmap(a);//SetBitmap 函数将位图显示到 m_bmp 控件中

	bm.Detach();//释放之前绑定的句柄
	times = 1;
	m_Angle = 0;
	xstep = 0;
	ystep = 0;
	m_bmp.ReleaseDC(pDc);
	bm.Attach(bmparray[pos - 1]);

}

3. 旋转按钮与输入编辑框

注意旋转按钮需要与输入编辑框配套使用

3.1 旋转角度的转换与上下移动的实现函数
//************对图片进行旋转上下左右移动的函数***************//
CBitmap* Cwork1Dlg::BmpRotate(CBitmap* cBmp)
{
	if (times >= 1)
	{
		BITMAP bmp;
		cBmp->GetBitmap(&bmp);
		BYTE* pBits = new BYTE[bmp.bmWidthBytes * bmp.bmHeight], * TempBits = new BYTE[bmp.bmWidthBytes * bmp.bmHeight];
		cBmp->GetBitmapBits(bmp.bmWidthBytes * bmp.bmHeight, pBits);
		int interval = bmp.bmWidthBytes / bmp.bmWidth;
		double rx0 = bmp.bmWidth * 0.5;
		double ry0 = bmp.bmHeight * 0.5;
		UpdateData(TRUE);
		m_Angle += _ttof(m_str);
		float Angle = m_Angle / 180 * 3.1415926;//化为弧度制
		for (int j = 0; j < bmp.bmHeight; j++)
		{
			for (int i = 0; i < bmp.bmWidth; i++)
			{
				int tempI, tempJ;
				//tempI = 2 * rx0 - i; //行坐标关于中心点的对称点
				//tempJ = 2 * ry0 - j;//列坐标关于中心点的对称点

				//下面利用极坐标求得旋转位置
				float R = sqrt(pow((i - rx0), 2) + pow((j - ry0), 2));//求半径

				//求目的坐标在极坐标中与极轴夹角的正弦、余弦值
				float cos_a = (i - rx0) / R * cos(Angle) - (j - ry0) / R * sin(Angle);
				float sin_a = sin(Angle) * (i - rx0) / R + cos(Angle) * (j - ry0) / R;
				tempI = R * cos_a + rx0 + xstep * 100.0;
				tempJ = R * sin_a + ry0 + ystep * 100.0;
				if (tempI > 0 && tempI < bmp.bmWidth)
					if (tempJ > 0 && tempJ < bmp.bmHeight)
					{
						for (int m = 0; m < interval; m++)
							TempBits[j * bmp.bmWidthBytes + i * interval + m] = pBits[tempJ * bmp.bmWidthBytes + interval * tempI + m];
						//TempBits[tempJ * bmp.bmWidthBytes + interval * tempI + m] = pBits[j * bmp.bmWidthBytes + i * interval + m];
					//j * bmp.bmWidthBytes定位到该行初始部分的像素位
					//i * interval + m以块状的方式填充某行中的像素位
					}
			}
		}
		CBitmap* m_bitmap;
		m_bitmap = new CBitmap;
		m_bitmap = cBmp;
		m_bitmap->SetBitmapBits(bmp.bmWidthBytes * bmp.bmHeight, TempBits);
		m_bmp2.SetBitmap((HBITMAP)*m_bitmap);
		cBmp->SetBitmapBits(bmp.bmWidthBytes * bmp.bmHeight, pBits);//每次旋转都要恢复原图片信息,防止旋转时失真
		delete[] pBits;
		delete[] TempBits;
		return m_bitmap;
	}
	else
	{//使用 Windows GDI 函数来实现图像的旋转和缩放
		BITMAP BMP;
		bmp[pos - 1].GetBitmap(&BMP);// 获取位图数组 bmp 中第 pos-1 个元素的位图信息,并存储在 BMP 中。
		HDC hdc = GetDC()->GetSafeHdc();// 获取设备上下文句柄。
		HDC memDC = CreateCompatibleDC(hdc);// 创建一个与屏幕兼容的内存设备上下文 memDC。
		
		// 创建一个新的位图 hBmpDest,其宽度和高度分别为原位图的宽度和高度乘以 times。
		HBITMAP hBmpDest = CreateCompatibleBitmap(hdc, BMP.bmWidth * times, BMP.bmHeight * times);
		
		// 将新位图选定为 memDC。
		HBITMAP hOldBmp = (HBITMAP)SelectObject(memDC, hBmpDest);
		m_Angle += _ttof(m_str);
		XFORM xForm;
		xForm.eM11 = (FLOAT)cos(m_Angle * 3.1415926 / 180.0);
		xForm.eM12 = (FLOAT)sin(m_Angle * 3.1415926 / 180.0);
		xForm.eM21 = (FLOAT)-sin(m_Angle * 3.1415926 / 180.0);
		xForm.eM22 = (FLOAT)cos(m_Angle * 3.1415926 / 180.0);
		xForm.eDx = (FLOAT)0.0;
		xForm.eDy = (FLOAT)0.0;

		SetGraphicsMode(memDC, GM_ADVANCED);
		SetWorldTransform(memDC, &xForm);

		StretchBlt(memDC, 0, 0, BMP.bmWidth * times, BMP.bmHeight * times, hdc, 0, 0, BMP.bmWidth, BMP.bmHeight, SRCCOPY);

		SelectObject(memDC, hOldBmp);
		DeleteDC(memDC);

		bmparray[pos - 1] = hBmpDest;
		m_bmp2.SetBitmap(bmparray[pos - 1]);

		bmparray[pos - 1] = (HBITMAP)bmp[pos - 1];
		return &bmp[pos - 1];

	}
}
3.2 旋转按钮

难点这里注意到其实控件与控件之间是有联系的,所以将控件关联起来非常重要
如果没有将其想关联,代码编译不会报错,但是代码运行后,无法点击实现功能,因为代码中存在空指针,会报【断言】的错误(别问我为什么知道,很痛QAQ)
需要在DoDataExchange(CDataExchange* pDX)函数中将控件关联起来
如图所示
关联

按钮函数:

void Cwork1Dlg::OnBnClickedBmprotatebutton()
{
	// TODO: 在此添加控件通知处理程序代码

	BmpRotate(&bm);
}
//编辑框的函数不用写任何内容
void Cwork1Dlg::OnEnChangeText()
{
	// TODO:  如果该控件是 RICHEDIT 控件,它将不
	// 发送此通知,除非重写 CDialogEx::OnInitDialog()
	// 函数并调用 CRichEditCtrl().SetEventMask(),
	// 同时将 ENM_CHANGE 标志“或”运算到掩码中。

	// TODO:  在此添加控件通知处理程序代码
}

4.放大缩小按钮

4.1 图片缩放

首先需要写一个进行图片缩放的函数,关于最近邻算法,详见最近邻算法
除最近邻算法以外还可以用双三次插值算法,双线性插值图像缩放算法等图像算法实现

/*****最近邻算法实现图片的缩放*******此函数接受一个源位图和新的宽度和高度作为参数,返回一个新的位图,表示缩放后的结果****************/

HBITMAP Cwork1Dlg::NearestNeighborResize(HBITMAP hBmpSrc, int newWidth, int newHeight)
{
	// 获取源位图的信息
	BITMAP bmpSrc;
	GetObject(hBmpSrc, sizeof(BITMAP), &bmpSrc);

	// 创建目标位图
	HDC hdc = GetDC()->GetSafeHdc();
	HBITMAP hBmpDest = CreateCompatibleBitmap(hdc, newWidth, newHeight);

	// 获取源位图和目标位图的数据
	int srcWidth = bmpSrc.bmWidth;
	int srcHeight = bmpSrc.bmHeight;
	int srcBytesPerPixel = bmpSrc.bmBitsPixel / 8;
	int srcBytesPerLine = ((srcWidth * srcBytesPerPixel + 3) / 4) * 4;
	int destBytesPerLine = ((newWidth * srcBytesPerPixel + 3) / 4) * 4;
	std::vector<BYTE> srcData(srcBytesPerLine * srcHeight);
	std::vector<BYTE> destData(destBytesPerLine * newHeight);
	GetBitmapBits(hBmpSrc, srcData.size(), srcData.data());

	// 执行最近邻缩放
	for (int y = 0; y < newHeight; y++)
	{
		for (int x = 0; x < newWidth; x++)
		{
			int srcX = x * srcWidth / newWidth;
			int srcY = y * srcHeight / newHeight;
			for (int i = 0; i < srcBytesPerPixel; i++)
			{
				destData[y * destBytesPerLine + x * srcBytesPerPixel + i] =
					srcData[srcY * srcBytesPerLine + srcX * srcBytesPerPixel + i];
			}
		}
	}
	// 设置目标位图的数据
	SetBitmapBits(hBmpDest, destData.size(), destData.data());
	return hBmpDest;
}
4.2 放大按钮
//*******放大****最近邻算法 (Nearest)实现图像缩放******由于是间隔画点,所以画出来的图像会不好看,采用像素点比较少的图片会比较明显
void Cwork1Dlg::OnBnClickednerast()
{
	// TODO: 在此添加控件通知处理程序代码
	BITMAP BMP;
	bmp[pos - 1].GetBitmap(&BMP);
	if (times <= 1)
		times *= 2;
	else
		times++;

	CRect rect;
	m_bmp.GetClientRect(&rect);

	// 使用最近邻算法来缩放位图
	bmparray[pos - 1] = NearestNeighborResize(bmparray[pos - 1], BMP.bmWidth * times, BMP.bmHeight * times);
	
	m_bmp2.SetBitmap(bmparray[pos - 1]);//SetBitmap 函数将位图显示到 m_bmp2 控件中

	bm.Detach(); //释放之前绑定的句柄
	bm.Attach(bmparray[pos - 1]);

	m_Angle -= _ttof(m_str);
	xstep = 0;
	ystep = 0;
	
	m_bmp2.SetBitmap(bmparray[pos - 1]);//SetBitmap 函数将位图显示到 m_bmp2 控件中

	bm.Detach(); //释放之前绑定的句柄
	bm.Attach(bmparray[pos - 1]);

	xstep = 0;
	ystep = 0;

}
3.3 缩小按钮
//缩小图片的按钮
void Cwork1Dlg::OnBnClickeddisnerast()
{
	// TODO: 在此添加控件通知处理程序代码
	BITMAP BMP;
	bmp[pos - 1].GetBitmap(&BMP);
	if (times <= 1)
		times /= 2;
	else
		times--;

	CRect rect;
	m_bmp.GetClientRect(&rect);

	// 使用最近邻算法来缩放位图
	bmparray[pos - 1] = NearestNeighborResize(bmparray[pos - 1], BMP.bmWidth * times, BMP.bmHeight * times);

	m_bmp2.SetBitmap(bmparray[pos - 1]);//SetBitmap 函数将位图显示到 m_bmp2 控件中

	bm.Detach(); //释放之前绑定的句柄
	bm.Attach(bmparray[pos - 1]);

	m_Angle -= _ttof(m_str);
	bmparray[pos - 1] = (HBITMAP)*BmpRotate(&bm);
	bmparray[pos - 1] = (HBITMAP)bmp[pos - 1];
	xstep = 0;
	ystep = 0;
}

5.左右移动按钮


//左移按钮
void Cwork1Dlg::OnBnClickedLButton()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Angle -= _ttof(m_str);
	xstep++;
	//BmpRotate(&bmp[pos - 1]);
	BmpRotate(&bm);
}

//右移按钮
void Cwork1Dlg::OnBnClickedRButton()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Angle -= _ttof(m_str);
	xstep--;
	//BmpRotate(&bmp[pos - 1]);
	BmpRotate(&bm);
}

6.上下移动按钮

void Cwork1Dlg::OnBnClickedUButton()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Angle -= _ttof(m_str);
	ystep++;
	//BmpRotate(&bmp[pos - 1]);
	BmpRotate(&bm);

}

void Cwork1Dlg::OnBnClickedDButton()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Angle -= _ttof(m_str);
	ystep--;
	//BmpRotate(&bmp[pos - 1]);
	BmpRotate(&bm);
}

}

6.上下移动按钮

void Cwork1Dlg::OnBnClickedUButton()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Angle -= _ttof(m_str);
	ystep++;
	//BmpRotate(&bmp[pos - 1]);
	BmpRotate(&bm);

}

void Cwork1Dlg::OnBnClickedDButton()
{
	// TODO: 在此添加控件通知处理程序代码
	m_Angle -= _ttof(m_str);
	ystep--;
	//BmpRotate(&bmp[pos - 1]);
	BmpRotate(&bm);
}

写在最后:

代码相关变量声明需要在.h文件声明!!!

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写的什么石山代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值