| /* | |
| * 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 lite_rtti_override<RenderShader, PLSGradient> | |
| { | |
| public: | |
| static rcp<PLSGradient> MakeLinear(float sx, | |
| float sy, | |
| float ex, | |
| float ey, | |
| const ColorInt colors[], // [count] | |
| const float stops[], // [count] | |
| size_t count); | |
| static rcp<PLSGradient> MakeRadial(float cx, | |
| float cy, | |
| float radius, | |
| const ColorInt colors[], // [count] | |
| const float stops[], // [count] | |
| size_t count); | |
| 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; } | |
| bool isOpaque() const; | |
| private: | |
| PLSGradient(PaintType paintType, | |
| PLSGradDataArray<ColorInt>&& colors, // [count] | |
| PLSGradDataArray<float>&& stops, // [count] | |
| size_t count, | |
| float coeffX, | |
| float coeffY, | |
| float coeffZ) : | |
| m_paintType(paintType), | |
| m_colors(std::move(colors)), | |
| m_stops(std::move(stops)), | |
| m_count(count), | |
| m_coeffs{coeffX, coeffY, coeffZ} | |
| { | |
| assert(paintType == PaintType::linearGradient || paintType == PaintType::radialGradient); | |
| } | |
| PaintType m_paintType; // Specifically, linearGradient or radialGradient. | |
| PLSGradDataArray<ColorInt> m_colors; | |
| PLSGradDataArray<float> m_stops; | |
| size_t m_count; | |
| std::array<float, 3> m_coeffs; | |
| mutable pls::TriState m_isOpaque = pls::TriState::unknown; | |
| }; | |
| // RenderPaint implementation for Rive's pixel local storage renderer. | |
| class PLSPaint : public lite_rtti_override<RenderPaint, PLSPaint> | |
| { | |
| public: | |
| PLSPaint(); | |
| ~PLSPaint(); | |
| void style(RenderPaintStyle style) override { m_stroked = style == RenderPaintStyle::stroke; } | |
| void color(ColorInt color) override; | |
| 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 { m_blendMode = mode; } | |
| void shader(rcp<RenderShader> shader) override; | |
| void image(rcp<const PLSTexture>, float opacity); | |
| void clipUpdate(uint32_t outerClipID); | |
| void invalidateStroke() override {} | |
| PaintType getType() const { return m_paintType; } | |
| bool getIsStroked() const { return m_stroked; } | |
| ColorInt getColor() const { return m_simpleValue.color; } | |
| float getThickness() const { return m_thickness; } | |
| const PLSGradient* getGradient() const { return m_gradient.get(); } | |
| const PLSTexture* getImageTexture() const { return m_imageTexture.get(); } | |
| float getImageOpacity() const { return m_simpleValue.imageOpacity; } | |
| float getOuterClipID() const { return m_simpleValue.outerClipID; } | |
| StrokeJoin getJoin() const { return m_join; } | |
| StrokeCap getCap() const { return m_cap; } | |
| BlendMode getBlendMode() const { return m_blendMode; } | |
| pls::SimplePaintValue getSimpleValue() const { return m_simpleValue; } | |
| bool getIsOpaque() const; | |
| private: | |
| PaintType m_paintType = PaintType::solidColor; | |
| pls::SimplePaintValue m_simpleValue; | |
| rcp<const PLSGradient> m_gradient; | |
| rcp<const PLSTexture> m_imageTexture; | |
| float m_thickness = 1; | |
| StrokeJoin m_join = StrokeJoin::miter; | |
| StrokeCap m_cap = StrokeCap::butt; | |
| BlendMode m_blendMode = BlendMode::srcOver; | |
| bool m_stroked = false; | |
| }; | |
| } // namespace rive::pls |