зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
9aa73e790c
Коммит
0569fbd9be
|
@ -138,46 +138,52 @@ Float FlattenedPath::ComputeLength() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Point FlattenedPath::ComputePointAtLength(Float aLength, Point* aTangent) {
|
Point FlattenedPath::ComputePointAtLength(Float aLength, Point* aTangent) {
|
||||||
// We track the last point that -wasn't- in the same place as the current
|
if (aLength < mCursor.mLength) {
|
||||||
// point so if we pass the edge of the path with a bunch of zero length
|
// If cursor is beyond the target length, reset to the beginning.
|
||||||
// paths we still get the correct tangent vector.
|
mCursor.Reset();
|
||||||
Point lastPointSinceMove;
|
} else {
|
||||||
Point currentPoint;
|
// Adjust aLength to account for the position where we'll start searching.
|
||||||
for (uint32_t i = 0; i < mPathOps.size(); i++) {
|
aLength -= mCursor.mLength;
|
||||||
if (mPathOps[i].mType == FlatPathOp::OP_MOVETO) {
|
}
|
||||||
if (Distance(currentPoint, mPathOps[i].mPoint)) {
|
|
||||||
lastPointSinceMove = currentPoint;
|
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 {
|
} else {
|
||||||
Float segmentLength = Distance(currentPoint, mPathOps[i].mPoint);
|
Float segmentLength = Distance(mCursor.mCurrentPoint, op.mPoint);
|
||||||
|
|
||||||
if (segmentLength) {
|
if (segmentLength) {
|
||||||
lastPointSinceMove = currentPoint;
|
mCursor.mLastPointSinceMove = mCursor.mCurrentPoint;
|
||||||
if (segmentLength > aLength) {
|
if (segmentLength > aLength) {
|
||||||
Point currentVector = mPathOps[i].mPoint - currentPoint;
|
Point currentVector = op.mPoint - mCursor.mCurrentPoint;
|
||||||
Point tangent = currentVector / segmentLength;
|
Point tangent = currentVector / segmentLength;
|
||||||
if (aTangent) {
|
if (aTangent) {
|
||||||
*aTangent = tangent;
|
*aTangent = tangent;
|
||||||
}
|
}
|
||||||
return currentPoint + tangent * aLength;
|
return mCursor.mCurrentPoint + tangent * aLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aLength -= segmentLength;
|
aLength -= segmentLength;
|
||||||
currentPoint = mPathOps[i].mPoint;
|
mCursor.mLength += segmentLength;
|
||||||
|
mCursor.mCurrentPoint = op.mPoint;
|
||||||
}
|
}
|
||||||
|
mCursor.mIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aTangent) {
|
if (aTangent) {
|
||||||
Point currentVector = currentPoint - lastPointSinceMove;
|
Point currentVector = mCursor.mCurrentPoint - mCursor.mLastPointSinceMove;
|
||||||
if (auto h = hypotf(currentVector.x, currentVector.y)) {
|
if (auto h = hypotf(currentVector.x, currentVector.y)) {
|
||||||
*aTangent = currentVector / h;
|
*aTangent = currentVector / h;
|
||||||
} else {
|
} else {
|
||||||
*aTangent = Point();
|
*aTangent = Point();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return currentPoint;
|
return mCursor.mCurrentPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function explicitly permits aControlPoints to refer to the same object
|
// This function explicitly permits aControlPoints to refer to the same object
|
||||||
|
|
|
@ -24,8 +24,6 @@ class FlattenedPath : public PathSink {
|
||||||
public:
|
public:
|
||||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FlattenedPath, override)
|
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FlattenedPath, override)
|
||||||
|
|
||||||
FlattenedPath() : mCachedLength(0), mCalculatedLength(false) {}
|
|
||||||
|
|
||||||
virtual void MoveTo(const Point& aPoint) override;
|
virtual void MoveTo(const Point& aPoint) override;
|
||||||
virtual void LineTo(const Point& aPoint) override;
|
virtual void LineTo(const Point& aPoint) override;
|
||||||
virtual void BezierTo(const Point& aCP1, const Point& aCP2,
|
virtual void BezierTo(const Point& aCP1, const Point& aCP2,
|
||||||
|
@ -43,10 +41,26 @@ class FlattenedPath : public PathSink {
|
||||||
Point ComputePointAtLength(Float aLength, Point* aTangent);
|
Point ComputePointAtLength(Float aLength, Point* aTangent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Float mCachedLength;
|
Float mCachedLength = 0.0f;
|
||||||
bool mCalculatedLength;
|
bool mCalculatedLength = false;
|
||||||
|
|
||||||
std::vector<FlatPathOp> mPathOps;
|
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
|
} // namespace gfx
|
||||||
|
|
Загрузка…
Ссылка в новой задаче