blob: 8628a850bdb2c1a10c17b3ced485eac10f68707e [file] [log] [blame]
#include "skia_renderer.hpp"
#include "SkData.h"
#include "SkGradientShader.h"
#include "SkPath.h"
#include "SkVertices.h"
#include "rive/math/vec2d.hpp"
#include "rive/shapes/paint/color.hpp"
#include "to_skia.hpp"
using namespace rive;
class SkiaBuffer : public RenderBuffer {
const size_t m_ElemSize;
void* m_Buffer;
public:
SkiaBuffer(const void* src, size_t count, size_t elemSize) :
RenderBuffer(count), m_ElemSize(elemSize) {
size_t bytes = count * elemSize;
m_Buffer = malloc(bytes);
memcpy(m_Buffer, src, bytes);
}
~SkiaBuffer() { free(m_Buffer); }
const float* f32s() const {
assert(m_ElemSize == sizeof(float));
return static_cast<const float*>(m_Buffer);
}
const uint16_t* u16s() const {
assert(m_ElemSize == sizeof(uint16_t));
return static_cast<const uint16_t*>(m_Buffer);
}
const SkPoint* points() const { return reinterpret_cast<const SkPoint*>(this->f32s()); }
static const SkiaBuffer* Cast(const RenderBuffer* buffer) {
return reinterpret_cast<const SkiaBuffer*>(buffer);
}
};
template <typename T> rcp<RenderBuffer> make_buffer(Span<T> span) {
return rcp<RenderBuffer>(new SkiaBuffer(span.data(), span.size(), sizeof(T)));
}
class SkiaRenderShader : public RenderShader {
public:
SkiaRenderShader(sk_sp<SkShader> sh) : shader(std::move(sh)) {}
sk_sp<SkShader> shader;
};
void SkiaRenderPath::fillRule(FillRule value) { m_Path.setFillType(ToSkia::convert(value)); }
void SkiaRenderPath::reset() { m_Path.reset(); }
void SkiaRenderPath::addRenderPath(RenderPath* path, const Mat2D& transform) {
m_Path.addPath(reinterpret_cast<SkiaRenderPath*>(path)->m_Path, ToSkia::convert(transform));
}
void SkiaRenderPath::moveTo(float x, float y) { m_Path.moveTo(x, y); }
void SkiaRenderPath::lineTo(float x, float y) { m_Path.lineTo(x, y); }
void SkiaRenderPath::cubicTo(float ox, float oy, float ix, float iy, float x, float y) {
m_Path.cubicTo(ox, oy, ix, iy, x, y);
}
void SkiaRenderPath::close() { m_Path.close(); }
SkiaRenderPaint::SkiaRenderPaint() { m_Paint.setAntiAlias(true); }
void SkiaRenderPaint::style(RenderPaintStyle style) {
switch (style) {
case RenderPaintStyle::fill:
m_Paint.setStyle(SkPaint::Style::kFill_Style);
break;
case RenderPaintStyle::stroke:
m_Paint.setStyle(SkPaint::Style::kStroke_Style);
break;
}
}
void SkiaRenderPaint::color(unsigned int value) { m_Paint.setColor(value); }
void SkiaRenderPaint::thickness(float value) { m_Paint.setStrokeWidth(value); }
void SkiaRenderPaint::join(StrokeJoin value) { m_Paint.setStrokeJoin(ToSkia::convert(value)); }
void SkiaRenderPaint::cap(StrokeCap value) { m_Paint.setStrokeCap(ToSkia::convert(value)); }
void SkiaRenderPaint::blendMode(BlendMode value) { m_Paint.setBlendMode(ToSkia::convert(value)); }
void SkiaRenderPaint::shader(rcp<RenderShader> rsh) {
SkiaRenderShader* sksh = (SkiaRenderShader*)rsh.get();
m_Paint.setShader(sksh ? sksh->shader : nullptr);
}
void SkiaRenderer::save() { m_Canvas->save(); }
void SkiaRenderer::restore() { m_Canvas->restore(); }
void SkiaRenderer::transform(const Mat2D& transform) {
m_Canvas->concat(ToSkia::convert(transform));
}
void SkiaRenderer::drawPath(RenderPath* path, RenderPaint* paint) {
m_Canvas->drawPath(reinterpret_cast<SkiaRenderPath*>(path)->path(),
reinterpret_cast<SkiaRenderPaint*>(paint)->paint());
}
void SkiaRenderer::clipPath(RenderPath* path) {
m_Canvas->clipPath(reinterpret_cast<SkiaRenderPath*>(path)->path(), true);
}
void SkiaRenderer::drawImage(const RenderImage* image, BlendMode blendMode, float opacity) {
SkPaint paint;
paint.setAlphaf(opacity);
paint.setBlendMode(ToSkia::convert(blendMode));
auto skiaImage = reinterpret_cast<const SkiaRenderImage*>(image);
SkSamplingOptions sampling(SkFilterMode::kLinear);
m_Canvas->drawImage(skiaImage->skImage(), 0.0f, 0.0f, sampling, &paint);
}
#define SKIA_BUG_13047
void SkiaRenderer::drawImageMesh(const RenderImage* image,
rcp<RenderBuffer> vertices,
rcp<RenderBuffer> uvCoords,
rcp<RenderBuffer> indices,
BlendMode blendMode,
float opacity) {
// need our vertices and uvs to agree
assert(vertices->count() == uvCoords->count());
// vertices and uvs are arrays of floats, so we need their counts to be
// even, since we treat them as arrays of points
assert((vertices->count() & 1) == 0);
const int vertexCount = vertices->count() >> 1;
SkMatrix scaleM;
const SkPoint* uvs = SkiaBuffer::Cast(uvCoords.get())->points();
#ifdef SKIA_BUG_13047
// The local matrix is ignored for drawVertices, so we have to manually scale
// the UVs to match Skia's convention...
std::vector<SkPoint> scaledUVs(vertexCount);
for (int i = 0; i < vertexCount; ++i) {
scaledUVs[i] = {uvs[i].fX * image->width(), uvs[i].fY * image->height()};
}
uvs = scaledUVs.data();
#else
// We do this because our UVs are normalized, but Skia expects them to be
// sized to the shader (i.e. 0..width, 0..height).
// To accomdate this, we effectively scaling the image down to 0..1 to
// match the scale of the UVs.
scaleM = SkMatrix::Scale(2.0f / image->width(), 2.0f / image->height());
#endif
auto skiaImage = reinterpret_cast<const SkiaRenderImage*>(image)->skImage();
const SkSamplingOptions sampling(SkFilterMode::kLinear);
auto shader = skiaImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, sampling, &scaleM);
SkPaint paint;
paint.setAlphaf(opacity);
paint.setBlendMode(ToSkia::convert(blendMode));
paint.setShader(shader);
const SkColor* no_colors = nullptr;
auto vertexMode = SkVertices::kTriangles_VertexMode;
// clang-format off
auto vt = SkVertices::MakeCopy(vertexMode,
vertexCount,
SkiaBuffer::Cast(vertices.get())->points(),
uvs,
no_colors,
indices->count(),
SkiaBuffer::Cast(indices.get())->u16s());
// clang-format on
// The blend mode is ignored if we don't have colors && uvs
m_Canvas->drawVertices(vt, SkBlendMode::kModulate, paint);
}
bool SkiaRenderImage::decode(Span<const uint8_t> encodedData) {
sk_sp<SkData> data = SkData::MakeWithoutCopy(encodedData.data(), encodedData.size());
m_SkImage = SkImage::MakeFromEncoded(data);
// Our optimized skia buld seems to have broken lazy-image decode.
// As a work-around for now, force the image to be decoded.
if (m_SkImage) {
m_SkImage = m_SkImage->makeRasterImage();
}
if (m_SkImage) {
m_Width = m_SkImage->width();
m_Height = m_SkImage->height();
return true;
}
return false;
}
rcp<RenderShader>
SkiaRenderImage::makeShader(RenderTileMode tx, RenderTileMode ty, const Mat2D* localMatrix) const {
const SkMatrix lm = localMatrix ? ToSkia::convert(*localMatrix) : SkMatrix();
const SkSamplingOptions options(SkFilterMode::kLinear);
auto sh = m_SkImage->makeShader(ToSkia::convert(tx), ToSkia::convert(ty), options, &lm);
return rcp<RenderShader>(new SkiaRenderShader(std::move(sh)));
}
namespace rive {
rcp<RenderBuffer> makeBufferU16(Span<const uint16_t> data) { return make_buffer(data); }
rcp<RenderBuffer> makeBufferU32(Span<const uint32_t> data) { return make_buffer(data); }
rcp<RenderBuffer> makeBufferF32(Span<const float> data) { return make_buffer(data); }
RenderPath*
makeRenderPath(Span<const Vec2D> points, Span<const uint8_t> verbs, FillRule fillrule) {
return new SkiaRenderPath(SkPath::Make((const SkPoint*)points.data(),
points.size(),
verbs.data(),
verbs.size(),
nullptr,
0, // conics
ToSkia::convert(fillrule),
false));
}
RenderPath* makeRenderPath() { return new SkiaRenderPath(); }
RenderPaint* makeRenderPaint() { return new SkiaRenderPaint(); }
RenderImage* makeRenderImage() { return new SkiaRenderImage(); }
rcp<RenderShader> makeLinearGradient(float sx,
float sy,
float ex,
float ey,
const ColorInt colors[],
const float stops[],
int count,
RenderTileMode mode,
const Mat2D* localm) {
const SkPoint pts[] = {{sx, sy}, {ex, ey}};
const SkMatrix lm = localm ? ToSkia::convert(*localm) : SkMatrix();
auto sh = SkGradientShader::MakeLinear(
pts, (const SkColor*)colors, stops, count, ToSkia::convert(mode), 0, &lm);
return rcp<RenderShader>(new SkiaRenderShader(std::move(sh)));
}
rcp<RenderShader> makeRadialGradient(float cx,
float cy,
float radius,
const ColorInt colors[],
const float stops[],
int count,
RenderTileMode mode,
const Mat2D* localm) {
const SkMatrix lm = localm ? ToSkia::convert(*localm) : SkMatrix();
auto sh = SkGradientShader::MakeRadial(
{cx, cy}, radius, (const SkColor*)colors, stops, count, ToSkia::convert(mode), 0, &lm);
return rcp<RenderShader>(new SkiaRenderShader(std::move(sh)));
}
rcp<RenderShader> makeSweepGradient(float cx,
float cy,
const ColorInt colors[],
const float stops[],
int count,
RenderTileMode mode,
const Mat2D* localm) {
const SkMatrix lm = localm ? ToSkia::convert(*localm) : SkMatrix();
auto sh = SkGradientShader::MakeSweep(cx, cy, (const SkColor*)colors, stops, count, 0, &lm);
return rcp<RenderShader>(new SkiaRenderShader(std::move(sh)));
}
} // namespace rive