| #ifdef RIVE_WASM_MODULE |
| // In-module implementations of the abstract render interfaces the bindings |
| // call through: geometry stays in module memory as RawPath and syncs to the |
| // host lazily; every native contact is a u32 handle crossing a rive_*_v1 |
| // import. Compiled only into the script module (never the runtime). |
| #include "rive/factory.hpp" |
| #include "rive/math/raw_path.hpp" |
| #include "rive/renderer.hpp" |
| #include "rive/wasm/module_render.hpp" |
| #include "rive/wasm/rive_bindings_v1.h" |
| |
| #include <stdio.h> |
| |
| using namespace rive; |
| |
| namespace |
| { |
| |
| class ModuleRenderPath : public RenderPath |
| { |
| public: |
| ModuleRenderPath() = default; |
| ModuleRenderPath(RawPath& rawPath, FillRule rule) : |
| m_rawPath(rawPath), m_fillRule(rule) |
| {} |
| ~ModuleRenderPath() override |
| { |
| if (m_handle != 0) |
| { |
| rive_path_release(m_handle); |
| } |
| } |
| |
| void rewind() override |
| { |
| m_rawPath.rewind(); |
| m_dirty = true; |
| } |
| void fillRule(FillRule value) override |
| { |
| m_fillRule = value; |
| m_dirty = true; |
| } |
| void addPath(CommandPath* path, const Mat2D& transform) override |
| { |
| addRenderPath(path->renderPath(), transform); |
| } |
| void addRenderPath(const RenderPath* path, const Mat2D& transform) override |
| { |
| auto modulePath = static_cast<const ModuleRenderPath*>(path); |
| m_rawPath.addPath(modulePath->m_rawPath, &transform); |
| m_dirty = true; |
| } |
| void moveTo(float x, float y) override |
| { |
| m_rawPath.moveTo(x, y); |
| m_dirty = true; |
| } |
| void lineTo(float x, float y) override |
| { |
| m_rawPath.lineTo(x, y); |
| m_dirty = true; |
| } |
| void cubicTo(float ox, float oy, float ix, float iy, float x, float y) |
| override |
| { |
| m_rawPath.cubicTo(ox, oy, ix, iy, x, y); |
| m_dirty = true; |
| } |
| void close() override |
| { |
| m_rawPath.close(); |
| m_dirty = true; |
| } |
| void addRawPath(const RawPath& path) override |
| { |
| m_rawPath.addPath(path, nullptr); |
| m_dirty = true; |
| } |
| |
| // Pushes geometry to the host when it changed since the last draw. |
| uint32_t sync() |
| { |
| if (m_handle == 0) |
| { |
| m_handle = rive_path_new(); |
| m_dirty = true; |
| } |
| if (m_dirty) |
| { |
| rive_path_update(m_handle, |
| (const uint8_t*)m_rawPath.verbs().data(), |
| (uint32_t)m_rawPath.verbs().size(), |
| (const float*)m_rawPath.points().data(), |
| (uint32_t)m_rawPath.points().size() * 2, |
| (uint32_t)m_fillRule); |
| m_dirty = false; |
| } |
| return m_handle; |
| } |
| |
| private: |
| RawPath m_rawPath; |
| FillRule m_fillRule = FillRule::nonZero; |
| uint32_t m_handle = 0; |
| bool m_dirty = false; |
| }; |
| |
| class ModuleRenderPaint : public RenderPaint |
| { |
| public: |
| ModuleRenderPaint() : m_handle(rive_paint_new()) {} |
| ~ModuleRenderPaint() override { rive_paint_release(m_handle); } |
| |
| void style(RenderPaintStyle value) override |
| { |
| rive_paint_style(m_handle, (uint32_t)value); |
| } |
| void color(ColorInt value) override { rive_paint_color(m_handle, value); } |
| void thickness(float value) override |
| { |
| rive_paint_thickness(m_handle, value); |
| } |
| void join(StrokeJoin value) override |
| { |
| rive_paint_join(m_handle, (uint32_t)value); |
| } |
| void cap(StrokeCap value) override |
| { |
| rive_paint_cap(m_handle, (uint32_t)value); |
| } |
| void feather(float value) override { rive_paint_feather(m_handle, value); } |
| void blendMode(BlendMode value) override |
| { |
| rive_paint_blend_mode(m_handle, (uint32_t)value); |
| } |
| void shader(rcp<RenderShader> value) override; |
| void invalidateStroke() override {} |
| |
| uint32_t handle() const { return m_handle; } |
| |
| private: |
| rcp<RenderShader> m_shader; |
| uint32_t m_handle; |
| }; |
| |
| class ModuleRenderShader : public RenderShader |
| { |
| public: |
| ModuleRenderShader(uint32_t handle) : m_handle(handle) {} |
| ~ModuleRenderShader() override { rive_shader_release(m_handle); } |
| uint32_t handle() const { return m_handle; } |
| |
| private: |
| uint32_t m_handle; |
| }; |
| |
| // Out of line so ModuleRenderShader's handle accessor is visible. |
| void ModuleRenderPaint::shader(rcp<RenderShader> value) |
| { |
| // Pinned so the host handle outlives the assignment. |
| m_shader = value; |
| rive_paint_shader( |
| m_handle, |
| value != nullptr |
| ? static_cast<ModuleRenderShader*>(value.get())->handle() |
| : 0); |
| } |
| |
| // Contents live in module memory and push to the host on unmap. |
| class ModuleRenderBuffer : public RenderBuffer |
| { |
| public: |
| ModuleRenderBuffer(RenderBufferType type, |
| RenderBufferFlags flags, |
| size_t sizeInBytes) : |
| RenderBuffer(type, flags, sizeInBytes), |
| m_handle(rive_buffer_new((uint32_t)type, |
| (uint32_t)flags, |
| (uint32_t)sizeInBytes)), |
| m_store(new uint8_t[sizeInBytes]) |
| {} |
| ~ModuleRenderBuffer() override |
| { |
| rive_buffer_release(m_handle); |
| delete[] m_store; |
| } |
| uint32_t handle() const { return m_handle; } |
| |
| protected: |
| void* onMap() override { return m_store; } |
| void onUnmap() override |
| { |
| rive_buffer_update(m_handle, m_store, (uint32_t)sizeInBytes()); |
| } |
| |
| private: |
| uint32_t m_handle; |
| uint8_t* m_store; |
| }; |
| |
| class ModuleRenderImage : public RenderImage |
| { |
| public: |
| ModuleRenderImage(uint32_t handle) : m_handle(handle) |
| { |
| m_Width = (int)rive_image_width(handle); |
| m_Height = (int)rive_image_height(handle); |
| } |
| ~ModuleRenderImage() override { rive_image_release(m_handle); } |
| uint32_t handle() const { return m_handle; } |
| |
| private: |
| uint32_t m_handle; |
| }; |
| |
| class ModuleFactory : public Factory |
| { |
| public: |
| #if defined(RIVE_CANVAS) && defined(RIVE_ORE) |
| ore::Context* ore() override { return wasmModuleOreContext(); } |
| #endif |
| rcp<RenderBuffer> makeRenderBuffer(RenderBufferType type, |
| RenderBufferFlags flags, |
| size_t sizeInBytes) override |
| { |
| return make_rcp<ModuleRenderBuffer>(type, flags, sizeInBytes); |
| } |
| rcp<RenderShader> makeLinearGradient(float sx, |
| float sy, |
| float ex, |
| float ey, |
| const ColorInt colors[], |
| const float stops[], |
| size_t count) override |
| { |
| return make_rcp<ModuleRenderShader>( |
| rive_shader_linear(sx, sy, ex, ey, colors, stops, (uint32_t)count)); |
| } |
| rcp<RenderShader> makeRadialGradient(float cx, |
| float cy, |
| float radius, |
| const ColorInt colors[], |
| const float stops[], |
| size_t count) override |
| { |
| return make_rcp<ModuleRenderShader>( |
| rive_shader_radial(cx, cy, radius, colors, stops, (uint32_t)count)); |
| } |
| rcp<RenderPath> makeRenderPath(RawPath& rawPath, FillRule rule) override |
| { |
| return make_rcp<ModuleRenderPath>(rawPath, rule); |
| } |
| rcp<RenderPath> makeEmptyRenderPath() override |
| { |
| return make_rcp<ModuleRenderPath>(); |
| } |
| rcp<RenderPaint> makeRenderPaint() override |
| { |
| return make_rcp<ModuleRenderPaint>(); |
| } |
| rcp<RenderImage> decodeImage(Span<const uint8_t>) override |
| { |
| return nullptr; |
| } |
| }; |
| |
| class ModuleRenderer : public Renderer |
| { |
| public: |
| ModuleRenderer(uint32_t handle) : m_handle(handle) {} |
| void save() override { rive_renderer_save(m_handle); } |
| void restore() override { rive_renderer_restore(m_handle); } |
| void transform(const Mat2D& transform) override |
| { |
| rive_renderer_transform(m_handle, |
| transform.xx(), |
| transform.xy(), |
| transform.yx(), |
| transform.yy(), |
| transform.tx(), |
| transform.ty()); |
| } |
| void drawPath(RenderPath* path, RenderPaint* paint) override |
| { |
| rive_renderer_draw_path( |
| m_handle, |
| static_cast<ModuleRenderPath*>(path)->sync(), |
| static_cast<ModuleRenderPaint*>(paint)->handle()); |
| } |
| void clipPath(RenderPath* path) override |
| { |
| rive_renderer_clip_path(m_handle, |
| static_cast<ModuleRenderPath*>(path)->sync()); |
| } |
| void drawImage(const RenderImage* image, |
| ImageSampler sampler, |
| BlendMode blend, |
| float opacity) override |
| { |
| rive_renderer_draw_image( |
| m_handle, |
| static_cast<const ModuleRenderImage*>(image)->handle(), |
| sampler.asKey(), |
| (uint32_t)blend, |
| opacity); |
| } |
| void drawImageMesh(const RenderImage* image, |
| ImageSampler sampler, |
| rcp<RenderBuffer> vertices, |
| rcp<RenderBuffer> uvCoords, |
| rcp<RenderBuffer> indices, |
| uint32_t vertexCount, |
| uint32_t indexCount, |
| BlendMode blend, |
| float opacity) override |
| { |
| rive_renderer_draw_image_mesh( |
| m_handle, |
| static_cast<const ModuleRenderImage*>(image)->handle(), |
| sampler.asKey(), |
| static_cast<ModuleRenderBuffer*>(vertices.get())->handle(), |
| static_cast<ModuleRenderBuffer*>(uvCoords.get())->handle(), |
| static_cast<ModuleRenderBuffer*>(indices.get())->handle(), |
| (uint32_t)blend, |
| opacity); |
| } |
| void modulateOpacity(float) override {} |
| void modulateColor(ColorInt color, bool replace) override |
| { |
| rive_renderer_modulate_color(m_handle, color, replace); |
| } |
| uint32_t handle() const { return m_handle; } |
| |
| private: |
| uint32_t m_handle; |
| }; |
| |
| } // namespace |
| |
| namespace rive |
| { |
| uint32_t wasmModuleImageHandle(RenderImage* image) |
| { |
| return image != nullptr ? static_cast<ModuleRenderImage*>(image)->handle() |
| : 0; |
| } |
| |
| Factory* wasmModuleFactory() |
| { |
| static ModuleFactory factory; |
| return &factory; |
| } |
| |
| std::unique_ptr<Renderer> makeWasmModuleRenderer(uint32_t handle) |
| { |
| return std::make_unique<ModuleRenderer>(handle); |
| } |
| |
| uint32_t wasmModuleRendererHandle(Renderer* renderer) |
| { |
| return renderer != nullptr |
| ? static_cast<ModuleRenderer*>(renderer)->handle() |
| : 0; |
| } |
| |
| rcp<RenderImage> makeWasmModuleRenderImage(uint32_t handle) |
| { |
| return make_rcp<ModuleRenderImage>(handle); |
| } |
| } // namespace rive |
| |
| #endif |