From bbea8d1bca7aa20ca818c7e274b7dc235902e352 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Wed, 22 Aug 2018 01:22:28 +0000 Subject: [PATCH] Bug 1429298 - Part 6: Apply motion path transform matrix. r=nical We implement the layout part of offset-path. Now we don't have offset-distance, so use the default value, 0%, for it. Note: rename mCombinedTransform as mIndividualTransform, which only stores the combined individual transforms. We apply the individual transforms, motion path transform, and specified transform in ReadTransforms. (We have to follow the order, so we don't combine the specified transform in FinishStyle.) Depends on D2967 Differential Revision: https://phabricator.services.mozilla.com/D2968 --HG-- extra : moz-landing-system : lando --- dom/svg/moz.build | 1 + layout/base/nsLayoutUtils.cpp | 92 +++++++++++++++++++++++++ layout/base/nsLayoutUtils.h | 11 +++ layout/painting/ActiveLayerTracker.cpp | 24 ++++--- layout/painting/nsDisplayList.cpp | 25 ++++--- layout/painting/nsDisplayList.h | 13 ++++ layout/style/nsStyleStruct.cpp | 34 ++++----- layout/style/nsStyleStruct.h | 23 +++---- layout/style/nsStyleTransformMatrix.cpp | 60 +++++++++++++--- layout/style/nsStyleTransformMatrix.h | 14 ++++ 10 files changed, 239 insertions(+), 58 deletions(-) diff --git a/dom/svg/moz.build b/dom/svg/moz.build index 6f79fc07d6e3..74162ff1131b 100644 --- a/dom/svg/moz.build +++ b/dom/svg/moz.build @@ -81,6 +81,7 @@ EXPORTS.mozilla.dom += [ 'SVGMatrix.h', 'SVGMetadataElement.h', 'SVGMPathElement.h', + 'SVGPathData.h', 'SVGPathElement.h', 'SVGPatternElement.h', 'SVGPolygonElement.h', diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 7aae26eca05a..ee9841af61aa 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -76,6 +76,7 @@ #include "mozilla/dom/DOMRect.h" #include "mozilla/dom/DOMStringList.h" #include "mozilla/dom/KeyframeEffect.h" +#include "mozilla/dom/SVGPathData.h" #include "mozilla/layers/APZCCallbackHelper.h" #include "imgIRequest.h" #include "nsIImageLoadingContent.h" @@ -10266,3 +10267,94 @@ nsLayoutUtils::StyleForScrollbar(nsIFrame* aScrollbarPart) // held strongly by the element. return style.get(); } + +static float +ResolveTransformOrigin(const nsStyleCoord& aCoord, + TransformReferenceBox& aRefBox, + TransformReferenceBox::DimensionGetter aGetter) +{ + float result = 0.0; + const float scale = mozilla::AppUnitsPerCSSPixel(); + if (aCoord.GetUnit() == eStyleUnit_Calc) { + const nsStyleCoord::Calc *calc = aCoord.GetCalcValue(); + result = NSAppUnitsToFloatPixels((aRefBox.*aGetter)(), scale) * + calc->mPercent + + NSAppUnitsToFloatPixels(calc->mLength, scale); + } else if (aCoord.GetUnit() == eStyleUnit_Percent) { + result = NSAppUnitsToFloatPixels((aRefBox.*aGetter)(), scale) * + aCoord.GetPercentValue(); + } else { + MOZ_ASSERT(aCoord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); + result = NSAppUnitsToFloatPixels(aCoord.GetCoordValue(), scale); + } + return result; +} + +/* static */ Maybe +nsLayoutUtils::ResolveMotionPath(const nsIFrame* aFrame) +{ + MOZ_ASSERT(aFrame); + + const nsStyleDisplay* display = aFrame->StyleDisplay(); + if (!display->mMotion || !display->mMotion->HasPath()) { + return Nothing(); + } + + const UniquePtr& motion = display->mMotion; + // Bug 1429299 - Implement offset-distance for motion path. For now, we use + // the default value, i.e. 0%. + float distance = 0.0; + float angle = 0.0; + Point point; + if (motion->OffsetPath().GetType() == StyleShapeSourceType::Path) { + // Build the path and compute the point and angle for creating the + // equivalent translate and rotate. + // Here we only need to build a valid path for motion path, so + // using the default values of stroke-width, stoke-linecap, and fill-rule + // is fine for now because what we want is get the point and its normal + // vector along the path, instead of rendering it. + // FIXME: Bug 1484780, we should cache the path to avoid rebuilding it here + // at every restyle. (Caching the path avoids the cost of flattening it + // again each time.) + RefPtr drawTarget = + gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); + RefPtr builder = + drawTarget->CreatePathBuilder(FillRule::FILL_WINDING); + RefPtr gfxPath = + SVGPathData::BuildPath(motion->OffsetPath().GetPath()->Path(), + builder, + NS_STYLE_STROKE_LINECAP_BUTT, + 0.0); + if (!gfxPath) { + return Nothing(); + } + float pathLength = gfxPath->ComputeLength(); + float computedDistance = distance * pathLength; + Point tangent; + point = gfxPath->ComputePointAtLength(computedDistance, &tangent); + // Bug 1429301 - Implement offset-rotate for motion path. + // After implement offset-rotate, |angle| will be adjusted more. + // For now, the default value of offset-rotate is "auto", so we use the + // directional tangent vector. + angle = atan2(tangent.y, tangent.x); + } else { + // Bug 1480665: Implement ray() function. + NS_WARNING("Unsupported offset-path value"); + } + + // Compute the offset for motion path translate. + // We need to resolve transform-origin here to calculate the correct path + // translate. (i.e. Center transform-origin on the path.) + TransformReferenceBox refBox(aFrame); + Point origin( + ResolveTransformOrigin(display->mTransformOrigin[0], + refBox, + &TransformReferenceBox::Width), + ResolveTransformOrigin(display->mTransformOrigin[1], + refBox, + &TransformReferenceBox::Height) + ); + // Bug 1186329: the translate parameters will be adjusted more after we + // implement offset-position and offset-anchor. + return Some(MotionPathData { point - origin, angle }); +} diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 3f7715911d3c..bfd437dbc298 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -119,6 +119,11 @@ struct DisplayPortMarginsPropertyData { uint32_t mPriority; }; +struct MotionPathData { + gfx::Point mTranslate; + float mRotate; +}; + } // namespace mozilla // For GetDisplayPort @@ -3113,6 +3118,12 @@ public: */ static ComputedStyle* StyleForScrollbar(nsIFrame* aScrollbarPart); + /** + * Generate the motion path transform result. + **/ + static mozilla::Maybe + ResolveMotionPath(const nsIFrame* aFrame); + private: static uint32_t sFontSizeInflationEmPerLine; static uint32_t sFontSizeInflationMinTwips; diff --git a/layout/painting/ActiveLayerTracker.cpp b/layout/painting/ActiveLayerTracker.cpp index ae2b2d4cc141..4d23ac2b6a26 100644 --- a/layout/painting/ActiveLayerTracker.cpp +++ b/layout/painting/ActiveLayerTracker.cpp @@ -249,22 +249,30 @@ static void IncrementScaleRestyleCountIfNeeded(nsIFrame* aFrame, LayerActivity* aActivity) { const nsStyleDisplay* display = aFrame->StyleDisplay(); - RefPtr transformList = display->GetCombinedTransform(); - if (!transformList) { + if (!display->mSpecifiedTransform && + !display->HasIndividualTransform() && + !(display->mMotion && display->mMotion->HasPath())) { // The transform was removed. aActivity->mPreviousTransformScale = Nothing(); - IncrementMutationCount(&aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]); + IncrementMutationCount( + &aActivity->mRestyleCounts[LayerActivity::ACTIVITY_SCALE]); return; } // Compute the new scale due to the CSS transform property. bool dummyBool; nsStyleTransformMatrix::TransformReferenceBox refBox(aFrame); - Matrix4x4 transform = - nsStyleTransformMatrix::ReadTransforms(transformList->mHead, - refBox, - AppUnitsPerCSSPixel(), - &dummyBool); + Matrix4x4 transform = nsStyleTransformMatrix::ReadTransforms( + display->mIndividualTransform + ? display->mIndividualTransform->mHead + : nullptr, + nsLayoutUtils::ResolveMotionPath(aFrame), + display->mSpecifiedTransform + ? display->mSpecifiedTransform->mHead + : nullptr, + refBox, + AppUnitsPerCSSPixel(), + &dummyBool); Matrix transform2D; if (!transform.Is2D(&transform2D)) { // We don't attempt to handle 3D transforms; just assume the scale changed. diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 2988ca57c0f4..f68ad6705ce6 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -7849,11 +7849,14 @@ nsDisplayTransform::ComputePerspectiveMatrix(const nsIFrame* aFrame, return true; } -nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsIFrame* aFrame, - float aAppUnitsPerPixel, - const nsRect* aBoundsOverride) +nsDisplayTransform::FrameTransformProperties::FrameTransformProperties( + const nsIFrame* aFrame, + float aAppUnitsPerPixel, + const nsRect* aBoundsOverride) : mFrame(aFrame) - , mTransformList(aFrame->StyleDisplay()->GetCombinedTransform()) + , mIndividualTransformList(aFrame->StyleDisplay()->mIndividualTransform) + , mMotion(nsLayoutUtils::ResolveMotionPath(aFrame)) + , mTransformList(aFrame->StyleDisplay()->mSpecifiedTransform) , mToTransformOrigin(GetDeltaToTransformOrigin(aFrame, aAppUnitsPerPixel, aBoundsOverride)) { } @@ -7922,10 +7925,16 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp frame && frame->IsSVGTransformed(&svgTransform, &parentsChildrenOnlyTransform); /* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */ - if (aProperties.mTransformList) { - result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead, - refBox, aAppUnitsPerPixel, - &dummyBool); + if (aProperties.HasTransform()) { + result = nsStyleTransformMatrix::ReadTransforms( + aProperties.mIndividualTransformList + ? aProperties.mIndividualTransformList->mHead + : nullptr, + aProperties.mMotion, + aProperties.mTransformList + ? aProperties.mTransformList->mHead + : nullptr, + refBox, aAppUnitsPerPixel, &dummyBool); } else if (hasSVGTransforms) { // Correct the translation components for zoom: float pixelsPerCSSPx = AppUnitsPerCSSPixel() / diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 7012d7353399..1faa5e806627 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -68,6 +68,7 @@ enum class nsDisplayOwnLayerFlags; namespace mozilla { class FrameLayerBuilder; +struct MotionPathData; namespace layers { class Layer; class ImageLayer; @@ -6577,6 +6578,11 @@ public: FrameTransformProperties(const nsIFrame* aFrame, float aAppUnitsPerPixel, const nsRect* aBoundsOverride); + // This constructor is used on the compositor (for animations). + // Bug 1186329, Bug 1425837, If we want to support compositor animationsf + // or individual transforms and motion path, we may need to update this. + // For now, let mIndividualTransformList and mMotion as nullptr and + // Nothing(). FrameTransformProperties(RefPtr&& aTransformList, const Point3D& aToTransformOrigin) @@ -6585,7 +6591,14 @@ public: , mToTransformOrigin(aToTransformOrigin) {} + bool HasTransform() const + { + return mIndividualTransformList || mTransformList || mMotion.isSome(); + } + const nsIFrame* mFrame; + const RefPtr mIndividualTransformList; + const mozilla::Maybe mMotion; const RefPtr mTransformList; const Point3D mToTransformOrigin; }; diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index dd437c759afb..f8f93d7892fb 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -3636,10 +3636,10 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) , mSpecifiedRotate(aSource.mSpecifiedRotate) , mSpecifiedTranslate(aSource.mSpecifiedTranslate) , mSpecifiedScale(aSource.mSpecifiedScale) + , mIndividualTransform(aSource.mIndividualTransform) , mMotion(aSource.mMotion ? MakeUnique(*aSource.mMotion) : nullptr) - , mCombinedTransform(aSource.mCombinedTransform) , mTransformOrigin{ aSource.mTransformOrigin[0], aSource.mTransformOrigin[1], aSource.mTransformOrigin[2] } @@ -3704,9 +3704,8 @@ nsStyleDisplay::~nsStyleDisplay() mSpecifiedTranslate); ReleaseSharedListOnMainThread("nsStyleDisplay::mSpecifiedScale", mSpecifiedScale); - ReleaseSharedListOnMainThread("nsStyleDisplay::mCombinedTransform", - mCombinedTransform); - + ReleaseSharedListOnMainThread("nsStyleDisplay::mIndividualTransform", + mIndividualTransform); MOZ_COUNT_DTOR(nsStyleDisplay); } @@ -3734,7 +3733,7 @@ nsStyleDisplay::FinishStyle( } } - GenerateCombinedTransform(); + GenerateCombinedIndividualTransform(); } static inline nsChangeHint @@ -4031,17 +4030,17 @@ nsStyleDisplay::CalcDifference(const nsStyleDisplay& aNewData) const } void -nsStyleDisplay::GenerateCombinedTransform() +nsStyleDisplay::GenerateCombinedIndividualTransform() { // FIXME(emilio): This should probably be called from somewhere like what we // do for image layers, instead of FinishStyle. // // This does and undoes the work a ton of times in Stylo. - mCombinedTransform = nullptr; + mIndividualTransform = nullptr; // Follow the order defined in the spec to append transform functions. // https://drafts.csswg.org/css-transforms-2/#ctm - AutoTArray shareLists; + AutoTArray shareLists; if (mSpecifiedTranslate) { shareLists.AppendElement(mSpecifiedTranslate.get()); } @@ -4051,24 +4050,20 @@ nsStyleDisplay::GenerateCombinedTransform() if (mSpecifiedScale) { shareLists.AppendElement(mSpecifiedScale.get()); } - if (mSpecifiedTransform) { - shareLists.AppendElement(mSpecifiedTransform.get()); - } if (shareLists.Length() == 0) { return; } - if (shareLists.Length() == 1) { - mCombinedTransform = shareLists[0]; + mIndividualTransform = shareLists[0]; return; } - // In common, we may have 3 transform functions(for rotate, translate and - // scale) in mSpecifiedTransform, one rotate function in mSpecifiedRotate, - // one translate function in mSpecifiedTranslate, and one scale function in - // mSpecifiedScale. So 6 slots are enough for the most cases. - AutoTArray valueLists; + // In common, we may have 3 transform functions: + // 1. one rotate function in mSpecifiedRotate, + // 2. one translate function in mSpecifiedTranslate, + // 3. one scale function in mSpecifiedScale. + AutoTArray valueLists; for (auto list: shareLists) { if (list) { valueLists.AppendElement(list->mHead->Clone()); @@ -4083,8 +4078,9 @@ nsStyleDisplay::GenerateCombinedTransform() valueLists[i]->mNext = valueLists[i + 1]; } - mCombinedTransform = new nsCSSValueSharedList(valueLists[0]); + mIndividualTransform = new nsCSSValueSharedList(valueLists[0]); } + // -------------------- // nsStyleVisibility // diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 1f69997897db..d1c7b8142cce 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2181,14 +2181,11 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay RefPtr mSpecifiedRotate; RefPtr mSpecifiedTranslate; RefPtr mSpecifiedScale; + // Used to store the final combination of mSpecifiedRotate, + // mSpecifiedTranslate, and mSpecifiedScale. + RefPtr mIndividualTransform; mozilla::UniquePtr mMotion; - // Used to store the final combination of mSpecifiedTranslate, - // mSpecifiedRotate, mSpecifiedScale and mSpecifiedTransform. - // Use GetCombinedTransform() to get the final transform, instead of - // accessing mCombinedTransform directly. - RefPtr mCombinedTransform; - nsStyleCoord mTransformOrigin[3]; // percent, coord, calc, 3rd param is coord, calc only nsStyleCoord mChildPerspective; // none, coord nsStyleCoord mPerspectiveOrigin[2]; // percent, coord, calc @@ -2528,21 +2525,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay inline bool IsFixedPosContainingBlockForTransformSupportingFrames() const; /** - * Returns the final combined transform. + * Returns the final combined individual transform. **/ - already_AddRefed GetCombinedTransform() const { - if (mCombinedTransform) { - return do_AddRef(mCombinedTransform); - } - - // backward compatible to gecko-backed style system. - return mSpecifiedTransform ? do_AddRef(mSpecifiedTransform) : nullptr; + already_AddRefed GetCombinedTransform() const + { + return mIndividualTransform ? do_AddRef(mIndividualTransform) : nullptr; } private: // Helpers for above functions, which do some but not all of the tests // for them (since transform must be tested separately for each). - void GenerateCombinedTransform(); + void GenerateCombinedIndividualTransform(); }; struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTable diff --git a/layout/style/nsStyleTransformMatrix.cpp b/layout/style/nsStyleTransformMatrix.cpp index fe67064f40ad..8b6ed4b18d9e 100644 --- a/layout/style/nsStyleTransformMatrix.cpp +++ b/layout/style/nsStyleTransformMatrix.cpp @@ -926,14 +926,12 @@ SetIdentityMatrix(nsCSSValue::Array* aMatrix) } } -Matrix4x4 -ReadTransforms(const nsCSSValueList* aList, - TransformReferenceBox& aRefBox, - float aAppUnitsPerMatrixUnit, - bool* aContains3dTransform) +static void +ReadTransformsImpl(Matrix4x4& aMatrix, + const nsCSSValueList* aList, + TransformReferenceBox& aRefBox, + bool* aContains3dTransform) { - Matrix4x4 result; - for (const nsCSSValueList* curr = aList; curr != nullptr; curr = curr->mNext) { const nsCSSValue &currElem = curr->mValue; if (currElem.GetUnit() != eCSSUnit_Function) { @@ -947,9 +945,55 @@ ReadTransforms(const nsCSSValueList* aList, "Incoming function is too short!"); /* Read in a single transform matrix. */ - MatrixForTransformFunction(result, currElem.GetArrayValue(), aRefBox, + MatrixForTransformFunction(aMatrix, currElem.GetArrayValue(), aRefBox, aContains3dTransform); } +} + +Matrix4x4 +ReadTransforms(const nsCSSValueList* aList, + TransformReferenceBox& aRefBox, + float aAppUnitsPerMatrixUnit, + bool* aContains3dTransform) +{ + Matrix4x4 result; + ReadTransformsImpl(result, aList, aRefBox, aContains3dTransform); + + float scale = float(AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit; + result.PreScale(1/scale, 1/scale, 1/scale); + result.PostScale(scale, scale, scale); + + return result; +} + +Matrix4x4 +ReadTransforms(const nsCSSValueList* aIndividualTransforms, + const Maybe& aMotion, + const nsCSSValueList* aTransform, + TransformReferenceBox& aRefBox, + float aAppUnitsPerMatrixUnit, + bool* aContains3dTransform) +{ + Matrix4x4 result; + + if (aIndividualTransforms) { + ReadTransformsImpl(result, aIndividualTransforms, aRefBox, + aContains3dTransform); + } + + if (aMotion.isSome()) { + // Create the equivalent translate and rotate function, according to the + // order in spec. We combine the translate and then the rotate. + // https://drafts.fxtf.org/motion-1/#calculating-path-transform + result.PreTranslate(aMotion->mTranslate.x, aMotion->mTranslate.y, 0.0); + if (aMotion->mRotate != 0.0) { + result.RotateZ(aMotion->mRotate); + } + } + + if (aTransform) { + ReadTransformsImpl(result, aTransform, aRefBox, aContains3dTransform); + } float scale = float(AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit; result.PreScale(1/scale, 1/scale, 1/scale); diff --git a/layout/style/nsStyleTransformMatrix.h b/layout/style/nsStyleTransformMatrix.h index cbe0cf2bcc86..3cc88f8b0393 100644 --- a/layout/style/nsStyleTransformMatrix.h +++ b/layout/style/nsStyleTransformMatrix.h @@ -24,6 +24,10 @@ class nsPresContext; struct gfxQuaternion; struct nsRect; +namespace mozilla { +struct MotionPathData; +} + /** * A helper to generate gfxMatrixes from css transform functions. */ @@ -200,6 +204,16 @@ namespace nsStyleTransformMatrix { float aAppUnitsPerMatrixUnit, bool* aContains3dTransform); + // Generate the gfx::Matrix for CSS Transform Module Level 2. + // https://drafts.csswg.org/css-transforms-2/#ctm + mozilla::gfx::Matrix4x4 + ReadTransforms(const nsCSSValueList* aIndividualTransforms, + const mozilla::Maybe& aMotion, + const nsCSSValueList* aTransform, + TransformReferenceBox& aRefBox, + float aAppUnitsPerMatrixUnit, + bool* aContains3dTransform); + /** * Given two nsStyleCoord values, compute the 2d position with respect to the * given TransformReferenceBox that these values describe, in device pixels.