Bug 734953 - Restore transform: skew() support; r=dbaron

This commit is contained in:
Aryeh Gregor 2012-07-30 16:48:02 +03:00
parent 2f43fde306
commit 4fc090c08b
17 changed files with 177 additions and 31 deletions

View File

@ -37,33 +37,33 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=435293
-moz-transform: skewy(60deg);
}
#test3 {
-moz-transform: skewx(45deg);
-moz-transform: skew(45deg, 45deg);
}
#test4 {
-moz-transform: skewy(360deg);
-moz-transform: skew(360deg, 45deg);
}
#test5 {
-moz-transform: skewx(80%);
-moz-transform: skew(45deg, 150grad);
}
#test6 {
-moz-transform: skewy(2em);
-moz-transform: skew(80%, 78px);
}
#test7 {
-moz-transform: skewx(-45deg);
-moz-transform: skew(2em, 40ex);
}
#test8 {
-moz-transform: skewy(-465deg);
-moz-transform: skew(-45deg, -465deg);
}
#test9 {
-moz-transform: skewx(30deg, 30deg);
-moz-transform: skew(30deg, 30deg, 30deg);
}
/* approach the singularity from the negative side */
#test10 {
-moz-transform: skewx(90.001deg);
-moz-transform: skew(50grad, 90.001deg);
}
#test11 {
-moz-transform: skewy(90.001deg);
-moz-transform: skew(300grad, 90.001deg);
}
</style>
</head>
@ -117,49 +117,49 @@ function runtests() {
is((+tformValues[5]), 0, "Test2: skewy: param 5 is 0");
style = window.getComputedStyle(document.getElementById("test3"), "");
is(style.getPropertyValue("-moz-transform"), "matrix(1, 0, 1, 1, 0, 0)",
is(style.getPropertyValue("-moz-transform"), "matrix(1, 1, 1, 1, 0, 0)",
"Test3: Skew proper matrix is applied");
style = window.getComputedStyle(document.getElementById("test4"), "");
is(style.getPropertyValue("-moz-transform"), "matrix(1, 0, 0, 1, 0, 0)",
is(style.getPropertyValue("-moz-transform"), "matrix(1, 1, 0, 1, 0, 0)",
"Test4: Skew angle wrap: proper matrix is applied");
style = window.getComputedStyle(document.getElementById("test5"), "");
is(style.getPropertyValue("-moz-transform"), "none",
"Test5: Skewx with invalid units");
is(style.getPropertyValue("-moz-transform"), "matrix(1, -1, 1, 1, 0, 0)",
"Test5: Skew mixing deg and grad");
style = window.getComputedStyle(document.getElementById("test6"), "");
is(style.getPropertyValue("-moz-transform"), "none",
"Test6: Skewy with invalid units");
"Test6: Skew with invalid units");
style = window.getComputedStyle(document.getElementById("test7"), "");
is(style.getPropertyValue("-moz-transform"), "matrix(1, 0, -1, 1, 0, 0)",
"Test7: Skewx with negative angle");
is(style.getPropertyValue("-moz-transform"), "none",
"Test7: Skew with more invalid units");
// Test 8: skew with negative degrees, here again we must handle rounding.
// The matrix should be: matrix(1, 3.73206, 0, 1, 0, 0)
// The matrix should be: matrix(1, 3.73206, -1, 1, 0, 0)
style = window.getComputedStyle(document.getElementById("test8"), "");
tformStyle = style.getPropertyValue("-moz-transform");
tformValues = tformStyle.substring(tformStyle.indexOf('(') + 1,
tformStyle.indexOf(')')).split(',');
is(tformValues[0], 1, "Test8: Test skew with negative degrees-param 0 is 1");
ok(verifyRounded(tformValues[1], 3.73206), "Test8: Rounded param 1 is in bounds");
is((+tformValues[2]), 0, "Test8: param 2 is 0");
is((+tformValues[2]), -1, "Test8: param 2 is -1");
is((+tformValues[3]), 1, "Test8: param 3 is 1");
is((+tformValues[4]), 0, "Test8: param 4 is 0");
is((+tformValues[5]), 0, "Test8: param 5 is 0");
style = window.getComputedStyle(document.getElementById("test9"), "");
is(style.getPropertyValue("-moz-transform"), "none",
"Test9: Skewx with two parameters should be ignored");
"Test9: Skew in 3d should be ignored");
style = window.getComputedStyle(document.getElementById("test10"), "");
is(style.getPropertyValue("-moz-transform"), "matrix(1, 0, -10000, 1, 0, 0)",
"Test10: Skewx with nearly infinite numbers");
is(style.getPropertyValue("-moz-transform"), "matrix(1, -10000, 1, 1, 0, 0)",
"Test10: Skew with nearly infinite numbers");
style = window.getComputedStyle(document.getElementById("test11"), "");
is(style.getPropertyValue("-moz-transform"), "matrix(1, -10000, 0, 1, 0, 0)",
"Test11: Skewy with nearly infinite numbers");
is(style.getPropertyValue("-moz-transform"), "matrix(1, -10000, 10000, 1, 0, 0)",
"Test11: Skew with more infinite numbers");
}
// Verifies that aVal is +/- 0.00001 of aTrueVal

View File

@ -16,7 +16,7 @@
</head>
<body>
<div style="position:relative; left:400px; top:200px;">
<div style="-moz-transform: skewx(15deg);">
<div style="-moz-transform: skew(15deg);">
<div style="-moz-transform: rotate(90deg);">
<div style="-moz-transform: scale(2);">
<div style="-moz-transform: translate(100px);">

View File

@ -23,7 +23,7 @@
<div style="-moz-transform: translate(100px);">
<div style="-moz-transform: scale(2);">
<div style="-moz-transform: rotate(90deg);">
<div style="-moz-transform: skewx(15deg);">
<div style="-moz-transform: skew(15deg);">
<div class="test">
</div>
</div>

View File

@ -20,7 +20,7 @@
</head>
<body>
<div style="position:relative; left:400px; top:200px;">
<div class="test" style="-moz-transform: translate(100px) scale(2) rotate(90deg) skewx(15deg);">
<div class="test" style="-moz-transform: translate(100px) scale(2) rotate(90deg) skew(15deg);">
</div>
</div>
</body>

View File

@ -82,6 +82,10 @@ random == rotate-1f.html rotate-1-ref.html
== transform-svg-1a.xhtml transform-svg-1-ref.xhtml
== transform-svg-2a.xhtml transform-svg-2-ref.xhtml
!= transform-svg-2a.xhtml transform-svg-2-fail.xhtml
# skew should allow a mix of one and two parameters.
== skew-1a.html skew-1-ref.html
== skew-1b.html skew-1-ref.html
== skew-2a.html skew-2-ref.html
# matrix with values equal to other transforms should behave indistinguishably
== matrix-1a.html matrix-1-ref.html
== matrix-2a.html matrix-2-ref.html

View File

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
<div style="width: 100px; height: 100px; background-color: gold; border: 1px solid black; -moz-transform: skewx(10deg);">
</div>
</body>
</html>

View File

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
<div style="width: 100px; height: 100px; background-color: gold; border: 1px solid black; -moz-transform: skew(10deg);">
</div>
</body>
</html>

View File

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
<div style="width: 100px; height: 100px; background-color: gold; border: 1px solid black; -moz-transform: skew(10deg, 0deg);">
</div>
</body>
</html>

View File

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
<div style="width: 100px; height: 100px; background-color: gold; border: 1px solid black; -moz-transform: skewy(10deg);">
</div>
</body>
</html>

View File

@ -0,0 +1,8 @@
<html>
<head>
</head>
<body>
<div style="width: 100px; height: 100px; background-color: gold; border: 1px solid black; -moz-transform: skew(0deg, 10deg);">
</div>
</body>
</html>

View File

@ -400,6 +400,7 @@ CSS_KEY(semi-condensed, semi_condensed)
CSS_KEY(semi-expanded, semi_expanded)
CSS_KEY(separate, separate)
CSS_KEY(show, show)
CSS_KEY(skew, skew)
CSS_KEY(skewx, skewx)
CSS_KEY(skewy, skewy)
CSS_KEY(small, small)

View File

@ -8187,6 +8187,12 @@ static bool GetFunctionParseInformation(nsCSSKeyword aToken,
aMinElems = 1U;
aMaxElems = 2U;
break;
case eCSSKeyword_skew:
/* Exactly one or two angles. */
variantIndex = eTwoAngles;
aMinElems = 1U;
aMaxElems = 2U;
break;
case eCSSKeyword_scale:
/* One or two scale factors. */
variantIndex = eTwoNumbers;

View File

@ -125,6 +125,7 @@ AppendFunction(nsCSSKeyword aTransformFunction)
nargs = 3;
break;
case eCSSKeyword_translate:
case eCSSKeyword_skew:
case eCSSKeyword_scale:
nargs = 2;
break;
@ -1202,6 +1203,59 @@ AppendTransformFunction(nsCSSKeyword aTransformFunction,
* Thus, after step 5, C = -sin(φ), D = cos(φ), and the XY shear is tan(φ).
* Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
* In step 7, the rotation is thus φ.
*
* skew(θ, φ), which is matrix(1, tan(φ), tan(θ), 1, 0, 0), which decomposes
* to 'rotate(φ) skewX(θ + φ) scale(sec(φ), cos(φ))' since (ignoring
* the alternate sign possibilities that would get fixed in step 6):
* In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
* Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
* In step 4, the XY shear is cos(φ)tan(θ) + sin(φ).
* Thus, after step 4,
* C = tan(θ) - cos(φ)(cos(φ)tan(θ) + sin(φ)) = tan(θ)sin²(φ) - cos(φ)sin(φ)
* D = 1 - sin(φ)(cos(φ)tan(θ) + sin(φ)) = cos²(φ) - sin(φ)cos(φ)tan(θ)
* Thus, in step 5, the Y scale is sqrt(C² + D²) =
* sqrt(tan²(θ)(sin(φ) + sin²(φ)cos²(φ)) -
* 2 tan(θ)(sin³(φ)cos(φ) + sin(φ)cos³(φ)) +
* (sin²(φ)cos²(φ) + cos(φ))) =
* sqrt(tan²(θ)sin²(φ) - 2 tan(θ)sin(φ)cos(φ) + cos²(φ)) =
* cos(φ) - tan(θ)sin(φ) (taking the negative of the obvious solution so
* we avoid flipping in step 6).
* After step 5, C = -sin(φ) and D = cos(φ), and the XY shear is
* (cos(φ)tan(θ) + sin(φ)) / (cos(φ) - tan(θ)sin(φ)) =
* (dividing both numerator and denominator by cos(φ))
* (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)) = tan(θ + φ).
* (See http://en.wikipedia.org/wiki/List_of_trigonometric_identities .)
* Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
* In step 7, the rotation is thus φ.
*
* To check this result, we can multiply things back together:
*
* [ cos(φ) -sin(φ) ] [ 1 tan(θ + φ) ] [ sec(φ) 0 ]
* [ sin(φ) cos(φ) ] [ 0 1 ] [ 0 cos(φ) ]
*
* [ cos(φ) cos(φ)tan(θ + φ) - sin(φ) ] [ sec(φ) 0 ]
* [ sin(φ) sin(φ)tan(θ + φ) + cos(φ) ] [ 0 cos(φ) ]
*
* but since tan(θ + φ) = (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)),
* cos(φ)tan(θ + φ) - sin(φ)
* = cos(φ)(tan(θ) + tan(φ)) - sin(φ) + sin(φ)tan(θ)tan(φ)
* = cos(φ)tan(θ) + sin(φ) - sin(φ) + sin(φ)tan(θ)tan(φ)
* = cos(φ)tan(θ) + sin(φ)tan(θ)tan(φ)
* = tan(θ) (cos(φ) + sin(φ)tan(φ))
* = tan(θ) sec(φ) (cos²(φ) + sin²(φ))
* = tan(θ) sec(φ)
* and
* sin(φ)tan(θ + φ) + cos(φ)
* = sin(φ)(tan(θ) + tan(φ)) + cos(φ) - cos(φ)tan(θ)tan(φ)
* = tan(θ) (sin(φ) - sin(φ)) + sin(φ)tan(φ) + cos(φ)
* = sec(φ) (sin²(φ) + cos²(φ))
* = sec(φ)
* so the above is:
* [ cos(φ) tan(θ) sec(φ) ] [ sec(φ) 0 ]
* [ sin(φ) sec(φ) ] [ 0 cos(φ) ]
*
* [ 1 tan(θ) ]
* [ tan(φ) 1 ]
*/
/*
@ -1536,6 +1590,27 @@ AddTransformLists(const nsCSSValueList* aList1, double aCoeff1,
// skews with infinite tangents, and behavior changes pretty
// drastically when crossing such skews (since the direction of
// animation flips), so interop is probably more important here.
case eCSSKeyword_skew: {
NS_ABORT_IF_FALSE(a1->Count() == 2 || a1->Count() == 3,
"unexpected count");
NS_ABORT_IF_FALSE(a2->Count() == 2 || a2->Count() == 3,
"unexpected count");
nsCSSValue zero(0.0f, eCSSUnit_Radian);
// Add Y component of skew.
AddCSSValueAngle(a1->Count() == 3 ? a1->Item(2) : zero,
aCoeff1,
a2->Count() == 3 ? a2->Item(2) : zero,
aCoeff2,
arr->Item(2));
// Add X component of skew (which can be merged with case below
// in non-DEBUG).
AddCSSValueAngle(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
arr->Item(1));
break;
}
case eCSSKeyword_skewx:
case eCSSKeyword_skewy:
case eCSSKeyword_rotate:
@ -1970,8 +2045,8 @@ nsStyleAnimation::AddWeighted(nsCSSProperty aProperty,
// We want to avoid the matrix decomposition when we can, since
// avoiding it can produce better results both for compound
// transforms and for skewY (see below). We can do this in two
// cases:
// transforms and for skew and skewY (see below). We can do this
// in two cases:
// (1) if one of the transforms is 'none'
// (2) if the lists have the same length and the transform
// functions match

View File

@ -382,6 +382,19 @@ ProcessSkewY(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
ProcessSkewHelper(aMatrix, 0.0, aData->Item(1).GetAngleValueInRadians());
}
/* Function that converts a skew transform into a matrix. */
static void
ProcessSkew(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
{
NS_ASSERTION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
double xSkew = aData->Item(1).GetAngleValueInRadians();
double ySkew = (aData->Count() == 2
? 0.0 : aData->Item(2).GetAngleValueInRadians());
ProcessSkewHelper(aMatrix, xSkew, ySkew);
}
/* Function that converts a rotate transform into a matrix. */
static void
ProcessRotateZ(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
@ -543,6 +556,9 @@ MatrixForTransformFunction(gfx3DMatrix& aMatrix,
case eCSSKeyword_skewy:
ProcessSkewY(aMatrix, aData);
break;
case eCSSKeyword_skew:
ProcessSkew(aMatrix, aData);
break;
case eCSSKeyword_rotatex:
ProcessRotateX(aMatrix, aData);
break;

View File

@ -1291,9 +1291,7 @@ var gCSSProperties = {
"translatex(-moz-max(5px,10%))",
"translate(10px, calc(min(5px,10%)))",
"translate(calc(max(5px,10%)), 10%)",
"matrix(1, 0, 0, 1, -moz-max(5px * 3), calc(10% - 3px))",
// Bug 734953
"skew(45deg)", "skew(45deg, 45deg)",
"matrix(1, 0, 0, 1, max(5px * 3), calc(10% - 3px))"
].concat(SpecialPowers.getBoolPref("layout.3d-transforms.enabled") ? [
"perspective(0px)", "perspective(-10px)", "matrix3d(dinosaur)",
"matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)",

View File

@ -24,6 +24,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=721136
[" sCaLeX( 2 ) ", "scaleX(2)"],
[" sCaLeY( 2 ) ", "scaleY(2)"],
[" sCaLeZ( 2 ) ", "scaleZ(2)"],
[" sKeW( 45dEg ) ", "skew(45deg)"],
[" sKeW( 45dEg,45DeG ) ", "skew(45deg, 45deg)"],
[" sKeWx( 45DeG ) ", "skewX(45deg)"],
[" sKeWy( 45DeG ) ", "skewY(45deg)"],
[" tRaNsLaTe( 1Px ) ", "translate(1px)"],

View File

@ -1428,6 +1428,10 @@ function test_transform_transition(prop) {
expected_uncomputed: 'skewX(33.75deg)' },
{ start: 'skewY(45deg)', end: 'none',
expected_uncomputed: 'skewY(33.75deg)' },
{ start: 'skew(45deg)', end: 'none',
expected_uncomputed: 'skew(33.75deg)' },
{ start: 'skew(45deg, 45deg)', end: 'none',
expected_uncomputed: 'skew(33.75deg, 33.75deg)' },
{ start: 'skewX(45deg)', end: 'skewX(-45deg)',
expected_uncomputed: 'skewX(22.5deg)' },
{ start: 'skewX(0)', end: 'skewX(-45deg)',