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文件声明!!!