Bug 1399310 - (Part 1) Make `nsTextFrame::DrawTextRunAndDecorations` draw only in the range of the text.
We create a clip region with the text length to make the decoration line
would be only drawn in the area. This allows the decoration line would
not be drawn multiple times when the text is being selected.
MozReview-Commit-ID: 4gjawk71eSu
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -7265,29 +7265,67 @@ nsTextFrame::DrawTextRunAndDecorations(R
bCoord = (frameBStart - dec.mBaselineOffset) / app;
params.color = dec.mColor;
params.offset = metrics.*lineOffset;
params.style = dec.mStyle;
PaintDecorationLine(params);
};
+ // We create a clip region in order to draw the decoration lines only in the
+ // range of the text. Restricting the draw area prevents the decoration lines
+ // to be drawn multiple times when a part of the text is selected.
+
+ // We skip clipping for the following cases:
+ // - drawing the whole text
+ // - having different orientation of the text and the writing-mode, such as
+ // "text-combine-upright" (Bug 1408825)
+ bool skipClipping = aRange.Length() == mTextRun->GetLength() ||
+ verticalDec != verticalRun;
+
+ gfxRect clipRect;
+ if (!skipClipping) {
+ // Get the inline-size according to the specified range.
+ gfxFloat clipLength = mTextRun->GetAdvanceWidth(aRange, aParams.provider);
+
+ clipRect.width = verticalDec ? frameSize.width : clipLength / app;
+ clipRect.height = verticalDec ? clipLength / app : frameSize.height;
+
+ const bool isInlineReversed = mTextRun->IsInlineReversed();
+ if (verticalDec) {
+ clipRect.y = (isInlineReversed ? aTextBaselinePt.y - clipLength
+ : aTextBaselinePt.y) / app;
+ } else {
+ clipRect.x = (isInlineReversed ? aTextBaselinePt.x - clipLength
+ : aTextBaselinePt.x) / app;
+ }
+
+ clipRect.Round();
+ params.context->Clip(clipRect);
+ }
+
// Underlines
params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
for (const LineDecoration& dec : Reversed(aDecorations.mUnderlines)) {
paintDecorationLine(dec, &Metrics::underlineSize,
&Metrics::underlineOffset);
}
// Overlines
params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
for (const LineDecoration& dec : Reversed(aDecorations.mOverlines)) {
paintDecorationLine(dec, &Metrics::underlineSize, &Metrics::maxAscent);
}
+ // Some glyphs and emphasis marks may extend outside the region, so we reset
+ // the clip region here. For an example, italic glyphs.
+ if (!skipClipping) {
+ params.context->PopClip();
+ }
+
{
gfxContextMatrixAutoSaveRestore unscaledRestorer;
if (scaledRestorer.HasMatrix()) {
unscaledRestorer.SetContext(aParams.context);
aParams.context->SetMatrix(scaledRestorer.Matrix());
}
// CSS 2.1 mandates that text be painted after over/underlines,
@@ -7295,22 +7333,31 @@ nsTextFrame::DrawTextRunAndDecorations(R
DrawTextRun(aRange, aTextBaselinePt, aParams);
}
// Emphasis marks
DrawEmphasisMarks(aParams.context, wm,
aTextBaselinePt, aParams.framePt, aRange,
aParams.decorationOverrideColor, aParams.provider);
+ // Re-apply the clip region when the line-through is being drawn.
+ if (!skipClipping) {
+ params.context->Clip(clipRect);
+ }
+
// Line-throughs
params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
for (const LineDecoration& dec : Reversed(aDecorations.mStrikes)) {
paintDecorationLine(dec, &Metrics::strikeoutSize,
&Metrics::strikeoutOffset);
}
+
+ if (!skipClipping) {
+ params.context->PopClip();
+ }
}
void
nsTextFrame::DrawText(Range aRange, const gfx::Point& aTextBaselinePt,
const DrawTextParams& aParams)
{
TextDecorations decorations;
GetTextDecorations(aParams.textStyle->PresContext(),