113上位机VC MFC任意角度旋转位图
113上位机VC MFC任意角度旋转位图
功能展示
对位图的旋转操作涉及有一些旋转公式,可以自行编写程序实现有借助第三方库文件,我们当前例程通过自己编写函数ExeRotateBmp(nAngle)实现,只要传递一角度便可实现任意角度的旋转,效果如图 要点提示 旋转公式并不是我们关心和问题,我们只要知道,公式的计算用到了些三角函数,只要包含函数所在的头文件便可,也就是添加一句#include<math.h>,就可以在打开位图的发问下,调用例程中函数ExeRotateBmp(intnDegree = 45);//执行旋转位图;它所带参数角度单位为度,0到360度任意设置;
实现功能 1.新建基于对话框的应用程序 2.新建一对话框资源,用于显示打开的位图,设置资源属性子窗口,无边框标题带垂直水平滚动条,再添加位图控件,修改ID为IDC_BMP。 为此对话框资源关联类class CBmpDlg : public Cdialog,之后给位图控件IDC_BMP也关联一变量CStatic m_Bmp;用于加载位图; 3.添加三变量private: BITMAPFILEHEADERm_bmFileHeader; //位图文件头 BITMAPINFOHEADER m_bmInfoHeader; //位图信息头 BYTE *m_pBmpData;//位图数据;再进行初始化 CRect rect; GetClientRect(rect);m_Bmp.MoveWindow(0,0,rect.Width(),rect.Height()); m_pBmpData = NULL; 4.添加四个函数,用于位图打开,保存,旋转操作public: CString LoadBmp(); //加载位图,返回位图路径 void SaveBmp(); //保存位图 voidRotateBmp(int nDegree=45); //旋转位图 void ExeRotateBmp(int nDegree = 45);//执行旋转位图;
函数体部份为:
- CString CBmpDlg::LoadBmp()
- {
- CString filename;//用于保存加载的位图路径
- CFileDialog dlg(TRUE,"","",OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"位图文件(BMP)|*.bmp",this);
- if (dlg.DoModal()==IDOK)
- {
- HBITMAP hBmp=NULL;
- filename = dlg.GetPathName();//获取位图路径加名称加后缀
- hBmp = (HBITMAP)LoadImage(NULL,filename,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);//加载位图
- if(hBmp)m_Bmp.SetBitmap((HBITMAP)hBmp);//加载成功,设置位置
- //>< 读取位图信息
- CFile file;
- file.Open(filename,CFile::modeRead);//打开文件
- //读取位图文件头
- file.Read(&m_bmFileHeader,sizeof(BITMAPFILEHEADER));
- //读取位图信息头
- file.Read(&m_bmInfoHeader,sizeof(BITMAPINFOHEADER));
- //判断是否为真彩色位图
- if (m_bmInfoHeader.biBitCount != 24)
- {
- file.Close();
- MessageBox("请选择真彩色位图!","工控编程吧-提示");
- return filename;
- }
- //><读取位图实际数据大小
- if (m_pBmpData != NULL)//如果之前加载了位图数据,则删除位图数据
- {
- delete []m_pBmpData;
- m_pBmpData = NULL;
- }
- m_pBmpData = new BYTE[m_bmInfoHeader.biSizeImage];//根据位图数据大小为位图数据分配空间
- file.ReadHuge(m_pBmpData,m_bmInfoHeader.biSizeImage);//读取位图数据到堆空间中
- file.Close();
- //><改为显示时的位图数据,位图每一个像素占用4个字节,这样每一行位图数据将是4的整数倍
- //计算修改后位图数据的大小
- int sizeofbuffer = m_bmInfoHeader.biWidth * m_bmInfoHeader.biHeight * 4;
- int externWidth;
- externWidth = m_bmInfoHeader.biWidth * 3;//计算源位图每行使用的字节数
-
复制代码- if(externWidth % 4 != 0)
- externWidth = 4 - externWidth % 4;
- else
- externWidth = 0;
- int k = 0;
-
- BYTE* pImageTempBuffer = new BYTE[sizeofbuffer];//构建新位图数据的缓冲区
- //真彩色位图通常是倒序的,因此,从底部向上访问位图数据
- for (long n = m_bmInfoHeader.biHeight - 1; n >= 0; n--)
- {
- //遍历每一个位图数据
- for (long m = 0; m < m_bmInfoHeader.biWidth * 3; m += 3)
- {
- //获取源位图数据中的像素颜色值(RGB值)
- pImageTempBuffer[k] = m_pBmpData[n*(m_bmInfoHeader.biWidth*3+externWidth)+m]; //blue
- pImageTempBuffer[k+1] = m_pBmpData[n*(m_bmInfoHeader.biWidth*3+externWidth)+m+1];//green
- pImageTempBuffer[k+2] = m_pBmpData[n*(m_bmInfoHeader.biWidth*3+externWidth)+m+2];//red
- pImageTempBuffer[k+3] = 255;
- k += 4; //设置下一个像素颜色值
- }
- }
-
- delete []m_pBmpData;//释放源位图数据
- m_pBmpData = new BYTE[sizeofbuffer];
- memcpy(m_pBmpData, pImageTempBuffer, sizeofbuffer);//复制新的位图数据
- delete []pImageTempBuffer;//释放pImageTempBuffer对象
-
- //><设置滚动条信息
- CRect bmprect;
- m_Bmp.GetClientRect(bmprect);
-
- SCROLLINFO vinfo;
- vinfo.cbSize = sizeof(vinfo);
- vinfo.fMask = SIF_ALL;
- vinfo.nPage = 100;
- vinfo.nMax= bmprect.Height();
- vinfo.nMin = 0;
- vinfo.nTrackPos = 0;
-
复制代码- vinfo.nPos = 0;
- SetScrollInfo(SB_VERT,&vinfo);//设置垂直滚动条信息
- //
- vinfo.fMask = SIF_ALL;
- vinfo.nPage = 10;
- vinfo.nMax= bmprect.Width();
- vinfo.nMin = 0;
- vinfo.nPos = 0;
- vinfo.nTrackPos = 0;
- vinfo.cbSize = sizeof(vinfo);
- SetScrollInfo(SB_HORZ,&vinfo);//设置水平滚动条信息
- }
- return filename;
- }
- void CBmpDlg::SaveBmp()
- {
-
- if (m_Bmp.GetBitmap())//已经加载过位图时
- {
- //定义文件保存对话框
- CFileDialog flDlg(FALSE, "bmp", "Demo.bmp", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "位图文件|*.bmp||");
- if (flDlg.DoModal()==IDOK) //打开文件保存对话框
- {
- try
- {
- //获取保存的文件名称
- CString csSaveName = flDlg.GetPathName();
- CFile file;
- file.Open(csSaveName,CFile::modeCreate|CFile::modeReadWrite);
- file.Write(&m_bmFileHeader,sizeof(m_bmFileHeader));//写入位图文件信息头
- file.Write(&m_bmInfoHeader,sizeof(m_bmInfoHeader));//写入位图信息头
- //确定转换后的位图的宽度和高度
- UINT outHeight = m_bmInfoHeader.biHeight;
- UINT outWidth = m_bmInfoHeader.biWidth;
- //定义一个指针,指向最后一行位图数据
- BYTE * pListData = m_pBmpData+((DWORD)outHeight-1)*outWidth*4;
- //计算字节对齐,如果每行位图数据字节数不是4的整数倍,需要进行补齐
- BYTE byByteAlign;
- if (outWidth % 4 != 0)
-
复制代码- byByteAlign = 4-((outWidth * 3L) % 4);
- else
- byByteAlign = 0;
- BYTE byZeroData = 0;
- //按行和列遍历位图数据
- for (UINT y=0; y<outHeight; y++)
- {
- for (UINT x=0; x<outWidth; x++)
- {
- //向文件中写入一个像素的位图数据
- file.Write(pListData, 3);
- pListData += 4; //指向下一个像素
- }
- //如果需要字节补齐,在每一行结尾需要填充适当的字节数据
- for (UINT i=0; i<byByteAlign; i++)
- {
- file.Write(&byZeroData,1);
- }
- //指向上一行数据
- pListData -= 2L*outWidth*4;
- }
- file.Close(); //关闭文件
- }
- catch(...)
- {
- MessageBox("文件保存失败!","工控编程吧-提示");
- }
- }
- }
- }
- void CBmpDlg::RotateBmp(int nDegree)
- {
- if (!m_Bmp.GetBitmap())//未加载过位图
- return;
- ExeRotateBmp(nDegree);//旋转位图
-
- //获取旋转后的位图大小
- UINT outHeight = m_bmInfoHeader.biHeight;
- UINT outWidth = m_bmInfoHeader.biWidth;
- BYTE* pBmpData = new BYTE [m_bmInfoHeader.biSizeImage];//为新位图分配堆空间
-
复制代码- memset(pBmpData,0,m_bmInfoHeader.biSizeImage);//初始化堆空间
- //获取位图数据中的最后以行数据
- BYTE * pListData =m_pBmpData+((DWORD)outHeight-1)*outWidth*4;
-
- //计算位图数据每行补齐的字节数
- BYTE byByteAlign ; //位图行字节对齐
- if (outWidth %4 != 0)
- byByteAlign = 4- ((outWidth*3L) % 4);
- else
- byByteAlign = 0;
-
- BYTE byZeroData = 0;
- BYTE* pTmpData = pBmpData;
- //对旋转后的位图数据进行修改,使得使用3个字节表示一个像素数据
- for (UINT y=0 ;y<outHeight;y++)
- {
- for (UINT x=0;x<outWidth;x++)
- {
- memcpy(pTmpData,pListData,3);
- pTmpData += 3;
- pListData += 4;
- }
- for (UINT i=0; i<byByteAlign; i++)
- {
- memcpy(pTmpData,&byZeroData,1);
- pTmpData =pTmpData + 1;
- }
- pListData -= 2L*outWidth*4;
- }
-
- CDC *pDC = m_Bmp.GetDC();
- BITMAPINFO bInfo;
- bInfo.bmiHeader = m_bmInfoHeader;
- //在图像控件中显示旋转后的位图
- m_Bmp.SetBitmap(CreateDIBitmap(pDC->m_hDC,&m_bmInfoHeader, CBM_INIT, pBmpData, &bInfo, DIB_RGB_COLORS));
- delete [] pBmpData;
-
- //设置滚动范围
- CRect bmpRC,wndRC;
- GetClientRect(wndRC);
- m_Bmp.GetWindowRect(bmpRC);
-
复制代码- OnHScroll(SB_LEFT, 1, NULL);
- OnVScroll(SB_LEFT, 1, NULL);
-
- SetScrollRange(SB_VERT,0,bmpRC.Height()-wndRC.Height()+100);
- SetScrollRange(SB_HORZ,0,bmpRC.Width()-wndRC.Width());
- }
- void CBmpDlg::ExeRotateBmp(int nDegree)
- {
- //源图像宽度和高度
- UINT srcWidth = m_bmInfoHeader.biWidth;
- UINT srcHeight = m_bmInfoHeader.biHeight;
- double pi = 3.1415926535;
- double dRadian =nDegree* (pi/180.0);//将角度转换为弧度
- //计算目标图像宽度和高度
- UINT desWidth = (abs)(srcHeight*sin(dRadian)) + (abs)(srcWidth*cos(dRadian))+1;
- UINT desHeight = (abs)(srcHeight*cos(dRadian)) + (abs)(srcWidth*sin(dRadian))+1;
- //<>更新旋转后的文件头信息
- UINT desLineBytes;
- desLineBytes = desWidth * m_bmInfoHeader.biBitCount / 8;
- int mod = desLineBytes % 4;
- if (mod != 0)
- desLineBytes += 4 - mod;
- m_bmInfoHeader.biSizeImage = desHeight*desLineBytes; //设置图像数据大小
- m_bmInfoHeader.biHeight = desHeight;
- m_bmInfoHeader.biWidth = desWidth;
- //<>进行图像数据部份旋转
- //在堆中分配一个空间,用于存储旋转后的位图数据
- BYTE* pDesData = NULL;
- pDesData = new BYTE[desWidth * desHeight * 4];
- memset(pDesData, 255, desWidth * desHeight * 4);//初始堆空间中的数据
- //计算dx和dy
- double dSin = sin(dRadian);
- double dCos = cos(dRadian);
- double dX = -0.5*desWidth*dCos - 0.5*desHeight*dSin + 0.5*srcWidth;
- double dY = 0.5*desWidth*dSin - 0.5*desHeight*dCos + 0.5*srcHeight;
-
复制代码- BYTE* pSrc = NULL;
- BYTE* pDes = NULL;
- int x = 0;
- int y = 0;
- for (int h = 0; h < desHeight; h++)
- {
- for (int w = 0; w < desWidth; w++)
- {
- //加0.5是为了向上取整
- //x,y表示目标区域w,h坐标点对应的源图像中的坐标
- x = (int)(w * dCos + h * dSin + dX + 0.5);
- y = (int)(-w * dSin + h * dCos + dY + 0.5);
- if (x == srcWidth)
- {
- x--;
- }
- if (y == srcHeight)
- {
- y--;
- }
- pSrc = m_pBmpData + y * srcWidth * 4 + x * 4;
- pDes = pDesData + h * desWidth * 4 + w * 4;
- //判断目标区域中的坐标是在源位图中存在坐标点,通常目标区域会比源位图区域大
- //因此,有些目标区域中的有些坐标在源位图中是没有对应的坐标点,程序过滤掉这些坐标点
- if (x >= 0 && x < srcWidth && y >= 0 && y < srcHeight)
- {
- memcpy(pDes, pSrc, 4);
- }
- }
- }
- //<> 更新保存旋转后数据到m_pBmpData
- //如果m_pBmpData包含位图数据,则先释放位图数据
- if (m_pBmpData != NULL)
- {
- delete []m_pBmpData;
- m_pBmpData = NULL;
- }
-
复制代码- //重新复制旋转之后的位图数据
- m_pBmpData = new BYTE[desHeight*desWidth*4];
- memset(m_pBmpData,255,desHeight*desWidth*4);
- memcpy(m_pBmpData,pDesData,desHeight*desWidth*4);
- //释放临时对象
- delete [] pDesData;
- pDesData = NULL;
- }
复制代码4.水平垂直滚动条的操作部分,可参考例程,我们来使用自定义的类,在主对话框资源中拖拽一位图控件修改ID为IDC_RECT,用于定位位图显示位置;添加变量CBmpDlg m_BmpDlg;并初始化 m_BmpDlg.Create(IDD_DIALOG1,this);//创建显示位图用窗口 CRect rc;GetDlgItem(IDC_RECT)->GetWindowRect(rc); ScreenToClient(rc); m_BmpDlg.MoveWindow(rc);m_BmpDlg.ShowWindow(SW_SHOW); 添加加载位图,旋转位置,保存位图按钮控件,关联函数分别调用 m_BmpDlg.LoadBmp(); m_BmpDlg.SaveBmp(); m_BmpDlg.RotateBmp(); 便可
我们来演示功能实现过程
|