Skip to content

Commit

Permalink
Bug 1835622 - Perform length unit conversion in a single place r=emilio
Browse files Browse the repository at this point in the history
Unit conversion happens in

UserSpaceMetrics::ResolveAbsoluteUnit
SVGLength::GetUserUnitsPerUnit
SVGAnimatedLength::GetPixelsPerUnit

If we do all the conversion in one place it's all much simpler if we want to add more units.

We also want to standardise naming so that we don't use UserUnits for some methods and Pixels for others so let's standardise on Pixels.

Differential Revision: https://phabricator.services.mozilla.com/D179438
  • Loading branch information
longsonr committed Jun 5, 2023
1 parent 088bb7c commit b0a759a
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 180 deletions.
20 changes: 11 additions & 9 deletions dom/svg/DOMSVGLength.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,17 +150,16 @@ float DOMSVGLength::GetValue(ErrorResult& aRv) {
}

if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) {
float value = InternalItem().GetValueInUserUnits(lengthList->Element(),
lengthList->Axis());
float value = InternalItem().GetValueInPixels(lengthList->Element(),
lengthList->Axis());
if (!std::isfinite(value)) {
aRv.Throw(NS_ERROR_FAILURE);
}
return value;
}

float unitToPx;
if (UserSpaceMetrics::ResolveAbsoluteUnit(mUnit, unitToPx)) {
return mValue * unitToPx;
if (SVGLength::IsAbsoluteUnit(mUnit)) {
return SVGLength(mValue, mUnit).GetValueInPixels(nullptr, 0);
}

// else [SVGWG issue] Can't convert this length's value to user units
Expand Down Expand Up @@ -188,12 +187,12 @@ void DOMSVGLength::SetValue(float aUserUnitValue, ErrorResult& aRv) {

if (nsCOMPtr<DOMSVGLengthList> lengthList = do_QueryInterface(mOwner)) {
SVGLength& internalItem = InternalItem();
if (internalItem.GetValueInUserUnits(
lengthList->Element(), lengthList->Axis()) == aUserUnitValue) {
if (internalItem.GetValueInPixels(lengthList->Element(),
lengthList->Axis()) == aUserUnitValue) {
return;
}
float uuPerUnit = internalItem.GetUserUnitsPerUnit(lengthList->Element(),
lengthList->Axis());
float uuPerUnit = internalItem.GetPixelsPerUnit(
SVGElementMetrics(lengthList->Element()), lengthList->Axis());
if (uuPerUnit > 0) {
float newValue = aUserUnitValue / uuPerUnit;
if (std::isfinite(newValue)) {
Expand Down Expand Up @@ -355,6 +354,9 @@ void DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit, ErrorResult& aRv) {
val = length.GetValueInSpecifiedUnit(aUnit, lengthList->Element(),
lengthList->Axis());
} else {
if (mUnit == aUnit) {
return;
}
val = SVGLength(mValue, mUnit).GetValueInSpecifiedUnit(aUnit, nullptr, 0);
}
if (std::isfinite(val)) {
Expand Down
86 changes: 25 additions & 61 deletions dom/svg/SVGAnimatedLength.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,72 +178,32 @@ float UserSpaceMetricsWithSize::GetAxisLength(uint8_t aCtxType) const {

float SVGAnimatedLength::GetPixelsPerUnit(SVGElement* aSVGElement,
uint8_t aUnitType) const {
return GetPixelsPerUnit(SVGElementMetrics(aSVGElement), aUnitType);
return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aSVGElement), aUnitType,
mCtxType);
}

float SVGAnimatedLength::GetPixelsPerUnit(SVGViewportElement* aCtx,
uint8_t aUnitType) const {
return GetPixelsPerUnit(SVGElementMetrics(aCtx, aCtx), aUnitType);
return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aCtx, aCtx), aUnitType,
mCtxType);
}

float SVGAnimatedLength::GetPixelsPerUnit(nsIFrame* aFrame,
uint8_t aUnitType) const {
nsIContent* content = aFrame->GetContent();
MOZ_ASSERT(!content->IsText(), "Not expecting text content");
if (content->IsSVGElement()) {
return GetPixelsPerUnit(
SVGElementMetrics(static_cast<SVGElement*>(content)), aUnitType);
return SVGLength::GetPixelsPerUnit(
SVGElementMetrics(static_cast<SVGElement*>(content)), aUnitType,
mCtxType);
}
return GetPixelsPerUnit(NonSVGFrameUserSpaceMetrics(aFrame), aUnitType);
}

// See https://www.w3.org/TR/css-values-3/#absolute-lengths
static const float DPI = 96.0f;

bool UserSpaceMetrics::ResolveAbsoluteUnit(uint8_t aUnitType, float& aRes) {
switch (aUnitType) {
case SVGLength_Binding::SVG_LENGTHTYPE_NUMBER:
case SVGLength_Binding::SVG_LENGTHTYPE_PX:
aRes = 1;
break;
case SVGLength_Binding::SVG_LENGTHTYPE_MM:
aRes = DPI / MM_PER_INCH_FLOAT;
break;
case SVGLength_Binding::SVG_LENGTHTYPE_CM:
aRes = 10.0f * DPI / MM_PER_INCH_FLOAT;
break;
case SVGLength_Binding::SVG_LENGTHTYPE_IN:
aRes = DPI;
break;
case SVGLength_Binding::SVG_LENGTHTYPE_PT:
aRes = DPI / POINTS_PER_INCH_FLOAT;
break;
case SVGLength_Binding::SVG_LENGTHTYPE_PC:
aRes = 12.0f * DPI / POINTS_PER_INCH_FLOAT;
break;
default:
return false;
}
return true;
return SVGLength::GetPixelsPerUnit(NonSVGFrameUserSpaceMetrics(aFrame),
aUnitType, mCtxType);
}

float SVGAnimatedLength::GetPixelsPerUnit(const UserSpaceMetrics& aMetrics,
uint8_t aUnitType) const {
float res;
if (UserSpaceMetrics::ResolveAbsoluteUnit(aUnitType, res)) {
return res;
}
switch (aUnitType) {
case SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE:
return aMetrics.GetAxisLength(mCtxType) / 100.0f;
case SVGLength_Binding::SVG_LENGTHTYPE_EMS:
return aMetrics.GetEmLength();
case SVGLength_Binding::SVG_LENGTHTYPE_EXS:
return aMetrics.GetExLength();
default:
MOZ_ASSERT_UNREACHABLE("Unknown unit type");
return 0;
}
return SVGLength::GetPixelsPerUnit(aMetrics, aUnitType, mCtxType);
}

void SVGAnimatedLength::SetBaseValueInSpecifiedUnits(float aValue,
Expand All @@ -270,14 +230,21 @@ nsresult SVGAnimatedLength::ConvertToSpecifiedUnits(uint16_t unitType,

if (mIsBaseSet && mSpecifiedUnitType == uint8_t(unitType)) return NS_OK;

float pixelsPerUnit = GetPixelsPerUnit(aSVGElement, unitType);
if (pixelsPerUnit == 0.0f) {
return NS_ERROR_ILLEGAL_VALUE;
}
float valueInSpecifiedUnits;

float valueInUserUnits =
mBaseVal * GetPixelsPerUnit(aSVGElement, mSpecifiedUnitType);
float valueInSpecifiedUnits = valueInUserUnits / pixelsPerUnit;
if (SVGLength::IsAbsoluteUnit(unitType) &&
SVGLength::IsAbsoluteUnit(mSpecifiedUnitType)) {
valueInSpecifiedUnits = mBaseVal * SVGLength::GetAbsUnitsPerAbsUnit(
unitType, mSpecifiedUnitType);
} else {
float pixelsPerUnit = GetPixelsPerUnit(aSVGElement, unitType);
if (pixelsPerUnit == 0.0f) {
return NS_ERROR_ILLEGAL_VALUE;
}
float valueInPixels =
mBaseVal * GetPixelsPerUnit(aSVGElement, mSpecifiedUnitType);
valueInSpecifiedUnits = valueInPixels / pixelsPerUnit;
}

if (!std::isfinite(valueInSpecifiedUnits)) {
return NS_ERROR_ILLEGAL_VALUE;
Expand Down Expand Up @@ -440,10 +407,7 @@ nsresult SVGAnimatedLength::SMILLength::ValueFromString(
SMILValue val(SMILFloatType::Singleton());
val.mU.mDouble = value * mVal->GetPixelsPerUnit(mSVGElement, unitType);
aValue = val;
aPreventCachingOfSandwich =
(unitType == SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE ||
unitType == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
unitType == SVGLength_Binding::SVG_LENGTHTYPE_EXS);
aPreventCachingOfSandwich = !SVGLength::IsAbsoluteUnit(unitType);

return NS_OK;
}
Expand Down
1 change: 0 additions & 1 deletion dom/svg/SVGAnimatedLength.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class SVGViewportElement;

class UserSpaceMetrics {
public:
static bool ResolveAbsoluteUnit(uint8_t aUnitType, float& aRes);
virtual ~UserSpaceMetrics() = default;

virtual float GetEmLength() const = 0;
Expand Down
4 changes: 1 addition & 3 deletions dom/svg/SVGAnimatedLengthList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,7 @@ nsresult SVGAnimatedLengthList::SMILAnimatedLengthList::ValueFromString(

for (uint32_t i = 0; i < llai->Length(); ++i) {
uint8_t unit = (*llai)[i].GetUnit();
if (unit == SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE ||
unit == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
unit == SVGLength_Binding::SVG_LENGTHTYPE_EXS) {
if (!SVGLength::IsAbsoluteUnit(unit)) {
aPreventCachingOfSandwich = true;
break;
}
Expand Down
6 changes: 2 additions & 4 deletions dom/svg/SVGElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1542,10 +1542,8 @@ void SVGElement::GetAnimatedLengthValues(float* aFirst, ...) {

while (f && i < info.mCount) {
uint8_t type = info.mValues[i].GetSpecifiedUnitType();
if (!ctx) {
if (type != SVGLength_Binding::SVG_LENGTHTYPE_NUMBER &&
type != SVGLength_Binding::SVG_LENGTHTYPE_PX)
ctx = GetCtx();
if (!ctx && !SVGLength::IsAbsoluteUnit(type)) {
ctx = GetCtx();
}
if (type == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
type == SVGLength_Binding::SVG_LENGTHTYPE_EXS)
Expand Down
114 changes: 49 additions & 65 deletions dom/svg/SVGLength.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ bool SVGLength::SetValueFromString(const nsAString& aString) {
return true;
}

inline static bool IsAbsoluteUnit(uint8_t aUnit) {
return aUnit >= SVGLength_Binding::SVG_LENGTHTYPE_CM &&
aUnit <= SVGLength_Binding::SVG_LENGTHTYPE_PC;
/*static*/
bool SVGLength::IsAbsoluteUnit(uint8_t aUnit) {
return aUnit == SVG_LENGTHTYPE_NUMBER ||
(aUnit >= SVG_LENGTHTYPE_PX && aUnit <= SVG_LENGTHTYPE_PC);
}

/**
Expand All @@ -67,30 +68,37 @@ inline static bool IsAbsoluteUnit(uint8_t aUnit) {
*
* Example usage: to find out how many centimeters there are per inch:
*
* GetAbsUnitsPerAbsUnit(SVGLength_Binding::SVG_LENGTHTYPE_CM,
* SVGLength_Binding::SVG_LENGTHTYPE_IN)
* GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_CM, SVG_LENGTHTYPE_IN)
*/
inline static float GetAbsUnitsPerAbsUnit(uint8_t aUnits, uint8_t aPerUnit) {
MOZ_ASSERT(IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
MOZ_ASSERT(IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");

float CSSAbsoluteUnitConversionFactors[5][5] = {
// columns: cm, mm, in, pt, pc
/*static*/
float SVGLength::GetAbsUnitsPerAbsUnit(uint8_t aUnits, uint8_t aPerUnit) {
MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");

static const float CSSAbsoluteUnitConversionFactors[6][6] = {
// columns: px, cm, mm, in, pt, pc
// px per...:
{1.0f, 37.7952755906f, 3.779528f, 96.0f, 1.33333333333333333f, 16.0f},
// cm per...:
{1.0f, 0.1f, 2.54f, 0.035277777777777778f, 0.42333333333333333f},
{0.02645833333f, 1.0f, 0.1f, 2.54f, 0.035277777777777778f,
0.42333333333333333f},
// mm per...:
{10.0f, 1.0f, 25.4f, 0.35277777777777778f, 4.2333333333333333f},
{0.26458333333f, 10.0f, 1.0f, 25.4f, 0.35277777777777778f,
4.2333333333333333f},
// in per...:
{0.39370078740157481f, 0.039370078740157481f, 1.0f, 0.013888888888888889f,
0.16666666666666667f},
{0.01041666666f, 0.39370078740157481f, 0.039370078740157481f, 1.0f,
0.013888888888888889f, 0.16666666666666667f},
// pt per...:
{28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f},
{0.75f, 28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f},
// pc per...:
{2.3622047244094489f, 0.23622047244094489f, 6.0f, 0.083333333333333333f,
1.0f}};
{0.0625f, 2.3622047244094489f, 0.23622047244094489f, 6.0f,
0.083333333333333333f, 1.0f}};

auto ToIndex = [](uint8_t aUnit) {
return aUnit == SVG_LENGTHTYPE_NUMBER ? 0 : aUnit - 5;
};

// First absolute unit is SVG_LENGTHTYPE_CM = 6
return CSSAbsoluteUnitConversionFactors[aUnits - 6][aPerUnit - 6];
return CSSAbsoluteUnitConversionFactors[ToIndex(aUnits)][ToIndex(aPerUnit)];
}

float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
Expand All @@ -99,10 +107,8 @@ float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
if (aUnit == mUnit) {
return mValue;
}
if ((aUnit == SVGLength_Binding::SVG_LENGTHTYPE_NUMBER &&
mUnit == SVGLength_Binding::SVG_LENGTHTYPE_PX) ||
(aUnit == SVGLength_Binding::SVG_LENGTHTYPE_PX &&
mUnit == SVGLength_Binding::SVG_LENGTHTYPE_NUMBER)) {
if ((aUnit == SVG_LENGTHTYPE_NUMBER && mUnit == SVG_LENGTHTYPE_PX) ||
(aUnit == SVG_LENGTHTYPE_PX && mUnit == SVG_LENGTHTYPE_NUMBER)) {
return mValue;
}
if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
Expand All @@ -113,9 +119,11 @@ float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
// succeed if aElement is non-null (although that's not sufficient to
// guarantee success).

float userUnitsPerCurrentUnit = GetUserUnitsPerUnit(aElement, aAxis);
auto userSpaceMetrics = SVGElementMetrics(const_cast<SVGElement*>(aElement));

float userUnitsPerCurrentUnit = GetPixelsPerUnit(userSpaceMetrics, aAxis);
float userUnitsPerNewUnit =
SVGLength(0.0f, aUnit).GetUserUnitsPerUnit(aElement, aAxis);
SVGLength(0.0f, aUnit).GetPixelsPerUnit(userSpaceMetrics, aAxis);

NS_ASSERTION(
userUnitsPerCurrentUnit >= 0 || !std::isfinite(userUnitsPerCurrentUnit),
Expand All @@ -133,51 +141,27 @@ float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
return std::numeric_limits<float>::quiet_NaN();
}

#define INCHES_PER_MM_FLOAT float(0.0393700787)
#define INCHES_PER_CM_FLOAT float(0.393700787)
// Helpers:

float SVGLength::GetUserUnitsPerUnit(const SVGElement* aElement,
uint8_t aAxis) const {
switch (mUnit) {
case SVGLength_Binding::SVG_LENGTHTYPE_NUMBER:
case SVGLength_Binding::SVG_LENGTHTYPE_PX:
/*static*/
float SVGLength::GetPixelsPerUnit(const UserSpaceMetrics& aMetrics,
uint8_t aUnitType, uint8_t aAxis) {
switch (aUnitType) {
case SVG_LENGTHTYPE_NUMBER:
case SVG_LENGTHTYPE_PX:
return 1.0f;
case SVGLength_Binding::SVG_LENGTHTYPE_MM:
return INCHES_PER_MM_FLOAT * GetUserUnitsPerInch();
case SVGLength_Binding::SVG_LENGTHTYPE_CM:
return INCHES_PER_CM_FLOAT * GetUserUnitsPerInch();
case SVGLength_Binding::SVG_LENGTHTYPE_IN:
return GetUserUnitsPerInch();
case SVGLength_Binding::SVG_LENGTHTYPE_PT:
return (1.0f / POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch();
case SVGLength_Binding::SVG_LENGTHTYPE_PC:
return (12.0f / POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch();
case SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE:
return GetUserUnitsPerPercent(aElement, aAxis);
case SVGLength_Binding::SVG_LENGTHTYPE_EMS:
return SVGContentUtils::GetFontSize(const_cast<SVGElement*>(aElement));
case SVGLength_Binding::SVG_LENGTHTYPE_EXS:
return SVGContentUtils::GetFontXHeight(const_cast<SVGElement*>(aElement));
case SVG_LENGTHTYPE_PERCENTAGE:
return aMetrics.GetAxisLength(aAxis) / 100.0f;
case SVG_LENGTHTYPE_EMS:
return aMetrics.GetEmLength();
case SVG_LENGTHTYPE_EXS:
return aMetrics.GetExLength();
default:
MOZ_ASSERT_UNREACHABLE("Unknown unit type");
return std::numeric_limits<float>::quiet_NaN();
}
}

/* static */
float SVGLength::GetUserUnitsPerPercent(const SVGElement* aElement,
uint8_t aAxis) {
if (aElement) {
dom::SVGViewportElement* viewportElement = aElement->GetCtx();
if (viewportElement) {
return std::max(viewportElement->GetLength(aAxis) / 100.0f, 0.0f);
}
MOZ_ASSERT(IsAbsoluteUnit(aUnitType));
return GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_PX, aUnitType);
}
return std::numeric_limits<float>::quiet_NaN();
}

// Helpers:

/* static */
void SVGLength::GetUnitString(nsAString& aUnit, uint16_t aUnitType) {
switch (aUnitType) {
Expand Down
Loading

0 comments on commit b0a759a

Please sign in to comment.