EUI-NEO

基础图元与文本图元

EUI-NEO 技术文档。

基础图元与文本图元

底层 OpenGL 绘制能力主要在:

页面和组件推荐使用 DSL,不要直接创建 primitive。core::dsl::Runtime 会把 DSL 元素同步到底层绘制对象,并统一处理 layout、事件、动画、dirty rect、framebuffer cache 和 DPI scale。

DSL 到 Primitive

DSL 元素底层对象说明
ui.row / ui.column / ui.stack无绘制 primitive只参与布局、裁剪、z-index、事件命中和渲染遍历
ui.rectRoundedRectPrimitive圆角矩形、颜色、渐变、边框、阴影、blur、transform
ui.polygonPolygonPrimitive多边形填充,当前 piechart 扇区和 tooltip 指针使用它
ui.text / ui.labelTextPrimitiveSDF 文本,支持字体、字号、换行、对齐
ui.imageImagePrimitive本地图片、网络图片、SVG、Bing daily 图片

组件层只组合这些 DSL 元素。例如 components::button(ui, id) 内部仍然是 Stack + Rect + Row + Text,不是新的渲染对象。

基础类型

struct Vec2 {
    float x = 0.0f;
    float y = 0.0f;
};

struct Color {
    float r = 1.0f;
    float g = 1.0f;
    float b = 1.0f;
    float a = 1.0f;
};

struct Rect {
    float x = 0.0f;
    float y = 0.0f;
    float width = 0.0f;
    float height = 0.0f;
};

坐标默认是逻辑坐标,左上角为原点,x 向右,y 向下。Runtime 渲染时会乘以 dpiScale 转成 framebuffer 像素坐标。

Rect::contains(...) 用于基础矩形命中测试。当前 transform 后的 hit-test 仍按布局矩形计算,不跟随旋转和缩放后的真实形状。

视觉结构

enum class GradientDirection {
    Horizontal,
    Vertical
};

struct Gradient {
    bool enabled = false;
    Color start;
    Color end;
    GradientDirection direction = GradientDirection::Vertical;
};

struct Border {
    float width = 0.0f;
    Color color;
};

struct Shadow {
    bool enabled = false;
    Vec2 offset = {0.0f, 4.0f};
    float blur = 8.0f;
    float spread = 0.0f;
    Color color = {0.0f, 0.0f, 0.0f, 0.28f};
};

struct Transform {
    Vec2 translate = {0.0f, 0.0f};
    Vec2 scale = {1.0f, 1.0f};
    float rotate = 0.0f;
    Vec2 origin = {0.5f, 0.5f};
};

这些结构由 DSL builder 写入目标状态,再由 Runtime 按 id 推进动画并同步到底层 primitive。

Rect 图元

RoundedRectPrimitive 是最常用的基础图元。

ui.rect("card")
    .size(360.0f, 260.0f)
    .color({0.10f, 0.12f, 0.16f, 1.0f})
    .radius(18.0f)
    .border(1.0f, {0.23f, 0.29f, 0.38f, 1.0f})
    .shadow(24.0f, 0.0f, 10.0f, {0.0f, 0.0f, 0.0f, 0.28f})
    .transition(0.18f)
    .build();

支持能力:

.states(...) 是 Runtime 维护的交互状态,不是 primitive 自己监听鼠标。它会开启 interactive,并根据 hover / pressed 插值颜色。

Polygon 图元

PolygonPrimitive 使用 GL_TRIANGLE_FAN 绘制点集。点坐标是元素局部坐标,实际渲染时会叠加元素 bounds 和 transform。

ui.polygon("tooltip.pointer")
    .size(24.0f, 10.0f)
    .points({
        {12.0f, 0.0f},
        {24.0f, 10.0f},
        {0.0f, 10.0f},
    })
    .color({0.12f, 0.14f, 0.18f, 1.0f})
    .build();

当前支持:

当前不支持 polygon 的渐变、边框、圆角、阴影和 blur。饼图扇区由组件把圆弧拆成多个点后交给 ui.polygon(...) 绘制。

TextPrimitive

文本图元在 core/text.hcore/text.cpp。它使用 3rd/stb_truetype.h 生成 glyph,并用 SDF 方式渲染。

ui.text("title")
    .size(420.0f, 48.0f)
    .text("Hello")
    .customFont("YouSheBiaoTiHei")
    .fontSize(24.0f)
    .lineHeight(30.0f)
    .color({1.0f, 1.0f, 1.0f, 1.0f})
    .horizontalAlign(core::HorizontalAlign::Center)
    .verticalAlign(core::VerticalAlign::Center)
    .build();

TextStyle 当前包含:

文本实现会缓存字体文件数据、glyph、layout 结果和顶点数据。只有文本内容、位置、字号、换行、对齐、visual scale 等影响排版或顶点的数据变化时才会重建顶点;单纯颜色和透明度变化不需要重新排版。

ImagePrimitive

图片图元在 core/image.hcore/image.cpp

ui.image("logo")
    .size(120.0f, 120.0f)
    .source("assets/icon.svg")
    .contain()
    .radius(18.0f)
    .build();

支持来源:

支持 fit:

网络图片和 Bing daily 可能存在 pending load。ImagePrimitive::consumeRemoteImageReady() 用于 Runtime 感知远程图片已准备好并触发刷新。

Shadow 与 Blur

阴影会扩大 dirty rect。Runtime 会把阴影区域纳入脏区,避免局部重绘时留下旧阴影。

ui.rect("card")
    .shadow(24.0f, 0.0f, 10.0f, {0.0f, 0.0f, 0.0f, 0.28f})
    .build();

blur(...) 当前是 backdrop blur,不是独立 layer blur。它依赖背后的 framebuffer cache 内容:

ui.rect("glass")
    .color({0.90f, 0.96f, 1.0f, 0.24f})
    .blur(18.0f)
    .build();

为了避免 blur 采样到半旧半新的 cache,Runtime 会检查 dirty rect 是否触碰 blur 的保护区域;触碰时本帧会升级为 full redraw,优先保证正确性。代价是 blur 附近交互更贵,所以业务页面应谨慎使用大面积 blur。

Transform

ui.rect("actor")
    .translate(20.0f, 0.0f)
    .scale(1.1f)
    .rotate(0.2f)
    .transformOrigin(0.5f, 0.5f)
    .transition(0.24f)
    .animate(core::AnimProperty::Transform)
    .build();

Runtime 的 dirty rect 会考虑 transform 后的外接矩形,避免旋转或缩放后残留旧区域。当前 hit-test 仍按未 transform 的布局矩形计算。

Dirty Rect 关系

Runtime 会按 id 缓存 RectInstancePolygonInstanceTextInstanceImageInstance。每帧更新时:

渲染时优先使用离屏 framebuffer cache + scissor 局部重绘。需要完整正确性的场景,例如窗口尺寸变化、页面重组、blur 保护区被触碰,会走 full redraw。

当前限制