From 14d5c86706fd5d9bbfc4580f603e8399850bdb37 Mon Sep 17 00:00:00 2001 From: Dom Chen Date: Sun, 25 Feb 2024 16:19:16 +0800 Subject: [PATCH] Add bounds cache for the Path class. --- src/core/Path.cpp | 22 +++++++--------------- src/core/PathRef.cpp | 30 ++++++++++++++++++++++++++++++ src/core/PathRef.h | 7 +++++++ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/core/Path.cpp b/src/core/Path.cpp index 949df6c..5c5ad88 100644 --- a/src/core/Path.cpp +++ b/src/core/Path.cpp @@ -161,16 +161,7 @@ bool Path::isLine(Point line[2]) const { } Rect Path::getBounds() const { - // Internally, SkPath lazily computes bounds. Use this function instead of path.getBounds() - // for thread safety. - const auto& path = pathRef->path; - auto count = path.countPoints(); - auto points = new SkPoint[static_cast(count)]; - path.getPoints(points, count); - auto rect = SkRect::MakeEmpty(); - rect.setBounds(points, count); - delete[] points; - return {rect.fLeft, rect.fTop, rect.fRight, rect.fBottom}; + return pathRef->getBounds(); } bool Path::isEmpty() const { @@ -308,14 +299,14 @@ void Path::addRoundRect(const Rect& rect, float radiusX, float radiusY, bool rev } void Path::addPath(const Path& src, PathOp op) { + if (isEmpty()) { + *this = src; + return; + } auto& path = writableRef()->path; const auto& newPath = src.pathRef->path; if (op == PathOp::Append) { - if (path.isEmpty()) { - path = newPath; - } else { - path.addPath(newPath); - } + path.addPath(newPath); return; } SkPathOp pathOp; @@ -401,6 +392,7 @@ PathRef* Path::writableRef() { pathRef = std::make_shared(pathRef->path); } else { pathRef->uniqueKey.reset(); + pathRef->resetBounds(); } return pathRef.get(); } diff --git a/src/core/PathRef.cpp b/src/core/PathRef.cpp index 8469032..aae4d69 100644 --- a/src/core/PathRef.cpp +++ b/src/core/PathRef.cpp @@ -33,4 +33,34 @@ SkPath& PathRef::WriteAccess(Path& path) { UniqueKey PathRef::GetUniqueKey(const Path& path) { return path.pathRef->uniqueKey.get(); } + +PathRef::~PathRef() { + resetBounds(); +} + +Rect PathRef::getBounds() { + auto cacheBounds = bounds.load(std::memory_order_acquire); + if (cacheBounds == nullptr) { + // Internally, SkPath lazily computes bounds. Use this function instead of path.getBounds() + // for thread safety. + auto count = path.countPoints(); + auto points = new SkPoint[static_cast(count)]; + path.getPoints(points, count); + auto rect = SkRect::MakeEmpty(); + rect.setBounds(points, count); + delete[] points; + auto newBounds = new Rect{rect.fLeft, rect.fTop, rect.fRight, rect.fBottom}; + if (bounds.compare_exchange_strong(cacheBounds, newBounds, std::memory_order_acq_rel)) { + cacheBounds = newBounds; + } else { + delete newBounds; + } + } + return *cacheBounds; +} + +void PathRef::resetBounds() { + auto oldBounds = bounds.exchange(nullptr, std::memory_order_acq_rel); + delete oldBounds; +} } // namespace tgfx \ No newline at end of file diff --git a/src/core/PathRef.h b/src/core/PathRef.h index 5ac0cd6..f84ab06 100644 --- a/src/core/PathRef.h +++ b/src/core/PathRef.h @@ -38,10 +38,17 @@ class PathRef { explicit PathRef(const pk::SkPath& path) : path(path) { } + ~PathRef(); + + Rect getBounds(); + private: LazyUniqueKey uniqueKey = {}; + std::atomic bounds = {nullptr}; pk::SkPath path = {}; + void resetBounds(); + friend bool operator==(const Path& a, const Path& b); friend bool operator!=(const Path& a, const Path& b); friend class Path;