Bug 1801463 - Use a cursor in FlattenedPath to accelerate successive calls to ComputePointAtLength for SVG text-on-a-path layout. r=gfx-reviewers,nical

This enables my local build to achieve 60fps on the js1k demo linked from the bug,
whereas without the patch I get barely 10fps.

Note: it's still possible for ComputePointAtLength would perform poorly if
the caller is iterating backwards or doing random access to a long path.
A potential mitigation for that would be to add an mLength field to FlatPathOp,
storing the length-so-far of the path, so that ComputePointAtLength could do
a binary search instead of linear accumulation. But this would add significant
memory overhead, and may not be worth doing; the low-overhead cursor here
appears to be enough to make text-on-a-path perform much better.

Differential Revision: https://phabricator.services.mozilla.com/D168937
This commit is contained in:
Jonathan Kew 2023-02-06 15:01:20 +00:00
Родитель 9aa73e790c
Коммит 0569fbd9be
2 изменённых файлов: 41 добавлений и 21 удалений

Просмотреть файл

@ -138,46 +138,52 @@ Float FlattenedPath::ComputeLength() {
}
Point FlattenedPath::ComputePointAtLength(Float aLength, Point* aTangent) {
// We track the last point that -wasn't- in the same place as the current
// point so if we pass the edge of the path with a bunch of zero length
// paths we still get the correct tangent vector.
Point lastPointSinceMove;
Point currentPoint;
for (uint32_t i = 0; i < mPathOps.size(); i++) {
if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) {
if (Distance(currentPoint, mPathOps[i].mPoint)) {
lastPointSinceMove = currentPoint;
if (aLength < mCursor.mLength) {
// If cursor is beyond the target length, reset to the beginning.
mCursor.Reset();
} else {
// Adjust aLength to account for the position where we'll start searching.
aLength -= mCursor.mLength;
}
while (mCursor.mIndex < mPathOps.size()) {
const auto& op = mPathOps[mCursor.mIndex];
if (op.mType == FlatPathOp::OP_MOVETO) {
if (Distance(mCursor.mCurrentPoint, op.mPoint) > 0.0f) {
mCursor.mLastPointSinceMove = mCursor.mCurrentPoint;
}
currentPoint = mPathOps[i].mPoint;
mCursor.mCurrentPoint = op.mPoint;
} else {
Float segmentLength = Distance(currentPoint, mPathOps[i].mPoint);
Float segmentLength = Distance(mCursor.mCurrentPoint, op.mPoint);
if (segmentLength) {
lastPointSinceMove = currentPoint;
mCursor.mLastPointSinceMove = mCursor.mCurrentPoint;
if (segmentLength > aLength) {
Point currentVector = mPathOps[i].mPoint - currentPoint;
Point currentVector = op.mPoint - mCursor.mCurrentPoint;
Point tangent = currentVector / segmentLength;
if (aTangent) {
*aTangent = tangent;
}
return currentPoint + tangent * aLength;
return mCursor.mCurrentPoint + tangent * aLength;
}
}
aLength -= segmentLength;
currentPoint = mPathOps[i].mPoint;
mCursor.mLength += segmentLength;
mCursor.mCurrentPoint = op.mPoint;
}
mCursor.mIndex++;
}
if (aTangent) {
Point currentVector = currentPoint - lastPointSinceMove;
Point currentVector = mCursor.mCurrentPoint - mCursor.mLastPointSinceMove;
if (auto h = hypotf(currentVector.x, currentVector.y)) {
*aTangent = currentVector / h;
} else {
*aTangent = Point();
}
}
return currentPoint;
return mCursor.mCurrentPoint;
}
// This function explicitly permits aControlPoints to refer to the same object

Просмотреть файл

@ -24,8 +24,6 @@ class FlattenedPath : public PathSink {
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FlattenedPath, override)
FlattenedPath() : mCachedLength(0), mCalculatedLength(false) {}
virtual void MoveTo(const Point& aPoint) override;
virtual void LineTo(const Point& aPoint) override;
virtual void BezierTo(const Point& aCP1, const Point& aCP2,
@ -43,10 +41,26 @@ class FlattenedPath : public PathSink {
Point ComputePointAtLength(Float aLength, Point* aTangent);
private:
Float mCachedLength;
bool mCalculatedLength;
Float mCachedLength = 0.0f;
bool mCalculatedLength = false;
std::vector<FlatPathOp> mPathOps;
// Used to accelerate ComputePointAtLength for the common case of iterating
// forward along the path.
struct {
uint32_t mIndex = 0;
Float mLength = 0.0f;
Point mCurrentPoint;
Point mLastPointSinceMove;
void Reset() {
mIndex = 0;
mLength = 0.0f;
mCurrentPoint = Point();
mLastPointSinceMove = Point();
}
} mCursor;
};
} // namespace gfx