使用现有画笔
Windows 提供三种备用画笔(Stock Pen):BLACK_PEN(黑色画笔)、WHITE_PEN(白色画笔)、NULL_PEN(不绘制任何图形的画笔)。
调用 GetStockObject 函数可以获取备用画笔的句柄(HPEN),调用 SelectObject 函数可以将指定的画笔选入设备环境,并返回之前选入设备环境的画笔句柄:
// 定义画笔句柄 HPEN hPen, hPrevPen; // 获取备用画笔的句柄 hPen = GetStockObject(WHITE_PEN); // 将画笔选入设备环境,函数返回之前选入设备环境的画笔的句柄 hPrevPen = SelectObject(hdc, hPen);
GDI 对象使用规则
- 最终应当删除所有由用户创建的 GDI 对象
- 当 GDI 对象被选入一个有效的设备环境时,不可删除它
- 不可删除备用对象(Stock Object)
创建画笔
调用 CreatePen 函数可以创建一个画笔,画笔句柄将作为返回值返回:
HPEN CreatePen( int fnPenStyle, // 画笔样式(决定绘制的是实线、虚线或点线) int nWidth, // 画笔宽度(为 0 时,将设为 1 个像素。虚线或点线只能为 1 个像素,否则将被设为实线) COLORREF crColor // 画笔颜色(COLORREF 值,可以通过 RGB 宏指定) );
调用 CreatePenIndirect 函数可以根据 LOGPEN(逻辑画笔)结构来建立一个画笔,画笔句柄将作为返回值返回:
LOGPEN 结构:
typedef struct tagLOGPEN { UINT lopnStyle; // 画笔样式 POINT lopnWidth; // 画笔宽度(Windows 仅使用 x 字段) COLORREF lopnColor; // 画笔颜色 } LOGPEN, *PLOGPEN;
CreatePenIndirect 函数:
HPEN CreatePenIndirect( CONST LOGPEN *lplgpn // LOGPEN 结构的地址 );
选择画笔
调用 SelectObject 函数,可以将刚刚创建的画笔选入设备环境,并返回之前选入设备环境的画笔句柄:
HGDIOBJ SelectObject( HDC hdc, // 设备环境句柄 HGDIOBJ hgdiobj // GDI 对象句柄(这里指画笔句柄) );
删除画笔
调用 DeleteObject 函数,可以将使用完的画笔删除:
BOOL DeleteObject( HGDIOBJ hObject // GDI 对象句柄(这里指画笔句柄) );
注:不要删除已被选入设备环境的当前画笔
获取创建的画笔
调用 GetObject 函数可以从指定画笔句柄中,得到关于此画笔的 LOGPEN 结构的各个字段的值:
GetObject(hPen, sizeof(LOGPEN), (LPVOID)&logpen);
调用 GetCurrentObject 函数可以获取当前被选入设备环境的画笔句柄:
hPen = GetCurentObject(hdc, OBJ_PEN);
填充空隙
空隙的颜色由设备环境的背景模式和背景颜色所决定,默认的背景模式是 OPAQUE(不透明),即用背景颜色(默认为白色)填充。
调用 SetBkColor 函数可以改变 Windows 填充空隙的背景颜色:
COLORREF SetBkColor( HDC hdc, // 设备环境句柄 COLORREF crColor // 背景颜色值(COLORREF) );
调用 GetBkColor 函数可以得到 Windows 填充空隙的背景颜色,函数将它作为返回值返回:
COLORREF GetBkColor( HDC hdc // 设备环境句柄 );
调用 SetBkMode 函数可以设置背景模式,设置成 TRANSPARENT 可以阻止 Windows 填充空隙:
int SetBkMode( HDC hdc, // 设备环境句柄 int iBkMode // 背景模式,可选 QPAQUE(不透明)和 TRANSPARENT(透明)两种模式 );
绘图模式
二元光栅操作(ROP2,raster operation 2):Windows 绘制直线时,将画笔的像素颜色和目标显示表面的像素颜色按位进行布尔运算
默认情况下,绘图模式是 R2_COPYPEN,像素的简单复制。
- R2_NOTCOPYPEN 像素复制为画笔颜色的反色
- R2_BLACK 总是绘制为黑色
- R2_WHITE 总是绘制为白色
- R2_NOP 不操作
- R2_NOT 将目标颜色取反,来获取绘制颜色
调用 SetROP2 函数可以设置一种新的绘图模式:
int SetROP2( HDC hdc, // 设备环境句柄 int fnDrawMode // 绘图模式(以 R2 为前缀的标志) );
调用 GetROP2 函数可以获取当前的绘图模式,作为返回值返回:
int GetROP2( HDC hdc // 设备环境句柄 );
PENDEMO 示例程序
#include <windows.h> #include <math.h> #define NUMS 1000 #define TWOPI 6.283185307179586476925286766559 LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; static LOGPEN logpen; static HPEN hRedPen; static HPEN hBluePen; static int cxClient, cyClient; POINT apt[NUMS]; int i; switch (message) { case WM_CREATE: logpen.lopnColor = RGB(255, 0, 0); logpen.lopnStyle = PS_DASH; logpen.lopnWidth.x = 1; hRedPen = CreatePenIndirect(&logpen); logpen.lopnColor = RGB(0, 0, 255); logpen.lopnStyle = PS_INSIDEFRAME; logpen.lopnWidth.x = 3; hBluePen = CreatePenIndirect(&logpen); return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); SelectObject(hdc, hRedPen); MoveToEx(hdc, 0, cyClient / 2, NULL); LineTo(hdc, cxClient, cyClient / 2); SelectObject(hdc, hBluePen); for (i = 0; i < NUMS; i++) { apt[i].x = i * cxClient / NUMS; apt[i].y = (int)(( 1 - sin(TWOPI * i / NUMS)) * cyClient / 2); } SetTextAlign(hdc, TA_TOP | TA_RIGHT); TextOut(hdc, cxClient - 12, 12, TEXT("y = sin x"), 12); TextOut(hdc, cxClient / 2 - 12, cyClient / 2 + 12, TEXT("( π, 0 )"), 9); TextOut(hdc, cxClient - 12, cyClient / 2 + 12, TEXT("( 2 π, 0 )"), 11); SetTextAlign(hdc, TA_TOP | TA_LEFT); TextOut(hdc, 12, cyClient / 2 + 12, TEXT("( 0, 0 )"), 9); PolyBezier(hdc, apt, NUMS); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: DeleteObject(hRedPen); DeleteObject(hBluePen); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { LPCTSTR lpszClassName = TEXT("PenDemo"); LPCTSTR lpszWindowName = TEXT("Pen Demo Program"); WNDCLASS wndclass; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hInstance = hInstance; wndclass.lpfnWndProc = WindowProc; wndclass.lpszClassName = lpszClassName; wndclass.lpszMenuName = NULL; wndclass.style = CS_HREDRAW | CS_VREDRAW; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), lpszWindowName, MB_ICONERROR); return 0; } HWND hwnd = CreateWindow( lpszClassName, lpszWindowName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }