Chapter 79 · SVG 引擎

Paint 树遍历与遮罩

Traverse 深度优先遍历 Paint 树,Mask/Clip 为单个节点设置遮罩和裁剪。

79

API 速查

函数说明
int xgeSvgPaintTraverse(xge_svg_paint pPaint, xge_svg_paint_visit_proc onPaint, void* pUser)深度优先遍历
int xgeSvgPaintMaskShapeSet(xge_svg_paint pPaint, xge_shape_ex pTarget, int iMethod)Shape 遮罩
int xgeSvgPaintMaskSceneSet(xge_svg_paint pPaint, xge_shape_ex_scene pTarget, int iMethod)Scene 遮罩
int xgeSvgPaintMaskClear(xge_svg_paint pPaint)清除遮罩
int xgeSvgPaintMaskGet(xge_svg_paint pPaint, int* pMethod, int* pTargetType, xge_shape_ex* ppShape, xge_shape_ex_scene* ppScene)查询遮罩
int xgeSvgPaintClipShapeSet(xge_svg_paint pPaint, xge_shape_ex pClipShape)设置裁剪
int xgeSvgPaintClipClear(xge_svg_paint pPaint)清除裁剪
int xgeSvgPaintClipRectGet(xge_svg_paint pPaint, xge_rect_t* pRect, int* pEnabled)获取矩形裁剪
int xgeSvgPaintGetBounds(xge_svg_paint pPaint, float fTolerance, xge_rect_t* pBounds)节点包围盒

编译本章示例

gcc -O2 -I. -o ch79.exe ch79_main.c build\xge.lib -lm -lws2_32 -liphlpapi -lgdi32 -luser32 -lshell32 -lopengl32 -lole32 -lwinmm -lavrt
.\ch79.exe

示例:遍历 + 圆形遮罩

// 遍历所有节点,统计数量
static int s_count = 0;
static void OnPaint(xge_svg_paint p, void* u) {
    s_count++;
}
xge_svg_paint root;
xgeSvgPaintGetPicture(svg, &root);
xgeSvgPaintTraverse(root, OnPaint, NULL);

// 为头像节点设置圆形遮罩
xge_svg_paint avatar;
xgeSvgPaintGetByName(svg, "avatar", &avatar);

xge_shape_ex circle = NULL;
xgeShapeExCreate(&circle);
xgeShapeExAppendCircle(circle, 32,32, 30, 1);
xgeShapeExFillColor(circle, 0xFFFFFFFF);

xgeSvgPaintMaskShapeSet(avatar, circle,
    XGE_SHAPE_EX_MASK_ALPHA);
xgeShapeExDestroy(circle);

完整代码

将以下代码保存为 ch79_main.c,编译后运行即可看到 Paint 树遍历 + 圆形遮罩效果:

#include "xge.h"

static xge_svg g_svg = NULL;
static int g_iCount = 0;

static void onPaint(xge_svg_paint p, void* u) {
    (void)u;
    g_iCount++;
    // 为每个节点设置半透明
    xgeSvgPaintOpacitySet(p, 0.7f);
}

static int onFrame(void* pUser) {
    (void)pUser;
    xgeClear(0x081114FF);

    xge_rect_t dst = { 250, 100, 300, 400 };
    xgeSvgDraw(g_svg, dst, 0.25f);
    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 800;
    d.iHeight = 600;
    d.sTitle  = "ch79 - Paint Traverse & Mask";
    d.iFlags  = XGE_INIT_VSYNC;
    xgeInit(&d);

    xgeSvgCreate(&g_svg);
    xgeSvgLoad(g_svg, "assets/character.svg");

    // 遍历所有节点
    xge_svg_paint root = NULL;
    xgeSvgPaintGetPicture(g_svg, &root);
    xgeSvgPaintTraverse(root, onPaint, NULL);

    // 为头像节点设置圆形遮罩
    xge_svg_paint avatar = NULL;
    xgeSvgPaintGetByName(g_svg, "avatar", &avatar);
    if (avatar) {
        xge_shape_ex circle = NULL;
        xgeShapeExCreate(&circle);
        xgeShapeExAppendCircle(circle, 32,32, 30, 1);
        xgeShapeExFillColor(circle, 0xFFFFFFFF);
        xgeSvgPaintMaskShapeSet(avatar, circle,
            XGE_SHAPE_EX_MASK_ALPHA);
        xgeShapeExDestroy(circle);
    }

    xgeRun(onFrame, NULL);
    xgeSvgDestroy(g_svg);
    xgeUnit();
    return 0;
}

代码讲解

  • PaintTraverse:深度优先遍历所有节点,回调中设置每个节点 0.7 透明度。
  • PaintGetPicture:获取根 Paint 节点,作为遍历起点。
  • MaskShapeSet:为头像节点设置圆形 Alpha 遮罩,实现圆形头像效果。
  • Mask vs Clip:Mask 支持半透明过渡,Clip 是硬边裁切。

要点与陷阱

  • Traverse:回调中不应修改树结构(Add/Remove)。
  • Mask vs Clip:Mask 支持半透明过渡,Clip 是硬边裁切。
  • Paint 遮罩坐标:在 Paint 节点的局部坐标系中定义。
  • GetBounds:返回节点及其子树的包围盒。

进阶练习

  1. 用 Traverse 实现"全部隐藏"和"按类型筛选"。
  2. 为 SVG 中的图片元素添加圆角裁剪。
Paint 树遍历与遮罩效果
图:Paint 树遍历与遮罩渲染效果