c++课程设计简易绘图程序.docx
- 文档编号:29930287
- 上传时间:2023-08-03
- 格式:DOCX
- 页数:25
- 大小:1.28MB
c++课程设计简易绘图程序.docx
《c++课程设计简易绘图程序.docx》由会员分享,可在线阅读,更多相关《c++课程设计简易绘图程序.docx(25页珍藏版)》请在冰豆网上搜索。
c++课程设计简易绘图程序
1、需求与功能分析
为了检验一个学期以来自己学习c++这门课程能够学到多少知识,加上原来就对图像处理就比较感兴趣,曾经更有想过要做一个可以合成红蓝3D图片的一款软件,就是那种需要带上红蓝眼镜才可以看到3D效果的那种图片,自己也有用photoshop做过这样的图片,但是photoshop需要调很多东西才能合成这样的图片,而且还要用相机送两个不同的角度拍两张照片作为源图片去合成红蓝3D图片,虽然我知道原理并不难,但是当我选择了”简易绘图程序”这个课程设计的题目之后,才发现每实现一个功能都需要花很多的精力,而且实现那些功能之后还会发现各种各校的bug,需要细心去寻找解决的办法。
如今课程设计也要上交,一样还有很多我想实现的功能都还没实现,最终也没有实现我想要实现的功能---合成红蓝3D图片。
只做了一些简单的绘图功能。
功能分析,目前这个软件的主要功能有如下:
1、画直线:
通过OnLine()函数实现。
2、画矩形:
通过OnRectangle()函数实现。
3、画椭圆:
通过OnEllipse()函数实现。
4、铅笔工具:
可以画任意线条。
通过OnPencil()函数实现。
5、橡皮擦:
可以擦除所画的线条。
该功能还没实现。
6、右键弹出菜单:
可以在客户中点击鼠标右键,快速选择常用菜单。
通过OnContextMenu函数实现。
7、状态栏显示鼠标移动的坐标:
在程序的右下角显示,通过OnUpdateX(CCmdUI*pCmdUI)和OnUpdateY(CCmdUI*pCmdUI)这两个函数实现。
8、画图颜色选择:
可以画任何颜色的线条,通过OnEditColor()函数实现。
9、读取bmp位图:
可以在电脑中选择一个bmp位图,在程序中显示出来,通过新建一个Cdib类来读取bmp文件信息再view类和Doc类调用来显示该位图。
10、保存为bmp位图:
可以将在程序所画的图形保存为一幅bmp图片,通过view中的SaveAsBmp(CStringfilename)实现。
11、线条类型及线宽的设置:
可以将画出的线条设置成实线、虚线、点线,还可以设置线条的粗细,通过示例这一功能可以立刻显示所选择线条的粗细及线型。
通过新建CSettingDlg类,再在view类中调用OnSetting()函数实现画笔的对话框,CSettingDlg类中的OnPaint()函数是实现示例功能的。
12、窗口的重绘时不擦除原来的内容:
可以在窗口发生变化时,窗口的内容不消失,程序在窗口尺寸发生变化时,将会发送WM_PAINT消息,这时首先会擦除窗口背景,然后进行重绘操作,这样就把窗口中先前绘制的内容擦除了。
我是使用双缓冲来解决这个问题的,双缓冲设计思路:
如果只是为了让窗口闪烁不那么厉害,避免背景色的填充是最直接的办法。
但是那样的话,窗体上会变的一团糟。
因为每次绘制图象的时候都没有将原来的图象清除,造成了图象的残留,于是窗体重绘时,画面往往会变的乱七八糟。
所以单纯的禁止背景重绘是不够的。
我们还要进行重新绘图,但要求速度很快,于是想到了使用BitBlt函数。
它可以支持图形块的复制,速度很快。
我们可以先在内存中作图,然后用此函数将做好的图复制到前台,同时禁止背景刷新,这样就消除了闪烁。
以上就是双缓冲绘图的基本的思路。
一部分代码写在了OnLButtonUp(UINTnFlags,CPointpoint)函数里面,也有一部分打码写在了OnDraw(CDC*pDC)函数里面。
2、系统总体框架
图1总体框架
3、模块设计与分析
3.1画图模块:
该模块的大部份功能都写在了view类里面,这样造成了后期的修改造成了一定的影响,对于排错,修复bug增加了难度,对于初次使用mfc做一个相对大一点的程序,在函数分配,类的建立都有很多不足的地方。
该模块也是此次程序的重点模块之一,主要负责画一些简单图形,例如:
直线、矩形、椭圆、任意曲线(铅笔)等等;有线型、线宽的设置;还有画笔颜色的选择,可以画各种各样颜色的线条。
该模块的设计思路是:
简易画图工具——线型、线宽设置——颜色选择。
3.2贴图模块:
该模块是为了读取bmp位图设计的,主要代码写在CDib类里面,再通过view类和Doc类去调用显示位图,这个模块同样是此次程序的重点模块之一,也是花费时间最多的一个模块,开始的时候,我使用了读取程序里面的位图资源,但是这样的办法虽然能够成功显示出位图,但是却十分麻烦,就是要先把想要显示的位图放到程序的位图资源里面,然后再通过LoadBitmap函数把位图资源加载到程序再显示出来。
这样对使用都来说是非常不合理的,但是我却从这里懂得了一个显示位图的思路,要显示一幅位图需要有以下几点:
(1)读取bmp位图信息,bmp位图的信息包括:
位图文件头BITMAPFILEHEADER和位图信息头BITMAPINFOHEADER,还有就是颜色表RGBOUAD。
最后一部分是实际的位图数据IMAGEDATA。
(2)创建兼容DC,这和相对比较容易,通过CreateCompatibleDC函数创建。
(3)将位图选入兼容DC中,也就是先把位图存到内存之中,通过SelectObject函数实现。
(4)将兼容DC中的位图贴到当前DC中,也就是显示在屏幕上,通过BitBlt或者StretchBlt这两个函数都可以实现,区别在于,前者不可以自动压缩图片在一个窗口显示全部的图片内容,而后者可以。
后来我在另一本书上看到了其它的方法,也了解了DDB(设备相关图)和DIB(设备无关图),DDB位图的格式相当依赖于设备,所以它不适用于图像的交换,所以,大部分的BMP位图都会包含DIB。
为了完成位图的显示,需要完成:
装载位图、获取位图文件头信息、获取位图信息、获取颜色表信息、获取做准备数据信息、获取位图尺寸、获取颜色总数、获取颜色位数、创建位图调色板、绘制位图等等。
但是windows并没有提供DIB操作的类,需要自己动手写一个CDib的类,虽然这个类网上有可以直接复制,但是我还是按照书本那里,自己跟着课本的思路,手动把代码写上去,所以花费的时间比较多,即使是这样,有一些还是没有完全理解。
封装好CDib类之后,再在View类和Doc类中写上相应的调用函数。
便可以在程序中通过“打开”从磁盘中读取bmp位图了。
设计思路基本上和上面的步骤一样。
此时还需要面临另外一个问题,就是窗口的重绘。
此功能已在功能分析上有说明,就是利用双缓冲解决这个问题。
3.3窗口模块:
这部分大多数都是编译器自动生成,只做了一些修改,例如:
使程序一开始运行便最大话,还是自定义程序的标题。
刚开始做的时候,创建成单文档的了,后来需要用到多文档的,然后又不想再重新做过,所以只能手动修改,把单文档改成了多文档的。
新建一个多文档的程序,然后用旧的那个单文档对照着多文档的源代码,一个个类的修改。
3.4菜单模块:
该部分同样大部分都是编译器自动生成的,自己增添了一部分菜单例如:
绘图工具、颜色设置、线条设置等等。
还有右键弹出菜单,有新建窗口、颜色及线条设置、退出程序等等,让使用者可以进行快速的操作。
3.5工具栏与状态栏模块:
除了编译器自动生成的之外,还加入了画直线、矩形、椭圆、铅笔等工具的快速选择,状态栏可以显示鼠标移动的坐标。
该部分让我觉得有点难度的就是那些工具栏那些图标,我画了很久才画出那个铅笔的图标,听说可以从磁盘里加载这些图标的,但是我不知道怎么要怎么做。
4、类的设计与分析
4.1、CAboutDlg类
由编译器生成,只做了两处的修改:
m_pMainWnd->SetWindowText(_T("简单绘图工具"));//设定主框架窗口标题
pMainFrame->ShowWindow(SW_MAXIMIZE);//显示主框架窗口默认最大化
4.2、CChildFrame类
由编译器生成,只做了一处的修改:
cs.style|=WS_VISIBLE|WS_MAXIMIZE;//使子窗口一打开就是最大化
4.3、CDib类
按照书本上的代码进行封装,以下是各成员函数和数据成员
表1CDib类说明表
名称
属性
说明
lpbminfo
数据成员
指向BITMAPINFO结构的指针
lpbmihrd
数据成员
指向BITMAPINFOHEADER结构的指针
bmfHeader
数据成员
BITMAPFILEHEADER结构
lpdib
数据成员
指向DIB的指针
lpDIBBits
数据成员
DIB像素指针
dwDIBSize
数据成员
DIB大小
m_hDib
数据成员
DIB对象的句柄
*lpRgbQuag
数据成员
指向颜色表的指针
DrawDib()
成员函数
显示位图
ConstructPalette()
成员函数
构造逻辑调色板
GetBits()
成员函数
取得位图数据的入口地址
GetWidth()
成员函数
取得位图的宽度
GetHeight()
成员函数
取得位图的高度
GetPalSize()
成员函数
取得调色板的大小
GetColorNum()
成员函数
取得位图包含的颜色数目
GetBitCount()
成员函数
取得位图的颜色深度
CopyHandle()
成员函数
用于复制位图对象
LoadFile()
成员函数
从文件中加载位图
GetRectWidth()
成员函数
取得区域的宽度
GetRectHeight()
成员函数
取得区域的高度
GetReqByteWidth()
成员函数
转换后的字节数
ClearMemory()
成员函数
清除内存中的DC
InitMembers()
成员函数
初始化成员变量
4.4、CDrawingDoc类
由编译器生成,添加不少内容:
因为显示bmp位图时,有一部分操作需要在这里进行,首先在头文件中添加了以下:
public:
HGLOBALGetHObject()const{returnm_hDIB;}//获取DIB对象的句柄
CPalette*GetDocPal()const{returnm_palDIB;}//获取调色板指针
CSizeGetDocDimension()const{returnm_sizeDoc;}//获取调色板大小
voidUpdateObject(HGLOBALhDIB);//更新DIB对象
voidSetDib();//初始化DIB对象
protected:
HGLOBALm_hDIB;//定义指向DIB的句柄变量
CPalette*m_palDIB;//定义指向逻辑调色板的指针
CSizem_sizeDoc;//文档大小的变量
4.5、CDrawingView类
编译器自动生成的类,全部的画图操作都在此类中完成,包括bmp位图的显示也是在该类中通过调CDib类中的函数来完成的。
表1CDrawingView类说明表
名称
属性
说明
m_Shape
数据成员
用于选择图形的类型(直线,矩形,椭圆,铅笔)
m_MouseDown
数据成员
用于判断鼠标按下的消息响应
m_LineEnd
数据成员
画图的终点
m_LineOrg
数据成员
画图的起点
m_Mousex
数据成员
状态栏x坐标
m_Mousey
数据成员
状态栏y坐标
m_clr
数据成员
画笔颜色选择
m_nLineWidth
数据成员
指向颜色表的指针
onece
数据成员
判断窗口变化时,避免m_dcCompatible的多次创建
m_dcCompatible
数据成员
建立与屏幕显示兼容的内存显示设备
OnContextMenu()
成员函数
右键弹出菜单
OnDraw()
成员函数
屏幕显示bmp位图内容和窗口重绘
OnEditColor()
成员函数
颜色选择
OnLine()
成员函数
画直线
OnRectangle()
成员函数
画矩形
OnEllipse()
成员函数
画椭圆
OnPencil()
成员函数
铅笔工具
OnLButtonDown()
成员函数
鼠标按下响应函数
OnLButtonUp()
成员函数
鼠标弹起响应函数
OnMouseMove()
成员函数
鼠标移动响应函数
OnUpdateX()
成员函数
状态栏x坐标显示与更新
OnUpdateY()
成员函数
状态栏y坐标显示与更新
OnEraseBkgnd()
成员函数
窗口重画
OnInitialUpdate()
成员函数
初始化pView指针值
OnSetting()
成员函数
画笔设置对话框响应函数
SaveAsBmp()
成员函数
把所画的图保存为bmp文件
OnFileSaveAs()
成员函数
另存为..消息响应函数
4.6、CMainFrame类
主要同编译器生成,修改小部分:
staticUINTindicators[]=
{
ID_SEPARATOR,//statuslineindicator
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
ID_X,
ID_Y
};
ID_X,ID_Y是状态栏中的x和y坐标.
cs.style&=~FWS_ADDTOTITLE;//使主窗口的标题不发生变化
4.7、CSettingDlg类
由自己创建,主要是响应菜单中的各个设置选项,该类实现的功能比较少。
表1CSettingDlg类说明表
名称
属性
说明
m_nLineWidth
数据成员
记录线的粗细
m_nLineStyle
数据成员
记录线条的类型
OnChangeLineWidth()
成员函数
编辑框控件(IDC_LINE_WIDTH)的响应函数
OnRadio1()
成员函数
选择画实线
OnRadio2()
成员函数
选择画虚线
OnRadio3()
成员函数
选择画点线
OnPaint()
成员函数
在示例中显示所需要的线型
5、特色算法分析
在函数或成员方法里找出你认为比较有特色的1~2个算法。
5.1CDib类中的显示bmp位图
流程图:
流程图1:
设备无关位图的读取流程图2:
设备无关图的显示流程
N
N
Y
Y
代码:
功能:
在磁盘中读取一幅bmp位图,然后显示到屏幕上。
算法思路:
首先理解bmp位图里面所包含的信息然后再以下步骤实现:
<1>读取bmp位图信息,bmp位图的信息包括:
位图文件头BITMAPFILEHEADER和位图信息头BITMAPINFOHEADER,还有就是颜色表RGBOUAD。
最后一部分是实际的位图数据IMAGEDATA。
<2>创建兼容DC,这和相对比较容易,通过CreateCompatibleDC函数创建。
<3>将位图选入兼容DC中,也就是先把位图存到内存之中,通过SelectObject函数实现。
<4>将兼容DC中的位图贴到当前DC中,也就是显示在屏幕上,通过BitBlt或者StretchBlt这两个函数都可以实现,区别在于,前者不可以自动压缩图片在一个窗口显示全部的图片内容,而后者可以自动压缩和拉伸图片让它布满整个客户区。
加载位图信息代码:
HGLOBALCDib:
:
LoadFile(CFile&file)//将指定的文件中的DIB对象读到指定的内存区域中
{
DWORDdwFileSize;
dwFileSize=file.GetLength();//获取文件大小
if(file.Read((LPSTR)&bmfHeader,sizeof(bmfHeader))!
=sizeof(bmfHeader))//读取文件头
{returnNULL;}//大小不一致,返回NULL
if(bmfHeader.bfType!
=DIB_MARKER)//判断是否是DIB对象
{returnNULL;}
m_hDib=(HGLOBAL):
:
GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,dwFileSize-sizeof(BITMAPFILEHEADER));//分配DIB内存
if(m_hDib==0)
{returnNULL;}
//给CDib类的成员变量赋值
lpdib=(LPSTR):
:
GlobalLock((HGLOBAL)m_hDib);//锁定
lpbminfo=(BITMAPINFO*)lpdib;
lpbmihrd=(BITMAPINFOHEADER*)lpdib;
lpRgbQuag=(RGBQUAD*)(lpdib+lpbmihrd->biSize);
intm_numberOfColors=GetColorNum((LPSTR)lpbmihrd);
if(lpbmihrd->biClrUsed==0)
lpbmihrd->biClrUsed=m_numberOfColors;
DWORDcolorTableSize=m_numberOfColors*sizeof(RGBQUAD);
lpDIBBits=lpdib+lpbmihrd->biSize+colorTableSize;
if(file.ReadHuge(lpdib,dwFileSize-sizeof(BITMAPFILEHEADER))!
=dwFileSize-sizeof(BITMAPFILEHEADER))//读像素,大小不一致时
{:
:
GlobalUnlock((HGLOBAL)m_hDib);//解除锁定
:
:
GlobalFree((HGLOBAL)m_hDib);//释放内存
returnNULL;}
:
:
GlobalUnlock((HGLOBAL)m_hDib);//解除锁定
returnm_hDib;//返回DIB句柄
}
显示位图部分函数代码:
BOOLCDib:
:
DrawDib(HDChDC,LPRECTlpDCRect,HGLOBALhDIB,LPRECTlpDIBRect,CPalette*pPal)//绘制DIB对象
{
BOOLbSuccess=FALSE;//重画成功标志
HPALETTEhOldPal=NULL;//以前的调色板
if(hDIB==NULL)//判断是否是有效的DIB对象
{
returnFALSE;//不是,则返回
}
lpdib=(LPSTR):
:
GlobalLock(hDIB);//锁定DIB
lpDIBBits=GetBits(lpdib);//找到DIB图像像素起始位置
if(pPal!
=NULL)//获取调色板,并选取到设备环境中
{
HPALETTEhPal=(HPALETTE)pPal->m_hObject;
hOldPal=:
:
SelectPalette(hDC,hPal,TRUE);
}
:
:
SetStretchBltMode(hDC,COLORONCOLOR);//设置显示模式
bSuccess=:
:
StretchDIBits(hDC,lpDCRect->left,lpDCRect->top,GetRectWidth(lpDCRect),GetRectHeight(lpDCRect),//目标文件信息
lpDIBRect->left,lpDIBRect->top,GetRectWidth(lpDIBRect),GetRectHeight(lpDIBRect),//源文件信息
lpDIBBits,(LPBITMAPINFO)lpdib,DIB_RGB_COLORS,SRCCOPY);//指向DIB像素指针,指向位图信息结构指针,使用颜色数目,光栅操作类型
:
:
GlobalUnlock(hDIB);
if(hOldPal==NULL)
{
:
:
SelectPalette(hDC,hOldPal,TRUE);
}
returnbSuccess;
}
5.2CDrawingView类中的画图部分
功能:
在客户区中实现画直线、矩形、椭圆、任意曲线。
画完之后可以保存为bmp格式的文件。
算法思路:
在View类中创建画笔,然后通过调用各个函数去画不同的形状在画图方面,我写了4个函数,OnLine()、OnRectangle()、OnEllipse()、OnPencil()。
OnLine()函数:
SetClassLong(this->GetSafeHwnd(),GCL_HCURSOR,(LONG)LoadCursor(NULL,IDC_CROSS));
DC.LineTo(m_LineEnd);
DC.MoveTo(m_LineOrg);
m_LineEnd=point;
DC.LineTo(m_LineEnd);
OnRectangle()函数:
DC.SelectStockObject(HOLLOW_BRUSH);
DC.Rectangle(m_LineOrg.x,m_LineOrg.y,m_LineEnd.x,m_LineEnd.y);
DC.MoveTo(m_LineOrg);
m_LineEnd=point;
DC.Rectangle(m_LineOrg.x,m_LineOrg.y,m_LineEnd.x,m_LineEnd.y);
OnEllipse()函数:
DC.SelectStockObject(HOLLOW_BRUSH);
DC.Ellipse(m_LineOrg.x,m_LineOrg.y,m_LineEnd.x,m_LineEnd.y);
DC.MoveTo(m_LineOrg);
m_LineEnd=point;
DC.Ellipse(m_LineOrg.x,m_LineOrg.y,m_LineEnd.x,m_LineEnd.y);
OnPencil()函数:
DC.SetROP2(R2_COPYPEN);//保证重叠的线不会变化,使画出来的线条始终为黑色
DC.MoveTo(m_LineOrg);
DC.LineTo(point);
m_LineOrg=point;
(注:
这些代码不是写在OnLine()、OnRectangle()、OnEllipse()、OnPencil()这4个函数中,而是写在OnLButtonUp()和OnMouseMove()中)
分别把以上的四个功能的代码同时写在OnLButtonUp()和OnMouseMove()中,因为如果只写在OnLButtonUp()里面的话,画线条时在鼠标移动中线条不会立刻显示,要等鼠标左键弹起之后才显示画出来,同时在OnMouseMove()便可以解决这个问题,但是如果只在OnMouseMo
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- c+ 课程设计 简易 绘图 程序
![提示](https://static.bdocx.com/images/bang_tan.gif)