1、计算机图形学 实验二 计算机科学系实验报告(首页) 课程名称计算机图形学班级12网络2实验名称VC存取BMP图像及其几何变换教导教师吴志攀姓名李文森学号1214080613213日期2014 .11.21一、实验目的1. 掌握VC中BMP图像的存取方法;2. 掌握BMP图像平移、旋转、变比等几何变换。二、实验设备与环境TC2.0,Windows XP三、实验内容、程序清单及运行结果1.打开VC+ 6.0,选择File|New进入界面。在Projects中选择MFC AppWinzard(exe),在Project name中输入项目名称,本例为ReadBMP,在Location中输入项目要保存
2、的文件夹。点击“OK”进入下一步。如下图21所示。图212.选择文档类型。在本例中使用的是单文档视图结构,所以这里选择Single document。其余部分设置使用VC+ 6.0的默认设置,点击“Finish”完成项目创建。如下图22所示:图223.为了将BMP中的数据读入到内存中,在项目中导入专门处理BMP文件头和数据的文件:DIBAPI.H和DIBAPI.CPP,在其中实现对BMP文件的大部分处理。在工作区“FileView”选项卡的“Header Files”中点右键,在“添加文件到目录”添加“DIBAPI.H”文件。如下图23所示:图23在工作区“FileView”选项卡的“Sour
3、ce Files”中点右键,在“添加文件到目录”添加“DIBAPI.CPP”文件。并在“ReadBMPDoc.h”添加头文件dibapi.h,如下所示:#include dibapi.h4在CReadBMPDoc类中添加保护成员变量CPalette* m_palDIB,HDIB m_hDIB和CSize m_sizeDoc。m_hDIB用于保存当前BMP图像句柄,m_palDIB用于指向BMP图像对应的调色板。protected: HDIB m_hDIB; CPalette* m_palDIB; CSize m_sizeDoc;5.为了取得保存在当前文档中的HDIB和Palette数据,在“R
4、eadBMPDoc.h”的CReadBMPDoc类中添加方法:GetHDIB,GetDocPalette和GDocSize。如下所示:/ Attributespublic: HDIB GetHDIB() const return m_hDIB; CPalette* GetDocPalette() const return m_palDIB; CSize GetDocSize() const return m_sizeDoc; 在CReadBMPDoc.cpp的构造函数中初始化: / 初始化变量 m_hDIB = NULL; m_palDIB = NULL; m_sizeDoc = CSize(
5、1,1);6.响应类CReadBMPDoc OnOpenDocument事件,以实现打开文件的操作。 从View|ClassWizard进入MFC ClassWizard界面,在Message Maps选项中完成消息映射。下图24所示:图24在BOOL CReadBMPDoc:OnOpenDocument(LPCTSTR lpszPathName)函数中添加如下代码: /if (!CDocument:OnOpenDocument(lpszPathName) /return FALSE; / TODO: Add your specialized creation code here /retur
6、n TRUE; CFile file; CFileException fe; / 打开文件 if (!file.Open(lpszPathName, CFile:modeRead | CFile:shareDenyWrite, &fe) / 失败 ReportSaveLoadException(lpszPathName, &fe, FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); / 返回FALSE return FALSE; DeleteContents(); / 更改光标形状 BeginWaitCursor(); / 尝试调用ReadDIBFile()读取图像 TRY
7、 m_hDIB = :ReadDIBFile(file); CATCH (CFileException, eLoad) / 读取失败 file.Abort(); / 恢复光标形状 EndWaitCursor(); / 报告失败 ReportSaveLoadException(lpszPathName, eLoad, FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); / 设置DIB为空 m_hDIB = NULL; / 返回FALSE return FALSE; END_CATCH / 初始化DIB InitDIBData(); / 恢复光标形状 EndWaitCursor
8、(); / 判断读取文件是否成功 if (m_hDIB = NULL) / 失败,可能非BMP格式 CString strMsg; strMsg = 读取图像时出错!可能是不支持该类型的图像文件!; / 提示出错 MessageBox(NULL, strMsg, 系统提示, MB_ICONINFORMATION | MB_OK); / 返回FALSE return FALSE; / 设置文件名称 SetPathName(lpszPathName); / 初始化胀标记为FALSE SetModifiedFlag(FALSE); / 返回TRUE return TRUE;并在ReadBMPDoc.
9、cpp添加Public成员函数InitDIBData,并添加如下程序:void CReadBMPDoc:InitDIBData() / 初始化DIB对象 / 判断调色板是否为空 if (m_palDIB != NULL) / 删除调色板对象 delete m_palDIB; / 重置调色板为空 m_palDIB = NULL; / 如果DIB对象为空,直接返回 if (m_hDIB = NULL) / 返回 return; LPSTR lpDIB = (LPSTR) :GlobalLock(HGLOBAL) m_hDIB); / 判断图像是否过大 if (:DIBWidth(lpDIB) IN
10、T_MAX |:DIBHeight(lpDIB) INT_MAX) :GlobalUnlock(HGLOBAL) m_hDIB); / 释放DIB对象 :GlobalFree(HGLOBAL) m_hDIB); / 设置DIB为空 m_hDIB = NULL; CString strMsg; strMsg = BMP图像太大!; / 提示用户 MessageBox(NULL, strMsg, 系统提示, MB_ICONINFORMATION | MB_OK); / 返回 return; / 设置文档大小 m_sizeDoc = CSize(int) :DIBWidth(lpDIB), (int
11、) :DIBHeight(lpDIB); :GlobalUnlock(HGLOBAL) m_hDIB); / 创建新调色板 m_palDIB = new CPalette; / 判断是否创建成功 if (m_palDIB = NULL) / 失败,可能是内存不足 :GlobalFree(HGLOBAL) m_hDIB); / 设置DIB对象为空 m_hDIB = NULL; / 返回 return; / 调用CreateDIBPalette来创建调色板 if (:CreateDIBPalette(m_hDIB, m_palDIB) = NULL) / 返回空,可能该DIB对象没有调色板 / 删
12、除 delete m_palDIB; / 设置为空 m_palDIB = NULL; / 返回 return; / 调用CreateDIBPalette来创建调色板 if (:CreateDIBPalette(m_hDIB, m_palDIB) = NULL) / 返回空,可能该DIB对象没有调色板 / 删除 delete m_palDIB; / 设置为空 m_palDIB = NULL; / 返回 return; 7.完成图片的打开操作之后,图片的数据就已经被保存在程序中,为了将图片显示出来还需要响应类CReadBMPView的OnDraw事件,在其中完成图像显示。void CReadBMP
13、View:OnDraw(CDC* pDC) /CReadBMPDoc* pDoc = GetDocument(); /ASSERT_VALID(pDoc); / TODO: add draw code for native data here / 显示等待光标 BeginWaitCursor(); / 获取文档 CReadBMPDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); / 获取DIB HDIB hDIB = pDoc-GetHDIB(); / 判断DIB是否为空 if (hDIB != NULL) LPSTR lpDIB = (LPSTR)
14、:GlobalLock(HGLOBAL) hDIB); / 获取DIB宽度 int cxDIB = (int) :DIBWidth(lpDIB); / 获取DIB高度 int cyDIB = (int) :DIBHeight(lpDIB); :GlobalUnlock(HGLOBAL) hDIB); CRect rcDIB; rcDIB.top = rcDIB.left = 0; rcDIB.right = cxDIB; rcDIB.bottom = cyDIB; CRect rcDest; / 判断是否是打印 if (pDC-IsPrinting() / 是打印,计算输出图像的位置和大小,以
15、便符合页面 / 获取打印页面的水平宽度(象素) int cxPage = pDC-GetDeviceCaps(HORZRES); / 获取打印页面的垂直高度(象素) int cyPage = pDC-GetDeviceCaps(VERTRES); / 获取打印机每英寸象素数 int cxInch = pDC-GetDeviceCaps(LOGPIXELSX); int cyInch = pDC-GetDeviceCaps(LOGPIXELSY); / 计算打印图像大小(缩放,根据页面宽度调整图像大小) rcDest.top = rcDest.left = 0; rcDest.bottom =
16、(int)(double)cyDIB * cxPage * cyInch) / (double)cxDIB * cxInch); rcDest.right = cxPage; / 计算打印图像位置(垂直居中) int temp = cyPage - (rcDest.bottom - rcDest.top); rcDest.bottom += temp/2; rcDest.top += temp/2; else / 非打印 / 不必缩放图像 rcDest = rcDIB; / 输出DIB :PaintDIB(pDC-m_hDC, &rcDest, pDoc-GetHDIB(), &rcDIB,
17、pDoc-GetDocPalette(); / 恢复正常光标 EndWaitCursor(); 8.编译、调试并运行程序,自此一个用于打开BMP图像的单文档视图结构的程序就完成了。通过修改当前位图句柄m_hDIB中存放像素的数据就可以对图像进行改变了。 9. 在项目中导入专门处理BMP几何变换的文件:GeoTrans.H和GeoTrans.CPP。并在ReadBMPView.cpp文件中添加:#include GeoTrans.h打开“Resource View”对菜单进行修改,添加“几何变换”菜单项。添加“图像平移”子菜单,ID为“ID_Geo_Trans”。在“CReadBMPView”中
18、建立名为“OnGeoTrans”的“OnCommand”函数。void CReadBMPView:OnGeoTrans() / TODO: Add your command handler code here / 平移位图 / 获取文档 CReadBMPDoc* pDoc = GetDocument(); / 平移位图 / 获取文档 /CCh1_1Doc* pDoc = GetDocument(); / 指向DIB的指针 LPSTR lpDIB; / 指向DIB象素指针 LPSTR lpDIBBits; / 锁定DIB lpDIB = (LPSTR) :GlobalLock(HGLOBAL)
19、pDoc-GetHDIB(); / 判断是否是8-bpp位图(这里为了方便,只处理8-bpp位图的平移,其它的可以类推) if (:DIBNumColors(lpDIB) != 256) / 提示用户 MessageBox(目前只支持256色位图的平移!, 系统提示 , MB_ICONINFORMATION | MB_OK); / 解除锁定 :GlobalUnlock(HGLOBAL) pDoc-GetHDIB(); / 返回 return; LONG lXOffset; LONG lYOffset; / 创建对话框 /CDlgGeoTran dlgPara; / 初始化变量值 /dlgPar
20、a.m_XOffset = 100; /dlgPara.m_YOffset = 100; / 显示对话框,提示用户设定平移量 /if (dlgPara.DoModal() != IDOK) / / 返回 /return; / / 获取用户设定的平移量 lXOffset = 100; lYOffset = 100; / 删除对话框 /delete dlgPara; / 更改光标形状 BeginWaitCursor(); / 找到DIB图像象素起始位置 lpDIBBits = :FindDIBBits(lpDIB); / 调用TranslationDIB()函数平移DIB if (Translat
21、ionDIB1(lpDIBBits, :DIBWidth(lpDIB), :DIBHeight(lpDIB), lXOffset, lYOffset) / 设置脏标记 pDoc-SetModifiedFlag(TRUE); / 更新视图 pDoc-UpdateAllViews(NULL); else / 提示用户 MessageBox(分配内存失败!, 系统提示 , MB_ICONINFORMATION | MB_OK); / 解除锁定 :GlobalUnlock(HGLOBAL) pDoc-GetHDIB(); / 恢复光标 EndWaitCursor(); 10. 在“几何变换”菜单项中添
22、加“垂直镜像”子菜单,ID为“ID_Geo_MirV”。在“CReadBMPView”中建立名为“OnGeoMirV”的“OnCommand”函数。 void CReadBMPView:OnGeoMirV() / TODO: Add your command handler code here / 垂直镜像 / 获取文档 CReadBMPDoc* pDoc = GetDocument(); / 指向DIB的指针 LPSTR lpDIB; / 指向DIB象素指针 LPSTR lpDIBBits; / 锁定DIB lpDIB = (LPSTR) :GlobalLock(HGLOBAL) pDoc-
23、GetHDIB(); / 判断是否是8-bpp位图(这里为了方便,只处理8-bpp位图的垂直镜像,其它的可以类推) if (:DIBNumColors(lpDIB) != 256) / 提示用户 MessageBox(目前只支持256色位图的垂直镜像!, 系统提示 , MB_ICONINFORMATION | MB_OK); / 解除锁定 :GlobalUnlock(HGLOBAL) pDoc-GetHDIB(); / 返回 return; / 更改光标形状 BeginWaitCursor(); / 找到DIB图像象素起始位置 lpDIBBits = :FindDIBBits(lpDIB);
24、/ 调用MirrorDIB()函数垂直镜像DIB if (MirrorDIB(lpDIBBits, :DIBWidth(lpDIB), :DIBHeight(lpDIB), FALSE) / 设置脏标记 pDoc-SetModifiedFlag(TRUE); / 更新视图 pDoc-UpdateAllViews(NULL); else / 提示用户 MessageBox(分配内存失败!, 系统提示 , MB_ICONINFORMATION | MB_OK); / 解除锁定 :GlobalUnlock(HGLOBAL) pDoc-GetHDIB(); / 恢复光标 EndWaitCursor()
25、;11. 在“几何变换”菜单项中添加“水平镜像”子菜单,ID为“ID_Geo_MirH”。在“CReadBMPView”中建立名为“OnGeoMirH”的“OnCommand”函数。void CReadBMPView:OnGeoMirH() / 水平镜像 / 获取文档 CReadBMPDoc* pDoc = GetDocument(); / 指向DIB的指针 LPSTR lpDIB; / 指向DIB象素指针 LPSTR lpDIBBits; / 锁定DIB lpDIB = (LPSTR) :GlobalLock(HGLOBAL) pDoc-GetHDIB(); / 判断是否是8-bpp位图(这
26、里为了方便,只处理8-bpp位图的水平镜像,其它的可以类推) if (:DIBNumColors(lpDIB) != 256) / 提示用户 MessageBox(目前只支持256色位图的水平镜像!, 系统提示 , MB_ICONINFORMATION | MB_OK); / 解除锁定 :GlobalUnlock(HGLOBAL) pDoc-GetHDIB(); / 返回 return; / 更改光标形状 BeginWaitCursor(); / 找到DIB图像象素起始位置 lpDIBBits = :FindDIBBits(lpDIB); / 调用MirrorDIB()函数水平镜像DIB if
27、 (MirrorDIB(lpDIBBits, :DIBWidth(lpDIB), :DIBHeight(lpDIB), TRUE) / 设置脏标记 pDoc-SetModifiedFlag(TRUE); / 更新视图 pDoc-UpdateAllViews(NULL); else / 提示用户 MessageBox(分配内存失败!, 系统提示 , MB_ICONINFORMATION | MB_OK); / 解除锁定 :GlobalUnlock(HGLOBAL) pDoc-GetHDIB(); / 恢复光标 EndWaitCursor();12. 在“几何变换”菜单项中添加“图像转置”子菜单,ID为“ID_Geo_Transpose”。在“CReadBMPView”中建立名为“OnGeoTranspose”的“OnCommand”函数。void CReadBMPView:OnGeoTranspose() / TOD