1/* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include "config.h" 32#include "core/animation/CompositorAnimations.h" 33 34#include "core/animation/AnimationNode.h" 35#include "core/animation/AnimationTranslationUtil.h" 36#include "core/animation/CompositorAnimationsImpl.h" 37#include "core/animation/animatable/AnimatableDouble.h" 38#include "core/animation/animatable/AnimatableFilterOperations.h" 39#include "core/animation/animatable/AnimatableTransform.h" 40#include "core/animation/animatable/AnimatableValue.h" 41#include "core/rendering/RenderBoxModelObject.h" 42#include "core/rendering/RenderLayer.h" 43#include "core/rendering/RenderObject.h" 44#include "core/rendering/compositing/CompositedLayerMapping.h" 45#include "platform/geometry/FloatBox.h" 46#include "public/platform/Platform.h" 47#include "public/platform/WebCompositorAnimation.h" 48#include "public/platform/WebCompositorSupport.h" 49#include "public/platform/WebFilterAnimationCurve.h" 50#include "public/platform/WebFilterKeyframe.h" 51#include "public/platform/WebFloatAnimationCurve.h" 52#include "public/platform/WebFloatKeyframe.h" 53#include "public/platform/WebTransformAnimationCurve.h" 54#include "public/platform/WebTransformKeyframe.h" 55 56#include <algorithm> 57#include <cmath> 58 59namespace blink { 60 61namespace { 62 63void getKeyframeValuesForProperty(const KeyframeEffectModelBase* effect, CSSPropertyID id, double scale, PropertySpecificKeyframeVector& values) 64{ 65 ASSERT(values.isEmpty()); 66 const PropertySpecificKeyframeVector& group = effect->getPropertySpecificKeyframes(id); 67 68 for (size_t i = 0; i < group.size(); ++i) { 69 double offset = group[i]->offset() * scale; 70 values.append(group[i]->cloneWithOffset(offset)); 71 } 72} 73 74} 75 76bool CompositorAnimations::getAnimatedBoundingBox(FloatBox& box, const AnimationEffect& effect, double minValue, double maxValue) const 77{ 78 const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect); 79 80 PropertySet properties = keyframeEffect.properties(); 81 82 if (properties.isEmpty()) 83 return true; 84 85 minValue = std::min(minValue, 0.0); 86 maxValue = std::max(maxValue, 1.0); 87 88 for (PropertySet::const_iterator it = properties.begin(); it != properties.end(); ++it) { 89 // TODO: Add the ability to get expanded bounds for filters as well. 90 if (*it != CSSPropertyTransform && *it != CSSPropertyWebkitTransform) 91 continue; 92 93 const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropertySpecificKeyframes(*it); 94 if (frames.isEmpty() || frames.size() < 2) 95 continue; 96 97 FloatBox originalBox(box); 98 99 for (size_t j = 0; j < frames.size() - 1; ++j) { 100 const AnimatableTransform* startTransform = toAnimatableTransform(frames[j]->getAnimatableValue().get()); 101 const AnimatableTransform* endTransform = toAnimatableTransform(frames[j+1]->getAnimatableValue().get()); 102 // TODO: Add support for inflating modes other than Replace. 103 if (frames[j]->composite() != AnimationEffect::CompositeReplace) 104 return false; 105 106 const TimingFunction& timing = frames[j]->easing(); 107 double min = 0; 108 double max = 1; 109 if (j == 0) { 110 float frameLength = frames[j+1]->offset(); 111 if (frameLength > 0) { 112 min = minValue / frameLength; 113 } 114 } 115 116 if (j == frames.size() - 2) { 117 float frameLength = frames[j+1]->offset() - frames[j]->offset(); 118 if (frameLength > 0) { 119 max = 1 + (maxValue - 1) / frameLength; 120 } 121 } 122 123 FloatBox bounds; 124 timing.range(&min, &max); 125 if (!endTransform->transformOperations().blendedBoundsForBox(originalBox, startTransform->transformOperations(), min, max, &bounds)) 126 return false; 127 box.expandTo(bounds); 128 } 129 } 130 return true; 131} 132 133// ----------------------------------------------------------------------- 134// CompositorAnimations public API 135// ----------------------------------------------------------------------- 136 137bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& timing, const AnimationEffect& effect, double playerPlaybackRate) 138{ 139 const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect); 140 141 PropertySet properties = keyframeEffect.properties(); 142 143 if (properties.isEmpty()) 144 return false; 145 146 for (PropertySet::const_iterator it = properties.begin(); it != properties.end(); ++it) { 147 const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropertySpecificKeyframes(*it); 148 ASSERT(frames.size() >= 2); 149 for (size_t i = 0; i < frames.size(); ++i) { 150 const Keyframe::PropertySpecificKeyframe *frame = frames[i].get(); 151 // FIXME: Determine candidacy based on the CSSValue instead of a snapshot AnimatableValue. 152 if (frame->composite() != AnimationEffect::CompositeReplace || !frame->getAnimatableValue()) 153 return false; 154 155 switch (*it) { 156 case CSSPropertyOpacity: 157 break; 158 case CSSPropertyTransform: 159 if (toAnimatableTransform(frame->getAnimatableValue().get())->transformOperations().dependsOnBoxSize()) 160 return false; 161 break; 162 case CSSPropertyWebkitFilter: { 163 const FilterOperations& operations = toAnimatableFilterOperations(frame->getAnimatableValue().get())->operations(); 164 if (operations.hasFilterThatMovesPixels()) 165 return false; 166 break; 167 } 168 default: 169 return false; 170 } 171 172 // FIXME: Remove this check when crbug.com/229405 is resolved 173 if (i < frames.size() - 1 && frame->easing().type() == TimingFunction::StepsFunction) 174 return false; 175 } 176 } 177 178 CompositorAnimationsImpl::CompositorTiming out; 179 if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, 0, out, playerPlaybackRate)) 180 return false; 181 182 if (timing.timingFunction->type() != TimingFunction::LinearFunction) { 183 // Checks the of size of KeyframeVector instead of PropertySpecificKeyframeVector. 184 const KeyframeVector& keyframes = keyframeEffect.getFrames(); 185 if (keyframes.size() == 2 && keyframes[0]->easing().type() == TimingFunction::LinearFunction && timing.timingFunction->type() != TimingFunction::StepsFunction) 186 return true; 187 188 // FIXME: Support non-linear timing functions in the compositor for 189 // more than two keyframes and step timing functions in the compositor. 190 return false; 191 } 192 193 return true; 194} 195 196bool CompositorAnimations::canStartAnimationOnCompositor(const Element& element) 197{ 198 return element.renderer() && element.renderer()->compositingState() == PaintsIntoOwnBacking; 199} 200 201bool CompositorAnimations::startAnimationOnCompositor(const Element& element, double startTime, double timeOffset, const Timing& timing, const AnimationEffect& effect, Vector<int>& startedAnimationIds, double playerPlaybackRate) 202{ 203 ASSERT(startedAnimationIds.isEmpty()); 204 ASSERT(isCandidateForAnimationOnCompositor(timing, effect, playerPlaybackRate)); 205 ASSERT(canStartAnimationOnCompositor(element)); 206 207 const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect); 208 209 RenderLayer* layer = toRenderBoxModelObject(element.renderer())->layer(); 210 ASSERT(layer); 211 212 Vector<OwnPtr<WebCompositorAnimation> > animations; 213 CompositorAnimationsImpl::getAnimationOnCompositor(timing, startTime, timeOffset, keyframeEffect, animations, playerPlaybackRate); 214 ASSERT(!animations.isEmpty()); 215 for (size_t i = 0; i < animations.size(); ++i) { 216 int id = animations[i]->id(); 217 if (!layer->compositedLayerMapping()->mainGraphicsLayer()->addAnimation(animations[i].release())) { 218 // FIXME: We should know ahead of time whether these animations can be started. 219 for (size_t j = 0; j < startedAnimationIds.size(); ++j) 220 cancelAnimationOnCompositor(element, startedAnimationIds[j]); 221 startedAnimationIds.clear(); 222 return false; 223 } 224 startedAnimationIds.append(id); 225 } 226 ASSERT(!startedAnimationIds.isEmpty()); 227 return true; 228} 229 230void CompositorAnimations::cancelAnimationOnCompositor(const Element& element, int id) 231{ 232 if (!canStartAnimationOnCompositor(element)) { 233 // When an element is being detached, we cancel any associated 234 // AnimationPlayers for CSS animations. But by the time we get 235 // here the mapping will have been removed. 236 // FIXME: Defer remove/pause operations until after the 237 // compositing update. 238 return; 239 } 240 toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->removeAnimation(id); 241} 242 243void CompositorAnimations::pauseAnimationForTestingOnCompositor(const Element& element, int id, double pauseTime) 244{ 245 // FIXME: canStartAnimationOnCompositor queries compositingState, which is not necessarily up to date. 246 // https://code.google.com/p/chromium/issues/detail?id=339847 247 DisableCompositingQueryAsserts disabler; 248 249 if (!canStartAnimationOnCompositor(element)) { 250 ASSERT_NOT_REACHED(); 251 return; 252 } 253 toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->pauseAnimation(id, pauseTime); 254} 255 256// ----------------------------------------------------------------------- 257// CompositorAnimationsImpl 258// ----------------------------------------------------------------------- 259 260bool CompositorAnimationsImpl::convertTimingForCompositor(const Timing& timing, double timeOffset, CompositorTiming& out, double playerPlaybackRate) 261{ 262 timing.assertValid(); 263 264 // All fill modes are supported (the calling code handles them). 265 266 if (timing.iterationCount <= 0) 267 return false; 268 269 if (std::isnan(timing.iterationDuration) || !timing.iterationDuration) 270 return false; 271 272 // All directions are supported. 273 274 // Now attempt an actual conversion 275 out.scaledDuration = timing.iterationDuration; 276 ASSERT(out.scaledDuration > 0); 277 278 double scaledStartDelay = timing.startDelay; 279 if (scaledStartDelay > 0 && scaledStartDelay > out.scaledDuration * timing.iterationCount) 280 return false; 281 282 out.direction = timing.direction; 283 284 if (!std::isfinite(timing.iterationCount)) { 285 out.adjustedIterationCount = -1; 286 } else { 287 out.adjustedIterationCount = timing.iterationCount; 288 ASSERT(out.adjustedIterationCount > 0); 289 } 290 291 // Compositor's time offset is positive for seeking into the animation. 292 out.scaledTimeOffset = -scaledStartDelay + timeOffset; 293 out.playbackRate = timing.playbackRate * playerPlaybackRate; 294 out.fillMode = timing.fillMode == Timing::FillModeAuto ? Timing::FillModeNone : timing.fillMode; 295 out.iterationStart = timing.iterationStart; 296 297 return true; 298} 299 300namespace { 301 302template<typename PlatformAnimationCurveType, typename PlatformAnimationKeyframeType> 303void addKeyframeWithTimingFunction(PlatformAnimationCurveType& curve, const PlatformAnimationKeyframeType& keyframe, const TimingFunction* timingFunction) 304{ 305 if (!timingFunction) { 306 curve.add(keyframe); 307 return; 308 } 309 310 switch (timingFunction->type()) { 311 case TimingFunction::LinearFunction: 312 curve.add(keyframe, WebCompositorAnimationCurve::TimingFunctionTypeLinear); 313 return; 314 315 case TimingFunction::CubicBezierFunction: { 316 const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(timingFunction); 317 318 if (cubic->subType() == CubicBezierTimingFunction::Custom) { 319 curve.add(keyframe, cubic->x1(), cubic->y1(), cubic->x2(), cubic->y2()); 320 } else { 321 322 WebCompositorAnimationCurve::TimingFunctionType easeType; 323 switch (cubic->subType()) { 324 case CubicBezierTimingFunction::Ease: 325 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEase; 326 break; 327 case CubicBezierTimingFunction::EaseIn: 328 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseIn; 329 break; 330 case CubicBezierTimingFunction::EaseOut: 331 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseOut; 332 break; 333 case CubicBezierTimingFunction::EaseInOut: 334 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseInOut; 335 break; 336 337 // Custom Bezier are handled seperately. 338 case CubicBezierTimingFunction::Custom: 339 default: 340 ASSERT_NOT_REACHED(); 341 return; 342 } 343 344 curve.add(keyframe, easeType); 345 } 346 return; 347 } 348 349 case TimingFunction::StepsFunction: 350 default: 351 ASSERT_NOT_REACHED(); 352 return; 353 } 354} 355 356} // namespace anoymous 357 358void CompositorAnimationsImpl::addKeyframesToCurve(WebCompositorAnimationCurve& curve, const PropertySpecificKeyframeVector& keyframes, const Timing& timing) 359{ 360 for (size_t i = 0; i < keyframes.size(); i++) { 361 const TimingFunction* keyframeTimingFunction = 0; 362 if (i < keyframes.size() - 1) { // Ignore timing function of last frame. 363 if (keyframes.size() == 2 && keyframes[0]->easing().type() == TimingFunction::LinearFunction) { 364 keyframeTimingFunction = timing.timingFunction.get(); 365 } else { 366 keyframeTimingFunction = &keyframes[i]->easing(); 367 } 368 } 369 370 // FIXME: This relies on StringKeyframes being eagerly evaluated, which will 371 // not happen eventually. Instead we should extract the CSSValue here 372 // and convert using another set of toAnimatableXXXOperations functions. 373 const AnimatableValue* value = keyframes[i]->getAnimatableValue().get(); 374 375 switch (curve.type()) { 376 case WebCompositorAnimationCurve::AnimationCurveTypeFilter: { 377 OwnPtr<WebFilterOperations> ops = adoptPtr(Platform::current()->compositorSupport()->createFilterOperations()); 378 toWebFilterOperations(toAnimatableFilterOperations(value)->operations(), ops.get()); 379 380 WebFilterKeyframe filterKeyframe(keyframes[i]->offset(), ops.release()); 381 WebFilterAnimationCurve* filterCurve = static_cast<WebFilterAnimationCurve*>(&curve); 382 addKeyframeWithTimingFunction(*filterCurve, filterKeyframe, keyframeTimingFunction); 383 break; 384 } 385 case WebCompositorAnimationCurve::AnimationCurveTypeFloat: { 386 WebFloatKeyframe floatKeyframe(keyframes[i]->offset(), toAnimatableDouble(value)->toDouble()); 387 WebFloatAnimationCurve* floatCurve = static_cast<WebFloatAnimationCurve*>(&curve); 388 addKeyframeWithTimingFunction(*floatCurve, floatKeyframe, keyframeTimingFunction); 389 break; 390 } 391 case WebCompositorAnimationCurve::AnimationCurveTypeTransform: { 392 OwnPtr<WebTransformOperations> ops = adoptPtr(Platform::current()->compositorSupport()->createTransformOperations()); 393 toWebTransformOperations(toAnimatableTransform(value)->transformOperations(), ops.get()); 394 395 WebTransformKeyframe transformKeyframe(keyframes[i]->offset(), ops.release()); 396 WebTransformAnimationCurve* transformCurve = static_cast<WebTransformAnimationCurve*>(&curve); 397 addKeyframeWithTimingFunction(*transformCurve, transformKeyframe, keyframeTimingFunction); 398 break; 399 } 400 default: 401 ASSERT_NOT_REACHED(); 402 } 403 } 404} 405 406void CompositorAnimationsImpl::getAnimationOnCompositor(const Timing& timing, double startTime, double timeOffset, const KeyframeEffectModelBase& effect, Vector<OwnPtr<WebCompositorAnimation> >& animations, double playerPlaybackRate) 407{ 408 ASSERT(animations.isEmpty()); 409 CompositorTiming compositorTiming; 410 bool timingValid = convertTimingForCompositor(timing, timeOffset, compositorTiming, playerPlaybackRate); 411 ASSERT_UNUSED(timingValid, timingValid); 412 413 PropertySet properties = effect.properties(); 414 ASSERT(!properties.isEmpty()); 415 for (PropertySet::iterator it = properties.begin(); it != properties.end(); ++it) { 416 417 PropertySpecificKeyframeVector values; 418 getKeyframeValuesForProperty(&effect, *it, compositorTiming.scaledDuration, values); 419 420 WebCompositorAnimation::TargetProperty targetProperty; 421 OwnPtr<WebCompositorAnimationCurve> curve; 422 switch (*it) { 423 case CSSPropertyOpacity: { 424 targetProperty = WebCompositorAnimation::TargetPropertyOpacity; 425 426 WebFloatAnimationCurve* floatCurve = Platform::current()->compositorSupport()->createFloatAnimationCurve(); 427 addKeyframesToCurve(*floatCurve, values, timing); 428 curve = adoptPtr(floatCurve); 429 break; 430 } 431 case CSSPropertyWebkitFilter: { 432 targetProperty = WebCompositorAnimation::TargetPropertyFilter; 433 WebFilterAnimationCurve* filterCurve = Platform::current()->compositorSupport()->createFilterAnimationCurve(); 434 addKeyframesToCurve(*filterCurve, values, timing); 435 curve = adoptPtr(filterCurve); 436 break; 437 } 438 case CSSPropertyTransform: { 439 targetProperty = WebCompositorAnimation::TargetPropertyTransform; 440 WebTransformAnimationCurve* transformCurve = Platform::current()->compositorSupport()->createTransformAnimationCurve(); 441 addKeyframesToCurve(*transformCurve, values, timing); 442 curve = adoptPtr(transformCurve); 443 break; 444 } 445 default: 446 ASSERT_NOT_REACHED(); 447 continue; 448 } 449 ASSERT(curve.get()); 450 451 OwnPtr<WebCompositorAnimation> animation = adoptPtr(Platform::current()->compositorSupport()->createAnimation(*curve, targetProperty)); 452 453 if (!std::isnan(startTime)) 454 animation->setStartTime(startTime); 455 456 animation->setIterations(compositorTiming.adjustedIterationCount); 457 animation->setIterationStart(compositorTiming.iterationStart); 458 animation->setTimeOffset(compositorTiming.scaledTimeOffset); 459 460 switch (compositorTiming.direction) { 461 case Timing::PlaybackDirectionNormal: 462 animation->setDirection(blink::WebCompositorAnimation::DirectionNormal); 463 break; 464 case Timing::PlaybackDirectionReverse: 465 animation->setDirection(blink::WebCompositorAnimation::DirectionReverse); 466 break; 467 case Timing::PlaybackDirectionAlternate: 468 animation->setDirection(blink::WebCompositorAnimation::DirectionAlternate); 469 break; 470 case Timing::PlaybackDirectionAlternateReverse: 471 animation->setDirection(blink::WebCompositorAnimation::DirectionAlternateReverse); 472 break; 473 default: 474 ASSERT_NOT_REACHED(); 475 } 476 animation->setPlaybackRate(compositorTiming.playbackRate); 477 478 switch (compositorTiming.fillMode) { 479 case Timing::FillModeNone: 480 animation->setFillMode(blink::WebCompositorAnimation::FillModeNone); 481 break; 482 case Timing::FillModeForwards: 483 animation->setFillMode(blink::WebCompositorAnimation::FillModeForwards); 484 break; 485 case Timing::FillModeBackwards: 486 animation->setFillMode(blink::WebCompositorAnimation::FillModeBackwards); 487 break; 488 case Timing::FillModeBoth: 489 animation->setFillMode(blink::WebCompositorAnimation::FillModeBoth); 490 break; 491 default: 492 ASSERT_NOT_REACHED(); 493 } 494 animations.append(animation.release()); 495 } 496 ASSERT(!animations.isEmpty()); 497} 498 499} // namespace blink 500