文号
t73280
阅读
3346
回复
1
收藏
1
【源码】在Win32中两个实用的PatBlt绘制技术
2015/08/23
IP:山东
【绘制边界拖动轨迹(筛状实心矩形)】

类似“最佳性能”配置下拖动窗口时,目标边界所出现的轨迹。主要用于分割条的拖动处理,提高流畅性。移植自WTL。
使用方法:DrawGhostBar(窗口句柄, 绘制的阴影矩形)
此函数将矩形内的像素按筛子状反色,看上去就好像一个阴影一样。在同一位置再次调用此函数将会再次反色,消除阴影。

使用示例:
case WM_MOUSEMOVE:
    {
        POINTS pts = MAKEPOINTS(lParam); // 获取鼠标指针位置
        RECT rc = { 0 };
        GetClientRect(hWnd, &rc); // 获取客户区大小(用于矩形高度)
        rc.left = pts.x - 10; // 设定矩形宽度
        rc.right = pts.x + 10;
        static RECT oldRc = { 0 };
        DrawGhostBar(hWnd, &oldRc); // 消除旧矩形
        oldRc = rc;
        DrawGhostBar(hWnd, &rc); // 绘制新矩形
    }
    return 0;

效果:

无标题1.png

源代码:
// 创建筛子状画刷
HBRUSH GetHalftoneBrush()
{
    HBRUSH halftoneBrush = NULL;
    // 用筛子状图案填充位块(0101 / 1010)
    WORD grayPattern[8] = { 0 };
    for (int j = 0; j < 8; j++)
        grayPattern[j] = (WORD)(0x5555 << (j & 1));
    // 从位块创建位图
    HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern);
    if (grayBitmap != NULL)
    {
        // 从位图创建图案画刷
        halftoneBrush = CreatePatternBrush(grayBitmap);
        // 删除位图
        DeleteObject(grayBitmap);
    }
    return halftoneBrush;
}
// 绘制边界拖动轨迹
void DrawGhostBar(HWND hWnd, RECT *pRect)
{
    // 把客户区坐标转换为窗口坐标
    RECT rcWnd = { 0 };
    GetWindowRect(hWnd, &rcWnd);
    MapWindowPoints(NULL, hWnd, (LPPOINT)&rcWnd, 2);
    OffsetRect(pRect, -rcWnd.left, -rcWnd.top);
                       
    // 绘制边界拖动轨迹
    HDC hDC = GetWindowDC(hWnd);
    if (hDC)
    {
        // 获取筛子画刷(见函数GetHalftoneBrush)
        HBRUSH hBrush = GetHalftoneBrush();
        if (hBrush != NULL)
        {
            // 使用筛子画刷并保留原画刷
            HBRUSH hBrushOld = (HBRUSH)SelectObject(hDC, hBrush);
            // 按画刷反色像素,绘制矩形
            PatBlt(hDC, pRect->left, pRect->top,
                pRect->right - pRect->left, pRect->bottom - pRect->top, PATINVERT);
            // 恢复原画刷
            SelectObject(hDC, hBrushOld);
            // 删除筛子画刷
            DeleteObject(hBrush);
        }
        ReleaseDC(hWnd, hDC);
    }
}

【绘制(经典的)选择框(筛状空心矩形)】

方法与绘制框架轨迹类似,只要把内部矩形排除就行了。

使用示例:
case WM_MOUSEMOVE:
    {
        POINTS pts = MAKEPOINTS(lParam); // 获取鼠标指针位置
        RECT rc = { 100, 100, pts.x, pts.y }; // 指定选择矩形位置
        static RECT oldRc = { 0 };
        DrawSelectRect(hWnd, &oldRc); // 消除旧矩形
        oldRc = rc;
        DrawSelectRect(hWnd, &rc); // 绘制新矩形
    }
    return 0;

效果:

无标题2.png

源代码:
// 绘制选择框
void DrawSelectRect(HWND hWnd, RECT *pRect)
{
    // 计算内部矩形的大小
    RECT innerRect = *pRect;
    InflateRect(&innerRect,
        2 * (innerRect.left > innerRect.right) - 1,
        2 * (innerRect.top > innerRect.bottom) - 1);
                    
    // 绘制选择框
    HDC hDC = GetDC(hWnd);
    if (hDC)
    {
        // 获取筛子画刷(见函数GetHalftoneBrush)
        HBRUSH hBrush = GetHalftoneBrush();
        if (hBrush != NULL)
        {
            // 使用筛子画刷并保留原画刷
            HBRUSH hBrushOld = (HBRUSH)SelectObject(hDC, hBrush);
            // 排除内部矩形
            ExcludeClipRect(hDC, innerRect.left, innerRect.top,
                innerRect.right, innerRect.bottom);
            // 按画刷反色像素,绘制外部矩形
            PatBlt(hDC, pRect->left, pRect->top,
                pRect->right - pRect->left, pRect->bottom - pRect->top, PATINVERT);
            // 恢复原画刷
            SelectObject(hDC, hBrushOld);
            // 删除筛子画刷
            DeleteObject(hBrush);
        }
        ReleaseDC(hWnd, hDC);
    }
}
[ 修改于11年4个月前 - 2015/08/27 17:09:01 ]
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的