Bug 1078122 part 3 - Move animation value building down to the Animation objects; r=dholbert

This patch extracts the logic for calculating animation styles from
AnimationPlayerCollection and puts the bulk of it into the Animation objects.
Some of the initial logic surrounding the animation player state (e.g. is it
paused or not, etc.) is put into AnimationPlayer.

In future we may shift this logic even further down to the AnimationEffect
objects but currently we don't create such objects unless necessary.
This commit is contained in:
Brian Birtles 2014-10-20 13:55:46 +09:00
parent b46fbc6e51
commit 3458ab6540
6 changed files with 134 additions and 99 deletions

View File

@ -7,6 +7,8 @@
#include "mozilla/dom/AnimationBinding.h"
#include "mozilla/dom/AnimationEffect.h"
#include "mozilla/FloatingPoint.h"
#include "AnimationCommon.h"
#include "nsCSSPropertySet.h"
namespace mozilla {
@ -262,5 +264,88 @@ Animation::HasAnimationOfProperty(nsCSSProperty aProperty) const
return false;
}
void
Animation::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
nsCSSPropertySet& aSetProperties)
{
ComputedTiming computedTiming = GetComputedTiming();
// If the time fraction is null, we don't have fill data for the current
// time so we shouldn't animate.
if (computedTiming.mTimeFraction == ComputedTiming::kNullTimeFraction) {
return;
}
MOZ_ASSERT(0.0 <= computedTiming.mTimeFraction &&
computedTiming.mTimeFraction <= 1.0,
"timing fraction should be in [0-1]");
for (size_t propIdx = 0, propEnd = mProperties.Length();
propIdx != propEnd; ++propIdx)
{
const AnimationProperty& prop = mProperties[propIdx];
MOZ_ASSERT(prop.mSegments[0].mFromKey == 0.0, "incorrect first from key");
MOZ_ASSERT(prop.mSegments[prop.mSegments.Length() - 1].mToKey == 1.0,
"incorrect last to key");
if (aSetProperties.HasProperty(prop.mProperty)) {
// Animations are composed by AnimationPlayerCollection by iterating
// from the last animation to first. For animations targetting the
// same property, the later one wins. So if this property is already set,
// we should not override it.
return;
}
aSetProperties.AddProperty(prop.mProperty);
MOZ_ASSERT(prop.mSegments.Length() > 0,
"property should not be in animations if it has no segments");
// FIXME: Maybe cache the current segment?
const AnimationPropertySegment *segment = prop.mSegments.Elements(),
*segmentEnd = segment + prop.mSegments.Length();
while (segment->mToKey < computedTiming.mTimeFraction) {
MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys");
++segment;
if (segment == segmentEnd) {
MOZ_ASSERT_UNREACHABLE("incorrect time fraction");
break; // in order to continue in outer loop (just below)
}
MOZ_ASSERT(segment->mFromKey == (segment-1)->mToKey, "incorrect keys");
}
if (segment == segmentEnd) {
continue;
}
MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys");
MOZ_ASSERT(segment >= prop.mSegments.Elements() &&
size_t(segment - prop.mSegments.Elements()) <
prop.mSegments.Length(),
"out of array bounds");
if (!aStyleRule) {
// Allocate the style rule now that we know we have animation data.
aStyleRule = new css::AnimValuesStyleRule();
}
double positionInSegment =
(computedTiming.mTimeFraction - segment->mFromKey) /
(segment->mToKey - segment->mFromKey);
double valuePosition =
segment->mTimingFunction.GetValue(positionInSegment);
StyleAnimationValue *val = aStyleRule->AddEmptyValue(prop.mProperty);
#ifdef DEBUG
bool result =
#endif
StyleAnimationValue::Interpolate(prop.mProperty,
segment->mFromValue,
segment->mToValue,
valuePosition, *val);
MOZ_ASSERT(result, "interpolate must succeed now");
}
}
} // namespace dom
} // namespace mozilla

View File

@ -21,8 +21,12 @@
#include "nsStyleStruct.h" // for nsTimingFunction
struct JSContext;
class nsCSSPropertySet;
namespace mozilla {
namespace css {
class AnimValuesStyleRule;
} // namespace css
/**
* Input timing parameters.
@ -259,6 +263,13 @@ public:
return mProperties;
}
// Updates |aStyleRule| with the animation values produced by this
// Animation for the current time except any properties already contained
// in |aSetProperties|.
// Any updated properties are added to |aSetProperties|.
void ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
nsCSSPropertySet& aSetProperties);
protected:
virtual ~Animation() { }

View File

@ -210,6 +210,22 @@ AnimationPlayer::CanThrottle() const
return mSource->LastNotification() == Animation::LAST_NOTIFICATION_END;
}
void
AnimationPlayer::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
nsCSSPropertySet& aSetProperties,
bool& aNeedsRefreshes)
{
if (!mSource || mSource->IsFinishedTransition()) {
return;
}
if (PlayState() == AnimationPlayState::Running) {
aNeedsRefreshes = true;
}
mSource->ComposeStyle(aStyleRule, aSetProperties);
}
void
AnimationPlayer::FlushStyle() const
{

View File

@ -21,8 +21,12 @@
#endif
struct JSContext;
class nsCSSPropertySet;
namespace mozilla {
namespace css {
class AnimValuesStyleRule;
} // namespace css
class CSSAnimationPlayer;
@ -100,6 +104,17 @@ public:
// running on the compositor).
bool CanThrottle() const;
// Updates |aStyleRule| with the animation values of this player's source
// content, if any.
// Any properties already contained in |aSetProperties| are not changed. Any
// properties that are changed are added to |aSetProperties|.
// |aNeedsRefreshes| will be set to true if this player expects to update
// the style rule on the next refresh driver tick as well (because it
// is running and has source content to sample).
void ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
nsCSSPropertySet& aSetProperties,
bool& aNeedsRefreshes);
// The beginning of the delay period.
Nullable<TimeDuration> mStartTime; // Timeline timescale

View File

@ -512,108 +512,15 @@ AnimationPlayerCollection::EnsureStyleRuleFor(TimeStamp aRefreshTime,
// We'll set mNeedsRefreshes to true below in all cases where we need them.
mNeedsRefreshes = false;
// FIXME(spec): assume that properties in higher animations override
// those in lower ones.
// Therefore, we iterate from last animation to first.
// If multiple animations specify behavior for the same property the
// animation which occurs last in the value of animation-name wins.
// As a result, we iterate from last animation to first and, if a
// property has already been set, we don't leave it.
nsCSSPropertySet properties;
for (size_t playerIdx = mPlayers.Length(); playerIdx-- != 0; ) {
AnimationPlayer* player = mPlayers[playerIdx];
if (!player->GetSource() || player->GetSource()->IsFinishedTransition()) {
continue;
}
// The GetComputedTiming() call here handles pausing. But:
// FIXME: avoid recalculating every time when paused.
ComputedTiming computedTiming = player->GetSource()->GetComputedTiming();
if ((computedTiming.mPhase == ComputedTiming::AnimationPhase_Before ||
computedTiming.mPhase == ComputedTiming::AnimationPhase_Active) &&
!player->IsPaused()) {
mNeedsRefreshes = true;
}
// If the time fraction is null, we don't have fill data for the current
// time so we shouldn't animate.
// Likewise, if the player has no source content.
if (computedTiming.mTimeFraction == ComputedTiming::kNullTimeFraction) {
continue;
}
NS_ABORT_IF_FALSE(0.0 <= computedTiming.mTimeFraction &&
computedTiming.mTimeFraction <= 1.0,
"timing fraction should be in [0-1]");
const Animation* anim = player->GetSource();
for (size_t propIdx = 0, propEnd = anim->Properties().Length();
propIdx != propEnd; ++propIdx)
{
const AnimationProperty& prop = anim->Properties()[propIdx];
NS_ABORT_IF_FALSE(prop.mSegments[0].mFromKey == 0.0,
"incorrect first from key");
NS_ABORT_IF_FALSE(prop.mSegments[prop.mSegments.Length() - 1].mToKey
== 1.0,
"incorrect last to key");
if (properties.HasProperty(prop.mProperty)) {
// A later animation already set this property.
continue;
}
properties.AddProperty(prop.mProperty);
NS_ABORT_IF_FALSE(prop.mSegments.Length() > 0,
"property should not be in animations if it "
"has no segments");
// FIXME: Maybe cache the current segment?
const AnimationPropertySegment *segment = prop.mSegments.Elements(),
*segmentEnd = segment + prop.mSegments.Length();
while (segment->mToKey < computedTiming.mTimeFraction) {
NS_ABORT_IF_FALSE(segment->mFromKey < segment->mToKey,
"incorrect keys");
++segment;
if (segment == segmentEnd) {
NS_ABORT_IF_FALSE(false, "incorrect time fraction");
break; // in order to continue in outer loop (just below)
}
NS_ABORT_IF_FALSE(segment->mFromKey == (segment-1)->mToKey,
"incorrect keys");
}
if (segment == segmentEnd) {
continue;
}
NS_ABORT_IF_FALSE(segment->mFromKey < segment->mToKey,
"incorrect keys");
NS_ABORT_IF_FALSE(segment >= prop.mSegments.Elements() &&
size_t(segment - prop.mSegments.Elements()) <
prop.mSegments.Length(),
"out of array bounds");
if (!mStyleRule) {
// Allocate the style rule now that we know we have animation data.
mStyleRule = new css::AnimValuesStyleRule();
}
double positionInSegment =
(computedTiming.mTimeFraction - segment->mFromKey) /
(segment->mToKey - segment->mFromKey);
double valuePosition =
segment->mTimingFunction.GetValue(positionInSegment);
StyleAnimationValue *val =
mStyleRule->AddEmptyValue(prop.mProperty);
#ifdef DEBUG
bool result =
#endif
StyleAnimationValue::Interpolate(prop.mProperty,
segment->mFromValue,
segment->mToValue,
valuePosition, *val);
NS_ABORT_IF_FALSE(result, "interpolate must succeed now");
}
mPlayers[playerIdx]->ComposeStyle(mStyleRule, properties,
mNeedsRefreshes);
}
}
}

View File

@ -21,6 +21,7 @@ EXPORTS += [
'nsCSSParser.h',
'nsCSSPropAliasList.h',
'nsCSSProperty.h',
'nsCSSPropertySet.h',
'nsCSSPropList.h',
'nsCSSProps.h',
'nsCSSPseudoClasses.h',