C++学习 2019-1-16

本文详细介绍了如何使用C++在MFC框架下实现桌面截图功能,包括使用位图栈存储截图、显示截图,以及在截图上进行绘图操作的方法。通过创建兼容性DC、选择位图、BitBlt函数拷贝位图,实现了截图的获取与显示。同时,文章还讲解了如何在工具栏添加绘图按钮,以及在鼠标操作下进行曲线、直线、矩形、三角形和圆形的绘制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.继续截屏工具

1.1 考虑将桌面位图用栈来进行存储

1.首先我们是在主窗口中进行保存位图的,因此我们需要在 Frame 类中添加头文件 stack ,并创建一个成员用来存储位图: stack<CBitmap*> btmp_box ;对于这个栈,我们需要在 Frame 的析构函数中循环删除栈中的元素,在构造函数中获取桌面的位图并存储到这个栈中;
2.在构造函数中,我们首先创获取桌面DC: CWindowDC windc(GetDesktopWindow()) ;
3.我们创建一个兼容性DC用来拷贝桌面位图: CDC cmptdc ; cmptdc.CreateCompatibleDC(&windc) ;
4.定义一个空位图对象(用来存放桌面的位图): CBitmap* p_bitmap = new CBitmap ;对于这个位图对象,我们创建其兼容性空位图: p_bitmap->CreateCompatibleBitmap(&windc, m_screen_cx, m_screen_cy) ;其中的 m_screen_cx 与 m_screen_cy 是屏幕的宽和高,可以通过 ::GetSystemMetrics 函数获得;
5.将空位图选择进兼容性DC句柄: cmptdc.SelectObject(p_bitmap) ;
6.将位图拷贝到兼容性DC上(从主DC上往下拿图片,因此是兼容性DC调用的函数): cmptdc.BitBlt(0, 0, m_screen_cx, m_screen_cy, &windc, 0, 0, SRCCOPY) ;
7.将桌面位图压到栈中: btmp_box.push(p_bitmap) ;

CMainFrame::CMainFrame()
{
	// 1. 获取桌面图片
	// 1.1 获取桌面的长宽
	m_screen_cx = ::GetSystemMetrics(SM_CXSCREEN);
	m_screen_cy = ::GetSystemMetrics(SM_CYSCREEN);
	// 1.2 获取一张空位图,并将桌面的图放到空位图里
	CWindowDC windc(GetDesktopWindow());
	CDC cmptdc;
	cmptdc.CreateCompatibleDC(&windc);
	CBitmap *p_bitmap = new CBitmap;
	p_bitmap->CreateCompatibleBitmap(&windc, m_screen_cx, m_screen_cy);
	cmptdc.SelectObject(p_bitmap);
	cmptdc.BitBlt(0, 0, m_screen_cx, m_screen_cy, &windc, 0, 0, SRCCOPY);

	// 2. 将桌面位图拷贝到栈中
	btmp_box.push(p_bitmap);
}

1.2 将栈的栈顶位图进行显示

1.首先确定是在 View 类的 OnDraw 函数中进行显示位图的操作;
2.由于会用到 Frame 类的成员 btmp_box ,因此需要首先创建 Frame 的对象: CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd() ;
3.创建目标DC: CClientDC target_dc(this) ;
4.创建兼容性DC: CDC cdc ; cdc.CreateCompatibleDC(&target_dc) ;
5.选择位图进兼容性DC: cdc.SelectObject(pFrame->btmp_box.top()) ;
6.拷贝操作: target_dc.BitBlt(0, 0, pFrame->m_screen_cx, pFrame->m_screen_cy, &cdc, 0, 0, SRCCOPY) ;

void CShutScreenView::OnDraw(CDC* /*pDC*/)
{
	CShutScreenDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// 3. 对栈顶图片进行显示
	CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd();
	CClientDC target_dc(this);
	CDC cdc;
	cdc.CreateCompatibleDC(&target_dc);
	cdc.SelectObject(pFrame->btmp_box.top());
	target_dc.BitBlt(0, 0, pFrame->m_screen_cx, pFrame->m_screen_cy, &cdc, 0, 0, SRCCOPY);
}

1.3 给工具条添加按钮

1.首先我们需要在之前只有一个“X”的工具条上继续创建几个图标用于画直线、曲线、三角形、矩形等操作的按钮;
2.我们需要知道四类消息分别会经过哪几个类;
标准消息:先查找子类的消息映射表(若消息存在则执行,否则查找父类的消息映射表),再查找父类的消息映射表,再到更高级的父类的消息映射表
命令消息:消息的流经类的顺序 View->Doc->DocTempture(Doc模版)->Frame->App
控件消息:先查找子窗口的消息映射表,再查找父窗口的消息映射表
自定义消息:查找调用的类的消息映射表
3.其次我们在 View 类中定义一个变量用来记录我们点击的是哪一个工具条的按钮: int m_draw_type ;使用枚举类型表示 m_draw_type : enum {CURVE, LINE, RECTANGLE, TRIANGLE, CIRCLE} ;枚举的顺序由工具条按钮的顺序决定;在 View 类的构造函数里默认为画曲线(int值为0);
4.将工具条按钮的值修改为连续的(我的值从EXIT开始依次为):32771(ID_TOOLBAR_EXIT)、32772(ID_TOOLBAR_CURVE)、32773(ID_TOOLBAR_LINE)、32774(ID_TOOLBAR_RECT)、32775(ID_TOOLBAR_TRIANGLE)、32776(ID_TOOLBAR_CIRCLE) ;
5.更改为连续的之后我们就可以进行批量的操作: ON_COMMAND_RANGE(ID_TOOLBAR_CURVE, ID_TOOLBAR_CIRCLE, &CShutScreenView::OnDrawTypeChange) (第一个参数是起始的ID,第二个参数是结束的ID,第三个参数是处理函数的地址);
6.在 View 类中定义一个批量处理的函数: afx_msg void OnDrawTypeChange(UINT uID) (参数是用来记录传入的是第几个按钮的ID);在函数中进行更改绘图的变量 m_draw_type: m_draw_type = uID - ID_TOOLBAR_CURVE ;

BEGIN_MESSAGE_MAP(CShutScreenView, CView)
	... ...
	ON_COMMAND_RANGE(ID_TOOLBAR_CURVE, ID_TOOLBAR_CIRCLE, &CShutScreenView::OnDrawTypeChange)
END_MESSAGE_MAP()

enum {CURVE, LINE, RECTANGLE, TRIANGLE, CIRCLE};

CShutScreenView::CShutScreenView()
{
	// 默认为画曲线
	m_draw_type = CURVE;
}

void CShutScreenView::OnDrawTypeChange(UINT uID)
{
	// 更改画图的样式
	m_draw_type = uID - ID_TOOLBAR_CURVE;
}

1.4 当按下按钮后进行绘图

1.首先回忆之前我们进行的操作:在 LButtonDown 中标记开始绘图并记录按下鼠标的坐标,在 LButtonUp 中停止绘图,在 OnMouseMove 中进行绘图;
2.给 View 类添加上述三个命令,并添加一个用来标记是否进行绘制的 bool 型变量和一个记录鼠标按下时坐标的 CPoint 变量: bool is_draw ; CPoint pointDown ; is_draw 在 View 的构造里初始化为 false ;
3.在 LButtonDown 函数中记录开始画: pointDown = point ; is_draw = true ;
4.在 LButtonUp 函数中停止画: is_draw = false ;
5.在 OnMouseMove 函数中进行绘制,首先判断一下是否进行画图,若画图则获取DC CClientDC dc(this) ,并判断画的是什么图形(画曲线时需要一直显示,画其他的图形则需要删除之前绘制的,所以大体分为画曲线和画非曲线);
6.画曲线时: dc.MoveTo(pointDown) ; dc.LineTo(point) ; pointDown = point ;
7.画非曲线时使用 switch 语句来进行分割;

void CShutScreenView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// 记录按下的坐标并画图
	pointDown = point;
	is_draw = true;

	CView::OnLButtonDown(nFlags, point);
}

void CShutScreenView::OnLButtonUp(UINT nFlags, CPoint point)
{
	// 抬起则取消画图
	is_draw = false;
	// pointDown = point;

	CView::OnLButtonUp(nFlags, point);
}

void CShutScreenView::OnMouseMove(UINT nFlags, CPoint point)
{
	// 先判断是否需要进行画图
	if(is_draw)
	{
		// 需要画图则进行获取dc
		CClientDC dc(this);

		// 画曲线
		if(m_draw_type == CURVE)
		{
			CPen pen(PS_DASH, 3, color);			// color = RGB(255, 0, 0),写在了 View 的构造函数中
			dc.SelectObject(pen);
			dc.MoveTo(pointDown);
			dc.LineTo(point);
			pointDown = point;
			return;
		}

		// 其他画图类型
		switch (m_draw_type)
		{
		case LINE:
			{
				dc.MoveTo(pointDown);
				dc.LineTo(point);
			}
			break;
		case RECTANGLE:
			{
				dc.SelectStockObject(NULL_BRUSH);
				dc.Rectangle(pointDown.x, pointDown.y, point.x, point.y);
			}
			break;
		case  TRIANGLE:
			{
				POINT pointArr[] = {
					{(pointDown.x+point.x)/2, pointDown.y},
					{point.x, point.y},
					{pointDown.x, point.y}
				};														// 设置一个三角形的三个角的数组
				dc.Polygon(pointArr, 3);								// 绘制一个多边形
			}
			break;
		case CIRCLE:
			{
				dc.Ellipse(pointDown.x, pointDown.y, point.x, point.y);
			}
			break;
		default:
			break;
		}
	}

	CView::OnMouseMove(nFlags, point);
}

1.5 在绘制图形的过程中,我们可以选择一种画笔来绘制边框有颜色的图形, CPen pen(PS_DASH, 3, color) ;在使用DC进行选中即可: dc.SelectObject(pen) 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值