void KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule, nsCSSPropertyIDSet& aSetProperties) { ComputedTiming computedTiming = GetComputedTiming(); mProgressOnLastCompose = computedTiming.mProgress; // If the progress is null, we don't have fill data for the current // time so we shouldn't animate. if (computedTiming.mProgress.IsNull()) { return; } 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 EffectCompositor 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. continue; } if (!prop.mWinsInCascade) { // This isn't the winning declaration, so don't add it to style. // For transitions, this is important, because it's how we // implement the rule that CSS transitions don't run when a CSS // animation is running on the same property and element. For // animations, this is only skipping things that will otherwise be // overridden. continue; } 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.mProgress.Value()) { MOZ_ASSERT(segment->mFromKey <= segment->mToKey, "incorrect keys"); if ((segment+1) == segmentEnd) { break; } ++segment; MOZ_ASSERT(segment->mFromKey == (segment-1)->mToKey, "incorrect keys"); } 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 AnimValuesStyleRule(); } // Special handling for zero-length segments if (segment->mToKey == segment->mFromKey) { if (computedTiming.mProgress.Value() < 0) { aStyleRule->AddValue(prop.mProperty, segment->mFromValue); } else { aStyleRule->AddValue(prop.mProperty, segment->mToValue); } 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, valuePosition, val)) { aStyleRule->AddValue(prop.mProperty, Move(val)); } else if (valuePosition < 0.5) { aStyleRule->AddValue(prop.mProperty, segment->mFromValue); } else { aStyleRule->AddValue(prop.mProperty, segment->mToValue); } } }
void KeyframeEffectReadOnly::ComposeStyle( RefPtr<AnimValuesStyleRule>& aStyleRule, const nsCSSPropertyIDSet& aPropertiesToSkip) { ComputedTiming computedTiming = GetComputedTiming(); mProgressOnLastCompose = computedTiming.mProgress; mCurrentIterationOnLastCompose = computedTiming.mCurrentIteration; // If the progress is null, we don't have fill data for the current // time so we shouldn't animate. if (computedTiming.mProgress.IsNull()) { return; } 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 (aPropertiesToSkip.HasProperty(prop.mProperty)) { continue; } 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.mProgress.Value()) { MOZ_ASSERT(segment->mFromKey <= segment->mToKey, "incorrect keys"); if ((segment+1) == segmentEnd) { break; } ++segment; MOZ_ASSERT(segment->mFromKey == (segment-1)->mToKey, "incorrect keys"); } 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 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, Move(fromValue)); } else { 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, fromValue, toValue, valuePosition, val)) { aStyleRule->AddValue(prop.mProperty, Move(val)); } else if (valuePosition < 0.5) { aStyleRule->AddValue(prop.mProperty, Move(fromValue)); } else { aStyleRule->AddValue(prop.mProperty, Move(toValue)); } } }