当前位置: 首页 > news >正文

MFC开发:图形的绘制

文章目录

    • 一、获取指定窗口的设备上下文
    • 二、画笔的介绍和使用
    • 三、绘制直线
    • 四、画刷的介绍和使用
    • 五、绘制扇形
    • 六、绘制圆形
    • 七、绘制文本

一、获取指定窗口的设备上下文

1.GetDC()函数的作用
GetDC() 是 Windows API 中的一个函数,它用于获取指定窗口的设备上下文(Device Context,简称 DC)。设备上下文是 GDI(图形设备接口)用于绘图的关键对象,它封装了与设备(如屏幕或打印机)相关的绘图信息。

函数原型:

HDC GetDC(HWND hWnd);

参数:

  • hWnd:指定窗口的句柄。如果 hWnd 为 NULL,则返回整个屏幕的设备上下文(相当于桌面)。否则,返回指定窗口的设备上下文。

返回值:

  • 成功:返回指定窗口的设备上下文句柄(HDC)。
  • 否则,返回指定窗口的设备上下文。

用法:
GetDC() 主要用于获取窗口的 DC,以便进行绘图操作。一般用于:

  • 在窗口客户区绘图(不包括标题栏、菜单栏等)。
  • 获取屏幕 DC 进行全屏截图等操作。

2.获取屏幕DC进行截图
在窗口客户区绘图:

#include <windows.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps); // 获取DC
        TextOut(hdc, 50, 50, L"Hello, Windows!", 15); // 绘制文本
        EndPaint(hWnd, &ps); // 释放DC
        break;
    }
    case WM_LBUTTONDOWN: {
        HDC hdc = GetDC(hWnd); // 获取窗口DC
        TextOut(hdc, 100, 100, L"Mouse Click!", 12); // 绘制文本
        ReleaseDC(hWnd, hdc); // 释放DC
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

说明:

  • GetDC(hWnd) 直接获取窗口的 DC,不用 BeginPaint(),适用于即时绘图(如鼠标点击)。
  • 用完 DC 后,必须调用 ReleaseDC(hWnd, hdc); 释放它,否则会导致资源泄露。

获取屏幕 DC 进行截图:

HDC hScreenDC = GetDC(NULL); // 获取整个屏幕的DC
HDC hMemDC = CreateCompatibleDC(hScreenDC);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, 1920, 1080);
SelectObject(hMemDC, hBitmap);
BitBlt(hMemDC, 0, 0, 1920, 1080, hScreenDC, 0, 0, SRCCOPY);
ReleaseDC(NULL, hScreenDC);
DeleteDC(hMemDC);

说明:

  • GetDC(NULL) 获取整个屏幕的 DC。
  • BitBlt() 将屏幕内容拷贝到 hMemDC 进行截图。
  • 使用完 DC 后,需要调用 ReleaseDC() 释放它。

注意事项:

  • GetDC() 只适用于客户区绘图,如果要绘制整个窗口(包括非客户区,如标题栏),请用 GetWindowDC()。
  • 必须调用 ReleaseDC() 释放 DC,GetDC() 分配的 DC 需要 ReleaseDC() 释放,否则会导致资源泄露。
  • 与 BeginPaint() 的区别:GetDC() 适用于即时绘图,适合响应鼠标事件等。BeginPaint() 仅在 WM_PAINT 处理时使用,并会自动更新无效区域,适用于窗口重绘。

二、画笔的介绍和使用

CPen 是 MFC(Microsoft Foundation Class) 提供的一个 GDI(图形设备接口)对象,用于在 Windows 应用程序中创建和管理画笔(Pen)。画笔用于绘制直线、边框、曲线 等图形元素。

1. CPen 类的基本介绍
CPen 继承自 CGdiObject,用于定义画笔的颜色、线条样式和宽度,并可以与 CDC(设备上下文)一起使用进行绘图。

2. CPen 的构造函数

CPen(int nPenStyle, int nWidth, COLORREF crColor);

参数说明:

  • nPenStyle:线条样式,如 实线、虚线、点线 等(见下表)。
  • nWidth:线条宽度(像素)。
  • crColor:颜色,使用 RGB(r, g, b) 指定。

3. 线条样式 (nPenStyle)
在这里插入图片描述
4. CPen 的使用
(1)创建和使用画笔

void CMyView::OnDraw(CDC* pDC) {
    // 创建一个红色、2 像素宽的实线画笔
    CPen pen(PS_SOLID, 2, RGB(255, 0, 0));
    
    // 选择画笔,并保存旧画笔
    CPen* pOldPen = pDC->SelectObject(&pen);

    // 画一条直线
    pDC->MoveTo(50, 50);
    pDC->LineTo(200, 200);

    // 恢复旧画笔
    pDC->SelectObject(pOldPen);
}

说明:

  • SelectObject(&pen) 选择新画笔,并返回旧画笔指针(必须恢复)。
  • MoveTo(x, y) 移动到起点。
  • LineTo(x, y) 画一条直线。

(2)使用 CPen 变量

void CMyView::OnDraw(CDC* pDC) {
    CPen bluePen(PS_DOT, 3, RGB(0, 0, 255));  // 蓝色点线,3px 宽
    CPen* pOldPen = pDC->SelectObject(&bluePen);

    pDC->Ellipse(50, 50, 200, 200);  // 画一个椭圆

    pDC->SelectObject(pOldPen);  // 恢复原画笔
}

(3)使用 CreatePen()

void CMyView::OnDraw(CDC* pDC) {
    CPen pen;
    pen.CreatePen(PS_DASHDOT, 5, RGB(0, 255, 0)); // 绿色虚点线,5px 宽

    CPen* pOldPen = pDC->SelectObject(&pen);

    pDC->Rectangle(100, 100, 300, 300); // 画一个矩形

    pDC->SelectObject(pOldPen);
}

(4)创建 LOGBRUSH 自定义画笔

void CMyView::OnDraw(CDC* pDC) {
    LOGBRUSH lb;
    lb.lbStyle = BS_SOLID;  // 纯色填充
    lb.lbColor = RGB(255, 165, 0); // 橙色
    lb.lbHatch = 0;  

    CPen pen;
    pen.CreatePen(PS_SOLID | PS_ENDCAP_ROUND, 10, &lb); // 圆头 10px 画笔

    CPen* pOldPen = pDC->SelectObject(&pen);

    pDC->MoveTo(50, 50);
    pDC->LineTo(300, 50);

    pDC->SelectObject(pOldPen);
}

5. CPen 使用注意事项

  • 必须恢复旧画笔:SelectObject() 之前存储旧画笔,绘制完毕后恢复它,避免影响后续绘图。
  • 不要在 OnPaint() 中创建临时 CPen 对象:CPen 对象应在 OnDraw() 或 OnPaint() 内部创建并销毁,避免资源泄漏。
  • CPen 对象生存期要合理:如果是临时画笔,直接在 OnDraw() 里使用,不要 new 分配。若 CPen 需要在多个函数中使用,应定义为 成员变量,避免频繁创建销毁。

三、绘制直线

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	// 保存起点位置到成员变量中
	m_pOrigin = point;
	CWnd::OnLButtonDown(nFlags, point);
}

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值

	// 画线
	// 获得设备上下文
	CDC* pDC = GetDC();

	// 画笔移动到起始点
	pDC->MoveTo(m_pOrigin);
	// 从起点到终点画一条直线
	pDC->LineTo(point);

	// 释放设备上下文
	ReleaseDC(pDC);

	CWnd::OnLButtonUp(nFlags, point);
}

结果展示:
在这里插入图片描述

四、画刷的介绍和使用

CBrush 是 MFC(Microsoft Foundation Classes)提供的 GDI(图形设备接口) 对象之一,用于创建和管理 画刷(Brush)。画刷主要用于填充图形的 内部区域,例如填充矩形、椭圆、圆形、区域等。

1. CBrush 类的基本介绍
CBrush 继承自 CGdiObject,用于定义画刷的 颜色、填充模式和样式,并可与 CDC(设备上下文)一起使用进行绘图。

2. CBrush 的构造函数

CBrush(COLORREF crColor);
CBrush(int nIndex, COLORREF crColor);
CBrush(int nStyle, COLORREF crColor);
CBrush(int nStyle, CBitmap* pBitmap);

参数说明:

  • crColor:画刷的颜色(使用 RGB(r, g, b) 指定)。
  • nIndex:系统颜色索引(如 COLOR_WINDOW)。
  • nStyle:画刷样式(如 HS_DIAGCROSS、HS_HORIZONTAL)。
  • pBitmap:用于创建 位图画刷 的 CBitmap 指针。

3. CBrush 画刷样式
在这里插入图片描述
4. CBrush 的使用
(1)创建纯色画刷

void CMyView::OnDraw(CDC* pDC) {
    CBrush brush(RGB(255, 0, 0)); // 创建红色画刷
    CBrush* pOldBrush = pDC->SelectObject(&brush);

    pDC->Rectangle(50, 50, 200, 200); // 画填充矩形

    pDC->SelectObject(pOldBrush); // 恢复旧画刷
}

说明:

  • SelectObject(&brush) 选择画刷,并返回原来的画刷(后续需要恢复)。
  • Rectangle(x1, y1, x2, y2) 使用画刷填充矩形。

(2)创建带有样式的画刷

void CMyView::OnDraw(CDC* pDC) {
    CBrush brush(HS_CROSS, RGB(0, 255, 0)); // 绿色十字交叉填充
    CBrush* pOldBrush = pDC->SelectObject(&brush);

    pDC->Ellipse(50, 50, 200, 200); // 画填充椭圆

    pDC->SelectObject(pOldBrush);
}

常见 HS_ 样式:
在这里插入图片描述
(3)创建 CreateSolidBrush()

void CMyView::OnDraw(CDC* pDC) {
    CBrush brush;
    brush.CreateSolidBrush(RGB(0, 0, 255)); // 创建蓝色画刷

    CBrush* pOldBrush = pDC->SelectObject(&brush);
    pDC->Rectangle(100, 100, 300, 300); // 填充矩形

    pDC->SelectObject(pOldBrush);
}

说明:

  • CreateSolidBrush(RGB(0,0,255)) 以指定颜色创建画刷。
  • 适用于动态创建 画刷的情况。

(4)创建位图填充画刷

void CMyView::OnDraw(CDC* pDC) {
    CBitmap bitmap;
    bitmap.LoadBitmap(IDB_BITMAP1); // 加载资源中的位图

    CBrush brush;
    brush.CreatePatternBrush(&bitmap); // 创建位图画刷

    CBrush* pOldBrush = pDC->SelectObject(&brush);
    pDC->Rectangle(50, 50, 250, 250); // 用位图填充矩形

    pDC->SelectObject(pOldBrush);
}

说明:

  • CreatePatternBrush(&bitmap) 使用位图创建画刷,适用于 纹理填充。
  • LoadBitmap(IDB_BITMAP1) 加载 资源位图,IDB_BITMAP1 需要在 资源管理器 中定义。

(5)结合 CPen 和 CBrush 使用

void CMyView::OnDraw(CDC* pDC) {
    CPen pen(PS_SOLID, 2, RGB(0, 0, 255));  // 蓝色边框
    CBrush brush(RGB(255, 255, 0));         // 黄色填充

    CPen* pOldPen = pDC->SelectObject(&pen);
    CBrush* pOldBrush = pDC->SelectObject(&brush);

    pDC->Rectangle(50, 50, 200, 200); // 画填充的矩形

    pDC->SelectObject(pOldPen);
    pDC->SelectObject(pOldBrush);
}

说明:

  • CPen 负责边框颜色,CBrush 负责填充颜色。
  • Rectangle(x1, y1, x2, y2) 画出填充的矩形。

5. CBrush 使用注意事项

  • 必须恢复旧画刷:SelectObject() 返回旧画刷,需要在绘制完成后恢复它,避免影响后续绘图。
  • 不要在 OnPaint() 中创建临时 CBrush:CBrush 应该在 OnDraw() 或 OnPaint() 内部创建并销毁,以避免资源泄露。
  • 使用 CreateSolidBrush() 动态创建画刷:如果 CBrush 需要在多个函数中使用,推荐 使用成员变量 避免重复创建。

五、绘制扇形

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	// 保存起点位置到成员变量中
	m_pOrigin = point;
	m_bDraw = true;
	
	CWnd::OnLButtonDown(nFlags, point);
}

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_bDraw = false;
	CWnd::OnLButtonUp(nFlags, point);
}


void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	if (m_bDraw) {
		CClientDC dc(this);
		
		dc.MoveTo(m_pOrigin);
		dc.LineTo(point);

		m_pOrigin = point;
	}

	CWnd::OnMouseMove(nFlags, point);
}

在这里插入图片描述

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	// 保存起点位置到成员变量中
	m_pOrigin = point;
	m_bDraw = true;
	
	CWnd::OnLButtonDown(nFlags, point);
}

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_bDraw = false;
	CWnd::OnLButtonUp(nFlags, point);
}


void CChildView::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	if (m_bDraw) {
		CClientDC dc(this);
		CPen pen(PS_SOLID, 1, RGB(0, 255, 255));
		CPen* pOldPen = dc.SelectObject(&pen);
		
		dc.MoveTo(m_pOrigin);
		dc.LineTo(point);

		dc.SelectObject(pOldPen);
	}

	CWnd::OnMouseMove(nFlags, point);
}

在这里插入图片描述

六、绘制圆形

void CChildView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	
	// 保存起点位置到成员变量中
	m_pOrigin = point;
	m_bDraw = true;
	
	CWnd::OnLButtonDown(nFlags, point);
}

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_bDraw = false;

	CClientDC dc(this);
	
	CPen pen(PS_DASH, 1, RGB(0, 0, 255));
	CPen* pOldpen = dc.SelectObject(&pen);
	
	// 画椭圆
	//dc.Ellipse(CRect(m_pOrigin, point));
	
	// 计算两点之间x坐标的差值
	long len = point.x - m_pOrigin.x;
	dc.Ellipse(m_pOrigin.x, m_pOrigin.y, m_pOrigin.x + len, m_pOrigin.y + len);
	dc.SelectObject(pOldpen);

	CWnd::OnLButtonUp(nFlags, point);
}

在这里插入图片描述

七、绘制文本

void CChildView::OnLButtonUp(UINT nFlags, CPoint point)
{
	CString str(_T("你好我好大家好!"));

	CClientDC dc(this);
	
	CFont font;
	// 设置字体
	font.CreatePointFont(200, _T("宋体"));
	CFont* pOldFont = dc.SelectObject(&font);


	// 设置文字颜色
	dc.SetTextColor(RGB(125, 0, 127));
	// 输出字符串
	dc.TextOutW(point.x, point.y, str);
	dc.SelectObject(pOldFont);

	CWnd::OnLButtonUp(nFlags, point);
}

在这里插入图片描述

相关文章:

  • C++八大常见的设计模式的实现与实践指南
  • 【亲测有效,已顺利上线】你好,你的小程序涉及提供播放、观看等服务,请补充选择:文娱-其他视频类目。(多种没有资质的解决方案)
  • 【EDA】Altium Designer关于禁网设置的那些事
  • 数据库系统概论
  • 【大模型基础_毛玉仁】3.1 Prompt 工程简介
  • 从0到1彻底掌握Trae:手把手带你实战开发AI Chatbot,提升开发效率的必备指南!
  • 活码在实际操作中的具体场景有哪些?怎么应用?
  • 蓝桥杯嵌入式组第十四届省赛题目解析+STM32G431RBT6实现源码
  • 整合百款经典街机游戏的模拟器介绍
  • 算法刷题记录——LeetCode篇(3) [第201~300题](持续更新)
  • 谱分析方法
  • 为什么“连接断开可能导致锁未释放”
  • 3.17 模拟赛总结(虚树求交,FWT/容斥, 后缀数组SA)
  • 【八股文】从浏览器输入一个url到服务器的流程
  • 密度估计:参数与非参数
  • 获取ftp目录及子目录下的文件并下载下来
  • 让人感到疑惑的const
  • Java Fork/Join框架详解(并行计算框架、高效负载均衡、分治)
  • GigE数据接口的工业相机 稳健、性能好
  • 人形机器人领域的地位与应用前景分析
  • 昆明警方重拳打击经济领域违法犯罪:去年抓获905名嫌犯
  • 重庆市委原常委、政法委原书记陆克华被决定逮捕
  • 经济日报整版聚焦:上海构建法治化营商环境,交出高分答卷
  • 国家林草局原党组成员、副局长李春良接受审查调查
  • 俄乌拟在土耳其举行会谈,特朗普:我可能飞过去
  • 世界期待中美对话合作带来更多确定性和稳定性