commit 4bc276362491d28731154efdf229b95463e4ef29
parent 26fd9020d71577fa82471cde3b0004e0415b7c5d
Author: Jonathan Kew <jkew@mozilla.com>
Date: Sat, 4 Oct 2025 09:10:54 +0000
Bug 1991689 - Cache advances of text ranges measured by TextRenderedRun::GetClipEdges in the root SVGTextFrame to accelerate ReflowSVG. r=firefox-svg-reviewers,longsonr
Differential Revision: https://phabricator.services.mozilla.com/D267439
Diffstat:
3 files changed, 81 insertions(+), 13 deletions(-)
diff --git a/gfx/thebes/gfxTextRun.h b/gfx/thebes/gfxTextRun.h
@@ -163,6 +163,10 @@ class gfxTextRun : public gfxShapedText {
Range(uint32_t aStart, uint32_t aEnd) : start(aStart), end(aEnd) {}
explicit Range(const gfxTextRun* aTextRun)
: Range(0, aTextRun->GetLength()) {}
+
+ bool Intersects(const Range& aOther) const {
+ return start < aOther.end && end > aOther.start;
+ }
};
// All coordinates are in layout/app units
diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp
@@ -409,13 +409,14 @@ struct TextRenderedRun {
* paint it. See the comments documenting the member variables below
* for descriptions of the arguments.
*/
- TextRenderedRun(nsTextFrame* aFrame, const gfxPoint& aPosition,
- float aLengthAdjustScaleFactor, double aRotate,
- float aFontSizeScaleFactor, nscoord aBaseline,
+ TextRenderedRun(nsTextFrame* aFrame, SVGTextFrame* aSVGTextFrame,
+ const gfxPoint& aPosition, float aLengthAdjustScaleFactor,
+ double aRotate, float aFontSizeScaleFactor, nscoord aBaseline,
uint32_t aTextFrameContentOffset,
uint32_t aTextFrameContentLength,
uint32_t aTextElementCharIndex)
: mFrame(aFrame),
+ mRoot(aSVGTextFrame),
mPosition(aPosition),
mLengthAdjustScaleFactor(aLengthAdjustScaleFactor),
mRotate(static_cast<float>(aRotate)),
@@ -662,6 +663,11 @@ struct TextRenderedRun {
nsTextFrame* mFrame;
/**
+ * The SVGTextFrame to which our text frame belongs.
+ */
+ SVGTextFrame* mRoot;
+
+ /**
* The point in user space that the text is positioned at.
*
* For a horizontal run:
@@ -962,15 +968,43 @@ void TextRenderedRun::GetClipEdges(nscoord& aVisIStartEdge,
// characters.
Range frameRange = ConvertOriginalToSkipped(it, frameOffset, frameLength);
- // Measure the advance width in the text run between the start of
- // frame's content and the start of the rendered run's content,
- nscoord startEdge = textRun->GetAdvanceWidth(
- Range(frameRange.start, runRange.start), &provider);
+ // Get the advance of aRange, using the aCachedRange if available to
+ // accelerate textrun measurement.
+ auto MeasureUsingCache = [&](SVGTextFrame::CachedMeasuredRange& aCachedRange,
+ const Range& aRange) -> nscoord {
+ if (aRange.Intersects(aCachedRange.mRange)) {
+ // We only need to measure the differences between the cached range and
+ // aRange.
+ // TODO: check whether this will in fact involve less measurement!
+ if (aRange.start < aCachedRange.mRange.start) {
+ aCachedRange.mAdvance += textRun->GetAdvanceWidth(
+ Range(aRange.start, aCachedRange.mRange.start), &provider);
+ } else if (aRange.start > aCachedRange.mRange.start) {
+ aCachedRange.mAdvance -= textRun->GetAdvanceWidth(
+ Range(aCachedRange.mRange.start, aRange.start), &provider);
+ }
+ if (aRange.end > aCachedRange.mRange.end) {
+ aCachedRange.mAdvance += textRun->GetAdvanceWidth(
+ Range(aCachedRange.mRange.end, aRange.end), &provider);
+ } else if (aRange.end < aCachedRange.mRange.end) {
+ aCachedRange.mAdvance -= textRun->GetAdvanceWidth(
+ Range(aRange.end, aCachedRange.mRange.end), &provider);
+ }
+ } else {
+ // Just measure the range, and cache the result.
+ aCachedRange.mAdvance = textRun->GetAdvanceWidth(aRange, &provider);
+ }
+ aCachedRange.mRange = aRange;
+ return aCachedRange.mAdvance;
+ };
- // and between the end of the rendered run's content and the end
- // of the frame's content.
+ mRoot->SetCurrentFrameForCaching(mFrame);
+ nscoord startEdge =
+ MeasureUsingCache(mRoot->CachedRange(SVGTextFrame::WhichRange::Before),
+ Range(frameRange.start, runRange.start));
nscoord endEdge =
- textRun->GetAdvanceWidth(Range(runRange.end, frameRange.end), &provider);
+ MeasureUsingCache(mRoot->CachedRange(SVGTextFrame::WhichRange::After),
+ Range(runRange.end, frameRange.end));
if (textRun->IsRightToLeft()) {
aVisIStartEdge = endEdge;
@@ -1900,9 +1934,9 @@ TextRenderedRun TextRenderedRunIterator::Next() {
}
}
- mCurrent = TextRenderedRun(frame, pt, Root()->mLengthAdjustScaleFactor,
- rotate, mFontSizeScaleFactor, baseline, offset,
- length, charIndex);
+ mCurrent = TextRenderedRun(
+ frame, Root(), pt, Root()->mLengthAdjustScaleFactor, rotate,
+ mFontSizeScaleFactor, baseline, offset, length, charIndex);
return mCurrent;
}
@@ -5108,6 +5142,9 @@ void SVGTextFrame::DoReflow() {
RemoveStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
}
+ // Forget any cached measurements of one of our children.
+ mFrameForCachedRanges = nullptr;
+
nsPresContext* presContext = PresContext();
nsIFrame* kid = PrincipalChildList().FirstChild();
if (!kid) {
diff --git a/layout/svg/SVGTextFrame.h b/layout/svg/SVGTextFrame.h
@@ -586,6 +586,33 @@ class SVGTextFrame final : public SVGDisplayContainerFrame {
* lengthAdjust="spacingAndGlyphs".
*/
float mLengthAdjustScaleFactor = 1.0f;
+
+ public:
+ struct CachedMeasuredRange {
+ Range mRange;
+ nscoord mAdvance;
+ };
+
+ void SetCurrentFrameForCaching(const nsTextFrame* aFrame) {
+ if (mFrameForCachedRanges != aFrame) {
+ PodArrayZero(mCachedRanges);
+ mFrameForCachedRanges = aFrame;
+ }
+ }
+
+ enum WhichRange {
+ Before,
+ After,
+ CachedRangeCount,
+ };
+
+ CachedMeasuredRange& CachedRange(WhichRange aWhichRange) {
+ return mCachedRanges[aWhichRange];
+ }
+
+ private:
+ const nsTextFrame* mFrameForCachedRanges = nullptr;
+ CachedMeasuredRange mCachedRanges[CachedRangeCount];
};
} // namespace mozilla