windows编程(4) - GDI绘图基础

news2024/11/19 16:43:00

基础概念

  • GDI:Graphic Device Interface 图形设备接口。
  • GUI:Graphic User Interface 图形用户接口。
  • HDC:Handle of Device Context: 图形设备上下文句柄。

字符界面的基本单位是字符。

图形界面的基本单位是像素。

像素:px,表示一个点。

绘图与贴图的消息处理: WM_PAINT 消息。

当然你也可以指定 Timer 计时器来每隔多长时间刷新一次。


绘图

基础绘图

首先要获取HDC:

HDC GetDC(
  [in] HWND hWnd
);

检索DC的窗口句柄,并且返回在指定窗口工作的DC句柄。

ReleaseDC HDC的释放:

ReleaseDC(hwnd, hdc);

SetPixel 来绘制像素点

COLORREF SetPixel(
  [in] HDC      hdc,
  [in] int      x,
  [in] int      y,
  [in] COLORREF color
);
  • HDC:设备上下文句柄
  • x,y:绘制的坐标
  • color:颜色

绘制一条线:

//绘制像素点
for (int i = 1; i <= 100; i++) {
	SetPixel(hdc, 100 + i, 100, RGB(255, 0, 0));
}

x = 100 − 200 , y = 100 x=100-200, y=100 x=100200y=100 的位置绘制了100个像素点,即绘制了一条线。


MoveToEx 来更新当前的焦点坐标:

BOOL MoveToEx(
  [in]  HDC     hdc,
  [in]  int     x,
  [in]  int     y,
  [out] LPPOINT lppt
);
  • HDC:设备上下文句柄。
  • x,y:需要移动的位置。
  • lppt:可用于保存上一个的位置

LineTo 来绘制一条线的路径:

BOOL LineTo(
  [in] HDC hdc,
  [in] int x,
  [in] int y
);

参数同样需要指定HDC与到达的坐标。

示例:来绘制一个简单的三角形:

//绘制像素点
for (int i = 1; i <= 100; i++) {
	SetPixel(hdc, 100 + i, 100, RGB(255, 0, 0));
}
POINT oldPos{};
MoveToEx(hdc, 200, 100, NULL);
LineTo(hdc, 300, 300);
//保存(300,300)的位置,并且移动到(100,100)的位置
MoveToEx(hdc, 100, 100, &oldPos);  
//画线到(300,300)的位置
LineTo(hdc, oldPos.x, oldPos.y);  
//形成一个三角形

解释:

  • 首先绘制一条线,从 ( 100 , 100 ) (100,100) (100,100) ( 200 , 100 ) (200,100) (200,100)
  • 首先移动到 ( 200 , 100 ) (200,100) (200,100)的位置,然后调用LineTo移动到(300,300)的位置,表示我们绘制了一条从 ( 200 , 100 ) (200,100) (200,100) ( 300 , 300 ) (300,300) (300,300)一条线路径。
  • 然后我们使用MoveToEx的最后一个参数来保存当前点的坐标,并且移动到 ( 100 , 100 ) (100,100) (100,100)的位置,然后再次绘制到当前位置。

最后效果如下:
在这里插入图片描述

🍎当然,我们这样做只是为了测试这几个函数的功能,一定还有其他的更加简便的方法。


使用画笔

我们首先需要创建画笔,然后需要将画笔交给画家,然后画家再画画,这是一个绘图的编程模型:

  1. 创建画笔。
  2. 传递画笔给画家。
  3. 绘制。

CreatePen 创建画笔:

HPEN CreatePen(
  [in] int      iStyle,
  [in] int      cWidth,
  [in] COLORREF color
);
  • iStyle:画笔的样式,比较常用的有PS_SOLID,PS_DASH等等。
  • cWidth:画笔的宽度,以像素点为单位。
  • color:画笔的颜色。

SelectObject 传递画笔给画家:

所谓画家其实就是HDC,即绘图设备上下文句柄。

HGDIOBJ SelectObject(
  [in] HDC     hdc,
  [in] HGDIOBJ h
);
  • hdc:绘图的上下文句柄。
  • h:是一个函数指针,表示的是对象句柄,这个对象可以是画笔,画刷,Bitmap,区域 等等。
  • 返回值为HGDIOBJ对象。

接下来就是绘图的过程了,我们只需要传递绘制指定图形的函数即可,这个例子中我绘制了一个矩形:

//1.自制画笔
HPEN pSolidPen1 =  CreatePen(PS_SOLID, 3, RGB(255, 0, 0));
HPEN pDashPen1 = CreatePen(PS_DASH, 5, RGB(0, 255, 0));

//2.画笔交给画家
HGDIOBJ hOldObj = SelectObject(hdc, pSolidPen1);
MoveToEx(hdc,100, 100, NULL);
LineTo(hdc, 200, 200);  //使用pSolidPen1来绘制

hOldObj = SelectObject(hdc, pDashPen1); //pOldObj保存pSolidPen1
LineTo(hdc, 300, 300); //使用pDashPen1来绘制

SelectObject(hdc, hOldObj); //使用pSolidPen1来绘制一个矩形
Rectangle(hdc, 100,200,150,300);

注意:我创建了两个画笔,然后分别使用这两种样式的画笔绘制了图形,我们只需要 SelectObject 选择相应的画笔即可,然后便可以直接绘制图形。

我们使用这个函数的返回值来保存当前的绘图画笔,然后再下面可以复原此次画笔。

在这里插入图片描述


使用画刷

使用画刷与画笔的过程是一样的,我们只需要:

  1. 创建画刷。
  2. 传递画刷给画家。
  3. 绘图。

CreateSolidBrush

HBRUSH CreateSolidBrush(
  [in] COLORREF color
);
  • color:画刷的颜色。
  • 返回值是一个HBRUSH,即一个画刷对象

然后 SelectObject 可以传递画刷给画家(使得HDC为此画刷类型)。

接着绘图即可:

//1.自制画刷
HBRUSH hBrush1 = CreateSolidBrush(RGB(30, 30, 30));
HBRUSH hBrush2 = CreateSolidBrush(RGB(20, 210, 20));

//2.画刷给画家
SelectObject(hdc, hBrush1);
Rectangle(hdc, 10, 10, 100, 100);

SelectObject(hdc, hBrush2);
Ellipse(hdc, 200, 200, 400, 400);

在这里插入图片描述


贴图

贴图的过程比较复杂,大致可以分为如下的四个步骤:

  1. 加载图片。
  2. 创建兼容DC。
  3. 图片选择到兼容DC中。
  4. 兼容DC绘制到HDC上。

我们需要获得HDC与HINSTANCE等信息,因此首先需要获得他们的信息:

HDC hdc =  GetDC(hwnd); //获取HDC
HINSTANCE hInstanc =  GetModuleHandle(NULL); //获取当前窗口的实例句柄

LoadImage 加载图片:

注意我们必须加载图片是 bmp 类型

HANDLE LoadImageA(
  [in, optional] HINSTANCE hInst,
  [in]           LPCSTR    name,
  [in]           UINT      type,
  [in]           int       cx,
  [in]           int       cy,
  [in]           UINT      fuLoad
);
  • hInst:贴图位置的实例句柄。
  • name:图片的路劲
  • type:要加载的图片的类型,位图,游标,或者是图标。
  • cx,cy:加载的图片的宽度与高度。
  • fuLoad:图片加载方式,可以选择从指定文件中打开: **LR_LOADFROMFILE**

CreateCompatibleDC 创建兼容DC:

HDC CreateCompatibleDC(
  [in] HDC hdc
);
  • hdc:需要为NULL,则会创建与当前窗口兼容的DC。
  • 返回值是我们创建的兼容DC

SelectObject 图片选择到兼容DC,没错,又是这个函数。

BitBlt 兼容DC绘制到HDC:

这个函数完成的是从指定源设备上下文目标设备上下文中的像素矩形对应的颜色数据的位块传输。

BOOL BitBlt(
  [in] HDC   hdc,
  [in] int   x,
  [in] int   y,
  [in] int   cx,
  [in] int   cy,
  [in] HDC   hdcSrc,
  [in] int   x1,
  [in] int   y1,
  [in] DWORD rop
);
  • hdc:绘制的目标设备上下文句柄
  • x,y,cx,cy:绘制的左上角坐标,与绘制宽度高度。
  • hdcSrc:原设备的上下文句柄。
  • x1,y1:原设备的左上角坐标
  • rop:对于颜色的操作,常见的有:SRCCOPY SRCAND SRCPAINT

简单的操作示例:

//2. 贴图
void putImg(HWND hwnd) {
	HDC hdc =  GetDC(hwnd);
	HINSTANCE hInstanc =  GetModuleHandle(NULL);

	//1. 加载图片
	HANDLE p1 =  LoadImage(hInstanc, L"img1.bmp", IMAGE_BITMAP, 207, 293, LR_LOADFROMFILE);
	HANDLE p2 =  LoadImage(hInstanc, L"img2.bmp", IMAGE_BITMAP, 299, 286, LR_LOADFROMFILE);
	HANDLE p3 =  LoadImage(hInstanc, L"img3.bmp", IMAGE_BITMAP, 265, 330, LR_LOADFROMFILE);

	//2. 创建兼容DC
	HDC hDc1 = CreateCompatibleDC(NULL);//与指定设备兼容的DC
	HDC hDc2 = CreateCompatibleDC(NULL);
	HDC hDc3 = CreateCompatibleDC(NULL);
	//3. 把图片选择到兼容DC
	SelectObject(hDc1, p1); //将对象选择到按指定的设备上下文中
	SelectObject(hDc2, p2);
	SelectObject(hDc3, p3);
	//4. 从兼容DC绘制到HDC
	BitBlt(hdc, 10, 10, 207, 293, hDc1, 0, 0, SRCCOPY);
	BitBlt(hdc, 300, 10, 299, 286, hDc2, 0, 0, SRCCOPY);
	BitBlt(hdc, 600, 10, 265, 330, hDc3, 0, 0, SRCCOPY);
}

效果如下所示:

在这里插入图片描述


多级缓冲绘图

我们使用上面的绘图方式是可以的,但是有可能会出现闪烁的情况,因此我们必须使用多级缓冲绘图,来解决这种问题,使得图片可以稳定加载。

步骤如下:

  1. 加载图片。
  2. 创建兼容位图。
  3. 创建兼容DC。
  4. 兼容位图选择到兼容DC。
  5. 图片选择到兼容DC。
  6. 兼容DC绘制到最终兼容DC。
  7. 最终兼容DC绘制到HDC。

在这里插入图片描述

CreateCompatibleBitmap :创建兼容位图:

HBITMAP CreateCompatibleBitmap(
  [in] HDC hdc,
  [in] int cx,
  [in] int cy
);
  • HDC:设备上下文句柄
  • cx,cy:宽度与高度

其他的函数与上面基本一致。

TransparentBlt 是另一种绘图方式,与 BitBlt 是一样的:

BOOL TransparentBlt(
  [in] HDC  hdcDest,
  [in] int  xoriginDest,
  [in] int  yoriginDest,
  [in] int  wDest,
  [in] int  hDest,
  [in] HDC  hdcSrc,
  [in] int  xoriginSrc,
  [in] int  yoriginSrc,
  [in] int  wSrc,
  [in] int  hSrc,
  [in] UINT crTransparent
);
  • hdcDest:目标HDC
  • 目标HDC的坐标与宽高。
  • 源HDC的信息。
  • crTransparent:与 BitBlt 的最后一个参数相同

代码示例:

//2. 贴图
void putImg(HWND hwnd) {
	HDC hdc =  GetDC(hwnd);
	HINSTANCE hInstanc =  GetModuleHandle(NULL);

	//1. 加载图片
	HANDLE bk = LoadImage(hInstanc, L"bk.bmp", IMAGE_BITMAP, 1400, 770, LR_LOADFROMFILE);
	HANDLE p1 =  LoadImage(hInstanc, L"img1.bmp", IMAGE_BITMAP, 207, 293, LR_LOADFROMFILE);
	HANDLE p2 =  LoadImage(hInstanc, L"img2.bmp", IMAGE_BITMAP, 299, 286, LR_LOADFROMFILE);
	HANDLE p3 =  LoadImage(hInstanc, L"img3.bmp", IMAGE_BITMAP, 265, 330, LR_LOADFROMFILE);

	//2. 创建兼容位图
	HBITMAP hBit1 = CreateCompatibleBitmap(hdc, 1500, 770);
	//3. 创建兼容DC
	HDC hBk = CreateCompatibleDC(NULL);
	HDC hDc1 =  CreateCompatibleDC(NULL);
	HDC hDc2 = CreateCompatibleDC(NULL);
	HDC hDc3 = CreateCompatibleDC(NULL);
	HDC hDst = CreateCompatibleDC(NULL); //最终的画布
	//4. 兼容位图设置到兼容DC中
	SelectObject(hDst, hBit1);
	//5. 图片选择到兼容DC中
	SelectObject(hBk, bk);
	SelectObject(hDc1, p1);
	SelectObject(hDc2, p2);
	SelectObject(hDc3, p3);
	//6. 每个兼容DC绘制到总的兼容DC中(总画布)
	BitBlt(hDst, 0, 0, 1500, 770, hBk, 10, 10, SRCCOPY);
	BitBlt(hDst, 0, 0, 207, 293, hDc1, 0, 0, SRCCOPY);
	BitBlt(hDst, 250,10, 299, 286, hDc2, 0, 0, SRCCOPY);
	BitBlt(hDst, 600, 10, 265, 330, hDc3, 0, 0, SRCCOPY);
	//7. 最终兼容DC绘制到HDC
	TransparentBlt(hdc, 0, 0,
		GetSystemMetrics(SM_CXFULLSCREEN),
		GetSystemMetrics(SM_CYFULLSCREEN),
		hDst, 0, 0, 1500, 770, SRCCOPY);
}

文字

CreateFont 来创建字体的样式:

参考链接:

CreateFontA 函数 (wingdi.h) - Win32 apps

HFONT CreateFontA(
  [in] int    cHeight,
  [in] int    cWidth,
  [in] int    cEscapement,
  [in] int    cOrientation,
  [in] int    cWeight,
  [in] DWORD  bItalic,
  [in] DWORD  bUnderline,
  [in] DWORD  bStrikeOut,
  [in] DWORD  iCharSet,
  [in] DWORD  iOutPrecision,
  [in] DWORD  iClipPrecision,
  [in] DWORD  iQuality,
  [in] DWORD  iPitchAndFamily,
  [in] LPCSTR pszFaceName
);

然后便可以使用 TestOut 来创建文字了:

void text(HWND hwnd) {
		HDC hdc = GetDC(hwnd);
		HFONT hFont1 = CreateFont(48, 0, 0, 0, FW_DONTCARE, FALSE, TRUE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
			CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Impact"));
		SelectObject(hdc, hFont1);
		wchar_t buff[256] = L"这是一段文字";
		TextOut(hdc, 100, 100, buff, wcslen(buff));
	
		ReleaseDC(hwnd,hdc);
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/418954.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

从数据展示中汉字缺失了解字符编码知识

有人在使用皕杰报表时遇到如下问题&#xff1a; 有些汉字变成了“&#xff1f;”&#xff0c;这是为什么呢&#xff1f;实际上就是你用的字符集里没有这个汉字导致的&#xff0c;要想搞懂这个问题&#xff0c;还得从字符、字符集、字符编码说起。 所谓字符&#xff0c;就是各…

定时任务练习----Linux 定时发送邮件 ( QQ 邮箱 为例)

邮件设置 &#xff1a; 在 QQ 邮箱的最上面 &#xff0c;点击设置。 在账户 这一栏&#xff0c;往下面走 找POP3 开头的栏目 在 POP3/SMTP 服务这一行&#xff0c;点击开启 &#xff08; 本身是 关闭状态 &#xff09; 关于 POP3 和 SMTP 服务需要做以说明 ; >>> 我…

技术管理笔记1

看点杂篇&#xff0c;整理下笔记&#xff1a; 目录&#xff1a; 1技术的本质 2 技术团队管理的本质 3 技术管理者的能力要求 4 技术管理者风格类型 5 实战案例分析&#xff1a; 一技术的本质 技术存在感低&#xff0c;缺乏话语权&#xff0c;以业务导向为主。 二 技术团…

Spring Cloud第二季--OpenFeign和Feign

文章目录一、Feign二、Feign和OpenFeign的区别三、案例测试1、eureka注册中心集群7001/70022、两个微服务3、OpenFeign一、Feign Spring Cloud Feign的介绍在Spring Cloud学习–声明式调用&#xff08;Feign&#xff09;中详细介绍。 简单回顾下&#xff0c;Feign是一个声明式…

UTF-8(Unicode Transformation Format)

文章目录一、Unicode示例代码&#xff1a;二、网络传输与Unicode三、UTF-8如何编码四、使用UTF-8转换传输Unicode五、利用Java-API进行UTF8编码和解码六、利用代码输出Unicode编码和UTF8编码七、手写UTF8编码、解码八、总结UTF8一、Unicode 示例代码&#xff1a; package demo…

【Ubuntu安装选项】

关于Ubuntu系统安装选项 [TOC](关于Ubuntu系统安装选项) 安装选项选择 一、*Try or Install Ubuntu 二、Ubunru (safe graphics) 三、OEM install &#xff08;for manufacturers&#xff09; 四、Test memory 总结 安装选项选择 在安装Ubuntu系统时会有四个选项&#xff0c;搜…

模型部署学习--有三AI(视频要收费So没学完)

视频地址&#xff1a;深度学习之模型部署 模型的整个使用流程 从模型训练到部署 一 部署平台选择&#xff1a; 1、在线服务器端部署&#xff0c;精度优先&#xff1a; 大模型/分布式&#xff08;如千亿级参数模型GPT-3&#xff09; 延迟不敏感&#xff08;如以图搜图应用&am…

chatgpt批量写作-chatgpt批量生成文章

cchatgpt写作 ChatGPT是一种基于Transformer架构的自然语言处理技术&#xff0c;它可以用于文本生成和对话场景&#xff0c;可以辅助写作、创作等任务。以下是一些使用ChatGPT进行写作的方法和技巧&#xff1a; Fine-tuning预训练模型&#xff1a;ChatGPT模型预训练时需要大量…

数字孪生卫星:概念、关键技术及应用

源自&#xff1a;软件定义世界 摘 要 在分析卫星产业发展趋势与升级转型新需求后&#xff0c;为推动卫星与新技术融合发展&#xff0c;提升大型卫星工程的整体管理水平与流程管控能力&#xff0c;促进卫星产业数字化、网络化、智能化、服务化转型升级&#xff0c;将数字孪生技…

百度天工AIoT设备应用使能平台助力企业低成本开发

数字中国建设的顶层文件《数字中国建设整体布局规划》&#xff08;以下简称《规划》&#xff09;于近日印发&#xff0c;作为数字中国建设的重要基础&#xff0c;《规划》指出&#xff0c;要全面赋能经济社会发展&#xff0c;推动数字技术和实体经济的深度融合&#xff0c;产业…

C++语法(15)---- 继承

C语法&#xff08;14&#xff09;---- 模板进阶_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130092939?spm1001.2014.3001.5501 目录 1.继承概念和定义 1.概念 2.定义 1.格式 2. 继承关系和访问限定符 2.基类和派生类对象赋值转换 3.…

ERTEC200P-2 PROFINET设备完全开发手册(5-2)

5.2 TIA 数据记录操作 在PLC的程序中&#xff0c;可以通过指令RDREC和WRREC读写数据记录&#xff0c;在参考代码里可以看到读写操作都实现了index 2的记录数据&#xff0c;并且初始化为&#xff1a; #define DEMO_RECORD "ABCDEFGH" 首先定义要写入和读出的数据…

【LeetCode】剑指 Offer(26)

目录 题目&#xff1a;剑指 Offer 51. 数组中的逆序对 - 力扣&#xff08;Leetcode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 写在最后&#xff1a; 题目&#xff1a;剑指 Offer 51. 数组中…

数据库MySQL —— 锁

目录 一、概述 二、全局锁 三、表级锁 1. 表锁 2. 元数据锁 3. 意向锁 四、行级锁 1. 行锁 2. 间隙锁 / 临键锁 一、概述 锁 是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O)的争用以外&…

channel 源码解析(5问)

目录 1.channel底层数据结构是什么 2.channel创建的底层实现 3.channel 的发送过程 4.channel的接受过程 5.关闭 channel 1.channel底层数据结构是什么 channel底层的数据结构是hchan,包括一个循环链表和2个双向链表 type hchan struct {qcount uint // tota…

Linux命令·route

Linux系统的route命令用于显示和操作IP路由表&#xff08;show / manipulate the IP routing table&#xff09;。要实现两个不同的子网之间的通信&#xff0c;需要一台连接两个网络的路由器&#xff0c;或者同时位于两个网络的网关来实现。在Linux系统中&#xff0c;设置路由通…

BGP小型实验

实验分析 1.主要考察的是对BGP配置的熟练 2.实验需要在R1与R5分别发布一条路由可以在BGP 中使用network 网段 掩码命令 3.R1与R2,R4与R5是EBGP&#xff0c;而R2,R3,R4是IBGP 实验操作 1.配置接口ip,与环回路由 以R1为例 2.AS内部需要实现非直连的建立是需要保证IBGP内部是通的所…

网络编程之IP协议

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

ChatGPT 存在很大的隐私问题

当 OpenAI 发布时 2020 年 7 月的 GPT-3&#xff0c;它提供了用于训练大型语言模型的数据的一瞥。 根据一篇技术论文&#xff0c;从网络、帖子、书籍等中收集的数百万页被用于创建生成文本系统。 在此数据中收集的是您在网上分享的一些关于您自己的个人信息,这些数据现在让 O…

YGG 代表 Web3 Gaming 参加 2023 年游戏开发者大会

Yield Guild Games&#xff08;YGG&#xff09;在 2023 年 3 月 20 日至 24 日在加州旧金山举行的游戏开发者大会&#xff08;GDC&#xff09;上大显身手&#xff0c;这是游戏开发者的重要交流学习活动。虽然 GDC 本身提供了多种多样的活动&#xff0c;包括讲座、小组讨论、圆桌…