win32 - 绘图

绘图编程

1. 绘图基础

绘图设备 DC (Device Context),绘图上下文 / 绘图描述表。os提供的绘画工具,由它代用户进行绘画。

HDC:DC句柄,表示绘图设备

GDI:Windows graphics device interface(Win32 提供的绘图API)

颜色:RGB 三原生

每个颜色用3个字节保存24位保存 0~2^24-1。
曾经使用16位:最低5位红色,中间5位绿色,后6位蓝色。
还有使用32位:前3个8位代表RGB,最后8位代表透明度,用于三维图形开发。

  • 颜色使用:COLORREF类 - 实际为 DWORD。例如:COLORREF nColor = 0
  • 赋值使用RGB宏:nColor = RGB(0, 0, 255);
  • 获取RGB:GetRValue / GetGValue / GetBValue 。 BYTE nRed = GetRValue(nColor);

 

获得绘图设备

WINUSERAPI
HDC
WINAPI
BeginPaint(
    _In_ HWND hWnd,
    _Out_ LPPAINTSTRUCT lpPaint);

绘图操作

 

释放绘图设备

WINUSERAPI
BOOL
WINAPI
EndPaint(
    _In_ HWND hWnd,
    _In_ CONST PAINTSTRUCT *lpPaint);

2. 基本图形绘制

画点

// 设置定点的颜色
WINGDIAPI COLORREF WINAPI SetPixel(
    _In_ HDC hdc,  // dc句柄
    _In_ int x,    // X 坐标
    _In_ int y,    // Y 坐标
    _In_ COLORREF color // 设置的颜色
); // 返回原来的颜色

WM_PAINT 消息中调用函数绘制图形:

void OnPaint(HWND hwnd)
{
	PAINTSTRUCT ps = { 0 };
	HDC hdc = BeginPaint(hwnd, &ps);

	for (int i = 50; i < 100; i++)
	{
		for (int j = 50; j < 100; j++)
		{
			SetPixel(hdc, i, j, RGB(i, 0, j));
		}
	}

	EndPaint(hwnd, &ps);
}

画线

需要配合使用

  1. MoveToEx:指明窗口当前点
  2. LineTo:从窗口当前点到指定点绘制一条直线,同时也会调用 MoveToEx
  3. 当前点:上一次绘图时最后一点,初始值为 ( 0, 0 ) 点。每个窗口都有。

在处理 WM_PAINT 消息内调用:

void DrawLine(HWND hwnd)
{
	PAINTSTRUCT ps = { 0 };
	HDC hdc = BeginPaint(hwnd, &ps);

	MoveToEx(hdc, 50, 50, NULL);
	LineTo(hdc, 300, 300);

	EndPaint(hwnd, &ps);
}

封闭图形

能够用画刷填充的图形 Rectangle / Ellipse

void DrawRect(HWND hwnd)
{
	PAINTSTRUCT ps = { 0 };
	HDC hdc = BeginPaint(hwnd, &ps);

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

	EndPaint(hwnd, &ps);
	
}

3. GDI 绘图对象

使用Paint画图时只能画出黑色

画笔

线的颜色、线型(实线、虚线、点线)、线粗
HPEN 画笔句柄

1. 创建画笔
// 创建成功返回句柄
WINGDIAPI 
HPEN
WINAPI CreatePen( 
    _In_ int iStyle,    // 画笔样式
    _In_ int cWidth,    // 画笔的粗细
    _In_ COLORREF color // 画笔颜色
);
// 画笔样式
PS_SOILD  实心线,第二个参数可支持多个像素宽,其他线型只能时一个像素宽



2. 将画笔应用到DC中
// 将画笔送给绘图设备。返回旧GDI绘图对象句柄,注意:保存原DC当中的画笔
// 可以形象的理解为用一个新的GDI绘图对象从DC那里交换旧的GDI绘图对象。
WINGDIAPI 
HGDIOBJ 
WINAPI 
SelectObject(
    _In_ HDC hdc,   // 绘图设备句柄
    _In_ HGDIOBJ h  // GDI 绘图对象句柄,画笔句柄。HPEN 是 HGDIOBJ 的一种
);



3. 绘图


4. 取出DC中的画笔
将原来的画笔,使用SelectObject函数,放入DC中,交换出我们创建的画笔


5. 释放画笔
WINGDIAPI 
BOOL 
WINAPI 
DeleteObject( 
    _In_ HGDIOBJ ho  // GDI 绘图句柄,画笔句柄
);
只能删除不被DC使用的画笔,在释放前,必须将画笔从DC中取出。

例子,在 WM_PAINT 消息中处理:

void Draw(HWND hwnd)
{
	PAINTSTRUCT ps = { 0 };
	HDC hdc = BeginPaint(hwnd, &ps);

	HGDIOBJ hPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
	HGDIOBJ oldGDI = SelectObject(hdc, hPen);

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

	hPen = SelectObject(hdc, oldGDI); // 因为这个画笔是在这个函数中创建的,所以亦可以不接收
	DeleteObject(hPen);

	EndPaint(hwnd, &ps);
	
}


使用 CreatePen(PS_DASH, 1, RGB(255, 0, 0))时第二个参数必须为1,如果为其他值,这个画笔失效。

画刷

给封闭图形填充颜色、图案。HBRUSH 画刷句柄。

使用:

  1. 创建画刷
    CreateSoliBrush  CreateHatchBrush
  2. 将画刷应用到DC中
    SelectObject
  3. 绘图
  4. 将画刷从DC中取出
    SelectObject
  5. 删除画刷
    DeleteObje

示例:

void Draw(HWND hwnd)
{
	PAINTSTRUCT ps = { 0 };
	HDC hdc = BeginPaint(hwnd, &ps);

	HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0));
	HGDIOBJ hOldBrush = SelectObject(hdc, hBrush); // 默认有一把白色画刷

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

	SelectObject(hdc, hOldBrush);
	DeleteObject(hBrush);

	EndPaint(hwnd, &ps);
}

void Draw(HWND hwnd)
{
	PAINTSTRUCT ps = { 0 };
	HDC hdc = BeginPaint(hwnd, &ps);

	//HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0));
	HBRUSH hBrush = CreateHatchBrush(HS_CROSS, RGB(0, 0, 255));
	HGDIOBJ hOldBrush = SelectObject(hdc, hBrush); // 默认有一把白色画刷

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

	SelectObject(hdc, hOldBrush);
	DeleteObject(hBrush);

	EndPaint(hwnd, &ps);
}

透明画刷

通常情况下希望图形填充颜色与背景相同。

通过 GetStockObject 获取系统维护的画刷、画笔等。如无需画刷,可通过 NULL_BRUSH NULL_PEN 获取不填充的画刷、画笔。这个函数返回的画刷无需 DeleteObject

void Draw(HWND hwnd)
{
	PAINTSTRUCT ps = { 0 };
	HDC hdc = BeginPaint(hwnd, &ps);

	HGDIOBJ hBrush = GetStockObject(NULL_BRUSH);
	HGDIOBJ hOldObj = SelectObject(hdc, hBrush);
	HGDIOBJ hPen = GetStockObject(NULL_PEN);
	HGDIOBJ hOldPen = SelectObject(hdc, hPen);

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

	SelectObject(hdc, hOldObj);
	SelectObject(hdc, hOldPen);
	EndPaint(hwnd, &ps);
}

 

位图

1. 位图绘制

位图相关:

  • 光栅图形:记录图像中每一点的颜色等信息(常见)。bitmap 就是严格记录了每一个点的信息。
  • 矢量图形:记录图像算法、绘图指令等(用于图谱分析、科学计算等)

HBITMAP:位图句柄

位图使用:

  1. 在资源中添加位图资源——兼具 GDIOBJ资源 的特点
     
  2. 从资源中加载位图 LoadBitmap
    HBITMAP
    LoadBitmapA(
        _In_opt_ HINSTANCE hInstance,
        _In_ LPCSTR lpBitmapName
    );
  3. 创建一个与当前DC相匹配的DC(内存DC)
    当前DC在屏幕上绘画
    内存DC在内存中绘画
    HDC CreateCompatibleDC( _In_opt_ HDC hdc); // 传入当前DC,为NULL时代表屏幕DC;在内存构建一个虚拟地区,并返回内存DC用于在虚拟地区绘画
  4. 将bitmap放入匹配的DC中,SelectObject
    将bitmap放入内存中后,内存里会立即画出图片。
  5. 成像(1:1 比例 )
    将内存中的图像成像到屏幕上
    WINGDIAPI BOOL  WINAPI BitBlt( 
        _In_ HDC hdc,  // 目标DC,一般为“当前DC”
        _In_ int x,    // 目的左上X坐标
        _In_ int y,    // 目的左上Y坐标,在窗口的什么位置成像
        _In_ int cx,   // 目标宽度
        _In_ int cy,   // 目标高度,在窗口中开辟多大空间用于成像
        _In_opt_ HDC hdcSrc,   // 源DC,“内存DC”
        _In_ int x1,   // 源左上X坐标
        _In_ int y1,   // 源左上Y坐标,从内存中图像的哪个位置开始成像
        _In_ DWORD rop // 成像方法 SRCCOPY:原样成像
    );
    // 当目标区域小于原图时,只能显示部分图像。

    缩放成像,缩小或放大:由“目标DC中高宽”和“源DC高宽”的比例决定

    BOOL StretchBlt(
        _In_ HDC hdcDest, // 目的DC
        _In_ int xDest,   // X
        _In_ int yDest,   // Y
        _In_ int wDest,   // 目标宽
        _In_ int hDest,   // 目标高
        _In_opt_ HDC hdcSrc,  // 源DC
        _In_ int xSrc,    // X
        _In_ int ySrc,    // Y
        _In_ int wSrc,    // 源DC宽
        _In_ int hSrc,    // 源DC高  这两个参数指定成像多大区域
        _In_ DWORD rop
    );
  6. 取出位图
    SelectObject
  7. 释放位图
    DeleteObject
  8. 释放匹配的DC
    DeleteDC

前2步与资源操作类似,第345步与绘图类似。

void Draw(HWND hwnd)
{
	PAINTSTRUCT ps = { 0 };
	HDC hdc = BeginPaint(hwnd, &ps);
	//1. 添加资源

	//2. 加载 bitmap
	HBITMAP hBitmap =  LoadBitmap(g_hInstance, (LPCWSTR)IDB_BITMAP1);
	
	//3. 创建内存DC,并构建虚拟区域,在内存DC中画图
	HDC hMemDC = CreateCompatibleDC(hdc);

	//4. 将位图送给内存DC,内存DC在虚拟区域画出
	HGDIOBJ hOldGdiObj = SelectObject(hMemDC, hBitmap);

	//5. 将虚拟区域的图像成像到窗口中
	BitBlt(hdc, 50, 50, 48, 48, hMemDC, 0, 0, SRCCOPY);
	StretchBlt(hdc, 0, 0, 24, 24, hMemDC, 0, 0, 48, 48, SRCCOPY);

	//6.
	SelectObject(hMemDC, hOldGdiObj);
	//7.
	DeleteObject(hBitmap);
	//8.
	DeleteDC(hMemDC);

	EndPaint(hwnd, &ps);
}

  1:1 成像 , 缩放到原来一半

 

 

文本绘制

TextOut // 功能最弱,不能换行,不能对齐

int DrawTextA(
    _In_ HDC hdc,      // DC句柄
    LPCSTR lpchText,   // 字符串
    _In_ int cchText,  // 字符数
    _Inout_ LPRECT lprc,  // 绘制文字的矩形框
    _In_ UINT format   // 绘制方式
);

 示例:

LRESULT CALLBACK WnProc(
	HWND hwnd,
	UINT msg,
	WPARAM wparam,
	LPARAM lparam)
{
	switch (msg)
	{
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hwnd, &ps);

		TCHAR szText[] = TEXT("12345678901234567890 qwert asdf");
		TextOut(hdc, 100, 100, szText, lstrlen(szText));

		Rectangle(hdc, 100, 150, 200, 200);

		RECT rc;
		rc.left = 100;
		rc.top = 150;
		rc.right = 200;
		rc.bottom = 200;
		
        // DT_CENTER 不能与DT_WORDBREAK 一起使用。前者只适用于DT_SINGLELINE,一定靠顶端
		DrawText(hdc, szText, lstrlen(szText), &rc, DT_WORDBREAK);

		EndPaint(hwnd, &ps);
		break;
	}
	default:
		break;
	}
	return DefWindowProc(hwnd, msg, wparam, lparam);
}

美化

颜色

  • SetTextColor
  • SetBkColor  背景色默认白色,只适用于 OPAQUE 模式
  • SetBkMode (OPAQUE / TRANSPARENT)  文字背景模式,只有这两种
    • OPAQUE 默认的不透明模式
    • TRANSPARENT 透明模式
SetTextColor(hdc, RGB(255, 0, 0));
SetColor(hdc, RGB(0, 255, 0));
SetBkMode(hdc, TRANSPARENT);

 字体

 windows常用TrueType字体。字体名:标识字体类型。HFONT:字体句柄。

  1. 创建字体
    HFONT CreateFontA( 
        _In_ int cHeight,     // 字体高
        _In_ int cWidth,      // 字体宽
        _In_ int cEscapement, // 字符串倾斜角度,
        _In_ int cOrientation,// 字符旋转角度,以x轴为中心向里或向外旋转
        _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_opt_ LPCSTR pszFaceName  // 字体名称
    );
  2. 应用字体到DC
    SelectObject
  3. 绘制文字
    DrawText / TextOut
  4. 取出字体
    SelectObject
  5. 删除字体
    DeleteObject

 

case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hwnd, &ps);

		HFONT hFont = CreateFont(30, 0, 45, 0, 900, 1, 1, 1, GB2312_CHARSET, 0, 0, 0, 0, L"黑体");
		HGDIOBJ hOldObj = SelectObject(hdc, hFont);

		TCHAR szText[] = TEXT("12345678901234567890 qwert asdf");
		TextOut(hdc, 100, 100, szText, lstrlen(szText));

		SelectObject(hdc, hOldObj);
		DeleteObject(hFont);  // 字体占用内存大,一定记得释放
		EndPaint(hwnd, &ps);
		break;
	}

 

posted @ 2022-08-02 08:49  某某人8265  阅读(461)  评论(1编辑  收藏  举报