layout/style/nsStyleTransformMatrix.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /*
michael@0 7 * A class used for intermediate representations of the -moz-transform property.
michael@0 8 */
michael@0 9
michael@0 10 #include "nsStyleTransformMatrix.h"
michael@0 11 #include "nsCSSValue.h"
michael@0 12 #include "nsPresContext.h"
michael@0 13 #include "nsRuleNode.h"
michael@0 14 #include "nsCSSKeywords.h"
michael@0 15 #include "nsStyleAnimation.h"
michael@0 16 #include "gfxMatrix.h"
michael@0 17
michael@0 18 using namespace mozilla;
michael@0 19
michael@0 20 namespace nsStyleTransformMatrix {
michael@0 21
michael@0 22 /* Note on floating point precision: The transform matrix is an array
michael@0 23 * of single precision 'float's, and so are most of the input values
michael@0 24 * we get from the style system, but intermediate calculations
michael@0 25 * involving angles need to be done in 'double'.
michael@0 26 */
michael@0 27
michael@0 28 /* Force small values to zero. We do this to avoid having sin(360deg)
michael@0 29 * evaluate to a tiny but nonzero value.
michael@0 30 */
michael@0 31 static double FlushToZero(double aVal)
michael@0 32 {
michael@0 33 if (-FLT_EPSILON < aVal && aVal < FLT_EPSILON)
michael@0 34 return 0.0f;
michael@0 35 else
michael@0 36 return aVal;
michael@0 37 }
michael@0 38
michael@0 39 float
michael@0 40 ProcessTranslatePart(const nsCSSValue& aValue,
michael@0 41 nsStyleContext* aContext,
michael@0 42 nsPresContext* aPresContext,
michael@0 43 bool& aCanStoreInRuleTree,
michael@0 44 nscoord aSize,
michael@0 45 float aAppUnitsPerMatrixUnit)
michael@0 46 {
michael@0 47 nscoord offset = 0;
michael@0 48 float percent = 0.0f;
michael@0 49
michael@0 50 if (aValue.GetUnit() == eCSSUnit_Percent) {
michael@0 51 percent = aValue.GetPercentValue();
michael@0 52 } else if (aValue.GetUnit() == eCSSUnit_Pixel ||
michael@0 53 aValue.GetUnit() == eCSSUnit_Number) {
michael@0 54 // Handle this here (even though nsRuleNode::CalcLength handles it
michael@0 55 // fine) so that callers are allowed to pass a null style context
michael@0 56 // and pres context to SetToTransformFunction if they know (as
michael@0 57 // nsStyleAnimation does) that all lengths within the transform
michael@0 58 // function have already been computed to pixels and percents.
michael@0 59 //
michael@0 60 // Raw numbers are treated as being pixels.
michael@0 61 //
michael@0 62 // Don't convert to aValue to AppUnits here to avoid precision issues.
michael@0 63 return aValue.GetFloatValue() *
michael@0 64 (float(nsPresContext::AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit);
michael@0 65 } else if (aValue.IsCalcUnit()) {
michael@0 66 nsRuleNode::ComputedCalc result =
michael@0 67 nsRuleNode::SpecifiedCalcToComputedCalc(aValue, aContext, aPresContext,
michael@0 68 aCanStoreInRuleTree);
michael@0 69 percent = result.mPercent;
michael@0 70 offset = result.mLength;
michael@0 71 } else {
michael@0 72 offset = nsRuleNode::CalcLength(aValue, aContext, aPresContext,
michael@0 73 aCanStoreInRuleTree);
michael@0 74 }
michael@0 75
michael@0 76 return (percent * NSAppUnitsToFloatPixels(aSize, aAppUnitsPerMatrixUnit)) +
michael@0 77 NSAppUnitsToFloatPixels(offset, aAppUnitsPerMatrixUnit);
michael@0 78 }
michael@0 79
michael@0 80 /**
michael@0 81 * Helper functions to process all the transformation function types.
michael@0 82 *
michael@0 83 * These take a matrix parameter to accumulate the current matrix.
michael@0 84 */
michael@0 85
michael@0 86 /* Helper function to process a matrix entry. */
michael@0 87 static void
michael@0 88 ProcessMatrix(gfx3DMatrix& aMatrix,
michael@0 89 const nsCSSValue::Array* aData,
michael@0 90 nsStyleContext* aContext,
michael@0 91 nsPresContext* aPresContext,
michael@0 92 bool& aCanStoreInRuleTree,
michael@0 93 nsRect& aBounds, float aAppUnitsPerMatrixUnit)
michael@0 94 {
michael@0 95 NS_PRECONDITION(aData->Count() == 7, "Invalid array!");
michael@0 96
michael@0 97 gfxMatrix result;
michael@0 98
michael@0 99 /* Take the first four elements out of the array as floats and store
michael@0 100 * them.
michael@0 101 */
michael@0 102 result.xx = aData->Item(1).GetFloatValue();
michael@0 103 result.yx = aData->Item(2).GetFloatValue();
michael@0 104 result.xy = aData->Item(3).GetFloatValue();
michael@0 105 result.yy = aData->Item(4).GetFloatValue();
michael@0 106
michael@0 107 /* The last two elements have their length parts stored in aDelta
michael@0 108 * and their percent parts stored in aX[0] and aY[1].
michael@0 109 */
michael@0 110 result.x0 = ProcessTranslatePart(aData->Item(5),
michael@0 111 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 112 aBounds.Width(), aAppUnitsPerMatrixUnit);
michael@0 113 result.y0 = ProcessTranslatePart(aData->Item(6),
michael@0 114 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 115 aBounds.Height(), aAppUnitsPerMatrixUnit);
michael@0 116
michael@0 117 aMatrix.PreMultiply(result);
michael@0 118 }
michael@0 119
michael@0 120 static void
michael@0 121 ProcessMatrix3D(gfx3DMatrix& aMatrix,
michael@0 122 const nsCSSValue::Array* aData,
michael@0 123 nsStyleContext* aContext,
michael@0 124 nsPresContext* aPresContext,
michael@0 125 bool& aCanStoreInRuleTree,
michael@0 126 nsRect& aBounds, float aAppUnitsPerMatrixUnit)
michael@0 127 {
michael@0 128 NS_PRECONDITION(aData->Count() == 17, "Invalid array!");
michael@0 129
michael@0 130 gfx3DMatrix temp;
michael@0 131
michael@0 132 temp._11 = aData->Item(1).GetFloatValue();
michael@0 133 temp._12 = aData->Item(2).GetFloatValue();
michael@0 134 temp._13 = aData->Item(3).GetFloatValue();
michael@0 135 temp._14 = aData->Item(4).GetFloatValue();
michael@0 136 temp._21 = aData->Item(5).GetFloatValue();
michael@0 137 temp._22 = aData->Item(6).GetFloatValue();
michael@0 138 temp._23 = aData->Item(7).GetFloatValue();
michael@0 139 temp._24 = aData->Item(8).GetFloatValue();
michael@0 140 temp._31 = aData->Item(9).GetFloatValue();
michael@0 141 temp._32 = aData->Item(10).GetFloatValue();
michael@0 142 temp._33 = aData->Item(11).GetFloatValue();
michael@0 143 temp._34 = aData->Item(12).GetFloatValue();
michael@0 144 temp._44 = aData->Item(16).GetFloatValue();
michael@0 145
michael@0 146 temp._41 = ProcessTranslatePart(aData->Item(13),
michael@0 147 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 148 aBounds.Width(), aAppUnitsPerMatrixUnit);
michael@0 149 temp._42 = ProcessTranslatePart(aData->Item(14),
michael@0 150 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 151 aBounds.Height(), aAppUnitsPerMatrixUnit);
michael@0 152 temp._43 = ProcessTranslatePart(aData->Item(15),
michael@0 153 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 154 aBounds.Height(), aAppUnitsPerMatrixUnit);
michael@0 155
michael@0 156 aMatrix.PreMultiply(temp);
michael@0 157 }
michael@0 158
michael@0 159 /* Helper function to process two matrices that we need to interpolate between */
michael@0 160 void
michael@0 161 ProcessInterpolateMatrix(gfx3DMatrix& aMatrix,
michael@0 162 const nsCSSValue::Array* aData,
michael@0 163 nsStyleContext* aContext,
michael@0 164 nsPresContext* aPresContext,
michael@0 165 bool& aCanStoreInRuleTree,
michael@0 166 nsRect& aBounds, float aAppUnitsPerMatrixUnit)
michael@0 167 {
michael@0 168 NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
michael@0 169
michael@0 170 gfx3DMatrix matrix1, matrix2;
michael@0 171 if (aData->Item(1).GetUnit() == eCSSUnit_List) {
michael@0 172 matrix1 = nsStyleTransformMatrix::ReadTransforms(aData->Item(1).GetListValue(),
michael@0 173 aContext, aPresContext,
michael@0 174 aCanStoreInRuleTree,
michael@0 175 aBounds, aAppUnitsPerMatrixUnit);
michael@0 176 }
michael@0 177 if (aData->Item(2).GetUnit() == eCSSUnit_List) {
michael@0 178 matrix2 = ReadTransforms(aData->Item(2).GetListValue(),
michael@0 179 aContext, aPresContext,
michael@0 180 aCanStoreInRuleTree,
michael@0 181 aBounds, aAppUnitsPerMatrixUnit);
michael@0 182 }
michael@0 183 double progress = aData->Item(3).GetPercentValue();
michael@0 184
michael@0 185 aMatrix = nsStyleAnimation::InterpolateTransformMatrix(matrix1, matrix2, progress) * aMatrix;
michael@0 186 }
michael@0 187
michael@0 188 /* Helper function to process a translatex function. */
michael@0 189 static void
michael@0 190 ProcessTranslateX(gfx3DMatrix& aMatrix,
michael@0 191 const nsCSSValue::Array* aData,
michael@0 192 nsStyleContext* aContext,
michael@0 193 nsPresContext* aPresContext,
michael@0 194 bool& aCanStoreInRuleTree,
michael@0 195 nsRect& aBounds, float aAppUnitsPerMatrixUnit)
michael@0 196 {
michael@0 197 NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
michael@0 198
michael@0 199 gfxPoint3D temp;
michael@0 200
michael@0 201 temp.x = ProcessTranslatePart(aData->Item(1),
michael@0 202 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 203 aBounds.Width(), aAppUnitsPerMatrixUnit);
michael@0 204 aMatrix.Translate(temp);
michael@0 205 }
michael@0 206
michael@0 207 /* Helper function to process a translatey function. */
michael@0 208 static void
michael@0 209 ProcessTranslateY(gfx3DMatrix& aMatrix,
michael@0 210 const nsCSSValue::Array* aData,
michael@0 211 nsStyleContext* aContext,
michael@0 212 nsPresContext* aPresContext,
michael@0 213 bool& aCanStoreInRuleTree,
michael@0 214 nsRect& aBounds, float aAppUnitsPerMatrixUnit)
michael@0 215 {
michael@0 216 NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
michael@0 217
michael@0 218 gfxPoint3D temp;
michael@0 219
michael@0 220 temp.y = ProcessTranslatePart(aData->Item(1),
michael@0 221 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 222 aBounds.Height(), aAppUnitsPerMatrixUnit);
michael@0 223 aMatrix.Translate(temp);
michael@0 224 }
michael@0 225
michael@0 226 static void
michael@0 227 ProcessTranslateZ(gfx3DMatrix& aMatrix,
michael@0 228 const nsCSSValue::Array* aData,
michael@0 229 nsStyleContext* aContext,
michael@0 230 nsPresContext* aPresContext,
michael@0 231 bool& aCanStoreInRuleTree,
michael@0 232 float aAppUnitsPerMatrixUnit)
michael@0 233 {
michael@0 234 NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
michael@0 235
michael@0 236 gfxPoint3D temp;
michael@0 237
michael@0 238 temp.z = ProcessTranslatePart(aData->Item(1),
michael@0 239 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 240 0, aAppUnitsPerMatrixUnit);
michael@0 241 aMatrix.Translate(temp);
michael@0 242 }
michael@0 243
michael@0 244 /* Helper function to process a translate function. */
michael@0 245 static void
michael@0 246 ProcessTranslate(gfx3DMatrix& aMatrix,
michael@0 247 const nsCSSValue::Array* aData,
michael@0 248 nsStyleContext* aContext,
michael@0 249 nsPresContext* aPresContext,
michael@0 250 bool& aCanStoreInRuleTree,
michael@0 251 nsRect& aBounds, float aAppUnitsPerMatrixUnit)
michael@0 252 {
michael@0 253 NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Invalid array!");
michael@0 254
michael@0 255 gfxPoint3D temp;
michael@0 256
michael@0 257 temp.x = ProcessTranslatePart(aData->Item(1),
michael@0 258 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 259 aBounds.Width(), aAppUnitsPerMatrixUnit);
michael@0 260
michael@0 261 /* If we read in a Y component, set it appropriately */
michael@0 262 if (aData->Count() == 3) {
michael@0 263 temp.y = ProcessTranslatePart(aData->Item(2),
michael@0 264 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 265 aBounds.Height(), aAppUnitsPerMatrixUnit);
michael@0 266 }
michael@0 267 aMatrix.Translate(temp);
michael@0 268 }
michael@0 269
michael@0 270 static void
michael@0 271 ProcessTranslate3D(gfx3DMatrix& aMatrix,
michael@0 272 const nsCSSValue::Array* aData,
michael@0 273 nsStyleContext* aContext,
michael@0 274 nsPresContext* aPresContext,
michael@0 275 bool& aCanStoreInRuleTree,
michael@0 276 nsRect& aBounds, float aAppUnitsPerMatrixUnit)
michael@0 277 {
michael@0 278 NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
michael@0 279
michael@0 280 gfxPoint3D temp;
michael@0 281
michael@0 282 temp.x = ProcessTranslatePart(aData->Item(1),
michael@0 283 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 284 aBounds.Width(), aAppUnitsPerMatrixUnit);
michael@0 285
michael@0 286 temp.y = ProcessTranslatePart(aData->Item(2),
michael@0 287 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 288 aBounds.Height(), aAppUnitsPerMatrixUnit);
michael@0 289
michael@0 290 temp.z = ProcessTranslatePart(aData->Item(3),
michael@0 291 aContext, aPresContext, aCanStoreInRuleTree,
michael@0 292 0, aAppUnitsPerMatrixUnit);
michael@0 293
michael@0 294 aMatrix.Translate(temp);
michael@0 295 }
michael@0 296
michael@0 297 /* Helper function to set up a scale matrix. */
michael@0 298 static void
michael@0 299 ProcessScaleHelper(gfx3DMatrix& aMatrix,
michael@0 300 float aXScale,
michael@0 301 float aYScale,
michael@0 302 float aZScale)
michael@0 303 {
michael@0 304 aMatrix.Scale(aXScale, aYScale, aZScale);
michael@0 305 }
michael@0 306
michael@0 307 /* Process a scalex function. */
michael@0 308 static void
michael@0 309 ProcessScaleX(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 310 {
michael@0 311 NS_PRECONDITION(aData->Count() == 2, "Bad array!");
michael@0 312 ProcessScaleHelper(aMatrix, aData->Item(1).GetFloatValue(), 1.0f, 1.0f);
michael@0 313 }
michael@0 314
michael@0 315 /* Process a scaley function. */
michael@0 316 static void
michael@0 317 ProcessScaleY(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 318 {
michael@0 319 NS_PRECONDITION(aData->Count() == 2, "Bad array!");
michael@0 320 ProcessScaleHelper(aMatrix, 1.0f, aData->Item(1).GetFloatValue(), 1.0f);
michael@0 321 }
michael@0 322
michael@0 323 static void
michael@0 324 ProcessScaleZ(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 325 {
michael@0 326 NS_PRECONDITION(aData->Count() == 2, "Bad array!");
michael@0 327 ProcessScaleHelper(aMatrix, 1.0f, 1.0f, aData->Item(1).GetFloatValue());
michael@0 328 }
michael@0 329
michael@0 330 static void
michael@0 331 ProcessScale3D(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 332 {
michael@0 333 NS_PRECONDITION(aData->Count() == 4, "Bad array!");
michael@0 334 ProcessScaleHelper(aMatrix,
michael@0 335 aData->Item(1).GetFloatValue(),
michael@0 336 aData->Item(2).GetFloatValue(),
michael@0 337 aData->Item(3).GetFloatValue());
michael@0 338 }
michael@0 339
michael@0 340 /* Process a scale function. */
michael@0 341 static void
michael@0 342 ProcessScale(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 343 {
michael@0 344 NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
michael@0 345 /* We either have one element or two. If we have one, it's for both X and Y.
michael@0 346 * Otherwise it's one for each.
michael@0 347 */
michael@0 348 const nsCSSValue& scaleX = aData->Item(1);
michael@0 349 const nsCSSValue& scaleY = (aData->Count() == 2 ? scaleX :
michael@0 350 aData->Item(2));
michael@0 351
michael@0 352 ProcessScaleHelper(aMatrix,
michael@0 353 scaleX.GetFloatValue(),
michael@0 354 scaleY.GetFloatValue(),
michael@0 355 1.0f);
michael@0 356 }
michael@0 357
michael@0 358 /* Helper function that, given a set of angles, constructs the appropriate
michael@0 359 * skew matrix.
michael@0 360 */
michael@0 361 static void
michael@0 362 ProcessSkewHelper(gfx3DMatrix& aMatrix, double aXAngle, double aYAngle)
michael@0 363 {
michael@0 364 aMatrix.SkewXY(aXAngle, aYAngle);
michael@0 365 }
michael@0 366
michael@0 367 /* Function that converts a skewx transform into a matrix. */
michael@0 368 static void
michael@0 369 ProcessSkewX(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 370 {
michael@0 371 NS_ASSERTION(aData->Count() == 2, "Bad array!");
michael@0 372 ProcessSkewHelper(aMatrix, aData->Item(1).GetAngleValueInRadians(), 0.0);
michael@0 373 }
michael@0 374
michael@0 375 /* Function that converts a skewy transform into a matrix. */
michael@0 376 static void
michael@0 377 ProcessSkewY(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 378 {
michael@0 379 NS_ASSERTION(aData->Count() == 2, "Bad array!");
michael@0 380 ProcessSkewHelper(aMatrix, 0.0, aData->Item(1).GetAngleValueInRadians());
michael@0 381 }
michael@0 382
michael@0 383 /* Function that converts a skew transform into a matrix. */
michael@0 384 static void
michael@0 385 ProcessSkew(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 386 {
michael@0 387 NS_ASSERTION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
michael@0 388
michael@0 389 double xSkew = aData->Item(1).GetAngleValueInRadians();
michael@0 390 double ySkew = (aData->Count() == 2
michael@0 391 ? 0.0 : aData->Item(2).GetAngleValueInRadians());
michael@0 392
michael@0 393 ProcessSkewHelper(aMatrix, xSkew, ySkew);
michael@0 394 }
michael@0 395
michael@0 396 /* Function that converts a rotate transform into a matrix. */
michael@0 397 static void
michael@0 398 ProcessRotateZ(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 399 {
michael@0 400 NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
michael@0 401 double theta = aData->Item(1).GetAngleValueInRadians();
michael@0 402 aMatrix.RotateZ(theta);
michael@0 403 }
michael@0 404
michael@0 405 static void
michael@0 406 ProcessRotateX(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 407 {
michael@0 408 NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
michael@0 409 double theta = aData->Item(1).GetAngleValueInRadians();
michael@0 410 aMatrix.RotateX(theta);
michael@0 411 }
michael@0 412
michael@0 413 static void
michael@0 414 ProcessRotateY(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 415 {
michael@0 416 NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
michael@0 417 double theta = aData->Item(1).GetAngleValueInRadians();
michael@0 418 aMatrix.RotateY(theta);
michael@0 419 }
michael@0 420
michael@0 421 static void
michael@0 422 ProcessRotate3D(gfx3DMatrix& aMatrix, const nsCSSValue::Array* aData)
michael@0 423 {
michael@0 424 NS_PRECONDITION(aData->Count() == 5, "Invalid array!");
michael@0 425
michael@0 426 /* We want our matrix to look like this:
michael@0 427 * | 1 + (1-cos(angle))*(x*x-1) -z*sin(angle)+(1-cos(angle))*x*y y*sin(angle)+(1-cos(angle))*x*z 0 |
michael@0 428 * | z*sin(angle)+(1-cos(angle))*x*y 1 + (1-cos(angle))*(y*y-1) -x*sin(angle)+(1-cos(angle))*y*z 0 |
michael@0 429 * | -y*sin(angle)+(1-cos(angle))*x*z x*sin(angle)+(1-cos(angle))*y*z 1 + (1-cos(angle))*(z*z-1) 0 |
michael@0 430 * | 0 0 0 1 |
michael@0 431 * (see http://www.w3.org/TR/css3-3d-transforms/#transform-functions)
michael@0 432 */
michael@0 433
michael@0 434 /* The current spec specifies a matrix that rotates in the wrong direction. For now we just negate
michael@0 435 * the angle provided to get the correct rotation direction until the spec is updated.
michael@0 436 * See bug 704468.
michael@0 437 */
michael@0 438 double theta = -aData->Item(4).GetAngleValueInRadians();
michael@0 439 float cosTheta = FlushToZero(cos(theta));
michael@0 440 float sinTheta = FlushToZero(sin(theta));
michael@0 441
michael@0 442 float x = aData->Item(1).GetFloatValue();
michael@0 443 float y = aData->Item(2).GetFloatValue();
michael@0 444 float z = aData->Item(3).GetFloatValue();
michael@0 445
michael@0 446 /* Normalize [x,y,z] */
michael@0 447 float length = sqrt(x*x + y*y + z*z);
michael@0 448 if (length == 0.0) {
michael@0 449 return;
michael@0 450 }
michael@0 451 x /= length;
michael@0 452 y /= length;
michael@0 453 z /= length;
michael@0 454
michael@0 455 gfx3DMatrix temp;
michael@0 456
michael@0 457 /* Create our matrix */
michael@0 458 temp._11 = 1 + (1 - cosTheta) * (x * x - 1);
michael@0 459 temp._12 = -z * sinTheta + (1 - cosTheta) * x * y;
michael@0 460 temp._13 = y * sinTheta + (1 - cosTheta) * x * z;
michael@0 461 temp._14 = 0.0f;
michael@0 462 temp._21 = z * sinTheta + (1 - cosTheta) * x * y;
michael@0 463 temp._22 = 1 + (1 - cosTheta) * (y * y - 1);
michael@0 464 temp._23 = -x * sinTheta + (1 - cosTheta) * y * z;
michael@0 465 temp._24 = 0.0f;
michael@0 466 temp._31 = -y * sinTheta + (1 - cosTheta) * x * z;
michael@0 467 temp._32 = x * sinTheta + (1 - cosTheta) * y * z;
michael@0 468 temp._33 = 1 + (1 - cosTheta) * (z * z - 1);
michael@0 469 temp._34 = 0.0f;
michael@0 470 temp._41 = 0.0f;
michael@0 471 temp._42 = 0.0f;
michael@0 472 temp._43 = 0.0f;
michael@0 473 temp._44 = 1.0f;
michael@0 474
michael@0 475 aMatrix = temp * aMatrix;
michael@0 476 }
michael@0 477
michael@0 478 static void
michael@0 479 ProcessPerspective(gfx3DMatrix& aMatrix,
michael@0 480 const nsCSSValue::Array* aData,
michael@0 481 nsStyleContext *aContext,
michael@0 482 nsPresContext *aPresContext,
michael@0 483 bool &aCanStoreInRuleTree,
michael@0 484 float aAppUnitsPerMatrixUnit)
michael@0 485 {
michael@0 486 NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
michael@0 487
michael@0 488 float depth = ProcessTranslatePart(aData->Item(1), aContext,
michael@0 489 aPresContext, aCanStoreInRuleTree,
michael@0 490 0, aAppUnitsPerMatrixUnit);
michael@0 491 aMatrix.Perspective(depth);
michael@0 492 }
michael@0 493
michael@0 494
michael@0 495 /**
michael@0 496 * SetToTransformFunction is essentially a giant switch statement that fans
michael@0 497 * out to many smaller helper functions.
michael@0 498 */
michael@0 499 static void
michael@0 500 MatrixForTransformFunction(gfx3DMatrix& aMatrix,
michael@0 501 const nsCSSValue::Array * aData,
michael@0 502 nsStyleContext* aContext,
michael@0 503 nsPresContext* aPresContext,
michael@0 504 bool& aCanStoreInRuleTree,
michael@0 505 nsRect& aBounds,
michael@0 506 float aAppUnitsPerMatrixUnit)
michael@0 507 {
michael@0 508 NS_PRECONDITION(aData, "Why did you want to get data from a null array?");
michael@0 509 // It's OK if aContext and aPresContext are null if the caller already
michael@0 510 // knows that all length units have been converted to pixels (as
michael@0 511 // nsStyleAnimation does).
michael@0 512
michael@0 513
michael@0 514 /* Get the keyword for the transform. */
michael@0 515 switch (TransformFunctionOf(aData)) {
michael@0 516 case eCSSKeyword_translatex:
michael@0 517 ProcessTranslateX(aMatrix, aData, aContext, aPresContext,
michael@0 518 aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
michael@0 519 break;
michael@0 520 case eCSSKeyword_translatey:
michael@0 521 ProcessTranslateY(aMatrix, aData, aContext, aPresContext,
michael@0 522 aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
michael@0 523 break;
michael@0 524 case eCSSKeyword_translatez:
michael@0 525 ProcessTranslateZ(aMatrix, aData, aContext, aPresContext,
michael@0 526 aCanStoreInRuleTree, aAppUnitsPerMatrixUnit);
michael@0 527 break;
michael@0 528 case eCSSKeyword_translate:
michael@0 529 ProcessTranslate(aMatrix, aData, aContext, aPresContext,
michael@0 530 aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
michael@0 531 break;
michael@0 532 case eCSSKeyword_translate3d:
michael@0 533 ProcessTranslate3D(aMatrix, aData, aContext, aPresContext,
michael@0 534 aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
michael@0 535 break;
michael@0 536 case eCSSKeyword_scalex:
michael@0 537 ProcessScaleX(aMatrix, aData);
michael@0 538 break;
michael@0 539 case eCSSKeyword_scaley:
michael@0 540 ProcessScaleY(aMatrix, aData);
michael@0 541 break;
michael@0 542 case eCSSKeyword_scalez:
michael@0 543 ProcessScaleZ(aMatrix, aData);
michael@0 544 break;
michael@0 545 case eCSSKeyword_scale:
michael@0 546 ProcessScale(aMatrix, aData);
michael@0 547 break;
michael@0 548 case eCSSKeyword_scale3d:
michael@0 549 ProcessScale3D(aMatrix, aData);
michael@0 550 break;
michael@0 551 case eCSSKeyword_skewx:
michael@0 552 ProcessSkewX(aMatrix, aData);
michael@0 553 break;
michael@0 554 case eCSSKeyword_skewy:
michael@0 555 ProcessSkewY(aMatrix, aData);
michael@0 556 break;
michael@0 557 case eCSSKeyword_skew:
michael@0 558 ProcessSkew(aMatrix, aData);
michael@0 559 break;
michael@0 560 case eCSSKeyword_rotatex:
michael@0 561 ProcessRotateX(aMatrix, aData);
michael@0 562 break;
michael@0 563 case eCSSKeyword_rotatey:
michael@0 564 ProcessRotateY(aMatrix, aData);
michael@0 565 break;
michael@0 566 case eCSSKeyword_rotatez:
michael@0 567 case eCSSKeyword_rotate:
michael@0 568 ProcessRotateZ(aMatrix, aData);
michael@0 569 break;
michael@0 570 case eCSSKeyword_rotate3d:
michael@0 571 ProcessRotate3D(aMatrix, aData);
michael@0 572 break;
michael@0 573 case eCSSKeyword_matrix:
michael@0 574 ProcessMatrix(aMatrix, aData, aContext, aPresContext,
michael@0 575 aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
michael@0 576 break;
michael@0 577 case eCSSKeyword_matrix3d:
michael@0 578 ProcessMatrix3D(aMatrix, aData, aContext, aPresContext,
michael@0 579 aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
michael@0 580 break;
michael@0 581 case eCSSKeyword_interpolatematrix:
michael@0 582 ProcessInterpolateMatrix(aMatrix, aData, aContext, aPresContext,
michael@0 583 aCanStoreInRuleTree, aBounds, aAppUnitsPerMatrixUnit);
michael@0 584 break;
michael@0 585 case eCSSKeyword_perspective:
michael@0 586 ProcessPerspective(aMatrix, aData, aContext, aPresContext,
michael@0 587 aCanStoreInRuleTree, aAppUnitsPerMatrixUnit);
michael@0 588 break;
michael@0 589 default:
michael@0 590 NS_NOTREACHED("Unknown transform function!");
michael@0 591 }
michael@0 592 }
michael@0 593
michael@0 594 /**
michael@0 595 * Return the transform function, as an nsCSSKeyword, for the given
michael@0 596 * nsCSSValue::Array from a transform list.
michael@0 597 */
michael@0 598 nsCSSKeyword
michael@0 599 TransformFunctionOf(const nsCSSValue::Array* aData)
michael@0 600 {
michael@0 601 MOZ_ASSERT(aData->Item(0).GetUnit() == eCSSUnit_Enumerated);
michael@0 602 return aData->Item(0).GetKeywordValue();
michael@0 603 }
michael@0 604
michael@0 605 gfx3DMatrix
michael@0 606 ReadTransforms(const nsCSSValueList* aList,
michael@0 607 nsStyleContext* aContext,
michael@0 608 nsPresContext* aPresContext,
michael@0 609 bool &aCanStoreInRuleTree,
michael@0 610 nsRect& aBounds,
michael@0 611 float aAppUnitsPerMatrixUnit)
michael@0 612 {
michael@0 613 gfx3DMatrix result;
michael@0 614
michael@0 615 for (const nsCSSValueList* curr = aList; curr != nullptr; curr = curr->mNext) {
michael@0 616 const nsCSSValue &currElem = curr->mValue;
michael@0 617 NS_ASSERTION(currElem.GetUnit() == eCSSUnit_Function,
michael@0 618 "Stream should consist solely of functions!");
michael@0 619 NS_ASSERTION(currElem.GetArrayValue()->Count() >= 1,
michael@0 620 "Incoming function is too short!");
michael@0 621
michael@0 622 /* Read in a single transform matrix. */
michael@0 623 MatrixForTransformFunction(result, currElem.GetArrayValue(), aContext,
michael@0 624 aPresContext, aCanStoreInRuleTree,
michael@0 625 aBounds, aAppUnitsPerMatrixUnit);
michael@0 626 }
michael@0 627
michael@0 628 return result;
michael@0 629 }
michael@0 630
michael@0 631 } // namespace nsStyleTransformMatrix

mercurial