nsStyleTransformMatrix.cpp (24663B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * A class used for intermediate representations of the transform and 9 * transform-like properties. 10 */ 11 12 #include "nsStyleTransformMatrix.h" 13 14 #include "gfxMatrix.h" 15 #include "gfxQuaternion.h" 16 #include "mozilla/MotionPathUtils.h" 17 #include "mozilla/SVGUtils.h" 18 #include "mozilla/ServoBindings.h" 19 #include "mozilla/StaticPrefs_layout.h" 20 #include "mozilla/StyleAnimationValue.h" 21 #include "nsLayoutUtils.h" 22 #include "nsPresContext.h" 23 24 using namespace mozilla; 25 using namespace mozilla::gfx; 26 27 namespace nsStyleTransformMatrix { 28 29 /* Note on floating point precision: The transform matrix is an array 30 * of single precision 'float's, and so are most of the input values 31 * we get from the style system, but intermediate calculations 32 * involving angles need to be done in 'double'. 33 */ 34 35 // Define UNIFIED_CONTINUATIONS here and in nsDisplayList.cpp 36 // to have the transform property try 37 // to transform content with continuations as one unified block instead of 38 // several smaller ones. This is currently disabled because it doesn't work 39 // correctly, since when the frames are initially being reflowed, their 40 // continuations all compute their bounding rects independently of each other 41 // and consequently get the wrong value. 42 // #define UNIFIED_CONTINUATIONS 43 44 static nsRect GetSVGBox(const nsIFrame* aFrame) { 45 auto computeViewBox = [&]() { 46 // Percentages in transforms resolve against the width/height of the 47 // nearest viewport (or its viewBox if one is applied), and the 48 // transform is relative to {0,0} in current user space. 49 CSSSize size = CSSSize::FromUnknownSize(SVGUtils::GetContextSize(aFrame)); 50 return nsRect(-aFrame->GetPosition(), CSSPixel::ToAppUnits(size)); 51 }; 52 53 auto transformBox = aFrame->StyleDisplay()->mTransformBox; 54 if ((transformBox == StyleTransformBox::StrokeBox || 55 transformBox == StyleTransformBox::BorderBox) && 56 aFrame->StyleSVGReset()->HasNonScalingStroke()) { 57 // To calculate stroke bounds for an element with `non-scaling-stroke` we 58 // need to resolve its transform to its outer-svg, but to resolve that 59 // transform when it has `transform-box:stroke-box` (or `border-box`) 60 // may require its stroke bounds. There's no ideal way to break this 61 // cyclical dependency, but we break it by converting to 62 // `transform-box:fill-box` here. 63 // https://github.com/w3c/csswg-drafts/issues/9640 64 transformBox = StyleTransformBox::FillBox; 65 } 66 67 // For SVG elements without associated CSS layout box, the used value for 68 // content-box is fill-box and for border-box is stroke-box. 69 // https://drafts.csswg.org/css-transforms-1/#transform-box 70 switch (transformBox) { 71 case StyleTransformBox::ContentBox: 72 case StyleTransformBox::FillBox: { 73 // Percentages in transforms resolve against the SVG bbox, and the 74 // transform is relative to the top-left of the SVG bbox. 75 nsRect bboxInAppUnits = nsLayoutUtils::ComputeSVGReferenceRect( 76 const_cast<nsIFrame*>(aFrame), StyleGeometryBox::FillBox); 77 // The mRect of an SVG nsIFrame is its user space bounds *including* 78 // stroke and markers, whereas bboxInAppUnits is its user space bounds 79 // including fill only. We need to note the offset of the reference box 80 // from the frame's mRect in mX/mY. 81 return {bboxInAppUnits.x - aFrame->GetPosition().x, 82 bboxInAppUnits.y - aFrame->GetPosition().y, bboxInAppUnits.width, 83 bboxInAppUnits.height}; 84 } 85 case StyleTransformBox::BorderBox: 86 case StyleTransformBox::StrokeBox: { 87 // We are using SVGUtils::PathExtentsToMaxStrokeExtents() to compute the 88 // bbox contribution for stroke box (if it doesn't have simple bounds), 89 // so the |strokeBox| here may be larger than the author's expectation. 90 // Using Moz2D to compute the tighter bounding box is another way but it 91 // has some potential issues (see SVGGeometryFrame::GetBBoxContribution() 92 // for more details), and its result depends on the drawing backend. So 93 // for now we still rely on our default calcuclation for SVG geometry 94 // frame reflow code. At least this works for the shape elements which 95 // have simple bounds. 96 // FIXME: Bug 1849054. We may have to update 97 // SVGGeometryFrame::GetBBoxContribution() to get tighter stroke bounds. 98 nsRect strokeBox = nsLayoutUtils::ComputeSVGReferenceRect( 99 const_cast<nsIFrame*>(aFrame), StyleGeometryBox::StrokeBox, 100 nsLayoutUtils::MayHaveNonScalingStrokeCyclicDependency::Yes); 101 // The |nsIFrame::mRect| includes markers, so we have to compute the 102 // offsets without markers. 103 return nsRect{strokeBox.x - aFrame->GetPosition().x, 104 strokeBox.y - aFrame->GetPosition().y, strokeBox.width, 105 strokeBox.height}; 106 } 107 case StyleTransformBox::ViewBox: 108 return computeViewBox(); 109 } 110 111 MOZ_ASSERT_UNREACHABLE("All transform box should be handled."); 112 return {}; 113 } 114 115 void TransformReferenceBox::EnsureDimensionsAreCached() { 116 if (mIsCached) { 117 return; 118 } 119 120 MOZ_ASSERT(mFrame); 121 122 mIsCached = true; 123 124 if (mFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) { 125 mBox = GetSVGBox(mFrame); 126 return; 127 } 128 129 // For elements with associated CSS layout box, the used value for fill-box is 130 // content-box and for stroke-box and view-box is border-box. 131 // https://drafts.csswg.org/css-transforms-1/#transform-box 132 switch (mFrame->StyleDisplay()->mTransformBox) { 133 case StyleTransformBox::FillBox: 134 case StyleTransformBox::ContentBox: { 135 mBox = mFrame->GetContentRectRelativeToSelf(); 136 return; 137 } 138 case StyleTransformBox::StrokeBox: 139 // TODO: Implement this in the following patches. 140 return; 141 case StyleTransformBox::ViewBox: 142 case StyleTransformBox::BorderBox: { 143 // If UNIFIED_CONTINUATIONS is not defined, this is simply the frame's 144 // bounding rectangle, translated to the origin. Otherwise, it is the 145 // smallest rectangle containing a frame and all of its continuations. For 146 // example, if there is a <span> element with several continuations split 147 // over several lines, this function will return the rectangle containing 148 // all of those continuations. 149 150 nsRect rect; 151 152 #ifndef UNIFIED_CONTINUATIONS 153 rect = mFrame->GetRect(); 154 #else 155 // Iterate the continuation list, unioning together the bounding rects: 156 for (const nsIFrame* currFrame = mFrame->FirstContinuation(); 157 currFrame != nullptr; currFrame = currFrame->GetNextContinuation()) { 158 // Get the frame rect in local coordinates, then translate back to the 159 // original coordinates: 160 rect.UnionRect(result, nsRect(currFrame->GetOffsetTo(mFrame), 161 currFrame->GetSize())); 162 } 163 #endif 164 165 mBox = {0, 0, rect.Width(), rect.Height()}; 166 return; 167 } 168 } 169 } 170 171 float ProcessTranslatePart( 172 const LengthPercentage& aValue, TransformReferenceBox* aRefBox, 173 TransformReferenceBox::DimensionGetter aDimensionGetter) { 174 return aValue.ResolveToCSSPixelsWith([&] { 175 return aRefBox && !aRefBox->IsEmpty() 176 ? CSSPixel::FromAppUnits((aRefBox->*aDimensionGetter)()) 177 : CSSCoord(0); 178 }); 179 } 180 181 /** 182 * Helper functions to process all the transformation function types. 183 * 184 * These take a matrix parameter to accumulate the current matrix. 185 */ 186 187 /* Helper function to process a matrix entry. */ 188 static void ProcessMatrix(Matrix4x4& aMatrix, 189 const StyleTransformOperation& aOp) { 190 const auto& matrix = aOp.AsMatrix(); 191 gfxMatrix result; 192 193 result._11 = matrix.a; 194 result._12 = matrix.b; 195 result._21 = matrix.c; 196 result._22 = matrix.d; 197 result._31 = matrix.e; 198 result._32 = matrix.f; 199 200 aMatrix = result * aMatrix; 201 } 202 203 static void ProcessMatrix3D(Matrix4x4& aMatrix, 204 const StyleTransformOperation& aOp) { 205 Matrix4x4 temp; 206 207 const auto& matrix = aOp.AsMatrix3D(); 208 209 temp._11 = matrix.m11; 210 temp._12 = matrix.m12; 211 temp._13 = matrix.m13; 212 temp._14 = matrix.m14; 213 temp._21 = matrix.m21; 214 temp._22 = matrix.m22; 215 temp._23 = matrix.m23; 216 temp._24 = matrix.m24; 217 temp._31 = matrix.m31; 218 temp._32 = matrix.m32; 219 temp._33 = matrix.m33; 220 temp._34 = matrix.m34; 221 222 temp._41 = matrix.m41; 223 temp._42 = matrix.m42; 224 temp._43 = matrix.m43; 225 temp._44 = matrix.m44; 226 227 aMatrix = temp * aMatrix; 228 } 229 230 // For accumulation for transform functions, |aOne| corresponds to |aB| and 231 // |aTwo| corresponds to |aA| for StyleAnimationValue::Accumulate(). 232 class Accumulate { 233 public: 234 template <typename T> 235 static T operate(const T& aOne, const T& aTwo, double aCoeff) { 236 return aOne + aTwo * aCoeff; 237 } 238 239 static Point4D operateForPerspective(const Point4D& aOne, const Point4D& aTwo, 240 double aCoeff) { 241 return (aOne - Point4D(0, 0, 0, 1)) + 242 (aTwo - Point4D(0, 0, 0, 1)) * aCoeff + Point4D(0, 0, 0, 1); 243 } 244 static Point3D operateForScale(const Point3D& aOne, const Point3D& aTwo, 245 double aCoeff) { 246 // For scale, the identify element is 1, see AddTransformScale in 247 // StyleAnimationValue.cpp. 248 return (aOne - Point3D(1, 1, 1)) + (aTwo - Point3D(1, 1, 1)) * aCoeff + 249 Point3D(1, 1, 1); 250 } 251 252 static Matrix4x4 operateForRotate(const gfxQuaternion& aOne, 253 const gfxQuaternion& aTwo, double aCoeff) { 254 if (aCoeff == 0.0) { 255 return aOne.ToMatrix(); 256 } 257 258 double theta = acos(std::clamp(aTwo.w, -1.0, 1.0)); 259 double scale = (theta != 0.0) ? 1.0 / sin(theta) : 0.0; 260 theta *= aCoeff; 261 scale *= sin(theta); 262 263 gfxQuaternion result = gfxQuaternion(scale * aTwo.x, scale * aTwo.y, 264 scale * aTwo.z, cos(theta)) * 265 aOne; 266 return result.ToMatrix(); 267 } 268 269 static Matrix4x4 operateForFallback(const Matrix4x4& aMatrix1, 270 const Matrix4x4& aMatrix2, 271 double aProgress) { 272 return aMatrix1; 273 } 274 275 static Matrix4x4 operateByServo(const Matrix4x4& aMatrix1, 276 const Matrix4x4& aMatrix2, double aCount) { 277 Matrix4x4 result; 278 Servo_MatrixTransform_Operate(/* aInterpolate = */ false, 279 &aMatrix1.components, &aMatrix2.components, 280 aCount, &result.components); 281 return result; 282 } 283 }; 284 285 class Interpolate { 286 public: 287 template <typename T> 288 static T operate(const T& aOne, const T& aTwo, double aCoeff) { 289 return aOne + (aTwo - aOne) * aCoeff; 290 } 291 292 static Point4D operateForPerspective(const Point4D& aOne, const Point4D& aTwo, 293 double aCoeff) { 294 return aOne + (aTwo - aOne) * aCoeff; 295 } 296 297 static Point3D operateForScale(const Point3D& aOne, const Point3D& aTwo, 298 double aCoeff) { 299 return aOne + (aTwo - aOne) * aCoeff; 300 } 301 302 static Matrix4x4 operateForRotate(const gfxQuaternion& aOne, 303 const gfxQuaternion& aTwo, double aCoeff) { 304 return aOne.Slerp(aTwo, aCoeff).ToMatrix(); 305 } 306 307 static Matrix4x4 operateForFallback(const Matrix4x4& aMatrix1, 308 const Matrix4x4& aMatrix2, 309 double aProgress) { 310 return aProgress < 0.5 ? aMatrix1 : aMatrix2; 311 } 312 313 static Matrix4x4 operateByServo(const Matrix4x4& aMatrix1, 314 const Matrix4x4& aMatrix2, double aProgress) { 315 Matrix4x4 result; 316 Servo_MatrixTransform_Operate(/* aInterpolate = */ true, 317 &aMatrix1.components, &aMatrix2.components, 318 aProgress, &result.components); 319 return result; 320 } 321 }; 322 323 template <typename Operator> 324 static void ProcessMatrixOperator(Matrix4x4& aMatrix, 325 const StyleTransform& aFrom, 326 const StyleTransform& aTo, float aProgress, 327 TransformReferenceBox& aRefBox) { 328 float appUnitPerCSSPixel = AppUnitsPerCSSPixel(); 329 Matrix4x4 matrix1 = ReadTransforms(aFrom, aRefBox, appUnitPerCSSPixel); 330 Matrix4x4 matrix2 = ReadTransforms(aTo, aRefBox, appUnitPerCSSPixel); 331 aMatrix = Operator::operateByServo(matrix1, matrix2, aProgress) * aMatrix; 332 } 333 334 /* Helper function to process two matrices that we need to interpolate between 335 */ 336 void ProcessInterpolateMatrix(Matrix4x4& aMatrix, 337 const StyleTransformOperation& aOp, 338 TransformReferenceBox& aRefBox) { 339 const auto& args = aOp.AsInterpolateMatrix(); 340 ProcessMatrixOperator<Interpolate>(aMatrix, args.from_list, args.to_list, 341 args.progress._0, aRefBox); 342 } 343 344 void ProcessAccumulateMatrix(Matrix4x4& aMatrix, 345 const StyleTransformOperation& aOp, 346 TransformReferenceBox& aRefBox) { 347 const auto& args = aOp.AsAccumulateMatrix(); 348 ProcessMatrixOperator<Accumulate>(aMatrix, args.from_list, args.to_list, 349 args.count, aRefBox); 350 } 351 352 /* Helper function to process a translatex function. */ 353 static void ProcessTranslateX(Matrix4x4& aMatrix, 354 const LengthPercentage& aLength, 355 TransformReferenceBox& aRefBox) { 356 Point3D temp; 357 temp.x = 358 ProcessTranslatePart(aLength, &aRefBox, &TransformReferenceBox::Width); 359 aMatrix.PreTranslate(temp); 360 } 361 362 /* Helper function to process a translatey function. */ 363 static void ProcessTranslateY(Matrix4x4& aMatrix, 364 const LengthPercentage& aLength, 365 TransformReferenceBox& aRefBox) { 366 Point3D temp; 367 temp.y = 368 ProcessTranslatePart(aLength, &aRefBox, &TransformReferenceBox::Height); 369 aMatrix.PreTranslate(temp); 370 } 371 372 static void ProcessTranslateZ(Matrix4x4& aMatrix, const Length& aLength) { 373 Point3D temp; 374 temp.z = aLength.ToCSSPixels(); 375 aMatrix.PreTranslate(temp); 376 } 377 378 /* Helper function to process a translate function. */ 379 static void ProcessTranslate(Matrix4x4& aMatrix, const LengthPercentage& aX, 380 const LengthPercentage& aY, 381 TransformReferenceBox& aRefBox) { 382 Point3D temp; 383 temp.x = ProcessTranslatePart(aX, &aRefBox, &TransformReferenceBox::Width); 384 temp.y = ProcessTranslatePart(aY, &aRefBox, &TransformReferenceBox::Height); 385 aMatrix.PreTranslate(temp); 386 } 387 388 static void ProcessTranslate3D(Matrix4x4& aMatrix, const LengthPercentage& aX, 389 const LengthPercentage& aY, const Length& aZ, 390 TransformReferenceBox& aRefBox) { 391 Point3D temp; 392 393 temp.x = ProcessTranslatePart(aX, &aRefBox, &TransformReferenceBox::Width); 394 temp.y = ProcessTranslatePart(aY, &aRefBox, &TransformReferenceBox::Height); 395 temp.z = aZ.ToCSSPixels(); 396 397 aMatrix.PreTranslate(temp); 398 } 399 400 /* Helper function to set up a scale matrix. */ 401 static void ProcessScaleHelper(Matrix4x4& aMatrix, float aXScale, float aYScale, 402 float aZScale) { 403 aMatrix.PreScale(aXScale, aYScale, aZScale); 404 } 405 406 static void ProcessScale3D(Matrix4x4& aMatrix, 407 const StyleTransformOperation& aOp) { 408 const auto& scale = aOp.AsScale3D(); 409 ProcessScaleHelper(aMatrix, scale._0, scale._1, scale._2); 410 } 411 412 /* Helper function that, given a set of angles, constructs the appropriate 413 * skew matrix. 414 */ 415 static void ProcessSkewHelper(Matrix4x4& aMatrix, const StyleAngle& aXAngle, 416 const StyleAngle& aYAngle) { 417 aMatrix.SkewXY(aXAngle.ToRadians(), aYAngle.ToRadians()); 418 } 419 420 static void ProcessRotate3D(Matrix4x4& aMatrix, float aX, float aY, float aZ, 421 const StyleAngle& aAngle) { 422 Matrix4x4 temp; 423 temp.SetRotateAxisAngle(aX, aY, aZ, aAngle.ToRadians()); 424 aMatrix = temp * aMatrix; 425 } 426 427 static void ProcessPerspective( 428 Matrix4x4& aMatrix, 429 const StyleGenericPerspectiveFunction<Length>& aPerspective) { 430 if (aPerspective.IsNone()) { 431 return; 432 } 433 float p = aPerspective.AsLength().ToCSSPixels(); 434 if (!std::isinf(p)) { 435 aMatrix.Perspective(std::max(p, 1.0f)); 436 } 437 } 438 439 static void MatrixForTransformFunction(Matrix4x4& aMatrix, 440 const StyleTransformOperation& aOp, 441 TransformReferenceBox& aRefBox) { 442 /* Get the keyword for the transform. */ 443 switch (aOp.tag) { 444 case StyleTransformOperation::Tag::TranslateX: 445 ProcessTranslateX(aMatrix, aOp.AsTranslateX(), aRefBox); 446 break; 447 case StyleTransformOperation::Tag::TranslateY: 448 ProcessTranslateY(aMatrix, aOp.AsTranslateY(), aRefBox); 449 break; 450 case StyleTransformOperation::Tag::TranslateZ: 451 ProcessTranslateZ(aMatrix, aOp.AsTranslateZ()); 452 break; 453 case StyleTransformOperation::Tag::Translate: 454 ProcessTranslate(aMatrix, aOp.AsTranslate()._0, aOp.AsTranslate()._1, 455 aRefBox); 456 break; 457 case StyleTransformOperation::Tag::Translate3D: 458 return ProcessTranslate3D(aMatrix, aOp.AsTranslate3D()._0, 459 aOp.AsTranslate3D()._1, aOp.AsTranslate3D()._2, 460 aRefBox); 461 break; 462 case StyleTransformOperation::Tag::ScaleX: 463 ProcessScaleHelper(aMatrix, aOp.AsScaleX(), 1.0f, 1.0f); 464 break; 465 case StyleTransformOperation::Tag::ScaleY: 466 ProcessScaleHelper(aMatrix, 1.0f, aOp.AsScaleY(), 1.0f); 467 break; 468 case StyleTransformOperation::Tag::ScaleZ: 469 ProcessScaleHelper(aMatrix, 1.0f, 1.0f, aOp.AsScaleZ()); 470 break; 471 case StyleTransformOperation::Tag::Scale: 472 ProcessScaleHelper(aMatrix, aOp.AsScale()._0, aOp.AsScale()._1, 1.0f); 473 break; 474 case StyleTransformOperation::Tag::Scale3D: 475 ProcessScale3D(aMatrix, aOp); 476 break; 477 case StyleTransformOperation::Tag::SkewX: 478 ProcessSkewHelper(aMatrix, aOp.AsSkewX(), StyleAngle::Zero()); 479 break; 480 case StyleTransformOperation::Tag::SkewY: 481 ProcessSkewHelper(aMatrix, StyleAngle::Zero(), aOp.AsSkewY()); 482 break; 483 case StyleTransformOperation::Tag::Skew: 484 ProcessSkewHelper(aMatrix, aOp.AsSkew()._0, aOp.AsSkew()._1); 485 break; 486 case StyleTransformOperation::Tag::RotateX: 487 aMatrix.RotateX(aOp.AsRotateX().ToRadians()); 488 break; 489 case StyleTransformOperation::Tag::RotateY: 490 aMatrix.RotateY(aOp.AsRotateY().ToRadians()); 491 break; 492 case StyleTransformOperation::Tag::RotateZ: 493 aMatrix.RotateZ(aOp.AsRotateZ().ToRadians()); 494 break; 495 case StyleTransformOperation::Tag::Rotate: 496 aMatrix.RotateZ(aOp.AsRotate().ToRadians()); 497 break; 498 case StyleTransformOperation::Tag::Rotate3D: 499 ProcessRotate3D(aMatrix, aOp.AsRotate3D()._0, aOp.AsRotate3D()._1, 500 aOp.AsRotate3D()._2, aOp.AsRotate3D()._3); 501 break; 502 case StyleTransformOperation::Tag::Matrix: 503 ProcessMatrix(aMatrix, aOp); 504 break; 505 case StyleTransformOperation::Tag::Matrix3D: 506 ProcessMatrix3D(aMatrix, aOp); 507 break; 508 case StyleTransformOperation::Tag::InterpolateMatrix: 509 ProcessInterpolateMatrix(aMatrix, aOp, aRefBox); 510 break; 511 case StyleTransformOperation::Tag::AccumulateMatrix: 512 ProcessAccumulateMatrix(aMatrix, aOp, aRefBox); 513 break; 514 case StyleTransformOperation::Tag::Perspective: 515 ProcessPerspective(aMatrix, aOp.AsPerspective()); 516 break; 517 default: 518 MOZ_ASSERT_UNREACHABLE("Unknown transform function!"); 519 } 520 } 521 522 Matrix4x4 ReadTransforms(const StyleTransform& aTransform, 523 TransformReferenceBox& aRefBox, 524 float aAppUnitsPerMatrixUnit) { 525 Matrix4x4 result; 526 527 for (const StyleTransformOperation& op : aTransform.Operations()) { 528 MatrixForTransformFunction(result, op, aRefBox); 529 } 530 531 float scale = float(AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit; 532 result.PreScale(1 / scale, 1 / scale, 1 / scale); 533 result.PostScale(scale, scale, scale); 534 535 return result; 536 } 537 538 static void ProcessTranslate(Matrix4x4& aMatrix, 539 const StyleTranslate& aTranslate, 540 TransformReferenceBox& aRefBox) { 541 switch (aTranslate.tag) { 542 case StyleTranslate::Tag::None: 543 return; 544 case StyleTranslate::Tag::Translate: 545 return ProcessTranslate3D(aMatrix, aTranslate.AsTranslate()._0, 546 aTranslate.AsTranslate()._1, 547 aTranslate.AsTranslate()._2, aRefBox); 548 default: 549 MOZ_ASSERT_UNREACHABLE("Huh?"); 550 } 551 } 552 553 static void ProcessRotate(Matrix4x4& aMatrix, const StyleRotate& aRotate) { 554 switch (aRotate.tag) { 555 case StyleRotate::Tag::None: 556 return; 557 case StyleRotate::Tag::Rotate: 558 aMatrix.RotateZ(aRotate.AsRotate().ToRadians()); 559 return; 560 case StyleRotate::Tag::Rotate3D: 561 return ProcessRotate3D(aMatrix, aRotate.AsRotate3D()._0, 562 aRotate.AsRotate3D()._1, aRotate.AsRotate3D()._2, 563 aRotate.AsRotate3D()._3); 564 default: 565 MOZ_ASSERT_UNREACHABLE("Huh?"); 566 } 567 } 568 569 static void ProcessScale(Matrix4x4& aMatrix, const StyleScale& aScale) { 570 switch (aScale.tag) { 571 case StyleScale::Tag::None: 572 return; 573 case StyleScale::Tag::Scale: 574 return ProcessScaleHelper(aMatrix, aScale.AsScale()._0, 575 aScale.AsScale()._1, aScale.AsScale()._2); 576 default: 577 MOZ_ASSERT_UNREACHABLE("Huh?"); 578 } 579 } 580 581 Matrix4x4 ReadTransforms(const StyleTranslate& aTranslate, 582 const StyleRotate& aRotate, const StyleScale& aScale, 583 const ResolvedMotionPathData* aMotion, 584 const StyleTransform& aTransform, 585 TransformReferenceBox& aRefBox, 586 float aAppUnitsPerMatrixUnit) { 587 Matrix4x4 result; 588 589 ProcessTranslate(result, aTranslate, aRefBox); 590 ProcessRotate(result, aRotate); 591 ProcessScale(result, aScale); 592 593 if (aMotion) { 594 // Create the equivalent translate and rotate function, according to the 595 // order in spec. We combine the translate and then the rotate. 596 // https://drafts.fxtf.org/motion-1/#calculating-path-transform 597 // 598 // Besides, we have to shift the object by the delta between anchor-point 599 // and transform-origin, to make sure we rotate the object according to 600 // anchor-point. 601 result.PreTranslate(aMotion->mTranslate.x + aMotion->mShift.x, 602 aMotion->mTranslate.y + aMotion->mShift.y, 0.0); 603 if (aMotion->mRotate != 0.0) { 604 result.RotateZ(aMotion->mRotate); 605 } 606 // Shift the origin back to transform-origin. 607 result.PreTranslate(-aMotion->mShift.x, -aMotion->mShift.y, 0.0); 608 } 609 610 for (const StyleTransformOperation& op : aTransform.Operations()) { 611 MatrixForTransformFunction(result, op, aRefBox); 612 } 613 614 float scale = float(AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit; 615 result.PreScale(1 / scale, 1 / scale, 1 / scale); 616 result.PostScale(scale, scale, scale); 617 618 return result; 619 } 620 621 mozilla::CSSPoint Convert2DPosition(const mozilla::LengthPercentage& aX, 622 const mozilla::LengthPercentage& aY, 623 const CSSSize& aSize) { 624 return { 625 aX.ResolveToCSSPixels(aSize.width), 626 aY.ResolveToCSSPixels(aSize.height), 627 }; 628 } 629 630 CSSPoint Convert2DPosition(const LengthPercentage& aX, 631 const LengthPercentage& aY, 632 TransformReferenceBox& aRefBox) { 633 return { 634 aX.ResolveToCSSPixelsWith( 635 [&] { return CSSPixel::FromAppUnits(aRefBox.Width()); }), 636 aY.ResolveToCSSPixelsWith( 637 [&] { return CSSPixel::FromAppUnits(aRefBox.Height()); }), 638 }; 639 } 640 641 Point Convert2DPosition(const LengthPercentage& aX, const LengthPercentage& aY, 642 TransformReferenceBox& aRefBox, 643 int32_t aAppUnitsPerPixel) { 644 float scale = mozilla::AppUnitsPerCSSPixel() / float(aAppUnitsPerPixel); 645 CSSPoint p = Convert2DPosition(aX, aY, aRefBox); 646 return {p.x * scale, p.y * scale}; 647 } 648 649 } // namespace nsStyleTransformMatrix