ReactOS 图形系统分析(41):画刷子系统 — brush.cpp

ReactOS 图形系统分析(41):画刷子系统 --- brush.cpp

1. 概述

brush.cpp(file:///d:/reactos/win32ss/gdi/ntgdi/brush.cpp) 实现 win32k 内核态(ntgdi)的画刷(BRUSH)子系统 。它负责逻辑画刷对象的创建(纯色 / 影线 / 图案 / DIB 图案)、颜色修改、对象查询与销毁,并把逻辑画刷交给引擎(engbrush.c)实现为已实现画刷(realized brush),最终供填充、描边等绘制操作使用。

本文分析的文件及范围:

文件 作用
win32ss/gdi/ntgdi/brush.cpp BRUSH 类实现、内部工厂、7 个 NtGdi* 入口、Int* 内部接口(共约 580 行)
win32ss/gdi/ntgdi/brush.h BRUSHBODY / EBRUSHOBJ 结构、BR_* 属性标志、EBRUSHOBJ_* 引擎接口声明
win32ss/gdi/ntgdi/brush.hpp BRUSH C++ 类定义(内存分配、Lock 系列、成员函数声明)
win32ss/gdi/eng/engbrush.c 引擎侧实现:EBRUSHOBJ 初始化/更新/实现、影线位图、BRUSHOBJ_* DDI 导出
win32ss/include/ntgdihdl.h BRUSH_ATTR、SC_BB_STOCKOBJ 等句柄表相关定义

1.1 架构图

复制代码
应用层 (gdi32.dll / user32.dll)
    CreateSolidBrush / CreateHatchBrush / CreatePatternBrush / CreateDIBPatternBrush
    SetDCBrushColor / SetBrushAttributes / GetObject / DeleteObject ...
        │  (用户态,gdi32/objects/brush.c 等)
        ▼
系统调用层
    NtGdiCreateSolidBrush          NtGdiCreateHatchBrushInternal
    NtGdiCreatePatternBrushInternal NtGdiCreateDIBBrush
    NtGdiGetObjectBitmapHandle     NtGdiSetBrushAttributes / NtGdiClearBrushAttributes
        │
        ▼
brush.cpp  (逻辑画刷层)
    CreateBrushInternal(flAttrs, crColor, iHatch, hbmPattern, pvClient)
        │  new BRUSH(...)  →  BASEOBJECT + BRUSHBODY
        │  bAllocateBrushAttr()  (非全局画刷:分配进程私有 BRUSH_ATTR)
        │  hInsertObject(owner)  (PUBLIC / POWNED,插入句柄表)
        ▼
BRUSH 逻辑画刷对象(句柄表,GDILoObjType_LO_BRUSH_TYPE)
        │  绘制时(DC 级联)
        ▼
engbrush.c  (引擎实现层)
    EBRUSHOBJ_vInit / EBRUSHOBJ_vInitFromDC / EBRUSHOBJ_vUpdateFromDC
    EBRUSHOBJ_vSetSolidRGBColor  (颜色 → iSolidColor)
    EBRUSHOBJ_bRealizeBrush      (DrvRealizeBrush / EngRealizeBrush)
        │
        ▼
EBRUSHOBJ(已实现画刷:pengbrush 位图 / pvRbrush 驱动私有数据 / psoMask 掩码)
        │
        ▼
绘制引擎:bitblt.c(PatBlt)、fill.c(填充)、line.c(描边)...

1.2 设计动机

  1. 逻辑画刷与已实现画刷分离。 逻辑画刷(BRUSH)只描述"画刷长什么样"(颜色、影线样式、图案位图句柄),与具体输出表面(surface)、调色板(palette)、设备无关。真正可被绘制引擎使用的颜色值(iSolidColor)、图案表面(pengbrush)、掩码(psoMask)必须针对目标表面的像素格式做转换(realization),这部分由 EBRUSHOBJ 承担。这样同一个逻辑画刷可以被多个 DC、多种格式的表面复用,转换只在实际绘制时按需进行(lazy realization)。

  2. BRUSHBODY 与 PEN 共用布局。 brush.h 中明确注释:The layout of this structure is taken from "Windows Graphics Programming" book written by Feng Yuan,且严禁随意修改 。该布局中后半部分(lWidth、eWidth、ulPenStyle、pStyle、jJoin、jEndCap、iBrushStyle、ulStyleSize、ListHead)仅画笔使用,画刷不使用。之所以共用,是为了让 win32k 的句柄表对象管理器(GDIOBJ)以统一方式管理 BRUSH 与 PEN 两种对象------两者的类型 ID 也同属 GDILoObjType_LO_BRUSH_TYPE / GDILoObjType_LO_PEN_TYPE / GDILoObjType_LO_EXTPEN_TYPE,共用同一个释放回调 BRUSH_vDeleteObject(见其断言:三种类型皆允许)。

  3. 全局画刷与进程私有属性分离(BRUSH_ATTR)。 库存对象(stock objects)与系统画刷(syscolor brushes)是全局共享的(BR_IS_GLOBAL),它们的 BrushAttr 内嵌在对象体内;而用户进程通过 NtGdi* 创建的画刷需要每进程私有 的属性,因此 bAllocateBrushAttr() 会从 GDI 池分配一块 BRUSH_ATTR,并通过 vSetObjectAttr() 登记到句柄表,使 gdi32 的 GdiGetHandleUserData 可以按进程访问。NtGdiSetBrushAttributes / NtGdiClearBrushAttributes 的语义正是"把画刷在全局(stock)与私有(user)属性之间切换"。

  4. 影线(hatch)位图预生成。 六种标准影线样式是固定 8×8 的 1bpp 图案,InitBrushImpl() 在系统初始化阶段用 EngCreateBitmap 一次性生成并缓存到 gahsurfHatch[HS_DDI_MAX],之后创建影线画刷时零成本引用。这套做法与 Windows NT 一致(HS_DDI_MAX=6,对应 HS_HORIZONTAL...HS_DIAGCROSS)。

  5. C++ 与 C 双接口。 内核对象层使用 C++ 类(BRUSH 继承 BASEOBJECT),但 win32k 的其余模块(以及 hook 回调)都是 C 代码,因此提供 BRUSH_vDeleteObjectBRUSH_GetObject 等薄 C 包装,把 C 调用映射到 C++ 成员函数。


2. 核心数据结构

2.1 BRUSHBODY ------ 画刷对象体(brush.h)

_BRUSHBODY 是 BRUSH 的"对象体",C++ 侧由 class BRUSH : public BASEOBJECT, protected _BRUSHBODY 继承,C 侧则内嵌为 BRUSH 结构的第二个成员。逐字段说明如下:

c 复制代码
typedef struct _BRUSHBODY
{
    ULONG iHatch;           // 注意:不是画刷样式,而是"影线样式"!
    HBITMAP hbmPattern;     // 图案位图(内核持有的真正图案,如 BITMAP_CopyBitmap 的副本)
    HBITMAP hbmClient;      // 客户端位图句柄(CreatePatternBrush 传入的原始位图 / DIB 的 pvClient)
    ULONG flAttrs;          // 画刷属性标志(BR_IS_SOLID / BR_IS_HATCH / BR_IS_BITMAP / ...)
    ULONG ulBrushUnique;    // 全局唯一序号(InterlockedIncrement 自增,供引擎缓存判断)
    BRUSH_ATTR *pBrushAttr; // 指向"当前激活"的画刷属性(可能指向对象体内或进程池)
    BRUSH_ATTR BrushAttr;   // 内嵌属性(全局画刷使用;私有画刷的"模板")
    POINT ptOrigin;         // 画刷原点(SetBrushOrgEx 设置,图案对齐基准)
    ULONG bCacheGrabbed;    // 缓存是否已被占用(引擎缓存标志)
    COLORREF crBack;        // 背景色
    COLORREF crFore;        // 前景色
    ULONG ulPalTime;        // 调色板时间戳(调色板变化后强制重新实现)
    ULONG ulSurfTime;       // 表面时间戳(表面格式变化后强制重新实现)
    PVOID pvRBrush;         // 驱动私有"已实现画刷"指针(DrvRealizeBrush 返回)
    HDEV hdev;              // 关联的设备句柄(所属 PDEV)
    //DWORD unk054;

    /* 以下字段仅供 PEN 使用 */
    LONG lWidth;            // 画笔宽度
    FLOATOBJ eWidth;        // 几何画笔宽度(浮点)
    ULONG ulPenStyle;       // 画笔样式(PS_SOLID / PS_DASH ...)
    DWORD *pStyle;          // 用户自定义虚线样式数组
    ULONG dwStyleCount;     // 样式数组元素个数
    BYTE jJoin;             // 0x06c 几何宽线的连接样式
    BYTE jEndCap;           // 几何宽线的端点样式
    INT iBrushStyle;        // 0x070 画刷样式
    ULONG ulStyleSize;      // 样式数组大小(字节)
    LIST_ENTRY ListHead;    // 0x080 链表节点(缓存链表挂接)
} BRUSHBODY;

要点:

  • iHatch 语义 :注释特别强调 This is not the brush style, but the hatch style!。即它存的是 HS_HORIZONTAL(0)...HS_DIAGCROSS(5) 影线样式编号,而不是 BS_HATCHED(2) 这类画刷样式。BS_* 样式由 flAttrs 派生(见 cjGetObject 的映射),iHatch 只作为索引使用(ppdev->ahsurf[pbr->iHatch]gahsurfHatch[i])。
  • hbmPattern 与 hbmClient :对于 NtGdiCreatePatternBrushInternal,内核 BITMAP_CopyBitmap 拷贝一份作为 hbmPattern(画刷拥有,析构时删除),而调用者的原始位图作为 hbmClient 保存,供 GetObject 返回 lbHatch = (ULONG_PTR)hbmClient。这是 Win32 语义:GetObject(BS_PATTERN)lbHatch 必须返回创建时传入的位图句柄。
  • ulPalTime / ulSurfTime :调色板或目标表面变化(时间戳更新)后,缓存的已实现画刷失效,需要重新实现。vSetSolidColor 把两者置为 -1 强制失效。
  • pvRBrush :驱动实现画刷的私有数据(BRUSHOBJ_pvAllocRbrush 分配、EngFreeMem 释放)。注意它同时也出现在 EBRUSHOBJ.BrushObject.pvRbrush 中,这里 BRUSHBODY.pvRBrush 是逻辑画刷层面的缓存。

2.2 BRUSH ------ C/C++ 双视图

C 视图(C 模块使用):

c 复制代码
typedef struct _BRUSH
{
    /* 句柄表中所有 GDI 对象的公共头,不可移动 */
    BASEOBJECT BaseObject;
    BRUSHBODY;
} BRUSH, *PBRUSH;

C++ 视图(brush.hpp):

cpp 复制代码
class BRUSH : public BASEOBJECT, protected _BRUSHBODY

BASEOBJECT 提供句柄(hHmgr)、锁(Lock/Unlock/Reference)、句柄表插入(hInsertObject)、对象属性(vSetObjectAttr)等公共设施。_BRUSHBODY 以 protected 继承,保证只有 BRUSH 成员函数能直接访问对象体字段,外部代码通过公开成员函数访问。

2.3 BS_* 画刷样式(wingdi.h,公共 API 层面)

常量 含义 cjGetObject 映射来源
BS_SOLID 0 纯色画刷 BR_IS_SOLID
BS_NULL / BS_HOLLOW 1 空(中空)画刷 BR_IS_NULL
BS_HATCHED 2 影线画刷 BR_IS_HATCH
BS_PATTERN 3 图案画刷(DDB) BR_IS_BITMAP
BS_INDEXED 4 索引色画刷 ---
BS_DIBPATTERN 5 DIB 图案画刷(句柄形式) BR_IS_DIB
BS_DIBPATTERNPT 6 DIB 图案画刷(指针形式) ---
BS_PATTERN8X8 7 8×8 图案画刷 ---
BS_DIBPATTERN8X8 8 8×8 DIB 图案画刷 ---
BS_MONOPATTERN 9 单色图案画刷 ---

BS_* 值并不显式存储,而是由 flAttrs 的 BR_IS_* 位推导(见 2.6 与 BRUSH::cjGetObject)。

2.4 BR_* 画刷属性标志(brush.h)

c 复制代码
#define BR_NEED_FG_CLR      0x00000001  /* 需要前景色 */
#define BR_NEED_BK_CLR      0x00000002  /* 需要背景色 */
#define BR_DITHER_OK        0x00000004  /* 允许颜色抖动(dithering) */
#define BR_IS_SOLID         0x00000010  /* 纯色画刷 */
#define BR_IS_HATCH         0x00000020  /* 影线画刷 */
#define BR_IS_BITMAP        0x00000040  /* DDB 图案画刷 */
#define BR_IS_DIB           0x00000080  /* DIB 图案画刷 */
#define BR_IS_NULL          0x00000100  /* 空/中空画刷 */
#define BR_IS_GLOBAL        0x00000200  /* 库存对象(全局共享) */
#define BR_IS_PEN           0x00000400  /* 画笔 */
#define BR_IS_OLDSTYLEPEN   0x00000800  /* 几何画笔 */
#define BR_IS_DIBPALCOLORS  0x00001000  /* DIB 颜色表为调色板索引(DIB_PAL_COLORS) */
#define BR_IS_DIBPALINDICES 0x00002000  /* DIB 位图直接存设备调色板索引 */
#define BR_IS_DEFAULTSTYLE  0x00004000  /* 默认样式(样式数组无需释放) */
#define BR_IS_MASKING       0x00008000  /* 图案位图用作透明掩码 */
#define BR_IS_INSIDEFRAME   0x00010000  /* 内框画刷 */
#define BR_CACHED_ENGINE    0x00040000  /* 引擎已实现画刷已缓存 */
#define BR_CACHED_IS_SOLID  0x80000000  /* 缓存标记:纯色 */

语义要点:

  • 互斥的"种类"位 :BR_IS_SOLID / BR_IS_HATCH / BR_IS_BITMAP / BR_IS_DIB / BR_IS_NULL 五者构成画刷种类,cjGetObject 按固定顺序判断(SOLID → HATCH → DIB → BITMAP → NULL)。
  • 组合位BR_IS_BITMAP | BR_IS_DIB 表示"来自 DIB 的图案画刷"(NtGdiCreateDIBBrush 设置);BR_IS_DIBPALCOLORS / BR_IS_DIBPALINDICES 是 DIB 颜色表用法的补充标记;BR_IS_NULL 在 DIB_PAL_INDICES 分支被"hack"式附加(源码注释 FIXME: This makes tests pass)。
  • BR_IS_GLOBAL:决定画刷是否内嵌属性即可(全局)还是必须分配进程私有 BRUSH_ATTR(非全局),也决定句柄表 owner 是 PUBLIC 还是 POWNED。

2.5 BRUSH_ATTR ------ 画刷属性(ntgdihdl.h)

c 复制代码
typedef struct _BRUSH_ATTR /* Used with pen too. */
{
    FLONG    AttrFlags;
    COLORREF lbColor;
} BRUSH_ATTR, *PBRUSH_ATTR;

只有两个字段:属性标志(AttrFlags)和颜色(lbColor,注意名称与 LOGBRUSH 的 lbColor 一致,因为它正是 GetObject 要回填的颜色来源)。BRUSH 通过 pBrushAttr 指针解引用当前激活的属性:

  • 全局画刷:pBrushAttr = &BrushAttr(对象体内)。
  • 进程私有画刷:bAllocateBrushAttr() 从 GDI 池分配一块,拷贝内嵌属性内容,pBrushAttr 指向池中块,并 vSetObjectAttr(pBrushAttr) 挂到句柄表(gdi32 可通过 GdiGetHandleUserData 直接读写)。
  • 释放时:析构函数 / vReleaseAttribute()GdiPoolFree(GetBrushAttrPool(), ...) 归还池。

GetBrushAttrPool() 声明在 brush.hpp 末尾,标有 /* HACK! */------它返回进程 GDI 池(PGDI_POOL),用于分配/释放用户模式可见的画刷属性。

2.6 EBRUSHOBJ ------ 已实现画刷对象(brush.h,eng 侧使用)

c 复制代码
typedef struct _EBRUSHOBJ
{
    BRUSHOBJ    BrushObject;        // 对外暴露的 DDI 画刷对象(驱动可见)
    COLORREF    crRealize;          // 本次实现的 RGB 颜色
    ULONG       ulRGBColor;         // 当前 RGB 颜色(BRUSHOBJ_ulGetBrushColor 返回)
    PVOID       pengbrush;          // 引擎实现的图案表面(SURFACE*,1bpp/目标格式)
    ULONG       ulSurfPalTime;      // 表面调色板时间戳
    ULONG       ulDCPalTime;        // DC 调色板时间戳
    COLORREF    crCurrentText;      // 当前前景色(影线画刷前景)
    COLORREF    crCurrentBack;      // 当前背景色
    COLORADJUSTMENT *pca;           // 颜色调整(ColorAdjustment)
    SURFACE *   psurfTrg;           // 目标表面(可为默认位图表面)
    PALETTE *   ppalSurf;           // 目标表面调色板(引用计数 +1)
    PALETTE *   ppalDC;             // DC 调色板(引用计数 +1,缺省 gppalDefault)
    PALETTE *   ppalDIB;            // DIB 画刷专用调色板(FixupDIBBrushPalette 产物)
    PBRUSH      pbrush;             // 回指逻辑画刷
    FLONG       flattrs;            // 逻辑画刷 flAttrs 的快照(BR_IS_* 位)
    DWORD       ulUnique;           // 逻辑画刷 ulBrushUnique 快照
    SURFOBJ *   psoMask;            // 掩码表面(影线画刷 = 影线位图;1bpp)
} EBRUSHOBJ, *PEBRUSHOBJ;

设计要点:

  • EBRUSHOBJ 是"逻辑画刷 × DC × 目标表面"三者的交汇 。每个 DC 内嵌两个 EBRUSHOBJ(pdc->eboFill 填充画刷、pdc->eboLine 线条画刷,见 dcobjs.c),当 SelectObject 换画刷或颜色变化时通过 EBRUSHOBJ_vUpdateFromDC 重新初始化。
  • BRUSHOBJ 是 DDI 契约BrushObject 成员即驱动看到的 BRUSHOBJ(含 iSolidColor、pvRbrush 等),CONTAINING_RECORD(pbo, EBRUSHOBJ, BrushObject) 宏(如 BRUSHOBJ_pvGetRbrush、BRUSHOBJ_ulGetBrushColor 中)用于从驱动传回的 BRUSHOBJ* 反推出 EBRUSHOBJ*。
  • pengbrush 与 pvRbrush 双轨EBRUSHOBJ_bRealizeBrush(pebo, bCallDriver) 中,bCallDriver=TRUE 时调用显示驱动 DrvRealizeBrush(产物在 BrushObject.pvRbrush,走 BRUSHOBJ_pvGetRbrush 惰性触发);bCallDriver=FALSE 时调用引擎自带的 EngRealizeBrush(产物在 pengbrush,走 EBRUSHOBJ_pvGetEngBrush / EBRUSHOBJ_psoPattern 惰性触发)。
  • iSolidColor 三态EBRUSHOBJ_vInit 中------BR_IS_NULL → 0;BR_IS_SOLID → 经 EBRUSHOBJ_vSetSolidRGBColor 转换为目标格式索引;图案画刷 → 0xFFFFFFFF(表示"非纯色,需要图案表面")。

2.7 BRUSH 类(brush.hpp)------ 内存分配与锁

cpp 复制代码
class BRUSH : public BASEOBJECT, protected _BRUSHBODY
{
public:
    void* operator new(size_t cjSize) throw()
    {
        return ExAllocatePoolWithTag(PagedPool, cjSize, GDITAG_HMGR_BRUSH_TYPE);
        //return BASEOBJECT::pvAllocate(GDIObjType_BRUSH_TYPE, cjSize);
    }
    void operator delete(void *pvObject)
    {
        /// HACK! better would be to extract the exact object type's tag
        ExFreePool(pvObject);
    }
    ...
};
  • operator new :从**分页池(PagedPool)**以 GDITAG_HMGR_BRUSH_TYPE 标签分配对象内存(注释掉的旧方案是走 BASEOBJECT 分配器)。
  • operator deleteExFreePool 直接释放(注释承认"无法精确提取对象类型的标签"是个 HACK)。
  • 三种锁 (全部基于 BASEOBJECT::LockShared,类型强制为 GDILoObjType_LO_BRUSH_TYPE):
    • LockForRead(hbr)OWNER::PUBLIC------要求对象为公共(全局)所有,常用于只读查询。
    • LockForWrite(hbr)OWNER::POWNED------要求对象为进程私有,用于修改。
    • LockAny(hbr)OWNER::NONE------不限制所有权,用于无法预先判断归属的场合(如 IntGdiSetSolidBrushColorNtGdiSetBrushAttributes)。
  • 锁函数失败返回 NULL(句柄无效或类型不符),调用方必须判空。

3. 构造函数与析构函数

3.1 BRUSH::BRUSH(构造函数)

cpp 复制代码
BRUSH::BRUSH(
    _In_ FLONG flAttrs,
    _In_ COLORREF crColor,
    _In_ ULONG iHatch,
    _In_opt_ HBITMAP hbmPattern,
    _In_opt_ PVOID pvClient,
    _In_ GDILOOBJTYPE loobjtype = GDILoObjType_LO_BRUSH_TYPE)
    : BASEOBJECT(loobjtype)
{
    static ULONG ulGlobalBrushUnique = 0;
    this->ulBrushUnique = InterlockedIncrementUL(&ulGlobalBrushUnique);
    ...
}

实现流程:

  1. InterlockedIncrementUL 从静态计数器取全局唯一序号存入 ulBrushUnique(无锁原子自增,供引擎侧缓存与 EBRUSHOBJ.ulUnique 比对)。
  2. pBrushAttr = &this->BrushAttr------默认使用内嵌属性(后续 bAllocateBrushAttr 可改指向池中块)。
  3. 直接赋值参数:flAttrsiHatchhbmPatternhbmClient = (HBITMAP)pvClientpBrushAttr->lbColor = crColor
  4. 零初始化其余字段:ptOrigin=(0,0)bCacheGrabbed=FALSEcrBack=0crFore=0ulPalTime=0ulSurfTime=0pvRBrush=NULLhdev=NULL
  5. 顺带初始化画笔字段 dwStyleCount=0pStyle=NULL------注释说明"理论上只在 PEN 构造函数做,但析构函数需要",保证析构时 pStyle 判定安全。

注意:构造函数 对 hbmPattern 增加引用计数------约定"画刷接管位图所有权"(CreateBrushInternal 注释:brush takes ownership of the bitmap),失败路径由调用方负责清理。

3.2 BRUSH::~BRUSH(析构函数)

cpp 复制代码
BRUSH::~BRUSH(VOID)
{
    if (this->pBrushAttr != &this->BrushAttr)
    {
        GdiPoolFree(GetBrushAttrPool(), this->pBrushAttr);   // 释放进程私有属性
    }
    if (this->hbmPattern != NULL && GreIsHandleValid(this->hbmPattern))
    {
        GreSetBitmapOwner(this->hbmPattern, BASEOBJECT::OWNER::POWNED);
        GreDeleteObject(this->hbmPattern);                   // 删除图案位图
    }
    if ((this->pStyle != NULL) && !(this->flAttrs & BR_IS_DEFAULTSTYLE))
    {
        ExFreePoolWithTag(this->pStyle, GDITAG_PENSTYLE);    // 释放画笔样式数组
    }
}

清理三件事:

  1. 进程私有 BRUSH_ATTR :若 pBrushAttr 指向池中块(而非内嵌),归还给进程 GDI 池。
  2. 图案位图 :若 hbmPattern 有效,先 GreSetBitmapOwner(..., POWNED)GreDeleteObject。注释提示:位图可能已在 GDI 清理流程中先行删除,所以必须 GreIsHandleValid 校验。GreDeleteObject 会真正释放位图(此时画刷是最后一个引用者)。
  3. 画笔样式数组 :仅当 pStyle 非空且非默认样式时释放(BR_IS_DEFAULTSTYLE 表示样式是静态/共享的,不能释放)。

3.3 BRUSH::vDeleteObject(静态)与 C 包装 BRUSH_vDeleteObject

cpp 复制代码
VOID BRUSH::vDeleteObject(_In_ PVOID pvObject)
{
    PBRUSH pbr = static_cast<PBRUSH>(pvObject);
    NT_ASSERT((GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_BRUSH_TYPE) ||
              (GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_PEN_TYPE) ||
              (GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_EXTPEN_TYPE));
    delete pbr;
}
  • 断言句柄表中对象类型必须是 BRUSH / PEN / EXTPEN 三者之一------这正是"BRUSHBODY 与 PEN 共用布局、共用释放回调"的体现。
  • delete pbr 触发 ~BRUSH()(虚析构经由 BASEOBJECT 释放机制)。
  • C 包装:
cpp 复制代码
VOID NTAPI BRUSH_vDeleteObject(PVOID pvObject)
{
    BRUSH::vDeleteObject(pvObject);
}

该函数注册为 GDI 对象类型 BRUSH 的析构回调(GDIOBJ 句柄表释放对象时调用)。

3.4 BRUSH::bAllocateBrushAttr ------ 分配进程私有属性

cpp 复制代码
BOOL BRUSH::bAllocateBrushAttr(VOID)
{
    NT_ASSERT(this->pBrushAttr == &this->BrushAttr);
    pBrushAttr = static_cast<PBRUSH_ATTR>(GdiPoolAllocate(GetBrushAttrPool()));
    if (pBrushAttr == NULL) { ERR("Could not allocate brush attr\n"); return FALSE; }
    this->pBrushAttr = pBrushAttr;
    *this->pBrushAttr = this->BrushAttr;   // 拷贝内嵌属性作为初始值
    vSetObjectAttr(pBrushAttr);            // 登记到句柄表(gdi32 可见)
    return TRUE;
}

要点:

  • 前置断言要求当前仍使用内嵌属性(防止重复分配泄漏)。
  • 从进程 GDI 池分配后整体拷贝内嵌属性(AttrFlags + lbColor),保证私有属性初始值与全局一致。
  • vSetObjectAttr(pBrushAttr) 把池地址写入句柄表条目,使 gdi32 的 GdiGetHandleUserData 能拿到它(这就是 win32k 与 user32 共享画刷属性的通道)。
  • 返回 FALSE 时调用方(CreateBrushInternal)会删除画刷对象。

3.5 BRUSH::vReleaseAttribute ------ 释放私有属性回到内嵌

cpp 复制代码
VOID BRUSH::vReleaseAttribute(VOID)
{
    if (this->pBrushAttr != &this->BrushAttr)
    {
        this->BrushAttr = *this->pBrushAttr;   // 回写内嵌
        GdiPoolFree(GetBrushAttrPool(), this->pBrushAttr);
        this->pBrushAttr = &this->BrushAttr;
    }
}

与 bAllocateBrushAttr 相反:把私有属性当前值回写到内嵌 BrushAttr,归还池块,指针复位。这是 NtGdiSetBrushAttributes(hbr, SC_BB_STOCKOBJ) 的核心动作------画刷转为"库存/全局"语义时放弃私有属性。


4. 成员函数展开

4.1 BRUSH::vSetSolidColor

cpp 复制代码
VOID BRUSH::vSetSolidColor(_In_ COLORREF crColor)
{
    NT_ASSERT(this->flAttrs & BR_IS_SOLID);        // 只能用于纯色画刷
    this->pBrushAttr->lbColor = crColor & 0xFFFFFF; // 屏蔽高位(COLORREF 低 24 位为 RGB)
    this->ulPalTime = -1;                           // 调色板时间戳失效
    this->ulSurfTime = -1;                          // 表面时间戳失效
}
  • 断言画刷必须是 BR_IS_SOLID(否则修改颜色无意义)。
  • 颜色写入当前激活的属性pBrushAttr,可能是进程私有块)。
  • crColor & 0xFFFFFF 确保只保留 RGB 分量。
  • ulPalTime = ulSurfTime = -1 强制下一次实现(realize)时忽略调色板/表面缓存------颜色变了,旧的已实现画刷作废。

调用者:IntGdiSetSolidBrushColor(见 6.4)、IntSetSysColors(系统颜色变化时批量刷新系统画刷)。

4.2 BRUSH::hbmGetBitmapHandle

cpp 复制代码
HBITMAP BRUSH::hbmGetBitmapHandle(_Out_ PUINT puUsage) const
{
    *puUsage = (this->flAttrs & BR_IS_DIBPALCOLORS) ? DIB_PAL_COLORS :
               (this->flAttrs & BR_IS_DIBPALINDICES) ? DIB_PAL_INDICES :
               DIB_RGB_COLORS;
    return this->hbmPattern;
}
  • 按属性位推导 DIB 颜色用法:BR_IS_DIBPALCOLORS → DIB_PAL_COLORS(1)BR_IS_DIBPALINDICES → DIB_PAL_INDICES(2)、否则 DIB_RGB_COLORS(0)
  • 返回 hbmPattern(内核持有的图案位图)。注意常量定义位置:DIB_PAL_COLORS 在 wingdi.h(值 0),DIB_PAL_INDICES 在 ntgdityp.h(值 2,ReactOS 扩展),DIB_PAL_BRUSHHACK 在 dib.h(值 3,内部使用)。
  • NtGdiGetObjectBitmapHandle(见 6.10)调用,该 Nt 服务是 gdi32 实现 GetObject 对 BS_PATTERN/BS_DIBPATTERN 画刷返回位图句柄的底层。

4.3 BRUSH::cjGetObject ------ 填充 LOGBRUSH

cpp 复制代码
UINT BRUSH::cjGetObject(_In_ UINT cjSize, _Out_bytecap_(cjSize) PLOGBRUSH plb) const
{
    if (plb == NULL) return sizeof(LOGBRUSH);   // 只询问大小
    if (cjSize == 0) return 0;                  // 缓冲区非法
    plb->lbColor = this->BrushAttr.lbColor;     // 注意:总是内嵌属性的颜色
    if (this->flAttrs & BR_IS_SOLID)      { plb->lbStyle = BS_SOLID;    plb->lbHatch = 0; }
    else if (this->flAttrs & BR_IS_HATCH) { plb->lbStyle = BS_HATCHED;  plb->lbHatch = this->iHatch; }
    else if (this->flAttrs & BR_IS_DIB)   { plb->lbStyle = BS_DIBPATTERN; plb->lbHatch = (ULONG_PTR)this->hbmClient; }
    else if (this->flAttrs & BR_IS_BITMAP){ plb->lbStyle = BS_PATTERN;  plb->lbHatch = (ULONG_PTR)this->hbmClient; }
    else if (this->flAttrs & BR_IS_NULL)  { plb->lbStyle = BS_NULL;     plb->lbHatch = 0; }
    else { NT_ASSERT(FALSE); }
    return sizeof(LOGBRUSH);
}

规则与细节:

  • 两段式查询 :plbNULL 时只返回所需字节数(GetObject 常见用法:先查大小再填数据);cjSize0 视为非法返回 0;否则必须至少有 LOGBRUSH 大小。
  • 样式映射:严格按 SOLID → HATCH → DIB → BITMAP → NULL 优先级。注意 DIB 在 BITMAP 之前判断------因为 DIB 画刷同时带 BR_IS_BITMAP | BR_IS_DIB 两个位,必须先判 DIB 才能返回 BS_DIBPATTERN。
  • lbHatch 语义随样式不同
    • BS_HATCHED:填影线样式号 iHatch(0~5)。
    • BS_PATTERN / BS_DIBPATTERN:填客户端位图句柄 (hbmClient)------这正是 Win32 GetObject 对图案画刷返回"创建时传入的位图"的语义,而内核副本 hbmPattern 对用户不可见。
  • lbColor 来源 :取 this->BrushAttr.lbColor(内嵌属性),而非 pBrushAttr------对 stock 对象两者一致;对私有画刷,内嵌属性在 bAllocateBrushAttr 时已拷贝过初始值,但注意修改颜色走的是 pBrushAttr(vSetSolidColor),因此对私有画刷 GetObject 可能返回旧颜色------这是当前实现的已知行为(以源码为准)。
  • C 包装 BRUSH_GetObject(FASTCALL)直接转发:
cpp 复制代码
INT FASTCALL BRUSH_GetObject(PBRUSH pbr, INT cjBuffer, LPLOGBRUSH plbBuffer)
{
    return pbr->cjGetObject(cjBuffer, plbBuffer);
}

gdi32 GetObject 对画刷的调用链:NtGdiGetObject → win32k 按对象类型分发 → BRUSH_GetObject


5. 内部工厂 CreateBrushInternal(static)

cpp 复制代码
static HBRUSH
CreateBrushInternal(
    _In_ ULONG flAttrs,
    _In_ COLORREF crColor,
    _In_ ULONG iHatch,
    _In_opt_ HBITMAP hbmPattern,
    _In_opt_ PVOID pvClient)
{
    BASEOBJECT::OWNER owner;
    PBRUSH pbr;
    HBRUSH hbr;

    NT_ASSERT(((flAttrs & BR_IS_BITMAP) == 0) || (hbmPattern != NULL));
    ...
}

流程(所有创建入口的公共路径):

  1. 参数断言:带 BR_IS_BITMAP 标志必须同时提供非空 hbmPattern。
  2. 分配对象pbr = new BRUSH(flAttrs, crColor, iHatch, hbmPattern, pvClient)。若失败,画刷未接管位图所有权,由本函数负责归还:GreSetBitmapOwner(hbmPattern, POWNED); GreDeleteObject(hbmPattern); 返回 NULL。(注释:brush takes ownership of the bitmap。)
  3. 私有属性 :若非 BR_IS_GLOBAL,调用 pbr->bAllocateBrushAttr();失败则 delete pbr 返回 NULL(此时析构函数会清理位图------所有权已转移)。
  4. owner 决策BR_IS_GLOBAL ? PUBLIC : POWNED。PUBLIC 对象所有进程可见、不随进程退出销毁(库存对象);POWNED 对象归创建进程所有,进程退出时自动清理。
  5. 插入句柄表hbr = pbr->hInsertObject(owner);失败 delete pbr 返回 NULL。
  6. 解锁pbr->vUnlock()(hInsertObject 内部已加锁,插入完成解锁)返回 hbr。

所有权规则汇总:

路径 位图所有权
成功 画刷持有(析构时 GreDeleteObject)
new 失败 CreateBrushInternal 直接删除(此时所有权尚未移交)
bAllocateBrushAttr / hInsertObject 失败 delete pbr → 析构函数删除(所有权已移交)
NtGdiCreatePatternBrushInternal 中 BITMAP_CopyBitmap 失败 返回 NULL,不影响原图

6. 全部导出函数逐一展开

6.1 BRUSH_vDeleteObject(C 释放回调)

  • 签名VOID NTAPI BRUSH_vDeleteObject(PVOID pvObject)
  • 作用:GDI 句柄表释放 BRUSH 类型对象时的回调。
  • 实现 :转发给 BRUSH::vDeleteObject(见 3.3),即 delete pbr
  • 使用方式 :注册于 GDI 对象类型表(GDIOBJ),DeleteObject/进程清理/句柄表回收时由内核调用;外部模块一般不直接调用。
  • 注意:被删对象类型须为 BRUSH/PEN/EXTPEN 之一(断言);图案位图与私有属性的清理都在析构函数内完成。

6.2 BRUSH_GetObject

  • 签名INT FASTCALL BRUSH_GetObject(PBRUSH GdiObject, INT Count, LPLOGBRUSH Buffer)
  • 作用:GetObject 的画刷分支------把逻辑画刷导出为 LOGBRUSH。
  • 实现 :一行转发 pbr->cjGetObject(cjBuffer, plbBuffer)(见 4.3)。
  • 使用方式 :由 NtGdiGetObject 按类型分发调用;gdi32 的 GetObject(hbr, ...) 最终落到这里。
  • 注意:Count 语义是"缓冲区字节数";返回值为实际写入字节数或所需字节数(Buffer==NULL 时)。

6.3 IntGdiCreateNullBrush

  • 签名HBRUSH NTAPI IntGdiCreateNullBrush(VOID)
  • 作用:创建空(中空)画刷------填充时不产生任何像素。
  • 实现return CreateBrushInternal(BR_IS_NULL | BR_IS_GLOBAL, 0, 0, NULL, NULL);------种类位 BR_IS_NULL,全局对象,无颜色无影线无图案。
  • 使用方式 :win32k 内部(如 stockobj.c 创建库存 NULL_BRUSH:StockObjects[NULL_BRUSH] = IntGdiCreateNullBrush();)。
  • 注意 :由于带 BR_IS_GLOBAL,不分配私有属性,owner=PUBLIC;EBRUSHOBJ_vInit 遇到 BR_IS_NULL 直接 iSolidColor = 0 且不做任何颜色转换。

6.4 IntGdiCreateSolidBrush

  • 签名HBRUSH APIENTRY IntGdiCreateSolidBrush(COLORREF crColor)
  • 作用:创建全局纯色画刷。
  • 实现CreateBrushInternal(BR_IS_SOLID | BR_IS_GLOBAL, crColor, 0, NULL, NULL)
  • 使用方式 :win32k 内部高频接口------库存对象(stockobj.c 的 WHITE_BRUSH/LTGRAY_BRUSH/GRAY_BRUSH/DKGRAY_BRUSH/BLACK_BRUSH/DC_BRUSH)、系统颜色画刷(CreateSysColorObjectsgpsi->ahbrSystem[i] = IntGdiCreateSolidBrush(SysColors[i]))、ntuser 菜单(menu.c:IntGdiCreateSolidBrush(color))等。
  • 注意:与 NtGdiCreateSolidBrush 的差异仅在 BR_IS_GLOBAL------内部接口创建"全局共享"画刷,系统调用入口创建"进程私有"画刷。

6.5 IntGdiCreatePatternBrush

  • 签名HBRUSH NTAPI IntGdiCreatePatternBrush(HBITMAP hbmPattern)
  • 作用:用已有位图创建全局图案画刷(BS_PATTERN)。
  • 实现
    1. NT_ASSERT(hbmPattern != NULL)
    2. GreSetBitmapOwner(hbmPattern, BASEOBJECT::OWNER::PUBLIC)------把位图改为公共所有(因为画刷是全局的)。
    3. CreateBrushInternal(BR_IS_BITMAP | BR_IS_GLOBAL, 0, 0, hbmPattern, NULL)
  • 使用方式 :ntuser.c 创建灰色画刷:gpsi->hbrGray = IntGdiCreatePatternBrush(hPattern55AABitmap)
  • 注意:位图句柄直接引用(不拷贝);pvClient=NULL 所以 GetObject 的 lbHatch 返回 0。

6.6 IntGdiSetSolidBrushColor

  • 签名VOID NTAPI IntGdiSetSolidBrushColor(_In_ HBRUSH hbr, _In_ COLORREF crColor)
  • 作用:修改纯色画刷颜色(内核内部接口)。
  • 实现
    1. pbr = BRUSH::LockAny(hbr)(不限制 owner,兼容全局/私有画刷);失败 ERR 返回。
    2. pbr->vSetSolidColor(crColor)(断言 BR_IS_SOLID,写 pBrushAttr->lbColor,失效时间戳)。
    3. pbr->vUnlock()
  • 使用方式IntSetSysColors(stockobj.c)------系统颜色变更时刷新 gpsi->ahbrSystem[Element] 的颜色。
  • 注意 :与 gdi32 的 SetDCBrushColor(DC 层颜色)不同------这是"改画刷对象本身颜色"的接口。DC 的 DC_BRUSH 颜色走 IntSetDCBrushColor(见第 9 节链路)。

6.7 NtGdiCreateSolidBrush

  • 签名__kernel_entry HBRUSH APIENTRY NtGdiCreateSolidBrush(_In_ COLORREF crColor, _In_opt_ HBRUSH hbr)
  • 作用:CreateSolidBrush 的系统调用入口(gdi32 转发)。
  • 实现
    1. 若 hbr 非空:WARN("hbr is not supported, ignoring\n")------第二参数(Windows 用于缓存/复用)未实现,忽略。
    2. return CreateBrushInternal(BR_IS_SOLID, crColor, 0, NULL, NULL);------不带 BR_IS_GLOBAL,即进程私有画刷(POWNED)。
  • 使用方式 :应用 CreateSolidBrush → gdi32 → NtGdiCreateSolidBrush。
  • 注意:与 IntGdiCreateSolidBrush 的关键区别就是 BR_IS_GLOBAL 位;私有画刷会走 bAllocateBrushAttr 分配进程私有属性,GetObject 可见。

6.8 NtGdiCreateHatchBrushInternal

  • 签名__kernel_entry HBRUSH APIENTRY NtGdiCreateHatchBrushInternal(_In_ ULONG iHatch, _In_ COLORREF crColor, _In_ BOOL bPen)
  • 作用:CreateHatchBrush 的系统调用入口。
  • 实现
    1. bPen 非 0:WARN("bPen is not supported, ignoring\n")
    2. 影线号归一化
      • iHatch < HS_DDI_MAX(6)flAttr = BR_IS_HATCH(真正的影线画刷,0...5 对应六种标准样式)。
      • iHatch < HS_API_MAX(12)flAttr = BR_IS_SOLID(6...11 是"伪影线"------Windows 把 HS_API 范围内的编号按纯色处理,以兼容旧应用)。
      • 其余 → ERR("Invalid iHatch: %lu\n"),返回 NULL。
    3. CreateBrushInternal(flAttr, crColor, iHatch, NULL, NULL)------注意 iHatch 原样传入并保存在 BRUSHBODY.iHatch。
  • 使用方式CreateHatchBrush(HS_BDIAGONAL, RGB(...))
  • 注意 :HS_DDI_MAX=6 定义在 winddi.h,HS_API_MAX=12 定义在 wingdi.h;影线画刷的图案不在此处创建------绘制时 EBRUSHOBJ 层从 ppdev->ahsurf[iHatch](每 PDEV 的影线位图缓存,源自 InitBrushImpl 的 gahsurfHatch)取用。

6.9 NtGdiCreatePatternBrushInternal

  • 签名__kernel_entry HBRUSH APIENTRY NtGdiCreatePatternBrushInternal(_In_ HBITMAP hbmClient, _In_ BOOL bPen, _In_ BOOL b8X8)
  • 作用:CreatePatternBrush 的系统调用入口。
  • 实现
    1. b8X8 / bPen 非 0:WARN 忽略(8×8 优化与画笔变体未实现)。
    2. hbmPattern = BITMAP_CopyBitmap(hbmClient)------深拷贝用户位图,内核持有副本(防止用户随后修改/删除原图影响画刷)。
    3. 失败:ERR 返回 NULL。
    4. CreateBrushInternal(BR_IS_BITMAP, 0, 0, hbmPattern, hbmClient)------hbmClient 作为 pvClient 保存(GetObject 返回原句柄),失败时内部会删除 hbmPattern 副本。
  • 使用方式CreatePatternBrush(hbm)
  • 注意 :与 IntGdiCreatePatternBrush 的区别:系统调用入口拷贝 位图且画刷为进程私有(无 BR_IS_GLOBAL);内部接口直接引用公共位图。两处 hbmClient/hbmPattern 的区分体现了"用户句柄 ↔ 内核句柄"隔离。

6.10 NtGdiCreateDIBBrush

  • 签名__kernel_entry HBRUSH APIENTRY NtGdiCreateDIBBrush(_In_reads_bytes_(cj) PVOID pv, _In_ FLONG uUsage, _In_ UINT cj, _In_ BOOL b8X8, _In_ BOOL bPen, _In_ PVOID pvClient)
  • 作用:CreateDIBPatternBrushPt / CreateDIBPatternBrush 的系统调用入口------由打包 DIB(BITMAPINFO + 像素)创建图案画刷。
  • 实现流程
    1. b8X8 / bPen:WARN 忽略。
    2. 参数校验uUsage > DIB_PAL_INDICES(2)ERR + EngSetLastError(ERROR_INVALID_PARAMETER) 返回 NULL(uUsage 只允许 DIB_RGB_COLORS=0 / DIB_PAL_COLORS=1 / DIB_PAL_INDICES=2)。
    3. 安全拷贝用户内存ExAllocatePoolWithTag(PagedPool, cj, GDITAG_TEMP) 分配临时缓冲;_SEH2_TRY { ProbeForRead(pv, cj, 1); RtlCopyMemory(...); }------对用户指针做探测读取,异常转入 cleanup。
    4. 属性组合flAttrs = BR_IS_BITMAP | BR_IS_DIB
      • uUsage == DIB_PAL_COLORS:追加 BR_IS_DIBPALCOLORS,并把 uUsage 改成内部专用值 DIB_PAL_BRUSHHACK(3)(dib.h)------见注释:用 BRUSHHACK 创建"特殊"调色板(后续 FixupDIBBrushPalette 依赖)。
      • uUsage == DIB_PAL_INDICES:追加 BR_IS_DIBPALINDICES,并追加 BR_IS_NULL (源码注释 FIXME: This makes tests pass, but needs investigation.------使 GetObject 表现为 BS_NULL 的兼容性 hack)。
    5. hbm = GreCreateDIBitmapFromPackedDIB(pvPackedDIB, cj, uUsage) 从打包 DIB 构造内核位图;失败 goto cleanup。
    6. CreateBrushInternal(flAttrs, 0, 0, hbm, pvClient)(失败时内部删除 hbm)。
    7. cleanup:ExFreePoolWithTag(pvPackedDIB, GDITAG_TEMP) 释放临时缓冲,返回 hbr。
  • 使用方式CreateDIBPatternBrushPt(pvPackedDIB, DIB_RGB_COLORS)
  • 注意:pvClient 是"客户端"指针/句柄原样保存(不探测其内容,仅作为 GetObject 回填值);DIB 位图在 EBRUSHOBJ_bRealizeBrush 实现时才结合 DC 调色板做最终转换。

6.11 NtGdiGetObjectBitmapHandle

  • 签名__kernel_entry HBITMAP APIENTRY NtGdiGetObjectBitmapHandle(_In_ HBRUSH hbr, _Out_ UINT *piUsage)
  • 作用:取图案画刷的位图句柄与颜色用法。
  • 实现
    1. pbr = BRUSH::LockForRead(hbr)(要求 PUBLIC 所有权);失败 ERR 返回 NULL。
    2. hbm = pbr->hbmGetBitmapHandle(&uUsage)
    3. pbr->vUnlock()
    4. _SEH2_TRY { ProbeForWrite(piUsage, sizeof(*piUsage), 1); *piUsage = uUsage; }------写回用户缓冲,异常时置 hbm=NULL。
    5. 返回 hbm。
  • 使用方式:gdi32 GetObject 对图案画刷(BS_PATTERN / BS_DIBPATTERN)取 lbHatch 位图句柄的底层服务(gdi32 侧无法直接读内核句柄,需要此 Nt 服务)。
  • 注意 :返回的是内核持有的图案位图(hbmPattern),而 GetObject 最终回填给应用的是 hbmClient------语义转换发生在 gdi32 层。

6.12 NtGdiSetBrushAttributes

  • 签名__kernel_entry HBRUSH APIENTRY NtGdiSetBrushAttributes(_In_ HBRUSH hbr, _In_ DWORD dwFlags)
  • 作用:把画刷转为库存(stock)语义。
  • 实现
    1. 仅支持 SC_BB_STOCKOBJ(1) 标志(ntgdihdl.h);无该位直接返回 NULL。
    2. GDIOBJ_ConvertToStockObj((HGDIOBJ*)&hbr)------句柄表层面把画刷条目转换为 stock 对象(成功后 hbr 被改写)。失败返回 NULL。
    3. 成功:pbr = BRUSH::LockAny(hbr)pbr->vReleaseAttribute()(放弃进程私有属性,回写内嵌,见 3.5);vUnlock();返回 hbr。
  • 使用方式 :gdi32 SetBrushAttributes(hbr, SC_BB_STOCKOBJ)(brush.c,校验 dwFlags & ~SC_BB_STOCKOBJ 后调用)。应用层直接调用者为数极少,主要供 COM/OLE 与兼容性场景。
  • 注意:转换后画刷变为全局共享,进程退出不再销毁它;属性回写内嵌后 GetObject 仍能读到最新颜色(vReleaseAttribute 先回写再释放)。

6.13 NtGdiClearBrushAttributes

  • 签名__kernel_entry HBRUSH APIENTRY NtGdiClearBrushAttributes(_In_ HBRUSH hbr, _In_ DWORD dwFlags)
  • 作用:把库存画刷转回进程私有语义(SetBrushAttributes 的逆操作)。
  • 实现
    1. 仅支持 SC_BB_STOCKOBJ;否则返回 NULL。
    2. GDIOBJ_ConvertFromStockObj((HGDIOBJ*)&hbr)------句柄表条目从 stock 转普通(成功后 hbr 被改写)。失败返回 NULL。
    3. 成功:LockAnybAllocateBrushAttr()(重新分配进程私有属性并拷贝内嵌值);失败仅 ERR(画刷仍可用);vUnlock();返回 hbr。
  • 使用方式 :gdi32 ClearBrushAttributes(hbr, SC_BB_STOCKOBJ)
  • 注意:与 Set 对称------Set 是"私有 → 全局",Clear 是"全局 → 私有";Clear 后画刷随进程退出销毁。

7. 各画刷类型创建流程详解

7.1 纯色画刷(BS_SOLID)

复制代码
CreateSolidBrush(RGB)  ── gdi32 ──> NtGdiCreateSolidBrush(crColor, hbr=NULL)
    └─ CreateBrushInternal(BR_IS_SOLID, crColor, 0, NULL, NULL)
         ├─ new BRUSH:flAttrs=BR_IS_SOLID, BrushAttr.lbColor=crColor
         ├─ 非全局 → bAllocateBrushAttr()(进程私有属性)
         └─ hInsertObject(POWNED)

关键点:颜色在创建时存入 pBrushAttr->lbColor;绘制时 EBRUSHOBJ_vInit 检测 BR_IS_SOLID 调 EBRUSHOBJ_vSetSolidRGBColor,把 RGB 经 XLATEOBJ 转成目标表面的颜色索引(iSolidColor),纯色画刷不需要 图案表面。修改颜色用 vSetSolidColor(置时间戳 -1)。

7.2 影线画刷(BS_HATCHED)

复制代码
CreateHatchBrush(iHatch, crColor) ── gdi32 ──> NtGdiCreateHatchBrushInternal(iHatch, crColor, bPen)
    ├─ iHatch<6  → BR_IS_HATCH(真影线)
    ├─ iHatch<12 → BR_IS_SOLID(兼容纯色)
    └─ 否则 → NULL
    └─ CreateBrushInternal(flAttr, crColor, iHatch, NULL, NULL)

关键点:

  • 影线图案不在创建时生成 。系统初始化时 InitBrushImpl() 已把六种 8×8 1bpp 图案(gaulHatchBrushes 表)用 EngCreateBitmap 缓存到全局 gahsurfHatch[6];每个 PDEV 的 ahsurf[] 持有自己的副本(源自全局表)。
  • 绘制时 EBRUSHOBJ_bRealizeBrushBR_IS_HATCHhbmPattern = ppdev->ahsurf[pbr->iHatch]iHatch = pbr->iHatch,并把 pbr->BrushAttr.lbColor 作为影线前景色(EBRUSHOBJ_vInitpebo->crCurrentText = pbrush->BrushAttr.lbColor)。
  • 掩码:EBRUSHOBJ_psoMask 对影线画刷直接取 ppdev->ahsurf[iHatch](1bpp),供 DrvRealizeBrush 使用。

7.3 图案画刷(BS_PATTERN,DDB)

复制代码
CreatePatternBrush(hbm) ── gdi32 ──> NtGdiCreatePatternBrushInternal(hbm, bPen, b8X8)
    ├─ hbmPattern = BITMAP_CopyBitmap(hbm)     ← 深拷贝用户位图
    └─ CreateBrushInternal(BR_IS_BITMAP, 0, 0, hbmPattern, hbmClient=hbm)

关键点:

  • 双句柄:内核持有副本 hbmPattern(析构时删除),用户原图作为 hbmClient 仅用于 GetObject 回填。
  • 绘制时 EBRUSHOBJ_bRealizeBrush:非影线分支 hbmPattern = pbr->hbmPatterniHatch = -1SURFACE_ShareLockSurface 锁定后交给 DrvRealizeBrush 或 EngRealizeBrush 实现为目标格式图案表面。
  • IntGdiCreatePatternBrush(内部接口)则不拷贝,直接引用公共位图(ntuser 的 hbrGray 场景)。

7.4 DIB 图案画刷(BS_DIBPATTERN)

复制代码
CreateDIBPatternBrushPt(pvPackedDIB, uUsage) ── gdi32 ──> NtGdiCreateDIBBrush(pv, uUsage, cj, ...)
    ├─ ProbeForRead + RtlCopyMemory(SEH 保护拷贝)
    ├─ uUsage=DIB_PAL_COLORS → BR_IS_DIBPALCOLORS, uUsage=DIB_PAL_BRUSHHACK
    ├─ uUsage=DIB_PAL_INDICES → BR_IS_DIBPALINDICES (+BR_IS_NULL hack)
    ├─ hbm = GreCreateDIBitmapFromPackedDIB(pvPackedDIB, cj, uUsage)
    └─ CreateBrushInternal(BR_IS_BITMAP|BR_IS_DIB, 0, 0, hbm, pvClient)

关键点:

  • BRUSHHACK 调色板 :DIB_PAL_COLORS 的 DIB 颜色表是"16 位调色板索引",内核用 DIB_PAL_BRUSHHACK 值创建位图时,调色板索引被以 peRed=LOWORD, peGreen=HIWORD 方式存进特殊调色板。
  • 实现时颜色修复EBRUSHOBJ_bRealizeBrush 检测 BR_IS_DIBPALCOLORS → FixupDIBBrushPalette(psurfPattern->ppal, pebo->ppalDC):为每个颜色读回 WORD 索引(peRed | peGreen<<8)、%= ppalDC->NumColors 防越界、再取 DC 调色板对应 RGB 存入新调色板 pebo->ppalDIB(引用计数管理,vCleanup 释放)。
  • DIB_PAL_INDICES:位图像素本身是设备调色板索引,无需颜色表;BR_IS_NULL hack 使其 GetObject 表现为 BS_NULL。

8. 与 EBRUSHOBJ 的实现关系(engbrush.c)

8.1 InitBrushImpl ------ 影线位图预生成

c 复制代码
static const ULONG gaulHatchBrushes[HS_DDI_MAX][8] =
{
    {0xFF,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF}, /* HS_HORIZONTAL */
    {0xF7,0xF7,0xF7,0xF7,0xF7,0xF7,0xF7,0xF7}, /* HS_VERTICAL   */
    {0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F}, /* HS_FDIAGONAL  */
    {0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE}, /* HS_BDIAGONAL  */
    {0xF7,0xF7,0xF7,0xF7,0x00,0xF7,0xF7,0xF7}, /* HS_CROSS      */
    {0x7E,0xBD,0xDB,0xE7,0xE7,0xDB,0xBD,0x7E}  /* HS_DIAGCROSS  */
};
HSURF gahsurfHatch[HS_DDI_MAX];

CODE_SEG("INIT")
NTSTATUS NTAPI InitBrushImpl(VOID)
{
    for (i = 0; i < HS_DDI_MAX; i++)
        gahsurfHatch[i] = (HSURF)EngCreateBitmap(sizl={8,8}, 0, BMF_1BPP, 0,
                                                 (PVOID)gaulHatchBrushes[i]);
    return STATUS_SUCCESS;
}
  • 六组 8 字节即 8×8 的 1bpp 位图(每字节一行,0 位为影线像素)。
  • 代码段标 CODE_SEG("INIT"):仅初始化阶段使用,之后可被分页/回收。

8.2 EBRUSHOBJ_vInit ------ 初始化

c 复制代码
VOID NTAPI EBRUSHOBJ_vInit(EBRUSHOBJ *pebo, PBRUSH pbrush, PSURFACE psurf,
                           COLORREF crBackgroundClr, COLORREF crForegroundClr,
                           PPALETTE ppalDC)

流程:

  1. 清零 BrushObject.flColorTypepvRbrush;记录 pbrushpengbrush=NULLflattrs = pbrush->flAttrspsoMask=NULL
  2. 记录前景/背景色:crCurrentBackcrCurrentText
  3. 目标表面:psurfTrg = psurf;为空(新内存 DC)则用 psurfDefaultBitmap;断言 ppal 存在。
  4. 调色板引用:ppalSurf = psurfTrg->ppalGDIOBJ_vReferenceObjectByPointer 引用 +1);ppalDC = ppalDC ?: gppalDefault(引用 +1);ppalDIB = NULL
  5. 按种类初始化 iSolidColor
    • BR_IS_NULL → iSolidColor = 0(无需颜色)。
    • BR_IS_SOLID → EBRUSHOBJ_vSetSolidRGBColor(pebo, pbrush->BrushAttr.lbColor)
    • 其它(图案)→ iSolidColor = 0xFFFFFFFF(非纯色标记);若 BR_IS_HATCH,crCurrentText = pbrush->BrushAttr.lbColor(影线前景 = 画刷颜色)。

注意 EBRUSHOBJ_vInitNTAPI 且声明在 brush.h------brush.cpp 只负责逻辑画刷,实现(realize)全部在 engbrush.c。

8.3 EBRUSHOBJ_vInitFromDC / vUpdateFromDC

c 复制代码
EBRUSHOBJ_vInitFromDC(pebo, pbrush, pdc)
  → EBRUSHOBJ_vInit(pebo, pbrush, pdc->dclevel.pSurface,
                    pdc->pdcattr->crBackgroundClr, pdc->pdcattr->crForegroundClr,
                    pdc->dclevel.ppal);

EBRUSHOBJ_vUpdateFromDC(pebo, pbrush, pdc)
  → EBRUSHOBJ_vCleanup(pebo); EBRUSHOBJ_vInitFromDC(pebo, pbrush, pdc);
  • 从 DC 取表面、前景/背景色、DC 调色板。
  • vUpdateFromDC = 先清理旧实现再重建(SelectObject 换画刷、SetBkColor/SetTextColor 后调用------见 dcobjs.c DC_vUpdateFillBrush)。

8.4 EBRUSHOBJ_vSetSolidRGBColor ------ RGB → iSolidColor

c 复制代码
VOID FASTCALL EBRUSHOBJ_vSetSolidRGBColor(EBRUSHOBJ *pebo, COLORREF crColor)
{
    ASSERT(pebo->flattrs & BR_IS_SOLID);
    crColor &= 0xFFFFFF;
    pebo->crRealize = crColor;
    pebo->ulRGBColor = crColor;
    if (pebo->ppalSurf->flFlags & PAL_MONOCHROME)
    {
        /* 单色表面:最近色索引 + 反转位 */
        iBackIndex = PALETTE_ulGetNearestPaletteIndex(ppalSurf, crCurrentBack);
        iForeIndex = iBackIndex ^ 1;
        rgbBack    = PALETTE_ulGetRGBColorFromIndex(ppalSurf, iBackIndex);
        iSolidColor = (crColor == rgbBack || crColor == crCurrentBack) ? iBackIndex : iForeIndex;
        flattrs |= BR_NEED_BK_CLR;
    }
    else
    {
        EXLATEOBJ_vInitialize(&exlo, &gpalRGB, ppalSurf, crCurrentBack, 0, 0);
        iSolidColor = XLATEOBJ_iXlate(&exlo.xlo, crColor);   // RGB → 表面格式
        EXLATEOBJ_vCleanup(&exlo);
    }
    pebo->BrushObject.iSolidColor = iSolidColor;
}
  • 单色表面(PAL_MONOCHROME):背景索引取最近色,前景索引=背景索引异或 1(黑白对),再比对画刷颜色与背景色决定用哪个;并置 BR_NEED_BK_CLR 提示需要背景色。
  • 彩色表面:借助 XLATEOBJ(gpalRGB → 表面调色板)做颜色翻译,iSolidColor 即目标表面的颜色索引/像素值。

8.5 EBRUSHOBJ_bRealizeBrush ------ 核心实现函数

c 复制代码
BOOL NTAPI EBRUSHOBJ_bRealizeBrush(EBRUSHOBJ *pebo, BOOL bCallDriver)

流程:

  1. ppdev 取自 psurfTrg->SurfObj.hdev,空则用 gpmdev->ppdevGlobal
  2. 选择实现者:bCallDriver → ppdev->DriverFunctions.RealizeBrush(无则 ERR 返回 FALSE),掩码 psoMask = EBRUSHOBJ_psoMask(pebo);否则用引擎自带 EngRealizeBrush,掩码置 NULL(引擎实现不处理掩码,按需再取)。
  3. 取图案
    • 影线画刷:hbmPattern = (HBITMAP)ppdev->ahsurf[pbr->iHatch]iHatch = pbr->iHatch(PDEV 的影线位图缓存)。
    • 其它画刷:hbmPattern = pbr->hbmPatterniHatch = -1
  4. psurfPattern = SURFACE_ShareLockSurface(hbmPattern)(断言非空且带调色板)。
  5. DIB_PAL_COLORS 特殊处理BR_IS_DIBPALCOLORSppalPattern = FixupDIBBrushPalette(psurfPattern->ppal, pebo->ppalDC) 生成新调色板存入 pebo->ppalDIB(失败 ERR 返回 FALSE);否则直接用 psurfPattern->ppal
  6. EXLATEOBJ_vInitialize(&exlo, ppalPattern, psurfTrg->ppal, 0, crCurrentBack, crCurrentText)------建立图案调色板 → 目标表面调色板的翻译对象。
  7. 调用 pfnRealizeBrush(&pebo->BrushObject, &psurfTrg->SurfObj, &psurfPattern->SurfObj, psoMask, &exlo.xlo, iHatch)
  8. 清理:EXLATEOBJ_vCleanupSURFACE_ShareUnlockSurface(psurfPattern),返回结果。

注意:影线画刷的 ppdev->ahsurf 与全局 gahsurfHatch 的关系------PDEV 初始化时把全局影线表面登记进 ahsurf[](按 HS 索引),这里直接按 pbr->iHatch 取用;非影线画刷的图案来自逻辑画刷的 hbmPattern。

8.6 EngRealizeBrush ------ 引擎实现(非导出)

c 复制代码
BOOL APIENTRY EngRealizeBrush(BRUSHOBJ *pbo, SURFOBJ *psoDst, SURFOBJ *psoPattern,
                              SURFOBJ *psoMask, XLATEOBJ *pxlo, ULONG iHatch)
  • 注释:不导出,因为驱动没必要"把实现打回给引擎"。
  • 实现:按目标格式计算每行字节数 WIDTH_BYTES_ALIGN32EngCreateBitmap(psoPattern->sizlBitmap, lWidth, psoDst->iBitmapFormat, BMF_NOZEROINIT, NULL) 建目标格式位图 → SURFACE_ShareLockSurface 锁定 → EngDeleteSurface(句柄先行删除,靠引用计数存活)→ EngCopyBits 把图案按 XLATEOBJ 转换拷贝到新位图 → pebo->pengbrush = psurfRealize
  • 即:引擎实现 = "把图案位图转换成目标表面格式的一份拷贝",供 blt/填充引擎直接使用(如 1bpp 影线图案 → 32bpp 目标格式)。

8.7 FixupDIBBrushPalette ------ DIB_PAL_COLORS 调色板修复(static)

c 复制代码
static PPALETTE FixupDIBBrushPalette(_In_ PPALETTE ppalDIB, _In_ PPALETTE ppalDC)
  • PALETTE_AllocPalette(PAL_INDEXED, ppalDIB->NumColors, NULL, 0, 0, 0) 分配新调色板。
  • 逐颜色处理:iPalIndex = IndexedColors[i].peRed | (IndexedColors[i].peGreen << 8)(BRUSHHACK 把 WORD 索引拆存于 peRed=LOWORD / peGreen=HIWORD)→ iPalIndex %= ppalDC->NumColors(越界回绕)→ crColor = PALETTE_ulGetRGBColorFromIndex(ppalDC, iPalIndex)(取 DC 调色板的实际 RGB)→ PALETTE_vSetRGBColorForIndex(ppalNew, i, crColor)
  • 结果:DIB 中的"16 位调色板索引"被解析成 DC 调色板对应的真实 RGB,存进新调色板(pebo->ppalDIB),供 XLATEOBJ 翻译使用。

8.8 惰性取用:pvGetEngBrush / psoPattern / psoMask / vCleanup

c 复制代码
PVOID NTAPI EBRUSHOBJ_pvGetEngBrush(EBRUSHOBJ *pebo)
{
    if (!pebo->pengbrush)
    {
        bResult = EBRUSHOBJ_bRealizeBrush(pebo, FALSE);   // 引擎实现
        if (!bResult) { 失败时清理 pengbrush 并置 NULL; }
    }
    return pebo->pengbrush;
}
  • 惰性实现:第一次需要图案表面时才调用 bRealizeBrush(bCallDriver=FALSE);失败清理后下次重试。
  • EBRUSHOBJ_psoPattern(pebo)pvGetEngBrush 后返回 &psurfPattern->SurfObj,供填充引擎(如 PatBlt)取图案 SURFOBJ;宏 BRUSHOBJ_psoPattern(pbo)CONTAINING_RECORD 从 BRUSHOBJ* 反推 EBRUSHOBJ*。
  • EBRUSHOBJ_psoMask(pebo):掩码未取过时,对影线画刷从 ppdev->ahsurf[iHatch] 锁定(断言 1bpp)存入 psoMask;非影线画刷返回 NULL。掩码供 DrvRealizeBrush 区分前景/背景(ROP 处理)。
  • EBRUSHOBJ_vCleanup(pebo):对称释放------SURFACE_ShareUnlockSurface(pengbrush)EngFreeMem(pvRbrush)SURFACE_ShareUnlockSurface(psoMask)PALETTE_ShareUnlockPalette(ppalSurf/ppalDC/ppalDIB),全部复位。

8.9 驱动接口:BRUSHOBJ_pvAllocRbrush / pvGetRbrush / ulGetBrushColor

c 复制代码
PVOID APIENTRY BRUSHOBJ_pvAllocRbrush(IN BRUSHOBJ *pbo, IN ULONG cj)
{ pbo->pvRbrush = EngAllocMem(0, cj, GDITAG_RBRUSH); return pbo->pvRbrush; }
  • BRUSHOBJ_pvAllocRbrush :驱动在 DrvRealizeBrush 中分配私有已实现画刷内存(带 GDITAG_RBRUSH 标签,便于审计),返回后存于 pbo->pvRbrush
  • BRUSHOBJ_pvGetRbrush :惰性调用 EBRUSHOBJ_bRealizeBrush(pebo, TRUE)(驱动实现);失败时 EngFreeMem 释放残留并返回 NULL。驱动每次需要已实现画刷时调用。
  • BRUSHOBJ_ulGetBrushColor :返回 pebo->ulRGBColor(当前纯色 RGB),驱动可查画刷颜色。
  • 三者均为 @implemented 的 DDI 导出(winddi 接口),BRUSHOBJ_* 是驱动可见的"只读"视角(BRUSHOBJ 前缀),内部操作通过 CONTAINING_RECORD 回到 EBRUSHOBJ。

9. SetDCBrushColor 链路(DC 画刷颜色)

SetDCBrushColor 修改的是 DC 的画刷颜色(配合 DC_BRUSH 库存画刷使用),与 6.6 节"修改画刷对象颜色"(IntGdiSetSolidBrushColor)是两条不同的链路。完整链路如下:

复制代码
应用: SetDCBrushColor(hdc, crColor)               (gdi32/objects/dc.c)
  │
  ├─ HANDLE_EMETAFDC → EMFDRV_SetDCBrushColor     (增强元文件 DC 走 EMF 记录)
  │
  ▼
NtGdiSetDCBrushColor  →  IntSetDCBrushColor(hdc, crColor)   (ntgdi/dcutil.c)
  │
  ├─ dc->pdcattr->ulBrushClr = crColor            ("最近一次 SetDCBrushColor"缓存)
  ├─ if (crBrushClr != crColor):
  │     ulDirty_ |= DIRTY_FILL                    (标记填充画刷脏)
  │     pdcattr->crBrushClr = crColor             (实际生效的 DC 画刷颜色)
  │
  ▼
(下次 DC 被使用时,如 GrePatBlt / DC_vUpdateFillBrush)   (ntgdi/dcobjs.c)
  DC_vUpdateFillBrush(pdc):
  ├─ 若 hbrush 变化 → BRUSH_ShareLockBrush/UnlockBrush 换 pbrFill,置 DIRTY_FILL
  ├─ 若 DIRTY_FILL → EBRUSHOBJ_vUpdateFromDC(&pdc->eboFill, pbrFill, pdc)
  │     └─ EBRUSHOBJ_vCleanup + EBRUSHOBJ_vInit(重新按 DC 状态初始化)
  └─ 若 hbrush == StockObjects[DC_BRUSH]:
        EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboFill, pdcattr->crBrushClr)
          └─ XLATEOBJ 翻译 → eboFill.BrushObject.iSolidColor
  └─ 清除 ulDirty_ 的 DIRTY_FILL / DC_BRUSH_DIRTY

要点:

  • DC_BRUSH 是共享库存画刷 (stockobj.c:StockObjects[DC_BRUSH] = IntGdiCreateSolidBrush(RGB(255,255,255))),它本身是白色纯色画刷。每个 DC 通过 eboFill(EBRUSHOBJ)持有自己的实现状态SetDCBrushColor 之后并不修改 DC_BRUSH 对象本身,而是更新 pdcattr->crBrushClr 并把 DIRTY_FILL 置位,待 DC_vUpdateFillBrush 时对 eboFill 单独调用 EBRUSHOBJ_vSetSolidRGBColor 覆盖其 iSolidColor。这样所有 DC 共用同一个 DC_BRUSH 句柄,却各有各的填充颜色。
  • DIRTY_FILL / DC_BRUSH_DIRTYSetDCBrushColor 置 DIRTY_FILL;SelectObject(DC_BRUSH) 置 DC_BRUSH_DIRTY;两者任一置位都会在 DC 使用时触发 DC_vUpdateFillBrush 重建 eboFill。
  • 该链路与第 6.6 节的 IntGdiSetSolidBrushColor(改画刷对象本身,供 SetSysColors 刷新系统画刷)互补:前者是"DC 局部颜色",后者是"对象全局颜色"。

10. 调用链总览(Mermaid)

10.1 画刷创建与查询

#mermaid-svg-EjVPwpm27nIZSa4p{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EjVPwpm27nIZSa4p .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EjVPwpm27nIZSa4p .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EjVPwpm27nIZSa4p .error-icon{fill:#552222;}#mermaid-svg-EjVPwpm27nIZSa4p .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EjVPwpm27nIZSa4p .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EjVPwpm27nIZSa4p .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EjVPwpm27nIZSa4p .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EjVPwpm27nIZSa4p .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EjVPwpm27nIZSa4p .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EjVPwpm27nIZSa4p .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EjVPwpm27nIZSa4p .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EjVPwpm27nIZSa4p .marker.cross{stroke:#333333;}#mermaid-svg-EjVPwpm27nIZSa4p svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EjVPwpm27nIZSa4p p{margin:0;}#mermaid-svg-EjVPwpm27nIZSa4p .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-EjVPwpm27nIZSa4p .cluster-label text{fill:#333;}#mermaid-svg-EjVPwpm27nIZSa4p .cluster-label span{color:#333;}#mermaid-svg-EjVPwpm27nIZSa4p .cluster-label span p{background-color:transparent;}#mermaid-svg-EjVPwpm27nIZSa4p .label text,#mermaid-svg-EjVPwpm27nIZSa4p span{fill:#333;color:#333;}#mermaid-svg-EjVPwpm27nIZSa4p .node rect,#mermaid-svg-EjVPwpm27nIZSa4p .node circle,#mermaid-svg-EjVPwpm27nIZSa4p .node ellipse,#mermaid-svg-EjVPwpm27nIZSa4p .node polygon,#mermaid-svg-EjVPwpm27nIZSa4p .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EjVPwpm27nIZSa4p .rough-node .label text,#mermaid-svg-EjVPwpm27nIZSa4p .node .label text,#mermaid-svg-EjVPwpm27nIZSa4p .image-shape .label,#mermaid-svg-EjVPwpm27nIZSa4p .icon-shape .label{text-anchor:middle;}#mermaid-svg-EjVPwpm27nIZSa4p .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EjVPwpm27nIZSa4p .rough-node .label,#mermaid-svg-EjVPwpm27nIZSa4p .node .label,#mermaid-svg-EjVPwpm27nIZSa4p .image-shape .label,#mermaid-svg-EjVPwpm27nIZSa4p .icon-shape .label{text-align:center;}#mermaid-svg-EjVPwpm27nIZSa4p .node.clickable{cursor:pointer;}#mermaid-svg-EjVPwpm27nIZSa4p .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EjVPwpm27nIZSa4p .arrowheadPath{fill:#333333;}#mermaid-svg-EjVPwpm27nIZSa4p .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EjVPwpm27nIZSa4p .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EjVPwpm27nIZSa4p .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EjVPwpm27nIZSa4p .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EjVPwpm27nIZSa4p .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EjVPwpm27nIZSa4p .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EjVPwpm27nIZSa4p .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EjVPwpm27nIZSa4p .cluster text{fill:#333;}#mermaid-svg-EjVPwpm27nIZSa4p .cluster span{color:#333;}#mermaid-svg-EjVPwpm27nIZSa4p div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EjVPwpm27nIZSa4p .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EjVPwpm27nIZSa4p rect.text{fill:none;stroke-width:0;}#mermaid-svg-EjVPwpm27nIZSa4p .icon-shape,#mermaid-svg-EjVPwpm27nIZSa4p .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EjVPwpm27nIZSa4p .icon-shape p,#mermaid-svg-EjVPwpm27nIZSa4p .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EjVPwpm27nIZSa4p .icon-shape .label rect,#mermaid-svg-EjVPwpm27nIZSa4p .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EjVPwpm27nIZSa4p .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EjVPwpm27nIZSa4p .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EjVPwpm27nIZSa4p :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} gdi32
gdi32
gdi32
gdi32
gdi32
类型分发
gdi32
gdi32
gdi32
类型分发
CreateSolidBrush
NtGdiCreateSolidBrush
CreateHatchBrush
NtGdiCreateHatchBrushInternal
CreatePatternBrush
NtGdiCreatePatternBrushInternal
CreateDIBPatternBrushPt
NtGdiCreateDIBBrush
CreateBrushInternal
new BRUSH
bAllocateBrushAttr / hInsertObject
BRUSH 对象入句柄表
GetObject
NtGdiGetObject
BRUSH_GetObject
cjGetObject → LOGBRUSH
GetObject 图案画刷
NtGdiGetObjectBitmapHandle
hbmGetBitmapHandle
SetBrushAttributes / ClearBrushAttributes
NtGdiSetBrushAttributes / NtGdiClearBrushAttributes
GDIOBJ_ConvertTo/FromStockObj + vReleaseAttribute / bAllocateBrushAttr
DeleteObject
NtGdiDeleteObject
BRUSH_vDeleteObject → ~BRUSH

10.2 绘制时的实现(realize)

#mermaid-svg-I9jgqdbWsjMV3f6L{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-I9jgqdbWsjMV3f6L .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-I9jgqdbWsjMV3f6L .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-I9jgqdbWsjMV3f6L .error-icon{fill:#552222;}#mermaid-svg-I9jgqdbWsjMV3f6L .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-I9jgqdbWsjMV3f6L .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-I9jgqdbWsjMV3f6L .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-I9jgqdbWsjMV3f6L .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-I9jgqdbWsjMV3f6L .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-I9jgqdbWsjMV3f6L .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-I9jgqdbWsjMV3f6L .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-I9jgqdbWsjMV3f6L .marker{fill:#333333;stroke:#333333;}#mermaid-svg-I9jgqdbWsjMV3f6L .marker.cross{stroke:#333333;}#mermaid-svg-I9jgqdbWsjMV3f6L svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-I9jgqdbWsjMV3f6L p{margin:0;}#mermaid-svg-I9jgqdbWsjMV3f6L .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-I9jgqdbWsjMV3f6L .cluster-label text{fill:#333;}#mermaid-svg-I9jgqdbWsjMV3f6L .cluster-label span{color:#333;}#mermaid-svg-I9jgqdbWsjMV3f6L .cluster-label span p{background-color:transparent;}#mermaid-svg-I9jgqdbWsjMV3f6L .label text,#mermaid-svg-I9jgqdbWsjMV3f6L span{fill:#333;color:#333;}#mermaid-svg-I9jgqdbWsjMV3f6L .node rect,#mermaid-svg-I9jgqdbWsjMV3f6L .node circle,#mermaid-svg-I9jgqdbWsjMV3f6L .node ellipse,#mermaid-svg-I9jgqdbWsjMV3f6L .node polygon,#mermaid-svg-I9jgqdbWsjMV3f6L .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-I9jgqdbWsjMV3f6L .rough-node .label text,#mermaid-svg-I9jgqdbWsjMV3f6L .node .label text,#mermaid-svg-I9jgqdbWsjMV3f6L .image-shape .label,#mermaid-svg-I9jgqdbWsjMV3f6L .icon-shape .label{text-anchor:middle;}#mermaid-svg-I9jgqdbWsjMV3f6L .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-I9jgqdbWsjMV3f6L .rough-node .label,#mermaid-svg-I9jgqdbWsjMV3f6L .node .label,#mermaid-svg-I9jgqdbWsjMV3f6L .image-shape .label,#mermaid-svg-I9jgqdbWsjMV3f6L .icon-shape .label{text-align:center;}#mermaid-svg-I9jgqdbWsjMV3f6L .node.clickable{cursor:pointer;}#mermaid-svg-I9jgqdbWsjMV3f6L .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-I9jgqdbWsjMV3f6L .arrowheadPath{fill:#333333;}#mermaid-svg-I9jgqdbWsjMV3f6L .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-I9jgqdbWsjMV3f6L .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-I9jgqdbWsjMV3f6L .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-I9jgqdbWsjMV3f6L .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-I9jgqdbWsjMV3f6L .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-I9jgqdbWsjMV3f6L .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-I9jgqdbWsjMV3f6L .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-I9jgqdbWsjMV3f6L .cluster text{fill:#333;}#mermaid-svg-I9jgqdbWsjMV3f6L .cluster span{color:#333;}#mermaid-svg-I9jgqdbWsjMV3f6L div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-I9jgqdbWsjMV3f6L .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-I9jgqdbWsjMV3f6L rect.text{fill:none;stroke-width:0;}#mermaid-svg-I9jgqdbWsjMV3f6L .icon-shape,#mermaid-svg-I9jgqdbWsjMV3f6L .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-I9jgqdbWsjMV3f6L .icon-shape p,#mermaid-svg-I9jgqdbWsjMV3f6L .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-I9jgqdbWsjMV3f6L .icon-shape .label rect,#mermaid-svg-I9jgqdbWsjMV3f6L .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-I9jgqdbWsjMV3f6L .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-I9jgqdbWsjMV3f6L .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-I9jgqdbWsjMV3f6L :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
BR_IS_SOLID
BR_IS_NULL
图案/影线
bCallDriver=TRUE
bCallDriver=FALSE
DC 绘制 / SelectObject / SetDCBrushColor
DC_vUpdateFillBrush / DC_vUpdateLineBrush
画刷句柄或颜色变化?
EBRUSHOBJ_vUpdateFromDC
EBRUSHOBJ_vCleanup
EBRUSHOBJ_vInit / vInitFromDC
画刷种类
EBRUSHOBJ_vSetSolidRGBColor
iSolidColor = 0
iSolidColor = 0xFFFFFFFF
iSolidColor = XLATEOBJ_iXlate
EBRUSHOBJ_bRealizeBrush
DrvRealizeBrush → pvRbrush
EngRealizeBrush → pengbrush
EBRUSHOBJ_psoMask 影线掩码
FixupDIBBrushPalette DIB_PAL_COLORS
BRUSHOBJ_pvGetRbrush 惰性取用
EBRUSHOBJ_pvGetEngBrush / psoPattern 惰性取用
填充/描边引擎使用

10.3 系统初始化

#mermaid-svg-NExp08y7GVryMHne{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NExp08y7GVryMHne .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NExp08y7GVryMHne .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NExp08y7GVryMHne .error-icon{fill:#552222;}#mermaid-svg-NExp08y7GVryMHne .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NExp08y7GVryMHne .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NExp08y7GVryMHne .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NExp08y7GVryMHne .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NExp08y7GVryMHne .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NExp08y7GVryMHne .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NExp08y7GVryMHne .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NExp08y7GVryMHne .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NExp08y7GVryMHne .marker.cross{stroke:#333333;}#mermaid-svg-NExp08y7GVryMHne svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NExp08y7GVryMHne p{margin:0;}#mermaid-svg-NExp08y7GVryMHne .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NExp08y7GVryMHne .cluster-label text{fill:#333;}#mermaid-svg-NExp08y7GVryMHne .cluster-label span{color:#333;}#mermaid-svg-NExp08y7GVryMHne .cluster-label span p{background-color:transparent;}#mermaid-svg-NExp08y7GVryMHne .label text,#mermaid-svg-NExp08y7GVryMHne span{fill:#333;color:#333;}#mermaid-svg-NExp08y7GVryMHne .node rect,#mermaid-svg-NExp08y7GVryMHne .node circle,#mermaid-svg-NExp08y7GVryMHne .node ellipse,#mermaid-svg-NExp08y7GVryMHne .node polygon,#mermaid-svg-NExp08y7GVryMHne .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NExp08y7GVryMHne .rough-node .label text,#mermaid-svg-NExp08y7GVryMHne .node .label text,#mermaid-svg-NExp08y7GVryMHne .image-shape .label,#mermaid-svg-NExp08y7GVryMHne .icon-shape .label{text-anchor:middle;}#mermaid-svg-NExp08y7GVryMHne .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NExp08y7GVryMHne .rough-node .label,#mermaid-svg-NExp08y7GVryMHne .node .label,#mermaid-svg-NExp08y7GVryMHne .image-shape .label,#mermaid-svg-NExp08y7GVryMHne .icon-shape .label{text-align:center;}#mermaid-svg-NExp08y7GVryMHne .node.clickable{cursor:pointer;}#mermaid-svg-NExp08y7GVryMHne .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NExp08y7GVryMHne .arrowheadPath{fill:#333333;}#mermaid-svg-NExp08y7GVryMHne .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NExp08y7GVryMHne .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NExp08y7GVryMHne .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NExp08y7GVryMHne .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NExp08y7GVryMHne .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NExp08y7GVryMHne .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NExp08y7GVryMHne .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NExp08y7GVryMHne .cluster text{fill:#333;}#mermaid-svg-NExp08y7GVryMHne .cluster span{color:#333;}#mermaid-svg-NExp08y7GVryMHne div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NExp08y7GVryMHne .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NExp08y7GVryMHne rect.text{fill:none;stroke-width:0;}#mermaid-svg-NExp08y7GVryMHne .icon-shape,#mermaid-svg-NExp08y7GVryMHne .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NExp08y7GVryMHne .icon-shape p,#mermaid-svg-NExp08y7GVryMHne .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NExp08y7GVryMHne .icon-shape .label rect,#mermaid-svg-NExp08y7GVryMHne .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NExp08y7GVryMHne .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NExp08y7GVryMHne .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NExp08y7GVryMHne :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 系统启动
InitBrushImpl
gaulHatchBrushes 表 → EngCreateBitmap ×6
gahsurfHatch 缓存
CreateSysColorObjects
IntGdiCreateSolidBrush ×NUM_SYSCOLORS
gpsi->ahbrSystem + GDIOBJ_ConvertToStockObj
stockobj.c 库存对象
IntGdiCreateSolidBrush: WHITE/LTGRAY/GRAY/DKGRAY/BLACK/DC_BRUSH
IntGdiCreateNullBrush: NULL_BRUSH
ntuser: IntGdiCreatePatternBrush → hbrGray


11. 源码索引

文件 关键内容
brush.cpp(file:///d:/reactos/win32ss/gdi/ntgdi/brush.cpp) BRUSH 构造/析构、vDeleteObject、bAllocateBrushAttr、vReleaseAttribute、vSetSolidColor、hbmGetBitmapHandle、cjGetObject、CreateBrushInternal、BRUSH_vDeleteObject、BRUSH_GetObject、IntGdiCreateNullBrush/SolidBrush/PatternBrush、IntGdiSetSolidBrushColor、NtGdiCreateSolidBrush/CreateHatchBrushInternal/CreatePatternBrushInternal/CreateDIBBrush/GetObjectBitmapHandle/SetBrushAttributes/ClearBrushAttributes
brush.h(file:///d:/reactos/win32ss/gdi/ntgdi/brush.h) BRUSHBODY、BRUSH、EBRUSHOBJ 结构;BR* 标志;BRUSH_ShareLockBrush 宏;EBRUSHOBJ_* 声明
brush.hpp(file:///d:/reactos/win32ss/gdi/ntgdi/brush.hpp) BRUSH 类(operator new/delete、LockForRead/Write/Any、成员函数声明)、GetBrushAttrPool
engbrush.c(file:///d:/reactos/win32ss/gdi/eng/engbrush.c) gaulHatchBrushes、gahsurfHatch、InitBrushImpl、EBRUSHOBJ_vInit/vInitFromDC/vSetSolidRGBColor/vUpdateFromDC/vCleanup、EngRealizeBrush、FixupDIBBrushPalette、EBRUSHOBJ_bRealizeBrush/pvGetEngBrush/psoPattern/psoMask、BRUSHOBJ_pvAllocRbrush/pvGetRbrush/ulGetBrushColor
ntgdihdl.h(file:///d:/reactos/win32ss/include/ntgdihdl.h) BRUSH_ATTR、SC_BB_STOCKOBJ
ntgdityp.h(file:///d:/reactos/win32ss/include/ntgdityp.h) DIB_PAL_INDICES
dib.h(file:///d:/reactos/win32ss/gdi/ntgdi/dib.h) DIB_PAL_BRUSHHACK
dcobjs.c(file:///d:/reactos/win32ss/gdi/ntgdi/dcobjs.c) DC_vUpdateFillBrush(eboFill 更新、DC_BRUSH 颜色覆盖)
dcutil.c(file:///d:/reactos/win32ss/gdi/ntgdi/dcutil.c) IntSetDCBrushColor(ulBrushClr/crBrushClr/DIRTY_FILL)
stockobj.c(file:///d:/reactos/win32ss/gdi/ntgdi/stockobj.c) 库存画刷创建、CreateSysColorObjects、IntSetSysColors(→ IntGdiSetSolidBrushColor)
brush.c (gdi32)(file:///d:/reactos/win32ss/gdi/gdi32/objects/brush.c) SetBrushAttributes / ClearBrushAttributes 用户态包装
wingdi.h(file:///d:/reactos/sdk/include/psdk/wingdi.h) BS_*(1086 行起)、HS_HORIZONTAL...HS_DIAGCROSS、HS_API_MAX=12、DIB_RGB_COLORS
winddi.h(file:///d:/reactos/sdk/include/psdk/winddi.h) HS_DDI_MAX=6、BRUSHOBJ 接口

关键调用关系速查

  • IntGdiCreateSolidBrush / IntGdiCreateNullBrush / IntGdiCreatePatternBrush → 内部全局画刷(BR_IS_GLOBAL,owner=PUBLIC),供 stockobj/ntuser 使用。
  • NtGdiCreateSolidBrush / NtGdiCreateHatchBrushInternal / NtGdiCreatePatternBrushInternal / NtGdiCreateDIBBrush → 进程私有画刷(POWNED + bAllocateBrushAttr),供 gdi32 API 使用。
  • BRUSH::cjGetObject → BS_* 样式由 flAttrs 推导;lbHatch 对图案画刷返回 hbmClient。
  • EBRUSHOBJ_bRealizeBrush → 影线取 ppdev->ahsurf[iHatch]、图案取 pbr->hbmPattern;DIB_PAL_COLORS 走 FixupDIBBrushPalette;双轨实现(DrvRealizeBrush / EngRealizeBrush)。
  • DC_vUpdateFillBrush → DC_BRUSH 颜色经 EBRUSHOBJ_vSetSolidRGBColor 覆盖 eboFill.iSolidColor。

12. 已知限制与 FIXME(源码为准)

  1. NtGdiCreateSolidBrushhbr 参数未实现(WARN 忽略)------Windows 用它做句柄缓存优化,ReactOS 每次新建。
  2. NtGdiCreateHatchBrushInternal / NtGdiCreatePatternBrushInternal / NtGdiCreateDIBBrushbPenb8X8 参数均未实现(WARN 忽略)------几何画笔与 8×8 优化变体不受支持。
  3. NtGdiCreateDIBBrushDIB_PAL_INDICES 分支附加 BR_IS_NULL:源码注释 FIXME: This makes tests pass, but needs investigation.------疑似为通过 winetest 的权宜之计。
  4. BRUSH::cjGetObject 的 lbColor 取内嵌 BrushAttr.lbColor 而非 pBrushAttr:对进程私有画刷,修改颜色(vSetSolidColor 写 pBrushAttr)后 GetObject 可能读到旧颜色。
  5. operator newGDITAG_HMGR_BRUSH_TYPE 分配、operator delete 用无标签 ExFreePool 释放------注释 HACK! better would be to extract the exact object type's tag,标签不一致影响池审计精度。
  6. BRUSHBODY 布局冻结(Feng Yuan 书),新增字段必须谨慎,尤其不可移动 BASEOBJECT 头与既有偏移。

本文档基于 ReactOS 源代码 win32ss/gdi/ntgdi/brush.cppbrush.hbrush.hppwin32ss/gdi/eng/engbrush.c 及关联模块分析(2026 年 8 月)。所有代码引用均来自上述真实源码,未做任何虚构。

相关推荐
caimouse16 小时前
ReactOS 窗口系统分析(24):输入法 — ime.c
c语言·reactos
Escalating_xu16 小时前
【C++ STL简介】从六大组件到容器、迭代器与算法协作
java·c++·算法
Brilliantwxx17 小时前
【Linux】 进程(3)深度解析:从查看进程到进程状态
linux·服务器·网络·c++
啊啊啊啊啊!!!!17 小时前
【c++】二叉搜索树
开发语言·c++
ShineWinsu17 小时前
对于 C++:C++20中Concept(概念) 与 Coroutine(协程)的解析
linux·开发语言·网络·c++·c++20·epoll
liulilittle17 小时前
llmx 学习手册 06 —— CPU 指令集优化(AVX-512 三层演进)
c++·学习·算法·ai·llm
skr爱码士18 小时前
05_Qt 核心模块概览——Qt Core、Gui、Widgets、Quick 的职责划分
c++·qt·系统架构·客户端
caimouse18 小时前
ReactOS 窗口系统分析(13):滚动条 — scrollbar.c + scrollex.c
c语言·开发语言·reactos
郝学胜-神的一滴18 小时前
Horse3D 游戏引擎研发笔记(七):Clydesdale——从流式日志到多输出订阅
c++·qt·unity·游戏引擎·图形渲染·unreal engine·opengl
码匠许师傅1 天前
【C++ 面试真题】26. 聊聊 C++ 的智能指针
java·c++·面试