blob: aa2f2925414178f014ee7997faba9909ea95d1bc [file] [log] [blame]
#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");
}