Bug 505115 - Part 16 - Implement transitions/animations for 3d transforms. r=dbaron, derf

This commit is contained in:
Matt Woodrow 2011-09-27 10:53:33 +13:00
parent 29daf0fda9
commit 28ca5bb7d9
5 changed files with 378 additions and 156 deletions

View File

@ -1058,27 +1058,53 @@ nsComputedDOMStyle::DoGetMozTransform()
bounds,
float(nsDeviceContext::AppUnitsPerCSSPixel()));
if (!matrix.Is2D()) {
nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
PRBool is3D = !matrix.Is2D();
/* Set it to "none." */
val->SetIdent(eCSSKeyword_none);
return val;
nsAutoString resultString(NS_LITERAL_STRING("matrix"));
if (is3D) {
resultString.Append(NS_LITERAL_STRING("3d"));
}
nsAutoString resultString(NS_LITERAL_STRING("matrix("));
resultString.Append(NS_LITERAL_STRING("("));
resultString.AppendFloat(matrix._11);
resultString.Append(NS_LITERAL_STRING(", "));
resultString.AppendFloat(matrix._12);
resultString.Append(NS_LITERAL_STRING(", "));
if (is3D) {
resultString.AppendFloat(matrix._13);
resultString.Append(NS_LITERAL_STRING(", "));
resultString.AppendFloat(matrix._14);
resultString.Append(NS_LITERAL_STRING(", "));
}
resultString.AppendFloat(matrix._21);
resultString.Append(NS_LITERAL_STRING(", "));
resultString.AppendFloat(matrix._22);
resultString.Append(NS_LITERAL_STRING(", "));
if (is3D) {
resultString.AppendFloat(matrix._23);
resultString.Append(NS_LITERAL_STRING(", "));
resultString.AppendFloat(matrix._24);
resultString.Append(NS_LITERAL_STRING(", "));
resultString.AppendFloat(matrix._31);
resultString.Append(NS_LITERAL_STRING(", "));
resultString.AppendFloat(matrix._32);
resultString.Append(NS_LITERAL_STRING(", "));
resultString.AppendFloat(matrix._33);
resultString.Append(NS_LITERAL_STRING(", "));
resultString.AppendFloat(matrix._34);
resultString.Append(NS_LITERAL_STRING(", "));
}
resultString.AppendFloat(matrix._41);
resultString.Append(NS_LITERAL_STRING("px, "));
resultString.AppendFloat(matrix._42);
resultString.Append(NS_LITERAL_STRING("px)"));
resultString.Append(NS_LITERAL_STRING("px"));
if (is3D) {
resultString.Append(NS_LITERAL_STRING(", "));
resultString.AppendFloat(matrix._43);
resultString.Append(NS_LITERAL_STRING("px, "));
resultString.AppendFloat(matrix._44);
}
resultString.Append(NS_LITERAL_STRING(")"));
/* Create a value to hold our result. */
nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();

View File

@ -53,6 +53,7 @@
#include "prlog.h"
#include <math.h>
#include "gfxMatrix.h"
#include "gfxQuaternion.h"
namespace css = mozilla::css;
namespace dom = mozilla::dom;
@ -908,24 +909,43 @@ AppendTransformFunction(nsCSSKeyword aTransformFunction,
nsCSSValueList**& aListTail)
{
PRUint32 nargs;
if (aTransformFunction == eCSSKeyword_matrix) {
nargs = 6;
} else if (aTransformFunction == eCSSKeyword_translate ||
aTransformFunction == eCSSKeyword_skew ||
aTransformFunction == eCSSKeyword_scale) {
nargs = 2;
} else if (aTransformFunction == eCSSKeyword_interpolatematrix) {
nargs = 4;
} else {
NS_ABORT_IF_FALSE(aTransformFunction == eCSSKeyword_translatex ||
aTransformFunction == eCSSKeyword_translatey ||
aTransformFunction == eCSSKeyword_scalex ||
aTransformFunction == eCSSKeyword_scaley ||
aTransformFunction == eCSSKeyword_skewx ||
aTransformFunction == eCSSKeyword_skewy ||
aTransformFunction == eCSSKeyword_rotate,
"must be a transform function");
nargs = 1;
switch (aTransformFunction) {
case eCSSKeyword_matrix3d:
nargs = 16;
break;
case eCSSKeyword_matrix:
nargs = 6;
break;
case eCSSKeyword_rotate3d:
nargs = 4;
break;
case eCSSKeyword_interpolatematrix:
case eCSSKeyword_translate3d:
case eCSSKeyword_scale3d:
nargs = 3;
break;
case eCSSKeyword_translate:
case eCSSKeyword_skew:
case eCSSKeyword_scale:
nargs = 2;
break;
default:
NS_ERROR("must be a transform function");
case eCSSKeyword_translatex:
case eCSSKeyword_translatey:
case eCSSKeyword_translatez:
case eCSSKeyword_scalex:
case eCSSKeyword_scaley:
case eCSSKeyword_scalez:
case eCSSKeyword_skewx:
case eCSSKeyword_skewy:
case eCSSKeyword_rotate:
case eCSSKeyword_rotatex:
case eCSSKeyword_rotatey:
case eCSSKeyword_rotatez:
case eCSSKeyword_perspective:
nargs = 1;
break;
}
nsRefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(nargs + 1);
@ -1064,12 +1084,17 @@ AppendTransformFunction(nsCSSKeyword aTransformFunction,
*/
/*
* DecomposeMatrix implements the non-translation parts of the above
* decomposition algorithm.
* Decompose2DMatrix implements the above decomposition algorithm.
*/
#define XYSHEAR 0
#define XZSHEAR 1
#define YZSHEAR 2
static PRBool
DecomposeMatrix(const gfxMatrix &aMatrix,
float &aRotate, float &aXYShear, float &aScaleX, float &aScaleY)
Decompose2DMatrix(const gfxMatrix &aMatrix, gfxPoint3D &aScale,
float aShear[3], gfxQuaternion &aRotate,
gfxPoint3D &aTranslate)
{
float A = aMatrix.xx,
B = aMatrix.yx,
@ -1093,7 +1118,7 @@ DecomposeMatrix(const gfxMatrix &aMatrix,
D /= scaleY;
XYshear /= scaleY;
// A*D - B*C should now be 1 or -1
// A*D - B*C should now be 1 or -1
NS_ASSERTION(0.99 < NS_ABS(A*D - B*C) && NS_ABS(A*D - B*C) < 1.01,
"determinant should now be 1 or -1");
if (A * D < B * C) {
@ -1105,84 +1130,200 @@ DecomposeMatrix(const gfxMatrix &aMatrix,
scaleX = -scaleX;
}
float rotation = atan2f(B, A);
float rotate = atan2f(B, A);
aRotate = gfxQuaternion(0, 0, sin(rotate/2), cos(rotate/2));
aShear[XYSHEAR] = XYshear;
aScale.x = scaleX;
aScale.y = scaleY;
aTranslate.x = aMatrix.x0;
aTranslate.y = aMatrix.y0;
return PR_TRUE;
}
aRotate = rotation;
aXYShear = XYshear;
aScaleX = scaleX;
aScaleY = scaleY;
/**
* Implementation of the unmatrix algorithm, specified by:
*
* http://dev.w3.org/csswg/css3-2d-transforms/#unmatrix
*
* This, in turn, refers to the unmatrix program in Graphics Gems,
* available from http://tog.acm.org/resources/GraphicsGems/ , and in
* particular as the file GraphicsGems/gemsii/unmatrix.c
* in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
*/
static PRBool
Decompose3DMatrix(const gfx3DMatrix &aMatrix, gfxPoint3D &aScale,
float aShear[3], gfxQuaternion &aRotate,
gfxPoint3D &aTranslate, gfxPointH3D &aPerspective)
{
gfx3DMatrix local = aMatrix;
if (local[3][3] == 0) {
return PR_FALSE;
}
/* Normalize the matrix */
local.Normalize();
/**
* perspective is used to solve for perspective, but it also provides
* an easy way to test for singularity of the upper 3x3 component.
*/
gfx3DMatrix perspective = local;
gfxPointH3D empty(0, 0, 0, 1);
perspective.SetTransposedVector(3, empty);
if (perspective.Determinant() == 0.0) {
return PR_FALSE;
}
/* First, isolate perspective. */
if (local[0][3] != 0 || local[1][3] != 0 ||
local[2][3] != 0) {
/* aPerspective is the right hand side of the equation. */
aPerspective = local.TransposedVector(3);
/**
* Solve the equation by inverting perspective and multiplying
* aPerspective by the inverse.
*/
perspective.Invert();
aPerspective = perspective.TransposeTransform4D(aPerspective);
/* Clear the perspective partition */
local.SetTransposedVector(3, empty);
} else {
aPerspective = gfxPointH3D(0, 0, 0, 1);
}
/* Next take care of translation */
for (int i = 0; i < 3; i++) {
aTranslate[i] = local[3][i];
local[3][i] = 0;
}
/* Now get scale and shear. */
/* Compute X scale factor and normalize first row. */
aScale.x = local[0].Length();
local[0] /= aScale.x;
/* Compute XY shear factor and make 2nd local orthogonal to 1st. */
aShear[XYSHEAR] = local[0].DotProduct(local[1]);
local[1] -= local[0] * aShear[XYSHEAR];
/* Now, compute Y scale and normalize 2nd local. */
aScale.y = local[1].Length();
local[1] /= aScale.y;
aShear[XYSHEAR] /= aScale.y;
/* Compute XZ and YZ shears, make 3rd local orthogonal */
aShear[XZSHEAR] = local[0].DotProduct(local[2]);
local[2] -= local[0] * aShear[XZSHEAR];
aShear[YZSHEAR] = local[1].DotProduct(local[2]);
local[2] -= local[1] * aShear[YZSHEAR];
/* Next, get Z scale and normalize 3rd local. */
aScale.z = local[2].Length();
local[2] /= aScale.z;
aShear[XZSHEAR] /= aScale.z;
aShear[YZSHEAR] /= aScale.z;
/**
* At this point, the matrix (in locals) is orthonormal.
* Check for a coordinate system flip. If the determinant
* is -1, then negate the matrix and the scaling factors.
*/
if (local[0].DotProduct(local[1].CrossProduct(local[2])) < 0) {
aScale *= -1;
for (int i = 0; i < 3; i++) {
local[i] *= -1;
}
}
/* Now, get the rotations out */
aRotate = gfxQuaternion(local);
return PR_TRUE;
}
/* Force small values to zero. We do this to avoid having sin(360deg)
* evaluate to a tiny but nonzero value.
*/
static double FlushToZero(double aVal)
template<typename T>
T InterpolateNumerically(const T& aOne, const T& aTwo, double aCoeff)
{
if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON)
return 0.0f;
else
return aVal;
}
/* Computes tan(aTheta). For values of aTheta such that tan(aTheta) is
* undefined or very large, SafeTangent returns a manageably large value
* of the correct sign.
*/
static double SafeTangent(double aTheta)
{
const double kEpsilon = 0.0001;
/* tan(theta) = sin(theta)/cos(theta); problems arise when
* cos(theta) is too close to zero. Limit cos(theta) to the
* range [-1, -epsilon] U [epsilon, 1].
*/
double sinTheta = sin(aTheta);
double cosTheta = cos(aTheta);
if (cosTheta >= 0 && cosTheta < kEpsilon)
cosTheta = kEpsilon;
else if (cosTheta < 0 && cosTheta >= -kEpsilon)
cosTheta = -kEpsilon;
return FlushToZero(sinTheta / cosTheta);
return aOne + (aTwo - aOne) * aCoeff;
}
/* static */ gfxMatrix
nsStyleAnimation::InterpolateTransformMatrix(const gfxMatrix &aMatrix1,
double aCoeff1,
const gfxMatrix &aMatrix2,
double aCoeff2)
/* static */ gfx3DMatrix
nsStyleAnimation::InterpolateTransformMatrix(const gfx3DMatrix &aMatrix1,
const gfx3DMatrix &aMatrix2,
double aProgress)
{
float rotate1, XYshear1, scaleX1, scaleY1;
DecomposeMatrix(aMatrix1, rotate1, XYshear1, scaleX1, scaleY1);
float rotate2, XYshear2, scaleX2, scaleY2;
DecomposeMatrix(aMatrix2, rotate2, XYshear2, scaleX2, scaleY2);
// Decompose both matrices
float rotate = rotate1 * aCoeff1 + rotate2 * aCoeff2;
// TODO: What do we do if one of these returns PR_FALSE (singular matrix)
float skewX = atanf(XYshear1) * aCoeff1 + atanf(XYshear2) * aCoeff2;
gfxPoint3D scale1(1, 1, 1), translate1;
gfxPointH3D perspective1(0, 0, 0, 1);
gfxQuaternion rotate1;
float shear1[3] = { 0.0f, 0.0f, 0.0f};
// Handle scale, and the two matrix components where identity is 1, by
// subtracting 1, multiplying by the coefficients, and then adding 1
// back. This gets the right AddWeighted behavior and gets us the
// interpolation-against-identity behavior for free.
float scaleX =
((scaleX1 - 1.0f) * aCoeff1 + (scaleX2 - 1.0f) * aCoeff2) + 1.0f;
float scaleY =
((scaleY1 - 1.0f) * aCoeff1 + (scaleY2 - 1.0f) * aCoeff2) + 1.0f;
gfxPoint3D scale2(1, 1, 1), translate2;
gfxPointH3D perspective2(0, 0, 0, 1);
gfxQuaternion rotate2;
float shear2[3] = { 0.0f, 0.0f, 0.0f};
gfxMatrix result;
gfxMatrix matrix2d1, matrix2d2;
if (aMatrix1.Is2D(&matrix2d1) && aMatrix2.Is2D(&matrix2d2)) {
Decompose2DMatrix(matrix2d1, scale1, shear1, rotate1, translate1);
Decompose2DMatrix(matrix2d2, scale2, shear2, rotate2, translate2);
} else {
Decompose3DMatrix(aMatrix1, scale1, shear1,
rotate1, translate1, perspective1);
Decompose3DMatrix(aMatrix2, scale2, shear2,
rotate2, translate2, perspective2);
}
gfxMatrix skew;
skew.xy = SafeTangent(skewX);
result.Translate(gfxPoint(aMatrix1.x0 * aCoeff1 + aMatrix2.x0 * aCoeff2,
aMatrix1.y0 * aCoeff1 + aMatrix2.y0 * aCoeff2));
result.Rotate(rotate);
result.PreMultiply(skew);
result.Scale(scaleX, scaleY);
// Interpolate each of the pieces
gfx3DMatrix result;
gfxPointH3D perspective =
InterpolateNumerically(perspective1, perspective2, aProgress);
result.SetTransposedVector(3, perspective);
gfxPoint3D translate =
InterpolateNumerically(translate1, translate2, aProgress);
result.Translate(translate);
gfxQuaternion q3 = rotate1.Slerp(rotate2, aProgress);
gfx3DMatrix rotate = q3.ToMatrix();
if (!rotate.IsIdentity()) {
result = rotate * result;
}
// TODO: Would it be better to interpolate these as angles? How do we convert back to angles?
float yzshear =
InterpolateNumerically(shear1[YZSHEAR], shear2[YZSHEAR], aProgress);
if (yzshear != 0.0) {
result.SkewYZ(yzshear);
}
float xzshear =
InterpolateNumerically(shear1[XZSHEAR], shear2[XZSHEAR], aProgress);
if (xzshear != 0.0) {
result.SkewXZ(xzshear);
}
float xyshear =
InterpolateNumerically(shear1[XYSHEAR], shear2[XYSHEAR], aProgress);
if (xyshear != 0.0) {
result.SkewXY(xyshear);
}
gfxPoint3D scale =
InterpolateNumerically(scale1, scale2, aProgress);
if (scale != gfxPoint3D(1.0, 1.0, 1.0)) {
result.Scale(scale.x, scale.y, scale.z);
}
return result;
}
@ -1196,15 +1337,36 @@ AddDifferentTransformLists(const nsCSSValueList* aList1, double aCoeff1,
nsRefPtr<nsCSSValue::Array> arr;
arr = AppendTransformFunction(eCSSKeyword_interpolatematrix, resultTail);
// FIXME: We should change the other transform code to also only
// take a single progress value, as having values that don't
// sum to 1 doesn't make sense for these.
if (aList1 == aList2) {
arr->Item(1).Reset();
} else {
aList1->CloneInto(arr->Item(1).SetListValue());
}
arr->Item(1).SetPercentValue(aCoeff1);
aList1->CloneInto(arr->Item(2).SetListValue());
aList2->CloneInto(arr->Item(2).SetListValue());
arr->Item(3).SetPercentValue(aCoeff2);
aList2->CloneInto(arr->Item(4).SetListValue());
return result.forget();
}
static PRBool
TransformFunctionsMatch(nsCSSKeyword func1, nsCSSKeyword func2)
{
if (func1 == func2) {
return PR_TRUE;
}
if (func1 == eCSSKeyword_rotatez && func2 == eCSSKeyword_rotate ||
func1 == eCSSKeyword_rotate && func2 == eCSSKeyword_rotatez) {
return PR_TRUE;
}
return PR_FALSE;
}
static nsCSSValueList*
AddTransformLists(const nsCSSValueList* aList1, double aCoeff1,
const nsCSSValueList* aList2, double aCoeff2)
@ -1215,13 +1377,17 @@ AddTransformLists(const nsCSSValueList* aList1, double aCoeff1,
do {
const nsCSSValue::Array *a1 = aList1->mValue.GetArrayValue(),
*a2 = aList2->mValue.GetArrayValue();
NS_ABORT_IF_FALSE(nsStyleTransformMatrix::TransformFunctionOf(a1) ==
nsStyleTransformMatrix::TransformFunctionOf(a2),
NS_ABORT_IF_FALSE(TransformFunctionsMatch(nsStyleTransformMatrix::TransformFunctionOf(a1),
nsStyleTransformMatrix::TransformFunctionOf(a2)),
"transform function mismatch");
nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1);
nsRefPtr<nsCSSValue::Array> arr;
if (tfunc != eCSSKeyword_matrix && tfunc != eCSSKeyword_interpolatematrix) {
if (tfunc != eCSSKeyword_matrix &&
tfunc != eCSSKeyword_matrix3d &&
tfunc != eCSSKeyword_interpolatematrix &&
tfunc != eCSSKeyword_rotate3d &&
tfunc != eCSSKeyword_perspective) {
arr = AppendTransformFunction(tfunc, resultTail);
}
@ -1250,13 +1416,25 @@ AddTransformLists(const nsCSSValueList* aList1, double aCoeff1,
break;
}
case eCSSKeyword_translatex:
case eCSSKeyword_translatey: {
case eCSSKeyword_translatey:
case eCSSKeyword_translatez: {
NS_ABORT_IF_FALSE(a1->Count() == 2, "unexpected count");
NS_ABORT_IF_FALSE(a2->Count() == 2, "unexpected count");
AddTransformTranslate(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
arr->Item(1));
break;
}
case eCSSKeyword_translate3d: {
NS_ABORT_IF_FALSE(a1->Count() == 4, "unexpected count");
NS_ABORT_IF_FALSE(a2->Count() == 4, "unexpected count");
AddTransformTranslate(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
arr->Item(1));
AddTransformTranslate(a1->Item(2), aCoeff1, a2->Item(2), aCoeff2,
arr->Item(2));
AddTransformTranslate(a1->Item(3), aCoeff1, a2->Item(3), aCoeff2,
arr->Item(3));
break;
}
case eCSSKeyword_scale: {
NS_ABORT_IF_FALSE(a1->Count() == 2 || a1->Count() == 3,
"unexpected count");
@ -1281,7 +1459,8 @@ AddTransformLists(const nsCSSValueList* aList1, double aCoeff1,
break;
}
case eCSSKeyword_scalex:
case eCSSKeyword_scaley: {
case eCSSKeyword_scaley:
case eCSSKeyword_scalez: {
NS_ABORT_IF_FALSE(a1->Count() == 2, "unexpected count");
NS_ABORT_IF_FALSE(a2->Count() == 2, "unexpected count");
@ -1290,6 +1469,19 @@ AddTransformLists(const nsCSSValueList* aList1, double aCoeff1,
break;
}
case eCSSKeyword_scale3d: {
NS_ABORT_IF_FALSE(a1->Count() == 4, "unexpected count");
NS_ABORT_IF_FALSE(a2->Count() == 4, "unexpected count");
AddTransformScale(a1->Item(1), aCoeff1, a2->Item(1), aCoeff2,
arr->Item(1));
AddTransformScale(a1->Item(2), aCoeff1, a2->Item(2), aCoeff2,
arr->Item(2));
AddTransformScale(a1->Item(3), aCoeff1, a2->Item(3), aCoeff2,
arr->Item(3));
break;
}
// It would probably be nicer to animate skew in tangent space
// rather than angle space. However, it's easy to specify
// skews with infinite tangents, and behavior changes pretty
@ -1318,7 +1510,10 @@ AddTransformLists(const nsCSSValueList* aList1, double aCoeff1,
}
case eCSSKeyword_skewx:
case eCSSKeyword_skewy:
case eCSSKeyword_rotate: {
case eCSSKeyword_rotate:
case eCSSKeyword_rotatex:
case eCSSKeyword_rotatey:
case eCSSKeyword_rotatez: {
NS_ABORT_IF_FALSE(a1->Count() == 2, "unexpected count");
NS_ABORT_IF_FALSE(a2->Count() == 2, "unexpected count");
@ -1328,18 +1523,25 @@ AddTransformLists(const nsCSSValueList* aList1, double aCoeff1,
break;
}
case eCSSKeyword_matrix:
case eCSSKeyword_interpolatematrix: {
case eCSSKeyword_matrix3d:
case eCSSKeyword_interpolatematrix:
case eCSSKeyword_rotate3d:
case eCSSKeyword_perspective: {
// FIXME: If the matrix contains only numbers then we could decompose
// here. We can't do this for matrix3d though, so it's probably
// best to stay consistent.
// here.
// Construct temporary lists with only this item in them.
nsCSSValueList tempList1, tempList2;
tempList1.mValue = aList1->mValue;
tempList2.mValue = aList2->mValue;
*resultTail =
AddDifferentTransformLists(&tempList1, aCoeff1, &tempList2, aCoeff2);
if (aList1 == aList2) {
*resultTail =
AddDifferentTransformLists(&tempList1, aCoeff1, &tempList1, aCoeff2);
} else {
*resultTail =
AddDifferentTransformLists(&tempList1, aCoeff1, &tempList2, aCoeff2);
}
while ((*resultTail)->mNext) {
resultTail = &(*resultTail)->mNext;
@ -1774,11 +1976,11 @@ nsStyleAnimation::AddWeighted(nsCSSProperty aProperty,
result->mValue.SetNoneValue();
}
} else {
result = AddTransformLists(list2, aCoeff2, list2, 0);
result = AddTransformLists(list2, 0, list2, aCoeff2);
}
} else {
if (list2->mValue.GetUnit() == eCSSUnit_None) {
result = AddTransformLists(list1, aCoeff1, list1, 0);
result = AddTransformLists(list1, 0, list1, aCoeff1);
} else {
PRBool match = PR_TRUE;
@ -1789,7 +1991,8 @@ nsStyleAnimation::AddWeighted(nsCSSProperty aProperty,
item1->mValue.GetArrayValue());
nsCSSKeyword func2 = nsStyleTransformMatrix::TransformFunctionOf(
item2->mValue.GetArrayValue());
if (func1 != func2) {
if (!TransformFunctionsMatch(func1, func2)) {
break;
}

View File

@ -58,7 +58,7 @@ struct nsCSSValuePair;
struct nsCSSValueTriplet;
struct nsCSSValuePairList;
struct nsCSSRect;
struct gfxMatrix;
class gfx3DMatrix;
namespace mozilla {
namespace dom {
@ -233,12 +233,12 @@ public:
* Interpolates between 2 matrices by decomposing them.
*
* @param aMatrix1 First matrix, using CSS pixel units.
* @param aCoeff1 Interpolation value in the range [0.0, 1.0]
* @param aMatrix2 Second matrix, using CSS pixel units.
* @param aCoeff2 Interpolation value in the range [0.0, 1.0]
* @param aProgress Interpolation value in the range [0.0, 1.0]
*/
static gfxMatrix InterpolateTransformMatrix(const gfxMatrix &aMatrix1, double aCoeff1,
const gfxMatrix &aMatrix2, double aCoeff2);
static gfx3DMatrix InterpolateTransformMatrix(const gfx3DMatrix &aMatrix1,
const gfx3DMatrix &aMatrix2,
double aProgress);
/**
* The types and values for the values that we extract and animate.

View File

@ -206,34 +206,24 @@ nsStyleTransformMatrix::ProcessInterpolateMatrix(const nsCSSValue::Array* aData,
PRBool& aCanStoreInRuleTree,
nsRect& aBounds, float aAppUnitsPerMatrixUnit)
{
NS_PRECONDITION(aData->Count() == 5, "Invalid array!");
NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
double coeff1 = aData->Item(1).GetPercentValue();
gfx3DMatrix matrix1 = ReadTransforms(aData->Item(2).GetListValue(),
aContext, aPresContext,
aCanStoreInRuleTree,
aBounds, aAppUnitsPerMatrixUnit);
double coeff2 = aData->Item(3).GetPercentValue();
gfx3DMatrix matrix2 = ReadTransforms(aData->Item(4).GetListValue(),
aContext, aPresContext,
aCanStoreInRuleTree,
aBounds, aAppUnitsPerMatrixUnit);
gfx3DMatrix matrix1, matrix2;
if (aData->Item(1).GetUnit() == eCSSUnit_List) {
matrix1 = ReadTransforms(aData->Item(1).GetListValue(),
aContext, aPresContext,
aCanStoreInRuleTree,
aBounds, aAppUnitsPerMatrixUnit);
}
if (aData->Item(2).GetUnit() == eCSSUnit_List) {
matrix2 = ReadTransforms(aData->Item(2).GetListValue(),
aContext, aPresContext,
aCanStoreInRuleTree,
aBounds, aAppUnitsPerMatrixUnit);
}
double progress = aData->Item(3).GetPercentValue();
gfxMatrix matrix2d1, matrix2d2;
#ifdef DEBUG
PRBool is2d =
#endif
matrix1.Is2D(&matrix2d1);
NS_ABORT_IF_FALSE(is2d, "Can't do animations with 3d transforms!");
#ifdef DEBUG
is2d =
#endif
matrix2.Is2D(&matrix2d2);
NS_ABORT_IF_FALSE(is2d, "Can't do animations with 3d transforms!");
return gfx3DMatrix::From2D(
nsStyleAnimation::InterpolateTransformMatrix(matrix2d1, coeff1,
matrix2d2, coeff2));
return nsStyleAnimation::InterpolateTransformMatrix(matrix1, matrix2, progress);
}
/* Helper function to process a translatex function. */

View File

@ -1425,20 +1425,23 @@ function test_transform_transition(prop) {
// matrix : skewX
{ start: 'matrix(1, 0, 3, 1, 0px, 0px)', end: 'none',
expected: 'matrix(1, 0, ' + Math.tan(Math.atan(3) * 0.75) + ', 1, 0px, 0px)',
expected: 'matrix(1, 0, ' + 3 * 0.75 + ', 1, 0px, 0px)',
round_error_ok: true },
{ start: 'skewX(0)', end: 'skewX(-45deg) translate(0)',
expected: 'matrix(1, 0, -0.198912367, 1, 0px, 0px)',
expected: 'matrix(1, 0, -0.25, 1, 0px, 0px)',
round_error_ok: true },
// matrix : rotate
{ start: 'rotate(-30deg)', end: 'matrix(0, 1, -1, 0, 0px, 0px)',
expected: 'matrix(1, 0, 0, 1, 0px, 0px)' },
expected: 'matrix(1, 0, 0, 1, 0px, 0px)',
round_error_ok: true },
{ start: 'rotate(-30deg) translateX(0)',
end: 'translateX(0) rotate(-90deg)',
expected: c('rotate(-45deg)') },
expected: c('rotate(-45deg)'),
round_error_ok: true },
// matrix decomposition of skewY
{ start: 'skewY(60deg)', end: 'skewY(-60deg) translateX(0)',
expected: c('rotate(30deg) skewX(30deg) scale(2, 0.5)'),
/* rotate(30deg) skewX(60deg)/2 scale(2, 0.5) */
expected: c('rotate(30deg) skewX(' + Math.atan(Math.tan(Math.PI * 60/180) / 2) + 'rad) scale(2, 0.5)'),
round_error_ok: true },
// matrix decomposition
@ -1493,42 +1496,42 @@ function test_transform_transition(prop) {
{ start: 'none',
end: 'matrix(1, 0, 1.5, 1, 0pt, 0pt)',
/* skewX(atan(1.5)) */
expected: 'matrix(1, 0, ' + Math.tan(Math.atan(1.5) * 0.25).toFixed(6) + ', 1, 0px, 0px)',
expected: 'matrix(1, 0, ' + 1.5 * 0.25 + ', 1, 0px, 0px)',
round_error_ok: true },
{ start: 'none',
end: 'matrix(-1, 0, 2, -1, 0pt, 0pt)',
/* rotate(180deg) skewX(atan(-2)) */
expected: c('rotate(45deg) matrix(1, 0, ' + Math.tan(Math.atan(-2) * 0.25) + ', 1, 0px, 0px)'),
expected: c('rotate(45deg) matrix(1, 0, ' + -2 * 0.25 + ', 1, 0px, 0px)'),
round_error_ok: true },
{ start: 'none',
end: 'matrix(0, -1, 1, -3, 0pt, 0pt)',
/* rotate(-90deg) skewX(atan(3)) */
expected: c('rotate(-22.5deg) matrix(1, 0, ' + Math.tan(Math.atan(3) * 0.25) + ', 1, 0px, 0px)'),
expected: c('rotate(-22.5deg) matrix(1, 0, ' + 3 * 0.25 + ', 1, 0px, 0px)'),
round_error_ok: true },
{ start: 'none',
end: 'matrix(0, 1, -1, 4, 0pt, 0pt)',
/* rotate(90deg) skewX(atan(4)) */
expected: c('rotate(22.5deg) matrix(1, 0, ' + Math.tan(Math.atan(4) * 0.25) + ', 1, 0px, 0px)'),
expected: c('rotate(22.5deg) matrix(1, 0, ' + 4 * 0.25 + ', 1, 0px, 0px)'),
round_error_ok: true },
// and then four with negative determinants
{ start: 'none',
end: 'matrix(1, 0, 1, -1, 0pt, 0pt)',
/* rotate(-180deg) skewX(atan(-1)) scaleX(-1) */
expected: c('rotate(-45deg) matrix(1, 0, ' + Math.tan(Math.atan(-1) * 0.25) + ', 1, 0px, 0px) scaleX(0.5)'),
expected: c('rotate(-45deg) matrix(1, 0, ' + -1 * 0.25 + ', 1, 0px, 0px) scaleX(0.5)'),
round_error_ok: true },
{ start: 'none',
end: 'matrix(-1, 0, -1, 1, 0pt, 0pt)',
/* skewX(atan(-1)) scaleX(-1) */
expected: c('matrix(1, 0, ' + Math.tan(Math.atan(-1) * 0.25) + ', 1, 0px, 0px) scaleX(0.5)') },
expected: c('matrix(1, 0, ' + -1 * 0.25 + ', 1, 0px, 0px) scaleX(0.5)') },
{ start: 'none',
end: 'matrix(0, 1, 1, -2, 0pt, 0pt)',
/* rotate(-90deg) skewX(atan(2)) scaleX(-1) */
expected: c('rotate(-22.5deg) matrix(1, 0, ' + Math.tan(Math.atan(2) * 0.25) + ', 1, 0px, 0px) scaleX(0.5)'),
expected: c('rotate(-22.5deg) matrix(1, 0, ' + 2 * 0.25 + ', 1, 0px, 0px) scaleX(0.5)'),
round_error_ok: true },
{ start: 'none',
end: 'matrix(0, -1, -1, 0.5, 0pt, 0pt)',
/* rotate(90deg) skewX(atan(0.5)) scaleX(-1) */
expected: c('rotate(22.5deg) matrix(1, 0, ' + Math.tan(Math.atan(0.5) * 0.25) + ', 1, 0px, 0px) scaleX(0.5)'),
expected: c('rotate(22.5deg) matrix(1, 0, ' + 0.5 * 0.25 + ', 1, 0px, 0px) scaleX(0.5)'),
round_error_ok: true },
// lists vs. matrix decomposition
@ -1540,14 +1543,14 @@ function test_transform_transition(prop) {
expected_uncomputed: 'skewY(22.5deg) rotate(90deg)' },
{ start: 'skewY(45deg) rotate(90deg) translate(0)',
end: 'skewY(-45deg) rotate(90deg)',
expected: c('rotate(90deg) skewX(-22.5deg)'),
expected: 'matrix(0, 1, -1, -0.5, 0px, 0px)',
round_error_ok: true },
{ start: 'skewX(45deg) rotate(90deg)',
end: 'skewX(-45deg) rotate(90deg)',
expected_uncomputed: 'skewX(22.5deg) rotate(90deg)' },
{ start: 'skewX(-60deg) rotate(90deg) translate(0)',
end: 'skewX(60deg) rotate(90deg)',
expected: c('rotate(120deg) skewX(30deg) scale(2, 0.5)'),
expected: c('rotate(120deg) skewX(' + Math.atan(Math.tan(Math.PI * 60/180) / 2) + 'rad) scale(2, 0.5)'),
round_error_ok: true },
];