GDI 画笔(9)

使用现有画笔

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 
#include 

#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;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注