From 3d69be51bb799fd8154a9f29bf64903a0a48aa46 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Mon, 10 Sep 2018 16:12:21 -0400 Subject: [PATCH] add SkPath::shrinkToFit Bug: skia: Change-Id: Ief647bcea53c0aeae2750473288bd31f16521772 Reviewed-on: https://skia-review.googlesource.com/150967 Reviewed-by: Florin Malita Commit-Queue: Mike Reed Auto-Submit: Mike Reed --- include/core/SkPath.h | 4 ++++ include/private/SkPathRef.h | 1 + src/core/SkPathRef.cpp | 48 +++++++++++++++++++++++++++++++++++++ tests/PathTest.cpp | 37 ++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+) diff --git a/include/core/SkPath.h b/include/core/SkPath.h index 75331eb8e3..64dec9db7d 100644 --- a/include/core/SkPath.h +++ b/include/core/SkPath.h @@ -549,6 +549,8 @@ public: */ void incReserve(unsigned extraPtCount); + void shrinkToFit(); + /** Adds beginning of contour at SkPoint (x, y). @param x x-axis value of contour start @@ -1667,6 +1669,8 @@ public: bool pathRefIsValid() const { return fPathRef->isValid(); } #endif + SkDEBUGCODE(size_t debugging_private_getFreeSpace() const;) + private: sk_sp fPathRef; int fLastMoveToIndex; diff --git a/include/private/SkPathRef.h b/include/private/SkPathRef.h index 5a5a04118c..f473db2566 100644 --- a/include/private/SkPathRef.h +++ b/include/private/SkPathRef.h @@ -555,6 +555,7 @@ private: friend class PathRefTest_Private; friend class ForceIsRRect_Private; // unit test isRRect + friend class SkPath; }; #endif diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp index 20d3131607..dc280afc77 100644 --- a/src/core/SkPathRef.cpp +++ b/src/core/SkPathRef.cpp @@ -43,6 +43,54 @@ SkPathRef::Editor::Editor(sk_sp* pathRef, SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);) } +// Sort of like makeSpace(0) but the the additional requirement that we actively shrink the +// allocations to just fit the current needs. makeSpace() will only grow, but never shrinks. +// +void SkPath::shrinkToFit() { + const size_t kMinFreeSpaceForShrink = 8; // just made up a small number + + if (fPathRef->fFreeSpace <= kMinFreeSpaceForShrink) { + return; + } + + int pointCount = fPathRef->fPointCnt; + int verbCount = fPathRef->fVerbCnt; + + size_t ptsSize = sizeof(SkPoint) * pointCount; + size_t vrbSize = sizeof(uint8_t) * verbCount; + size_t minSize = ptsSize + vrbSize; + + void* newAlloc = sk_malloc_canfail(minSize); + if (!newAlloc) { + return; // couldn't allocate the smaller buffer, but that's ok + } + memcpy(newAlloc, fPathRef->fPoints, ptsSize); + memcpy((char*)newAlloc + minSize - vrbSize, fPathRef->fVerbs, vrbSize); + + SkPathRef* pr = fPathRef.get(); + if (fPathRef->unique()) { + sk_free(fPathRef->fPoints); + } else { + pr = new SkPathRef; + pr->fPointCnt = pointCount; + pr->fVerbCnt = verbCount; + pr->fConicWeights = fPathRef->fConicWeights; + fPathRef.reset(pr); + } + + pr->fPoints = static_cast(newAlloc); + pr->fVerbs = (uint8_t*)newAlloc + minSize; + pr->fFreeSpace = 0; + pr->fConicWeights.shrinkToFit(); + SkDEBUGCODE(pr->validate();) +} + +#ifdef SK_DEBUG +size_t SkPath::debugging_private_getFreeSpace() const { + return fPathRef->fFreeSpace; +} +#endif + ////////////////////////////////////////////////////////////////////////////// SkPathRef::~SkPathRef() { diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index 67bba43ec3..c3ede183bd 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -5103,3 +5103,40 @@ DEF_TEST(triangle_big, reporter) { draw_triangle(surface->getCanvas(), pts); } +static void add_verbs(SkPath* path, int count) { + path->moveTo(0, 0); + for (int i = 0; i < count; ++i) { + switch (i & 3) { + case 0: path->lineTo(10, 20); break; + case 1: path->quadTo(5, 6, 7, 8); break; + case 2: path->conicTo(1, 2, 3, 4, 0.5f); break; + case 3: path->cubicTo(2, 4, 6, 8, 10, 12); break; + } + } +} + +// Make sure when we call shrinkToFit() that we always shrink (or stay the same) +// and that if we call twice, we stay the same. +DEF_TEST(Path_shrinkToFit, reporter) { + SkPath path; + size_t max_free = 0; + for (int verbs = 0; verbs < 100; ++verbs) { + SkPath path; + add_verbs(&path, verbs); +#ifdef SK_DEBUG + size_t before = path.debugging_private_getFreeSpace(); +#endif + path.shrinkToFit(); +#ifdef SK_DEBUG + size_t after = path.debugging_private_getFreeSpace(); + REPORTER_ASSERT(reporter, before >= after); + max_free = std::max(max_free, before - after); + + size_t after2 = path.debugging_private_getFreeSpace(); + REPORTER_ASSERT(reporter, after == after2); +#endif + } + if (false) { + SkDebugf("max_free %zu\n", max_free); + } +}