这一篇文章分享本人学习win32绘图编程,其中包括GDI绘图对象,绘图基础,基本图形的绘制,画笔画刷的使用,文本绘制,以及文本字体的更改。
文章目录
- 一.绘图基础
- 1.BeginPaint函数
- 2.EndPaint函数
- 3.颜色的使用
- 二.基本图形绘制
- 1.点的绘制
- `SetPixel`函数
- 回调函数中处理绘图消息
- 2.线的绘制
- `MoveToEx`函数
- `LineTo`函数
- 3.封闭图形绘制
- 三.GDI绘图对象--画笔
- - 1.创建画笔
- - 2.将画笔给到设备上下文(DC)
- - 3.绘图
- - 4.取出设备上下文(DC)中的画笔
- - 5.释放画笔
- 四.GDI绘图对象-画刷
- 五.位图的使用
- - 1.位图相关:
- - 2.位图的使用
- 六.文本绘制
- 七.字体
- 1.字体相关
- 2.字体的使用
- 绘图设备DC(Device Context),有时也叫做绘图上下文/绘图描述表/显示设备上下文
- HDC-DC句柄,表示绘图设备
- GDI-Windows graphics device interface (win32提供的绘图API)
颜色:
绘图设备句柄:我们使用绘图设备,必须拿到绘图设备句柄
我们来想想这样一个场景:我们需要绘图,但是我们自己不会,我们把绘图设备比喻为一个画家,那么拿到绘图设备句柄的过程就相当于是把这个画家抓过来,抓过来让他完成工作后,还需要把画家放走。
PAINTSTRUCT ps = {0}; //在绘图之前,我们需要定义一个PAINTSTURCT结构体,我们不需要关注结构体内的内容
HDC hdc = BeginPaint(...); //抓绘图设备句柄
TextOut(hdc,100,100,"Hello",...); //绘制文本
EndPaint(...); //释放绘图设备
我们来细讲一下BeginPaint
函数和EndPaint
函数:
BeginPaint 函数准备用于绘制的指定窗口,并使用有关绘图的信息填充 PAINTSTRUCT 结构。
MSDN官方文档解释BeginPaint函数
HDC BeginPaint(
HWND hWnd,
LPPAINTSTRUCT lpPaintStruct
);
其中,hWnd参数指定了要重新绘制的窗口句柄,lpPaintStruct指向我们定义的PAINTSTRUCT结构,这个函数会填上该结构中所需要的信息。
MSDN官方文档解释EndPaint函数
EndPaint函数标记指定窗口中绘制的结尾,每次调用BeginPaint函数时都需要此函数,但仅在绘制完成后才需要此函数。
语法:
BOOL EndPaint(
HWND hWnd, //已经重新绘制的窗口的句柄
const PAINTSTRUCT* lpPaint //指向包含BeginPaint检索的绘画信息的PAINTSTRUCT结构的指针
);
返回值:返回值始终为非零值。
EndPaint函数释放BeginPaint函数检索道德显示设备上下文
colorref–实际DWORD–using long
例如:
COLORREF nColor = 0;
赋值RGB宏:
例:
`nColor =RGB(0,0,255);
获取RGB的值:
GetRValue/GetGValue/GetBValue
函数
例:
BYTE nRed = GetRValue(nColor);
SetPixel
函数MSDN官方文档解释SetPixel函数
SetPixel函数将指定坐标处的像素设置为指定颜色
函数原型:
COLORREF SetPixel(
HDC hdc, //设备上下文句柄
int x, //要设置的x坐标
int y, //要设置的y坐标
COLORREF color //用于绘制点的颜色
);
这里给出的是所有的回调函数内容,大家可以翻到最后直接看绘制窗口消息处理过程。
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam
)
{
char output[256] = { 0 };
switch (uMsg)
{
//常见消息
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
case WM_SYSCOMMAND: {
sprintf(output, TEXT("检测到WM_COMMAND消息\n"));
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_CREATE: {
sprintf(output, "检测到WM_CREATE消息,将创建窗口。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_SIZE: {
sprintf(output, "lParam:窗口宽变化为:%d,窗口高变化为:%d \n", HIWORD(lParam), LOWORD(lParam));
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
//菜单命令消息
case WM_COMMAND: {
switch(LOWORD(wParam)) {
case MY_OPEN: {
sprintf(output, "打开按钮被点击,请到回调函数中做具体处理。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case MY_QUIT: {
sprintf(output, "退出按钮被点击,请到回调函数中做具体处理。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case IDM_ABOUT: {
sprintf(output, "帮助按钮被点击,请到回调函数中做具体处理。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
}
case MY_NEWFILE: {
sprintf(output, "打开新文件按钮被点击,请到回调函数中做具体处理。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case MY_LASTTIMEFILE: {
sprintf(output, "上次打开文件按钮被点击,请到回调函数中做具体处理。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
}
break;
}
//键盘消息
case WM_KEYDOWN: {
sprintf(output, "检测到WM_KEYDOWN消息,键码值:%d.\n", wParam);
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_KEYUP: {
sprintf(output, "检测到WM_KEYUP消息,键码值:%d.该按键被放开\n", wParam);
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
//鼠标消息
case WM_LBUTTONDOWN: {
sprintf(output, "检测到WM_LBUTTONDOWN消息,鼠标左键被按下。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_LBUTTONUP: {
sprintf(output, "检测到WM_LBUTTONUP消息,鼠标左键被放开。\n");
WriteConsole(g_hOUTPUT, output, strlen(output),0,0);
break;
}
case WM_RBUTTONDOWN: {
sprintf(output, "检测到WM_RBUTTON消息,鼠标右键被按下。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_RBUTTONUP: {
sprintf(output, "检测到WM_RBUTTON消息,鼠标右键被放开。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
}
case WM_MOUSEMOVE: {
sprintf(output, "检测到WM_MOUSEMOVE消息,鼠标移动中,鼠标位置(%d,%d).\n", LOWORD(lParam), HIWORD(lParam));
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_MOUSEWHEEL: {
sprintf(output, "鼠标滚轮滚动中,偏移量:%d,鼠标当前位置(%d,%d)\n",HIWORD(wParam), LOWORD(lParam), HIWORD(lParam));
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_CONTEXTMENU: {
TrackPopupMenu(
CreatePopupMenu(),
TPM_LEFTALIGN,
0, 0, 0, hwnd, 0);
break;
}
//绘制窗口消息
case WM_PAINT: {
sprintf(output, "检测到WM_PAINT消息");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
SetPixel(hdc, 100, 100, RGB(6, 8, 9));
EndPaint(hwnd, &ps);
break;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
处理效果:
大家可以看到程序中出现的点。由于是一个像素,可能看的不是很清楚。
像绘制点一样,我们也需要抓到设备上下文的句柄,画线,释放设备上下文。
在绘制线条的时候,我们需要几个函数,我们先来学习一下这几个函数:
MoveToEx
函数MSDN官方文档解释MoveToEx函数
MoveToEx函数将当前位置更新为指定点,并选择性地返回上一个位置。
语法:
BOOL MoveToEx(
HDC hdc, //设备上下文句柄
int x,
int y, //指定当前点
LPPOINT lppt //指向接收当前位置的POINT指针,如果此参数为NULL指针,则不返回上一个位置
);
LineTo
函数LineTo函数从当前位置绘制一行,但不包括指定点。
MSDN官方文档解释LineTo函数
BOOL LineTo(
HDC hdc, //设备上下文句柄
int x,
int y //指定线条终点坐标
);
返回值:
回调函数处理:
case WM_PAINT: {
sprintf(output, "检测到WM_PAINT消息\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, 500, 500);
EndPaint(hwnd, &ps);
break;
}
封闭图形:能够用画刷填充的图形
这里给出绘制矩形和绘制圆形的函数,其他函数有很多,大家可以自行学习
Rectangle
函数BOOL Rectangle(
HDC hdc, //设备上下文句柄
int left,
int top,
int right,
int botton
);
回调函数处理:
case WM_PAINT: {
sprintf(output, "检测到WM_PAINT消息\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
/* MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, 500, 500);*/
Rectangle(hdc,100,100,200,200);
EndPaint(hwnd, &ps);
break;
}
Ellipse
函数MSDN官方文档解释Ellipse函数
BOOL Ellipse(
HDC hdc, //设备上下文句柄
int left,
int top,
int right,
int bottom
);
回调函数处理:
case WM_PAINT: {
sprintf(output, "检测到WM_PAINT消息\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
/* MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, 500, 500);*/ //绘制直线
// Rectangle(hdc, 100, 100, 200, 200); //绘制矩形
Ellipse(hdc, 100, 100, 50, 50); //绘制圆形
EndPaint(hwnd, &ps);
break;
}
画笔的使用:
我们在上文中将设备上下文比喻为画家,在这里我们继续使用上文的比喻,能够帮助大家更好地理解。
CreatePen
函数创建具有指定样式,宽度和颜色的逻辑笔。
MSDN官方文档解释CreatePen函数
HPEN CreatPen(
int isStyle, //笔样式
int cWisth, //笔的宽度
COLORREF color //笔的颜色
);
注意: 笔样式中,PS_SOILD是实心线,可以支持多个像素宽度,其他线性只能是一个像素宽。
SelectObject 函数将对象选择到指定的设备上下文中, (DC) 。 新对象替换同一类型的上一个对象。
这一步骤相当于将创建好的新画笔给画家,让画家拿着我们创建的新画笔绘图。
MSDN官方文档解释SelectObject函数
SelectObject(
HDC hdc, //设备上下文句柄
HGDIOBJ h //GDI绘图对象句柄,画笔句柄
);
这一步骤中使用绘图函数,绘制我们想要的图形
在这一步骤中,将原来的画笔使用SelectObject
函数,放入到设备上下文(DC)中,就会将我们的画笔取出
在绘图完成后,我们需要将我们的画笔释放:
MSDN官方文档解释SelectObject函数
BOOL SelectObject(
HGDIOBJ ho //逻辑笔,画笔,字体,位图,区域或调色板的句柄
);
如果函数成功,返回非零值。
注意: 只能删除不被DC使用的画笔,所以在释放之前,必须将画笔从DC中取出。
我们来到回调函数中处理:
case WM_PAINT: {
sprintf(output, "检测到WM_PAINT消息\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
/* MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, 500, 500);*/ //绘制直线
// Rectangle(hdc, 100, 100, 200, 200); //绘制矩形
HPEN hPen = CreatePen(PS_SOLID, 3, RGB(50, 80, 99));
HGDIOBJ hOldPen = SelectObject(hdc, hPen);
Ellipse(hdc, 100, 100, 50, 50); //绘制圆形
EndPaint(hwnd, &ps);
SelectObject(hdc, hOldPen);
DeleteObject(hOldPen);
break;
}
画刷的使用:
CreateSoildBrush
函数:创建实心画刷CreateHatchBrush
函数:创建纹理画刷我们看看回调函数处理过程:
case WM_PAINT: {
sprintf(output, "检测到WM_PAINT消息\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
/* MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, 500, 500);*/ //绘制直线
// Rectangle(hdc, 100, 100, 200, 200); //绘制矩形
HPEN hPen = CreatePen(PS_SOLID, 3, RGB(50, 80, 99));
HGDIOBJ hOldPen = SelectObject(hdc, hPen);
HGDIOBJ hBrush = CreateSolidBrush(RGB(255, 0, 0));
HGDIOBJ hOldBrush = SelectObject(hdc, hBrush);
SelectObject(hdc, hBrush);
Ellipse(hdc, 100, 100, 50, 50); //绘制圆形
EndPaint(hwnd, &ps);
SelectObject(hdc, hOldPen);
SelectObject(hdc, hOldBrush);
DeleteObject(hPen);
DeleteObject(hBrush);
break;
}
光栅图形:记录图像中每一个点的颜色信息
矢量图形:记录图像算法,绘图指令等
LoadBitmap
函数:LoadBitmap(
HINSTANCE hInstance, //其可执行文件包含要加载的位图模块实例的句柄
LPSTR lpBitmapName //指向包含要加载的位图资源名称的空终止字符串的指针
);
返回值:如果函数成功,则返回指定位图的句柄
3.创建一个与当前DC相匹配的DC(内存DC)
HDC CreateCompatibleDC(
HDC hdc //当前DC句柄,可以为NULL(屏幕DC)
);创建成功,返回DC句柄
BOOL BitBlt(
HDC hdc, //目标设备上下文句柄
int x, //目的矩形左上角x坐标
int y, //目的矩形左上角y坐标
int cx, //源矩形和目标矩形的宽度
int cy, //源矩形和目标矩形的高度
HDC hdcsrc, //源设备上下文句柄
int x1, //源矩形左上角的x坐标
int y1, //源矩形左上角的y坐标
DWORD ROP //光栅操作代码
);如果函数成功,则返回非零值
SeleteObject
函数DeleteObject
函数我们来到回调函数中处理:
//绘制窗口消息
case WM_PAINT: {
sprintf(output, "检测到WM_PAINT消息\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
PAINTSTRUCT ps = { 0 };
HDC hdc = BeginPaint(hwnd, &ps);
/* MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, 500, 500);*/ //绘制直线
// Rectangle(hdc, 100, 100, 200, 200); //绘制矩形
/*HPEN hPen = CreatePen(PS_SOLID, 3, RGB(50, 80, 99));
HGDIOBJ hOldPen = SelectObject(hdc, hPen);
HGDIOBJ hBrush = CreateSolidBrush(RGB(255, 0, 0));
HGDIOBJ hOldBrush = SelectObject(hdc, hBrush);
SelectObject(hdc, hBrush);
Ellipse(hdc, 100, 100, 50, 50); //绘制圆形
EndPaint(hwnd, &ps);
SelectObject(hdc, hOldPen);
SelectObject(hdc, hOldBrush);
DeleteObject(hPen);
DeleteObject(hBrush);*/
//位图:
HBITMAP hBitmap = LoadBitmap(hIns, (LPCSTR)IDB_BITMAP1); //加载位图
HDC hNewHdc = CreateCompatibleDC(hdc); //创建与当前DC匹配的内存DC
HGDIOBJ hOldBitmap = SelectObject(hNewHdc, hBitmap);
BitBlt(hdc, 100, 100, 100,100, hNewHdc, 0, 0, SRCCOPY);
SelectObject(hNewHdc, hOldBitmap);
DeleteObject(hBitmap);
DeleteDC(hNewHdc);
break;
}
我们来看看处理效果:
这里我们只是将位图1:1成像,大家可以使用其他方法,使程序更加美观。
文字绘制:
int DrawText(
HDC hdc, //设备上下文句柄
LPCTSTR lpchText, //指向指定要绘制的文本的字符串的指针
int cchText, //字符串的长度
LPRECT lprc, //指向RECT结构的指针
UINT format //设置文本格式的方法
);
SetTextColor
函数SetBkColor
函数(只有在OPAQUE模式下适用)SetBkMode
函数,其中参数设置为OPANQUE(不透明),TRANSPARENT(透明)CreateFont
函数到硬盘查找字体文件HFONT CreateFintA(
int cHeight, //字体字符单元格时或字符的高度
int cWidth, //请求字体中字符的平均宽度
int cEscapement, //转义向量与设备x轴之间的角度
int cOrientation, //每个字符的基线与设备x轴之间的角度
int cWeight, //字体粗细值(0到1000)
DWORD bItalic, //如果设置为TRUE 则指定斜体
DWORD bUnderline, //如果指定为TRUE,则指定带下划线字体
DWORD bStrikeOut, //如果设置为TRUE,则为删除线字体
DWORD iCharSet, //字符集
DWORD iOutPracision, //输出精度
DWORD iClipPrecision, //剪辑精度
DWORD iQuality, //输出质量
DWORD iPitchAndFamily, //字体的音调和系列
LPCSTR pszFaceName //指向指定字体字号的 null 终止字符串的指针
);
本篇文章就分享到这里,大家如果有什么不理解的随时可以私信我,如果发现文章中的问题,希望大家及时指出来,我会非常虚心地学习,希望我们共同进步!!!