外部渲染器
RiveRenderer 和 rive::gpu::RenderContext 只是其中一种可用后端。核心运行时与渲染器无关 —— 你传给 StateMachineInstance::draw 的任何对象只需要实现两个接口:
rive::Renderer—— 接收绘制 / 裁剪 / 保存 / 恢复命令。rive::Factory—— 在文件导入期间,从原始字节或参数创建RenderPath、RenderPaint、RenderImage、RenderShader、RenderBuffer、Font、AudioSource。
实现这两者后,Rive 就会将所有内容都路由到它们。
使用场景
- 你已经有一个 2D 矢量引擎(Skia、Direct2D、自定义引擎),并希望 Rive 通过它来渲染。
- 你正在面向 Rive 尚未提供后端的平台。
- 你需要 CPU 侧命中测试或分析 —— 渲染到一个只记录命令数量的 no-op
Renderer。
如果你只需要在 Windows / macOS / iOS / Linux / Web / Android 上使用 Rive,请优先使用内置渲染器 —— 它们速度更快,功能也更完整。
Renderer 接口
class Renderer {
public:
virtual void save() = 0;
virtual void restore() = 0;
virtual void transform(const Mat2D&) = 0;
virtual void drawPath(RenderPath*, RenderPaint*) = 0;
virtual void clipPath(RenderPath*) = 0;
virtual void drawImage(const RenderImage*,
ImageSampler, BlendMode,
float opacity) = 0;
virtual void drawImageMesh(const RenderImage*, ImageSampler,
rcp<RenderBuffer> verts_f32,
rcp<RenderBuffer> uvs_f32,
rcp<RenderBuffer> indices_u16,
uint32_t vertexCount,
uint32_t indexCount,
BlendMode, float opacity) = 0;
virtual void modulateOpacity(float opacity) = 0;
};
有几个约束需要提前了解:
save/restore会形成一个栈,并且必须捕获:当前变换、裁剪栈以及调制后的不透明度。clipPath会添加到当前裁剪中 —— 永远不会替换当前裁剪。modulateOpacity是乘法式的;0.5后接0.2⇒ 有效不透明度为0.1,直到下一次restore。drawImageMesh的索引是 16 位;顶点 / UV 缓冲区是紧密排列的float二元组。
Factory 接口
class Factory {
public:
virtual rcp<RenderBuffer> makeRenderBuffer(
RenderBufferType, RenderBufferFlags, size_t sizeInBytes) = 0;
virtual rcp<RenderShader> makeLinearGradient(
float sx, float sy, float ex, float ey,
const ColorInt colors[], const float stops[], size_t count) = 0;
virtual rcp<RenderShader> makeRadialGradient(
float cx, float cy, float radius,
const ColorInt colors[], const float stops[], size_t count) = 0;
virtual rcp<RenderPath> makeRenderPath(RawPath&, FillRule) = 0;
virtual rcp<RenderPath> makeEmptyRenderPath() = 0;
virtual rcp<RenderPaint> makeRenderPaint() = 0;
virtual rcp<RenderImage> decodeImage(Span<const uint8_t>) = 0;
rcp<Font> decodeFont (Span<const uint8_t>); // non-virtual helper
rcp<AudioSource> decodeAudio(Span<const uint8_t>); // non-virtual helper
};
注意事项:
- 渐变的
colors[]是打包的 ARGB 整数;stops[]是归一化到 0..1 的值。 makeRenderPath(RawPath&, FillRule)可能会窃取该路径的存储 —— 调用后应将输入视为已被移动。decodeImage会收到原始 PNG / JPEG / WebP 字节等。你可以解码为渲染器偏好的任意像素格式,并将结果包装在RenderImage的子类中。decodeFont/decodeAudio是非虚拟辅助函数,会分发到 Rive 内置的 HarfBuzz / miniaudio 路径。它们不是子类重写点;如需自定义字体 shaping 或音频集成,请使用实际的虚拟Factory扩展点。
RenderPath / RenderPaint 子类
每个子类都保存后端在绘制时读取的状态:
class MyPath : public RenderPath {
public:
void rewind() override { /* clear */ }
void moveTo(float x, float y) override { /* … */ }
void lineTo(float x, float y) override { /* … */ }
void cubicTo(float ox, float oy,
float ix, float iy,
float x, float y) override { /* … */ }
void close() override { /* … */ }
void addRenderPath(RenderPath*, const Mat2D&) override { /* … */ }
void addRawPath(const RawPath&) override { /* … */ }
};
class MyPaint : public RenderPaint {
public:
void style(RenderPaintStyle) override;
void color(unsigned int) override;
void thickness(float) override;
void join(StrokeJoin) override;
void cap(StrokeCap) override;
void blendMode(BlendMode) override;
void shader(rcp<RenderShader>) override;
void invalidateStroke() override;
};
(方法签名与 rive/command_path.hpp(用于 CommandPath)和 rive/renderer.hpp(用于 RenderPath 和 RenderPaint)一致 —— 请阅读这些文件以查看完整集合。)
接入方式
class MyFactory : public rive::Factory { /* ... */ };
MyFactory factory;
rcp<File> file = File::import(bytes, &factory);
auto artboard = file->artboardDefault();
auto sm = artboard->defaultStateMachine();
MyRenderer renderer;
sm->advanceAndApply(dt);
sm->draw(&renderer);
这条路径中没有 RenderContext —— 你的 Renderer 负责发起 GPU 调用(或缓冲它们、统计它们,或执行任何你想做的事情)。
参考实现
以下所有路径都位于 rive-runtime 仓库中。
| 项目 | 用途 | 位置 |
|---|---|---|
rive::gpu::RenderContext | Pixel-local-storage GPU 渲染器 | renderer/src/ |
SkiaFactory / SkiaRenderer | Skia | skia/renderer/ |
CGFactory / CGRenderer | CoreGraphics | cg_renderer/ |
SokolFactory | Sokol(曲面细分) | tess/src/sokol/ |
Skia 和 CoreGraphics 实现是“转发到现有 2D 引擎”的 Renderer 最直接的模板。