基于MFC三维图形开发
提纲:
01.MFC上机操作步骤
02.MFC绘图方法
03.设备上下文的调用与释放
04.双缓冲机制
01.MFC上机操作步骤
以后在学习中会重点操作view类: ![在这里插入图片描述](https://img-blog.csdnimg.cn/5072de0859bc4e748696a0459fedfcd8.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_16,color_FFFFFF,t_70,g_se,x_16#pic_center)
最常用到的就是里面的OnDraw()函数,以后对图形的操作就是在里面进行: ![在这里插入图片描述](https://img-blog.csdnimg.cn/718ca264986949239759c483201de24b.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
02.MFC绘图方法
在微软基类库MFC中,CDC类是定义设备上下文对象的基类,封装了绘图所需的所有函数。当输出文字或图形时,就需要调用CDC类的成员函数,这些成员函数具备绘制和打印图形的功能。 (CDC就相当于画布)
1.CDC类
其中CClientDC类常用,被称为客户区,设备上下文…
CClientDC只能在窗口的客户区(不包括边框,标题栏,菜单栏以及状态栏的空白区域)进行绘图。 点(0,0)是客户区的左上角。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/92f84fa738b843a2b6083b9679f6a1e6.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
2.简单数据类型
![在这里插入图片描述](https://img-blog.csdnimg.cn/42f92b0453b446f4b59b32d77e3e5084.png#pic_center)
CPoint类:存放点的坐标(x,y);CRect类:存放矩形左上角顶点和右下角的顶点(left,top,right,bottom);CSize类:存放矩形宽度和高度的坐标(cx,cy),其中cx是矩形的宽度,cy是矩形的高度。
3.绘图工具类
其中最常用的就是CBrush(画刷)和CPen(画笔): ![在这里插入图片描述](https://img-blog.csdnimg.cn/6c7635e8e15e4e94991f9d8dea979075.png) ![在这里插入图片描述](https://img-blog.csdnimg.cn/b44a8a44f8a641998b5f5a76b1108ed0.png)
4.映射模式
把图形显示在屏幕坐标系的过程称之为映射; 根据映射模式不同可以分为逻辑坐标和设备坐标: 默认坐标系是下面这样: 上面坐标系中的就是设备坐标。 我们实际生活中使用的坐标系: 上面坐标系中的就是逻辑坐标。 映射模式 ![在这里插入图片描述](https://img-blog.csdnimg.cn/1aed36952b324e9bbb072b52567d8e13.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center) 相关函数: ![在这里插入图片描述](https://img-blog.csdnimg.cn/db671550a8004c65a5c1ad18ddc974d7.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
示例1:
CRect rect;//声明客户区矩形
GetClientRect(&rect);//获得客户区坐标
pDC->SetMapMode(MM_ANISOTROPIC);//设置映射模式
pDC->SetWindowExt(rect.Width(), rect.Height());//设置窗口
pDC->SetViewportExt(rect.Width(), -rect.Height());//x轴水平向右,y轴垂直向上
pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);//客户区中心为坐标系原点
rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);//将rect移回到客户区内
上面的代码只是将坐标平移好了,并没有显示出下面的坐标轴。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/60ff4e452952489bb9edfd8aa7fcf977.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/63a9ce5fd73c4a27ab3f25516d54f727.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
但是上面示例1的代码只是将坐标系调整了下,并没有显示出坐标轴和原点,那我们该怎么显示出来呢?
//画x轴
pDC->MoveTo(-rect.Width()/2, 0);
pDC->LineTo(rect.Width() / 2, 0);
//画x轴箭头
pDC->MoveTo(rect.Width()/ 2, 0);
pDC->LineTo(rect.Width() / 2 - 20, 20);
pDC->MoveTo(rect.Width() / 2, 0);
pDC->LineTo(rect.Width() / 2 - 20, -20);
//画y轴
pDC->MoveTo(0, -rect.Height ()/ 2);
pDC->LineTo(0, rect.Height() / 2);
//画y轴箭头
pDC->MoveTo(0, rect.Height() / 2);
pDC->LineTo(-20, rect.Height() / 2 - 20);
pDC->MoveTo(0, rect.Height() / 2);
pDC->LineTo(20, rect.Height() / 2 - 20);
pDC->TextOutW(100, 100, CString("第一象限"));
pDC->TextOutW(-100, 100, CString("第二象限"));
pDC->TextOutW(-100, -100, CString("第三象限"));
pDC->TextOutW(100, -100, CString("第四象限"));
pDC->TextOutW(20, 30, CString("O"));
效果图: ![在这里插入图片描述](https://img-blog.csdnimg.cn/60da9f23bb8f4ed6bfb01a372ed105b4.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
5.使用GDI对象
5.1 画笔函数
![在这里插入图片描述](https://img-blog.csdnimg.cn/9e29bd3034f8466baba63edcd933811a.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/ce94fabc126444fd9b07a13db5ff6e14.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center) ![在这里插入图片描述](https://img-blog.csdnimg.cn/0286c42a58f0479c85f3aacfac7f4d65.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
5.2 画刷函数
注意:创建了画笔画刷后并不是直接起作用,我们需要用到SelectObject()函数把画笔画刷的地址传进去,同时该函数会返回原先的画笔和画刷
5.3 选入GDI对象
![在这里插入图片描述](https://img-blog.csdnimg.cn/b994dce3c45d42e1b2cd8d296fe9d46a.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
示例:
//画一个矩形框
CPen pen1;
pen1.CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
pDC->SelectObject(&pen1);
CBrush b1;
b1.CreateSolidBrush(RGB(0, 255, 0));
pDC->SelectObject(&b1);
pDC->Rectangle(10, 20, 300, 200);
图示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/1596f23064ed4c6a8b1f031b1a979128.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_17,color_FFFFFF,t_70,g_se,x_16#pic_center)
5.4 绘制像素点函数
![在这里插入图片描述](https://img-blog.csdnimg.cn/0a295bdae1cf48f6b27f30d29e0f2eb5.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
5.5 获取像素点颜色函数
示例:
COLORREF clr;
int x = 20, y = 20;
int i;
for (i = 0; i SetPixel(x + i, y, RGB(0, 255, 0));
}
for (i = 0; i GetPixel(x + i, y);
pDC->SetPixel(x + 100 + i, y, clr);
}
图示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/30667ac8ab6d4d9f8f320f106ae2876f.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_11,color_FFFFFF,t_70,g_se,x_16#pic_center)
5.6 绘制直线段函数
示例:
CPen newPen, * oldPen;
newPen.CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
oldPen = pDC->SelectObject(&newPen);
pDC->MoveTo(100, 50);
pDC->LineTo(200, 300);
pDC->TextOutW(70, 50, CString("p1"));
pDC->TextOutW(230, 300, CString("p2"));
pDC->SelectObject(oldPen);
newPen.DeleteObject();
图示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/4ffa019c2530402494f2b41aec83ce6b.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_18,color_FFFFFF,t_70,g_se,x_16#pic_center)
5.7 绘制矩形函数
示例:
CPen p2;
p2.CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
pDC->SelectObject(&p2);
CBrush b2;
b2.CreateSolidBrush(RGB(0, 0, 255));
pDC->SelectObject(&b2);
pDC->RoundRect(10, 20, 300, 200, 30, 30);
图示: 示例:
CPen p3, *oldpen;//声明新画笔对象和旧画笔指针
p3.CreatePen(PS_SOLID, 1, RGB(0, 0, 255));//创建1像素宽的蓝色实线画笔
oldpen = pDC->SelectObject(&p3);
CBrush b3, * oldbrush;//声明新画刷对象和旧画刷指针
b3.CreateSolidBrush(RGB(0, 255, 0));//创建绿色实体画刷
oldbrush = pDC->SelectObject(&b3);//将新画刷选入设备上下文
pDC->Rectangle(100, 100, 600, 300);//绘制矩形
pDC->TextOutW(70, 100, CString("p0"));
pDC->TextOutW(630, 300, CString("p1"));
pDC->SelectObject(&oldpen);//恢复旧画笔
pDC->SelectObject(&oldbrush);//恢复旧画刷
p3.DeleteObject();// 删除已成自由状态的新画刷
b3.DeleteObject();//删除已成自由状态的新画刷
图示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/c0cad78eb23046dfb456d0c531f74a30.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
5.8 绘制椭圆函数
![在这里插入图片描述](https://img-blog.csdnimg.cn/3b3fecf06a9345389953f8aab1a49b9d.png#pic_center)
绘制圆的话就把他描述成一个正方形。
示例:
CPen p2;
p2.CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
pDC->SelectObject(&p2);
CBrush b2;
b2.CreateSolidBrush(RGB(0, 0, 255));
pDC->SelectObject(&b2);
pDC->Ellipse(10, 20, 300, 200);
pDC->Ellipse(-200, 300, -100, 200);
图示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/664569fe9cf34cfcb78a72c2f65fe7cf.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
5.9 绘制多边形函数
![在这里插入图片描述](https://img-blog.csdnimg.cn/d6aabe65423645bcb810650fe8d9ec41.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
在描述多边形各顶点时,应该顺时针或逆时针来描绘各顶点,不能交叉描述。
示例:
//绘制三角形
CPoint p[3];
p[0].x = 50;
p[0].y = 50;
p[1].x = 100;
p[1].y = 100;
p[2].x = 150;
p[2].y = 50;
pDC->Polygon(p, 3);
图示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/bdce23c0a2b141bc953b7e814bc814e7.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_12,color_FFFFFF,t_70,g_se,x_16#pic_center)
作业:用多边形函数绘制一个风车
CRect rect;
GetClientRect(&rect);
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(rect.Width(), rect.Height());
pDC->SetViewportExt(rect.Width(), -rect.Height());
pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);
rect.OffsetRect(-rect.Width()/2, -rect.Height()/2);
pDC->TextOutW(30, 20, CString("O"));
CPoint p[3];
p[0] = CPoint(0, 0);
p[1] = CPoint(100,-100);
p[2] = CPoint(300, 0);
pDC->Polygon(p, 3);
CPoint a[3];
a[0] = CPoint(0, 0);
a[1] = CPoint(100, 100);
a[2] = CPoint(0, 300);
pDC->Polygon(a, 3);
CPoint b[3];
b[0] = CPoint(0, 0);
b[1] = CPoint(-100, 100);
b[2] = CPoint(-300, 0);
pDC->Polygon(b, 3);
CPoint c[3];
c[0] = CPoint(0, 0);
c[1] = CPoint(-100, -100);
c[2] = CPoint(0,-300);
pDC->Polygon(c, 3);
图示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/096f3b9ba6994d0abbd8268520d0f7da.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
5.10 填充矩形函数
![在这里插入图片描述](https://img-blog.csdnimg.cn/c63ff75e60e34b589d013c9ffed8d5b7.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
pDC->FillSolidRect(&rect,RGB(0,0,0));
5.11 路径层函数
![在这里插入图片描述](https://img-blog.csdnimg.cn/ff42fb8fb2a94db1a6c063eca7e53dbc.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_16,color_FFFFFF,t_70,g_se,x_16#pic_center)
//绘制第一个多边形,用FillPath()函数填充
CPoint p[7];//声明多边形顶点数组
p[0] = CPoint(220, 140);
p[1] = CPoint(140, 60);
p[2] = CPoint(100, 160);
p[3] = CPoint(140, 270);
p[4] = CPoint(200, 200);
p[5] = CPoint(240, 270);
p[6] = CPoint(320, 120);
pDC->TextOutW(220, 100, CString("P0"));
pDC->TextOutW(140, 50, CString("P1"));
pDC->TextOutW(60, 160, CString("P2"));
pDC->TextOutW(140, 300, CString("P3"));
pDC->TextOutW(200, 260, CString("P4"));
pDC->TextOutW(240, 300, CString("P5"));
pDC->TextOutW(320, 120, CString("P6"));
CPen p1,*p2;
p1.CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
p2=pDC->SelectObject(&p1);
CBrush b1,*b2;
b1.CreateSolidBrush(RGB(0, 255, 0));
b2=pDC->SelectObject(&b1);
pDC->BeginPath();
pDC->MoveTo(p[0]);
for (int i =1; i LineTo(p[i]);
}
pDC->LineTo(p[0]);
pDC->EndPath();
pDC->FillPath();
//绘制第二个多边形,用StrokeAndFillPath()函数填充
CPoint c[7];//声明多边形顶点数组
c[0] = CPoint(-220, 140);
c[1] = CPoint(-140, 60);
c[2] = CPoint(-100, 160);
c[3] = CPoint(-140, 270);
c[4] = CPoint(-200, 200);
c[5] = CPoint(-240, 270);
c[6] = CPoint(-320, 120);
pDC->TextOutW(-220, 100, CString("P0"));
pDC->TextOutW(-140, 50, CString("P1"));
pDC->TextOutW(-60, 160, CString("P2"));
pDC->TextOutW(-140, 300, CString("P3"));
pDC->TextOutW(-200, 260, CString("P4"));
pDC->TextOutW(-240, 300, CString("P5"));
pDC->TextOutW(-320, 120, CString("P6"));
pDC->BeginPath();
pDC->MoveTo(c[0]);
for (int i = 1; i LineTo(c[i]);
}
pDC->LineTo(c[0]);
pDC->EndPath();
pDC->StrokeAndFillPath();
//绘制第三个多边形,用画刷填充
CPoint a[7];//声明多边形顶点数组
a[0] = CPoint(-220, 140);
a[1] = CPoint(-140, 60);
a[2] = CPoint(-100, 160);
a[3] = CPoint(-140, 270);
a[4] = CPoint(-200, 200);
a[5] = CPoint(-240, 270);
a[6] = CPoint(-320, 120);
pDC->TextOutW(-220, -100, CString("P0"));
pDC->TextOutW(-140, -50, CString("P1"));
pDC->TextOutW(-60, -160, CString("P2"));
pDC->TextOutW(-140, -300, CString("P3"));
pDC->TextOutW(-200, -260, CString("P4"));
pDC->TextOutW(-240, -300, CString("P5"));
pDC->TextOutW(-320, -120, CString("P6"));
pDC->Polygon(a, 7);
pDC->SelectObject(b2);
b1.DeleteObject();
pDC->SelectObject(p2);
p1.DeleteObject();
图示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/0f61f3682ea94932830c6eff1e9c970f.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
例题: 代码:
//第一种
CPoint p[7];//声明多边形顶点数组
p[0] = CPoint(-580, 140);
p[1] = CPoint(-660, 60);
p[2] = CPoint(-700, 160);
p[3] = CPoint(-660, 270);
p[4] = CPoint(-600, 200);
p[5] = CPoint(-560, 270);
p[6] = CPoint(-480, 120);
pDC->TextOutW(-580, 100, CString("P0"));
pDC->TextOutW(-660, 50, CString("P1"));
pDC->TextOutW(-740, 160, CString("P2"));
pDC->TextOutW(-660, 300, CString("P3"));
pDC->TextOutW(-600, 260, CString("P4"));
pDC->TextOutW(-560, 300, CString("P5"));
pDC->TextOutW(-480, 120, CString("P6"));
CPen p1, * p2;
p1.CreatePen(PS_SOLID, 4, RGB(0, 0, 255));
p2 = pDC->SelectObject(&p1);
CBrush b1, * b2;
b1.CreateSolidBrush(RGB(0, 255, 0));
b2 = pDC->SelectObject(&b1);
pDC->BeginPath();
pDC->MoveTo(p[0]);
for (int i = 1; i LineTo(p[i]);
}
pDC->LineTo(p[0]);
pDC->EndPath();
pDC->FillPath();
//第二种
CPoint d[7];//声明多边形顶点数组
d[0] = CPoint(-280, 140);
d[1] = CPoint(-360, 60);
d[2] = CPoint(-400, 160);
d[3] = CPoint(-360, 270);
d[4] = CPoint(-300, 200);
d[5] = CPoint(-260, 270);
d[6] = CPoint(-180, 120);
pDC->TextOutW(-280, 100, CString("P0"));
pDC->TextOutW(-360, 50, CString("P1"));
pDC->TextOutW(-440, 160, CString("P2"));
pDC->TextOutW(-360, 300, CString("P3"));
pDC->TextOutW(-300, 260, CString("P4"));
pDC->TextOutW(-260, 300, CString("P5"));
pDC->TextOutW(-180, 120, CString("P6"));
pDC->BeginPath();
pDC->MoveTo(d[0]);
for (int i = 1; i LineTo(d[i]);
}
pDC->LineTo(d[0]);
pDC->EndPath();
pDC->StrokeAndFillPath();
//第三种
CPoint e[7];//声明多边形顶点数组
e[0] = CPoint(20, 140);
e[1] = CPoint(-60, 60);
e[2] = CPoint(-100, 160);
e[3] = CPoint(-60, 270);
e[4] = CPoint(0, 200);
e[5] = CPoint(40, 270);
e[6] = CPoint(120, 120);
pDC->TextOutW(20, 100, CString("P0"));
pDC->TextOutW(-60, 50, CString("P1"));
pDC->TextOutW(-130, 160, CString("P2"));
pDC->TextOutW(-60, 300, CString("P3"));
pDC->TextOutW(0, 260, CString("P4"));
pDC->TextOutW(40, 300, CString("P5"));
pDC->TextOutW(120, 120, CString("P6"));
pDC->Polygon(e, 7);
图示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/d176ab432a004ba6a9c98e96a3d964cb.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
5.12 绘制Bezier样条函数
例题: 代码:
CPoint p[7];
p[0] = CPoint(100, 400);
p[1] = CPoint(200, 200);
p[2] = CPoint(500, 100);
p[3] = CPoint(550, 300);
double k = (p[3].y - p[2].y) / (p[3].x - p[2].x);
double x = 600;
double y = k * (x - p[3].x) + p[3].y;
p[4] = CPoint(ROUND(x), ROUND(y));
p[5] = CPoint(900, 400);
p[6] = CPoint(800, 120);
for (int i = 0; i MoveTo(p[i]);
else
pDC->LineTo(p[i]);
//绘制控制多边形顶点的小圆点
pDC->Ellipse(p[i].x - 5, p[i].y - 5, p[i].x + 5, p[i].y + 5);
}
CPen p1, * p2;
p1.CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
p2 = pDC->SelectObject(&p1);
pDC->PolyBezier(p, 7);
p1.DeleteObject();
这里的ROUND()是四舍五入的作用,需要加上宏定义:#define ROUND(d) int(d+0.5)
图示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/f3a2317462d345cdb1fec878cb089322.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
6.显示位图
![在这里插入图片描述](https://img-blog.csdnimg.cn/330fdd8432c946599cf94806185ad53c.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/a006546e499d428b90d305ec6d596c94.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/0345f98cb0e940deb47706aa31c4cb15.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
示例: ![在这里插入图片描述](https://img-blog.csdnimg.cn/60faf579f3dd4000bb70486f96622571.png#pic_center)
CRect rect;//声明客户区
GetClientRect(&rect);//获得客户区坐标
CDC memDC;//声明一个内存设备上下文对象
memDC.CreateCompatibleDC(pDC);//创建与显示设备上下文pDC兼容的内存设备上下文memDC
CBitmap newBitmap, * oldBitmap;//定义一个CBitmap的对象和指针
newBitmap.LoadBitmap(IDB_BITMAP1);//用LoadBitmap为newBitmap对象加载资源位图
oldBitmap = memDC.SelectObject(&newBitmap);//再将位图图像选入内存设备上下文中
BITMAP bmp;//声明位图结构体对象
newBitmap.GetBitmap(&bmp);//获得位图数据
//计算位图在窗口客户区居中显示的左上角坐标
int nX = rect.left + (rect.Width() - bmp.bmWidth) / 2;
int nY = rect.top + (rect.Height() - bmp.bmHeight) / 2;
//将内存DC中的位图拷贝到设备DC
pDC->BitBlt(nX, nY, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
//如果想要位图充满整个客户区,要用到StretchBlt函数
//pDC->StretchBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY);
//恢复设备上下文
memDC.SelectObject(oldBitmap);
如果出现“LoadBitmap(IDB_BITMAP1)----未定义标志符IDB_BITMAP1”这个报错,就需要加上#include"resource.h"的头文件
nX,nY计算详解: ![在这里插入图片描述](https://img-blog.csdnimg.cn/fa08e5bec9354397bc66a172a99b1634.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
图示: ![在这里插入图片描述](https://img-blog.csdnimg.cn/14828ed3d0324d80995d9b2fd31e2ba0.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
7. 设备上下文的调用与释放
![在这里插入图片描述](https://img-blog.csdnimg.cn/54a0a259d44a42b683914671662cae19.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
8.双缓冲动画
![在这里插入图片描述](https://img-blog.csdnimg.cn/3a6284e551864c33b5a2a41362a67f82.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/8894c9dece324e8291c7609ea17bdd51.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_18,color_FFFFFF,t_70,g_se,x_16#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/82471f48ffdf469fa815f560f0a7e8bc.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_18,color_FFFFFF,t_70,g_se,x_16#pic_center)
8.1 火焰动画示例:
首先要将位图加载到资源中,并修改名称为IDM_FLAME; 然后打开Test_2View.h文件,找到操作,在其中声明双缓冲函数: 打开Test_2View.cpp,在里面定义双缓冲函数: 其中小位图的总数m_TotalBmps需要在Test_2View.h文件中进行声明,并在Test_2View.cpp文件中的构造函数里面进行初始化。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/fdd08e66589a43e49cfc0a1fa2c3d96b.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/d7c327245af84add92b9225cf7e7635b.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
若要形成动画,在BitBlt函数中还需要一个参数,让这个函数按照一定的时间间隔拷贝位图,并且还要在Test_2View.h中声明这个参数,并在Test_2View.cpp文件中构造函数中初始化为0.
![在这里插入图片描述](https://img-blog.csdnimg.cn/c14ea87615464ecab2c95bea8b1c5e47.jpg#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/a9f0e65aa4124383853f60240fcebaa4.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_18,color_FFFFFF,t_70,g_se,x_16#pic_center)
void CTest2View::DoubleBuffer(CDC* pDC)
{
CRect rect;//声明客户区矩形
GetClientRect(&rect);//获得客户区坐标
pDC->FillSolidRect(rect,RGB(0, 0, 0));//将客户区背景色填充成黑色
CDC memDC;//声明一个内存设备上下文对象
memDC.CreateCompatibleDC(pDC);//创建与显示设备上下文对象pDC兼容的内存设备上下文对象
CBitmap newBitmap, * oldBitmap;
newBitmap.LoadBitmap(IDB_FLAME);//为newBitmap对象加载位图资源
oldBitmap = memDC.SelectObject(&newBitmap);//在内存设备上下文中选入位图图像
BITMAP bmp;//声明位图结构体对象
newBitmap.GetBitmap(&bmp);//将newBitmap对象中的位图信息存放到结构体变量中
//计算小位图宽度=刚导入的位图总宽/小位图总数
//m_TotalBmps需要在view.h中进行声明
int nbmpWidth = bmp.bmWidth / m_TotalBmps;
//计算位图居中显示时左上角点的坐标
int nX = rect.left + (rect.Width() - nbmpWidth) / 2;
int nY = rect.top + (rect.Height() - bmp.bmHeight) / 2;
//用位块传送函数依次将小位图从内存设备上下文拷贝到显示设备上下文中
//若要形成动画,这需要一个参数,让这个函数按照一定的时间间隔拷贝位图
//并且还要在Test_2View.h中声明这个参数,并在构造函数中初始化为0
pDC->BitBlt(nX, nY, nbmpWidth, rect.Height(), &memDC, m_Num * bmp.bmWidth / m_TotalBmps, 0, SRCCOPY);
//最后恢复设备上下文
memDC.SelectObject(oldBitmap);
}
最后如果我们想要m_Num这个参数起作用,还要设置定时器,那我们如何在MFC中添加呢? ![在这里插入图片描述](https://img-blog.csdnimg.cn/0f0f83abcc6b459a8f61dcf422fdfdaf.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/da5ad71e88e2497cb48ea01cbb1174f5.jpg?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAWWVlMTExNw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
在ondraw函数中设置定时器: 如果没有关闭定时器,会是闪动的动画:
flame动画
如果关闭了定时器,会是下面这样静止的画面: ![在这里插入图片描述](https://img-blog.csdnimg.cn/638c26f79d0c4f51a91a216be89b4796.jpg#pic_center)
8.2 双缓冲动画(小球碰撞:红色球与客户区碰撞)
在菜单项中进行一系列操作后,还要在view.h的头文件中对一些变量和函数进行声明:
protected:
BOOL bPlay;//用来控制动画的暂停和播放
int nWidth, nHeight;//客户区的宽,高
CPoint direction;//小球的运动方向
//用圆来代替球
int r;//半径
CPoint pt;//圆的中心点
// 操作
public:
void DoubleBuffer(CDC* pDC);//双缓冲函数
void DrawObject(CDC* pDC);//绘制函数
void BorderTest();//遇到边界要有碰撞检测
在veiw.cpp文件的构造函数中进行初始化:
CTest2FootballTestView::CTest2FootballTestView() noexcept
{
// TODO: 在此处添加构造代码
bPlay = FALSE;//开关设为关
pt = CPoint(200, 200);//中心点赋值
direction = CPoint(1, 1);//运动方向:x,y方向各一步长
}
在ondraw函数中调用双缓冲:
void CTest2FootballTestView::OnDraw(CDC* pDC)
{
CTest2FootballTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: 在此处为本机数据添加绘制代码
//调用双缓冲
DoubleBuffer(pDC);
}
定义双缓冲:
//定义双缓冲
void CTest2FootballTestView::DoubleBuffer(CDC* pDC)
{
//该案例不需要自定义坐标系,直接来写双缓冲即可
CRect rect;//声明客户区矩形
GetClientRect(&rect);//获得客户区坐标
//设置客户区的宽,高
nWidth = rect.Width();
nHeight = rect.Height();
//定义内存DC
CDC memDC;
//创建一个与显示DC兼容的内存DC
memDC.CreateCompatibleDC(pDC);
CBitmap newBitmap, * oldBitmap;
//创建兼容内存位图
newBitmap.CreateCompatibleBitmap(pDC,rect.Width(),rect.Height());
//将兼容位图选入内存DC
oldBitmap = memDC.SelectObject(&newBitmap);
//绘制
DrawObject(&memDC);
//碰撞检测
BorderTest();
//显示内存位图
pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0,0, SRCCOPY);
//恢复
memDC.SelectObject(oldBitmap);
newBitmap.DeleteObject();
}
定义绘制函数:
//定义绘制函数
void CTest2FootballTestView::DrawObject(CDC* pDC)
{
//不绘制小球的边界线
CPen *oldPen = (CPen *)pDC->SelectStockObject(NULL_PEN);
CBrush newBrush(RGB(255, 0, 0));
CBrush* oldBrush = pDC->SelectObject(&newBrush);
//设置半径
r = nWidth / 10;
//绘制圆(左上角坐标:圆心坐标-r;右下角坐标:圆心坐标+r)
pDC->Ellipse(pt.x - r, pt.y - r, pt.x + r, pt.y + r);
pDC->SelectObject(oldBrush);
pDC->SelectObject(oldPen);
}
定义边界检测函数:
//定义边界检测函数
void CTest2FootballTestView::BorderTest()
{
if (pt.x + r >= nWidth)//说明与右边界进行了碰撞
direction.x = -1;//那么x方向变为朝左
if (pt.x - r = nHeight)//与下边界碰撞
direction.y = -1;
if (pt.y - r SetCheck(TRUE);
//菜单提示下一次按下会停止
pCmdUI->SetText(_T("停止"));
}
else
{
//设置菜单项未选中时图标是弹起状态
pCmdUI->SetCheck(FALSE);
//菜单提示下一次按下会播放
pCmdUI->SetText(_T("播放"));
}
}
运行结果如下:
双缓冲动画-小球碰撞
|