Bug 1216843 - Part 2: Implement effect iteration composition. r?birtles, smaug
MozReview-Commit-ID: 6u7WtXwL3y3
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -122,16 +122,32 @@ KeyframeEffect::SetTarget(const Nullable
}
} else if (mEffectOptions.mSpacingMode == SpacingMode::paced) {
// New target is null, so fall back to distribute spacing.
KeyframeUtils::ApplyDistributeSpacing(mKeyframes);
}
}
void
+KeyframeEffect::SetIterationComposite(
+ const IterationCompositeOperation& aIterationComposite)
+{
+ if (mEffectOptions.mIterationComposite == aIterationComposite) {
+ return;
+ }
+
+ if (mAnimation && mAnimation->IsRelevant()) {
+ nsNodeUtils::AnimationChanged(mAnimation);
+ }
+
+ mEffectOptions.mIterationComposite = aIterationComposite;
+ RequestRestyle(EffectCompositor::RestyleType::Layer);
+}
+
+void
KeyframeEffect::SetSpacing(JSContext* aCx,
const nsAString& aSpacing,
ErrorResult& aRv)
{
SpacingMode spacingMode = SpacingMode::distribute;
nsCSSPropertyID pacedProperty = eCSSProperty_UNKNOWN;
nsAutoString invalidPacedProperty;
KeyframeEffectParams::ParseSpacing(aSpacing,
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -62,14 +62,16 @@ public:
// This method calls GetTargetStyleContext which is not safe to use when
// we are in the middle of updating style. If we need to use this when
// updating style, we should pass the nsStyleContext into this method and use
// that to update the properties rather than calling
// GetStyleContextForElement.
void SetTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget);
void SetSpacing(JSContext* aCx, const nsAString& aSpacing, ErrorResult& aRv);
+ void SetIterationComposite(
+ const IterationCompositeOperation& aIterationComposite);
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_KeyframeEffect_h
--- a/dom/animation/KeyframeEffectParams.h
+++ b/dom/animation/KeyframeEffectParams.h
@@ -4,16 +4,21 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_KeyframeEffectParams_h
#define mozilla_KeyframeEffectParams_h
#include "nsCSSProps.h"
#include "nsString.h"
+// X11 has a #define for None
+#ifdef None
+#undef None
+#endif
+#include "mozilla/dom/KeyframeEffectBinding.h" // IterationCompositeOperation
namespace mozilla {
class ErrorResult;
enum class SpacingMode
{
distribute,
@@ -46,17 +51,18 @@ struct KeyframeEffectParams
* @param [out] aRv The error result.
*/
static void ParseSpacing(const nsAString& aSpacing,
SpacingMode& aSpacingMode,
nsCSSPropertyID& aPacedProperty,
nsAString& aInvalidPacedProperty,
ErrorResult& aRv);
- // FIXME: Bug 1216843: Add IterationCompositeOperations and
- // Bug 1216844: Add CompositeOperation
+ dom::IterationCompositeOperation mIterationComposite =
+ dom::IterationCompositeOperation::Replace;
+ // FIXME: Bug 1216844: Add CompositeOperation
SpacingMode mSpacingMode = SpacingMode::distribute;
nsCSSPropertyID mPacedProperty = eCSSProperty_UNKNOWN;
};
} // namespace mozilla
#endif // mozilla_KeyframeEffectParams_h
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -72,17 +72,17 @@ KeyframeEffectReadOnly::WrapObject(JSCon
JS::Handle<JSObject*> aGivenProto)
{
return KeyframeEffectReadOnlyBinding::Wrap(aCx, this, aGivenProto);
}
IterationCompositeOperation
KeyframeEffectReadOnly::IterationComposite() const
{
- return IterationCompositeOperation::Replace;
+ return mEffectOptions.mIterationComposite;
}
CompositeOperation
KeyframeEffectReadOnly::Composite() const
{
return CompositeOperation::Replace;
}
@@ -368,45 +368,75 @@ KeyframeEffectReadOnly::ComposeStyle(Ref
prop.mSegments.Length(),
"out of array bounds");
if (!aStyleRule) {
// Allocate the style rule now that we know we have animation data.
aStyleRule = new AnimValuesStyleRule();
}
+ StyleAnimationValue fromValue = segment->mFromValue;
+ StyleAnimationValue toValue = segment->mToValue;
+ // Iteration composition for accumulate
+ if (mEffectOptions.mIterationComposite ==
+ IterationCompositeOperation::Accumulate &&
+ computedTiming.mCurrentIteration > 0) {
+ const AnimationPropertySegment& lastSegment =
+ prop.mSegments.LastElement();
+ // FIXME: Bug 1293492: Add a utility function to calculate both of
+ // below StyleAnimationValues.
+ DebugOnly<bool> accumulateResult =
+ StyleAnimationValue::Accumulate(prop.mProperty,
+ fromValue,
+ lastSegment.mToValue,
+ computedTiming.mCurrentIteration);
+ // We can't check the accumulation result in case of filter property.
+ // That's because some filter property can't accumulate,
+ // e.g. 'contrast(2) brightness(2)' onto 'brightness(1) contrast(1)'
+ // because of mismatch of the order.
+ MOZ_ASSERT(accumulateResult || prop.mProperty == eCSSProperty_filter,
+ "could not accumulate value");
+ accumulateResult =
+ StyleAnimationValue::Accumulate(prop.mProperty,
+ toValue,
+ lastSegment.mToValue,
+ computedTiming.mCurrentIteration);
+ MOZ_ASSERT(accumulateResult || prop.mProperty == eCSSProperty_filter,
+ "could not accumulate value");
+ }
+
// Special handling for zero-length segments
if (segment->mToKey == segment->mFromKey) {
if (computedTiming.mProgress.Value() < 0) {
- aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
+ aStyleRule->AddValue(prop.mProperty, Move(fromValue));
} else {
- aStyleRule->AddValue(prop.mProperty, segment->mToValue);
+ aStyleRule->AddValue(prop.mProperty, Move(toValue));
}
continue;
}
double positionInSegment =
(computedTiming.mProgress.Value() - segment->mFromKey) /
(segment->mToKey - segment->mFromKey);
double valuePosition =
ComputedTimingFunction::GetPortion(segment->mTimingFunction,
positionInSegment,
computedTiming.mBeforeFlag);
MOZ_ASSERT(IsFinite(valuePosition), "Position value should be finite");
StyleAnimationValue val;
if (StyleAnimationValue::Interpolate(prop.mProperty,
- segment->mFromValue,
- segment->mToValue,
+ fromValue,
+ toValue,
valuePosition, val)) {
aStyleRule->AddValue(prop.mProperty, Move(val));
} else if (valuePosition < 0.5) {
- aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
+ aStyleRule->AddValue(prop.mProperty, Move(fromValue));
} else {
- aStyleRule->AddValue(prop.mProperty, segment->mToValue);
+ aStyleRule->AddValue(prop.mProperty, Move(toValue));
}
}
}
bool
KeyframeEffectReadOnly::IsRunningOnCompositor() const
{
// We consider animation is running on compositor if there is at least
@@ -477,16 +507,17 @@ KeyframeEffectParamsFromUnion(const Opti
if (!aOptions.IsUnrestrictedDouble()) {
const KeyframeEffectOptions& options =
KeyframeEffectOptionsFromUnion(aOptions);
KeyframeEffectParams::ParseSpacing(options.mSpacing,
result.mSpacingMode,
result.mPacedProperty,
aInvalidPacedProperty,
aRv);
+ result.mIterationComposite = options.mIterationComposite;
}
return result;
}
/* static */ Maybe<OwningAnimationTarget>
KeyframeEffectReadOnly::ConvertTarget(
const Nullable<ElementOrCSSPseudoElement>& aTarget)
{
--- a/dom/webidl/KeyframeEffect.webidl
+++ b/dom/webidl/KeyframeEffect.webidl
@@ -64,17 +64,16 @@ partial interface KeyframeEffectReadOnly
};
[Func="nsDocument::IsWebAnimationsEnabled",
Constructor ((Element or CSSPseudoElement)? target,
object? keyframes,
optional (unrestricted double or KeyframeEffectOptions) options)]
interface KeyframeEffect : KeyframeEffectReadOnly {
inherit attribute (Element or CSSPseudoElement)? target;
- // Bug 1216843 - implement animation composition
- // inherit attribute IterationCompositeOperation iterationComposite;
+ inherit attribute IterationCompositeOperation iterationComposite;
// Bug 1216844 - implement additive animation
// inherit attribute CompositeOperation composite;
[SetterThrows]
inherit attribute DOMString spacing;
[Throws]
void setKeyframes (object? keyframes);
};
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -11,16 +11,17 @@
#include "LayerManagerComposite.h" // for LayerManagerComposite, etc
#include "Layers.h" // for Layer, ContainerLayer, etc
#include "gfxPoint.h" // for gfxPoint, gfxSize
#include "gfxPrefs.h" // for gfxPrefs
#include "mozilla/StyleAnimationValue.h" // for StyleAnimationValue, etc
#include "mozilla/WidgetUtils.h" // for ComputeTransformForRotation
#include "mozilla/dom/KeyframeEffectReadOnly.h"
#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
+#include "mozilla/dom/KeyframeEffectBinding.h" // for dom::IterationComposite
#include "mozilla/gfx/BaseRect.h" // for BaseRect
#include "mozilla/gfx/Point.h" // for RoundedToInt, PointTyped
#include "mozilla/gfx/Rect.h" // for RoundedToInt, RectTyped
#include "mozilla/gfx/ScaleFactor.h" // for ScaleFactor
#include "mozilla/layers/APZUtils.h" // for CompleteAsyncTransform
#include "mozilla/layers/Compositor.h" // for Compositor
#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
#include "mozilla/layers/CompositorThread.h"
@@ -548,28 +549,55 @@ AsyncCompositionManager::AlignFixedAndSt
// all of the translation correctly. In such a case,
// |a[Previous|Current]TransformForRoot| would need to be adjusted
// to reflect only the unconsumed part of the translation.
return translationConsumed ? TraversalFlag::Skip : TraversalFlag::Continue;
});
}
static void
-SampleValue(float aPortion, Animation& aAnimation, StyleAnimationValue& aStart,
- StyleAnimationValue& aEnd, Animatable* aValue, Layer* aLayer)
+SampleValue(float aPortion, Animation& aAnimation,
+ const StyleAnimationValue& aStart, const StyleAnimationValue& aEnd,
+ const StyleAnimationValue& aLastValue, uint64_t aCurrentIteration,
+ Animatable* aValue, Layer* aLayer)
{
- StyleAnimationValue interpolatedValue;
NS_ASSERTION(aStart.GetUnit() == aEnd.GetUnit() ||
aStart.GetUnit() == StyleAnimationValue::eUnit_None ||
aEnd.GetUnit() == StyleAnimationValue::eUnit_None,
"Must have same unit");
+
+ StyleAnimationValue startValue = aStart;
+ StyleAnimationValue endValue = aEnd;
+ // Iteration composition for accumulate
+ if (static_cast<dom::IterationCompositeOperation>
+ (aAnimation.iterationComposite()) ==
+ dom::IterationCompositeOperation::Accumulate &&
+ aCurrentIteration > 0) {
+ // FIXME: Bug 1293492: Add a utility function to calculate both of
+ // below StyleAnimationValues.
+ DebugOnly<bool> accumulateResult =
+ StyleAnimationValue::Accumulate(aAnimation.property(),
+ startValue,
+ aLastValue,
+ aCurrentIteration);
+ MOZ_ASSERT(accumulateResult, "could not accumulate value");
+ accumulateResult =
+ StyleAnimationValue::Accumulate(aAnimation.property(),
+ endValue,
+ aLastValue,
+ aCurrentIteration);
+ MOZ_ASSERT(accumulateResult, "could not accumulate value");
+ }
+
+ StyleAnimationValue interpolatedValue;
// This should never fail because we only pass transform and opacity values
// to the compositor and they should never fail to interpolate.
DebugOnly<bool> uncomputeResult =
- StyleAnimationValue::Interpolate(aAnimation.property(), aStart, aEnd,
+ StyleAnimationValue::Interpolate(aAnimation.property(),
+ startValue, endValue,
aPortion, interpolatedValue);
MOZ_ASSERT(uncomputeResult, "could not uncompute value");
if (aAnimation.property() == eCSSProperty_opacity) {
*aValue = interpolatedValue.GetFloatValue();
return;
}
@@ -678,18 +706,22 @@ SampleAnimations(Layer* aLayer, TimeStam
double portion =
ComputedTimingFunction::GetPortion(animData.mFunctions[segmentIndex],
positionInSegment,
computedTiming.mBeforeFlag);
// interpolate the property
Animatable interpolatedValue;
- SampleValue(portion, animation, animData.mStartValues[segmentIndex],
- animData.mEndValues[segmentIndex], &interpolatedValue, layer);
+ SampleValue(portion, animation,
+ animData.mStartValues[segmentIndex],
+ animData.mEndValues[segmentIndex],
+ animData.mEndValues.LastElement(),
+ computedTiming.mCurrentIteration,
+ &interpolatedValue, layer);
LayerComposite* layerComposite = layer->AsLayerComposite();
switch (animation.property()) {
case eCSSProperty_opacity:
{
layerComposite->SetShadowOpacity(interpolatedValue.get_float());
layerComposite->SetShadowOpacitySetByAnimation(true);
break;
}
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -199,16 +199,17 @@ struct Animation {
float iterationStart;
// This uses the NS_STYLE_ANIMATION_DIRECTION_* constants.
uint8_t direction;
nsCSSPropertyID property;
AnimationData data;
float playbackRate;
// This is used in the transformed progress calculation.
TimingFunction easingFunction;
+ uint8_t iterationComposite;
};
// Change a layer's attributes
struct CommonLayerAttributes {
IntRect layerBounds;
LayerIntRegion visibleRegion;
EventRegions eventRegions;
TransformMatrix transform;
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -425,16 +425,19 @@ AddAnimationForProperty(nsIFrame* aFrame
animation->duration() = computedTiming.mDuration;
animation->iterations() = computedTiming.mIterations;
animation->iterationStart() = computedTiming.mIterationStart;
animation->direction() = static_cast<uint8_t>(timing.mDirection);
animation->property() = aProperty.mProperty;
animation->playbackRate() = aAnimation->PlaybackRate();
animation->data() = aData;
animation->easingFunction() = ToTimingFunction(timing.mFunction);
+ animation->iterationComposite() =
+ static_cast<uint8_t>(aAnimation->GetEffect()->
+ AsKeyframeEffect()->IterationComposite());
for (uint32_t segIdx = 0; segIdx < aProperty.mSegments.Length(); segIdx++) {
const AnimationPropertySegment& segment = aProperty.mSegments[segIdx];
AnimationSegment* animSegment = animation->segments().AppendElement();
if (aProperty.mProperty == eCSSProperty_transform) {
animSegment->startState() = InfallibleTArray<TransformFunction>();
animSegment->endState() = InfallibleTArray<TransformFunction>();
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -2796,16 +2796,36 @@ StyleAnimationValue::AddWeighted(nsCSSPr
return true;
}
}
MOZ_ASSERT(false, "Can't interpolate using the given common unit");
return false;
}
+bool
+StyleAnimationValue::Accumulate(nsCSSPropertyID aProperty,
+ StyleAnimationValue& aDest,
+ const StyleAnimationValue& aValueToAccumulate,
+ uint64_t aCount)
+{
+ Unit commonUnit =
+ GetCommonUnit(aProperty, aDest.GetUnit(), aValueToAccumulate.GetUnit());
+ switch (commonUnit) {
+ // FIXME: implement them!
+ //case eUnit_Color:
+ //case eUnit_Shadow:
+ //case eUnit_Filter:
+ default:
+ return Add(aProperty, aDest, aValueToAccumulate, aCount);
+ }
+ MOZ_ASSERT_UNREACHABLE("Can't accumulate using the given common unit");
+ return false;
+}
+
already_AddRefed<css::StyleRule>
BuildStyleRule(nsCSSPropertyID aProperty,
dom::Element* aTargetElement,
const nsAString& aSpecifiedValue,
bool aUseSVGMode)
{
// Set up an empty CSS Declaration
RefPtr<css::Declaration> declaration(new css::Declaration());
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -124,16 +124,37 @@ public:
* positive.
*/
static MOZ_MUST_USE bool
AddWeighted(nsCSSPropertyID aProperty,
double aCoeff1, const StyleAnimationValue& aValue1,
double aCoeff2, const StyleAnimationValue& aValue2,
StyleAnimationValue& aResultValue);
+ /**
+ * Accumulates |aValueToAccumulate| onto |aDest| |aCount| times.
+ * The result is stored in |aDest| on success.
+ *
+ * @param aDest The base value to be accumulated.
+ * @param aValueToAccumulate The value to accumulate.
+ * @param aCount The number of times to accumulate
+ * aValueToAccumulate.
+ * @return true on success, false on failure.
+ *
+ * NOTE: This function will work as a wrapper of StyleAnimationValue::Add()
+ * if |aProperty| isn't color or shadow or filter. For these properties,
+ * this function may return a color value that at least one of its components
+ * has a value which is outside the range [0, 1] so that we can calculate
+ * plausible values as interpolation with the return value.
+ */
+ static MOZ_MUST_USE bool
+ Accumulate(nsCSSPropertyID aProperty, StyleAnimationValue& aDest,
+ const StyleAnimationValue& aValueToAccumulate,
+ uint64_t aCount);
+
// Type-conversion methods
// -----------------------
/**
* Creates a computed value for the given specified value
* (property ID + string). A style context is needed in case the
* specified value depends on inherited style or on the values of other
* properties.
*
--- a/testing/web-platform/meta/web-animations/interfaces/KeyframeEffect/iterationComposite.html.ini
+++ b/testing/web-platform/meta/web-animations/interfaces/KeyframeEffect/iterationComposite.html.ini
@@ -1,74 +1,11 @@
[iterationComposite.html]
type: testharness
- [iterationComposite of <length> type animation]
- expected: FAIL
-
- [iterationComposite of <percentage> type animation]
- expected: FAIL
-
[iterationComposite of <color> type animation]
expected: FAIL
- [iterationComposite of <number> type animation]
- expected: FAIL
-
- [iterationComposite of <shape> type animation]
- expected: FAIL
-
- [iterationComposite of <calc()> value animation]
- expected: FAIL
-
- [iterationComposite of opacity animation]
- expected: FAIL
-
- [iterationComposite of filter blur animation]
- expected: FAIL
-
- [iterationComposite of filter brightness for different unit animation]
- expected: FAIL
-
[iterationComposite of filter drop-shadow animation]
expected: FAIL
- [iterationComposite of same filter list animation]
- expected: FAIL
-
- [iterationComposite of discrete filter list because of mismatch of the order]
- expected: FAIL
-
- [iterationComposite of different length filter list animation]
- expected: FAIL
-
- [iterationComposite of transform: [ scale(1), scale(2) \] animation]
- expected: FAIL
-
- [iterationComposite of transform: scale(2) animation]
- expected: FAIL
-
- [iterationComposite of transform list animation]
- expected: FAIL
-
- [iterationComposite starts with non-zero value animation]
- expected: FAIL
-
- [iterationComposite with negative final value animation]
- expected: FAIL
-
- [interationComposite changes]
- expected: FAIL
-
- [duration changes with iterationComposite(accumulate)]
- expected: FAIL
-
[iterationComposite of box-shadow animation]
expected: FAIL
- [iterationComposite of <color> type animation that green component is decreasing]
- expected: FAIL
-
- [iterationComposite of <calc()> value animation that the values can'tbe reduced]
- expected: FAIL
-
- [iterationComposite of transform list animation whose order is mismatched]
- expected: FAIL
-