| /* |
| * Copyright 2022 Rive |
| */ |
| |
| #pragma once |
| |
| #include "rive/math/raw_path.hpp" |
| #include "rive/renderer.hpp" |
| #include "rive/pls/aligned_buffer.hpp" |
| #include "rive/pls/fixed_queue.hpp" |
| #include "rive/pls/pls.hpp" |
| #include "rive/pls/pls_render_context.hpp" |
| #include <vector> |
| |
| namespace rive |
| { |
| class GrInnerFanTriangulator; |
| }; |
| |
| namespace rive::pls |
| { |
| class PLSPath; |
| class PLSPaint; |
| class PLSRenderContext; |
| |
| // Renderer implementation for Rive's pixel local storage renderer. |
| class PLSRenderer : public Renderer |
| { |
| public: |
| PLSRenderer(PLSRenderContext*); |
| ~PLSRenderer() override; |
| |
| void save() override; |
| void restore() override; |
| void transform(const Mat2D& matrix) override; |
| void drawPath(RenderPath*, RenderPaint*) override; |
| void clipPath(RenderPath*) override; |
| void drawImage(const RenderImage*, BlendMode, float opacity) override; |
| void drawImageMesh(const RenderImage*, |
| rcp<RenderBuffer> vertices_f32, |
| rcp<RenderBuffer> uvCoords_f32, |
| rcp<RenderBuffer> indices_u16, |
| BlendMode, |
| float opacity) override; |
| |
| // Most likely temporary. Determines if a path is an axis-aligned rectangle that can be |
| // represented by the struct rive::AABB. Used to detect artboard clip candidates. |
| static bool IsAABB(const RawPath&); |
| |
| private: |
| class InteriorTriangulationHelper; |
| |
| // Pushes any necessary clip updates to m_pathBatch and writes back the clipID the next path |
| // should be drawn with. |
| // Returns false if the operation failed, at which point the caller should flush and try again. |
| [[nodiscard]] bool applyClip(uint32_t* clipID); |
| |
| // Pushes all paths in m_pathBatch to the GPU. All paths in the batch are assumed to be clip |
| // updates except the final one, which is drawn with 'finalPathPaint'. |
| [[nodiscard]] bool pushInternalPathBatch(PLSPaint* finalPathPaint); |
| |
| struct ContourData |
| { |
| ContourData(const RawPath::Iter& endOfContour_, |
| size_t endLineIdx_, |
| size_t endCurveIdx_, |
| size_t endRotationIdx_, |
| Vec2D midpoint_, |
| bool closed_, |
| size_t strokeJoinCount_) : |
| endOfContour(endOfContour_), |
| endLineIdx(endLineIdx_), |
| endCurveIdx(endCurveIdx_), |
| endRotationIdx(endRotationIdx_), |
| midpoint(midpoint_), |
| closed(closed_), |
| strokeJoinCount(strokeJoinCount_) |
| {} |
| RawPath::Iter endOfContour; |
| size_t endLineIdx; |
| size_t endCurveIdx; |
| size_t endRotationIdx; // We measure rotations on both curves and round joins. |
| Vec2D midpoint; |
| bool closed; |
| size_t strokeJoinCount; |
| uint32_t strokeCapSegmentCount = 0; |
| uint32_t paddingVertexCount = 0; |
| RIVE_DEBUG_CODE(uint32_t tessVertexCount = 0;) |
| }; |
| |
| void pushContour(RawPath::Iter iter, |
| const ContourData&, |
| size_t curveIdx, |
| size_t rotationIdx, |
| float matrixMaxScale, |
| const PLSPaint* strokePaint); |
| |
| // Emulates a stroke cap before the given cubic by pushing a copy of the cubic, reversed, with 0 |
| // tessellation segments leading up to the join section, and a 180-degree join that looks like |
| // the desired stroke cap. |
| void pushEmulatedStrokeCapAsJoinBeforeCubic(const Vec2D cubic[], |
| uint32_t emulatedCapAsJoinFlags, |
| uint32_t strokeCapSegmentCount); |
| |
| // Called when we ran out of room in GPU buffers. Does an intermediate flush of all currently |
| // queued GPU work. |
| void intermediateFlush(); |
| |
| struct RenderState |
| { |
| RenderState(const Mat2D& m, size_t h) : matrix(m), clipStackHeight(h) {} |
| Mat2D matrix; |
| size_t clipStackHeight; |
| }; |
| std::vector<RenderState> m_stack{{Mat2D(), 0}}; |
| |
| struct ClipElement |
| { |
| Mat2D matrix; |
| RawPath path; |
| AABB pathBounds; |
| FillRule fillRule; |
| uint32_t clipID; |
| }; |
| std::vector<ClipElement> m_clipStack; |
| bool m_hasArtboardClipCandidate = false; |
| |
| PLSRenderContext* const m_context; |
| |
| // Internal list of path draws that get pushed to the GPU in a single batch. |
| struct PathDraw |
| { |
| PathDraw(const Mat2D* matrix_, |
| const RawPath* rawPath_, |
| const AABB& pathBounds_, |
| FillRule fillRule_, |
| uint32_t clipID_) : |
| matrix(matrix_), |
| rawPath(rawPath_), |
| pathBounds(pathBounds_), |
| fillRule(fillRule_), |
| clipID(clipID_) |
| {} |
| const Mat2D* matrix; |
| const RawPath* rawPath; |
| AABB pathBounds; |
| FillRule fillRule; |
| uint32_t clipID; |
| GrInnerFanTriangulator* triangulator = nullptr; // Non-null if using interior triangulation. |
| size_t firstContourIdx = 0; |
| size_t contourCount = 0; |
| uint32_t tessVertexCount = 0; |
| uint32_t paddingVertexCount = 0; |
| }; |
| std::vector<PathDraw> m_pathBatch; |
| |
| // Temporary storage used during drawPath. Stored as a persistent class member to avoid |
| // redundant mallocs. |
| std::vector<ContourData> m_contourBatch; |
| |
| AlignedBuffer<4, float> m_parametricSegmentCounts_pow4; |
| FixedQueue<uint8_t> m_numChops; |
| FixedQueue<Vec2D> m_chops; |
| AlignedBuffer<1, std::array<Vec2D, 2>> m_tangentPairs; |
| AlignedBuffer<4, uint32_t> m_parametricSegmentCounts; |
| AlignedBuffer<4, uint32_t> m_polarSegmentCounts; |
| |
| // Used to build coarse path interiors for the "interior triangulation" algorithm. |
| RawPath m_scratchPath; |
| |
| // Consistency checks for pushContour. |
| RIVE_DEBUG_CODE(size_t m_pushedLineCount;) |
| RIVE_DEBUG_CODE(size_t m_pushedCurveCount;) |
| RIVE_DEBUG_CODE(size_t m_pushedRotationCount;) |
| RIVE_DEBUG_CODE(size_t m_pushedStrokeJoinCount;) |
| RIVE_DEBUG_CODE(size_t m_pushedStrokeCapCount;) |
| // Counts how many additional curves were pushed by pushEmulatedStrokeCapAsJoinBeforeCubic(). |
| RIVE_DEBUG_CODE(size_t m_pushedEmptyStrokeCountForCaps;) |
| }; |
| } // namespace rive::pls |