mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 505115 - Part 16 - Implement transitions/animations for 3d transforms. r=dbaron, derf
This commit is contained in:
parent
f022a015e7
commit
5e8876e4df
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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. */
|
||||
|
@ -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 },
|
||||
];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user