mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
Add RenderPath::addRawPath
Addresses this issue: https://2dimensions.slack.com/archives/C07VD44ASE5/p1739456802968219 Rive's RenderPath steals the memory for the RawPath the runtime builds, which is usually fine apart from cases where we expect to use the RawPath after drawing with it. For example, with inner feather we want to be able to compute the bounds of the RawPath after it has been drawn. If during animation a user turns on inner feathering on a path which has not changed recently, which need to be able to compute its bounds, we can't if the RawPath is now gone. This also can happen with trim paths and dash paths which are re-trimmed/dashed after having being rendered a few frames. Right now we force rebuild the whole path to re-trim it because we lose the RawPath. This PR allows the ShapePaintPath to hold on to its RawPath by adding RenderPath::addRawPath. This is optimized memory usage but not having the runtime need to re-alloc the RawPath whenever it animates, but it means we're further from making RenderPaths immutable. For the future: It'd be good to chat about a long term solution that can accommodate all goals of allocating less memory (prior to this change we were re-allocating RawPath memory every frame when animating), not rebuilding paths as often, and still allow for immutability. Diffs= 9058a3fdad Add RenderPath::addRawPath (#9038) Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
7a6019fb974ed415d8531980b0b1516ff9f095c8
|
||||
9058a3fdad2444ca9eab3ef06bc49376eadba9de
|
||||
|
||||
@@ -93,7 +93,11 @@ public:
|
||||
CGRenderPath(Span<const Vec2D> pts, Span<const PathVerb> vbs, FillRule rule)
|
||||
{
|
||||
m_fillMode = convert(rule);
|
||||
addRawPathCommands(pts, vbs);
|
||||
}
|
||||
|
||||
void addRawPathCommands(Span<const Vec2D> pts, Span<const PathVerb> vbs)
|
||||
{
|
||||
auto p = pts.data();
|
||||
for (auto v : vbs)
|
||||
{
|
||||
@@ -135,6 +139,11 @@ public:
|
||||
assert(p == pts.end());
|
||||
}
|
||||
|
||||
void addRawPath(const RawPath& path) override
|
||||
{
|
||||
addRawPathCommands(path.points(), path.verbs());
|
||||
}
|
||||
|
||||
CGPathRef path() const { return m_path.get(); }
|
||||
CGPathDrawingMode drawingMode(bool isStroke) const
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "rive/shapes/paint/stroke_cap.hpp"
|
||||
#include "rive/shapes/paint/stroke_join.hpp"
|
||||
#include "utils/lite_rtti.hpp"
|
||||
|
||||
#include "rive/math/raw_path.hpp"
|
||||
#include <stdio.h>
|
||||
#include <cstdint>
|
||||
|
||||
@@ -192,6 +192,8 @@ public:
|
||||
{
|
||||
// No-op on non rive renderer.
|
||||
}
|
||||
|
||||
virtual void addRawPath(const RawPath& path) = 0;
|
||||
};
|
||||
|
||||
class Renderer
|
||||
|
||||
@@ -27,7 +27,6 @@ protected:
|
||||
std::vector<ShapePaint*> m_ShapePaints;
|
||||
void addPaint(ShapePaint* paint);
|
||||
|
||||
// TODO: void draw(Renderer* renderer, PathComposer& composer);
|
||||
public:
|
||||
static ShapePaintContainer* from(Component* component);
|
||||
|
||||
|
||||
@@ -51,7 +51,10 @@ public:
|
||||
m_rawPath.addRect(aabb, dir);
|
||||
}
|
||||
|
||||
const bool hasRenderPath() const { return m_renderPath != nullptr; }
|
||||
const bool hasRenderPath() const
|
||||
{
|
||||
return m_renderPath != nullptr && !m_isRenderPathDirty;
|
||||
}
|
||||
|
||||
#ifdef TESTING
|
||||
size_t numContours()
|
||||
@@ -68,6 +71,7 @@ public:
|
||||
}
|
||||
#endif
|
||||
private:
|
||||
bool m_isRenderPathDirty = true;
|
||||
rcp<RenderPath> m_renderPath;
|
||||
RawPath m_rawPath;
|
||||
bool m_isLocal;
|
||||
|
||||
@@ -82,9 +82,12 @@ void RiveRenderPath::addRenderPath(RenderPath* path, const Mat2D& matrix)
|
||||
{
|
||||
assert(m_rawPathMutationLockCount == 0);
|
||||
RiveRenderPath* riveRenderPath = static_cast<RiveRenderPath*>(path);
|
||||
|
||||
bool isIdentity = matrix == Mat2D();
|
||||
RawPath::Iter transformedPathIter =
|
||||
m_rawPath.addPath(riveRenderPath->m_rawPath, &matrix);
|
||||
if (matrix != Mat2D())
|
||||
m_rawPath.addPath(riveRenderPath->m_rawPath,
|
||||
isIdentity ? nullptr : &matrix);
|
||||
if (!isIdentity)
|
||||
{
|
||||
// Prune any segments that became empty after the transform.
|
||||
m_rawPath.pruneEmptySegments(transformedPathIter);
|
||||
@@ -106,6 +109,11 @@ void RiveRenderPath::addRenderPathBackwards(RenderPath* path,
|
||||
m_dirt = kAllDirt;
|
||||
}
|
||||
|
||||
void RiveRenderPath::addRawPath(const RawPath& path)
|
||||
{
|
||||
m_rawPath.addPath(path, nullptr);
|
||||
}
|
||||
|
||||
const AABB& RiveRenderPath::getBounds() const
|
||||
{
|
||||
if (m_dirt & kPathBoundsDirt)
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
void addRenderPath(RenderPath* path, const Mat2D& matrix) override;
|
||||
void addRenderPathBackwards(RenderPath* path,
|
||||
const Mat2D& transform) override;
|
||||
|
||||
void addRawPath(const RawPath& path) override;
|
||||
const RawPath& getRawPath() const { return m_rawPath; }
|
||||
FillRule getFillRule() const { return m_fillRule; }
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
|
||||
void rewind() override;
|
||||
void addRenderPath(RenderPath* path, const Mat2D& transform) override;
|
||||
void addRawPath(const RawPath& path) override;
|
||||
void fillRule(FillRule value) override;
|
||||
void moveTo(float x, float y) override;
|
||||
void lineTo(float x, float y) override;
|
||||
@@ -132,6 +133,23 @@ void SkiaRenderPath::addRenderPath(RenderPath* path, const Mat2D& transform)
|
||||
m_Path.addPath(skPath->m_Path, ToSkia::convert(transform));
|
||||
}
|
||||
|
||||
void SkiaRenderPath::addRawPath(const RawPath& path)
|
||||
{
|
||||
const bool isVolatile = false;
|
||||
const SkScalar* conicWeights = nullptr;
|
||||
const int conicWeightCount = 0;
|
||||
auto skPath =
|
||||
SkPath::Make(reinterpret_cast<const SkPoint*>(path.points().data()),
|
||||
path.points().size(),
|
||||
(uint8_t*)path.verbs().data(),
|
||||
path.verbs().size(),
|
||||
conicWeights,
|
||||
conicWeightCount,
|
||||
m_Path.getFillType(),
|
||||
isVolatile);
|
||||
m_Path.addPath(skPath);
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -13,14 +13,14 @@ ShapePaintPath::ShapePaintPath(bool isLocal, FillRule fillRule) :
|
||||
void ShapePaintPath::rewind()
|
||||
{
|
||||
m_rawPath.rewind();
|
||||
m_renderPath = nullptr;
|
||||
m_isRenderPathDirty = true;
|
||||
}
|
||||
|
||||
void ShapePaintPath::addPath(const RawPath& rawPath, const Mat2D* transform)
|
||||
{
|
||||
auto iter = m_rawPath.addPath(rawPath, transform);
|
||||
m_rawPath.pruneEmptySegments(iter);
|
||||
m_renderPath = nullptr;
|
||||
m_isRenderPathDirty = true;
|
||||
}
|
||||
|
||||
void ShapePaintPath::addPathClockwise(const RawPath& rawPath,
|
||||
@@ -46,25 +46,30 @@ void ShapePaintPath::addPathBackwards(const RawPath& rawPath,
|
||||
{
|
||||
auto iter = m_rawPath.addPathBackwards(rawPath, transform);
|
||||
m_rawPath.pruneEmptySegments(iter);
|
||||
m_renderPath = nullptr;
|
||||
m_isRenderPathDirty = true;
|
||||
}
|
||||
|
||||
RenderPath* ShapePaintPath::renderPath(const Component* component)
|
||||
{
|
||||
assert(component != nullptr);
|
||||
if (!m_renderPath)
|
||||
{
|
||||
auto factory = component->artboard()->factory();
|
||||
m_renderPath = factory->makeRenderPath(m_rawPath, m_fillRule);
|
||||
}
|
||||
return m_renderPath.get();
|
||||
return renderPath(component->artboard()->factory());
|
||||
}
|
||||
|
||||
RenderPath* ShapePaintPath::renderPath(Factory* factory)
|
||||
{
|
||||
if (!m_renderPath)
|
||||
{
|
||||
m_renderPath = factory->makeRenderPath(m_rawPath, m_fillRule);
|
||||
m_renderPath = factory->makeEmptyRenderPath();
|
||||
m_renderPath->addRawPath(m_rawPath);
|
||||
m_renderPath->fillRule(m_fillRule);
|
||||
m_isRenderPathDirty = false;
|
||||
}
|
||||
else if (m_isRenderPathDirty)
|
||||
{
|
||||
m_renderPath->rewind();
|
||||
m_renderPath->addRawPath(m_rawPath);
|
||||
m_isRenderPathDirty = false;
|
||||
}
|
||||
|
||||
return m_renderPath.get();
|
||||
}
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
override;
|
||||
void close() override;
|
||||
void addRenderPath(RenderPath* path, const Mat2D& transform) override;
|
||||
void addRawPath(const RawPath& path) override;
|
||||
|
||||
const SegmentedContour& segmentedContour() const;
|
||||
bool triangulate();
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
void fillRule(FillRule value) override {}
|
||||
void addPath(CommandPath* path, const Mat2D& transform) override {}
|
||||
void addRenderPath(RenderPath* path, const Mat2D& transform) override {}
|
||||
void addRawPath(const RawPath& path) override {}
|
||||
|
||||
void moveTo(float x, float y) override {}
|
||||
void lineTo(float x, float y) override {}
|
||||
|
||||
@@ -46,6 +46,11 @@ void TessRenderPath::addRenderPath(RenderPath* path, const Mat2D& transform)
|
||||
m_subPaths.emplace_back(SubPath(path, transform));
|
||||
}
|
||||
|
||||
void TessRenderPath::addRawPath(const RawPath& path)
|
||||
{
|
||||
m_rawPath.addPath(path, nullptr);
|
||||
}
|
||||
|
||||
const SegmentedContour& TessRenderPath::segmentedContour() const
|
||||
{
|
||||
return m_segmentedContour;
|
||||
|
||||
@@ -153,6 +153,11 @@ protected:
|
||||
m_begin = shape->m_begin;
|
||||
}
|
||||
|
||||
void addRawPath(const RawPath& path) override
|
||||
{
|
||||
m_path->addRawPath(path);
|
||||
}
|
||||
|
||||
protected:
|
||||
Path m_path;
|
||||
Vec2D m_pen = {0, 0};
|
||||
|
||||
@@ -41,6 +41,10 @@ public:
|
||||
void addRenderPath(rive::RenderPath* path,
|
||||
const rive::Mat2D& transform) override
|
||||
{}
|
||||
void addRawPath(const rive::RawPath& path) override
|
||||
{
|
||||
rawPath.addPath(path, nullptr);
|
||||
}
|
||||
|
||||
void moveTo(float x, float y) override {}
|
||||
void lineTo(float x, float y) override {}
|
||||
|
||||
@@ -45,6 +45,8 @@ class TestRenderPath : public rive::RenderPath
|
||||
{
|
||||
public:
|
||||
std::vector<TestPathCommand> commands;
|
||||
void addRawPath(const rive::RawPath& path) override { path.addTo(this); }
|
||||
|
||||
void rewind() override
|
||||
{
|
||||
commands.emplace_back(TestPathCommand{TestPathCommandType::Reset,
|
||||
|
||||
@@ -38,6 +38,8 @@ public:
|
||||
override
|
||||
{}
|
||||
void close() override {}
|
||||
|
||||
void addRawPath(const RawPath& path) override {}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
|
||||
Reference in New Issue
Block a user