ReactOS 图形系统分析(17):区域填充 — EngPaint / IntEngPaint(paint.c)
ReactOS 图形系统分析17区域填充 — EngPaint / IntEngPaintpaint.c1. 概述paint.c位于win32ss/gdi/eng/paint.c实现 GDI 引擎的区域填充Paint/区域绘制。它向上服务内核 GDI 的FillRgn/PaintRgn请求向下把填充动作统一转化为一次无源的 BitBlt图案画刷填充从而复用引擎既有的 BitBlt 裁剪/ROP 管线。文件很小104 行由三部分构成构成作用gajRop2ToRop3[16]ROP21~16→ ROP3 查表MIX 转换的基础FillSolid纯色矩形逐扫描线填充DIB_HLine被EngEraseSurface复用EngPaint/IntEngPaint双层入口EngPaintMIX→ROP4 并转发 BitBltIntEngPaintDrvPaint 挂钩决策FillRgn(hdc, hrgn, hbrush) / PaintRgn(hdc, hrgn) ↓ gdi32.dll → NtGdiFillRgn ntgdi/bitblt.c: IntGdiFillRgn (DC 裁剪合成 / 画刷准备 / MIX 构造) ↓ IntEngPaint 引擎: 目标表面挂钩了 DrvPaint? (HOOK_PAINT) ├─ 是 → GDIDEVFUNCS(pso).Paint (vga 等硬件驱动) └─ 否 → EngPaint ↓ MIX_TO_ROP4 转 ROP4断言无源 IntEngBitBlt(pso, NULL, NULL, pco, NULL, ..., pbo, ..., rop4) ↓ 无源 → 直接软件路径 EngBitBlt → BltPatCopy/纯色 → DIB 引擎写像素2. 调用链路从 API 到像素是否是否应用程序: FillRgn / PaintRgn / FrameRgngdi32.dllNtGdiFillRgnntgdi/bitblt.c: NtGdiFillRgn(锁 DC / 锁区域 / 锁画刷)ntgdi: IntGdiFillRgn合成裁剪区域 prgnClip DC 可见区 ∩ RAO/Vis 区域 DC 原点偏移构造 MIXmix rop2Fg | (BkModeOPAQUE ? rop2Fg : R2_NOP)8EBRUSHOBJ_vInit 初始化画刷对象IntEngPaint(psurf, xcoClip, pbo, ptlFillOrigin, mix)表面非 STYPE_BITMAP且 HOOK_PAINT?DrvPaint(vga: VGADDIPaintRgn)EngPaintMIX_TO_ROP4(mix)gajRop2ToRop3 查表低字节前景ROP3 高字节背景ROP3NT_ASSERT(!ROP4_USES_SOURCE)IntEngBitBlt(pso, NULL src, NULL mask,pco, rclBounds, NULL, NULL, pbo,pptlBrushOrg, rop4)目标表面 HOOK_BITBLT?驱动 DrvBitBltEngBitBlt 软件路径(BltPatCopy / 纯色 BltPatCopy)DibFunctionsForBitmapFormatDIB_*_BitBlt / DIB_HLine显存/帧缓冲2.1 ntgdi 层IntGdiFillRgn关键决策[bitblt.c](file:///d:/reactos/win32ss/gdi/ntgdi/bitblt.c#L1228-L1333) 的IntGdiFillRgn在调用引擎前完成四件事/* 1. 合成裁剪区域DC 可见区与系统/RAO 区域求交并平移到 DC 原点 */IntGdiCombineRgn(prgnClip,prgnClip,pdc-prgnRao,RGN_AND);REGION_bOffsetRgn(prgnClip,pdc-ptlDCOrig.x,pdc-ptlDCOrig.y);/* 2. 构造 MIX前景当前 ROP2背景 OPAQUE 时同 ROP2TRANSPARENT 时为 R2_NOP */rop2FgFIXUP_ROP2(pdc-pdcattr-jROP2);mixrop2Fg|(pdc-pdcattr-jBkModeOPAQUE?rop2Fg:R2_NOP)8;/* 3. 初始化画刷显式画刷或 DC 填充画刷含 DIRTY 更新 */EBRUSHOBJ_vInit(eboFill,pbrFill,pdc-dclevel.pSurface,0x00FFFFFF,0,NULL);/* ... 或 DC_vUpdateFillBrush pdc-eboFill.BrushObject *//* 4. 调用引擎 */bRetIntEngPaint(pdc-dclevel.pSurface-SurfObj,(CLIPOBJ*)xcoClip,pbo,pdc-ptlFillOrigin,mix);入口 API 对照FillRgn指定画刷NtGdiFillRgn、PaintRgn用 DC 当前画刷IntGdiPaintRgn→IntGdiFillRgn(pdc, prgn, NULL)、FrameRgn先GreCreateFrameRgn生成边框区域再 FillRgn。3. 核心机制3.1 gajRop2ToRop3 查表 MIX_TO_ROP4[eng.h](file:///d:/reactos/win32ss/gdi/eng/eng.h#L3-L7)填充的笔语义由 ROP216 种描述引擎内部 BitBlt 统一使用 ROP3/ROP4。paint.c开头提供一张ROP2 → ROP3 映射表constBYTE gajRop2ToRop3[16]{0x00,// 1: R2_BLACK 00x05,// 2: R2_NOTMERGEPEN DPon0x0A,// 3: R2_MASKNOTPEN DPna0x0F,// 4: R2_NOTCOPYPEN Pn0x50,// 5: R2_MASKPENNOT PDna0x55,// 6: R2_NOT Dn0x5A,// 7: R2_XORPEN DPx0x5F,// 8: R2_NOTMASKPEN DPan0xA0,// 9: R2_MASKPEN DPa0xA5,// 10: R2_NOTXORPEN PDxn0xAA,// 11: R2_NOP D0xAF,// 12: R2_MERGENOTPEN DPno0xF0,// 13: R2_COPYPEN P0xF5,// 14: R2_MERGEPENNOT PDno0xFA,// 15: R2_MERGEPEN DPo0xFF,// 16: R2_WHITE 1};#defineMIX_TO_ROP4(mix)\(((ULONG)gajRop2ToRop3[((mix)-1)0xF])|\((ULONG)gajRop2ToRop3[(((mix)8)-1)0xF]8))MIX 编码Windows 惯例低 8 位 前景 ROP2高 8 位 背景 ROP2R2_BLACK1 ~ R2_WHITE16。MIX_TO_ROP4用(mix-1)0xF索引-1对齐 1~16 → 0~15把前后景分别映射成 ROP3低字节 前景高字节 背景恰好构成 ROP4。例默认R2_COPYPEN(13)→0xF0OPAQUE 背景 → ROP4 0xF0F0。3.2 FillSolid — 纯色矩形逐扫描线填充BOOL APIENTRYFillSolid(SURFOBJ*pso,PRECTL pRect,ULONG iColor){LONG y;ULONG LineWidth;LineWidthpRect-right-pRect-left;for(ypRect-top;ypRect-bottom;y){DibFunctionsForBitmapFormat[pso-iBitmapFormat].DIB_HLine(pso,pRect-left,pRect-right,y,iColor);}returnTRUE;}实现要点逐行调用按位深分发的DIB_HLine函数签名PFN_DIB_HLine(SURFOBJ*, LONG left, LONG right, LONG y, ULONG iColor)见 [dib.h](file:///d:/reactos/win32ss/gdi/dib/dib.h#L36)与 lineto.c 的水平线快路径同源。不做 ROP/掩码处理只做整行覆盖写——专用于纯色直接擦除场景。唯一调用者是EngEraseSurface[surface.c](file:///d:/reactos/win32ss/gdi/eng/surface.c#L583-L593)用于设备表面初始擦除显示驱动DrvEnableSurface之后和EngEraseSurfaceDDI 服务。3.3 EngPaint — MIX→ROP4 并转发 BitBltBOOL APIENTRYEngPaint(SURFOBJ*pso,CLIPOBJ*pco,BRUSHOBJ*pbo,POINTL*pptlBrushOrg,MIX mix){ROP4 rop4;rop4MIX_TO_ROP4(mix);/* MIX(2×ROP2) → ROP4 */NT_ASSERT(!ROP4_USES_SOURCE(rop4));/* 填充永不使用源表面 */returnIntEngBitBlt(pso,NULL,NULL,pco,NULL,pco-rclBounds,NULL,NULL,pbo,pptlBrushOrg,rop4);/* 无源/无掩码的图案填充 */}设计本质区域填充在引擎层被规约为一次特化 BitBlt——目标表面 画刷图案源和掩码均为 NULL。由于 ROP4 必然不依赖源ROP4_USES_SOURCE断言保证安全。之后完全复用《分析_4》第 3.1 节的 IntEngBitBlt 管线目标 HOOK_BITBLT → 驱动 DrvBitBlt否则 EngBitBlt 软件路径纯色走BltPatCopy快路径。这也是引擎复用哲学的又一体现不写专门的填充渲染器。3.4 IntEngPaint — DrvPaint 挂钩决策BOOL APIENTRYIntEngPaint(SURFOBJ*pso,CLIPOBJ*pco,BRUSHOBJ*pbo,POINTL*pptlBrushOrg,MIX mix){SURFACE*psurfCONTAINING_RECORD(pso,SURFACE,SurfObj);/* 设备表面且挂钩了 DrvPaint → 调驱动否则引擎软件实现 */if((pso-iType!STYPE_BITMAP)(psurf-flagsHOOK_PAINT)){returnGDIDEVFUNCS(pso).Paint(pso,pco,pbo,pptlBrushOrg,mix);}returnEngPaint(pso,pco,pbo,pptlBrushOrg,mix);}挂钩条件两层pso-iType ! STYPE_BITMAP— 只有设备表面屏幕才可能让硬件接管内存位图一律引擎实现。psurf-flags HOOK_PAINT— 表面是否在EngAssociateSurface时声明HOOK_PAINT。4. DDI 交互DrvPaint 与 HOOK_PAINT4.1 标志与函数表HOOK_PAINT 0x00000010定义于 [winddi.h](file:///d:/reactos/sdk/include/psdk/winddi.h#L1424)注意eng/surface.h中 HOOK_* 宏被注释掉了实际编译用的是 winddi.h 的定义。DDI 函数表DRIVER_FUNCTIONS.Paint[ntgdityp.h](file:///d:/reactos/win32ss/include/ntgdityp.h#L586)vga 驱动以{INDEX_DrvPaint, (PFN) DrvPaint}注册[vga/enable.c](file:///d:/reactos/win32ss/drivers/displays/vga/main/enable.c#L25)并在关联表面时声明HOOK_BITBLT | HOOK_PAINT | HOOK_LINETO | HOOK_COPYBITS | ...[enable.c](file:///d:/reactos/win32ss/drivers/displays/vga/main/enable.c#L491)。4.2 vga 驱动 DrvPaint 示例[vga/objects/paint.c](file:///d:/reactos/win32ss/drivers/displays/vga/objects/paint.c#L183-L201)DrvPaint(Surface, ClipRegion, Brush, BrushOrigin, Mix) ↓ 取 iSolidColor当前只支持纯色画刷 VGADDIPaintRgn(...) ├─ DC_RECT → 直接 VGADDIFillSolid(整矩形) └─ 其他 → CLIPOBJ_cEnumStart CLIPOBJ_bEnum 枚举矩形 逐矩形 VGADDIFillSolid ↓ VGADDIFillSolid: 按字节对齐拆分 左掩码/中间整字节/右掩码 通过 VGA 图形控制器端口GRA_I/GRA_D 写掩码 直接写显存规避 CPU 不可寻址字节注意 vga 的DrvPaint绕过引擎直接操作端口体现了 DDI 挂钩的意义硬件驱动用更高效的方式完成 GDI 兜底要做的同一件事。5. 服务导出与调用方函数导出调用方EngPaint[win32k.spec](file:///d:/reactos/win32ss/win32k.spec#L103) 直接导出DDI 服务驱动可选调用内核经 IntEngPaint 调用NtGdiEngPaint[gdi32.spec](file:///d:/reactos/win32ss/gdi/gdi32/gdi32.spec#L187) → win32u → [umpdstubs.c](file:///d:/reactos/win32ss/gdi/eng/umpdstubs.c#L319-L331)UMPD 用户模式驱动路径当前 UNIMPLEMENTED 桩FillSolid引擎内部[paint.h](file:///d:/reactos/win32ss/gdi/ntgdi/paint.h#L3) 声明EngEraseSurfaceIntEngPaint内部IntGdiFillRgn内核驱动路径: DrvPaint ← GDIDEVFUNCS(pso).Paint ← IntEngPaint ← IntGdiFillRgn UMPD 驱动路径: EngPaint ← NtGdiEngPaint(umpdstubs) ← gdi32.EngPaint ← UMPD 软件兜底路径: EngPaint → IntEngBitBlt → EngBitBlt → DIB6. 与《分析_4》标注对照标注核对《_4》第 37 行paint.c 「区域填充」准确。补充实现主体是EngPaint/IntEngPaint/FillSolid其中EngPaint是 win32k 导出的 DDI 服务NtGdiEngPaintUMPD 路径为桩7. 源码索引文件关键内容[paint.c](file:///d:/reactos/win32ss/gdi/eng/paint.c)gajRop2ToRop3 表、FillSolid、EngPaint、IntEngPaint[eng.h](file:///d:/reactos/win32ss/gdi/eng/eng.h#L5-L7)MIX_TO_ROP4 宏、gajRop2ToRop3 外部声明[inteng.h](file:///d:/reactos/win32ss/gdi/eng/inteng.h#L143)IntEngPaint 声明[ntgdi/bitblt.c](file:///d:/reactos/win32ss/gdi/ntgdi/bitblt.c#L1228-L1333)IntGdiFillRgn / IntGdiPaintRgn / NtGdiFillRgn / NtGdiFrameRgn[ntgdi/paint.h](file:///d:/reactos/win32ss/gdi/ntgdi/paint.h)FillSolid 声明[surface.c](file:///d:/reactos/win32ss/gdi/eng/surface.c#L583-L593)EngEraseSurface → FillSolid[bitblt.c](file:///d:/reactos/win32ss/gdi/eng/bitblt.c#L1104)IntEngBitBltEngPaint 转发目标[winddi.h](file:///d:/reactos/sdk/include/psdk/winddi.h#L1424)HOOK_PAINT 定义[vga/objects/paint.c](file:///d:/reactos/win32ss/drivers/displays/vga/objects/paint.c)DrvPaint / VGADDIPaintRgn / VGADDIFillSolid硬件填充示例[umpdstubs.c](file:///d:/reactos/win32ss/gdi/eng/umpdstubs.c#L319-L331)NtGdiEngPaintUMPD 桩关联模块上层调用者[ntgdi/](file:///d:/reactos/win32ss/gdi/ntgdi/bitblt.c)FillRgn/PaintRgn/FrameRgn 系统调用软件渲染后端[gdi/dib/](file:///d:/reactos/win32ss/gdi/dib/)DIB_HLine / DIB_BitBlt 函数表BitBlt 管线[eng/bitblt.c](file:///d:/reactos/win32ss/gdi/eng/bitblt.c)详见《分析_4》3.1 节硬件填充示例[vga 驱动](file:///d:/reactos/win32ss/drivers/displays/vga/objects/paint.c)本文档基于 ReactOS 源代码win32ss/gdi/eng/paint.c及关联模块分析2026 年 8 月