Skip to content

Commit 713a02b

Browse files
committed
Bug 1979915 Part 1 - Apply text-decoration-trim during decoration rect calculation and decoration painting. r=layout-reviewers,layout-jp-market-reviewers,jfkthame
This is still pref'ed off, even though we don't directly check the pref in this code. Without the pref layout.css.text-decoration-trim.enabled set, this should do nothing as the intial value of zero will result in no text decoration trim. This change still leaves a few situations with incorrect painting: The current implementation will always have zero trim on a line break, but it is possible that a very large trim value and the leftover trim from the next line should then overflow to the previous line. No distinction is made between BIDI continuations and line breaks. Combined with the previous limitation, this means that in the case of a BIDI continuation at the start or end of a line which is smaller than the text trim, the decoration will begin where the inline frames meet. This also causes incorrect painting when the box-decoration-break value is `clone` in a line with BIDI continuations, as the trim will now be applied where the continuations meet. Differential Revision: https://phabricator.services.mozilla.com/D265726
1 parent d7b3050 commit 713a02b

File tree

2 files changed

+101
-2
lines changed

2 files changed

+101
-2
lines changed

layout/generic/nsTextFrame.cpp

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5772,6 +5772,97 @@ static gfxFloat ComputeDecorationLineOffset(
57725772
return 0;
57735773
}
57745774

5775+
// Helper to determine decoration trim offset.
5776+
// Returns false if the trim would cut off the decoration entirely.
5777+
static bool ComputeDecorationTrim(
5778+
const nsTextFrame* aFrame, const nsPresContext* aPresCtx,
5779+
const nsIFrame* aDecFrame, const gfxFont::Metrics& aMetrics,
5780+
nsCSSRendering::DecorationRectParams& aParams) {
5781+
const gfxFloat app = aPresCtx->AppUnitsPerDevPixel();
5782+
const WritingMode wm = aDecFrame->GetWritingMode();
5783+
bool verticalDec = wm.IsVertical();
5784+
5785+
aParams.trimLeft = 0.0;
5786+
aParams.trimRight = 0.0;
5787+
5788+
// Find the trim values for this frame.
5789+
const StyleTextDecorationTrim& cssTrim =
5790+
aDecFrame->StyleTextReset()->mTextDecorationTrim;
5791+
gfxFloat trimLeft, trimRight;
5792+
if (cssTrim.IsAuto()) {
5793+
// Use the EM size divide by 8, or 1 dev pixel if this is too
5794+
// small to ensure that at least some separation occurs.
5795+
const gfxFloat scale = aPresCtx->CSSToDevPixelScale().scale;
5796+
const nscoord autoDecorationTrim =
5797+
std::max(aMetrics.emHeight * 0.125, scale);
5798+
trimLeft = autoDecorationTrim;
5799+
trimRight = autoDecorationTrim;
5800+
} else {
5801+
MOZ_ASSERT(cssTrim.IsLength(), "Impossible text-decoration-trim");
5802+
const auto& length = cssTrim.AsLength();
5803+
if (length.start.IsZero() && length.end.IsZero()) {
5804+
// We can avoid doing the geometric calculations below, potentially
5805+
// walking up and back down the frame tree, and walking continuations.
5806+
return true;
5807+
}
5808+
trimLeft = NSAppUnitsToDoublePixels(length.start.ToAppUnits(), app);
5809+
trimRight = NSAppUnitsToDoublePixels(length.end.ToAppUnits(), app);
5810+
}
5811+
5812+
if (wm.IsBidiRTL()) {
5813+
std::swap(trimLeft, trimRight);
5814+
}
5815+
const nsPoint offset = aFrame->GetOffsetTo(aDecFrame);
5816+
const nsSize decSize = aDecFrame->GetSize();
5817+
const nsSize size = aFrame->GetSize();
5818+
nscoord start, end, max;
5819+
if (verticalDec) {
5820+
start = offset.y;
5821+
max = size.height;
5822+
end = decSize.height - (size.height + offset.y);
5823+
} else {
5824+
start = offset.x;
5825+
max = size.width;
5826+
end = decSize.width - (size.width + offset.x);
5827+
}
5828+
5829+
const bool cloneDecBreak = aDecFrame->StyleBorder()->mBoxDecorationBreak ==
5830+
StyleBoxDecorationBreak::Clone;
5831+
// TODO alaskanemily: This will not correctly account for the case that the
5832+
// continuations are bidi continuations.
5833+
const bool applyLeft = cloneDecBreak || !aDecFrame->GetPrevContinuation();
5834+
if (applyLeft) {
5835+
trimLeft -= NSAppUnitsToDoublePixels(start, app);
5836+
}
5837+
const bool applyRight = cloneDecBreak || !aDecFrame->GetNextContinuation();
5838+
if (applyRight) {
5839+
trimRight -= NSAppUnitsToDoublePixels(end, app);
5840+
}
5841+
5842+
if (trimLeft >= NSAppUnitsToDoublePixels(max, app) - trimRight) {
5843+
// This frame does not contain the decoration at all.
5844+
return false;
5845+
}
5846+
// TODO alaskanemily: We currently determine if we should have a negative
5847+
// trim value by checking if we are at the edge of frame from which the
5848+
// decloration comes from.
5849+
//
5850+
// This is not absolutely correct, there could in theory be a zero-width
5851+
// frame before/after this frame, and we will draw the decoration extension
5852+
// twice (causing a visible inaccuracy for semi-transparent decorations).
5853+
//
5854+
// I am unsure if it's possible that the first/last frame might be inset
5855+
// for some reason, as well, in which case we will not draw the outset
5856+
// decorations.
5857+
if (applyLeft && (trimLeft > 0.0 || start == 0)) {
5858+
aParams.trimLeft = trimLeft;
5859+
}
5860+
if (applyRight && (trimRight > 0.0 || end == 0)) {
5861+
aParams.trimRight = trimRight;
5862+
}
5863+
return true;
5864+
}
5865+
57755866
void nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
57765867
nsIFrame* aBlock,
57775868
PropertyProvider& aProvider,
@@ -5913,6 +6004,11 @@ void nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
59136004
metrics, appUnitsPerDevUnit, this, parentWM.IsCentralBaseline(),
59146005
swapUnderline);
59156006

6007+
if (!ComputeDecorationTrim(this, aPresContext, dec.mFrame, metrics,
6008+
params)) {
6009+
return;
6010+
}
6011+
59166012
nsRect decorationRect =
59176013
nsCSSRendering::GetTextDecorationRect(aPresContext, params) +
59186014
(verticalDec ? nsPoint(frameBStart - dec.mBaselineOffset, 0)
@@ -7669,7 +7765,10 @@ void nsTextFrame::DrawTextRunAndDecorations(
76697765
GetInflationForTextDecorations(dec.mFrame, inflationMinFontSize);
76707766
const Metrics metrics = GetFirstFontMetrics(
76717767
GetFontGroupForFrame(dec.mFrame, inflation), useVerticalMetrics);
7672-
7768+
if (!ComputeDecorationTrim(this, aParams.textStyle->PresContext(),
7769+
dec.mFrame, metrics, params)) {
7770+
return;
7771+
}
76737772
bCoord = (frameBStart - dec.mBaselineOffset) / app;
76747773

76757774
params.color = dec.mColor;

layout/painting/nsCSSRendering.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ struct nsCSSRendering {
554554
mozilla::Side aStartBevelSide, nscoord aStartBevelOffset,
555555
mozilla::Side aEndBevelSide, nscoord aEndBevelOffset);
556556

557-
// NOTE: pt, dirtyRect, lineSize, ascent, offset, trimStart and trimEnd in
557+
// NOTE: pt, dirtyRect, lineSize, ascent, offset, trimLeft, and trimRight in
558558
// the following structs are non-rounded device pixels, not app units.
559559
struct DecorationRectParams {
560560
// Checks if either start or end trim value is negative.

0 commit comments

Comments
 (0)