Bug 631436 part 1 - use the nearest orient or length value for the result unit when interpolating r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D191514
This commit is contained in:
Robert Longson 2023-11-21 03:37:34 +00:00
parent b3d1d40819
commit 5ec06e8577
8 changed files with 216 additions and 45 deletions

View file

@ -174,8 +174,7 @@ class SVGLengthList {
*/
class SVGLengthListAndInfo : public SVGLengthList {
public:
SVGLengthListAndInfo()
: mElement(nullptr), mAxis(0), mCanZeroPadList(false) {}
SVGLengthListAndInfo() : mElement(nullptr), mAxis(0), mCanZeroPadList(true) {}
SVGLengthListAndInfo(dom::SVGElement* aElement, uint8_t aAxis,
bool aCanZeroPadList)

View file

@ -24,12 +24,7 @@ SVGLengthListSMILType SVGLengthListSMILType::sSingleton;
void SVGLengthListSMILType::Init(SMILValue& aValue) const {
MOZ_ASSERT(aValue.IsNull(), "Unexpected value type");
SVGLengthListAndInfo* lengthList = new SVGLengthListAndInfo();
// See the comment documenting Init() in our header file:
lengthList->SetCanZeroPadList(true);
aValue.mU.mPtr = lengthList;
aValue.mU.mPtr = new SVGLengthListAndInfo();
aValue.mType = this;
}
@ -251,19 +246,28 @@ nsresult SVGLengthListSMILType::Interpolate(const SMILValue& aStartVal,
return NS_ERROR_OUT_OF_MEMORY;
}
// If units differ, we use the unit of the nearest item.
// We leave it to the frame code to check that values are finite.
bool useEndUnits = (aUnitDistance > 0.5);
uint32_t i = 0;
for (; i < start.Length() && i < end.Length(); ++i) {
float s;
float s, e;
uint8_t unit;
if (start[i].GetUnit() == end[i].GetUnit()) {
unit = start[i].GetUnit();
s = start[i].GetValueInCurrentUnits();
e = end[i].GetValueInCurrentUnits();
} else if (useEndUnits) {
unit = end[i].GetUnit();
s = start[i].GetValueInSpecifiedUnit(unit, end.Element(), end.Axis());
e = end[i].GetValueInCurrentUnits();
} else {
// If units differ, we use the unit of the item in 'end'.
// We leave it to the frame code to check that values are finite.
s = start[i].GetValueInSpecifiedUnit(end[i].GetUnit(), end.Element(),
end.Axis());
unit = start[i].GetUnit();
s = start[i].GetValueInCurrentUnits();
e = end[i].GetValueInSpecifiedUnit(unit, start.Element(), start.Axis());
}
float e = end[i].GetValueInCurrentUnits();
result[i].SetValueAndUnit(s + (e - s) * aUnitDistance, end[i].GetUnit());
result[i].SetValueAndUnit(s + (e - s) * aUnitDistance, unit);
}
// In the case that start.Length() != end.Length(), one of the following

View file

@ -42,9 +42,7 @@ SVGNumberListSMILType SVGNumberListSMILType::sSingleton;
void SVGNumberListSMILType::Init(SMILValue& aValue) const {
MOZ_ASSERT(aValue.IsNull(), "Unexpected value type");
SVGNumberListAndInfo* numberList = new SVGNumberListAndInfo();
aValue.mU.mPtr = numberList;
aValue.mU.mPtr = new SVGNumberListAndInfo();
aValue.mType = this;
}

View file

@ -56,6 +56,15 @@ bool SVGOrientSMILType::IsEqual(const SMILValue& aLeft,
aLeft.mU.mOrient.mOrientType == aRight.mU.mOrient.mOrientType;
}
static float ValueInDegrees(const SMILValue& aValue) {
MOZ_ASSERT(aValue.mU.mOrient.mOrientType == SVG_MARKER_ORIENT_ANGLE);
return aValue.mU.mOrient.mAngle == 0.0f
? 0.0f
: aValue.mU.mOrient.mAngle * SVGAnimatedOrient::GetDegreesPerUnit(
aValue.mU.mOrient.mUnit);
}
nsresult SVGOrientSMILType::Add(SMILValue& aDest, const SMILValue& aValueToAdd,
uint32_t aCount) const {
MOZ_ASSERT(aValueToAdd.mType == aDest.mType, "Trying to add invalid types");
@ -69,13 +78,8 @@ nsresult SVGOrientSMILType::Add(SMILValue& aDest, const SMILValue& aValueToAdd,
// We may be dealing with two different angle units, so we normalize to
// degrees for the add:
float currentAngle =
aDest.mU.mOrient.mAngle *
SVGAnimatedOrient::GetDegreesPerUnit(aDest.mU.mOrient.mUnit);
float angleToAdd =
aValueToAdd.mU.mOrient.mAngle *
SVGAnimatedOrient::GetDegreesPerUnit(aValueToAdd.mU.mOrient.mUnit) *
aCount;
float currentAngle = ValueInDegrees(aDest);
float angleToAdd = ValueInDegrees(aValueToAdd) * aCount;
// And then we give the resulting animated value the same units as the value
// that we're animating to/by (i.e. the same as aValueToAdd):
@ -100,12 +104,7 @@ nsresult SVGOrientSMILType::ComputeDistance(const SMILValue& aFrom,
}
// Normalize both to degrees in case they're different angle units:
double from = aFrom.mU.mOrient.mAngle *
SVGAnimatedOrient::GetDegreesPerUnit(aFrom.mU.mOrient.mUnit);
double to = aTo.mU.mOrient.mAngle *
SVGAnimatedOrient::GetDegreesPerUnit(aTo.mU.mOrient.mUnit);
aDistance = fabs(to - from);
aDistance = fabs(ValueInDegrees(aTo) - ValueInDegrees(aFrom));
return NS_OK;
}
@ -125,17 +124,20 @@ nsresult SVGOrientSMILType::Interpolate(const SMILValue& aStartVal,
return NS_ERROR_FAILURE;
}
float start =
aStartVal.mU.mOrient.mAngle *
SVGAnimatedOrient::GetDegreesPerUnit(aStartVal.mU.mOrient.mUnit);
float end = aEndVal.mU.mOrient.mAngle *
SVGAnimatedOrient::GetDegreesPerUnit(aEndVal.mU.mOrient.mUnit);
float start = ValueInDegrees(aStartVal);
float end = ValueInDegrees(aEndVal);
float result = (start + (end - start) * aUnitDistance);
// Again, we use the unit of the to/by value for the result:
aResult.mU.mOrient.mAngle =
result / SVGAnimatedOrient::GetDegreesPerUnit(aEndVal.mU.mOrient.mUnit);
aResult.mU.mOrient.mUnit = aEndVal.mU.mOrient.mUnit;
// we use the unit of the nearest value for the result:
if (aUnitDistance > 0.5) {
aResult.mU.mOrient.mAngle =
result / SVGAnimatedOrient::GetDegreesPerUnit(aEndVal.mU.mOrient.mUnit);
aResult.mU.mOrient.mUnit = aEndVal.mU.mOrient.mUnit;
} else {
aResult.mU.mOrient.mAngle = result / SVGAnimatedOrient::GetDegreesPerUnit(
aStartVal.mU.mOrient.mUnit);
aResult.mU.mOrient.mUnit = aStartVal.mU.mOrient.mUnit;
}
return NS_OK;
}

View file

@ -23,9 +23,7 @@ SVGPointListSMILType SVGPointListSMILType::sSingleton;
void SVGPointListSMILType::Init(SMILValue& aValue) const {
MOZ_ASSERT(aValue.IsNull(), "Unexpected value type");
SVGPointListAndInfo* pointList = new SVGPointListAndInfo();
aValue.mU.mPtr = pointList;
aValue.mU.mPtr = new SVGPointListAndInfo();
aValue.mType = this;
}

View file

@ -24,8 +24,7 @@ using TransformArray = FallibleTArray<SVGTransformSMILData>;
void SVGTransformListSMILType::Init(SMILValue& aValue) const {
MOZ_ASSERT(aValue.IsNull(), "Unexpected value type");
TransformArray* transforms = new TransformArray(1);
aValue.mU.mPtr = transforms;
aValue.mU.mPtr = new TransformArray(1);
aValue.mType = this;
}

View file

@ -0,0 +1,83 @@
<!doctype html>
<html>
<meta charset="utf-8">
<title>Test change of unit type for SVGAngle animation.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
<svg>
</svg>
<script>
var rootSVGElement = document.querySelector("svg");
// Setup test document
var defs = createSVGElement("defs");
var marker = createSVGElement("marker");
marker.setAttribute("id", "marker");
marker.setAttribute("viewBox", "0 0 10 10");
marker.setAttribute("markerWidth", "4");
marker.setAttribute("markerHeight", "3");
marker.setAttribute("markerUnits", "strokeWidth");
marker.setAttribute("refX", "1");
marker.setAttribute("refY", "5");
marker.setAttribute("orient", "0deg");
defs.appendChild(marker);
var polyline = createSVGElement("polyline");
polyline.setAttribute("id", "polyline");
polyline.setAttribute("points", "0,0 10,5 0,10 1,5");
polyline.setAttribute("fill", "green");
marker.appendChild(polyline);
var path = createSVGElement("path");
path.setAttribute("id", "path");
path.setAttribute("d", "M45,50 L55,50");
path.setAttribute("stroke-width","10");
path.setAttribute("stroke", "green");
path.setAttribute("marker-end", "url(#marker)");
path.setAttribute("onclick", "executeTest()");
var animate = createSVGElement("animate");
animate.setAttribute("id", "animation");
animate.setAttribute("attributeName", "orient");
animate.setAttribute("begin", "0s");
animate.setAttribute("dur", "4s");
animate.setAttribute("from", "0deg");
animate.setAttribute("to", "200grad");
marker.appendChild(animate);
rootSVGElement.appendChild(defs);
rootSVGElement.appendChild(path);
// Setup animation test
function sample1() {
// Check initial/end conditions
assert_equals(marker.orientAngle.baseVal.unitType, SVGAngle.SVG_ANGLETYPE_DEG);
assert_equals(marker.orientAngle.animVal.unitType, SVGAngle.SVG_ANGLETYPE_DEG);
}
function sample2() {
assert_equals(marker.orientAngle.baseVal.unitType, SVGAngle.SVG_ANGLETYPE_DEG);
assert_equals(marker.orientAngle.animVal.unitType, SVGAngle.SVG_ANGLETYPE_DEG);
}
function sample3() {
assert_equals(marker.orientAngle.baseVal.unitType, SVGAngle.SVG_ANGLETYPE_DEG);
assert_equals(marker.orientAngle.animVal.unitType, SVGAngle.SVG_ANGLETYPE_GRAD);
}
smil_async_test((t) => {
const expectedValues = [
// [animationId, time, sampleCallback]
["animation", 0.0, sample1],
["animation", 2.0, sample2],
["animation", 3.999, sample3],
["animation", 4.001, sample1]
];
runAnimationTest(t, expectedValues);
});
</script>

View file

@ -0,0 +1,88 @@
<!doctype html>
<html>
<meta charset="utf-8">
<title>Test change of unit type for SVGLengthList animation.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
<svg>
</svg>
<script>
var rootSVGElement = document.querySelector("svg");
// Setup test document
var text = createSVGElement("text");
text.setAttribute("id", "text");
text.textContent = "ABCD";
text.setAttribute("dx", "50 70 90 110");
text.setAttribute("y", "50");
text.setAttribute("onclick", "executeTest()");
rootSVGElement.appendChild(text);
var animate = createSVGElement("animate");
animate.setAttribute("id", "animation");
animate.setAttribute("attributeName", "dx");
animate.setAttribute("begin", "0s");
animate.setAttribute("dur", "4s");
animate.setAttribute("from", "50px 70px 90px 100px");
animate.setAttribute("to", "60% 90% 120% 150%");
text.appendChild(animate);
// Setup animation test
function sample1() {
assert_equals(text.dx.animVal.numberOfItems, 4);
assert_equals(text.dx.animVal[0].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.animVal[1].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.animVal[2].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.animVal[3].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.baseVal.numberOfItems, 4);
assert_equals(text.dx.baseVal[0].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.baseVal[1].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.baseVal[2].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.baseVal[3].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
}
function sample2() {
assert_equals(text.dx.animVal.numberOfItems, 4);
assert_equals(text.dx.animVal[0].unitType, SVGLength.SVG_LENGTHTYPE_PX);
assert_equals(text.dx.animVal[1].unitType, SVGLength.SVG_LENGTHTYPE_PX);
assert_equals(text.dx.animVal[2].unitType, SVGLength.SVG_LENGTHTYPE_PX);
assert_equals(text.dx.animVal[3].unitType, SVGLength.SVG_LENGTHTYPE_PX);
assert_equals(text.dx.baseVal.numberOfItems, 4);
assert_equals(text.dx.baseVal[0].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.baseVal[1].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.baseVal[2].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.baseVal[3].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
}
function sample3() {
assert_equals(text.dx.animVal.numberOfItems, 4);
assert_equals(text.dx.animVal[0].unitType, SVGLength.SVG_LENGTHTYPE_PERCENTAGE);
assert_equals(text.dx.animVal[1].unitType, SVGLength.SVG_LENGTHTYPE_PERCENTAGE);
assert_equals(text.dx.animVal[2].unitType, SVGLength.SVG_LENGTHTYPE_PERCENTAGE);
assert_equals(text.dx.animVal[3].unitType, SVGLength.SVG_LENGTHTYPE_PERCENTAGE);
assert_equals(text.dx.baseVal.numberOfItems, 4);
assert_equals(text.dx.baseVal[0].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.baseVal[1].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.baseVal[2].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
assert_equals(text.dx.baseVal[3].unitType, SVGLength.SVG_LENGTHTYPE_NUMBER);
}
smil_async_test((t) => {
const expectedValues = [
// [animationId, time, sampleCallback]
["animation", 0.0, sample1],
["animation", 2.0, sample2],
["animation", 3.999, sample3],
["animation", 4.001, sample1]
];
runAnimationTest(t, expectedValues);
});
</script>