Bug 1892089: Support font-relative line-height units use in canvas via SVG filter. r=jfkthame

Differential Revision: https://phabricator.services.mozilla.com/D207871
This commit is contained in:
David Shin
2024-05-07 20:35:08 +00:00
parent 46a093dc96
commit e6ce1aa347
7 changed files with 137 additions and 15 deletions

View File

@@ -972,6 +972,7 @@ CanvasRenderingContext2D::ContextState::ContextState(const ContextState& aOther)
textRendering(aOther.textRendering),
letterSpacing(aOther.letterSpacing),
wordSpacing(aOther.wordSpacing),
fontLineHeight(aOther.fontLineHeight),
letterSpacingStr(aOther.letterSpacingStr),
wordSpacingStr(aOther.wordSpacingStr),
shadowColor(aOther.shadowColor),
@@ -2884,10 +2885,16 @@ void CanvasRenderingContext2D::ParseSpacing(const nsACString& aSpacing,
class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize {
public:
CanvasUserSpaceMetrics(const gfx::IntSize& aSize, const nsFont& aFont,
const StyleLineHeight& aLineHeight,
RefPtr<nsAtom> aFontLanguage,
bool aFontExplicitLanguage,
const ComputedStyle* aCanvasStyle,
nsPresContext* aPresContext)
: mSize(aSize),
mFont(aFont),
mLineHeight(aLineHeight),
mFontLanguage(std::move(aFontLanguage)),
mFontExplicitLanguage(aFontExplicitLanguage),
mCanvasStyle(aCanvasStyle),
mPresContext(aPresContext) {}
@@ -2912,7 +2919,20 @@ class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize {
float GetLineHeight(Type aType) const override {
// This is used if a filter is added through `url()`, and if the SVG
// filter being referred to is using line-height units.
// TODO(dshin): Implement
switch (aType) {
case Type::This: {
const auto wm = GetWritingModeForType(aType);
const auto lh = ReflowInput::CalcLineHeightForCanvas(
mLineHeight, mFont, mFontLanguage, mFontExplicitLanguage,
mPresContext, wm);
return nsPresContext::AppUnitsToFloatCSSPixels(lh);
}
case Type::Root: {
return SVGContentUtils::GetLineHeight(
mPresContext->Document()->GetRootElement());
}
}
MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
return 1.0f;
}
@@ -2951,6 +2971,9 @@ class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize {
gfx::IntSize mSize;
const nsFont& mFont;
StyleLineHeight mLineHeight;
RefPtr<nsAtom> mFontLanguage;
bool mFontExplicitLanguage;
RefPtr<const ComputedStyle> mCanvasStyle;
nsPresContext* mPresContext;
};
@@ -3006,8 +3029,10 @@ void CanvasRenderingContext2D::UpdateFilter(bool aFlushIfNeeded) {
CurrentState().filter = FilterInstance::GetFilterDescription(
mCanvasElement, CurrentState().filterChain.AsSpan(),
CurrentState().autoSVGFiltersObserver, writeOnly,
CanvasUserSpaceMetrics(GetSize(), CurrentState().fontFont, canvasStyle,
presContext),
CanvasUserSpaceMetrics(
GetSize(), CurrentState().fontFont, CurrentState().fontLineHeight,
CurrentState().fontLanguage, CurrentState().fontExplicitLanguage,
canvasStyle, presContext),
gfxRect(0, 0, mWidth, mHeight), CurrentState().filterAdditionalImages);
CurrentState().filterSourceGraphicTainted = writeOnly;
}
@@ -4048,6 +4073,7 @@ bool CanvasRenderingContext2D::SetFontInternal(const nsACString& aFont,
CurrentState().fontFont.size = fontStyle->mSize;
CurrentState().fontLanguage = fontStyle->mLanguage;
CurrentState().fontExplicitLanguage = fontStyle->mExplicitLanguage;
CurrentState().fontLineHeight = data.mStyle->StyleFont()->mLineHeight;
return true;
}
@@ -4256,6 +4282,8 @@ bool CanvasRenderingContext2D::SetFontInternalDisconnected(
CurrentState().fontFont.variantCaps = fontStyle.variantCaps;
CurrentState().fontLanguage = nullptr;
CurrentState().fontExplicitLanguage = false;
// We don't have any computed style, assume normal height.
CurrentState().fontLineHeight = StyleLineHeight::Normal();
return true;
}

View File

@@ -1051,6 +1051,8 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
gfx::Float letterSpacing = 0.0f;
gfx::Float wordSpacing = 0.0f;
mozilla::StyleLineHeight fontLineHeight =
mozilla::StyleLineHeight::Normal();
nsCString letterSpacingStr;
nsCString wordSpacingStr;

View File

@@ -2744,14 +2744,15 @@ static nscoord GetNormalLineHeight(nsFontMetrics* aFontMetrics) {
}
static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh,
const nsStyleFont& aRelativeToFont,
const nsFont& aFont, nsAtom* aLanguage,
bool aExplicitLanguage,
nsPresContext* aPresContext,
bool aIsVertical, nscoord aBlockBSize,
float aFontSizeInflation) {
if (aLh.IsLength()) {
nscoord result = aLh.AsLength().ToAppUnits();
if (aFontSizeInflation != 1.0f) {
result = NSToCoordRound(result * aFontSizeInflation);
result = NSToCoordRound(static_cast<float>(result) * aFontSizeInflation);
}
return result;
}
@@ -2760,8 +2761,7 @@ static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh,
// For factor units the computed value of the line-height property
// is found by multiplying the factor by the font's computed size
// (adjusted for min-size prefs and text zoom).
return aRelativeToFont.mFont.size
.ScaledBy(aLh.AsNumber() * aFontSizeInflation)
return aFont.size.ScaledBy(aLh.AsNumber() * aFontSizeInflation)
.ToAppUnits();
}
@@ -2770,13 +2770,21 @@ static inline nscoord ComputeLineHeight(const StyleLineHeight& aLh,
return aBlockBSize;
}
auto size = aRelativeToFont.mFont.size;
auto size = aFont.size;
size.ScaleBy(aFontSizeInflation);
if (aPresContext) {
RefPtr<nsFontMetrics> fm = nsLayoutUtils::GetMetricsFor(
aPresContext, aIsVertical, &aRelativeToFont, size,
/* aUseUserFontSet = */ true);
nsFont font = aFont;
font.size = size;
nsFontMetrics::Params params;
params.language = aLanguage;
params.explicitLanguage = aExplicitLanguage;
params.orientation =
aIsVertical ? nsFontMetrics::eVertical : nsFontMetrics::eHorizontal;
params.userFontSet = aPresContext->GetUserFontSet();
params.textPerf = aPresContext->GetTextPerfMetrics();
params.featureValueLookup = aPresContext->GetFontFeatureValuesLookup();
RefPtr<nsFontMetrics> fm = aPresContext->GetMetricsFor(font, params);
return GetNormalLineHeight(fm);
}
// If we don't have a pres context, use a 1.2em fallback.
@@ -2828,8 +2836,9 @@ nscoord ReflowInput::CalcLineHeight(
nsPresContext* aPresContext, bool aIsVertical, const nsIContent* aContent,
nscoord aBlockBSize, float aFontSizeInflation) {
nscoord lineHeight =
ComputeLineHeight(aLh, aRelativeToFont, aPresContext, aIsVertical,
aBlockBSize, aFontSizeInflation);
ComputeLineHeight(aLh, aRelativeToFont.mFont, aRelativeToFont.mLanguage,
aRelativeToFont.mExplicitLanguage, aPresContext,
aIsVertical, aBlockBSize, aFontSizeInflation);
NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");
@@ -2839,8 +2848,9 @@ nscoord ReflowInput::CalcLineHeight(
// have a line-height smaller than 'normal'.
if (!aLh.IsNormal()) {
nscoord normal = ComputeLineHeight(
StyleLineHeight::Normal(), aRelativeToFont, aPresContext, aIsVertical,
aBlockBSize, aFontSizeInflation);
StyleLineHeight::Normal(), aRelativeToFont.mFont,
aRelativeToFont.mLanguage, aRelativeToFont.mExplicitLanguage,
aPresContext, aIsVertical, aBlockBSize, aFontSizeInflation);
if (lineHeight < normal) {
lineHeight = normal;
}
@@ -2850,6 +2860,17 @@ nscoord ReflowInput::CalcLineHeight(
return lineHeight;
}
nscoord ReflowInput::CalcLineHeightForCanvas(const StyleLineHeight& aLh,
const nsFont& aRelativeToFont,
nsAtom* aLanguage,
bool aExplicitLanguage,
nsPresContext* aPresContext,
mozilla::WritingMode aWM) {
return ComputeLineHeight(aLh, aRelativeToFont, aLanguage, aExplicitLanguage,
aPresContext, aWM.IsVertical() && !aWM.IsSideways(),
NS_UNCONSTRAINEDSIZE, 1.0f);
}
bool SizeComputationInput::ComputeMargin(WritingMode aCBWM,
nscoord aPercentBasis,
LayoutFrameType aFrameType) {

View File

@@ -741,6 +741,13 @@ struct ReflowInput : public SizeComputationInput {
const nsIContent* aContent, nscoord aBlockBSize,
float aFontSizeInflation);
static nscoord CalcLineHeightForCanvas(const StyleLineHeight& aLh,
const nsFont& aRelativeToFont,
nsAtom* aLanguage,
bool aExplicitLanguage,
nsPresContext* aPresContext,
mozilla::WritingMode aWM);
static constexpr float kNormalLineHeightFactor = 1.2f;
mozilla::LogicalSize ComputeContainingBlockRectangle(

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html class="test-wait">
<title>HTML Canvas reference: SVG filter using CSS font-relative line-height units</title>
<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
<style>
:root {
font: 20px Ahem;
}
div {
display: inline-block;
width: 100px;
height: 100px;
background: green;
}
.r {
background: red;
}
</style>
<div><div class="r" style="width: 48px;"></div></div><br>
<div><div class="r" style="width: 24px;"></div></div>

View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html class="test-wait">
<title>HTML Canvas test: SVG filter using CSS font-relative line-height units</title>
<link rel="match" href="svg-filter-lh-rlh-expected.html"/>
<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
<style>
:root {
font: 20px Ahem;
}
</style>
<svg width="0" height="0">
<!-- https://html.spec.whatwg.org/multipage/canvas.html#working-with-externally-defined-svg-filters -->
<use href="./svg-filter.svg#filter-lh" />
<use href="./svg-filter.svg#filter-rlh" />
</svg
><canvas id="canvas-lh" width="100" height="100"></canvas><br
><canvas id="canvas-rlh" width="100" height="100"></canvas>
<script>
function use_filter(canvas, filter_id) {
const ctx = canvas.getContext("2d");
ctx.font = "40px Ahem";
ctx.filter = `url(./svg-filter.svg#${filter_id})`;
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 100, 100);
}
window.addEventListener("load", async () => {
use_filter(document.getElementById("canvas-lh"), "filter-lh");
use_filter(document.getElementById("canvas-rlh"), "filter-rlh");
document.documentElement.classList.remove('test-wait');
});
</script>
</html>

View File

@@ -0,0 +1,10 @@
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<filter id="filter-lh">
<feFlood result="floodFill" x="1lh" y="0" width="100" height="100" flood-color="green" />
<feBlend in="SourceGraphic" in2="floodFill" mode="color-dodge" />
</filter>
<filter id="filter-rlh">
<feFlood result="floodFill" x="1rlh" y="0" width="100" height="100" flood-color="green" />
<feBlend in="SourceGraphic" in2="floodFill" mode="color-dodge" />
</filter>
</svg>

After

Width:  |  Height:  |  Size: 479 B