Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 |