Bug 505115 - Part 3 - Convert nsStyleTransformMatrix to be backed by a 4x4 matrix * * * Enable test when GPU layers are enabled since it passes there. diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2236,18 +2236,22 @@ nsDisplayTransform::GetResultingTransfor /* Get the underlying transform matrix. This requires us to get the * bounds of the frame. */ const nsStyleDisplay* disp = aFrame->GetStyleDisplay(); nsRect bounds = (aBoundsOverride ? *aBoundsOverride : nsDisplayTransform::GetFrameBoundsForTransform(aFrame)); /* Get the matrix, then change its basis to factor in the origin. */ + PRBool dummy; return nsLayoutUtils::ChangeMatrixBasis - (newOrigin + toMozOrigin, disp->mTransform.GetThebesMatrix(bounds, aFactor)); + (newOrigin + toMozOrigin, nsStyleTransformMatrix::ReadTransforms(disp->mSpecifiedTransform, + aFrame->GetStyleContext(), + aFrame->PresContext(), + dummy, bounds, aFactor)); } already_AddRefed nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder, LayerManager *aManager) { gfxMatrix newTransformMatrix = GetResultingTransformMatrix(mFrame, ToReferenceFrame(), mFrame->PresContext()->AppUnitsPerDevPixel(), @@ -2392,41 +2396,47 @@ nsRect nsDisplayTransform::GetBounds(nsD * certainly contains the actual (non-axis-aligned) untransformed rect. */ nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder, PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - const nsStyleDisplay* disp = mFrame->GetStyleDisplay(); nsRect untransformedVisible = UntransformRect(mVisibleRect, mFrame, ToReferenceFrame()); + + float factor = nsPresContext::AppUnitsPerCSSPixel(); + gfxMatrix matrix = + GetResultingTransformMatrix(mFrame, ToReferenceFrame(), + factor, nsnull); + nsRegion result; - if (disp->mTransform.GetMainMatrixEntry(1) == 0.0f && - disp->mTransform.GetMainMatrixEntry(2) == 0.0f && + if (matrix.PreservesAxisAlignedRectangles() && mStoredList.GetOpaqueRegion(aBuilder).Contains(untransformedVisible)) { result = mVisibleRect; } return result; } /* The transform is uniform if it fills the entire bounding rect and the * wrapped list is uniform. See GetOpaqueRegion for discussion of why this * works. */ PRBool nsDisplayTransform::IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor) { - const nsStyleDisplay* disp = mFrame->GetStyleDisplay(); nsRect untransformedVisible = UntransformRect(mVisibleRect, mFrame, ToReferenceFrame()); - return disp->mTransform.GetMainMatrixEntry(1) == 0.0f && - disp->mTransform.GetMainMatrixEntry(2) == 0.0f && - mStoredList.GetVisibleRect().Contains(untransformedVisible) && - mStoredList.IsUniform(aBuilder, aColor); + float factor = nsPresContext::AppUnitsPerCSSPixel(); + gfxMatrix matrix = + GetResultingTransformMatrix(mFrame, ToReferenceFrame(), + factor, nsnull); + return matrix.PreservesAxisAlignedRectangles() && + mStoredList.GetVisibleRect().Contains(untransformedVisible) && + mStoredList.IsUniform(aBuilder, aColor); } /* If UNIFIED_CONTINUATIONS is defined, we can merge two display lists that * share the same underlying content. Otherwise, doing so results in graphical * glitches. */ #ifndef UNIFIED_CONTINUATIONS diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -298,16 +298,17 @@ CSS_KEY(infobackground, infobackground) CSS_KEY(infotext, infotext) CSS_KEY(inherit, inherit) CSS_KEY(inline, inline) CSS_KEY(inline-axis, inline_axis) CSS_KEY(inline-block, inline_block) CSS_KEY(inline-table, inline_table) CSS_KEY(inset, inset) CSS_KEY(inside, inside) +CSS_KEY(interpolatematrix, interpolatematrix) #ifdef GFX_HAS_INVERT CSS_KEY(invert, invert) #endif CSS_KEY(italic, italic) CSS_KEY(justify, justify) CSS_KEY(katakana, katakana) CSS_KEY(katakana-iroha, katakana_iroha) CSS_KEY(khz, khz) diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -952,18 +952,16 @@ nsComputedDOMStyle::DoGetMozTransformOri /* If the property is "none", hand back "none" wrapped in a value. * Otherwise, compute the aggregate transform matrix and hands it back in a * "matrix" wrapper. */ nsIDOMCSSValue* nsComputedDOMStyle::DoGetMozTransform() { - static const PRInt32 NUM_FLOATS = 4; - /* First, get the display data. We'll need it. */ const nsStyleDisplay* display = GetStyleDisplay(); /* If the "no transforms" flag is set, then we should construct a * single-element entry and hand it back. */ if (!display->HasTransform()) { nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); @@ -973,52 +971,50 @@ nsComputedDOMStyle::DoGetMozTransform() return val; } /* Otherwise, we need to compute the current value of the transform matrix, * store it in a string, and hand it back to the caller. */ nsAutoString resultString(NS_LITERAL_STRING("matrix(")); - /* Now, we need to convert the matrix into a string. We'll start by taking - * the first four entries and converting them directly to floating-point - * values. - */ - for (PRInt32 index = 0; index < NUM_FLOATS; ++index) { - resultString.AppendFloat(display->mTransform.GetMainMatrixEntry(index)); - resultString.Append(NS_LITERAL_STRING(", ")); - } /* Use the inner frame for width and height. If we fail, assume zero. * TODO: There is no good way for us to represent the case where there's no * frame, which is problematic. The reason is that when we have percentage * transforms, there are a total of four stored matrix entries that influence * the transform based on the size of the element. However, this poses a * problem, because only two of these values can be explicitly referenced * using the named transforms. Until a real solution is found, we'll just * use this approach. */ nsRect bounds = (mInnerFrame ? nsDisplayTransform::GetFrameBoundsForTransform(mInnerFrame) : nsRect(0, 0, 0, 0)); - /* Now, compute the dX and dY components by adding the stored coord value - * (in CSS pixels) to the translate values. + PRBool dummy; + gfxMatrix matrix = + nsStyleTransformMatrix::ReadTransforms(display->mSpecifiedTransform, + nsnull, nsnull, dummy, + bounds, + float(nsDeviceContext::AppUnitsPerCSSPixel())); + + /* Now, we need to convert the matrix into a string. */ - - float deltaX = nsPresContext::AppUnitsToFloatCSSPixels - (display->mTransform.GetXTranslation(bounds)); - float deltaY = nsPresContext::AppUnitsToFloatCSSPixels - (display->mTransform.GetYTranslation(bounds)); - - - /* Append these values! */ - resultString.AppendFloat(deltaX); + resultString.AppendFloat(matrix.xx); + resultString.Append(NS_LITERAL_STRING(", ")); + resultString.AppendFloat(matrix.yx); + resultString.Append(NS_LITERAL_STRING(", ")); + resultString.AppendFloat(matrix.xy); + resultString.Append(NS_LITERAL_STRING(", ")); + resultString.AppendFloat(matrix.yy); + resultString.Append(NS_LITERAL_STRING(", ")); + resultString.AppendFloat(matrix.x0); resultString.Append(NS_LITERAL_STRING("px, ")); - resultString.AppendFloat(deltaY); + resultString.AppendFloat(matrix.y0); resultString.Append(NS_LITERAL_STRING("px)")); /* Create a value to hold our result. */ nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); val->SetString(resultString); return val; } diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -4423,32 +4423,28 @@ nsRuleNode::ComputeDisplayData(void* aSt case eCSSUnit_Initial: case eCSSUnit_None: display->mSpecifiedTransform = nsnull; break; case eCSSUnit_Inherit: display->mSpecifiedTransform = parentDisplay->mSpecifiedTransform; - if (parentDisplay->mSpecifiedTransform) - display->mTransform = parentDisplay->mTransform; canStoreInRuleTree = PR_FALSE; break; case eCSSUnit_List: case eCSSUnit_ListDep: { const nsCSSValueList* head = transformValue->GetListValue(); // can get a _None in here from transform animation if (head->mValue.GetUnit() == eCSSUnit_None) { NS_ABORT_IF_FALSE(head->mNext == nsnull, "none must be alone"); display->mSpecifiedTransform = nsnull; } else { display->mSpecifiedTransform = head; // weak pointer, owned by rule - display->mTransform = nsStyleTransformMatrix::ReadTransforms(head, - aContext, mPresContext, canStoreInRuleTree); } break; } default: NS_ABORT_IF_FALSE(false, "unrecognized transform unit"); } diff --git a/layout/style/nsStyleAnimation.cpp b/layout/style/nsStyleAnimation.cpp --- a/layout/style/nsStyleAnimation.cpp +++ b/layout/style/nsStyleAnimation.cpp @@ -890,16 +890,18 @@ AppendTransformFunction(nsCSSKeyword aTr { 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, @@ -1042,23 +1044,23 @@ AppendTransformFunction(nsCSSKeyword aTr * [ tan(φ) 1 ] */ /* * DecomposeMatrix implements the non-translation parts of the above * decomposition algorithm. */ static PRBool -DecomposeMatrix(const nsStyleTransformMatrix &aMatrix, +DecomposeMatrix(const gfxMatrix &aMatrix, float &aRotate, float &aXYShear, float &aScaleX, float &aScaleY) { - float A = aMatrix.GetMainMatrixEntry(0), - B = aMatrix.GetMainMatrixEntry(1), - C = aMatrix.GetMainMatrixEntry(2), - D = aMatrix.GetMainMatrixEntry(3); + float A = aMatrix.xx, + B = aMatrix.yx, + C = aMatrix.xy, + D = aMatrix.yy; if (A * D == B * C) { // singular matrix return PR_FALSE; } float scaleX = sqrt(A * A + B * B); A /= scaleX; B /= scaleX; @@ -1089,58 +1091,33 @@ DecomposeMatrix(const nsStyleTransformMa aRotate = rotation; aXYShear = XYshear; aScaleX = scaleX; aScaleY = scaleY; return PR_TRUE; } -static nsCSSValueList* -AddTransformMatrix(const nsStyleTransformMatrix &aMatrix1, double aCoeff1, - const nsStyleTransformMatrix &aMatrix2, double aCoeff2) + +/* static */ nsCSSValueList* +nsStyleAnimation::AddTransformMatrix(const gfxMatrix &aMatrix1, double aCoeff1, + const gfxMatrix &aMatrix2, double aCoeff2) { nsAutoPtr result; nsCSSValueList **resultTail = getter_Transfers(result); nsRefPtr arr; - // The translation part of the matrix comes first in our result list, - // but it's complicated by the mix of %s, possibly in between rotates. - - // append a rotate(90deg) - arr = AppendTransformFunction(eCSSKeyword_rotate, resultTail); - arr->Item(1).SetFloatValue(float(M_PI_2), eCSSUnit_Radian); - - // append the translation for parts of the % translation components - // that were from inside a rotation - float rtranslateXPercent = - aMatrix1.GetWidthRelativeYTranslation() * aCoeff1 + - aMatrix2.GetWidthRelativeYTranslation() * aCoeff2; - float rtranslateYPercent = - - (aMatrix1.GetHeightRelativeXTranslation() * aCoeff1 + - aMatrix2.GetHeightRelativeXTranslation() * aCoeff2); - arr = AppendTransformFunction(eCSSKeyword_translate, resultTail); - arr->Item(1).SetPercentValue(rtranslateXPercent); - arr->Item(2).SetPercentValue(rtranslateYPercent); - - // append a rotate(-90deg) - arr = AppendTransformFunction(eCSSKeyword_rotate, resultTail); - arr->Item(1).SetFloatValue(-float(M_PI_2), eCSSUnit_Radian); - + // The translation part of the matrix comes first in our result list. nscoord translateXCoord = NSToCoordRound( - aMatrix1.GetCoordXTranslation() * aCoeff1 + - aMatrix2.GetCoordXTranslation() * aCoeff2); + aMatrix1.x0 * aCoeff1 + + aMatrix2.x0 * aCoeff2); nscoord translateYCoord = NSToCoordRound( - aMatrix1.GetCoordYTranslation() * aCoeff1 + - aMatrix2.GetCoordYTranslation() * aCoeff2); - float translateXPercent = aMatrix1.GetWidthRelativeXTranslation() * aCoeff1 + - aMatrix2.GetWidthRelativeXTranslation() * aCoeff2; - float translateYPercent = aMatrix1.GetHeightRelativeYTranslation() * aCoeff1 + - aMatrix2.GetHeightRelativeYTranslation() * aCoeff2; + aMatrix1.y0 * aCoeff1 + + aMatrix2.y0 * aCoeff2); float rotate1, XYshear1, scaleX1, scaleY1; DecomposeMatrix(aMatrix1, rotate1, XYshear1, scaleX1, scaleY1); float rotate2, XYshear2, scaleX2, scaleY2; DecomposeMatrix(aMatrix2, rotate2, XYshear2, scaleX2, scaleY2); float rotate = rotate1 * aCoeff1 + rotate2 * aCoeff2; @@ -1150,22 +1127,16 @@ AddTransformMatrix(const nsStyleTransfor // 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; - // It's simpler to append an additional function for the percentage - // translate parts than to build a calc() expression. - arr = AppendTransformFunction(eCSSKeyword_translate, resultTail); - arr->Item(1).SetPercentValue(translateXPercent); - arr->Item(2).SetPercentValue(translateYPercent); - arr = AppendTransformFunction(eCSSKeyword_translate, resultTail); arr->Item(1).SetFloatValue( nsPresContext::AppUnitsToFloatCSSPixels(translateXCoord), eCSSUnit_Pixel); arr->Item(2).SetFloatValue( nsPresContext::AppUnitsToFloatCSSPixels(translateYCoord), eCSSUnit_Pixel); arr = AppendTransformFunction(eCSSKeyword_rotate, resultTail); arr->Item(1).SetFloatValue(rotate, eCSSUnit_Radian); @@ -1176,16 +1147,34 @@ AddTransformMatrix(const nsStyleTransfor arr = AppendTransformFunction(eCSSKeyword_scale, resultTail); arr->Item(1).SetFloatValue(scaleX, eCSSUnit_Number); arr->Item(2).SetFloatValue(scaleY, eCSSUnit_Number); return result.forget(); } static nsCSSValueList* +AddDifferentTransformLists(const nsCSSValueList* aList1, double aCoeff1, + const nsCSSValueList* aList2, double aCoeff2) +{ + nsAutoPtr result; + nsCSSValueList **resultTail = getter_Transfers(result); + + nsRefPtr arr; + arr = AppendTransformFunction(eCSSKeyword_interpolatematrix, resultTail); + + arr->Item(1).SetFloatValue(aCoeff1, eCSSUnit_Percent); + arr->Item(2).SetDependentListValue(aList1->Clone()); + arr->Item(3).SetFloatValue(aCoeff2, eCSSUnit_Percent); + arr->Item(4).SetDependentListValue(aList2->Clone()); + + return result.forget(); +} + +static nsCSSValueList* AddTransformLists(const nsCSSValueList* aList1, double aCoeff1, const nsCSSValueList* aList2, double aCoeff2) { nsAutoPtr result; nsCSSValueList **resultTail = getter_Transfers(result); do { const nsCSSValue::Array *a1 = aList1->mValue.GetArrayValue(), @@ -1301,27 +1290,28 @@ AddTransformLists(const nsCSSValueList* arr->Item(1)); break; } case eCSSKeyword_matrix: { NS_ABORT_IF_FALSE(a1->Count() == 7, "unexpected count"); NS_ABORT_IF_FALSE(a2->Count() == 7, "unexpected count"); - PRBool dummy; - nsStyleTransformMatrix matrix1, matrix2; - matrix1.SetToTransformFunction(a1, nsnull, nsnull, dummy); - matrix2.SetToTransformFunction(a2, nsnull, nsnull, dummy); + ///XXX: If the matrix contains only numbers then we could decompose here. + // If it contains % values, I think we can too, less sure about + // calc values though. + + + // Construct temporary lists with only this item in them. + nsCSSValueList tempList1, tempList2; + tempList1.mValue = aList1->mValue; + tempList2.mValue = aList2->mValue; *resultTail = - AddTransformMatrix(matrix1, aCoeff1, matrix2, aCoeff2); - - while ((*resultTail)->mNext) { - resultTail = &(*resultTail)->mNext; - } + AddDifferentTransformLists(&tempList1, aCoeff1, &tempList2, aCoeff2); break; } default: NS_ABORT_IF_FALSE(PR_FALSE, "unknown transform function"); } aList1 = aList1->mNext; @@ -1697,24 +1687,17 @@ nsStyleAnimation::AddWeighted(nsCSSPrope // Either |break| above or length mismatch. match = PR_FALSE; } } if (match) { result = AddTransformLists(list1, aCoeff1, list2, aCoeff2); } else { - PRBool dummy; - nsStyleTransformMatrix matrix1 = - nsStyleTransformMatrix::ReadTransforms(list1, nsnull, nsnull, - dummy), - matrix2 = - nsStyleTransformMatrix::ReadTransforms(list2, nsnull, nsnull, - dummy); - result = AddTransformMatrix(matrix1, aCoeff1, matrix2, aCoeff2); + result = AddDifferentTransformLists(list1, aCoeff1, list2, aCoeff2); } } } aResultValue.SetAndAdoptCSSValueListValue(result.forget(), eUnit_Transform); return PR_TRUE; } diff --git a/layout/style/nsStyleAnimation.h b/layout/style/nsStyleAnimation.h --- a/layout/style/nsStyleAnimation.h +++ b/layout/style/nsStyleAnimation.h @@ -44,16 +44,17 @@ #include "prtypes.h" #include "nsAString.h" #include "nsCRTGlue.h" #include "nsStringBuffer.h" #include "nsCSSProperty.h" #include "nsCoord.h" #include "nsColor.h" +#include "gfxMatrix.h" class nsPresContext; class nsStyleContext; class nsCSSValue; struct nsCSSValueList; struct nsCSSValuePair; struct nsCSSValuePairList; struct nsCSSRect; @@ -213,16 +214,19 @@ public: * @param aStyleContext The style context to check for the computed value. * @param [out] aComputedValue The resulting computed value. * @return PR_TRUE on success, PR_FALSE on failure. */ static PRBool ExtractComputedValue(nsCSSProperty aProperty, nsStyleContext* aStyleContext, Value& aComputedValue); + static nsCSSValueList* AddTransformMatrix(const gfxMatrix &aMatrix1, double aCoeff1, + const gfxMatrix &aMatrix2, double aCoeff2); + /** * The types and values for the values that we extract and animate. */ enum Unit { eUnit_Null, // not initialized eUnit_Normal, eUnit_Auto, eUnit_None, diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -2087,18 +2087,16 @@ nsStyleDisplay::nsStyleDisplay(const nsS mOverflowY = aSource.mOverflowY; mResize = aSource.mResize; mClipFlags = aSource.mClipFlags; mClip = aSource.mClip; mOpacity = aSource.mOpacity; /* Copy over the transformation information. */ mSpecifiedTransform = aSource.mSpecifiedTransform; - if (mSpecifiedTransform) - mTransform = aSource.mTransform; /* Copy over transform origin. */ mTransformOrigin[0] = aSource.mTransformOrigin[0]; mTransformOrigin[1] = aSource.mTransformOrigin[1]; } nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const { @@ -2142,17 +2140,17 @@ nsChangeHint nsStyleDisplay::CalcDiffere } else if (HasTransform()) { /* Otherwise, if we've kept the property lying around and we already had a * transform, we need to see whether or not we've changed the transform. * If so, we need to do a reflow and a repaint. The reflow is to recompute * the overflow rect (which probably changed if the transform changed) * and to redraw within the bounds of that new overflow rect. */ - if (mTransform != aOther.mTransform) + if (mSpecifiedTransform != aOther.mSpecifiedTransform) NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame, nsChangeHint_UpdateTransformLayer)); for (PRUint8 index = 0; index < 2; ++index) if (mTransformOrigin[index] != aOther.mTransformOrigin[index]) { NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame, nsChangeHint_RepaintFrame)); break; diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1514,17 +1514,16 @@ struct nsStyleDisplay { PRUint8 mResize; // [reset] see nsStyleConsts.h PRUint8 mClipFlags; // [reset] see nsStyleConsts.h // mSpecifiedTransform is the list of transform functions as // specified, or null to indicate there is no transform. (inherit or // initial are replaced by an actual list of transform functions, or // null, as appropriate.) (owned by the style rule) const nsCSSValueList *mSpecifiedTransform; // [reset] - nsStyleTransformMatrix mTransform; // [reset] The stored transform matrix nsStyleCoord mTransformOrigin[2]; // [reset] percent, coord, calc nsAutoTArray mTransitions; // [reset] // The number of elements in mTransitions that are not from repeating // a list due to another property being longer. PRUint32 mTransitionTimingFunctionCount, mTransitionDurationCount, mTransitionDelayCount, diff --git a/layout/style/nsStyleTransformMatrix.cpp b/layout/style/nsStyleTransformMatrix.cpp --- a/layout/style/nsStyleTransformMatrix.cpp +++ b/layout/style/nsStyleTransformMatrix.cpp @@ -14,16 +14,17 @@ * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Mozilla Corporation * * Contributor(s): * Keith Schwarz (original author) + * Matt Woodrow * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -42,16 +43,17 @@ #include "nsAutoPtr.h" #include "nsCSSValue.h" #include "nsStyleContext.h" #include "nsPresContext.h" #include "nsRuleNode.h" #include "nsCSSKeywords.h" #include "nsMathUtils.h" #include "CSSCalc.h" +#include "nsStyleAnimation.h" namespace css = mozilla::css; /* Note on floating point precision: The transform matrix is an array * of single precision 'float's, and so are most of the input values * we get from the style system, but intermediate calculations * involving angles need to be done in 'double'. */ @@ -85,136 +87,16 @@ static double SafeTangent(double aTheta) if (cosTheta >= 0 && cosTheta < kEpsilon) cosTheta = kEpsilon; else if (cosTheta < 0 && cosTheta >= -kEpsilon) cosTheta = -kEpsilon; return FlushToZero(sinTheta / cosTheta); } -/* Constructor sets the data to the identity matrix. */ -nsStyleTransformMatrix::nsStyleTransformMatrix() -{ - SetToIdentity(); -} - -/* SetToIdentity just fills in the appropriate values. */ -void nsStyleTransformMatrix::SetToIdentity() -{ - /* Set the main matrix to the identity. */ - mMain[0] = 1.0f; - mMain[1] = 0.0f; - mMain[2] = 0.0f; - mMain[3] = 1.0f; - mDelta[0] = 0; - mDelta[1] = 0; - - /* Both translation matrices are zero. */ - mX[0] = 0.0f; - mX[1] = 0.0f; - mY[0] = 0.0f; - mY[1] = 0.0f; -} - -/* Adds the constant translation to the scale factor translation components. */ -nscoord nsStyleTransformMatrix::GetXTranslation(const nsRect& aBounds) const -{ - return NSToCoordRound(aBounds.width * mX[0] + aBounds.height * mY[0]) + - mDelta[0]; -} -nscoord nsStyleTransformMatrix::GetYTranslation(const nsRect& aBounds) const -{ - return NSToCoordRound(aBounds.width * mX[1] + aBounds.height * mY[1]) + - mDelta[1]; -} - -/* GetThebesMatrix converts the stored matrix in a few steps. */ -gfxMatrix nsStyleTransformMatrix::GetThebesMatrix(const nsRect& aBounds, - float aScale) const -{ - /* Compute the graphics matrix. We take the stored main elements, along with - * the delta, and add in the matrices: - * - * | 0 0 dx1| - * | 0 0 dx2| * width - * | 0 0 0| - * - * | 0 0 dy1| - * | 0 0 dy2| * height - * | 0 0 0| - */ - return gfxMatrix(mMain[0], mMain[1], mMain[2], mMain[3], - NSAppUnitsToFloatPixels(GetXTranslation(aBounds), aScale), - NSAppUnitsToFloatPixels(GetYTranslation(aBounds), aScale)); -} - -/* Performs the matrix multiplication necessary to multiply the two matrices, - * then hands back a reference to ourself. - */ -nsStyleTransformMatrix& -nsStyleTransformMatrix::operator *= (const nsStyleTransformMatrix &aOther) -{ - /* We'll buffer all of our results into a temporary storage location - * during this operation since we don't want to overwrite the values of - * the old matrix with the values of the new. - */ - float newMatrix[4]; - nscoord newDelta[2]; - float newX[2]; - float newY[2]; - - /* [this] [aOther] - * |a1 c1 e1| |a0 c0 e0| |a0a1 + b0c1 c0a1 + d0c1 e0a1 + f0c1 + e1| - * |b1 d1 f1|x|b0 d0 f0| = |a0b1 + b0d1 c0b1 + d0d1 e0b1 + f0d1 + f1| - * |0 0 1 | | 0 0 1| | 0 0 1| - */ - newMatrix[0] = aOther.mMain[0] * mMain[0] + aOther.mMain[1] * mMain[2]; - newMatrix[1] = aOther.mMain[0] * mMain[1] + aOther.mMain[1] * mMain[3]; - newMatrix[2] = aOther.mMain[2] * mMain[0] + aOther.mMain[3] * mMain[2]; - newMatrix[3] = aOther.mMain[2] * mMain[1] + aOther.mMain[3] * mMain[3]; - newDelta[0] = NSToCoordRound(aOther.mDelta[0] * mMain[0] + - aOther.mDelta[1] * mMain[2]) + mDelta[0]; - newDelta[1] = NSToCoordRound(aOther.mDelta[0] * mMain[1] + - aOther.mDelta[1] * mMain[3]) + mDelta[1]; - - /* For consistent terminology, let u0, u1, v0, and v1 be the four transform - * coordinates from our matrix, and let x0, x1, y0, and y1 be the four - * transform coordinates from the other matrix. Then the new transform - * coordinates are: - * - * u0' = a1u0 + c1u1 + x0 - * u1' = b1u0 + d1u1 + x1 - * v0' = a1v0 + c1v1 + y0 - * v1' = b1v0 + d1v1 + y1 - */ - newX[0] = mMain[0] * aOther.mX[0] + mMain[2] * aOther.mX[1] + mX[0]; - newX[1] = mMain[1] * aOther.mX[0] + mMain[3] * aOther.mX[1] + mX[1]; - newY[0] = mMain[0] * aOther.mY[0] + mMain[2] * aOther.mY[1] + mY[0]; - newY[1] = mMain[1] * aOther.mY[0] + mMain[3] * aOther.mY[1] + mY[1]; - - /* Now, write everything back in. */ - for (PRInt32 index = 0; index < 4; ++index) - mMain[index] = newMatrix[index]; - for (PRInt32 index = 0; index < 2; ++index) { - mDelta[index] = newDelta[index]; - mX[index] = newX[index]; - mY[index] = newY[index]; - } - - /* As promised, return a reference to ourselves. */ - return *this; -} - -/* op* is implemented in terms of op*=. */ -const nsStyleTransformMatrix -nsStyleTransformMatrix::operator *(const nsStyleTransformMatrix &aOther) const -{ - return nsStyleTransformMatrix(*this) *= aOther; -} - /* Helper function to fill in an nscoord with the specified nsCSSValue. */ static nscoord CalcLength(const nsCSSValue &aValue, nsStyleContext* aContext, nsPresContext* aPresContext, PRBool &aCanStoreInRuleTree) { if (aValue.GetUnit() == eCSSUnit_Pixel) { // Handle this here (even though nsRuleNode::CalcLength handles it @@ -223,239 +105,324 @@ static nscoord CalcLength(const nsCSSVal // nsStyleAnimation does) that all lengths within the transform // function have already been computed to pixels and percents. return nsPresContext::CSSPixelsToAppUnits(aValue.GetFloatValue()); } return nsRuleNode::CalcLength(aValue, aContext, aPresContext, aCanStoreInRuleTree); } -static void ProcessTranslatePart(nscoord& aOffset, float& aPercent, - const nsCSSValue& aValue, - nsStyleContext* aContext, - nsPresContext* aPresContext, - PRBool& aCanStoreInRuleTree) +static void +ProcessTranslatePart(float& aResult, + const nsCSSValue& aValue, + nsStyleContext* aContext, + nsPresContext* aPresContext, + PRBool& aCanStoreInRuleTree, + nscoord aSize, float aFactor) { + nscoord offset; + float percent; + if (aValue.GetUnit() == eCSSUnit_Percent) { - aPercent = aValue.GetPercentValue(); + percent = aValue.GetPercentValue(); } else if (aValue.IsCalcUnit()) { nsRuleNode::ComputedCalc result = nsRuleNode::SpecifiedCalcToComputedCalc(aValue, aContext, aPresContext, aCanStoreInRuleTree); - aPercent = result.mPercent; - aOffset = result.mLength; + percent = result.mPercent; + offset = result.mLength; } else { - aOffset = CalcLength(aValue, aContext, aPresContext, + offset = CalcLength(aValue, aContext, aPresContext, aCanStoreInRuleTree); } + + aResult = NSAppUnitsToFloatPixels(percent * aSize + offset, aFactor); } /* Helper function to process a matrix entry. */ -static void ProcessMatrix(float aMain[4], nscoord aDelta[2], - float aX[2], float aY[2], - const nsCSSValue::Array* aData, - nsStyleContext* aContext, - nsPresContext* aPresContext, - PRBool& aCanStoreInRuleTree) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessMatrix(const nsCSSValue::Array* aData, + nsStyleContext* aContext, + nsPresContext* aPresContext, + PRBool& aCanStoreInRuleTree, + nsRect& aBounds, float aFactor, + PRBool *aPercentX, PRBool *aPercentY) { NS_PRECONDITION(aData->Count() == 7, "Invalid array!"); + gfx3DMatrix temp; + /* Take the first four elements out of the array as floats and store - * them in aMain. + * them. */ - for (PRUint16 index = 1; index <= 4; ++index) - aMain[index - 1] = aData->Item(index).GetFloatValue(); + temp._11 = aData->Item(1).GetFloatValue(); + temp._12 = aData->Item(2).GetFloatValue(); + temp._21 = aData->Item(3).GetFloatValue(); + temp._22 = aData->Item(4).GetFloatValue(); /* The last two elements have their length parts stored in aDelta * and their percent parts stored in aX[0] and aY[1]. */ - ProcessTranslatePart(aDelta[0], aX[0], aData->Item(5), - aContext, aPresContext, aCanStoreInRuleTree); - ProcessTranslatePart(aDelta[1], aY[1], aData->Item(6), - aContext, aPresContext, aCanStoreInRuleTree); + ProcessTranslatePart(temp._41, aData->Item(5), + aContext, aPresContext, aCanStoreInRuleTree, + aBounds.Width(), aFactor); + ProcessTranslatePart(temp._42, aData->Item(6), + aContext, aPresContext, aCanStoreInRuleTree, + aBounds.Height(), aFactor); + + return temp; +} + +/* Helper function to process two matrices that we need to interpolate between */ +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessInterpolateMatrix(const nsCSSValue::Array* aData, + nsStyleContext* aContext, + nsPresContext* aPresContext, + PRBool& aCanStoreInRuleTree, + nsRect& aBounds, float aFactor) +{ + NS_PRECONDITION(aData->Count() == 5, "Invalid array!"); + + double coeff1 = aData->Item(1).GetFloatValue(); + gfxMatrix matrix1 = ReadTransforms(aData->Item(2).GetListValue(), + aContext, aPresContext, + aCanStoreInRuleTree, + aBounds, aFactor); + double coeff2 = aData->Item(3).GetFloatValue(); + gfxMatrix matrix2 = ReadTransforms(aData->Item(4).GetListValue(), + aContext, aPresContext, + aCanStoreInRuleTree, + aBounds, aFactor); + + nsCSSValueList* result = nsStyleAnimation::AddTransformMatrix(matrix1, coeff1, + matrix2, coeff2); + + return gfx3DMatrix::From2D(ReadTransforms(result, + aContext, aPresContext, + aCanStoreInRuleTree, + aBounds, aFactor)); } /* Helper function to process a translatex function. */ -static void ProcessTranslateX(nscoord aDelta[2], float aX[2], - const nsCSSValue::Array* aData, - nsStyleContext* aContext, - nsPresContext* aPresContext, - PRBool& aCanStoreInRuleTree) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessTranslateX(const nsCSSValue::Array* aData, + nsStyleContext* aContext, + nsPresContext* aPresContext, + PRBool& aCanStoreInRuleTree, + nsRect& aBounds, float aFactor) { NS_PRECONDITION(aData->Count() == 2, "Invalid array!"); + gfx3DMatrix temp; + /* There are two cases. If we have a number, we want our matrix to look * like this: * - * | 1 0 dx| - * | 0 1 0| - * | 0 0 1| + * | 1 0 0 0 | + * | 0 1 0 0 | + * | 0 0 1 0 | + * | dx 0 0 1 | * So E = value * * Otherwise, we might have a percentage, so we want to set the dX component * to the percent. */ - ProcessTranslatePart(aDelta[0], aX[0], aData->Item(1), - aContext, aPresContext, aCanStoreInRuleTree); + ProcessTranslatePart(temp._41, aData->Item(1), + aContext, aPresContext, aCanStoreInRuleTree, + aBounds.Width(), aFactor); + return temp; } /* Helper function to process a translatey function. */ -static void ProcessTranslateY(nscoord aDelta[2], float aY[2], - const nsCSSValue::Array* aData, - nsStyleContext* aContext, - nsPresContext* aPresContext, - PRBool& aCanStoreInRuleTree) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessTranslateY(const nsCSSValue::Array* aData, + nsStyleContext* aContext, + nsPresContext* aPresContext, + PRBool& aCanStoreInRuleTree, + nsRect& aBounds, float aFactor) { NS_PRECONDITION(aData->Count() == 2, "Invalid array!"); + gfx3DMatrix temp; + /* There are two cases. If we have a number, we want our matrix to look * like this: * - * | 1 0 0| - * | 0 1 dy| - * | 0 0 1| + * | 1 0 0 0 | + * | 0 1 0 0 | + * | 0 0 1 0 | + * | 0 dy 0 1 | * So E = value * * Otherwise, we might have a percentage, so we want to set the dY component * to the percent. */ - ProcessTranslatePart(aDelta[1], aY[1], aData->Item(1), - aContext, aPresContext, aCanStoreInRuleTree); + ProcessTranslatePart(temp._42, aData->Item(1), + aContext, aPresContext, aCanStoreInRuleTree, + aBounds.Height(), aFactor); + return temp; } /* Helper function to process a translate function. */ -static void ProcessTranslate(nscoord aDelta[2], float aX[2], float aY[2], - const nsCSSValue::Array* aData, - nsStyleContext* aContext, - nsPresContext* aPresContext, - PRBool& aCanStoreInRuleTree) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessTranslate(const nsCSSValue::Array* aData, + nsStyleContext* aContext, + nsPresContext* aPresContext, + PRBool& aCanStoreInRuleTree, + nsRect& aBounds, float aFactor) { NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Invalid array!"); + gfx3DMatrix temp; + /* There are several cases to consider. * First, we might have one value, or we might have two. If we have * two, we need to consider both dX and dY components. * Next, the values might be lengths, or they might be percents. If they're * percents, store them in the dX and dY components. Otherwise, store them in * the main matrix. */ - ProcessTranslatePart(aDelta[0], aX[0], aData->Item(1), - aContext, aPresContext, aCanStoreInRuleTree); + ProcessTranslatePart(temp._41, aData->Item(1), + aContext, aPresContext, aCanStoreInRuleTree, + aBounds.Width(), aFactor); /* If we read in a Y component, set it appropriately */ if (aData->Count() == 3) { - ProcessTranslatePart(aDelta[1], aY[1], aData->Item(2), - aContext, aPresContext, aCanStoreInRuleTree); + ProcessTranslatePart(temp._42, aData->Item(2), + aContext, aPresContext, aCanStoreInRuleTree, + aBounds.Height(), aFactor); } + return temp; } /* Helper function to set up a scale matrix. */ -static void ProcessScaleHelper(float aXScale, float aYScale, float aMain[4]) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessScaleHelper(float aXScale, float aYScale, float aZScale) { /* We want our matrix to look like this: - * | dx 0 0| - * | 0 dy 0| - * | 0 0 1| + * | dx 0 0 0 | + * | 0 dy 0 0 | + * | 0 0 dz 0 | + * | 0 0 0 1 | * So A = value */ - aMain[0] = aXScale; - aMain[3] = aYScale; + gfx3DMatrix temp; + temp._11 = aXScale; + temp._22 = aYScale; + temp._33 = aZScale; + return temp; } /* Process a scalex function. */ -static void ProcessScaleX(float aMain[4], const nsCSSValue::Array* aData) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessScaleX(const nsCSSValue::Array* aData) { NS_PRECONDITION(aData->Count() == 2, "Bad array!"); - ProcessScaleHelper(aData->Item(1).GetFloatValue(), 1.0f, aMain); + return ProcessScaleHelper(aData->Item(1).GetFloatValue(), 1.0f, 1.0f); } /* Process a scaley function. */ -static void ProcessScaleY(float aMain[4], const nsCSSValue::Array* aData) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessScaleY(const nsCSSValue::Array* aData) { NS_PRECONDITION(aData->Count() == 2, "Bad array!"); - ProcessScaleHelper(1.0f, aData->Item(1).GetFloatValue(), aMain); + return ProcessScaleHelper(1.0f, aData->Item(1).GetFloatValue(), 1.0f); } /* Process a scale function. */ -static void ProcessScale(float aMain[4], const nsCSSValue::Array* aData) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessScale(const nsCSSValue::Array* aData) { NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Bad array!"); /* We either have one element or two. If we have one, it's for both X and Y. * Otherwise it's one for each. */ const nsCSSValue& scaleX = aData->Item(1); const nsCSSValue& scaleY = (aData->Count() == 2 ? scaleX : aData->Item(2)); - ProcessScaleHelper(scaleX.GetFloatValue(), - scaleY.GetFloatValue(), aMain); + return ProcessScaleHelper(scaleX.GetFloatValue(), + scaleY.GetFloatValue(), + 1.0f); } /* Helper function that, given a set of angles, constructs the appropriate * skew matrix. */ -static void ProcessSkewHelper(double aXAngle, double aYAngle, float aMain[4]) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessSkewHelper(double aXAngle, double aYAngle) { /* We want our matrix to look like this: - * | 1 tan(ThetaX) 0| - * | tan(ThetaY) 1 0| - * | 0 0 1| + * | 1 tan(ThetaY) 0 0 | + * | tan(ThetaX) 1 0 0 | + * | 0 0 1 0 | + * | 0 0 0 1 | * However, to avoid infinite values, we'll use the SafeTangent function * instead of the C standard tan function. */ - aMain[2] = SafeTangent(aXAngle); - aMain[1] = SafeTangent(aYAngle); + gfx3DMatrix temp; + temp._12 = SafeTangent(aYAngle); + temp._21 = SafeTangent(aXAngle); + return temp; } /* Function that converts a skewx transform into a matrix. */ -static void ProcessSkewX(float aMain[4], const nsCSSValue::Array* aData) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessSkewX(const nsCSSValue::Array* aData) { NS_ASSERTION(aData->Count() == 2, "Bad array!"); - ProcessSkewHelper(aData->Item(1).GetAngleValueInRadians(), 0.0, aMain); + return ProcessSkewHelper(aData->Item(1).GetAngleValueInRadians(), 0.0); } /* Function that converts a skewy transform into a matrix. */ -static void ProcessSkewY(float aMain[4], const nsCSSValue::Array* aData) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessSkewY(const nsCSSValue::Array* aData) { NS_ASSERTION(aData->Count() == 2, "Bad array!"); - ProcessSkewHelper(0.0, aData->Item(1).GetAngleValueInRadians(), aMain); + return ProcessSkewHelper(0.0, aData->Item(1).GetAngleValueInRadians()); } /* Function that converts a skew transform into a matrix. */ -static void ProcessSkew(float aMain[4], const nsCSSValue::Array* aData) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessSkew(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(xSkew, ySkew, aMain); + return ProcessSkewHelper(xSkew, ySkew);; } /* Function that converts a rotate transform into a matrix. */ -static void ProcessRotate(float aMain[4], const nsCSSValue::Array* aData) +/* static */ gfx3DMatrix +nsStyleTransformMatrix::ProcessRotateZ(const nsCSSValue::Array* aData) { NS_PRECONDITION(aData->Count() == 2, "Invalid array!"); /* We want our matrix to look like this: - * | cos(theta) -sin(theta) 0| - * | sin(theta) cos(theta) 0| - * | 0 0 1| + * | cos(theta) sin(theta) 0 0 | + * | -sin(theta) cos(theta) 0 0 | + * | 0 0 1 0 | + * | 0 0 0 1 | * (see http://www.w3.org/TR/SVG/coords.html#RotationDefined) */ double theta = aData->Item(1).GetAngleValueInRadians(); float cosTheta = FlushToZero(cos(theta)); float sinTheta = FlushToZero(sin(theta)); - aMain[0] = cosTheta; - aMain[1] = sinTheta; - aMain[2] = -sinTheta; - aMain[3] = cosTheta; + gfx3DMatrix temp; + + temp._11 = cosTheta; + temp._12 = sinTheta; + temp._21 = -sinTheta; + temp._22 = cosTheta; + return temp; } /** * Return the transform function, as an nsCSSKeyword, for the given * nsCSSValue::Array from a transform list. */ /* static */ nsCSSKeyword nsStyleTransformMatrix::TransformFunctionOf(const nsCSSValue::Array* aData) @@ -464,119 +431,88 @@ nsStyleTransformMatrix::TransformFunctio aData->Item(0).GetStringValue(keyword); return nsCSSKeywords::LookupKeyword(keyword); } /** * SetToTransformFunction is essentially a giant switch statement that fans * out to many smaller helper functions. */ -void +gfx3DMatrix nsStyleTransformMatrix::SetToTransformFunction(const nsCSSValue::Array * aData, nsStyleContext* aContext, nsPresContext* aPresContext, - PRBool& aCanStoreInRuleTree) + PRBool& aCanStoreInRuleTree, + nsRect& aBounds, float aFactor) { NS_PRECONDITION(aData, "Why did you want to get data from a null array?"); // It's OK if aContext and aPresContext are null if the caller already // knows that all length units have been converted to pixels (as // nsStyleAnimation does). - /* Reset the matrix to the identity so that each subfunction can just - * worry about its own components. - */ - SetToIdentity(); /* Get the keyword for the transform. */ switch (TransformFunctionOf(aData)) { case eCSSKeyword_translatex: - ProcessTranslateX(mDelta, mX, aData, aContext, aPresContext, - aCanStoreInRuleTree); - break; + return ProcessTranslateX(aData, aContext, aPresContext, + aCanStoreInRuleTree, aBounds, aFactor); case eCSSKeyword_translatey: - ProcessTranslateY(mDelta, mY, aData, aContext, aPresContext, - aCanStoreInRuleTree); - break; + return ProcessTranslateY(aData, aContext, aPresContext, + aCanStoreInRuleTree, aBounds, aFactor); case eCSSKeyword_translate: - ProcessTranslate(mDelta, mX, mY, aData, aContext, aPresContext, - aCanStoreInRuleTree); - break; + return ProcessTranslate(aData, aContext, aPresContext, + aCanStoreInRuleTree, aBounds, aFactor); case eCSSKeyword_scalex: - ProcessScaleX(mMain, aData); - break; + return ProcessScaleX(aData); case eCSSKeyword_scaley: - ProcessScaleY(mMain, aData); - break; + return ProcessScaleY(aData); case eCSSKeyword_scale: - ProcessScale(mMain, aData); - break; + return ProcessScale(aData); case eCSSKeyword_skewx: - ProcessSkewX(mMain, aData); - break; + return ProcessSkewX(aData); case eCSSKeyword_skewy: - ProcessSkewY(mMain, aData); - break; + return ProcessSkewY(aData); case eCSSKeyword_skew: - ProcessSkew(mMain, aData); - break; + return ProcessSkew(aData); case eCSSKeyword_rotate: - ProcessRotate(mMain, aData); - break; + return ProcessRotateZ(aData); case eCSSKeyword_matrix: - ProcessMatrix(mMain, mDelta, mX, mY, aData, aContext, aPresContext, - aCanStoreInRuleTree); - break; + return ProcessMatrix(aData, aContext, aPresContext, + aCanStoreInRuleTree, aBounds, aFactor); + case eCSSKeyword_interpolatematrix: + return ProcessInterpolateMatrix(aData, aContext, aPresContext, + aCanStoreInRuleTree, aBounds, aFactor); default: NS_NOTREACHED("Unknown transform function!"); } + return gfx3DMatrix(); } -/* Given a -moz-transform token stream, accumulates them into an - * nsStyleTransformMatrix - * - * @param aList The nsCSSValueList of arrays to read into transform functions. - * @param aContext The style context to use for unit conversion. - * @param aPresContext The presentation context to use for unit conversion - * @param aCanStoreInRuleTree This is set to PR_FALSE if the value cannot be stored in the rule tree. - * @return An nsStyleTransformMatrix corresponding to the net transform. - */ -/* static */ nsStyleTransformMatrix +/* static */ gfxMatrix nsStyleTransformMatrix::ReadTransforms(const nsCSSValueList* aList, nsStyleContext* aContext, nsPresContext* aPresContext, - PRBool &aCanStoreInRuleTree) + PRBool &aCanStoreInRuleTree, + nsRect& aBounds, + float aFactor) { - nsStyleTransformMatrix result; + gfx3DMatrix result; for (const nsCSSValueList* curr = aList; curr != nsnull; curr = curr->mNext) { const nsCSSValue &currElem = curr->mValue; NS_ASSERTION(currElem.GetUnit() == eCSSUnit_Function, "Stream should consist solely of functions!"); NS_ASSERTION(currElem.GetArrayValue()->Count() >= 1, "Incoming function is too short!"); /* Read in a single transform matrix, then accumulate it with the total. */ - nsStyleTransformMatrix currMatrix; - currMatrix.SetToTransformFunction(currElem.GetArrayValue(), aContext, - aPresContext, aCanStoreInRuleTree); - result *= currMatrix; + result = SetToTransformFunction(currElem.GetArrayValue(), aContext, + aPresContext, aCanStoreInRuleTree, + aBounds, aFactor) * result; } - return result; + + gfxMatrix matrix2d; + if (!result.Is2D(&matrix2d)) { + NS_ERROR("Only 2D transforms are supported!"); + } + return matrix2d; } -/* Does an element-by-element comparison and returns whether or not the - * matrices are equal. - */ -PRBool -nsStyleTransformMatrix::operator ==(const nsStyleTransformMatrix &aOther) const -{ - for (PRInt32 index = 0; index < 4; ++index) - if (mMain[index] != aOther.mMain[index]) - return PR_FALSE; - - for (PRInt32 index = 0; index < 2; ++index) - if (mDelta[index] != aOther.mDelta[index] || - mX[index] != aOther.mX[index] || - mY[index] != aOther.mY[index]) - return PR_FALSE; - - return PR_TRUE; -} diff --git a/layout/style/nsStyleTransformMatrix.h b/layout/style/nsStyleTransformMatrix.h --- a/layout/style/nsStyleTransformMatrix.h +++ b/layout/style/nsStyleTransformMatrix.h @@ -14,16 +14,17 @@ * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Mozilla Corporation * * Contributor(s): * Keith Schwarz (original author) + * Matt Woodrow * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -38,179 +39,102 @@ * A class representing three matrices that can be used for style transforms. */ #ifndef nsStyleTransformMatrix_h_ #define nsStyleTransformMatrix_h_ #include "nsCSSValue.h" #include "gfxMatrix.h" +#include "gfx3DMatrix.h" #include "nsRect.h" struct nsCSSValueList; /** - * A class representing a style transformation matrix. The class actually - * wraps three different matrices, a constant matrix and two matrices - * whose values are scaled by the width and the height of the bounding - * rectangle for the object to transform. Thus, given a frame rectangle - * of dimensions (width, height) and a point (x, y) to transform, the matrix - * corresponds to the transform operation - * - * | a c e | |0 0 dX1| |0 0 dY1| | x | - *(| b d f | + |0 0 dX2| (width) + |0 0 dY2| (height)) | y | - * | 0 0 1 | |0 0 0| |0 0 0| | 1 | - * - * Note that unlike the Thebes gfxMatrix, vectors are column vectors and - * consequently the multiplication of a matrix A and a vector x is Ax, not xA. + * A helper class to generate gfxMatrixes from css transform functions. */ class nsStyleContext; class nsPresContext; class nsStyleTransformMatrix { public: - /** - * Constructor sets the matrix to the identity. - */ - nsStyleTransformMatrix(); - - /** - * Given a frame's bounding rectangle, returns a gfxMatrix - * corresponding to the transformation represented by this - * matrix. The transformation takes points in the frame's - * local space and converts them to points in the frame's - * transformed space. - * - * @param aBounds The frame's bounding rectangle. - * @param aFactor The number of app units per device pixel. - * @return A Thebes matrix corresponding to the transform. - */ - gfxMatrix GetThebesMatrix(const nsRect& aBounds, float aFactor) const; - - /** - * Multiplies this matrix by another matrix, in that order. If A' - * is the value of A after A *= B, then for any vector x, the - * equivalence A'(x) == A(B(x)) holds. - * - * @param aOther The matrix to multiply this matrix by. - * @return A reference to this matrix. - */ - nsStyleTransformMatrix& operator *= (const nsStyleTransformMatrix &aOther); - - /** - * Returns a new nsStyleTransformMatrix that is equal to one matrix - * multiplied by another matrix, in that order. If C is the result of - * A * B, then for any vector x, the equivalence C(x) = A(B(x)). - * - * @param aOther The matrix to multiply this matrix by. - * @return A new nsStyleTransformMatrix equal to this matrix multiplied - * by the other matrix. - */ - const nsStyleTransformMatrix - operator * (const nsStyleTransformMatrix &aOther) const; /** * Return the transform function, as an nsCSSKeyword, for the given * nsCSSValue::Array from a transform list. */ static nsCSSKeyword TransformFunctionOf(const nsCSSValue::Array* aData); /** * Given an nsCSSValue::Array* containing a -moz-transform function, - * updates this matrix to hold the value of that function. + * returns a matrix containing the value of that function. * * @param aData The nsCSSValue::Array* containing the transform function. * @param aContext The style context, used for unit conversion. * @param aPresContext The presentation context, used for unit conversion. * @param aCanStoreInRuleTree Set to false if the result cannot be cached * in the rule tree, otherwise untouched. + * @param aBounds The frame's bounding rectangle. + * @param aFactor The number of app units per device pixel. + * * * aContext and aPresContext may be null if all of the (non-percent) * length values in aData are already known to have been converted to * eCSSUnit_Pixel (as they are in an nsStyleAnimation::Value) */ - void SetToTransformFunction(const nsCSSValue::Array* aData, - nsStyleContext* aContext, - nsPresContext* aPresContext, - PRBool& aCanStoreInRuleTree); + static gfx3DMatrix SetToTransformFunction(const nsCSSValue::Array* aData, + nsStyleContext* aContext, + nsPresContext* aPresContext, + PRBool& aCanStoreInRuleTree, + nsRect& aBounds, float aFactor); /** * The same as SetToTransformFunction, but for a list of transform * functions. */ - static nsStyleTransformMatrix ReadTransforms(const nsCSSValueList* aList, - nsStyleContext* aContext, - nsPresContext* aPresContext, - PRBool &aCanStoreInRuleTree); - /** - * Sets this matrix to be the identity matrix. - */ - void SetToIdentity(); - - /** - * Returns the value of the entry at the 2x2 submatrix of the - * transform matrix that defines the non-affine linear transform. - * The order is given as - * |elem[0] elem[2]| - * |elem[1] elem[3]| - * - * @param aIndex The element index. - * @return The value of the element at that index. - */ - float GetMainMatrixEntry(PRInt32 aIndex) const - { - NS_PRECONDITION(aIndex >= 0 && aIndex < 4, "Index out of bounds!"); - return mMain[aIndex]; - } - - /** - * Returns the value of the X or Y translation component of the matrix, - * given the specified bounds. - * - * @param aBounds The bounds of the element. - * @return The value of the X or Ytranslation component. - */ - nscoord GetXTranslation(const nsRect& aBounds) const; - nscoord GetYTranslation(const nsRect& aBounds) const; - - /** - * Get the raw components used for GetXTranslation and GetYTranslation. - */ - nscoord GetCoordXTranslation() const { return mDelta[0]; } - nscoord GetCoordYTranslation() const { return mDelta[1]; } - float GetWidthRelativeXTranslation() const { return mX[0]; } - float GetWidthRelativeYTranslation() const { return mX[1]; } - float GetHeightRelativeXTranslation() const { return mY[0]; } - float GetHeightRelativeYTranslation() const { return mY[1]; } - - /** - * Returns whether the two matrices are equal or not. - * - * @param aOther The matrix to compare to. - * @return Whether the two matrices are equal. - */ - PRBool operator== (const nsStyleTransformMatrix& aOther) const; - PRBool operator!= (const nsStyleTransformMatrix& aOther) const - { - return !(*this == aOther); - } + static gfxMatrix ReadTransforms(const nsCSSValueList* aList, + nsStyleContext* aContext, + nsPresContext* aPresContext, + PRBool &aCanStoreInRuleTree, + nsRect& aBounds, + float aFactor); private: - /* The three matrices look like this: - * |mMain[0] mMain[2] mDelta[0]| - * |mMain[1] mMain[3] mDelta[1]| <-- Constant matrix - * | 0 0 1| - * - * | 0 0 mX[0]| - * | 0 0 mX[1]| <-- Scaled by width of element - * | 0 0 0| - * - * | 0 0 mY[0]| - * | 0 0 mY[1]| <-- Scaled by height of element - * | 0 0 0| - */ - float mMain[4]; - nscoord mDelta[2]; - float mX[2]; - float mY[2]; + static gfx3DMatrix ProcessMatrix(const nsCSSValue::Array *aData, + nsStyleContext *aContext, + nsPresContext *aPresContext, + PRBool &aCanStoreInRuleTree, + nsRect& aBounds, float aFactor, + PRBool *aPercentX = nsnull, + PRBool *aPercentY = nsnull); + static gfx3DMatrix ProcessInterpolateMatrix(const nsCSSValue::Array *aData, + nsStyleContext *aContext, + nsPresContext *aPresContext, + PRBool &aCanStoreInRuleTree, + nsRect& aBounds, float aFactor); + static gfx3DMatrix ProcessTranslateX(const nsCSSValue::Array *aData, + nsStyleContext *aContext, + nsPresContext *aPresContext, + PRBool &aCanStoreInRuleTree, + nsRect& aBounds, float aFactor); + static gfx3DMatrix ProcessTranslateY(const nsCSSValue::Array *aData, + nsStyleContext *aContext, + nsPresContext *aPresContext, + PRBool &aCanStoreInRuleTree, + nsRect& aBounds, float aFactor); + static gfx3DMatrix ProcessTranslate(const nsCSSValue::Array *aData, + nsStyleContext *aContext, + nsPresContext *aPresContext, + PRBool &aCanStoreInRuleTree, + nsRect& aBounds, float aFactor); + static gfx3DMatrix ProcessScaleHelper(float aXScale, float aYScale, float aZScale); + static gfx3DMatrix ProcessScaleX(const nsCSSValue::Array *aData); + static gfx3DMatrix ProcessScaleY(const nsCSSValue::Array *aData); + static gfx3DMatrix ProcessScale(const nsCSSValue::Array *aData); + static gfx3DMatrix ProcessSkewHelper(double aXAngle, double aYAngle); + static gfx3DMatrix ProcessSkewX(const nsCSSValue::Array *aData); + static gfx3DMatrix ProcessSkewY(const nsCSSValue::Array *aData); + static gfx3DMatrix ProcessSkew(const nsCSSValue::Array *aData); + static gfx3DMatrix ProcessRotateZ(const nsCSSValue::Array *aData); }; #endif