blob: 74a11ef6715179d3da19d7716b22d1e7c2d95c88 [file]
/*
* Copyright 2022 Rive
*/
#pragma once
#include "rive/pls/pls.hpp"
#include "rive/renderer.hpp"
#include <array>
namespace rive::pls
{
// Copies an array of colors or stops for a gradient.
// Stores the data locally if there are 4 values or fewer.
// Spills onto the heap if there are >4 values.
template <typename T> class PLSGradDataArray
{
public:
static_assert(std::is_pod_v<T>);
PLSGradDataArray(const T data[], size_t count)
{
m_data = count <= m_localData.size() ? m_localData.data() : new T[count];
memcpy(m_data, data, count * sizeof(T));
}
PLSGradDataArray(PLSGradDataArray&& other)
{
if (other.m_data == other.m_localData.data())
{
m_localData = other.m_localData;
m_data = m_localData.data();
}
else
{
m_data = other.m_data;
other.m_data = other.m_localData.data(); // Don't delete[] other.m_data.
}
}
~PLSGradDataArray()
{
if (m_data != m_localData.data())
{
delete[] m_data;
}
}
const T* get() const { return m_data; }
const T operator[](size_t i) const { return m_data[i]; }
T& operator[](size_t i) { return m_data[i]; }
private:
std::array<T, 4> m_localData;
T* m_data;
};
// RenderShader implementation for Rive's pixel local storage renderer.
class PLSGradient : public RenderShader
{
public:
static rcp<PLSGradient> MakeLinear(PLSGradDataArray<ColorInt>&& colors, // [count]
PLSGradDataArray<float>&& stops, // [count]
size_t count,
Vec2D start,
Vec2D end);
static rcp<PLSGradient> MakeRadial(PLSGradDataArray<ColorInt>&& colors, // [count]
PLSGradDataArray<float>&& stops, // [count]
size_t count,
Vec2D center,
float radius);
PaintType paintType() const { return m_paintType; }
const float* coeffs() const { return m_coeffs.data(); }
const ColorInt* colors() const { return m_colors.get(); }
const float* stops() const { return m_stops.get(); }
int count() const { return m_count; }
private:
PLSGradient(PaintType paintType,
PLSGradDataArray<ColorInt>&& colors, // [count]
PLSGradDataArray<float>&& stops, // [count]
size_t count) :
m_paintType(paintType),
m_colors(std::move(colors)),
m_stops(std::move(stops)),
m_count(count)
{
assert(paintType == PaintType::linearGradient || paintType == PaintType::radialGradient);
}
PaintType m_paintType;
PLSGradDataArray<ColorInt> m_colors;
PLSGradDataArray<float> m_stops;
size_t m_count;
std::array<float, 3> m_coeffs;
};
// RenderPaint implementation for Rive's pixel local storage renderer.
class PLSPaint : public RenderPaint
{
public:
void style(RenderPaintStyle style) override { m_stroked = style == RenderPaintStyle::stroke; }
void color(ColorInt color) override
{
m_gradient.reset();
m_color = color;
}
void thickness(float thickness) override { m_thickness = fabsf(thickness); }
void join(StrokeJoin join) override { m_join = join; }
void cap(StrokeCap cap) override { m_cap = cap; }
void blendMode(BlendMode mode) override; // Set a rive BlendMode.
void blendMode(PLSBlendMode mode) { m_blendMode = mode; } // Set a pls BlendMode.
void shader(rcp<RenderShader> shader) override;
void invalidateStroke() override {}
PaintType getType() const
{
return m_gradient ? m_gradient->paintType() : PaintType::solidColor;
}
bool getIsStroked() const { return m_stroked; }
ColorInt getColor() const { return m_color; }
float getThickness() const { return m_thickness; }
const PLSGradient* getGradient() const { return m_gradient.get(); }
StrokeJoin getJoin() const { return m_join; }
StrokeCap getCap() const { return m_cap; }
PLSBlendMode getBlendMode() const { return m_blendMode; }
private:
bool m_stroked = false;
ColorInt m_color = 0xff000000;
rcp<PLSGradient> m_gradient;
float m_thickness = 1;
StrokeJoin m_join = StrokeJoin::bevel;
StrokeCap m_cap = StrokeCap::butt;
PLSBlendMode m_blendMode = PLSBlendMode::srcOver;
};
} // namespace rive::pls