跳到主要内容

外部渲染器

RiveRendererrive::gpu::RenderContext 只是其中一种可用后端。核心运行时与渲染器无关 —— 你传给 StateMachineInstance::draw 的任何对象只需要实现两个接口:

  • rive::Renderer —— 接收绘制 / 裁剪 / 保存 / 恢复命令。
  • rive::Factory —— 在文件导入期间,从原始字节或参数创建 RenderPathRenderPaintRenderImageRenderShaderRenderBufferFontAudioSource

实现这两者后,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(用于 RenderPathRenderPaint)一致 —— 请阅读这些文件以查看完整集合。)

接入方式

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::RenderContextPixel-local-storage GPU 渲染器renderer/src/
SkiaFactory / SkiaRendererSkiaskia/renderer/
CGFactory / CGRendererCoreGraphicscg_renderer/
SokolFactorySokol(曲面细分)tess/src/sokol/

Skia 和 CoreGraphics 实现是“转发到现有 2D 引擎”的 Renderer 最直接的模板。