| #include "bindings_c2d.hpp" |
| |
| #include "rive/factory.hpp" |
| #include "rive/renderer.hpp" |
| #include "rive/math/path_types.hpp" |
| #include "utils/factory_utils.hpp" |
| |
| #include "js_alignment.hpp" |
| |
| #include <emscripten.h> |
| #include <emscripten/bind.h> |
| #include <emscripten/val.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string> |
| #include <vector> |
| |
| using namespace emscripten; |
| |
| class RendererWrapper : public wrapper<rive::Renderer> |
| { |
| public: |
| EMSCRIPTEN_WRAPPER(RendererWrapper); |
| |
| void save() override { call<void>("save"); } |
| |
| void restore() override { call<void>("restore"); } |
| |
| void clear() { call<void>("clear"); } |
| |
| void transform(const rive::Mat2D& transform) override |
| { |
| call<void>("transform", |
| transform.xx(), |
| transform.xy(), |
| transform.yx(), |
| transform.yy(), |
| transform.tx(), |
| transform.ty()); |
| } |
| |
| void align(rive::Fit fit, JsAlignment alignment, const rive::AABB& foo, const rive::AABB& bar) |
| { |
| transform(computeAlignment(fit, convertAlignment(alignment), foo, bar)); |
| } |
| |
| void drawPath(rive::RenderPath* path, rive::RenderPaint* paint) override |
| { |
| call<void>("_drawPath", path, paint); |
| } |
| |
| void clipPath(rive::RenderPath* path) override { call<void>("_clipPath", path); } |
| |
| void drawImage(const rive::RenderImage* image, rive::BlendMode value, float opacity) override |
| { |
| call<void>("_drawImage", image, value, opacity); |
| } |
| |
| void drawImageMesh(const rive::RenderImage* image, |
| rive::rcp<rive::RenderBuffer> vertices_f32, |
| rive::rcp<rive::RenderBuffer> uvCoords_f32, |
| rive::rcp<rive::RenderBuffer> indices_u16, |
| rive::BlendMode value, |
| float opacity) override |
| {} |
| }; |
| |
| class RenderPathWrapper : public wrapper<rive::RenderPath> |
| { |
| public: |
| EMSCRIPTEN_WRAPPER(RenderPathWrapper); |
| |
| void rewind() override { call<void>("rewind"); } |
| |
| void addRenderPath(rive::RenderPath* path, const rive::Mat2D& transform) override |
| { |
| float xx = transform.xx(); |
| float xy = transform.xy(); |
| float yx = transform.yx(); |
| float yy = transform.yy(); |
| float tx = transform.tx(); |
| float ty = transform.ty(); |
| call<void>("addPath", path, xx, xy, yx, yy, tx, ty); |
| } |
| void fillRule(rive::FillRule value) override { call<void>("fillRule", value); } |
| |
| void moveTo(float x, float y) override { call<void>("moveTo", x, y); } |
| void lineTo(float x, float y) override { call<void>("lineTo", x, y); } |
| void cubicTo(float ox, float oy, float ix, float iy, float x, float y) override |
| { |
| call<void>("cubicTo", ox, oy, ix, iy, x, y); |
| } |
| void close() override { call<void>("close"); } |
| }; |
| |
| class RenderPaintWrapper; |
| class GradientShader : public rive::RenderShader |
| { |
| private: |
| std::vector<float> m_Stops; |
| std::vector<rive::ColorInt> m_Colors; |
| |
| public: |
| GradientShader(const rive::ColorInt colors[], const float stops[], int count) : |
| m_Stops(stops, stops + count), m_Colors(colors, colors + count) |
| {} |
| |
| void passStopsToJS(const RenderPaintWrapper& wrapper); |
| |
| virtual void passToJS(const RenderPaintWrapper& wrapper) = 0; |
| }; |
| |
| class LinearGradientShader : public GradientShader |
| { |
| private: |
| float m_StartX; |
| float m_StartY; |
| float m_EndX; |
| float m_EndY; |
| |
| public: |
| LinearGradientShader(const rive::ColorInt colors[], |
| const float stops[], |
| int count, |
| float sx, |
| float sy, |
| float ex, |
| float ey) : |
| GradientShader(colors, stops, count), m_StartX(sx), m_StartY(sy), m_EndX(ex), m_EndY(ey) |
| {} |
| |
| void passToJS(const RenderPaintWrapper& wrapper) override; |
| }; |
| |
| class RadialGradientShader : public GradientShader |
| { |
| private: |
| float m_CenterX; |
| float m_CenterY; |
| float m_Radius; |
| |
| public: |
| RadialGradientShader(const rive::ColorInt colors[], |
| const float stops[], |
| int count, |
| float cx, |
| float cy, |
| float r) : |
| GradientShader(colors, stops, count), m_CenterX(cx), m_CenterY(cy), m_Radius(r) |
| {} |
| |
| void passToJS(const RenderPaintWrapper& wrapper) override; |
| }; |
| |
| class RenderPaintWrapper : public wrapper<rive::RenderPaint> |
| { |
| public: |
| EMSCRIPTEN_WRAPPER(RenderPaintWrapper); |
| |
| void color(unsigned int value) override { call<void>("color", value); } |
| void thickness(float value) override { call<void>("thickness", value); } |
| void join(rive::StrokeJoin value) override { call<void>("join", value); } |
| void cap(rive::StrokeCap value) override { call<void>("cap", value); } |
| void blendMode(rive::BlendMode value) override { call<void>("blendMode", value); } |
| |
| void style(rive::RenderPaintStyle value) override { call<void>("style", value); } |
| |
| void shader(rive::rcp<rive::RenderShader> shader) override |
| { |
| if (shader == nullptr) |
| { |
| call<void>("clearGradient"); |
| return; |
| } |
| static_cast<GradientShader*>(shader.get())->passToJS(*this); |
| } |
| void invalidateStroke() override {} |
| }; |
| |
| void GradientShader::passStopsToJS(const RenderPaintWrapper& wrapper) |
| { |
| // Consider passing in a bulk op encoding into a single array. |
| for (std::size_t i = 0; i < m_Stops.size(); i++) |
| { |
| wrapper.call<void>("addStop", m_Colors[i], m_Stops[i]); |
| } |
| } |
| |
| void LinearGradientShader::passToJS(const RenderPaintWrapper& wrapper) |
| { |
| wrapper.call<void>("linearGradient", m_StartX, m_StartY, m_EndX, m_EndY); |
| passStopsToJS(wrapper); |
| } |
| |
| void RadialGradientShader::passToJS(const RenderPaintWrapper& wrapper) |
| { |
| wrapper.call<void>("radialGradient", m_CenterX, m_CenterY, m_CenterX + m_Radius, m_CenterY); |
| passStopsToJS(wrapper); |
| } |
| |
| class RenderImageWrapper : public wrapper<rive::RenderImage> |
| { |
| public: |
| EMSCRIPTEN_WRAPPER(RenderImageWrapper); |
| |
| void size(int width, int height) |
| { |
| m_Width = width; |
| m_Height = height; |
| } |
| }; |
| |
| namespace rive |
| { |
| class C2DFactory : public Factory |
| { |
| rcp<RenderBuffer> makeBufferU16(Span<const uint16_t> data) override |
| { |
| return rive::DataRenderBuffer::Make(data); |
| } |
| rcp<RenderBuffer> makeBufferU32(Span<const uint32_t> data) override |
| { |
| return rive::DataRenderBuffer::Make(data); |
| } |
| rcp<RenderBuffer> makeBufferF32(Span<const float> data) override |
| { |
| return rive::DataRenderBuffer::Make(data); |
| } |
| |
| rcp<RenderShader> makeLinearGradient(float sx, |
| float sy, |
| float ex, |
| float ey, |
| const ColorInt colors[], // [count] |
| const float stops[], // [count] |
| size_t count) override |
| { |
| return rcp<RenderShader>(new LinearGradientShader(colors, stops, count, sx, sy, ex, ey)); |
| } |
| rcp<RenderShader> makeRadialGradient(float cx, |
| float cy, |
| float radius, |
| const ColorInt colors[], // [count] |
| const float stops[], // [count] |
| size_t count) override |
| { |
| return rcp<RenderShader>(new RadialGradientShader(colors, stops, count, cx, cy, radius)); |
| } |
| |
| std::unique_ptr<RenderPath> makeRenderPath(RawPath& path, FillRule fr) override |
| { |
| val renderPath = val::module_property("renderFactory").call<val>("makeRenderPath"); |
| auto ptr = renderPath.as<RenderPath*>(allow_raw_pointers()); |
| |
| // It might be faster to do this on the JS side, and just pass up the arrays... |
| // for now, we do it one segment at a time (each turns into an up-call to JS) |
| ptr->fillRule(fr); |
| const Vec2D* pts = path.points().data(); |
| for (auto v : path.verbs()) |
| { |
| switch ((PathVerb)v) |
| { |
| case PathVerb::move: |
| ptr->move(*pts++); |
| break; |
| case PathVerb::line: |
| ptr->line(*pts++); |
| break; |
| case PathVerb::cubic: |
| ptr->cubic(pts[0], pts[1], pts[2]); |
| pts += 3; |
| break; |
| case PathVerb::close: |
| ptr->close(); |
| break; |
| default: |
| assert(false); // unexpected verb |
| } |
| } |
| assert(pts - path.points().data() == path.points().size()); |
| |
| return std::unique_ptr<RenderPath>(ptr); |
| } |
| |
| std::unique_ptr<RenderPath> makeEmptyRenderPath() override |
| { |
| val renderPath = val::module_property("renderFactory").call<val>("makeRenderPath"); |
| auto ptr = renderPath.as<RenderPath*>(allow_raw_pointers()); |
| return std::unique_ptr<RenderPath>(ptr); |
| } |
| |
| std::unique_ptr<RenderPaint> makeRenderPaint() override |
| { |
| val renderPaint = val::module_property("renderFactory").call<val>("makeRenderPaint"); |
| auto ptr = renderPaint.as<RenderPaint*>(allow_raw_pointers()); |
| return std::unique_ptr<RenderPaint>(ptr); |
| } |
| |
| std::unique_ptr<RenderImage> decodeImage(Span<const uint8_t> bytes) override { return nullptr; } |
| }; |
| |
| std::unique_ptr<rive::Factory> MakeC2DFactory() { return std::make_unique<rive::C2DFactory>(); } |
| |
| std::unique_ptr<rive::Renderer> MakeC2DRenderer(const char* canvasID) |
| { |
| val renderer = |
| val::module_property("renderFactory") |
| .call<val>("makeRenderer", reinterpret_cast<intptr_t>(canvasID), strlen(canvasID)); |
| auto ptr = renderer.as<rive::Renderer*>(allow_raw_pointers()); |
| return std::unique_ptr<rive::Renderer>(ptr); |
| } |
| |
| void ClearC2DRenderer(rive::Renderer* renderer) |
| { |
| static_cast<RendererWrapper*>(renderer)->clear(); |
| } |
| } // namespace rive |
| |
| EMSCRIPTEN_KEEPALIVE |
| EMSCRIPTEN_BINDINGS(RiveWASM_C2D) |
| { |
| class_<rive::Renderer>("Renderer") |
| .function("save", &RendererWrapper::save, pure_virtual(), allow_raw_pointers()) |
| .function("restore", &RendererWrapper::restore, pure_virtual(), allow_raw_pointers()) |
| .function("transform", &RendererWrapper::transform, pure_virtual(), allow_raw_pointers()) |
| .function("drawPath", &RendererWrapper::drawPath, pure_virtual(), allow_raw_pointers()) |
| .function("clipPath", &RendererWrapper::clipPath, pure_virtual(), allow_raw_pointers()) |
| .function("align", &RendererWrapper::align, pure_virtual(), allow_raw_pointers()) |
| .allow_subclass<RendererWrapper>("RendererWrapper"); |
| |
| class_<rive::RenderPath>("RenderPath") |
| .function("rewind", &RenderPathWrapper::rewind, pure_virtual(), allow_raw_pointers()) |
| .function("addPath", |
| &RenderPathWrapper::addRenderPath, |
| pure_virtual(), |
| allow_raw_pointers()) |
| .function("fillRule", &RenderPathWrapper::fillRule, pure_virtual()) |
| .function("moveTo", &RenderPathWrapper::moveTo, pure_virtual(), allow_raw_pointers()) |
| .function("lineTo", &RenderPathWrapper::lineTo, pure_virtual(), allow_raw_pointers()) |
| .function("cubicTo", &RenderPathWrapper::cubicTo, pure_virtual(), allow_raw_pointers()) |
| .function("close", &RenderPathWrapper::close, pure_virtual(), allow_raw_pointers()) |
| .allow_subclass<RenderPathWrapper>("RenderPathWrapper"); |
| enum_<rive::RenderPaintStyle>("RenderPaintStyle") |
| .value("fill", rive::RenderPaintStyle::fill) |
| .value("stroke", rive::RenderPaintStyle::stroke); |
| |
| enum_<rive::FillRule>("FillRule") |
| .value("nonZero", rive::FillRule::nonZero) |
| .value("evenOdd", rive::FillRule::evenOdd); |
| |
| enum_<rive::StrokeCap>("StrokeCap") |
| .value("butt", rive::StrokeCap::butt) |
| .value("round", rive::StrokeCap::round) |
| .value("square", rive::StrokeCap::square); |
| |
| enum_<rive::StrokeJoin>("StrokeJoin") |
| .value("miter", rive::StrokeJoin::miter) |
| .value("round", rive::StrokeJoin::round) |
| .value("bevel", rive::StrokeJoin::bevel); |
| |
| enum_<rive::BlendMode>("BlendMode") |
| .value("srcOver", rive::BlendMode::srcOver) |
| .value("screen", rive::BlendMode::screen) |
| .value("overlay", rive::BlendMode::overlay) |
| .value("darken", rive::BlendMode::darken) |
| .value("lighten", rive::BlendMode::lighten) |
| .value("colorDodge", rive::BlendMode::colorDodge) |
| .value("colorBurn", rive::BlendMode::colorBurn) |
| .value("hardLight", rive::BlendMode::hardLight) |
| .value("softLight", rive::BlendMode::softLight) |
| .value("difference", rive::BlendMode::difference) |
| .value("exclusion", rive::BlendMode::exclusion) |
| .value("multiply", rive::BlendMode::multiply) |
| .value("hue", rive::BlendMode::hue) |
| .value("saturation", rive::BlendMode::saturation) |
| .value("color", rive::BlendMode::color) |
| .value("luminosity", rive::BlendMode::luminosity); |
| |
| class_<rive::rcp<rive::RenderShader>>("RenderShader"); |
| |
| class_<rive::RenderPaint>("RenderPaint") |
| .function("color", &RenderPaintWrapper::color, pure_virtual(), allow_raw_pointers()) |
| |
| .function("style", &RenderPaintWrapper::style, pure_virtual(), allow_raw_pointers()) |
| .function("thickness", &RenderPaintWrapper::thickness, pure_virtual(), allow_raw_pointers()) |
| .function("join", &RenderPaintWrapper::join, pure_virtual(), allow_raw_pointers()) |
| .function("cap", &RenderPaintWrapper::cap, pure_virtual(), allow_raw_pointers()) |
| .function("blendMode", &RenderPaintWrapper::blendMode, pure_virtual(), allow_raw_pointers()) |
| .function("shader", &RenderPaintWrapper::shader, pure_virtual(), allow_raw_pointers()) |
| .allow_subclass<RenderPaintWrapper>("RenderPaintWrapper"); |
| |
| class_<rive::RenderImage>("RenderImage") |
| // .function("decode", &RenderImageWrapper::decode, pure_virtual(), |
| // allow_raw_pointers()) |
| .function("size", &RenderImageWrapper::size) |
| .allow_subclass<RenderImageWrapper>("RenderImageWrapper"); |
| } |