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

上传人:b****3 文档编号:5390586 上传时间:2023-05-08 格式:DOCX 页数:38 大小:278.93KB
下载 相关 举报
VC++ 实现画图功能 HDraw29.docx_第1页
第1页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第2页
第2页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第3页
第3页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第4页
第4页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第5页
第5页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第6页
第6页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第7页
第7页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第8页
第8页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第9页
第9页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第10页
第10页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第11页
第11页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第12页
第12页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第13页
第13页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第14页
第14页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第15页
第15页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第16页
第16页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第17页
第17页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第18页
第18页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第19页
第19页 / 共38页
VC++ 实现画图功能 HDraw29.docx_第20页
第20页 / 共38页
亲,该文档总共38页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

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

《VC++ 实现画图功能 HDraw29.docx》由会员分享,可在线阅读,更多相关《VC++ 实现画图功能 HDraw29.docx(38页珍藏版)》请在冰点文库上搜索。

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

VC++实现画图功能HDraw29

HDraw2.9程序文档

学生:

黄进东

时间:

2018-09-10

资料来源:

源码爱好者

 

目录

1.概述1

1.1.简介1

1.2.功能需求1

1.2.1.基本绘图功能:

(必须全部实现)1

1.2.2.高级编辑功能:

1

1.2.3.附加功能:

2

1.3.未实现的功能2

2.主要功能描述3

3.技术细节5

3.1.代码结构5

3.1.1.代码文件5

3.1.2.代码类5

3.2.SetROP2实现重绘6

3.3.嵌套View实现画布6

3.4.鼠标靠近目标时突出显示7

3.4.1.判断一点是否属于矩形HStrokeRect7

3.4.2.判断一点是否属于线段8

3.4.3.判断一点是否属于椭圆8

3.5.文档序列化9

3.6.打开保存导出10

3.7.友好用户界面13

3.8.右键菜单修改选中图形的属性14

3.9.撤销和恢复操作15

3.10.使用鼠标拖拽选中多个图形16

3.11.直线HStrokeLine的Tracker只显示两个Point17

3.12.键盘控制18

3.13.对话框控制20

3.14.动画程序图标20

3.15.LButtonDown流程21

3.16.LButtonUp流程:

21

3.17.MouseMove流程:

22

4.总结23

4.1.Tricks23

4.1.1.子View和父View公用一个Doc23

4.1.2.在类中获取其它类的句柄23

4.1.3.CRectTracker用法24

4.1.4.内存泄露25

4.2.收获25

4.2.1.MSDN文档25

4.2.2.Debug26

4.2.3.XX知道26

5.参考文献27

1.概述

1.1.简介

使用VC开发平台,MFC框架实现一个画图程序,尽可能多的实现Windows自带的画图功能,并扩展其功能。

1.2.功能需求

1.2.1.基本绘图功能:

(必须全部实现)

(1)能够用鼠标操控方式,绘制直线、矩形、椭圆。

(2)在绘图时,选择绘制某种图像后(如直线),在画布中按住鼠标左键后移动鼠标,在画布中实时的根据鼠标的移动显示相应的图形。

在松开鼠标左键后,一次绘图操作完成。

(3)能够在绘制一图形(如一条直线)前设置线的粗细、颜色。

(以菜单方式)

(4)可以以矢量图方式保存绘制的图形。

(5)可以读取保存的矢量图形文件,并显示绘图的结果。

界面友好的要求:

(6)有画直线、矩形、椭圆的工具箱。

(7)有颜色选择工具箱。

(8)对于当前选中的绘图工具,以“下沉”的形式显示。

(9)在状态栏中显示鼠标的位置。

(10)在鼠标移向一工具不动时,有工具的功能提示。

(11)在菜单上有当前选中的菜单项标识(即前面有小钩)

(12)可以用鼠标操作方式,通过“拖拽”方式,改变画布的大小。

(13)在画布大而外框小时,应有水平或垂直方向的滚动条。

1.2.2.高级编辑功能:

(1)具有Undo功能。

(2)可以用鼠标选中绘制的某一图形。

被选中的图形符号有标识(参见Word,如一直线段,其两端点上加了两个小框;矩形上有8个小框点)。

(3)当鼠标靠近某一目标时,鼠标的形状发生改变

(4)修改被选中的图形。

通过鼠标的“拖拽”,可以改变图形的位置、或大小。

(5)修改被选中图形的颜色、笔划的粗细。

(6)删除被选中的图形。

(7)可以使用鼠标“拖拽”一个虚矩形框,一次选择多个图形。

(8)可以使用Ctrl或Shift加鼠标左键选择多个图形对象。

1.2.3.附加功能:

(1)可选择打开或关闭工具栏。

(2)应用程序的标题栏上有程序的图标。

(3)将图形转换成位图文件的形式保存。

(4)在选择一个图形元素后(如直线),会有进一步选择线型或线宽的界面。

(5)仿Word,选择“线型”、“粗细”图标后,会出现进一步选择的选项卡。

1.3.未实现的功能

2.主要功能描述

右键修改选中图形的颜色,粗细,线型,删除选中图形

右键和鼠标调整图形大小

对话框矢量修改所有图形

3.技术细节

3.1.代码结构

3.1.1.代码文件

MFC自动生成的文件

1个CHDrawPView

1个HStroke

2个Dialog(HStrokeEditDlg+HStrokeTextDlg)

1个ToolBar(HColorBar)

3.1.2.代码类

HDrawPView文件只有一个类:

CHDrawPView,该类集成自MFC的CScrollView,主要实现维护画布类CHDrawView和滚动功能

HStroke文件里包含目前所有的图形类信息,包括集成与MFC的CObject类的基类HStroke,以及集成自HStroke的具体图形类HStrokeLine(直线),HStrokeRect(矩形),HStrokeEllipse(椭圆),HStrokeText(文本),HStrokePoly(曲线)。

HStrokeEditDlg文件只有一个类:

HStrokeEditDlg,该类集成自MFC的CDialog类,主要用来编辑已有图形类,如下图所示:

HStrokeTextDlg文件只有一个类:

HStrokeTextDlg,该类集成自MFC的CDialog类,主要用来画文本时输入文本信息,如图所示:

HColorBar类只有一个类:

HColorBar类,该类集成自MFC的CToolBar类,呈现一个颜色框,方便用户在绘图时选择不同的颜色。

3.2.SetROP2实现重绘

在画图状态下,鼠标移动时既要擦除旧图形,又要绘制新图形。

这里主要有两种实现方法:

一是全部重绘,二是先擦除旧图形。

如果使用矢量图全部重绘,频繁的绘图动作消耗很大,很容易造成屏幕闪动。

但是如果将已有图形保存为位图,然后重绘的时候只要绘制位图即可,这样能避免闪动。

第二种方法要考虑的就是擦除旧图形的问题,本程序使用SetROP2函数设置MASK的方式,每次绘图时采用非异或运算的方式擦除旧图形:

pDC->SetROP2(R2_NOTXORPEN);//设置ROP2

DrawStroke(pDC);//画图擦除旧线(自定义函数)

SetCurrentPoint(point);//设置新点的坐标(自定义函数)

DrawStroke(pDC);//画新线(自定义函数)

3.3.嵌套View实现画布

m_drawView=newCHDrawView();//创建画布View

if(!

m_drawView->CreateEx(WS_EX_LEFT|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-1,m_tracker.m_rect.bottom-1,

this->m_hWnd,NULL)){

TRACE0("Failedtocreatetoolbar\n");

return-1;//failtocreate

}

m_drawView->SetDocument((CHDrawDoc*)m_pDocument);//传递CDocument给新View

m_drawView->ShowWindow(SW_NORMAL);

m_drawView->UpdateWindow();

//设置背景View颜色为灰色

SetClassLong(m_hWnd,GCL_HBRBACKGROUND,(long)GetStockObject(GRAY_BRUSH));

3.4.鼠标靠近目标时突出显示

在鼠标移动的时候,OnMouseMove函数会遍历已有图形,判断鼠标所在点是否属于已有图形范围,如果是,则高亮显示该图形。

高亮显示的方法比较简单,只要增加CRectTracker即可,而判断当前点是否属于某图形比较有意思:

3.4.1.判断一点是否属于矩形HStrokeRect

使用用MFC的CRect类的IsPointIn方法,当鼠标在矩形边框附近时,认为该点属于HStrokeRect。

如图,实线矩形表示HStrokeRect。

外矩形为外面的虚线矩形,内矩形为里面的虚线矩形:

BOOLHStrokeRect:

:

IsPointIn(constCPoint&point){

//矩形左上角x坐标

intx1=m_points.GetAt(0).x

(1).x?

m_points.GetAt(0).x:

m_points.GetAt

(1).x;

//矩形左上角y坐标

inty1=m_points.GetAt(0).y

(1).y?

m_points.GetAt(0).y:

m_points.GetAt

(1).y;

//矩形右下角x坐标

intx2=m_points.GetAt(0).x>m_points.GetAt

(1).x?

m_points.GetAt(0).x:

m_points.GetAt

(1).x;

//矩形右下角y坐标

inty2=m_points.GetAt(0).y>m_points.GetAt

(1).y?

m_points.GetAt(0).y:

m_points.GetAt

(1).y;

//构建外矩行和内矩形

CRectrect(x1,y1,x2,y2),rect2(x1+5,y1+5,x2-5,y2-5);

//如果在外矩形内并在内矩形外

if(rect.PtInRect(point)&&!

rect2.PtInRect(point))

returnTRUE;

else

returnFALSE;

}

3.4.2.判断一点是否属于线段

首先判断一点是否属于这条线段所属的直线,根据直线的判定公式y1/x1=y2/x2得到x1*y2-x2*y1=0,但是在画图中应该在直线附近就能选中,所以在本程序中:

|x1*y2-x2*y1|<偏差,然后判断该点是否属于这条线段。

//计算该点到线段HStrokeLine的两个顶点的线段(x1,y1),(x2,y2)

intx1=point.x-m_points.GetAt(0).x;

intx2=point.x-m_points.GetAt

(1).x;

inty1=point.y-m_points.GetAt(0).y;

inty2=point.y-m_points.GetAt

(1).y;

//计算判断量x1*y2-x2*y1

intmeasure=x1*y2-x2*y1;

//误差允许范围,也就是直线的“附近”

intrule=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<0)

returnTRUE;;

}

returnFALSE;

3.4.3.判断一点是否属于椭圆

根据椭圆的定义椭圆上的点到椭圆的两个焦点的距离之和为2a,首先计算出椭圆的a,b,c,然后计算出椭圆的两个焦点。

针对某个点,首先根据点坐标和两个焦点的坐标计算出该点到椭圆焦点的距离,然后减去2a,如果在“附近”,则认为其属于HStrokeEllipse,否则不属于。

//计算椭圆的a,b,c

int_2a=abs(m_points.GetAt(0).x-m_points.GetAt

(1).x);

int_2b=abs(m_points.GetAt(0).y-m_points.GetAt

(1).y);

doublec=sqrt(abs(_2a*_2a-_2b*_2b))/2;

//计算椭圆的焦点

doublex1,y1,x2,y2;

if(_2a>_2b){//横椭圆

x1=(double)(m_points.GetAt(0).x+m_points.GetAt

(1).x)/2-c;

x2=x1+2*c;

y1=y2=(m_points.GetAt(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;

doublemeasure=sqrt((x1-point.x)*(x1-point.x)+(y1-point.y)*(y1-point.y))

+sqrt((point.x-x2)*(point.x-x2)+(point.y-y2)*(point.y-y2))

-_2a;

//计算椭圆的“附近”

doublerule=4*m_penWidth;

if(measure-rule)

returnTRUE;

else

returnFALSE;

3.5.文档序列化

MFC提供了良好的序列化机制,只要在类定义时加入DECLARE_SERIAL宏,在类构造函数的实现前加入IMPLEMENT_SERIAL宏,然后实现Serialize方法即可。

本程序即使用该方法序列化:

首先在CHDrawDoc类实现Serialize方法,保存画布大小和所有图形信息:

voidCHDrawDoc:

:

Serialize(CArchive&ar)

{

if(ar.IsStoring())

{

//保存时,首先保存画布高和宽,然后序列化所有图形

ar<

m_strokeList.Serialize(ar);

}

else

{

//打开时,首先打开画布高和宽,然后打开所有图形

ar>>m_cavasH>>m_cavasW;

m_strokeList.Serialize(ar);

}

}

m_strokeList.Serialize(ar);这一句很神奇,Debug追踪的时候会发现,容器类会自动序列化容器内的元素数量,并调用每个元素的序列化方法序列化,所以还需要对每个图形元素实现序列化,以HStrokeLine为例:

在HStrokeLine的类声明中:

classHStrokeLine:

publicHStroke

{

public:

HStrokeLine();

DECLARE_SERIAL(HStrokeLine)

然后在HStrokeLine的构造函数实现前:

IMPLEMENT_SERIAL(HStrokeLine,CObject,1)

HStrokeLine:

:

HStrokeLine()

{

m_picType=PIC_line;

}

最后实现HStrokeLine的序列化函数,因为这里HStrokeLine集成自HStroke类而且没有特殊的属性,而HStroke类实现了Serialize函数,所以HStrokeLine类不需要实现Serilize方法,看一下HStroke的Serialize方法即可:

voidHStroke:

:

Serialize(CArchive&ar)

{

if(ar.IsStoring()){

intenumIndex=m_picType;

ar<

m_points.Serialize(ar);

}

else{

intenumIndex;

ar>>enumIndex>>m_penWidth>>m_penColor;

m_picType=(enumHPicType)enumIndex;

m_points.Serialize(ar);

}

}

3.6.打开保存导出

文档序列化实现以后,程序的打开和保存功能就已经完成了。

但是从序列化方法可以看出,打开和保存的都是矢量图形,所以这里实现了一个导出为BMP图像的方法,导出:

//保存文件对话框,选择导出路径

CFileDialogdlg(FALSE,"bmp","hjz.bmp");

if(dlg.DoModal()!

=IDOK){

return;

}

CStringfilePath=dlg.GetPathName();

//

CClientDCclient(this);//用于本控件的,楼主可以不用此句

CDCcdc;

CBitmapbitmap;

RECTrect;CRectr;

GetClientRect(&rect);

intcx=rect.right-rect.left;

intcy=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(inti=0;im_strokeList.GetSize();i++){

GetDocument()->m_strokeList.GetAt(i)->DrawStroke(&cdc);

}

cdc.SelectObject(oldbitmap);

:

:

OpenClipboard(this->m_hWnd);

:

:

EmptyClipboard();

:

:

SetClipboardData(CF_BITMAP,bitmap);

:

:

CloseClipboard();

HBITMAPhBitmap=(HBITMAP)bitmap;

HDChDC;

intiBits;

WORDwBitCount;

DWORDdwPaletteSize=0,dwBmBitsSize=0,dwDIBSize=0,dwWritten=0;

BITMAPBitmap;

BITMAPFILEHEADERbmfHdr;

BITMAPINFOHEADERbi;

LPBITMAPINFOHEADERlpbi;

HANDLEfh,hDib,hPal,hOldPal=NULL;

hDC=CreateDC("DISPLAY",NULL,NULL,NULL);

iBits=GetDeviceCaps(hDC,BITSPIXEL)*GetDeviceCaps(hDC,PLANES);

DeleteDC(hDC);

if(iBits<=1)wBitCount=1;

elseif(iBits<=4)wBitCount=4;

elseif(iBits<=8)wBitCount=8;

elsewBitCount=24;

GetObject(hBitmap,sizeof(Bitmap),(LPSTR)&Bitmap);

bi.biSize=sizeof(BITMAPINFOHEADER);

bi.biWidth=Bitmap.bmWidth;

bi.biHeight=Bitmap.bmHeight;

bi.biPlanes=1;

bi.biBitCount=wBitCount;

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));

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-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2