工控编程吧
标题:
113上位机VC MFC任意角度旋转位图
[打印本页]
作者:
qq263946146
时间:
2015-12-23 21:09
标题:
113上位机VC MFC任意角度旋转位图
(, 下载次数: 1)
上传
点击文件名下载附件
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(); 便可
我们来演示功能实现过程
[iqiyi]http://player.video.qiyi.com/3b1d2530d84dd15f00ed54e409aa942e/0/0/w_19rt85slrh.swf-albumId=5077030209-tvId=5077030209-isPurchase=0-cnId=12[/iqiyi]
(, 下载次数: 0)
上传
点击文件名下载附件
[note]1[/note]
欢迎光临 工控编程吧 (https://www.gkbc8.com/)
Powered by Discuz! X3.4