Windows编程:图标资源、光标资源、字符串资源、加速键资源、WM_PAINT消息、绘图

news2024/11/22 9:24:00


承接前文:

  1. win32窗口编程
  2. windows 开发基础
  3. win32-注册窗口类、创建窗口
  4. win32-显示窗口、消息循环、消息队列
  5. win32-鼠标消息、键盘消息、计时器消息、菜单资源


本文目录

  • 图标资源
  • 光标资源
    • WM_SETCURSOR 消息
  • 字符串资源
  • 加速键资源
  • WM_PAINT 消息
  • 绘图
  • 绘图编程
    • 绘图基础
    • 基本图形绘制
    • GDI 绘图对象
      • 画笔
      • 画刷
      • 位图
      • 文本绘制
        • 字体
  • 请求一个图片的 url 并在窗口中显示




图标资源

.ico为后缀的图片,icon 图标资源。一个图标文件中可以有多个不同分辨率大小的图标,以适应不同场景下的使用。

想要使用图标资源,第一步添加图标资源,第二步加载图标资源,第三步设置窗口类。

HICON LoadIcon(
	HINSTANCE hInstance,   //handle to application instance
	LPCTSTR lpIconName     //name string or resource identifier
);    //成功返回HICON句柄

添加图标资源

在这里插入图片描述

添加图标资源后可以自己绘制图标:从图中可以看到有不同大小的图标(256x256像素、48x48像素等),有支持彩色的图标,也有黑白图标。

在这里插入图片描述

代码中添加资源头文件,并将图标赋值给窗口类的成员变量。

#include "resource.h"    //把资源头文件包含进来
... ...
wc.hIcon = LoadIcon(hIns, (char*)IDI_ICON1);

编译运行改变窗口图标:
在这里插入图片描述

在Visual Studio中编辑图标并不怎么方便,在这里可以将提前制作好的图标放在项目目录中,修改.rc 文件中对于图标资源的描述,修改为自己提前制作好的图标,然后在Visual Studio中重新加载被修改的 .rc 文件,再次编译运行。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述




光标资源

光标资源就是鼠标光标,这个光标在不同情况下是不同的:有的时候是箭头光标,有的时候是一个小手,有的时候是一闪一闪的竖杠,等等;在游戏中光标的形状也各不相同。一个光标就是一个图片,也是需要自己提前绘制,然后用代码进行应用。

光标资源使用步骤:

  • 第一步添加光标资源,光标的默认大小是32x32像素,每个光标有一个HotSpot热点,也就是点击时生效的点(因为一个光标图片有很多像素,但是生效的一个点只有一个点,这个点的位置可以自己设置,例如箭头光标的热点就在箭头的尖上)。
  • 第二步加载资源:
    HCURSOR LoadCursor(
    	HINSTANCE hInstance,     //handle to application instance
    	LPCTSTR lpCursorName     //name or resource identifier
    ); // hInstance 可以为 NULL, 获取系统默认的Cursor
    
  • 第三步设置光标:
    • 注册窗口类时设置。
    • 或 使用 SetCursor() 设置光标。

添加光标资源:

在这里插入图片描述

绘制光标资源:

在这里插入图片描述

使用图中的 Set Hot Spot Tool 来设置光标热点。

设置光标:

#include "resource.h"
... ...
//wc.hCursor = LoadCursor(NULL, IDC_ARROW);    //默认光标
wc.hCursor = LoadCursor(hIns, (char*)IDC_CURSOR1);

无论是 LoadCursor() 函数还是 LoadIcon() 函数,他们的作用都是通过窗口实例句柄和资源的ID来在内存中找到这个资源。窗口实例句柄 hInstance 的作用就是在内存中找到一块保存窗口各种信息的内存,这些资源被编译后会编译到 .exe 里面,当程序运行的时候,程序就会被加载到内存,其中就包括了各种资源。

自己创建的光标只在客户区起作用,在标题栏就会自动变成默认光标。

使用 SetCursor() 函数可以在程序运行的过程中修改光标

HCURSOR SetCursor(
	HCURSOR hCursor // handle to cursor
);

SetCursor()函数必须放在 WM_SETCURSOR 消息下进行调用。

WM_SETCURSOR 消息

只有光标有移动的情况,这个消息就会不断地出现。专职用法:改光标。

附带参数:

  • wParam :当前使用的光标句柄。
  • lParam :
    • LOWORD :当前区域的代码(Hit-Test code)HTCLIENT (客户区)/ HTCAPTION(标题栏、边框)。
    • HIWORD :没太大用处。

再绘制一个光标 IDC_CURSOR2,在程序运行时进行切换:

//添加全局变量 hInstance
HINSTANCE g_hInstance = 0;

//WinMain()函数中给hInstance赋值
g_hInstance = hIns;

//消息处理函数中添加代码:
case WM_SETCURSOR:
{
	HCURSOR hCursor = LoadCursor(g_hInstance, (char*)IDC_CURSOR2);
	SetCursor(hCursor);
}
break;
//运行后,鼠标移动时,光标闪烁变化,这是因为该消息产生后立马修改了光标,但是return DefWindowProc()又将光标修改回去
//SetCursor()后面加上 return 0;即可解决光标随着鼠标的移动不断闪烁修改的问题。
//修改如下
//在客户区光标时 IDC_CURSOR2,在标题栏 IDC_CURSOR1,在其他地方(顶层菜单栏、图标等位置处)为默认光标
case WM_SETCURSOR:
{
	HCURSOR hCursor1 = LoadCursor(g_hInstance, (char*)IDC_CURSOR1);
	HCURSOR hCursor2 = LoadCursor(g_hInstance, (char*)IDC_CURSOR2);
	if (LOWORD(lParam) == HTCLIENT) {
		SetCursor(hCursor2);
		return 0;
	}
	else if (LOWORD(lParam) == HTCAPTION) {
		SetCursor(hCursor1);
		return 0;
	}
}
break;



字符串资源

字符串资源存在的目的就是方便代码中各种字符串的修改,例如语言的切换(中英文切换等)。

具体来说,字符串资源的作用包括:

中心化管理文本:将应用程序中使用的所有文本信息集中管理,方便统一修改和维护。

多语言支持:通过为不同语言单独创建字符串资源文件,可以实现应用程序的多语言支持,使应用能够在不同语言环境下运行并显示相应的文本。

提高可维护性:由于文本信息集中管理,修改或更新某个文本只需在字符串资源中进行修改,而不需要在代码中逐个替换,提高了代码的可维护性。

便于国际化和本地化:通过使用字符串资源,可以轻松地将应用程序本地化为不同的语言和区域设置,满足不同用户群体的需求。

节省内存空间:采用字符串资源可以在编译时对字符串进行优化,并且可以节省内存空间。

  • 添加字符串资源:添加字符串表,在表中增加字符串。
  • 加载字符串资源:
    int LoadString(
    	HINSTANCE hInstance,     // handle to resource module
    	UINT uID,                //字符串ID
    	LPTSTR lpBuffer,         //存放字符串BUFF
    	int nBufferMax           //字符串BUFF长度
    ); //成功返回字符串长度,失败返回0
    

添加字符串资源:

在这里插入图片描述

添加字符串资源:

在这里插入图片描述

使用字符串资源:

char wTitle[20] = { 0 };
LoadString(hIns, STR_Chinese_WinTitle, wTitle, 20);

HWND hWnd = CreateWindowEx(
	0, pClassName, wTitle,
	WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_OVERLAPPEDWINDOW,
	200, 200, 640, 480,        //窗口位置(200,200),大小长宽(640,480)。
	nullptr, nullptr, hIns, nullptr
);

在程序中尽量多使用字符串资源(string table),这样可以方便文本管理,如果需要大面积的修改文本,也会方便很多。




加速键资源

加速键,就当快捷键理解。例如记事本的快捷键:

在这里插入图片描述

添加资源:资源添加加速键表,增加命令ID对应的加速键。

使用:

//加载加速键表
HACCEL LoadAccelerators(
	HINSTANCE hInstance,    //handle to module
	LPCTSTR lpTableName     // accelerator table name
);     //返回加速键表句柄

//翻译加速键
int TranslateAccelerator(
	HWND hWnd,   //处理消息的窗口句柄
	HACCEL hAccTable,    //加速键表句柄
	LPMSG lpMsg          //消息
);      //如果是加速键,返回非零。该函数内部首先判断是不是加速键。

添加加速键资源: Accelerator(加速)

在这里插入图片描述

编辑加速键:当加速键的ID和菜单中某一项ID相同时,就实现了加速键和某一个菜单项绑定。加速键和菜单项没有什么对应关系,当它们的ID相同时,就可以说是绑定;当然加速键的ID也可以单独设置,不一定非要和菜单项的ID相同。关于菜单资源参见上一篇文章菜单资源(点击跳转)。

在这里插入图片描述

在这里 ID_40001ID_40002ID_40003 分别对应着菜单项的ID 新建打开退出ID_other 不对应任何菜单项。

在这里插入图片描述

当菜单项被点击时,或者加速键被按下时,都可以产生 WM_COMMAND 消息。

WM_COMMAND 消息中:

  • wParam:
    • HIWORD 为 1 表示消息产生自加速键,为 0 表示消息产生自菜单项。
    • LOWORD 为命令 ID。
  • lParam :0.

TranslateAccelerator()函数的内部执行过程:

//伪代码
TranslateAccelerator(hWnd, hAccel, &nMsg){
	if(nMsg.message != WM_KEYDOWN)
		return 0;    //不是按键被按下,那就绝对不是加速键消息,直接返回0
	
	根据nMsg.wParam(键码值),获知哪些按键被按下(假如Ctrl + N)
	拿着(Ctrl + N)到 hAccel 加速键表中匹配具体加速键
	if(没找到)
		return 0;
	if(找到){
		SendMessage(hWnd, WM_COMMAND, HIWORD(1)|||LOWORD(ID_40001), ... );   //产生COMMAND消息
		return 1;
	}
}

加速键的使用

	//加速键要放在消息循环中使用。
	//使用前首先加载加速键表
	HACCEL hAccel = LoadAccelerators(hIns, (char*)IDR_ACCELERATOR1);

	//TranslateAccelerator()函数应该放在TranslateMessage()函数之前调用,因为TranslateMessage()会区分大小写。


	消息循环加上翻译加速键函数的调用后,完整代码如下:

	while (1)     //先进入死循环,循环体中进行判断是否退出循环
	{
		if (PeekMessage(&nMsg, NULL, 0, 0, PM_NOREMOVE))  //PeekMessage侦察兵侦察是否有消息,如果有消息
		{
			if ((gResult = GetMessage(&nMsg, NULL, 0, 0)) > 0)  //gResult值大于零意味着GetMessage没有抓到 WM_QUIT(返回值为0) 也没有出错(出错返回值为-1)
			{
				if (!TranslateAccelerator(hWnd, hAccel, &nMsg))     //返回值为0,不是加速键,则翻译、派发消息。是加速键消息,则产生一个 WM_COMMAND 消息,然后进入下次消息循环
				{
					TranslateMessage(&nMsg);
					DispatchMessage(&nMsg);
				}
			}
			else      //如果抓到 WM_QUIT 或者 出错 退出while循环
			{
				break;
			}
		}
		else    //如果PeekMessage()没有侦察到消息,空闲处理
		{
			//WriteConsole(g_dos_output, "No Message", strlen("No Message"), NULL, NULL);
		}
	}

	if (gResult == -1)   //GetMessage()出错返回值 -1
	{
		//错误处理或直接退出程序		
		return -1;
	}
	else   //抓到 WM_QUIT 消息,退出程序,返回 PostQuitMessage()的参数值
	{
		return nMsg.wParam;    //此时 wParam 是 PostQuitMessage()的参数值
	}

根据 WM_COMMAND 消息的 HIWORD(wParam) 可以区分出消息来自 加速键 被按下 还是 菜单项 被点击:

//消息处理函数中对于 WM_COMMAND 消息的处理:
case WM_COMMAND:
	OnCommand(hWnd, wParam);
break;

//OnCommand()函数的具体内容如下:
void OnCommand(HWND hWnd, WPARAM wParam) {
	switch (LOWORD(wParam)) {
	case ID_40001: 
	{
		if (!HIWORD(wParam)) {
			MessageBox(hWnd, "菜单项新建被点击", "Information", MB_OK);
		}
		else {
			MessageBox(hWnd, "新建快捷键Ctrl+N被按下", "Information", MB_OK);
		}
	}
	break;
	case ID_40002:
	{
		if (!HIWORD(wParam)) {
			MessageBox(hWnd, "菜单项打开被点击", "Information", MB_OK);
		}
		else {
			MessageBox(hWnd, "打开快捷键Ctrl+O被按下", "Information", MB_OK);
		}
	}
	break;
	case ID_40003:
	{
		if (!HIWORD(wParam)) {
			MessageBox(hWnd, "菜单项退出被点击", "Information", MB_OK);
		}
		else {
			MessageBox(hWnd, "退出快捷键Ctrl+Q被按下", "Information", MB_OK);
		}
	}
	break;
	case ID_other:
		MessageBox(hWnd, "快捷键Ctrl+R被按下", "Information", MB_OK);
	break;
	}
}

运行:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述




WM_PAINT 消息

WM_PAINT

  • 产生时间:当窗口需要绘制的时候。(窗口第一次显示,窗口被拖拽、大小发生变化等等)
  • 附带信息:
    • wParam 、lParam :0。
  • 专职用法:用于绘图。

ShowWindow() 函数在调用显示窗口的时候会发出一个 WM_PAINT 消息。

GetMessage() 函数在执行过程中也会检查窗口是否需要重新绘制,并发出 WM_PAINT 消息。

  • InvalidateRect :需要重新绘制的区域。

    BOOL InvalidateRect(
    	HWND hWnd,   //窗口句柄
    	CONST RECT* lpRect,   //区域的矩形坐标,参数为空的话表明整个窗口都需要重新绘制
    	BOOL bErase      //重绘前是否需要擦除
    );
    

    其中 RECT 是一个封装了矩形 上下左右的结构体:

    typedef struct tagRECT
    {
    	LONG    left;
    	LONG    top;
    	LONG    right;
    	LONG    bottom;
    } RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;
    

    InvalidateRect 该函数一旦调用,GetMessage() 也会发出 WM_PAINT 消息。

    用于通知Windows系统某个窗口或者指定的矩形区域需要重绘。当调用InvalidateRect函数时,系统会发送一个WM_PAINT消息给窗口,提示它需要重新绘制。这在开发Windows应用程序时非常有用,可以在需要更新界面时强制触发重绘操作。

    一般情况下,开发者会在需要更新窗口内容的时候调用InvalidateRect函数,然后系统会在适当的时间批量处理所有需要重绘的区域,提高绘制效率。通过使用InvalidateRect函数,开发者可以实现界面的实时更新和响应用户操作,提升用户体验。




绘图

绘图步骤

  • 开始绘图:
    HDC BeginPaint(
    	HWND hWnd,     //绘图窗口
    	LPPAINTSTRUCT lpPaint     //绘图参数的BUFF
    );     //返回绘图设备句柄 HDC
    
  • 正式绘图
  • 结束绘图
    BOOL EndPaint(
    	HWND hWnd,   //绘图窗口
    	CONST PAINTSTRUCT *lpPaint    //绘图参数的指针,BeginPaint返回
    );
    

按下鼠标左键进行字符串绘制:使用TextOut()函数,在客户区绘制字符串。

在这里插入图片描述




绘图编程

绘图基础

GDI - Windows graphics device interface Windows图形设备接口(Win32提供的绘图API), 是 Windows 操作系统提供的一组函数和数据结构,用于在屏幕上绘制图形、文本和图像。GDI 提供了访问和控制显示设备的功能,包括绘制形状、填充颜色、显示文本、裁剪图像等。开发人员可以使用 GDI 来创建图形用户界面(GUI)和进行图形处理,例如创建窗口、按钮、菜单等界面元素。GDI 在 Windows 中扮演着重要的角色,为应用程序提供了图形处理能力,帮助用户和开发者实现丰富的图形显示效果。

绘图设备 DC(Device Context 设备上下文)
Device Context (设备上下文) 是Windows开发中的重要概念,它指代一种用于绘制图形、文本和图像的抽象对象。

Device Context 封装了与设备相关的绘图操作,允许开发者在绘制到屏幕、打印机或其他输出设备上时使用统一的接口。通过与设备上下文进行交互,开发者可以绘制图形、操纵字体、绘制文本、处理图像和执行其他与绘图相关的操作。

DC可以看成 Windows 的画家,想要绘图就要先找到这个画家,通过 DC 句柄来找到画家。

HDC hdc = BeginPaint(hWnd, &pc); BeginPaint() 函数用来找到画家,并将返回值赋值给 HDC。BeginPaint() 的第一个参数 hWnd 窗口句柄,告诉画家在哪个窗口画画。

HDC - DC句柄,表示绘图设备。

抓到 HDC 以后,就可以使用各种绘图函数来进行图形绘制。

绘制图形结束后 EndPaint() 释放DC。

颜色:

  • 计算机使用 红、绿、蓝 (Red、Green、Blue:RGB) 作为三原色,计算机使用的三原色是物理中的三原色,通过这三种颜色的不同程度的配比可以得到各种各样的颜色,拿放大镜观察屏幕可以看到这三原色构成了显示器的各种颜色。(物理中的三原色要区别于美术中的三原色,美术中的三原色是指黄红蓝,这三种颜料可以配出绝大多数颜色的颜料。)
    • R值(红色值):0 ~ 255,(28 = 256,一个字节)。
    • G:0 ~ 255。
    • B:0 ~ 255。
    • 每个点的颜色是3个字节24位保存 0 ~ 224 - 1,总共有 16,777,216 种颜色。
    • 在过去的计算机中,使用16位来保存颜色值,低5位Red,第二个5位Green,最高6位Blue。
    • 32位表示颜色值:前面的24位用来表示红色(R)、绿色(G)和蓝色(B)的色彩强度,而后面的8位则用来表示 alpha 通道。Alpha 通道通常用来表示颜色的透明度,数值范围从 0 到 255,0 表示完全透明,255 表示完全不透明。通过调整alpha通道的数值,可以控制颜色的透明度,使得它可以适应各种混合和叠加效果。这种32位的颜色表示方式常用于计算机图形和游戏开发中。

颜色的使用:

使用 COLORREF 类型来声明一个颜色值。

typedef DWORD   COLORREF;     //本质是 DWORD。

typedef unsigned long       DWORD;      // DWORD 本质就是 unsigned long

unsigned long 类型占 4 个字节,正好 32 位,正好可以用来存储颜色值:
在这里插入图片描述


声明一个颜色值 COLORREF nColor = 0;

赋值使用 RGB宏:nColor = RGB(0,0,255); //蓝色

获取RGB值:GetRValue/GetGValue/GetBValue。BYTE nRed = GetRValue(nColor); 除了使用这几个函数,也自己自己通过 除法取余运算 来获得各个字节的值。


画点:

//SetPixel 设置指定点的颜色
COLORREF SetPixel(
	HDC hdc,   // DC 句柄
	int X,     // X 坐标
	int Y,     // Y 坐标
	COLORREF crColor     //设置颜色
);    //返回点原来的颜色

在消息处理函数的 switch 语句中添加对 WM_PAINT 消息的处理:在像素 100,100 的位置处绘制了一个非常小的黑点。

在这里插入图片描述


使用 for 循环绘制渐变色: 使用 for 循环绘制点,点成线,线成面,R和G的值在绘制的过程中不断变化,形成渐变色方块,第一个方块中,Blue的分量为255,第二个方块中 Blue的分量为128:

在这里插入图片描述

使用 for 循环一个一个点地去绘制,并不是一种高效的绘制方法。


基本图形绘制


线的使用(直线、弧线)

  • MoveToEx :指定窗口当前点(窗口当前点默认在(0,0)处,窗口客户区左上角)。
  • LineTo :从窗口当前点到指定点绘制一条直线。
MoveToEx(hdc, 0, 260, NULL);    //起点
LineTo(hdc,256, 390);           //终点(直线),LineTo 绘制完直线后,会将窗口当前点设置到256,390。

封闭图形:能够用画刷填充的图形(Rectangle,Ellipse)。

Rectangle(hdc, 100, 100, 300, 300);  //绘制直角矩形 坐标:左上->右下

Ellipse(hdc, 250, 100, 300,200);     //坐标:左上->右下,矩形中的内切圆或椭圆

更多的图形的绘制函数可以在网上查找,不赘述。


GDI 绘图对象

画笔

画笔的作用:线的颜色、线型、粗细。

HPEN 画笔句柄。

画笔的使用

  • 创建画笔
    •   HPEN CreatePen(
        	int fnPenStyle,   //画笔样式,PS_SOLID,实心线,可以支持多个像素宽,其他线型只能是一个像素宽。
        	int nWidth,         //画笔粗细
        	COLORREF crColor    //画笔颜色
        );      //创建成功返回句柄
      
  • 将画笔应用到DC中
    HGDIOBJ SelectObject(
    	HDC hdc,    //绘图设备句柄
    	HGDIOBJ hgdiobj    //GDI绘图对象句柄,画笔句柄
    );    //返回原来的GDI绘图对象句柄,注意保存原来DC当中的画笔
    
  • 绘图完成后,取出DC画笔,使用SelectObject()函数。
  • 释放画笔:
    BOOL DeleteObject(
    	HGDIOBJ hObject    //GDI绘图对象句柄,画笔句柄
    );     //只能删除不被DC使用的画笔,所以在释放前,必须将画笔从DC中取出
    

代码如图所示:

在这里插入图片描述

画笔样式也可以是虚线 PS_DASH ,非实线画笔像素宽度必须为 1 像素。
HPEN red_pen = CreatePen(PS_DASH, 1, RGB(255, 0, 0));

画刷

画刷的作用给封闭图形填充颜色或图案,封闭图形有 RectangleEllipse

HBRUSH :画刷句柄,保存画刷。

画刷的使用步骤跟画笔的使用步骤基本一样:

  • 创建画刷
    • CreateSolidBrush :创建实心画刷。
    • CreateHatchBrush :创建纹理画刷。
  • 将画刷应用到 DC 中:SelectObject()。
  • 绘图
  • 将画刷从 DC 中取出。
  • 删除画刷:DeleteObject()。
void OnPaint(HWND hWnd) {
	PAINTSTRUCT ps = { 0 };
	HDC hdc = BeginPaint(hWnd, &ps);    // 获得 DC 句柄

	HPEN red_pen = CreatePen(PS_DASH, 1, RGB(255, 0, 0));   //1个像素宽,虚线的红色画笔
	HGDIOBJ old_pen = SelectObject(hdc, red_pen);

	HBRUSH blue_brush = CreateSolidBrush(RGB(0, 0, 255));
	HGDIOBJ old_brush = SelectObject(hdc, blue_brush);    //将DC原来的画刷置换出来,DC默认画刷是白色的。

	MoveToEx(hdc, 0, 260, NULL);
	LineTo(hdc,256, 390);
	LineTo(hdc, 1, 1);
	LineTo(hdc, 23, 199);

	Rectangle(hdc, 100, 100, 300, 300);

	HBRUSH green_brush = CreateHatchBrush(HS_CROSS, RGB(0, 255, 0));
	SelectObject(hdc, green_brush);

	Ellipse(hdc, 250, 100, 300, 200);

	SelectObject(hdc, old_brush);
	DeleteObject(blue_brush);
	DeleteObject(green_brush);

	SelectObject(hdc, old_pen);    //置换出红色画笔
	DeleteObject(red_pen);

	EndPaint(hWnd, &ps);    //结束绘画
}

在这里插入图片描述


为了让绘制的封闭图形看上去与背景相契合,可以使用透明画刷。透明画刷已经存在于操作系统中,想要使用透明画刷,只需要向操作系统借用即可。

使用 GetStockObject() 函数获取由操作系统维护的画笔、画刷、字体等等。向操作系统借用的画笔、画刷等使用完后,不需要 DeleteObject()

向操作系统借用透明画刷:

HGDIOBJ transparent_brush = GetStockObject(NULL_BRUSH);
HGDIOBJ old_brush = SelectObject(hdc, transparent_brush);

位图

  • 光栅图形:记录图像中每一点的颜色等信息,光栅图形是由像素(图像的最小单元)组成的图形,它们在网格状的二维数组中描述。在光栅图形中,每个像素都有自己的颜色值,图像的清晰度和细节取决于像素的密度和分辨率。常见的光栅图形格式包括 JPEG、PNG、BMP 等。复杂的图像和真实世界的场景,但在缩放和变换时可能会失真。
  • 矢量图形:记录图像算法,绘图指令等,矢量图形使用数学公式描述图形,它由线条、曲线和填充区域等基本几何形状组成。与光栅图形不同,矢量图形以对象的形式存储,因此可以在任意比例下缩放而不失真。常见的矢量图形格式包括 SVG、EPS、AI 等。矢量形适合用于图标、标志、表和大型设计元素,能够保持清晰度并支持无损缩放。

位图句柄:HBITMAP,位图句柄能在内存中找到一块内存,里面保存着位图图像每个点的颜色值等信息。


位图的使用

  • 在资源中添加位图资源。

  • 从资源中加载位图 LoadBitmap()

  • 创建一个与当前 DC 相匹配的 DC (内存 DC)。

    •   HDC CeateCompatibleDC(
        	HDC hdc     //当前 DC 句柄,可以为 NULL (使用 屏幕 DC)
        );   //返回创建好的的 DC 句柄
        /*
        	这里的 内存 DC 是一个在内存中的 DC,可以理解为一个虚拟的 DC
        */
      
  • 使用 SelectObject 将位图的数据放入虚拟的内存DC中。

  • 成像(1:1比例):将内存 DC 中的图像显示在窗口上。

    •   BOOL BitBlt(
        	HDC hdcDest,       //目的DC
        	
        	int nXDest,         //目的DC左上角坐标
        	int nYDest,       //目的右上角坐标
        	int nWidth,       //目的宽度
        	int nHeight,      //目的高度,这四个参数确定成像位置和成像区域
        	
        	HDC hdcStr,      //源DC
        	int nXSrc,     //源左上角坐标
        	int nYSrc,     //源右上角坐标,从虚拟DC的什么位置开始成像
        	
        	DWORD dwRop       //成像方法 SRCCOPY
        );
      
  • 缩放成像:

    BOOL StretchBlt(
    	HDC hdcDest,     //handle to destination DC
    	int nXOriginDest, //x-coord of destination upper-left corner
    	int nYOriginDest, //y-coord of destination upper-right corner
    	int nwidthDest,   //width of destination rectangle
    	int nHeightDest,  //height of destination rectangle
    	HDC hdcSrc,       //handle to source DC
    	int nXOriginSrc,  //x-coord of source upper-left corner
    	int nYOriginSrc,  //y-coord of source upper-right corner
    	int nWidthSrc,    //源DC宽
    	int nHeightSrc,    //源DC高
    	DWORD dwRop        //raster operation code
    );
    
  • 使用 SelectObject 函数取出位图。

  • 释放位图 DeleteObject。

  • 释放匹配的 DC:DeleteDC。


添加 bitmap :添加 bitmap 成功之后,会显示 bitmap 的绘制界面,也可以修改 Resource.rc 文件中对于 bitmap 资源的描述,修改为从外部导入的 bitmap 文件。

在这里插入图片描述

在这里插入图片描述


1:1 成像代码:

PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hWnd, &ps);    // 获得 DC 句柄

//添加位图资源
HBITMAP hBmp1 = LoadBitmap(g_hInstance, (char*)IDB_BITMAP1);
HDC hMemdc = CreateCompatibleDC(hdc);     //创建一个DC,构建一个虚拟区域,并且内存DC在虚拟区域中绘图

HGDIOBJ oldbmp = SelectObject(hMemdc, hBmp1);
BitBlt(hdc, 10, 10, 1536, 1024, hMemdc, 0, 0, SRCCOPY);

SelectObject(hMemdc, oldbmp);
DeleteObject(hBmp1);
DeleteDC(hMemdc);

EndPaint(hWnd, &ps);    //结束绘画

在窗口中 1:1 显示位图:(该图高1536像素,宽1024像素,所以 1:1 显示无法看到全部图像。)

在这里插入图片描述


缩放成像:

StretchBlt(hdc, 5, 5, //窗口位置
 512, 768, //窗口区域
 hMemdc, 0, 0, //从图像的的哪里开始绘制
 1024, 1536, //显示图像的宽高
 SRCCOPY);

运行:
在这里插入图片描述

从上图的运行结果可以看到,图片缩小后显示,出现了很多的纹理。出现这种情况可能是因为在使用StretchBlt函数时,将图像进行缩放时导致了图像质量下降。StretchBlt函数可以对图像进行拉伸和压缩,但它是基于像素级的操作,因此在缩放时可能会导致图像的锯齿状边缘和失真。

想要在窗口中显示缩小的BMP图像并保持较好的质量,可以考虑使用双线性插值进行图像缩放。双线性插值是一种图像处理算法,它可以在进行图像缩放时减少锯齿状边缘和纹理。

使用 GDI+ 库来实现双线性插值

首先配置项目属性:在 Solution Explorer 栏下,右键项目名,项目属性 properties——>Linker 链接器——>Input——>Additional Dependencies 附加属性后面:添加一个值 gdiplus.lib; (注意每个值之间用英文版分号 ; 隔开)。
在这里插入图片描述

或者在代码第一行添加如下代码:

#pragma comment(lib,"Gdiplus.lib")     //这将使编译器在编译时自动链接所需的 Gdiplus 库,放在代码第一行
#include <gdiplus.h>     //包含所需要的头文件

using namespace Gdiplus;     //使用命名空间
... ...

void OnPaint(HWND hWnd) {
	//添加位图资源
	HBITMAP hBmp1 = LoadBitmap(g_hInstance, (char*)(IDB_BITMAP1));
	HBITMAP hBmp2 = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP2));
	HBITMAP hBmp3 = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP4));
	if ((hBmp1 == NULL) && (hBmp2 == NULL) && (hBmp3 == NULL))
	{
		MessageBox(hWnd, "Failed to load bitmap resource", "Error", MB_OK | MB_ICONERROR);
		exit(-1);
	}
	
	Bitmap bitmap1(hBmp1, NULL);
	Bitmap bitmap2(hBmp2, NULL);
	Bitmap bitmap3(hBmp3, NULL);

	// 缩小图片并保证质量
	HDC hdc = GetDC(hWnd);
	Graphics graphics(hdc);
	graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic);
	graphics.DrawImage(&bitmap1, 0, 0, 512, 768); // 以左上角为起点,缩放到 512x768 大小
	graphics.DrawImage(&bitmap2, 512, 0, 512, 768);
	graphics.DrawImage(&bitmap3, 1024, 0, 512, 768);
	
	// 释放资源
	DeleteObject(hBmp1);
	DeleteObject(hBmp2);
	DeleteObject(hBmp3);
	ReleaseDC(hWnd, hdc);
}

//消息处理
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
	switch (msgID)
	{
	case WM_PAINT:
		OnPaint(hWnd);
		break;
		... ...
}

... ...
//WinMain函数添加下面代码:
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
	//在程序开始时初始化GDI+库
	GdiplusStartupInput gdiplusStartupInput; //定义了一个名为gdiplusStartupInput的变量,其类型是GdiplusStartupInput。GdiplusStartupInput是一个结构体,用于指定GDI+库的初始化参数。通常情况下,你可以保持其默认值。
	ULONG_PTR gdiplusToken; //定义了一个名为gdiplusToken的变量,其类型是ULONG_PTR。在初始化GDI+库时,GdiplusStartup函数将为其分配一个令牌,并将此令牌存储在gdiplusToken中。这个令牌是用来标识GDI+库的实例化过程。
	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);  //调用了GdiplusStartup函数来初始化GDI+库。它接受三个参数:分别是一个指向令牌的指针(&gdiplusToken),一个指向GdiplusStartupInput结构的指针(&gdiplusStartupInput),以及一个GdiplusStartupOutput结构的指针,通常为NULL
	... ...
	
	while(1) {
		... ... //消息循环
	}
	
	//消息循环后面添加
	GdiplusShutdown(gdiplusToken); //调用了GdiplusShutdown函数来清理并关闭GDI+库。它接受一个参数,即在初始化时获得的令牌gdiplusToken。该函数用于释放GDI+库所使用的资源,确保在程序结束时正确清理。
	... ...
	return ...
}

运行:消除图片缩小时产生的纹理。

在这里插入图片描述

修改图片绘制的透明度为 0.5 :

// 设置透明度
ColorMatrix colorMatrix = {
1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.5f, 0.0f,  // 设置透明度,范围从0.0(完全透明)到1.0(完全不透明)
0.0f, 0.0f, 0.0f, 0.0f, 1.0f
};

ImageAttributes imageAttr;
imageAttr.SetColorMatrix(&colorMatrix, ColorMatrixFlagsDefault, ColorAdjustTypeBitmap);

// 绘制图像
graphics.DrawImage(&bitmap1, RectF(0, 0, 512, 768), 0, 0, bitmap1.GetWidth(), bitmap1.GetHeight(), UnitPixel, &imageAttr);
graphics.DrawImage(&bitmap2, RectF(512, 0, 512, 768), 0, 0, bitmap2.GetWidth(), bitmap2.GetHeight(), UnitPixel, &imageAttr);
graphics.DrawImage(&bitmap3, RectF(1024, 0, 512, 768), 0, 0, bitmap3.GetWidth(), bitmap3.GetHeight(), UnitPixel, &imageAttr);

运行:

在这里插入图片描述


ColorMatrix colorMatrix = {
1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.5f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f
};

这段代码创建了一个 ColorMatrix 对象,其中第四行第四列的值被设置为 0.5,即设置了图像的透明度为 50%。

ColorMatrix 是一个 5x5 的矩阵,用于在 GDI+ 中进行颜色转换。当我们将一个图像绘制到目标上时,可以使用 ColorMatrix 对图像进行颜色调整。它的基本原理是将每个像素的颜色值与 ColorMatrix 进行矩阵乘法运算,得到新的颜色值。

在这个 ColorMatrix 中,每行代表着输出颜色的一个分量(红、绿、蓝、透明度和偏移量),而每列代表输入颜色的一个分量。在这个矩阵中,第四行第四列的值表示了输出的 alpha(透明度)通道,因此将其设置为 0.5 就使得输出的透明度减半,从而实现了图像的半透明效果。

通过调整 ColorMatrix 中的各个元素,我们可以实现对图像的不同颜色分量的调整,包括亮度、对比度、饱和度以及透明度等。这种颜色转换的方式提供了一种非常灵活的方法,可以在绘制图像时对其进行各种颜色效果的处理。


文本绘制

在前文中提到了一个文本的绘制函数 TextOut (将文字绘制在指定坐标位置),这个函数对于文本的绘制功能比较基础。下面介绍 DrawText() 函数:

int DrawText(
	HDC hdc,    //DC句柄
	LPCTSTR lpString,   //字符串
	int nCount,     //字符数量
	LPRECT lpRect,   //绘制文字的矩形框
	UINT uFormat     //绘制的方式
);
RECT rc;    //矩形结构体
rc.left = 100;     //矩形距离窗口左边距离
rc.top = 150;      //矩形距离窗口顶端的距离
rc.right = 200;
rc.bottom = 200;
Rectangle(hdc, 100, 150, 200, 200);     //绘制矩形以查看字符串绘制的位置
DrawText(hdc, "你好 hello hello Long LONG LONG", strlen("你好 hello hello Long LONG LONG"), &rc, DT_LEFT | DT_TOP | DT_WORDBREAK | DT_NOCLIP);
  • DT_LEFT :水平靠左开始绘制字符串。
  • DT_TOP :垂直靠上绘制字符串。
  • DT_WORDBREAK :单行超出矩形右侧的文本另起一行绘制。
  • DT_NOCLIP :文本太长超出矩形范围时,不剪切字符串(完整显示所有文本)。
  • DT_CENTER :水平居中。
  • DT_VCENTER :垂直居中。(只能单行居中,与WORDBREAK冲突)
  • DT_SINGLELINE :单行绘制。
    … …

文字颜色和背景:

  • 文字颜色:SetTextColor。

    •   SetTextColor(hdc, RGB(0, 0, 255));   //蓝色字体
        
        DrawText(hdc, "你好\n hello hello\n Long\n LONG LONG Long\n LONG", strlen("你好\n hello hello\n Long\n LONG LONG Long\n LONG"), &rc, DT_CENTER | DT_VCENTER | DT_NOCLIP);
      
      在这里插入图片描述
  • 文字背景:SetBkColor。(默认背景色白色)

    •   SetBkColor(hdc, RGB(0, 128, 200));          //浅蓝色背景
      
      在这里插入图片描述
  • 文字背景模式:SetBkMode (OPAQUE :默认的不透明模式 / TRANSPARENT :透明模式)。

    •   SetBkMode(hdc, TRANSPARENT);
      
      在这里插入图片描述

字体

Windows 常用字体为 TrueType 格式的字体文件。

字体名:标识字体类型。
HFONT :字体句柄。

在 Windows 系统盘 C:/Windows/Fonts 目录下有着各种各样的字体:这些字体文件都是 TrueType 格式的,也就是每个字体文件中保存着每个字的真实点阵字型(位图字体,每个字的真实外观),即使双击打开,也不能查看到每个字,只是一个预览而已。

在这里插入图片描述

在这里插入图片描述


字体的使用

  • 创建字体:想要创建一个字体,那么该电脑中就要有该字体的字体文件。
    •   HFONT CreateFont(
        	int nHeight,    //字体高度
        	int nWidth,     //字体宽度,字体高度给出后,宽度填0即可自动匹配合适的宽度
        	int nEscapement,      //字符串倾斜角度,字符串与水平方向的夹角,以0.1度为单位。(字体站得竖直,但是字符串中所有的字符不在一水平线上)
        	int nOrientation,        //字符旋转角度,以x轴为轴心,向里或向外旋转角度。
        	int fnWeight,             //字体的粗细
        	DWORD fdwItalic,   //斜体,1或0。
        	DWORD fdwUnderline,     //字符下划线
        	DWORD fdwStrikeOut,     //删除线
        	DWORD fdwCharSet,       //字符集
        	DWORD fdwOutputPrecision,    //输出精度,没什么用
        	DWORD fdwClipPrecision,         //剪切精度,没什么用
        	DWORD fdwQuality,                   //输出质量,没什么用
        	DWORD fdwPitchAndFamily,     //匹配字体,没什么用
        	LPCTSTR lpszFace                  //字体名称
        );
      
  • 应用到字体 DC,(SelectObject)。
  • 绘制文本,(DrawText / TextOut)。
  • 取出字体,(SelectObject)。
  • 删除字体,(DeleteObject)。
SetTextColor(hdc, RGB(0, 128, 200));    //字体颜色浅蓝色
SetBkColor(hdc, RGB(0, 128, 200));      //字体背景色
SetBkMode(hdc, TRANSPARENT);            //设置字体背景为透明色,此时背景色会被覆盖

HFONT hFont = CreateFont(30, 0, 45, 0, 900, 1, 1, 1, GB2312_CHARSET, 0, 0, 0, 0, "楷体");   //创建字体
HGDIOBJ oldFont = SelectObject(hdc, hFont);

HGDIOBJ hBrush = GetStockObject(NULL_BRUSH);      //创建透明画刷
HGDIOBJ oldBrush = SelectObject(hdc, hBrush);

RECT rc;
rc.left = 100;
rc.top = 150;
rc.right = 200;
rc.bottom = 200;
Rectangle(hdc, 100, 150, 200, 200);     //绘制矩形背景为透明色
DrawText(hdc, "你好\n hello hello\n Long\n LONG LONG Long\n LONG", strlen("你好\n hello hello\n Long\n LONG LONG Long\n LONG"), &rc, DT_CENTER | DT_VCENTER | DT_NOCLIP);

// 释放资源
SelectObject(hdc, oldFont);
DeleteObject(hFont);
SelectObject(hdc, oldBrush);
DeleteObject(hBrush);

字体绘制如下:

在这里插入图片描述




请求一个图片的 url 并在窗口中显示

#include <Windows.h>
#include <WinINet.h>
#include <gdiplus.h>
#include <Shlwapi.h>
#include <vector>
#include <string>

#pragma comment(lib, "wininet.lib")
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "Shlwapi.lib")

using namespace Gdiplus;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // Initialize GDI+
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    // Register the window class
    const char CLASS_NAME[] = "Sample Window Class";

    WNDCLASS wc = {};
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    // Create the window
    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles
        CLASS_NAME,                     // Window class
        "HTTP Image Viewer",            // Window text
        WS_OVERLAPPEDWINDOW,            // Window style

        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 800,

        NULL,       // Parent window    
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
    );

    if (hwnd == NULL) {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);

    // Main message loop
    MSG msg = {};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // Shutdown GDI+
    GdiplusShutdown(gdiplusToken);

    return 0;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_PAINT: {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);

        // Example URL to request image from
        std::string url = "https://jackey-song.com/img/alipay.jpg";

        // Use WinINet to download image data (simplified)
        HINTERNET hInternet = InternetOpen("HTTP Example", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
        if (hInternet) {
            HINTERNET hConnect = InternetOpenUrl(hInternet, url.c_str(), NULL, 0, INTERNET_FLAG_RELOAD, 0);
            if (hConnect) {
                // Read image data into a buffer (simplified)
                BYTE buffer[1024];
                DWORD bytesRead;
                std::vector<BYTE> imageData;
                while (InternetReadFile(hConnect, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0) {
                    imageData.insert(imageData.end(), buffer, buffer + bytesRead);
                }

                // Create GDI+ Bitmap from memory stream (simplified)
                if (!imageData.empty()) {
                    IStream* pStream = SHCreateMemStream(&imageData[0], static_cast<UINT>(imageData.size()));
                    if (pStream) {
                        Bitmap bmp(pStream);

                        // Draw the image using GDI+
                        Graphics graphics(hdc);
                        graphics.DrawImage(&bmp, 0, 0, bmp.GetWidth(), bmp.GetHeight());

                        pStream->Release();
                    }
                    else {
                        OutputDebugString("Failed to create memory stream\n");
                    }
                }
                else {
                    OutputDebugString("Failed to download image data\n");
                }

                InternetCloseHandle(hConnect);
            }
            else {
                OutputDebugString("Failed to open URL\n");
            }

            InternetCloseHandle(hInternet);
        }
        else {
            OutputDebugString("Failed to open internet\n");
        }

        EndPaint(hwnd, &ps);
        break;
    }
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return 0;
}

代码中请求了一张图片:https://jackey-song.com/img/alipay.jpg ,没错就是我的支付宝的收款码,欢迎打赏。
在这里插入图片描述

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

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

相关文章

后端Web之分层解耦(控制反转IOC-依赖注入DI)

目录 1.三层架构 2.IOC-DI引入 3.IOC-DI使用 4.IOC细节 5.DI细节 内聚&#xff08;Cohesion&#xff09;和耦合&#xff08;Coupling&#xff09;是软件工程中两个重要的概念&#xff0c;它们衡量了软件组件的组织方式和组件之间的相互依赖程度。高内聚性意味着模块内的元…

2024真无线蓝牙耳机怎么选?24年四款性价比畅销爆款机型盘点

2024年&#xff0c;真无线蓝牙耳机的市场依旧竞争激烈&#xff0c;各种品牌和型号如雨后春笋般涌现&#xff0c;面对琳琅满目的选择&#xff0c;2024真无线蓝牙耳机怎么选&#xff1f;消费者在寻找具备高性价比和优秀性能的耳机时往往会感到困惑&#xff0c;那么我将针对大家的…

Nature Medicine | 常规机器学习构建蛋白质组衰老时钟!对于数学基础不好的同学,好好思考一下这种研究模式如何借鉴?

今天给各位老铁们分享一篇于2024年08月08号发表在 Nature Medicine [58.7] 的文章&#xff1a;"Proteomic aging clock predicts mortality and risk of common age-related diseases in diverse populations"&#xff0c;蛋白质组衰老时钟可预测不同人群的死亡率和常…

机器学习-卷积神经网络(CNN)

机器学习-卷积神经网络&#xff08;CNN&#xff09; 1. 卷积神经网络的基本概念1.1 卷积层&#xff08;Convolutional Layer&#xff09;1.1.1 卷积操作1.1.2 特征图&#xff08;Feature Map&#xff09; 1.2 激活函数&#xff08;Activation Function&#xff09;1.2.1 ReLU&a…

JavaEE从入门到起飞(八) ~ Git

git 概括 Git是一个分布式版本控制工具&#xff0c;主要用于管理开发过程中的源代码文件(Java类、xml文件、html页面等)。 学了git能干什么&#xff1f; 代码回溯 查看历史提交记录并恢复到之前的某个状态。这在发现错误或需要查看特定版本时非常有用。和CtrlZ的区别在于g…

【图机器学习系列】(一)图机器学习简介

微信公众号&#xff1a;leetcode_algos_life&#xff0c;代码随想随记 小红书&#xff1a;412408155 CSDN&#xff1a;https://blog.csdn.net/woai8339?typeblog &#xff0c;代码随想随记 GitHub: https://github.com/riverind 抖音【暂未开始&#xff0c;计划开始】&#xf…

Qt找不到QSound头文件,头文件QSoundEffect

目录 Qt找不到QSound头文件 CMake qmke 可能版本不同更新&#xff0c; 找不到QSound头文件 Qt找不到QSound头文件 #include <QSound> CMake 可以看一下这篇来自网上的文章 CMake中添加Qt模块的合理方法 - wjbooks - 博客园 (cnblogs.com) qmke 打开.pro文件&am…

Flowise在ubuntu22.04上的安装

之前我写过一篇FastGPT的文章&#xff0c;这里我再介绍另外一个开源的低代码工具&#xff0c;他提供开发人员可定制的LLM编排流程和AI代理。开发 LLM 应用程序通常涉及无数次迭代。Flowise的低代码和拖放式 UI 方法支持快速迭代&#xff0c;帮助您更快地从测试到生产。 推荐它的…

鸿蒙(API 12 Beta3版)【AVSession Kit简介】 音视频播控服务

AVSession Kit&#xff08;Audio & Video Session Kit&#xff0c;音视频播控服务&#xff09;是系统提供的音视频管控服务&#xff0c;用于统一管理系统中所有音视频行为&#xff0c;帮助开发者快速构建音视频统一展示和控制能力。 能力范围 提供音视频统一管控能力&…

Windows Server 域控制服务器安装及相关使用

目录 1.将客户机加入域 2.安装域控制器 3.新建域用户 4.设置用户登录时间&#xff0c;账户过期时间 5.软件分发 ​编辑 6.换壁纸 7.OU与GPO的概念 域为集中控制&#xff0c;拿下域控是拿下目标的关键 以Windows Server 2022为例 1.将客户机加入域 前提&#xff1a;客…

通过调整JVM的默认内存配置来解决内存溢出(‌OutOfMemoryError)‌或栈溢出(‌StackOverflowError)‌等错误

文章目录 引言I 调整JVM的默认堆内存配置java命令启动jar包Tomcat服务器部署java应用引言 问题: org.springframework.web.util.estedServletException: Handlerdispatch failed: nested exception isjava.lang.0utOfMemoryError: Java heap space原因分析: 查询查询平台所…

python 之可视化数据(地形图练习)

一、按要求构建疫情情况数据的地形图 二、代码展示 from pyecharts.charts import Map from pyecharts.options import VisualMapOptsmap Map() data [("北京市" , 994),("上海市" , 199),("湖南省" , 299),("台湾省" , 12),(&qu…

[游戏开发] LuaTable转string存读二进制文件

UE5和Unity通用此方案&#xff0c;只不过读写文件的接口略有不同&#xff0c;lua代码的处理是相同的。 下面两个方法是 LuaTable和字符串互相转换的代码 function XUtils.luaTableToString(tab, sp)sp sp or ""local s ""for k,v in pairs(tab) doif t…

高斯泼溅综合指南

高斯泼溅&#xff08;Gaussian Splatting&#xff09;是一种表示 3D 场景和渲染新视图的方法&#xff0c;在“实时辐射场渲染的 3D 高斯泼溅” 中引入。它可以被认为是 NeRF 类模型的替代品&#xff0c;就像当年的 NeRF 一样&#xff0c;高斯泼溅引发了大量新的研究工作&#x…

Android Kotlin - 通过 URL Scheme 打开应用

在 Android 应用中&#xff0c;可以通过自定义 URL Scheme 或 Deep Links 打开应用。这允许你定义一个自定义的 URL 格式&#xff0c;使得当用户点击包含这个 URL 的链接时&#xff0c;系统可以识别并打开你的 APP。以下是详细的实现步骤和一些注意事项。 1. 配置 AndroidMani…

RISC-V特权架构 - 时钟中断处理

RISC-V特权架构 - 时钟中断处理 1 MTI中断处理1.1 触发中断1.2 查询mie.MTIE与mip.MTIE1.3 若运行在M模式下1.4 若运行在S模式下1.5 若运行在U模式下 2 STI中断处理2.1 触发中断2.2 查询mie.STIE与mip.STIE2.3 若运行在M模式下2.4 若运行在S模式下2.5 若运行在U模式下 3 知识总…

Transformer架构的演进之路探究

1 引言 在2017年的开创性论文《Attention is All You Need&#xff08;注意力就是你所需要的一切&#xff09;》中&#xff0c;Vaswani等人提出了Transformer架构&#xff0c;这不仅在语音识别领域引起了一场革命&#xff0c;也对其他多个领域产生了深远的影响。本文将探讨Tra…

【早鸟优惠券】PostgreSQL 16 专栏优惠券

PostgreSQL 从入门到熟悉&#xff0c;本专栏只能做到从入门到熟悉。本专栏以 Markdown 格式书写&#xff0c;格式精美。有需要的朋友可以看下&#xff0c;使用的版本是 16。本专栏大约 20 万字左右&#xff0c;目前已完成了 18 万多字了。还有两篇&#xff08;事务、性能调优&a…

搭建内网开发环境(一)|基于docker快速部署开发环境

引言 最近因需要搭建一套简易版的纯内网的开发环境&#xff0c;服务器采用 centos8.0&#xff0c;容器化技术采用 docker 使用 docker-compose 进行容器编排。 该系列教程分为两大类&#xff1a; 软件安装和使用&#xff0c;这类是开发环境常用的软件部署和使用&#xff0c;涉…

Vue框架学习笔记-8

Vue中的跨域问题 在Vue项目中遇到跨域问题&#xff0c;通常是因为前端&#xff08;Vue&#xff09;和后端&#xff08;如Node.js, Django, Spring Boot等&#xff09;部署在不同的域名或端口上&#xff0c;浏览器出于安全考虑&#xff0c;会阻止跨域请求。解决Vue中的跨域问题…