Bug 594198: In SMIL animation of length-valued attributes/properties, allow interpolation between 0 values and unitless values. r=birtles a=blocking-final+

--HG--
rename : layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-px-px.svg => layout/reftests/svg/smil/style/anim-css-strokewidth-1-by-no-no.svg
rename : layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-by-px-px.svg => layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-by-no-no.svg
rename : layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-to-px-px.svg => layout/reftests/svg/smil/style/anim-css-strokewidth-1-from-to-no-no.svg
rename : layout/reftests/svg/smil/style/anim-css-strokewidth-1-to-px-px.svg => layout/reftests/svg/smil/style/anim-css-strokewidth-1-to-no-no.svg
rename : layout/reftests/svg/smil/style/anim-css-strokewidth-4-from-by-px-px.svg => layout/reftests/svg/smil/style/anim-css-strokewidth-4-from-by-no-no.svg
This commit is contained in:
Daniel Holbert 2010-11-06 12:13:01 -07:00
parent 2c965a8daa
commit 3426024d9c
10 changed files with 279 additions and 58 deletions

View File

@ -92,18 +92,61 @@ GetZeroValueForUnit(nsStyleAnimation::Unit aUnit)
}
}
static void
InvertSign(nsStyleAnimation::Value& aStyleCoord)
// This method requires at least one of its arguments to be non-null.
//
// If one argument is null, this method updates it to point to "zero"
// for the other argument's Unit (if applicable; otherwise, we return PR_FALSE).
//
// If neither argument is null, this method generally does nothing, though it
// may apply a workaround for the special case where a 0 length-value is mixed
// with a eUnit_Float value. (See comment below.)
//
// Returns PR_TRUE on success, or PR_FALSE.
static const PRBool
FinalizeStyleAnimationValues(const nsStyleAnimation::Value*& aValue1,
const nsStyleAnimation::Value*& aValue2)
{
switch (aStyleCoord.GetUnit()) {
NS_ABORT_IF_FALSE(aValue1 || aValue2,
"expecting at least one non-null value");
// Are we missing either val? (If so, it's an implied 0 in other val's units)
if (!aValue1) {
aValue1 = GetZeroValueForUnit(aValue2->GetUnit());
return !!aValue1; // Fail if we have no zero value for this unit.
}
if (!aValue2) {
aValue2 = GetZeroValueForUnit(aValue1->GetUnit());
return !!aValue2; // Fail if we have no zero value for this unit.
}
// Ok, both values were specified.
// Need to handle a special-case, though: unitless nonzero length (parsed as
// eUnit_Float) mixed with unitless 0 length (parsed as eUnit_Coord). These
// won't interoperate in nsStyleAnimation, since their Units don't match.
// In this case, we replace the eUnit_Coord 0 value with eUnit_Float 0 value.
if (*aValue1 == sZeroCoord &&
aValue2->GetUnit() == nsStyleAnimation::eUnit_Float) {
aValue1 = &sZeroFloat;
} else if (*aValue2 == sZeroCoord &&
aValue1->GetUnit() == nsStyleAnimation::eUnit_Float) {
aValue2 = &sZeroFloat;
}
return PR_TRUE;
}
static void
InvertSign(nsStyleAnimation::Value& aValue)
{
switch (aValue.GetUnit()) {
case nsStyleAnimation::eUnit_Coord:
aStyleCoord.SetCoordValue(-aStyleCoord.GetCoordValue());
aValue.SetCoordValue(-aValue.GetCoordValue());
break;
case nsStyleAnimation::eUnit_Percent:
aStyleCoord.SetPercentValue(-aStyleCoord.GetPercentValue());
aValue.SetPercentValue(-aValue.GetPercentValue());
break;
case nsStyleAnimation::eUnit_Float:
aStyleCoord.SetFloatValue(-aStyleCoord.GetFloatValue());
aValue.SetFloatValue(-aValue.GetFloatValue());
break;
default:
NS_NOTREACHED("Calling InvertSign with an unsupported unit");
@ -154,9 +197,6 @@ nsSMILCSSValueType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
if (!destWrapper) {
// barely-initialized dest -- need to alloc & copy
aDest.mU.mPtr = new ValueWrapper(*srcWrapper);
if (!aDest.mU.mPtr) {
return NS_ERROR_OUT_OF_MEMORY;
}
} else {
// both already fully-initialized -- just copy straight across
*destWrapper = *srcWrapper;
@ -221,33 +261,27 @@ nsSMILCSSValueType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
return NS_ERROR_FAILURE;
}
// Handle barely-initialized "zero" added value.
const nsStyleAnimation::Value* realValueToAdd = valueToAddWrapper ?
&valueToAddWrapper->mCSSValue :
GetZeroValueForUnit(destWrapper->mCSSValue.GetUnit());
if (!realValueToAdd) {
// No zero value for this unit --> doesn't support addition.
const nsStyleAnimation::Value* valueToAdd = valueToAddWrapper ?
&valueToAddWrapper->mCSSValue : nsnull;
const nsStyleAnimation::Value* destValue = destWrapper ?
&destWrapper->mCSSValue : nsnull;
if (!FinalizeStyleAnimationValues(valueToAdd, destValue)) {
return NS_ERROR_FAILURE;
}
// Did FinalizeStyleAnimationValues change destValue?
// If so, update outparam to use the new value.
if (destWrapper && &destWrapper->mCSSValue != destValue) {
destWrapper->mCSSValue = *destValue;
}
// Handle barely-initialized "zero" destination.
if (!destWrapper) {
// Need to fully initialize destination, since it's an outparam
const nsStyleAnimation::Value* zeroVal =
GetZeroValueForUnit(valueToAddWrapper->mCSSValue.GetUnit());
if (!zeroVal) {
// No zero value for this unit --> doesn't support addition.
return NS_ERROR_FAILURE;
}
aDest.mU.mPtr = destWrapper =
new ValueWrapper(property, *zeroVal, valueToAddWrapper->mPresContext);
if (!destWrapper) {
return NS_ERROR_OUT_OF_MEMORY;
}
new ValueWrapper(property, *destValue, valueToAddWrapper->mPresContext);
}
return nsStyleAnimation::Add(property, destWrapper->mCSSValue,
*realValueToAdd, aCount) ?
return nsStyleAnimation::Add(property,
destWrapper->mCSSValue, *valueToAdd, aCount) ?
NS_OK : NS_ERROR_FAILURE;
}
@ -265,15 +299,15 @@ nsSMILCSSValueType::ComputeDistance(const nsSMILValue& aFrom,
NS_ABORT_IF_FALSE(toWrapper, "expecting non-null endpoint");
const nsStyleAnimation::Value* fromCSSValue = fromWrapper ?
&fromWrapper->mCSSValue :
GetZeroValueForUnit(toWrapper->mCSSValue.GetUnit());
if (!fromCSSValue) {
// No zero value for this unit --> doesn't support distance-computation.
&fromWrapper->mCSSValue : nsnull;
const nsStyleAnimation::Value* toCSSValue = &toWrapper->mCSSValue;
if (!FinalizeStyleAnimationValues(fromCSSValue, toCSSValue)) {
return NS_ERROR_FAILURE;
}
return nsStyleAnimation::ComputeDistance(toWrapper->mPropID, *fromCSSValue,
toWrapper->mCSSValue, aDistance) ?
return nsStyleAnimation::ComputeDistance(toWrapper->mPropID,
*fromCSSValue, *toCSSValue,
aDistance) ?
NS_OK : NS_ERROR_FAILURE;
}
@ -290,27 +324,26 @@ nsSMILCSSValueType::Interpolate(const nsSMILValue& aStartVal,
NS_ABORT_IF_FALSE(aResult.mType == this, "Unexpected result type");
NS_ABORT_IF_FALSE(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
"unit distance value out of bounds");
NS_ABORT_IF_FALSE(!aResult.mU.mPtr, "expecting barely-initialized outparam");
const ValueWrapper* startWrapper = ExtractValueWrapper(aStartVal);
const ValueWrapper* endWrapper = ExtractValueWrapper(aEndVal);
NS_ABORT_IF_FALSE(endWrapper, "expecting non-null endpoint");
NS_ABORT_IF_FALSE(!aResult.mU.mPtr, "expecting barely-initialized outparam");
const nsStyleAnimation::Value* startCSSValue = startWrapper ?
&startWrapper->mCSSValue :
GetZeroValueForUnit(endWrapper->mCSSValue.GetUnit());
if (!startCSSValue) {
// No zero value for this unit --> doesn't support interpolation.
&startWrapper->mCSSValue : nsnull;
const nsStyleAnimation::Value* endCSSValue = &endWrapper->mCSSValue;
if (!FinalizeStyleAnimationValues(startCSSValue, endCSSValue)) {
return NS_ERROR_FAILURE;
}
nsStyleAnimation::Value resultValue;
if (nsStyleAnimation::Interpolate(endWrapper->mPropID, *startCSSValue,
endWrapper->mCSSValue, aUnitDistance,
resultValue)) {
if (nsStyleAnimation::Interpolate(endWrapper->mPropID,
*startCSSValue, *endCSSValue,
aUnitDistance, resultValue)) {
aResult.mU.mPtr = new ValueWrapper(endWrapper->mPropID, resultValue,
endWrapper->mPresContext);
return aResult.mU.mPtr ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
@ -389,11 +422,6 @@ nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID,
aString, parsedValue)) {
sSingleton.Init(aValue);
aValue.mU.mPtr = new ValueWrapper(aPropID, parsedValue, presContext);
if (!aValue.mU.mPtr) {
// Out of memory! Destroy outparam, to leave it as nsSMILNullType,
// which indicates to our caller that we failed.
sSingleton.Destroy(aValue);
}
}
}

View File

@ -52,7 +52,26 @@ var _fromByTestLists =
midComp: "rgb(65, 60, 55)",
toComp: "rgb(80, 70, 60)"}),
],
lengthNoUnits: [
new AnimTestcaseFromBy("0", "50", { fromComp: "0px", // 0 acts like 0px
midComp: "25px",
toComp: "50px"}),
new AnimTestcaseFromBy("30", "10", { fromComp: "30px",
midComp: "35px",
toComp: "40px"}),
],
lengthNoUnitsSVG: [
new AnimTestcaseFromBy("0", "50", { fromComp: "0px", // 0 acts like 0px
midComp: "25",
toComp: "50"}),
new AnimTestcaseFromBy("30", "10", { fromComp: "30",
midComp: "35",
toComp: "40"}),
],
lengthPx: [
new AnimTestcaseFromBy("0", "8px", { fromComp: "0px", // 0 acts like 0px
midComp: "4px",
toComp: "8px"}),
new AnimTestcaseFromBy("1px", "10px", { midComp: "6px", toComp: "11px"}),
],
opacity: [
@ -120,7 +139,9 @@ var gFromByBundles =
new AnimTestcaseFromBy("10px serif",
"normal normal 400 100px / 10px monospace"),
]),
new TestcaseBundle(gPropList.font_size, _fromByTestLists.lengthPx),
new TestcaseBundle(gPropList.font_size,
[].concat(_fromByTestLists.lengthNoUnits,
_fromByTestLists.lengthPx)),
new TestcaseBundle(gPropList.font_size_adjust, [
// These testcases implicitly have no effect, because font-size-adjust is
// non-additive (and is declared as such in db_smilCSSPropertyList.js)
@ -149,5 +170,7 @@ var gFromByBundles =
new AnimTestcaseFromBy("10", "5"),
new AnimTestcaseFromBy("1", "2, 3"),
]),
new TestcaseBundle(gPropList.stroke_width, _fromByTestLists.lengthPx),
new TestcaseBundle(gPropList.stroke_width,
[].concat(_fromByTestLists.lengthNoUnitsSVG,
_fromByTestLists.lengthPx))
];

View File

@ -105,7 +105,33 @@ var _fromToTestLists = {
"#gradB\") rgb(0, 0, 255)" },
"need support for URI-based paints"),
],
lengthNoUnits: [
new AnimTestcaseFromTo("0", "20", { fromComp: "0px",
midComp: "10px",
toComp: "20px"}),
new AnimTestcaseFromTo("50", "0", { fromComp: "50px",
midComp: "25px",
toComp: "0px"}),
new AnimTestcaseFromTo("30", "80", { fromComp: "30px",
midComp: "55px",
toComp: "80px"}),
],
lengthNoUnitsSVG: [
new AnimTestcaseFromTo("0", "20", { fromComp: "0px", // 0 acts like 0px
midComp: "10",
toComp: "20"}),
new AnimTestcaseFromTo("50", "0", { fromComp: "50",
midComp: "25",
toComp: "0px"}), // 0 acts like 0px
new AnimTestcaseFromTo("30", "80", { fromComp: "30",
midComp: "55",
toComp: "80"}),
],
lengthPx: [
new AnimTestcaseFromTo("0", "12px", { fromComp: "0px", // 0 acts like 0px
midComp: "6px"}),
new AnimTestcaseFromTo("16px", "0", { midComp: "8px",
toComp: "0px"}), // 0 acts like 0px
new AnimTestcaseFromTo("10px", "20px", { midComp: "15px"}),
new AnimTestcaseFromTo("41px", "1px", { midComp: "21px"}),
],
@ -266,7 +292,8 @@ var gFromToBundles = [
new AnimTestcaseFromTo("cursive", "monospace"),
]),
new TestcaseBundle(gPropList.font_size,
[].concat(_fromToTestLists.lengthPx, [
[].concat(_fromToTestLists.lengthNoUnits,
_fromToTestLists.lengthPx, [
new AnimTestcaseFromTo("10px", "40%", { midComp: "15px", toComp: "20px" }),
new AnimTestcaseFromTo("160%", "80%",
{ fromComp: "80px",
@ -331,7 +358,8 @@ var gFromToBundles = [
toComp: "optimizespeed" }),
]),
new TestcaseBundle(gPropList.letter_spacing,
[].concat(_fromToTestLists.lengthPx,
[].concat(_fromToTestLists.lengthNoUnits,
_fromToTestLists.lengthPx,
_fromToTestLists.lengthPxPctSVG)),
new TestcaseBundle(gPropList.letter_spacing,
_fromToTestLists.lengthPctSVG,
@ -388,7 +416,8 @@ var gFromToBundles = [
midComp: "1, 3, 3, 5, 5, 2, 2, 4, 4, 6"}),
])),
new TestcaseBundle(gPropList.stroke_dashoffset,
[].concat(_fromToTestLists.lengthPx,
[].concat(_fromToTestLists.lengthNoUnitsSVG,
_fromToTestLists.lengthPx,
_fromToTestLists.lengthPxPctSVG,
_fromToTestLists.lengthPctSVG)),
new TestcaseBundle(gPropList.stroke_linecap, [
@ -405,7 +434,8 @@ var gFromToBundles = [
]),
new TestcaseBundle(gPropList.stroke_opacity, _fromToTestLists.opacity),
new TestcaseBundle(gPropList.stroke_width,
[].concat(_fromToTestLists.lengthPx,
[].concat(_fromToTestLists.lengthNoUnitsSVG,
_fromToTestLists.lengthPx,
_fromToTestLists.lengthPxPctSVG,
_fromToTestLists.lengthPctSVG, [
new AnimTestcaseFromTo("inherit", "7px",
@ -438,7 +468,8 @@ var gFromToBundles = [
new AnimTestcaseFromTo("hidden", "collapse"),
]),
new TestcaseBundle(gPropList.word_spacing,
[].concat(_fromToTestLists.lengthPx,
[].concat(_fromToTestLists.lengthNoUnits,
_fromToTestLists.lengthPx,
_fromToTestLists.lengthPxPctSVG)),
new TestcaseBundle(gPropList.word_spacing,
_fromToTestLists.lengthPctSVG,

View File

@ -92,6 +92,47 @@ var _pacedTestLists =
},
"need support for URI-based paints"),
],
lengthNoUnits : [
new AnimTestcasePaced("2; 0; 4",
{ comp0: "2px",
comp1_6: "1px",
comp1_3: "0px",
comp2_3: "2px",
comp1: "4px"
}),
new AnimTestcasePaced("10; 12; 8",
{ comp0: "10px",
comp1_6: "11px",
comp1_3: "12px",
comp2_3: "10px",
comp1: "8px"
}),
],
lengthNoUnitsSVG : [
new AnimTestcasePaced("2; 0; 4",
{ comp0: "2",
comp1_6: "1",
comp1_3: "0px", // 0 acts like 0px
comp2_3: "2",
comp1: "4"
}),
new AnimTestcasePaced("10; 12; 8",
{ comp0: "10",
comp1_6: "11",
comp1_3: "12",
comp2_3: "10",
comp1: "8"
}),
],
lengthPx : [
new AnimTestcasePaced("0; 2px; 6px",
{ comp0: "0px", // 0 acts like 0px
comp1_6: "1px",
comp1_3: "2px",
comp2_3: "4px",
comp1: "6px"
}),
],
lengthPx : [
new AnimTestcasePaced("0px; 2px; 6px",
{ comp0: "0px",
@ -223,7 +264,8 @@ var gPacedBundles =
[].concat(_pacedTestLists.color,
_pacedTestLists.paintServer)),
new TestcaseBundle(gPropList.font_size,
[].concat(_pacedTestLists.lengthPx, [
[].concat(_pacedTestLists.lengthNoUnits,
_pacedTestLists.lengthPx, [
new AnimTestcasePaced("20%; 24%; 16%",
{ comp0: "10px",
comp1_6: "11px",
@ -280,11 +322,13 @@ var gPacedBundles =
}),
])),
new TestcaseBundle(gPropList.stroke_dashoffset,
[].concat(_pacedTestLists.lengthPx,
[].concat(_pacedTestLists.lengthNoUnitsSVG,
_pacedTestLists.lengthPx,
_pacedTestLists.lengthPctSVG,
_pacedTestLists.lengthPxPctSVG)),
new TestcaseBundle(gPropList.stroke_width,
[].concat(_pacedTestLists.lengthPx,
[].concat(_pacedTestLists.lengthNoUnitsSVG,
_pacedTestLists.lengthPx,
_pacedTestLists.lengthPctSVG,
_pacedTestLists.lengthPxPctSVG)),
// XXXdholbert TODO: test 'stroke-dasharray' once we support animating it

View File

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="reftest-wait"
onload="go()">
<style>
rect { stroke: blue; stroke-width: 10; }
</style>
<script xlink:href="../smil-grid.js" type="text/javascript"/>
<script xlink:href="../smil-util.js" type="text/javascript"/>
<script>
function go() {
var animAttrHash = { "attributeName" : "stroke-width",
"by" : "20" };
testAnimatedRectGrid("animate", [animAttrHash]);
}
</script>
</svg>

After

Width:  |  Height:  |  Size: 553 B

View File

@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="reftest-wait"
onload="go()">
<style>
rect { stroke: blue; stroke-width: 10px; }
</style>
<script xlink:href="../smil-grid.js" type="text/javascript"/>
<script xlink:href="../smil-util.js" type="text/javascript"/>
<script>
function go() {
var animAttrHash = { "attributeName" : "stroke-width",
"from" : "10",
"by" : "20" };
testAnimatedRectGrid("animate", [animAttrHash]);
}
</script>
</svg>

After

Width:  |  Height:  |  Size: 606 B

View File

@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="reftest-wait"
onload="go()">
<style>
rect { stroke: blue; stroke-width: 10px; }
</style>
<script xlink:href="../smil-grid.js" type="text/javascript"/>
<script xlink:href="../smil-util.js" type="text/javascript"/>
<script>
function go() {
var animAttrHash = { "attributeName" : "stroke-width",
"from" : "10",
"to" : "30" };
testAnimatedRectGrid("animate", [animAttrHash]);
}
</script>
</svg>

After

Width:  |  Height:  |  Size: 606 B

View File

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="reftest-wait"
onload="go()">
<style>
rect { stroke: blue; stroke-width: 10; }
</style>
<script xlink:href="../smil-grid.js" type="text/javascript"/>
<script xlink:href="../smil-util.js" type="text/javascript"/>
<script>
function go() {
var animAttrHash = { "attributeName" : "stroke-width",
"to" : "30" };
testAnimatedRectGrid("animate", [animAttrHash]);
}
</script>
</svg>

After

Width:  |  Height:  |  Size: 553 B

View File

@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="reftest-wait"
onload="go()">
<style>
rect { stroke: blue; stroke-width: 30px; }
</style>
<script xlink:href="../smil-grid.js" type="text/javascript"/>
<script xlink:href="../smil-util.js" type="text/javascript"/>
<script>
function go() {
var animAttrHash = { "attributeName" : "stroke-width",
"from" : "30",
"by" : "-20" };
testAnimatedRectGrid("animate", [animAttrHash]);
}
</script>
</svg>

After

Width:  |  Height:  |  Size: 607 B

View File

@ -102,6 +102,12 @@ fails == anim-css-strokedasharray-1.svg anim-css-strokedasharray-1-ref.svg
== anim-css-strokewidth-1-from-to-px-px.svg anim-css-strokewidth-1-ref.svg
== anim-css-strokewidth-1-to-px-px.svg anim-css-strokewidth-1-ref.svg
# 'stroke-width' property, from/by/to with unitless values only
== anim-css-strokewidth-1-by-no-no.svg anim-css-strokewidth-1-ref.svg
== anim-css-strokewidth-1-from-by-no-no.svg anim-css-strokewidth-1-ref.svg
== anim-css-strokewidth-1-from-to-no-no.svg anim-css-strokewidth-1-ref.svg
== anim-css-strokewidth-1-to-no-no.svg anim-css-strokewidth-1-ref.svg
# 'stroke-width' property, from/by/to with percent values
# XXXdholbert the mixed pct + px tests fail right now, because we need calc()
# in order to interpolate between pct and non-pct values, and we don't yet
@ -144,6 +150,7 @@ fails == anim-css-strokewidth-1-to-px-pct.svg anim-css-strokewidth-1-ref.s
== anim-css-strokewidth-4-from-by-px-px.svg anim-css-strokewidth-4-ref.svg
== anim-css-strokewidth-4-from-by-px-em.svg anim-css-strokewidth-4-ref.svg
== anim-css-strokewidth-4-from-by-em-em.svg anim-css-strokewidth-4-ref.svg
== anim-css-strokewidth-4-from-by-no-no.svg anim-css-strokewidth-4-ref.svg
# check correct handling of "!important" values
== important-set-1.svg anim-standard-ref.svg