bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& timing, const Element& targetElement, const Animation* animationToAdd, const EffectModel& effect, double animationPlaybackRate) { const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect); PropertyHandleSet properties = keyframeEffect.properties(); if (properties.isEmpty()) return false; unsigned transformPropertyCount = 0; for (const auto& property : properties) { if (!property.isCSSProperty()) return false; if (isTransformRelatedCSSProperty(property)) { if (targetElement.layoutObject() && targetElement.layoutObject()->isInline()) { return false; } transformPropertyCount++; } const PropertySpecificKeyframeVector& keyframes = keyframeEffect.getPropertySpecificKeyframes(property); ASSERT(keyframes.size() >= 2); for (const auto& keyframe : keyframes) { // FIXME: Determine candidacy based on the CSSValue instead of a snapshot AnimatableValue. bool isNeutralKeyframe = keyframe->isCSSPropertySpecificKeyframe() && !toCSSPropertySpecificKeyframe(keyframe.get())->value() && keyframe->composite() == EffectModel::CompositeAdd; if ((keyframe->composite() != EffectModel::CompositeReplace && !isNeutralKeyframe) || !keyframe->getAnimatableValue()) return false; switch (property.cssProperty()) { case CSSPropertyOpacity: break; case CSSPropertyRotate: case CSSPropertyScale: case CSSPropertyTranslate: case CSSPropertyTransform: if (toAnimatableTransform(keyframe->getAnimatableValue().get())->transformOperations().dependsOnBoxSize()) return false; break; case CSSPropertyWebkitFilter: case CSSPropertyBackdropFilter: { const FilterOperations& operations = toAnimatableFilterOperations(keyframe->getAnimatableValue().get())->operations(); if (operations.hasFilterThatMovesPixels()) return false; break; } default: // any other types are not allowed to run on compositor. return false; } } } // TODO: Support multiple transform property animations on the compositor if (transformPropertyCount > 1) return false; if (animationToAdd && hasIncompatibleAnimations(targetElement, *animationToAdd, effect)) return false; CompositorAnimationsImpl::CompositorTiming out; if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, 0, out, animationPlaybackRate)) return false; return true; }
void CompositorAnimationsImpl::getAnimationOnCompositor(const Timing& timing, int group, double startTime, double timeOffset, const KeyframeEffectModelBase& effect, Vector<OwnPtr<WebCompositorAnimation>>& animations, double animationPlaybackRate) { ASSERT(animations.isEmpty()); CompositorTiming compositorTiming; bool timingValid = convertTimingForCompositor(timing, timeOffset, compositorTiming, animationPlaybackRate); ASSERT_UNUSED(timingValid, timingValid); PropertyHandleSet properties = effect.properties(); ASSERT(!properties.isEmpty()); for (const auto& property : properties) { PropertySpecificKeyframeVector values; getKeyframeValuesForProperty(&effect, property, compositorTiming.scaledDuration, values); WebCompositorAnimation::TargetProperty targetProperty; OwnPtr<WebCompositorAnimationCurve> curve; switch (property.cssProperty()) { case CSSPropertyOpacity: { targetProperty = WebCompositorAnimation::TargetPropertyOpacity; WebFloatAnimationCurve* floatCurve = Platform::current()->compositorSupport()->createFloatAnimationCurve(); addKeyframesToCurve(*floatCurve, values, timing); setTimingFunctionOnCurve(*floatCurve, timing.timingFunction.get()); curve = adoptPtr(floatCurve); break; } case CSSPropertyWebkitFilter: case CSSPropertyBackdropFilter: { targetProperty = WebCompositorAnimation::TargetPropertyFilter; WebFilterAnimationCurve* filterCurve = Platform::current()->compositorSupport()->createFilterAnimationCurve(); addKeyframesToCurve(*filterCurve, values, timing); setTimingFunctionOnCurve(*filterCurve, timing.timingFunction.get()); curve = adoptPtr(filterCurve); break; } case CSSPropertyRotate: case CSSPropertyScale: case CSSPropertyTranslate: case CSSPropertyTransform: { targetProperty = WebCompositorAnimation::TargetPropertyTransform; WebTransformAnimationCurve* transformCurve = Platform::current()->compositorSupport()->createTransformAnimationCurve(); addKeyframesToCurve(*transformCurve, values, timing); setTimingFunctionOnCurve(*transformCurve, timing.timingFunction.get()); curve = adoptPtr(transformCurve); break; } default: ASSERT_NOT_REACHED(); continue; } ASSERT(curve.get()); OwnPtr<WebCompositorAnimation> animation = adoptPtr(Platform::current()->compositorSupport()->createAnimation(*curve, targetProperty, group, 0)); if (!std::isnan(startTime)) animation->setStartTime(startTime); animation->setIterations(compositorTiming.adjustedIterationCount); animation->setIterationStart(compositorTiming.iterationStart); animation->setTimeOffset(compositorTiming.scaledTimeOffset); switch (compositorTiming.direction) { case Timing::PlaybackDirectionNormal: animation->setDirection(WebCompositorAnimation::DirectionNormal); break; case Timing::PlaybackDirectionReverse: animation->setDirection(WebCompositorAnimation::DirectionReverse); break; case Timing::PlaybackDirectionAlternate: animation->setDirection(WebCompositorAnimation::DirectionAlternate); break; case Timing::PlaybackDirectionAlternateReverse: animation->setDirection(WebCompositorAnimation::DirectionAlternateReverse); break; default: ASSERT_NOT_REACHED(); } animation->setPlaybackRate(compositorTiming.playbackRate); switch (compositorTiming.fillMode) { case Timing::FillModeNone: animation->setFillMode(WebCompositorAnimation::FillModeNone); break; case Timing::FillModeForwards: animation->setFillMode(WebCompositorAnimation::FillModeForwards); break; case Timing::FillModeBackwards: animation->setFillMode(WebCompositorAnimation::FillModeBackwards); break; case Timing::FillModeBoth: animation->setFillMode(WebCompositorAnimation::FillModeBoth); break; default: ASSERT_NOT_REACHED(); } animations.append(animation.release()); } ASSERT(!animations.isEmpty()); }
bool CompositorAnimations::getAnimatedBoundingBox(FloatBox& box, const EffectModel& effect, double minValue, double maxValue) const { const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect); PropertyHandleSet properties = keyframeEffect.properties(); if (properties.isEmpty()) return true; minValue = std::min(minValue, 0.0); maxValue = std::max(maxValue, 1.0); for (const auto& property : properties) { if (!property.isCSSProperty()) continue; // TODO: Add the ability to get expanded bounds for filters as well. if (!isTransformRelatedCSSProperty(property)) continue; const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropertySpecificKeyframes(property); if (frames.isEmpty() || frames.size() < 2) continue; FloatBox originalBox(box); for (size_t j = 0; j < frames.size() - 1; ++j) { const AnimatableTransform* startTransform = toAnimatableTransform(frames[j]->getAnimatableValue().get()); const AnimatableTransform* endTransform = toAnimatableTransform(frames[j+1]->getAnimatableValue().get()); if (!startTransform || !endTransform) return false; // TODO: Add support for inflating modes other than Replace. if (frames[j]->composite() != EffectModel::CompositeReplace) return false; const TimingFunction& timing = frames[j]->easing(); double min = 0; double max = 1; if (j == 0) { float frameLength = frames[j+1]->offset(); if (frameLength > 0) { min = minValue / frameLength; } } if (j == frames.size() - 2) { float frameLength = frames[j+1]->offset() - frames[j]->offset(); if (frameLength > 0) { max = 1 + (maxValue - 1) / frameLength; } } FloatBox bounds; timing.range(&min, &max); if (!endTransform->transformOperations().blendedBoundsForBox(originalBox, startTransform->transformOperations(), min, max, &bounds)) return false; box.expandTo(bounds); } } return true; }