diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 59a84936f3b5..00c90f14c08c 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -357,19 +357,47 @@ static float GetSuitableScale(float aMaxScale, float aMinScale, return std::max(std::min(aMaxScale, displayVisibleRatio), aMinScale); } +// The first value in this pair is the min scale, and the second one is the max +// scale. +using MinAndMaxScale = Pair; + static inline void UpdateMinMaxScale(const nsIFrame* aFrame, const AnimationValue& aValue, - Size& aMinScale, Size& aMaxScale) { + MinAndMaxScale& aMinAndMaxScale) { Size size = aValue.GetScaleValue(aFrame); - aMaxScale.width = std::max(aMaxScale.width, size.width); - aMaxScale.height = std::max(aMaxScale.height, size.height); - aMinScale.width = std::min(aMinScale.width, size.width); - aMinScale.height = std::min(aMinScale.height, size.height); + Size& minScale = aMinAndMaxScale.first(); + Size& maxScale = aMinAndMaxScale.second(); + + minScale = Min(minScale, size); + maxScale = Max(maxScale, size); } -static void GetMinAndMaxScaleForAnimationProperty( - const nsIFrame* aFrame, nsTArray>& aAnimations, - Size& aMaxScale, Size& aMinScale) { +// The final transform matrix is calculated by merging the final results of each +// transform-like properties, so do the scale factors. In other words, the +// potential min/max scales could be gotten by multiplying the max/min scales of +// each properties. +// +// For example, there is an animation: +// from { "transform: scale(1, 1)", "scale: 3, 3" }; +// to { "transform: scale(2, 2)", "scale: 1, 1" }; +// +// the min scale is (1, 1) * (1, 1) = (1, 1), and +// The max scale is (2, 2) * (3, 3) = (6, 6). +// This means we multiply the min/max scale factor of transform property and the +// min/max scale factor of scale property to get the final max/min scale factor. +static Array GetMinAndMaxScaleForAnimationProperty( + const nsIFrame* aFrame, + const nsTArray>& aAnimations) { + // We use a fixed array to store the min/max scales for each property. + // The first element in the array is for eCSSProperty_transform, and the + // second one is for eCSSProperty_scale. + const MinAndMaxScale defaultValue = + MakePair(Size(std::numeric_limits::max(), + std::numeric_limits::max()), + Size(std::numeric_limits::min(), + std::numeric_limits::min())); + Array minAndMaxScales(defaultValue, defaultValue); + for (dom::Animation* anim : aAnimations) { // This method is only expected to be passed animations that are running on // the compositor and we only pass playing animations to the compositor, @@ -377,54 +405,101 @@ static void GetMinAndMaxScaleForAnimationProperty( // not yet finished or which are filling forwards). MOZ_ASSERT(anim->IsRelevant()); - dom::KeyframeEffect* effect = + const dom::KeyframeEffect* effect = anim->GetEffect() ? anim->GetEffect()->AsKeyframeEffect() : nullptr; MOZ_ASSERT(effect, "A playing animation should have a keyframe effect"); - for (size_t propIdx = effect->Properties().Length(); propIdx-- != 0;) { - const AnimationProperty& prop = effect->Properties()[propIdx]; - // FIXME: Bug 1526847: Make this accept rotate and scale. - if (prop.mProperty != eCSSProperty_transform) { + for (const AnimationProperty& prop : effect->Properties()) { + if (prop.mProperty != eCSSProperty_transform && + prop.mProperty != eCSSProperty_scale) { continue; } + // 0: eCSSProperty_transform. + // 1: eCSSProperty_scale. + MinAndMaxScale& scales = + minAndMaxScales[prop.mProperty == eCSSProperty_transform ? 0 : 1]; + // We need to factor in the scale of the base style if the base style // will be used on the compositor. - AnimationValue baseStyle = effect->BaseStyle(prop.mProperty); + const AnimationValue& baseStyle = effect->BaseStyle(prop.mProperty); if (!baseStyle.IsNull()) { - UpdateMinMaxScale(aFrame, baseStyle, aMinScale, aMaxScale); + UpdateMinMaxScale(aFrame, baseStyle, scales); } for (const AnimationPropertySegment& segment : prop.mSegments) { // In case of add or accumulate composite, StyleAnimationValue does // not have a valid value. if (segment.HasReplaceableFromValue()) { - UpdateMinMaxScale(aFrame, segment.mFromValue, aMinScale, aMaxScale); + UpdateMinMaxScale(aFrame, segment.mFromValue, scales); } + if (segment.HasReplaceableToValue()) { - UpdateMinMaxScale(aFrame, segment.mToValue, aMinScale, aMaxScale); + UpdateMinMaxScale(aFrame, segment.mToValue, scales); } } } } + + return minAndMaxScales; } Size nsLayoutUtils::ComputeSuitableScaleForAnimation( const nsIFrame* aFrame, const nsSize& aVisibleSize, const nsSize& aDisplaySize) { + const nsTArray> compositorAnimations = + EffectCompositor::GetAnimationsForCompositor( + aFrame, + nsCSSPropertyIDSet{eCSSProperty_transform, eCSSProperty_scale}); + + if (compositorAnimations.IsEmpty()) { + return Size(1.0, 1.0); + } + + const Array minAndMaxScales = + GetMinAndMaxScaleForAnimationProperty(aFrame, compositorAnimations); + + // This might cause an issue if users use std::numeric_limits::min() + // (or max()) as the scale value. However, in this case, we may render an + // extreme small (or large) element, so this may not be a problem. If so, + // please fix this. Size maxScale(std::numeric_limits::min(), std::numeric_limits::min()); Size minScale(std::numeric_limits::max(), std::numeric_limits::max()); - // FIXME: Bug 1526847: Add rotate and scale into this set. - nsTArray> compositorAnimations = - EffectCompositor::GetAnimationsForCompositor( - aFrame, nsCSSPropertyIDSet{eCSSProperty_transform}); - GetMinAndMaxScaleForAnimationProperty(aFrame, compositorAnimations, maxScale, - minScale); + auto isUnset = [](const Size& aMax, const Size& aMin) { + return aMax.width == std::numeric_limits::min() && + aMax.height == std::numeric_limits::min() && + aMin.width == std::numeric_limits::max() && + aMin.height == std::numeric_limits::max(); + }; - if (maxScale.width == std::numeric_limits::min()) { - // We didn't encounter a transform + // Iterate the slots to get the final scale value. + for (const auto& pair : minAndMaxScales) { + const Size& currMinScale = pair.first(); + const Size& currMaxScale = pair.second(); + + if (isUnset(currMaxScale, currMinScale)) { + // We don't have this animation property, so skip. + continue; + } + + if (isUnset(maxScale, minScale)) { + // Initialize maxScale and minScale. + maxScale = currMaxScale; + minScale = currMinScale; + } else { + // The scale factors of each transform-like property should be multiplied + // by others because we merge their sampled values as a final matrix by + // matrix multiplication, so here we multiply the scale factors by the + // previous one to get the possible max and min scale factors. + maxScale = maxScale * currMaxScale; + minScale = minScale * currMinScale; + } + } + + if (isUnset(maxScale, minScale)) { + // We didn't encounter any transform-like property. return Size(1.0, 1.0); } diff --git a/layout/reftests/transform/animate-layer-scale-inherit-4-ref.html b/layout/reftests/transform/animate-layer-scale-inherit-4-ref.html new file mode 100644 index 000000000000..16688d4def91 --- /dev/null +++ b/layout/reftests/transform/animate-layer-scale-inherit-4-ref.html @@ -0,0 +1,23 @@ + +Testcase, bug 1122526 + + +
+
+
+
diff --git a/layout/reftests/transform/animate-layer-scale-inherit-4.html b/layout/reftests/transform/animate-layer-scale-inherit-4.html new file mode 100644 index 000000000000..8ab439fc7a89 --- /dev/null +++ b/layout/reftests/transform/animate-layer-scale-inherit-4.html @@ -0,0 +1,27 @@ + +Testcase, bug 1526847 + + +
+
+
+
diff --git a/layout/reftests/transform/reftest.list b/layout/reftests/transform/reftest.list index 0004bfc489f3..9e0fb0e20abc 100644 --- a/layout/reftests/transform/reftest.list +++ b/layout/reftests/transform/reftest.list @@ -147,3 +147,5 @@ fuzzy-if(d2d,0-1,0-5) fuzzy-if(skiaContent,0-26,0-208) == table-overflowed-by-an fails == translate-rounding-3.html translate-rounding-viewport-ref.html # bug 1397146 == invalidate-transform-1.html invalidate-transform-1-ref.html == invalidate-svg-scale-1.html invalidate-svg-scale-1-ref.html +# Bug 1526847 +pref(layout.css.individual-transform.enabled,true) == animate-layer-scale-inherit-4.html animate-layer-scale-inherit-4-ref.html