Bug 1526847 - Let ComputeSuitableScaleForAnimation check other transform-like properties. r=hiro

Check all transform-like properties which may affect the scaling
factors when computing the suitable scale for animations.

Differential Revision: https://phabricator.services.mozilla.com/D19526

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Boris Chiou 2019-04-12 21:43:23 +00:00
parent a57c781b04
commit 9782f68abf
4 changed files with 152 additions and 25 deletions

View File

@ -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<Size, Size>;
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<float>(aMaxScale.width, size.width);
aMaxScale.height = std::max<float>(aMaxScale.height, size.height);
aMinScale.width = std::min<float>(aMinScale.width, size.width);
aMinScale.height = std::min<float>(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<RefPtr<dom::Animation>>& 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<MinAndMaxScale, 2> GetMinAndMaxScaleForAnimationProperty(
const nsIFrame* aFrame,
const nsTArray<RefPtr<dom::Animation>>& 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<float>::max(),
std::numeric_limits<float>::max()),
Size(std::numeric_limits<float>::min(),
std::numeric_limits<float>::min()));
Array<MinAndMaxScale, 2> 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<RefPtr<dom::Animation>> compositorAnimations =
EffectCompositor::GetAnimationsForCompositor(
aFrame,
nsCSSPropertyIDSet{eCSSProperty_transform, eCSSProperty_scale});
if (compositorAnimations.IsEmpty()) {
return Size(1.0, 1.0);
}
const Array<MinAndMaxScale, 2> minAndMaxScales =
GetMinAndMaxScaleForAnimationProperty(aFrame, compositorAnimations);
// This might cause an issue if users use std::numeric_limits<float>::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<float>::min(),
std::numeric_limits<float>::min());
Size minScale(std::numeric_limits<float>::max(),
std::numeric_limits<float>::max());
// FIXME: Bug 1526847: Add rotate and scale into this set.
nsTArray<RefPtr<dom::Animation>> 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<float>::min() &&
aMax.height == std::numeric_limits<float>::min() &&
aMin.width == std::numeric_limits<float>::max() &&
aMin.height == std::numeric_limits<float>::max();
};
if (maxScale.width == std::numeric_limits<float>::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);
}

View File

@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<title>Testcase, bug 1122526</title>
<style>
#outer, #inner {
display: inline-block;
background: white;
color: black;
}
#inner {
vertical-align: top;
height: 100px;
width: 100px;
background: repeating-linear-gradient(to top left, yellow, blue 10px);
will-change: transform;
}
</style>
<div id="outer">
<div id="inner">
</div>
</div>

View File

@ -0,0 +1,27 @@
<!DOCTYPE HTML>
<title>Testcase, bug 1526847</title>
<style>
#outer, #inner {
display: inline-block;
background: white;
color: black;
}
#outer { scale: 5 }
#inner {
vertical-align: top;
height: 100px;
width: 100px;
background: repeating-linear-gradient(to top left, yellow, blue 10px);
animation: HoldTransform linear infinite 1s;
}
@keyframes HoldTransform {
from, to { scale: 0.5; transform: scale(0.4); }
}
</style>
<div id="outer">
<div id="inner">
</div>
</div>

View File

@ -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