Chapter 48 · ShapeEx 填充与描边

描边:斜接限制与非缩放

MiterLimit 防止锐角处产生过长尖刺;NonScaling 让描边宽度不受变换缩放影响。

48

概念

  • MiterLimit:尖角长度 / 线宽 的比值上限,超出则自动退化为 Bevel。
  • 默认值 4.0:SVG 标准默认值,适合大多数场景。
  • NonScaling:启用后描边宽度保持像素恒定,不随 Shape 缩放变换改变。

API 速查

函数说明
int xgeShapeExStrokeMiterLimit(xge_shape_ex pShape, float fLimit)设置斜接限制
int xgeShapeExStrokeMiterLimitGet(xge_shape_ex pShape, float* pLimit)获取斜接限制
int xgeShapeExStrokeNonScaling(xge_shape_ex pShape, int bNonScaling)设置非缩放描边
int xgeShapeExStrokeNonScalingGet(xge_shape_ex pShape, int* pNonScaling)获取非缩放状态

编译本章示例

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

示例 1:MiterLimit 对比

// 锐角三角形 — 观察不同 MiterLimit 效果
float limits[] = { 1.0f, 4.0f, 20.0f };

for (int i = 0; i < 3; i++) {
    xge_shape_ex s = NULL;
    xgeShapeExCreate(&s);

    // 尖锐三角形
    xgeShapeExMoveTo(s, 100+i*250, 400);
    xgeShapeExLineTo(s, 200+i*250, 100);
    xgeShapeExLineTo(s, 300+i*250, 400);
    xgeShapeExClose(s);

    xgeShapeExStrokeColor(s, 0xFFB454FF);
    xgeShapeExStrokeWidth(s, 8.0f);
    xgeShapeExStrokeJoin(s, XGE_SHAPE_EX_JOIN_MITER);
    xgeShapeExStrokeMiterLimit(s, limits[i]);
    xgeShapeExDraw(s, 0.25f);
    xgeShapeExDestroy(s);
}

示例 2:NonScaling 描边

xge_shape_ex shape = NULL;
xgeShapeExCreate(&shape);
xgeShapeExAppendRect(shape, -50,-50, 100,100, 0,0, 1);
xgeShapeExStrokeColor(shape, 0x4FD8C2FF);
xgeShapeExStrokeWidth(shape, 2.0f);

// 启用非缩放 → 放大 3 倍后描边仍为 2px
xgeShapeExStrokeNonScaling(shape, 1);
xgeShapeExTransformScale(shape, 3.0f, 3.0f);
xgeShapeExTransformTranslate(shape, 400, 300);
xgeShapeExDraw(shape, 0.25f);

完整代码

将以下代码保存为 ch48_main.c,编译后运行即可看到 MiterLimit 与 NonScaling 描边效果:

#include "xge.h"

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

    // MiterLimit 对比:三个尖锐三角形
    float limits[] = { 1.0f, 4.0f, 20.0f };
    for (int i = 0; i < 3; i++) {
        xge_shape_ex s = NULL;
        xgeShapeExCreate(&s);
        xgeShapeExMoveTo(s, 60+i*250, 250);
        xgeShapeExLineTo(s, 160+i*250, 50);
        xgeShapeExLineTo(s, 260+i*250, 250);
        xgeShapeExClose(s);
        xgeShapeExStrokeColor(s, 0xFFB454FF);
        xgeShapeExStrokeWidth(s, 8.0f);
        xgeShapeExStrokeJoin(s, XGE_SHAPE_EX_JOIN_MITER);
        xgeShapeExStrokeMiterLimit(s, limits[i]);
        xgeShapeExDraw(s, 0.25f);
        xgeShapeExDestroy(s);
    }

    // NonScaling 描边:放大 3 倍后描边仍为 2px
    xge_shape_ex ns = NULL;
    xgeShapeExCreate(&ns);
    xgeShapeExAppendRect(ns, -50,-50, 100,100, 0,0, 1);
    xgeShapeExStrokeColor(ns, 0x4FD8C2FF);
    xgeShapeExStrokeWidth(ns, 2.0f);
    xgeShapeExStrokeNonScaling(ns, 1);
    xgeShapeExTransformScale(ns, 3.0f, 3.0f);
    xgeShapeExTransformTranslate(ns, 400, 430);
    xgeShapeExDraw(ns, 0.25f);
    xgeShapeExDestroy(ns);
    return XGE_OK;
}

int main(void) {
    xge_desc_t d = {0};
    d.iWidth  = 800;
    d.iHeight = 600;
    d.sTitle  = "ch48 - MiterLimit & NonScaling";
    d.iFlags  = XGE_INIT_VSYNC;
    xgeInit(&d);
    xgeRun(onFrame, NULL);
    xgeUnit();
    return 0;
}

代码讲解

  • MiterLimit:当尖角长度/宽度比超过 Limit 时,自动退化为 Bevel,防止尖刺。
  • Limit=1.0:几乎所有尖角都退化;Limit=20 则保留尖锐效果。
  • NonScaling:启用后描边宽度不受图形缩放影响,始终为设定像素值。
  • 典型场景:地图缩放时保持边界线宽度恒定。

要点与陷阱

  • 仅 Miter Join 有效:Round/Bevel 连接不受 MiterLimit 影响。
  • Limit = 1.0:几乎所有尖角都退化为 Bevel。
  • NonScaling 典型场景:地图缩放时保持边界线宽度恒定。
  • 性能:NonScaling 需要额外计算逆变换,大量图形时注意开销。

进阶练习

  1. 绘制星形,逐步降低 MiterLimit 观察尖角退化过程。
  2. 实现可缩放画布:NonScaling 描边 + 动态 Scale 变换。
描边:斜接限制与非缩放效果
图:描边:斜接限制与非缩放渲染效果