Chapter 52 · ShapeEx 进阶

填充规则与绘制顺序

FillRule 决定重叠区域的填充判定;PaintOrder 控制填充与描边的先后层次。

52

概念

  • NonZero(默认):路径绕数非零则填充,重叠区域全部填满。
  • EvenOdd:路径交叉奇数次则填充,重叠区域产生"镂空"。
  • PaintOrder:默认先填充后描边;StrokeFirst 则先描边后填充(描边被填充覆盖一半)。

API 速查

常量 / 函数说明
#define XGE_SHAPE_EX_FILL_NON_ZERO 0非零绕数规则
#define XGE_SHAPE_EX_FILL_EVEN_ODD 1奇偶规则
int xgeShapeExFillRule(xge_shape_ex pShape, int iRule)设置填充规则
int xgeShapeExFillRuleGet(xge_shape_ex pShape, int* pRule)获取填充规则
int xgeShapeExPaintOrder(xge_shape_ex pShape, int bStrokeFirst)设置绘制顺序
int xgeShapeExPaintOrderGet(xge_shape_ex pShape, int* pStrokeFirst)获取绘制顺序

编译本章示例

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

示例 1:EvenOdd 镂空效果

xge_shape_ex shape = NULL;
xgeShapeExCreate(&shape);

// 外圆 + 内圆 → 同心圆环
xgeShapeExAppendCircle(shape, 400, 300, 150, 150, 1);
xgeShapeExAppendCircle(shape, 400, 300, 80, 80, 1);

// EvenOdd → 内圆区域镂空
xgeShapeExFillRule(shape, XGE_SHAPE_EX_FILL_EVEN_ODD);
xgeShapeExFillColor(shape, 0x4FD8C2FF);
xgeShapeExDraw(shape, 0.25f);

示例 2:StrokeFirst 内描边

// 先描边后填充 → 描边外半被填充覆盖 → 视觉"内描边"
xgeShapeExStrokeColor(shape, 0xFFB454FF);
xgeShapeExStrokeWidth(shape, 8.0f);
xgeShapeExFillColor(shape, 0x1A1A2EFF);
xgeShapeExPaintOrder(shape, 1);  // StrokeFirst
xgeShapeExDraw(shape, 0.25f);

完整代码

将以下代码保存为 ch52_main.c,编译后运行即可看到填充规则与绘制顺序效果:

#include "xge.h"

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

    // EvenOdd 镂空:同心圆环
    xge_shape_ex ring = NULL;
    xgeShapeExCreate(&ring);
    xgeShapeExAppendCircle(ring, 200, 250, 130, 130, 1);
    xgeShapeExAppendCircle(ring, 200, 250, 70, 70, 1);
    xgeShapeExFillRule(ring, XGE_SHAPE_EX_FILL_EVEN_ODD);
    xgeShapeExFillColor(ring, 0x4FD8C2FF);
    xgeShapeExDraw(ring, 0.25f);
    xgeShapeExDestroy(ring);

    // NonZero 对比:同心圆不会镂空
    xge_shape_ex disc = NULL;
    xgeShapeExCreate(&disc);
    xgeShapeExAppendCircle(disc, 600, 250, 130, 130, 1);
    xgeShapeExAppendCircle(disc, 600, 250, 70, 70, 1);
    xgeShapeExFillRule(disc, XGE_SHAPE_EX_FILL_NON_ZERO);
    xgeShapeExFillColor(disc, 0xFFB454FF);
    xgeShapeExDraw(disc, 0.25f);
    xgeShapeExDestroy(disc);

    // StrokeFirst 内描边效果
    xge_shape_ex btn = NULL;
    xgeShapeExCreate(&btn);
    xgeShapeExAppendRect(btn, 200,440, 400,80, 12,12, 1);
    xgeShapeExStrokeColor(btn, 0xFFB454FF);
    xgeShapeExStrokeWidth(btn, 8.0f);
    xgeShapeExFillColor(btn, 0x1A1A2EFF);
    xgeShapeExPaintOrder(btn, 1);
    xgeShapeExDraw(btn, 0.25f);
    xgeShapeExDestroy(btn);
    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 800;
    d.iHeight = 600;
    d.sTitle  = "ch52 - Fill Rule & Paint Order";
    d.iFlags  = XGE_INIT_VSYNC;
    xgeInit(&d);
    xgeRun(onFrame, NULL);
    xgeUnit();
    return 0;
}

代码讲解

  • EvenOdd:同心圆路径交叉次数为偶数的区域不填充,产生镂空效果。
  • NonZero:同方向同心圆绕数不为零,全部填充(无镂空)。
  • PaintOrder(1):先描边后填充,填充覆盖描边外半,视觉上形成“内描边”。
  • 默认顺序:PaintOrder(0) = 先 Fill 后 Stroke,描边在填充上方。

要点与陷阱

  • 默认 NonZero:大多数简单图形两种规则效果相同。
  • 镂空必须 EvenOdd:同心路径、自交路径产生孔洞。
  • PaintOrder 默认 0:先 Fill 后 Stroke(描边在填充上方)。
  • 内描边技巧:StrokeFirst + 不透明填充 = 只显示内半描边。

进阶练习

  1. 用 EvenOdd + 五角星路径实现镂空星形。
  2. 对比 NonZero / EvenOdd 对自交八字路径的不同填充结果。
填充规则与绘制顺序效果
图:填充规则与绘制顺序渲染效果