ImageVerifierCode 换一换
格式:DOCX , 页数:38 ,大小:278.93KB ,
资源ID:11156181      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/11156181.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(VC++ 实现画图功能 HDraw29.docx)为本站会员(b****7)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

VC++ 实现画图功能 HDraw29.docx

1、VC+ 实现画图功能 HDraw29HDraw2.9程序文档学生:黄进东时间:2018-09-10资料来源:源码爱好者目录1. 概述 11.1. 简介 11.2. 功能需求 11.2.1. 基本绘图功能:(必须全部实现) 11.2.2. 高级编辑功能: 11.2.3. 附加功能: 21.3. 未实现的功能 22. 主要功能描述 33. 技术细节 53.1. 代码结构 53.1.1. 代码文件 53.1.2. 代码类 53.2. SetROP2实现重绘 63.3. 嵌套View实现画布 63.4. 鼠标靠近目标时突出显示 73.4.1. 判断一点是否属于矩形HStrokeRect 73.4.2.

2、 判断一点是否属于线段 83.4.3. 判断一点是否属于椭圆 83.5. 文档序列化 93.6. 打开保存导出 103.7. 友好用户界面 133.8. 右键菜单修改选中图形的属性 143.9. 撤销和恢复操作 153.10. 使用鼠标拖拽选中多个图形 163.11. 直线HStrokeLine的Tracker只显示两个Point 173.12. 键盘控制 183.13. 对话框控制 203.14. 动画程序图标 203.15. LButtonDown流程 213.16. LButtonUp流程: 213.17. MouseMove流程: 224. 总结 234.1. Tricks 234.1

3、.1. 子View和父View公用一个Doc 234.1.2. 在类中获取其它类的句柄 234.1.3. CRectTracker用法 244.1.4. 内存泄露 254.2. 收获 254.2.1. MSDN文档 254.2.2. Debug 264.2.3. XX知道 265. 参考文献 271. 概述1.1. 简介使用VC开发平台,MFC框架实现一个画图程序,尽可能多的实现Windows自带的画图功能,并扩展其功能。1.2. 功能需求1.2.1. 基本绘图功能:(必须全部实现)(1) 能够用鼠标操控方式,绘制直线、矩形、椭圆。(2) 在绘图时,选择绘制某种图像后(如直线),在画布中按住鼠

4、标左键后移动鼠标,在画布中实时的根据鼠标的移动显示相应的图形。在松开鼠标左键后,一次绘图操作完成。(3) 能够在绘制一图形(如一条直线)前设置线的粗细、颜色。(以菜单方式)(4) 可以以矢量图方式保存绘制的图形。(5) 可以读取保存的矢量图形文件,并显示绘图的结果。界面友好的要求:(6) 有画直线、矩形、椭圆的工具箱。(7) 有颜色选择工具箱。(8) 对于当前选中的绘图工具,以“下沉”的形式显示。(9) 在状态栏中显示鼠标的位置。(10) 在鼠标移向一工具不动时,有工具的功能提示。(11) 在菜单上有当前选中的菜单项标识(即前面有小钩)(12) 可以用鼠标操作方式,通过“拖拽”方式,改变画布的

5、大小。(13) 在画布大而外框小时,应有水平或垂直方向的滚动条。1.2.2. 高级编辑功能:(1) 具有Undo功能。(2) 可以用鼠标选中绘制的某一图形。被选中的图形符号有标识(参见Word,如一直线段,其两端点上加了两个小框;矩形上有8个小框点)。(3) 当鼠标靠近某一目标时,鼠标的形状发生改变(4) 修改被选中的图形。通过鼠标的“拖拽”,可以改变图形的位置、或大小。(5) 修改被选中图形的颜色、笔划的粗细。(6) 删除被选中的图形。(7) 可以使用鼠标“拖拽”一个虚矩形框,一次选择多个图形。(8) 可以使用 Ctrl 或Shift加鼠标左键选择多个图形对象。1.2.3. 附加功能:(1)

6、 可选择打开或关闭工具栏。(2) 应用程序的标题栏上有程序的图标。(3) 将图形转换成位图文件的形式保存。(4) 在选择一个图形元素后(如直线),会有进一步选择线型或线宽的界面。(5) 仿Word,选择“线型”、“粗细”图标后,会出现进一步选择的选项卡。1.3. 未实现的功能2. 主要功能描述右键修改选中图形的颜色,粗细,线型,删除选中图形右键和鼠标调整图形大小对话框矢量修改所有图形3. 技术细节3.1. 代码结构3.1.1. 代码文件MFC自动生成的文件1个CHDrawPView1个HStroke2个Dialog(HStrokeEditDlg+HStrokeTextDlg)1个ToolBar

7、(HColorBar)3.1.2. 代码类HDrawPView文件只有一个类:CHDrawPView,该类集成自MFC的CScrollView,主要实现维护画布类CHDrawView和滚动功能HStroke文件里包含目前所有的图形类信息,包括集成与MFC的CObject类的基类HStroke,以及集成自HStroke的具体图形类HStrokeLine(直线),HStrokeRect(矩形),HStrokeEllipse(椭圆),HStrokeText(文本),HStrokePoly(曲线)。HStrokeEditDlg文件只有一个类:HStrokeEditDlg,该类集成自MFC的CDialo

8、g类,主要用来编辑已有图形类,如下图所示:HStrokeTextDlg文件只有一个类:HStrokeTextDlg,该类集成自MFC的CDialog类,主要用来画文本时输入文本信息,如图所示:HColorBar类只有一个类:HColorBar类,该类集成自MFC的CToolBar类,呈现一个颜色框,方便用户在绘图时选择不同的颜色。3.2. SetROP2实现重绘在画图状态下,鼠标移动时既要擦除旧图形,又要绘制新图形。这里主要有两种实现方法:一是全部重绘,二是先擦除旧图形。如果使用矢量图全部重绘,频繁的绘图动作消耗很大,很容易造成屏幕闪动。但是如果将已有图形保存为位图,然后重绘的时候只要绘制位图

9、即可,这样能避免闪动。第二种方法要考虑的就是擦除旧图形的问题,本程序使用SetROP2函数设置MASK的方式,每次绘图时采用非异或运算的方式擦除旧图形: pDC-SetROP2(R2_NOTXORPEN); /设置ROP2 DrawStroke(pDC); /画图擦除旧线(自定义函数) SetCurrentPoint(point); /设置新点的坐标(自定义函数) DrawStroke(pDC); /画新线(自定义函数)3.3. 嵌套View实现画布 m_drawView = new CHDrawView();/创建画布View if (!m_drawView-CreateEx(WS_EX_L

10、EFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR, AfxRegisterWndClass(CS_VREDRAW | CS_HREDRAW,LoadCursor(NULL,IDC_CROSS), (HBRUSH)GetStockObject(WHITE_BRUSH),NULL),/白色画布 ,WS_CHILDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, m_tracker.m_rect.left,m_tracker.m_rect.top, m_tracker.m_rect.right-

11、1,m_tracker.m_rect.bottom-1, this-m_hWnd,NULL) TRACE0(Failed to create toolbarn); return -1; / fail to create m_drawView-SetDocument(CHDrawDoc*)m_pDocument);/传递CDocument给新View m_drawView-ShowWindow(SW_NORMAL); m_drawView-UpdateWindow(); /设置背景View颜色为灰色 SetClassLong(m_hWnd,GCL_HBRBACKGROUND,(long)GetS

12、tockObject(GRAY_BRUSH);3.4. 鼠标靠近目标时突出显示在鼠标移动的时候,OnMouseMove函数会遍历已有图形,判断鼠标所在点是否属于已有图形范围,如果是,则高亮显示该图形。高亮显示的方法比较简单,只要增加CRectTracker即可,而判断当前点是否属于某图形比较有意思:3.4.1. 判断一点是否属于矩形HStrokeRect使用用MFC的CRect类的IsPointIn方法,当鼠标在矩形边框附近时,认为该点属于HStrokeRect。如图,实线矩形表示HStrokeRect。外矩形为外面的虚线矩形,内矩形为里面的虚线矩形:BOOL HStrokeRect:IsPo

13、intIn(const CPoint &point) /矩形左上角x坐标 int x1 = m_points.GetAt(0).x m_points.GetAt(1).x ? m_points.GetAt(0).x : m_points.GetAt(1).x; /矩形左上角y坐标 int y1 = m_points.GetAt(0).y m_points.GetAt(1).x ? m_points.GetAt(0).x : m_points.GetAt(1).x; /矩形右下角y坐标 int y2 = m_points.GetAt(0).y m_points.GetAt(1).y ? m_po

14、ints.GetAt(0).y : m_points.GetAt(1).y; /构建外矩行和内矩形 CRect rect(x1,y1,x2,y2), rect2(x1+5,y1+5,x2-5,y2-5); /如果在外矩形内并在内矩形外 if(rect.PtInRect(point) & !rect2.PtInRect(point) return TRUE; else return FALSE;3.4.2. 判断一点是否属于线段首先判断一点是否属于这条线段所属的直线,根据直线的判定公式y1/x1 = y2/x2得到x1*y2-x2*y1=0,但是在画图中应该在直线附近就能选中,所以在本程序中:|

15、x1*y2-x2*y1| 偏差,然后判断该点是否属于这条线段。 /计算该点到线段HStrokeLine的两个顶点的线段(x1,y1), (x2,y2) int x1 = point.x - m_points.GetAt(0).x; int x2 = point.x - m_points.GetAt(1).x; int y1 = point.y - m_points.GetAt(0).y; int y2 = point.y - m_points.GetAt(1).y; /计算判断量x1*y2 - x2*y1 int measure = x1*y2 - x2*y1; /误差允许范围,也就是直线的“

16、附近” int rule = abs(m_points.GetAt(1).x - m_points.GetAt(0).x) +abs(m_points.GetAt(0).y - m_points.GetAt(1).y); rule *= m_penWidth;/将线宽考虑进去 /属于直线 if(measure -rule) /判断该点是否属于这条线段 if(x1 * x2 _2b)/横椭圆 x1 = (double)(m_points.GetAt(0).x + m_points.GetAt(1).x)/2 - c; x2 = x1 + 2*c; y1 = y2 = (m_points.GetA

17、t(0).y + m_points.GetAt(1).y)/2; else/纵椭圆 _2a = _2b; x1 = x2 = (m_points.GetAt(0).x + m_points.GetAt(1).x)/2; y1 = (m_points.GetAt(0).y + m_points.GetAt(1).y)/2 - c; y2 = y1 + 2*c; /点到两个焦点的距离之和,再减去2a /distance(point - p1) + distance(point - p2) = 2*a; double measure = sqrt(x1 - point.x)*(x1-point.x)

18、 + (y1 - point.y)*(y1-point.y) ) + sqrt( (point.x - x2)*(point.x - x2) + (point.y - y2)*(point.y - y2) - _2a; /计算椭圆的“附近” double rule = 4*m_penWidth; if(measure -rule) return TRUE; else return FALSE;3.5. 文档序列化MFC提供了良好的序列化机制,只要在类定义时加入DECLARE_SERIAL宏,在类构造函数的实现前加入IMPLEMENT_SERIAL宏,然后实现Serialize方法即可。本程序即

19、使用该方法序列化:首先在CHDrawDoc类实现Serialize方法,保存画布大小和所有图形信息:void CHDrawDoc:Serialize(CArchive& ar) if (ar.IsStoring() /保存时,首先保存画布高和宽,然后序列化所有图形 arm_cavasHm_cavasHm_cavasW; m_strokeList.Serialize(ar); m_strokeList.Serialize(ar);这一句很神奇,Debug追踪的时候会发现,容器类会自动序列化容器内的元素数量,并调用每个元素的序列化方法序列化,所以还需要对每个图形元素实现序列化,以HStrokeLi

20、ne为例:在HStrokeLine的类声明中:class HStrokeLine : public HStroke public: HStrokeLine(); DECLARE_SERIAL(HStrokeLine)然后在HStrokeLine的构造函数实现前:IMPLEMENT_SERIAL(HStrokeLine, CObject, 1)HStrokeLine:HStrokeLine() m_picType = PIC_line;最后实现HStrokeLine的序列化函数,因为这里HStrokeLine集成自HStroke类而且没有特殊的属性,而HStroke类实现了Serialize函数

21、,所以HStrokeLine类不需要实现Serilize方法,看一下HStroke的Serialize方法即可:void HStroke:Serialize(CArchive& ar) if(ar.IsStoring() int enumIndex = m_picType; arenumIndexm_penWidthenumIndexm_penWidthm_penColor; m_picType = (enum HPicType)enumIndex; m_points.Serialize(ar); 3.6. 打开保存导出文档序列化实现以后,程序的打开和保存功能就已经完成了。但是从序列化方法可以

22、看出,打开和保存的都是矢量图形,所以这里实现了一个导出为BMP图像的方法,导出: /保存文件对话框,选择导出路径 CFileDialog dlg(FALSE, bmp,hjz.bmp); if(dlg.DoModal() != IDOK) return ; CString filePath = dlg.GetPathName(); / CClientDC client(this);/用于本控件的,楼主可以不用此句 CDC cdc; CBitmap bitmap; RECT rect;CRect r; GetClientRect(&rect); int cx = rect.right - rec

23、t.left; int cy = rect.bottom - rect.top; bitmap.CreateCompatibleBitmap(&client, cx, cy); cdc.CreateCompatibleDC(NULL); /获取BMP对象 CBitmap * oldbitmap = (CBitmap* ) cdc.SelectObject(&bitmap); /白色画布 cdc.FillRect(&rect, CBrush:FromHandle(HBRUSH)GetStockObject(WHITE_BRUSH); /画图 for(int i = 0; i m_strokeLi

24、st.GetSize(); i +) GetDocument()-m_strokeList.GetAt(i)-DrawStroke(&cdc); cdc.SelectObject(oldbitmap); :OpenClipboard(this-m_hWnd); :EmptyClipboard(); :SetClipboardData(CF_BITMAP, bitmap); :CloseClipboard(); HBITMAP hBitmap = (HBITMAP)bitmap; HDC hDC; int iBits; WORD wBitCount; DWORD dwPaletteSize=0,

25、 dwBmBitsSize=0, dwDIBSize=0, dwWritten=0; BITMAP Bitmap; BITMAPFILEHEADER bmfHdr; BITMAPINFOHEADER bi; LPBITMAPINFOHEADER lpbi; HANDLE fh, hDib, hPal,hOldPal=NULL; hDC = CreateDC(DISPLAY, NULL, NULL, NULL); iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES); DeleteDC(hDC); if (iBits

26、 = 1) wBitCount = 1; else if (iBits = 4) wBitCount = 4; else if (iBits = 8) wBitCount = 8; else wBitCount = 24; GetObject(hBitmap, sizeof(Bitmap), (LPSTR)&Bitmap); bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = Bitmap.bmWidth; bi.biHeight = Bitmap.bmHeight; bi.biPlanes = 1; bi.biBitCount = wBitC

27、ount; bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrImportant = 0; bi.biClrUsed = 0; dwBmBitsSize = (Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight; hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER);

28、 lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib); *lpbi = bi; hPal = GetStockObject(DEFAULT_PALETTE); if (hPal) hDC = :GetDC(NULL); hOldPal = :SelectPalette(hDC, (HPALETTE)hPal, FALSE); RealizePalette(hDC); GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) +dwPaletteSize, (BITMAPIN

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1