MFC绘制多边形算法流程:
1.设置绘画使能标志符号
2.鼠标点击下进入OnLButtonUp(UINT nFlags, CPoint point)
3.记录一个坐标点point[i]
m_endPoint=point;
CClientDC dc(this);
hRedPen = ::CreatePen(PS_SOLID, 1, RGB(0xFF, 0x00, 0x00));
dc.SelectObject(hRedPen);
dc.SetROP2(R2_COPYPEN);
dc.SelectStockObject(NULL_BRUSH);
CWnd *pWnd=GetDlgItem(IDD_LP_CONF); // 取得控件的指针
if(m_bDrawState == drawpolygon)
{
pos_Plate[m_PointCount].x=point.x/m_fWidthProportion;
pos_Plate[m_PointCount].y=point.y/m_fHeightProportion;
if (m_PointCount==0) //由于数组下标从0开始,点一下,下表还未相加
{
m_pos_info.Format(_T("%d"),pos_Plate[m_PointCount].x);
GetParent()->GetDlgItem(IDC_POS_PLATE_X1)->SetWindowTextW(m_pos_info);
m_pos_info.Format(_T("%d"),pos_Plate[m_PointCount].y);
GetParent()->GetDlgItem(IDC_POS_PLATE_Y1)->SetWindowTextW(m_pos_info);
}
if (m_PointCount==1) //点两下
{
m_pos_info.Format(_T("%d"),pos_Plate[m_PointCount].x);
GetParent()->GetDlgItem(IDC_POS_PLATE_X2)->SetWindowTextW(m_pos_info);
m_pos_info.Format(_T("%d"),pos_Plate[m_PointCount].y);
GetParent()->GetDlgItem(IDC_POS_PLATE_Y2)->SetWindowTextW(m_pos_info);
}
if (m_PointCount==2) //点三下
{
m_pos_info.Format(_T("%d"),pos_Plate[m_PointCount].x);
GetParent()->GetDlgItem(IDC_POS_PLATE_X3)->SetWindowTextW(m_pos_info);
m_pos_info.Format(_T("%d"),pos_Plate[m_PointCount].y);
GetParent()->GetDlgItem(IDC_POS_PLATE_Y3)->SetWindowTextW(m_pos_info);
}
if (m_PointCount==3) //点四下
{
m_pos_info.Format(_T("%d"),pos_Plate[m_PointCount].x);
GetParent()->GetDlgItem(IDC_POS_PLATE_X4)->SetWindowTextW(m_pos_info);
m_pos_info.Format(_T("%d"),pos_Plate[m_PointCount].y);
GetParent()->GetDlgItem(IDC_POS_PLATE_Y4)->SetWindowTextW(m_pos_info);
}
m_PointPolygon[m_RowNum][m_PointCount].x = point.x;
m_PointPolygon[m_RowNum][m_PointCount].y = point.y;
if (m_PointCount<M_MaxPolygonPointNum) //M_MaxPolygonPointNum = 4; 最多可以有4个顶点,画第4个顶点时,左键自动完成画框
{
m_PointCount ++;
}
m_PointPolygon[m_RowNum][m_PointCount].x = point.x;
m_PointPolygon[m_RowNum][m_PointCount].y = point.y;
if(m_PointCount== M_MaxPolygonPointNum) //如果顶点数等于最大允许个数,不能继续画,直接生成框
{
m_bDrawState =polygondone; //鼠标右键,画完一个框
draw_Plate_flag=0;
GetParent()->GetDlgItem(IDC_BUTTON_DRAW_PLATE)->SetWindowTextW(_T("绘制"));
return ;
}
}</span>
4.移动过程双缓冲绘图
CDC *pDC=GetDC();
CRect rect,ellipseRect;
GetClientRect(&rect);
CDC dcMem;
CBitmap bmp;
dcMem.CreateCompatibleDC(pDC);//依附窗口DC创建兼容内存DC
bmp.CreateCompatibleBitmap(&dcMem,rect.Width(),rect.Height());//创建兼容位图
dcMem.SelectObject(&bmp);
//dcMem.FillSolidRect(rect,pDC->GetBkColor());//按原来背景填充客户区,不然会是黑色
hRedPen = ::CreatePen(PS_SOLID, 1, RGB(0xFF, 0x00, 0));
hWhitePen = ::CreatePen(PS_SOLID, 1, RGB(0xFF, 0xFF, 0xFF));
hYellowPen=::CreatePen(PS_SOLID,1,RGB(0xFF, 0xFF, 0x00));
dcMem.SetROP2(R2_COPYPEN);
dcMem.SelectStockObject(NULL_BRUSH);
dcMem.SelectObject(hYellowPen);
if(draw_down_flag==1)
{
draw_down_flag=0;
m_endPoint=point;
dcMem.Rectangle(CRect(m_startPoint,m_endPoint));
}
if(m_bDrawState == drawpolygon)
{
if (m_PointCount<=3)
{
m_PointPolygon[m_RowNum][m_PointCount].x = point.x;
m_PointPolygon[m_RowNum][m_PointCount].y = point.y;
}
if (m_PointCount==0)
{
// dc.MoveTo(point);
// dc.LineTo(point);
}
if (m_PointCount == 1) //点1下
{
dcMem.MoveTo(m_PointPolygon[m_RowNum][0]);
dcMem.LineTo(point);
dcMem.MoveTo(point);
dcMem.LineTo(m_PointPolygon[m_RowNum][0]);
}
else if (m_PointCount == 2) //点2下
{
dcMem.MoveTo(m_PointPolygon[m_RowNum][0]);
dcMem.LineTo(m_PointPolygon[m_RowNum][1]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][1]);
dcMem.LineTo(m_PointPolygon[m_RowNum][m_PointCount]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][m_PointCount]);
dcMem.LineTo(m_PointPolygon[m_RowNum][0]);
}
else if (m_PointCount == 3) //点3下
{
dcMem.MoveTo(m_PointPolygon[m_RowNum][0]);
dcMem.LineTo(m_PointPolygon[m_RowNum][1]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][1]);
dcMem.LineTo(m_PointPolygon[m_RowNum][2]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][2]);
dcMem.LineTo(m_PointPolygon[m_RowNum][3]);//实时鼠标点
dcMem.MoveTo(m_PointPolygon[m_RowNum][3]);//实时鼠标点
dcMem.LineTo(m_PointPolygon[m_RowNum][0]);
}
else if (m_PointCount == 4) //点4下
{
dcMem.MoveTo(m_PointPolygon[m_RowNum][0]);
dcMem.LineTo(m_PointPolygon[m_RowNum][1]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][1]);
dcMem.LineTo(m_PointPolygon[m_RowNum][2]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][2]);
dcMem.LineTo(m_PointPolygon[m_RowNum][3]);//已经点到的点
dcMem.MoveTo(m_PointPolygon[m_RowNum][3]);//已经点到的点
dcMem.LineTo(m_PointPolygon[m_RowNum][0]);
}
}
//pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,SRCCOPY);//将内存DC上的图象拷贝到前台
::TransparentBlt(pDC->m_hDC,0, 0,rect.Width(), rect.Width(),dcMem.m_hDC,0,0,rect.Width(),rect.Width(),RGB(0,0,0));
dcMem.DeleteDC(); //删除DC
bmp.DeleteObject(); //删除位图
pDC->DeleteDC();
5.循环2、3、4流程直到绘制顶点达需要的四个点
注意:绘画过程动态加载视频流需要重载OnPaint函数:
void CDialog_video::OnPaint()
{
//CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 __super::OnPaint();
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
}
else
{
CPaintDC pDC(this);
CRect rcWnd;
GetClientRect(&rcWnd);
int x = rcWnd.Width();
int y = rcWnd.Height();
long int width=(*Width)+1;
long int height=(*Height)+1;
m_fHeightProportion=(float)y/(float)height;
m_fWidthProportion=(float)x/(float)width;
// 视频显示区域
CRect rc = rcWnd;
m_CurRealVc = rc;
CRect rcFrame = rc;
// 创建视频兼容内存DC
CDC dcMem;
dcMem.CreateCompatibleDC(&pDC);
CBitmap btmap;
btmap.CreateCompatibleBitmap(&pDC, rcWnd.Width(), rcWnd.Height());
HBITMAP pOldBmp = (HBITMAP)dcMem.SelectObject(btmap);
dcMem.FillSolidRect(0,0,rcWnd.Width(),rcWnd.Height(),RGB(122,122,122));
DrawDibBegin(m_hDIB,dcMem,rcFrame.Width(),rcFrame.Height(),&gm_lpBmpInfo->bmiHeader,(*Width),(*Height),0);
DrawDibDraw(m_hDIB,dcMem,rcFrame.left,rcFrame.top,rcFrame.Width(),rcFrame.Height(),&gm_lpBmpInfo->bmiHeader,gBMPbuffer,0,0,(*Width),(*Height),DDF_SAME_DRAW);
DrawDibEnd(m_hDIB);
// 选入原DC
BITMAP bm;
btmap.GetBitmap(&bm);
//CClientDC dc(this);
hRedPen = ::CreatePen(PS_SOLID, 1, RGB(0xFF, 0x00, 0));
hWhitePen = ::CreatePen(PS_SOLID, 1, RGB(0xFF, 0xFF, 0xFF));
hYellowPen=::CreatePen(PS_SOLID,1,RGB(0xFF, 0xFF, 0x00));
dcMem.SetROP2(R2_COPYPEN);
dcMem.SelectStockObject(NULL_BRUSH);
dcMem.SelectObject(hWhitePen);
pos_Plate[0].x=m_fWidthProportion*GetParent()->GetDlgItemInt(IDC_POS_PLATE_X1);
pos_Plate[0].y=m_fHeightProportion*GetParent()->GetDlgItemInt(IDC_POS_PLATE_Y1);
pos_Plate[1].x=m_fWidthProportion*GetParent()->GetDlgItemInt(IDC_POS_PLATE_X2);
pos_Plate[1].y=m_fHeightProportion*GetParent()->GetDlgItemInt(IDC_POS_PLATE_Y2);
pos_Plate[2].x=m_fWidthProportion*GetParent()->GetDlgItemInt(IDC_POS_PLATE_X2);
pos_Plate[2].y=m_fHeightProportion*GetParent()->GetDlgItemInt(IDC_POS_PLATE_Y2);
pos_Plate[3].x=m_fWidthProportion*GetParent()->GetDlgItemInt(IDC_POS_PLATE_X3);
pos_Plate[3].y=m_fHeightProportion*GetParent()->GetDlgItemInt(IDC_POS_PLATE_Y3);
if (true==m_bInitFlag)
{
iniData();
m_bInitFlag=false;
}
m_startPoint1.x=cur_pos_1_x;
m_startPoint1.y=cur_pos_1_y;
m_endPoint1.x=cur_pos_1_x+cur_pos_1_w;
m_endPoint1.y=cur_pos_1_y+cur_pos_1_h;
m_startPoint2.x=cur_pos_2_x;
m_startPoint2.y=cur_pos_2_y;
m_endPoint2.x=cur_pos_2_x+cur_pos_2_w;
m_endPoint2.y=cur_pos_2_y+cur_pos_2_h;
m_startPoint3.x=cur_pos_3_x;
m_startPoint3.y=cur_pos_3_y;
m_endPoint3.x=cur_pos_3_x+cur_pos_3_w;
m_endPoint3.y=cur_pos_3_y+cur_pos_3_h;
if(getEnPosNum==1)
{
if(redraw1_flag==1)
{
dcMem.SelectObject(hWhitePen);
dcMem.Rectangle(CRect(m_startPoint1,m_endPoint1));
}
else
{
dcMem.SelectObject(hRedPen);
dcMem.Rectangle(CRect(m_startPoint1,m_endPoint1));
}
}
if(getEnPosNum==2)
{
if(redraw1_flag==1)
{
dcMem.SelectObject(hWhitePen);
dcMem.Rectangle(CRect(m_startPoint1,m_endPoint1));
}
else
{
dcMem.SelectObject(hRedPen);
dcMem.Rectangle(CRect(m_startPoint1,m_endPoint1));
}
if(redraw2_flag==1)
{
dcMem.SelectObject(hWhitePen);
dcMem.Rectangle(CRect(m_startPoint2,m_endPoint2));
}
else
{
dcMem.SelectObject(hRedPen);
dcMem.Rectangle(CRect(m_startPoint2,m_endPoint2));
}
}
if(getEnPosNum==3)
{
if(redraw1_flag==1)
{
dcMem.SelectObject(hWhitePen);
dcMem.Rectangle(CRect(m_startPoint1,m_endPoint1));
}
else
{
dcMem.SelectObject(hRedPen);
dcMem.Rectangle(CRect(m_startPoint1,m_endPoint1));
}
if(redraw2_flag==1)
{
dcMem.SelectObject(hWhitePen);
dcMem.Rectangle(CRect(m_startPoint2,m_endPoint2));
}
else
{
dcMem.SelectObject(hRedPen);
dcMem.Rectangle(CRect(m_startPoint2,m_endPoint2));
}
if(redraw3_flag==1)
{
dcMem.SelectObject(hWhitePen);
dcMem.Rectangle(CRect(m_startPoint3,m_endPoint3));
}
else
{
dcMem.SelectObject(hRedPen);
dcMem.Rectangle(CRect(m_startPoint3,m_endPoint3));
}
}
CPoint point;
GetCursorPos(&point);
if(m_bEnDrawPlate)
{
dcMem.SelectObject(hYellowPen);
if(m_bDrawState==drawpolygon)
{
if (m_PointCount==0)
{
// dcMem.MoveTo(m_PointPolygon[m_RowNum][0]);
// dcMem.LineTo(m_PointPolygon[m_RowNum][0]);
}
if (m_PointCount==1)
{
dcMem.MoveTo(m_PointPolygon[m_RowNum][0]);
dcMem.LineTo(m_PointPolygon[m_RowNum][1]);
}
if (m_PointCount==2)
{
dcMem.MoveTo(m_PointPolygon[m_RowNum][0]);
dcMem.LineTo(m_PointPolygon[m_RowNum][2]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][0]);
dcMem.LineTo(m_PointPolygon[m_RowNum][1]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][1]);
dcMem.LineTo(m_PointPolygon[m_RowNum][2]);
}
}
if (m_PointCount>=3)
{
dcMem.MoveTo(m_PointPolygon[m_RowNum][2]);
dcMem.LineTo(m_PointPolygon[m_RowNum][3]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][0]);
dcMem.LineTo(m_PointPolygon[m_RowNum][3]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][0]);
dcMem.LineTo(m_PointPolygon[m_RowNum][1]);
dcMem.MoveTo(m_PointPolygon[m_RowNum][1]);
dcMem.LineTo(m_PointPolygon[m_RowNum][2]);
}
}
BitBlt (pDC,rcWnd.left,rcWnd.top,bm.bmWidth,bm.bmHeight,dcMem,0,0,SRCCOPY); //将内存DC图贴回原DC
dcMem.SelectObject(pOldBmp);
btmap.DeleteObject();
dcMem.DeleteDC();
}
CDialog::OnPaint();
}
附:MFC的设备环境DC
- DC : 设备环境(Device Context)
1. 设备环境, 是 Window 内部的一种数据结构,用来保存与某个设备相关的绘制属性信息。
2. 所有的绘制调用都必须通过设备环境 dc 进行。这些对象封装了用于绘制线条、图形以及文本的 Window API 。
3. 设备环境允许 Window 在设备中进行与设备无关的绘制。
4. 设备环境可用于绘制到屏幕、打印机可图元文件。
- HDC : 设备环境句柄
一个指针类型对像,指向 dc 对象 在 Window 内部的位置。
- CDC、CClientDC、CPaintDC、CWindowDC的派生关系
------------------------------------------------------------------------------------------------------------------
1、CDC 类
Window 使用与设备无关的设备环境dc来进行显示。MFC 基础类库定义了设备环境对象类 -- CDC
其构造函数如下:
CDC::CDC()
{
m_hDC = NULL;
m_hAttribDC = NULL;
m_bPrinting = FALSE;
}
其析构函数如下:
CDC::~CDC()
{
if (m_hDC != NULL)
::DeleteDC(Detach());
}
需要注意的是:在生成CDC对象的时候,并不像它的派生类那样,在构造函数里获取相应的Windows设备上下文句柄。
所以,最好不要使用::GetDC等函数来获取一个设备描述表,而是使用BOOL CreateCompatibleDC(CDC*pDC )来创建一个设备描述表。
在 CDC析构函数中,如果设备描述表句柄不空,则调用DeleteDC删除它。这是直接使用CDC时最好创建Windows设备描述表的理由。
如果设备描述表不是创建的,则应该在析构函数被调用前分离出设备描述表句柄并用::RealeaseDC释放它,释放后m_hDC为空,则在析构函数调用时不会执行::DeleteDC。
当然,不用担心CDC的派生类的析构函数调用CDC的析构函数,因为CDC::~CDC()不是虚拟析构函数。
使用CDC有两种做法:
// 1.用 CWnd::GetDC()来初始化CDC对象
CDC *pDC = GetDC();
pDC->MoveTo(m_ptOrigin);
pDC->LineTo(point);
ReleaseDC(pDC);
// 2.CreateCompatibleDC(CDC* pDC ) 来创建 CDC 对象
// 用于为当前 dc 在内存创建一个兼容DC。这样要可消除闪烁
CDC dcMem;
dcMem.CreateCompatibleDC(&dc); //创建设备描述表
pbmOld = dcMem.SelectObject(&m_bmBall); //更改设备描述表属性
…//作一些绘制操作
dcMem.SelectObject(pbmOld); //恢复设备描述表的属性
dcMem.DeleteDC(); //可以不调用,而让析构函数去删除设备描述表
2、CClientDC 类
CClientDC 类,在构造函数中调用 Window API 函数 GetDC(),在析构函数中调用ReleaseDC()。
CClientDC 类只能在客户区绘图。面所谓的客户区是指窗口区域去掉边框、标题栏、菜单栏、工具栏、状态栏等之外的部分。
它是用户可操作的区域。
CClientDC类的窗口句柄保存在成员变量m_hWnd,为构造CClientDC,需将CWnd作为参数传递给构造函数。
在使用CClientDC进行绘图时,一般要调用GetClientRect()函数来获取客户区域的大小;
//CClientDC : public CDC 特点:构造函数时候GetDC() 析构函数时候调用ReleaseDC
// CClientDC dc(GetParent()); GetParent view的父窗口 也就是frame 但不能出客户区域
CClientDC dc(this); //指针构造DC
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
3、CPaintDC类
CPaintDC 用于响应窗口重绘消息(WM_PAINT)是的绘图输出。
CPaintDC在构造函数中调用BeginPaint()取得设备上下文,在析构函数中调用 EndPaint()释放设备上下文。
EndPaint()除了释放设备上下文外,还负责从消息队列中清除WM_PAINT消息。
因此,在处理窗口重画时,必须使用CPaintDC,否则WM_PAINT消息无法从消息队列中清除,将引起不断的窗口重画。
CPaintDC也只能用在WM_PAINT消息处理之中。
// MFC中 CView 对 WM_PAINT 消息的实现方法如下:
void CView::OnPaint()
{
// standard paint routine
CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}
// 在栈中定义了CPaintDC类型的变量dc,随着构造函数的调用获取了设备描述表;
// 设备描述表使用完毕,超出其有效范围就被自动地清除,随着析构函数的调用,其获取的设备描述表被释放。
// 如果希望在堆中创建,例如
CPaintDC *pDC;
pDC = new CPaintDC(this)
// 则在使用完毕时,用delete删除pDC:
delete pDC;
4、CWindowDC 类
CWindowDC对象在构造时调用Windows API函数GetWindowDC,在析构时调用相应的API函数ReleaseDC。
这意味着CWindowDC对象可访问CWnd所指向的为整个全屏幕区域;
CWindowDC允许在显示器的任意位置绘图。坐标原点在整个窗口的左上角。
在使用CWindowDC进行绘图时,一般要调用GetWindowRect函数来获取整个应用程序窗口区域的大小;
CWindowDC类的窗口句柄保存在成员变量m_hWnd,为构造CClientDC,需将CWnd作为参数传递给构造函数。
// CWindowDC 也是派生于CDC 构造、析构也是类似ClientDC 。可以访问整个程序区域 包括客户区与非客户区
// CWindowDC dc(GetParent()); 绘制父窗体 文档结构中 可以绘制到框架类 乃至非客户区上
// CWindowDC dc(GetDesktopWindow()); 可以绘制到整个非客户区 桌面上
CWindowDC dc(this); //绘制当前窗口
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
=================================================
附: Windows 程序设计 对 图形基础的描述:
1. 取得设备上下文句柄
Windows 提供了几种取得设备上下文句柄的方法。如果在处理一个消息时取得了设备上下文句柄,应该在退出窗口函数之前释放它(或者删除它)。
一旦释放了句柄,它就不再有效了。对于打印机设备上下文句柄,规则就没有这么严格。
最常用的用于取得并释放设备上下文句柄的方法是:在处理WM_PAINT 消息时,使用 BeginPaint 和 EndPaint 调用:
hdc = BeginPaint(hwnd, &ps);
// 其他操作
EndPaint(hwnd, &ps);
其中,ps 为PAINTSTRUCT 结构对象,该结构中的 hdc 字段是 BeginPaint 传回的设备上下文句柄,PAINTSTRUCT 结构又包含一个名为rcPaint 的RECT(矩形)结构,
rcPaint 定义一个包围窗口显示区域无效的矩形。使用从BeginPaint获得的设备上下文句柄,只能在这个区域内绘图。BeginPaint 调用使该区域有效。
Windows 程序还可以在处理非WM_PAINT消息时取得设备上下文句柄
hdc = GetDC(hwnd);
// 其他操作
ReleaseDC(hwnd, hdc);
这个设备上下文适用于窗口句柄为hwnd 的显示区域。
这些调用与BeginPaint 和 EndPaint 的组合之间的区别是,利用从GetDC传回的句柄可以在整个显示区域上绘图。
当然,GetDC 和 ReleaseDC 不使显示区域中任何可能的无效区域变成有效。
Windows 程序还可以取得适用于整个窗口的设备上下文句柄:
hdc = GetWindowDC(hwnd);
// 其他操作
ReleaseDC(hwnd, hdc);
这个设备上下文除了显示区域之外,还包括窗口的标题栏、菜单、滚动条、和框架。
GetWindowDC 函数很少使用,如果想尝试用一用它,则必须拦截处理WM_NCPAINT 消息,
Windows 使用该消息在窗口的非显示区域上绘图。
BeginPaint、GetDC和GetWindowDC获得的设备上下文句柄都和显示器上的某个特定窗口相关。
取得设备上下文句柄的另一个更通用的函数是CreateDC:
hdc = CreateDC(pszDreiver, pszDevice, pszOutput, pData);
// 其他操作
DeleteDC(hdc);
.......