mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1816628 Part 2 - Make text-shadow react to stroke properties and fill opacity r=jfkthame
Differential Revision: https://phabricator.services.mozilla.com/D200436
This commit is contained in:
parent
1cdd9ca5d0
commit
bca1520c01
@ -226,6 +226,7 @@ struct nsTextFrame::DrawTextRunParams {
|
||||
float textStrokeWidth = 0.0f;
|
||||
bool drawSoftHyphen = false;
|
||||
bool hasTextShadow = false;
|
||||
bool paintingShadows = false;
|
||||
DrawTextRunParams(gfxContext* aContext,
|
||||
mozilla::gfx::PaletteCache& aPaletteCache)
|
||||
: context(aContext), paletteCache(aPaletteCache) {}
|
||||
@ -276,6 +277,7 @@ struct nsTextFrame::PaintShadowParams {
|
||||
Point framePt;
|
||||
Point textBaselinePt;
|
||||
gfxContext* context;
|
||||
DrawPathCallbacks* callbacks = nullptr;
|
||||
nscolor foregroundColor = NS_RGBA(0, 0, 0, 0);
|
||||
const ClipEdges* clipEdges = nullptr;
|
||||
PropertyProvider* provider = nullptr;
|
||||
@ -5459,6 +5461,7 @@ struct nsTextFrame::PaintDecorationLineParams
|
||||
gfxFloat baselineOffset = 0.0f;
|
||||
DecorationType decorationType = DecorationType::Normal;
|
||||
DrawPathCallbacks* callbacks = nullptr;
|
||||
bool paintingShadows = false;
|
||||
};
|
||||
|
||||
void nsTextFrame::PaintDecorationLine(
|
||||
@ -5473,9 +5476,11 @@ void nsTextFrame::PaintDecorationLine(
|
||||
if (aParams.callbacks) {
|
||||
Rect path = nsCSSRendering::DecorationLineToPath(params);
|
||||
if (aParams.decorationType == DecorationType::Normal) {
|
||||
aParams.callbacks->PaintDecorationLine(path, params.color);
|
||||
aParams.callbacks->PaintDecorationLine(path, aParams.paintingShadows,
|
||||
params.color);
|
||||
} else {
|
||||
aParams.callbacks->PaintSelectionDecorationLine(path, params.color);
|
||||
aParams.callbacks->PaintSelectionDecorationLine(
|
||||
path, aParams.paintingShadows, params.color);
|
||||
}
|
||||
} else {
|
||||
nsCSSRendering::PaintDecorationLine(this, *aParams.context->GetDrawTarget(),
|
||||
@ -5937,6 +5942,7 @@ void nsTextFrame::PaintOneShadow(const PaintShadowParams& aParams,
|
||||
gfxFloat advanceWidth;
|
||||
nsTextPaintStyle textPaintStyle(this);
|
||||
DrawTextParams params(shadowContext, PresContext()->FontPaletteCache());
|
||||
params.paintingShadows = true;
|
||||
params.advanceWidth = &advanceWidth;
|
||||
params.dirtyRect = aParams.dirtyRect;
|
||||
params.framePt = aParams.framePt + shadowGfxOffset;
|
||||
@ -5944,9 +5950,10 @@ void nsTextFrame::PaintOneShadow(const PaintShadowParams& aParams,
|
||||
params.textStyle = &textPaintStyle;
|
||||
params.textColor =
|
||||
aParams.context == shadowContext ? shadowColor : NS_RGB(0, 0, 0);
|
||||
params.callbacks = aParams.callbacks;
|
||||
params.clipEdges = aParams.clipEdges;
|
||||
params.drawSoftHyphen = HasAnyStateBits(TEXT_HYPHEN_BREAK);
|
||||
// Multi-color shadow is not allowed, so we use the same color of the text
|
||||
// Multi-color shadow is not allowed, so we use the same color as the text
|
||||
// color.
|
||||
params.decorationOverrideColor = ¶ms.textColor;
|
||||
params.fontPalette = StyleFont()->GetFontPaletteAtom();
|
||||
@ -6252,6 +6259,7 @@ bool nsTextFrame::PaintTextWithSelectionColors(
|
||||
|
||||
PaintShadowParams shadowParams(aParams);
|
||||
shadowParams.provider = aParams.provider;
|
||||
shadowParams.callbacks = aParams.callbacks;
|
||||
shadowParams.clipEdges = &aClipEdges;
|
||||
|
||||
// Draw text
|
||||
@ -6814,6 +6822,7 @@ void nsTextFrame::PaintText(const PaintTextParams& aParams,
|
||||
shadowParams.textBaselinePt = textBaselinePt;
|
||||
shadowParams.leftSideOffset = snappedStartEdge;
|
||||
shadowParams.provider = &provider;
|
||||
shadowParams.callbacks = aParams.callbacks;
|
||||
shadowParams.foregroundColor = foregroundColor;
|
||||
shadowParams.clipEdges = &clipEdges;
|
||||
PaintShadows(textStyle->mTextShadow.AsSpan(), shadowParams);
|
||||
@ -6853,7 +6862,8 @@ static void DrawTextRun(const gfxTextRun* aTextRun,
|
||||
params.callbacks = aParams.callbacks;
|
||||
params.hasTextShadow = aParams.hasTextShadow;
|
||||
if (aParams.callbacks) {
|
||||
aParams.callbacks->NotifyBeforeText(aParams.textColor);
|
||||
aParams.callbacks->NotifyBeforeText(aParams.paintingShadows,
|
||||
aParams.textColor);
|
||||
params.drawMode = DrawMode::GLYPH_PATH;
|
||||
aTextRun->Draw(aRange, aTextBaselinePt, params);
|
||||
aParams.callbacks->NotifyAfterText();
|
||||
@ -6994,6 +7004,7 @@ void nsTextFrame::DrawTextRunAndDecorations(
|
||||
params.callbacks = aParams.callbacks;
|
||||
params.glyphRange = aParams.glyphRange;
|
||||
params.provider = aParams.provider;
|
||||
params.paintingShadows = aParams.paintingShadows;
|
||||
// pt is the physical point where the decoration is to be drawn,
|
||||
// relative to the frame; one of its coordinates will be updated below.
|
||||
params.pt = Point(x / app, y / app);
|
||||
|
@ -513,20 +513,22 @@ class nsTextFrame : public nsIFrame {
|
||||
* Called before (for under/over-line) or after (for line-through) the text
|
||||
* is drawn to have a text decoration line drawn.
|
||||
*/
|
||||
virtual void PaintDecorationLine(Rect aPath, nscolor aColor) {}
|
||||
virtual void PaintDecorationLine(Rect aPath, bool aPaintingShadows,
|
||||
nscolor aColor) {}
|
||||
|
||||
/**
|
||||
* Called after selected text is drawn to have a decoration line drawn over
|
||||
* the text. (All types of text decoration are drawn after the text when
|
||||
* text is selected.)
|
||||
*/
|
||||
virtual void PaintSelectionDecorationLine(Rect aPath, nscolor aColor) {}
|
||||
virtual void PaintSelectionDecorationLine(Rect aPath, bool aPaintingShadows,
|
||||
nscolor aColor) {}
|
||||
|
||||
/**
|
||||
* Called just before any paths have been emitted to the gfxContext
|
||||
* for the glyphs of the frame's text.
|
||||
*/
|
||||
virtual void NotifyBeforeText(nscolor aColor) {}
|
||||
virtual void NotifyBeforeText(bool aPaintingShadows, nscolor aColor) {}
|
||||
|
||||
/**
|
||||
* Called just after all the paths have been emitted to the gfxContext
|
||||
|
@ -2412,9 +2412,11 @@ class SVGTextDrawPathCallbacks final : public nsTextFrame::DrawPathCallbacks {
|
||||
void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect,
|
||||
nscolor aColor,
|
||||
DrawTarget& aDrawTarget) override;
|
||||
void PaintDecorationLine(Rect aPath, nscolor aColor) override;
|
||||
void PaintSelectionDecorationLine(Rect aPath, nscolor aColor) override;
|
||||
void NotifyBeforeText(nscolor aColor) override;
|
||||
void PaintDecorationLine(Rect aPath, bool aPaintingShadows,
|
||||
nscolor aColor) override;
|
||||
void PaintSelectionDecorationLine(Rect aPath, bool aPaintingShadows,
|
||||
nscolor aColor) override;
|
||||
void NotifyBeforeText(bool aPaintingShadows, nscolor aColor) override;
|
||||
void NotifyGlyphPathEmitted() override;
|
||||
void NotifyAfterText() override;
|
||||
|
||||
@ -2453,6 +2455,12 @@ class SVGTextDrawPathCallbacks final : public nsTextFrame::DrawPathCallbacks {
|
||||
*/
|
||||
void StrokeGeometry();
|
||||
|
||||
/*
|
||||
* Takes a colour and modifies it to account for opacity properties.
|
||||
*/
|
||||
void ApplyOpacity(sRGBColor& aColor, const StyleSVGPaint& aPaint,
|
||||
const StyleSVGOpacity& aOpacity) const;
|
||||
|
||||
SVGTextFrame* const mSVGTextFrame;
|
||||
gfxContext& mContext;
|
||||
nsTextFrame* const mFrame;
|
||||
@ -2466,6 +2474,11 @@ class SVGTextDrawPathCallbacks final : public nsTextFrame::DrawPathCallbacks {
|
||||
* painting selections or IME decorations.
|
||||
*/
|
||||
nscolor mColor;
|
||||
|
||||
/**
|
||||
* Whether we're painting text shadows.
|
||||
*/
|
||||
bool mPaintingShadows;
|
||||
};
|
||||
|
||||
void SVGTextDrawPathCallbacks::NotifySelectionBackgroundNeedsFill(
|
||||
@ -2486,8 +2499,10 @@ void SVGTextDrawPathCallbacks::NotifySelectionBackgroundNeedsFill(
|
||||
}
|
||||
}
|
||||
|
||||
void SVGTextDrawPathCallbacks::NotifyBeforeText(nscolor aColor) {
|
||||
void SVGTextDrawPathCallbacks::NotifyBeforeText(bool aPaintingShadows,
|
||||
nscolor aColor) {
|
||||
mColor = aColor;
|
||||
mPaintingShadows = aPaintingShadows;
|
||||
SetupContext();
|
||||
mContext.NewPath();
|
||||
}
|
||||
@ -2499,8 +2514,11 @@ void SVGTextDrawPathCallbacks::NotifyGlyphPathEmitted() {
|
||||
|
||||
void SVGTextDrawPathCallbacks::NotifyAfterText() { mContext.Restore(); }
|
||||
|
||||
void SVGTextDrawPathCallbacks::PaintDecorationLine(Rect aPath, nscolor aColor) {
|
||||
void SVGTextDrawPathCallbacks::PaintDecorationLine(Rect aPath,
|
||||
bool aPaintingShadows,
|
||||
nscolor aColor) {
|
||||
mColor = aColor;
|
||||
mPaintingShadows = aPaintingShadows;
|
||||
AntialiasMode aaMode =
|
||||
SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering);
|
||||
|
||||
@ -2513,14 +2531,15 @@ void SVGTextDrawPathCallbacks::PaintDecorationLine(Rect aPath, nscolor aColor) {
|
||||
mContext.Restore();
|
||||
}
|
||||
|
||||
void SVGTextDrawPathCallbacks::PaintSelectionDecorationLine(Rect aPath,
|
||||
nscolor aColor) {
|
||||
void SVGTextDrawPathCallbacks::PaintSelectionDecorationLine(
|
||||
Rect aPath, bool aPaintingShadows, nscolor aColor) {
|
||||
if (IsClipPathChild()) {
|
||||
// Don't paint selection decorations when in a clip path.
|
||||
return;
|
||||
}
|
||||
|
||||
mColor = aColor;
|
||||
mPaintingShadows = aPaintingShadows;
|
||||
|
||||
mContext.Save();
|
||||
mContext.NewPath();
|
||||
@ -2560,6 +2579,17 @@ void SVGTextDrawPathCallbacks::HandleTextGeometry() {
|
||||
}
|
||||
}
|
||||
|
||||
void SVGTextDrawPathCallbacks::ApplyOpacity(
|
||||
sRGBColor& aColor, const StyleSVGPaint& aPaint,
|
||||
const StyleSVGOpacity& aOpacity) const {
|
||||
if (aPaint.kind.tag == StyleSVGPaintKind::Tag::Color) {
|
||||
aColor.a *=
|
||||
sRGBColor::FromABGR(aPaint.kind.AsColor().CalcColor(*mFrame->Style()))
|
||||
.a;
|
||||
}
|
||||
aColor.a *= SVGUtils::GetOpacity(aOpacity, /*aContextPaint*/ nullptr);
|
||||
}
|
||||
|
||||
void SVGTextDrawPathCallbacks::MakeFillPattern(GeneralPattern* aOutPattern) {
|
||||
if (mColor == NS_SAME_AS_FOREGROUND_COLOR ||
|
||||
mColor == NS_40PERCENT_FOREGROUND_COLOR) {
|
||||
@ -2571,7 +2601,12 @@ void SVGTextDrawPathCallbacks::MakeFillPattern(GeneralPattern* aOutPattern) {
|
||||
return;
|
||||
}
|
||||
|
||||
aOutPattern->InitColorPattern(ToDeviceColor(mColor));
|
||||
sRGBColor color(sRGBColor::FromABGR(mColor));
|
||||
if (mPaintingShadows) {
|
||||
ApplyOpacity(color, mFrame->StyleSVG()->mFill,
|
||||
mFrame->StyleSVG()->mFillOpacity);
|
||||
}
|
||||
aOutPattern->InitColorPattern(ToDeviceColor(color));
|
||||
}
|
||||
|
||||
void SVGTextDrawPathCallbacks::FillAndStrokeGeometry() {
|
||||
@ -2606,6 +2641,9 @@ void SVGTextDrawPathCallbacks::FillAndStrokeGeometry() {
|
||||
}
|
||||
|
||||
void SVGTextDrawPathCallbacks::FillGeometry() {
|
||||
if (mFrame->StyleSVG()->mFill.kind.IsNone()) {
|
||||
return;
|
||||
}
|
||||
GeneralPattern fillPattern;
|
||||
MakeFillPattern(&fillPattern);
|
||||
if (fillPattern.GetPattern()) {
|
||||
@ -2621,39 +2659,44 @@ void SVGTextDrawPathCallbacks::FillGeometry() {
|
||||
|
||||
void SVGTextDrawPathCallbacks::StrokeGeometry() {
|
||||
// We don't paint the stroke when we are filling with a selection color.
|
||||
if (mColor == NS_SAME_AS_FOREGROUND_COLOR ||
|
||||
mColor == NS_40PERCENT_FOREGROUND_COLOR) {
|
||||
if (SVGUtils::HasStroke(mFrame, /*aContextPaint*/ nullptr)) {
|
||||
GeneralPattern strokePattern;
|
||||
SVGUtils::MakeStrokePatternFor(mFrame, &mContext, &strokePattern,
|
||||
mImgParams, /*aContextPaint*/ nullptr);
|
||||
if (strokePattern.GetPattern()) {
|
||||
if (!mFrame->GetParent()->GetContent()->IsSVGElement()) {
|
||||
// The cast that follows would be unsafe
|
||||
MOZ_ASSERT(false, "Our nsTextFrame's parent's content should be SVG");
|
||||
return;
|
||||
}
|
||||
SVGElement* svgOwner =
|
||||
static_cast<SVGElement*>(mFrame->GetParent()->GetContent());
|
||||
if (!(mColor == NS_SAME_AS_FOREGROUND_COLOR ||
|
||||
mColor == NS_40PERCENT_FOREGROUND_COLOR || mPaintingShadows)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply any stroke-specific transform
|
||||
gfxMatrix outerSVGToUser;
|
||||
if (SVGUtils::GetNonScalingStrokeTransform(mFrame, &outerSVGToUser) &&
|
||||
outerSVGToUser.Invert()) {
|
||||
mContext.Multiply(outerSVGToUser);
|
||||
}
|
||||
if (!SVGUtils::HasStroke(mFrame, /*aContextPaint*/ nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Path> path = mContext.GetPath();
|
||||
SVGContentUtils::AutoStrokeOptions strokeOptions;
|
||||
SVGContentUtils::GetStrokeOptions(&strokeOptions, svgOwner,
|
||||
mFrame->Style(),
|
||||
/*aContextPaint*/ nullptr);
|
||||
DrawOptions drawOptions;
|
||||
drawOptions.mAntialiasMode =
|
||||
SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering);
|
||||
mContext.GetDrawTarget()->Stroke(path, strokePattern, strokeOptions);
|
||||
}
|
||||
GeneralPattern strokePattern;
|
||||
if (mPaintingShadows) {
|
||||
sRGBColor color(sRGBColor::FromABGR(mColor));
|
||||
ApplyOpacity(color, mFrame->StyleSVG()->mStroke,
|
||||
mFrame->StyleSVG()->mStrokeOpacity);
|
||||
strokePattern.InitColorPattern(ToDeviceColor(color));
|
||||
} else {
|
||||
SVGUtils::MakeStrokePatternFor(mFrame, &mContext, &strokePattern,
|
||||
mImgParams, /*aContextPaint*/ nullptr);
|
||||
}
|
||||
if (strokePattern.GetPattern()) {
|
||||
SVGElement* svgOwner =
|
||||
SVGElement::FromNode(mFrame->GetParent()->GetContent());
|
||||
|
||||
// Apply any stroke-specific transform
|
||||
gfxMatrix outerSVGToUser;
|
||||
if (SVGUtils::GetNonScalingStrokeTransform(mFrame, &outerSVGToUser) &&
|
||||
outerSVGToUser.Invert()) {
|
||||
mContext.Multiply(outerSVGToUser);
|
||||
}
|
||||
|
||||
RefPtr<Path> path = mContext.GetPath();
|
||||
SVGContentUtils::AutoStrokeOptions strokeOptions;
|
||||
SVGContentUtils::GetStrokeOptions(&strokeOptions, svgOwner, mFrame->Style(),
|
||||
/*aContextPaint*/ nullptr);
|
||||
DrawOptions drawOptions;
|
||||
drawOptions.mAntialiasMode =
|
||||
SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering);
|
||||
mContext.GetDrawTarget()->Stroke(path, strokePattern, strokeOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4910,11 +4953,20 @@ bool SVGTextFrame::ShouldRenderAsPath(nsTextFrame* aFrame,
|
||||
|
||||
const nsStyleSVG* style = aFrame->StyleSVG();
|
||||
|
||||
// Fill is a non-solid paint, has a non-default fill-rule or has
|
||||
// non-1 opacity.
|
||||
// Fill is a non-solid paint or is not opaque.
|
||||
if (!(style->mFill.kind.IsNone() ||
|
||||
(style->mFill.kind.IsColor() && style->mFillOpacity.IsOpacity() &&
|
||||
style->mFillOpacity.AsOpacity() == 1))) {
|
||||
(style->mFill.kind.IsColor() &&
|
||||
SVGUtils::GetOpacity(style->mFillOpacity, /*aContextPaint*/ nullptr) ==
|
||||
1.0f))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we're going to need to draw a non-opaque shadow.
|
||||
// It's possible nsTextFrame will support non-opaque shadows in the future,
|
||||
// in which case this test can be removed.
|
||||
if (style->mFill.kind.IsColor() && aFrame->StyleText()->HasTextShadow() &&
|
||||
NS_GET_A(style->mFill.kind.AsColor().CalcColor(*aFrame->Style())) !=
|
||||
0xFF) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
<!doctype html>
|
||||
<style>
|
||||
svg { font: bold 64px Arial, sans-serif; fill: none; stroke-width: 4px; }
|
||||
</style>
|
||||
<svg width="240" height="80">
|
||||
<text x="40" y="60" stroke="grey">Hello</text>
|
||||
<text x="30" y="50" stroke="black">Hello</text>
|
||||
</svg>
|
@ -0,0 +1,10 @@
|
||||
<!doctype html>
|
||||
<title>CSS Test: 'text-shadow' respects 'fill="none"'</title>
|
||||
<link rel="help" href="https://www.w3.org/TR/css-text-decor-3/#text-shadow-property">
|
||||
<link rel="match" href="svg-fill-none-ref.html">
|
||||
<style>
|
||||
svg { font: bold 64px Arial, sans-serif; text-shadow: grey 10px 10px }
|
||||
</style>
|
||||
<svg width="240" height="80">
|
||||
<text x="30" y="50" fill="none" stroke="black" stroke-width="4">Hello</text>
|
||||
</svg>
|
@ -0,0 +1,7 @@
|
||||
<!doctype html>
|
||||
<style>
|
||||
svg { font: bold 64px Arial, sans-serif; text-shadow: grey 10px 10px }
|
||||
</style>
|
||||
<svg width="240" height="80">
|
||||
<text x="30" y="50" fill-opacity="0.5" fill="#FFFF00">Hello</text>
|
||||
</svg>
|
@ -0,0 +1,10 @@
|
||||
<!doctype html>
|
||||
<title>CSS Test: 'text-shadow' respects non-opaque fill</title>
|
||||
<link rel="help" href="https://www.w3.org/TR/css-text-decor-3/#text-shadow-property">
|
||||
<link rel="match" href="svg-fill-opacity-ref.html">
|
||||
<style>
|
||||
svg { font: bold 64px Arial, sans-serif; text-shadow: grey 10px 10px }
|
||||
</style>
|
||||
<svg width="240" height="80">
|
||||
<text x="30" y="50" fill="rgba(255, 255, 0, 0.5)">Hello</text>
|
||||
</svg>
|
@ -0,0 +1,8 @@
|
||||
<!doctype html>
|
||||
<style>
|
||||
svg { font: bold 64px Arial, sans-serif; fill: none; stroke-width: 2px; stroke-dasharray:2, 2; }
|
||||
</style>
|
||||
<svg width="240" height="80">
|
||||
<text x="40" y="60" stroke="grey">Hello</text>
|
||||
<text x="30" y="50" stroke="black">Hello</text>
|
||||
</svg>
|
@ -0,0 +1,10 @@
|
||||
<!doctype html>
|
||||
<title>CSS Test: 'text-shadow' respects stroke-dasharray</title>
|
||||
<link rel="help" href="https://www.w3.org/TR/css-text-decor-3/#text-shadow-property">
|
||||
<link rel="match" href="svg-stroke-dasharray-ref.html">
|
||||
<style>
|
||||
svg { font: bold 64px Arial, sans-serif; text-shadow: grey 10px 10px }
|
||||
</style>
|
||||
<svg width="240" height="80">
|
||||
<text x="30" y="50" fill="none" stroke="black" stroke-width="2" stroke-dasharray="2, 2">Hello</text>
|
||||
</svg>
|
@ -0,0 +1,8 @@
|
||||
<!doctype html>
|
||||
<style>
|
||||
svg { font: bold 64px Arial, sans-serif; stroke: black; stroke-width: 4px; }
|
||||
</style>
|
||||
<svg width="240" height="80">
|
||||
<text x="40" y="60" fill="grey" stroke="grey">Hello</text>
|
||||
<text x="30" y="50">Hello</text>
|
||||
</svg>
|
@ -0,0 +1,10 @@
|
||||
<!doctype html>
|
||||
<title>CSS Test: 'text-shadow' respects stroke</title>
|
||||
<link rel="help" href="https://www.w3.org/TR/css-text-decor-3/#text-shadow-property">
|
||||
<link rel="match" href="svg-stroke-ref.html">
|
||||
<style>
|
||||
svg { font: bold 64px Arial, sans-serif; text-shadow: grey 10px 10px }
|
||||
</style>
|
||||
<svg width="240" height="80">
|
||||
<text x="30" y="50" stroke="black" stroke-width="4">Hello</text>
|
||||
</svg>
|
Loading…
Reference in New Issue
Block a user