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/AnimatableDouble.h" 35 #include "core/animation/AnimatableFilterOperations.h" 36 #include "core/animation/AnimatableTransform.h" 37 #include "core/animation/AnimatableValue.h" 38 #include "core/animation/CompositorAnimationsImpl.h" 39 #include "core/platform/animation/AnimationTranslationUtil.h" 40 #include "core/rendering/CompositedLayerMapping.h" 41 #include "core/rendering/RenderBoxModelObject.h" 42 #include "core/rendering/RenderLayer.h" 43 #include "core/rendering/RenderObject.h" 44 #include "public/platform/Platform.h" 45 #include "public/platform/WebAnimation.h" 46 #include "public/platform/WebCompositorSupport.h" 47 #include "public/platform/WebFilterAnimationCurve.h" 48 #include "public/platform/WebFilterKeyframe.h" 49 #include "public/platform/WebFloatAnimationCurve.h" 50 #include "public/platform/WebFloatKeyframe.h" 51 #include "public/platform/WebTransformAnimationCurve.h" 52 #include "public/platform/WebTransformKeyframe.h" 53 54 #include <algorithm> 55 #include <cmath> 56 57 namespace WebCore { 58 59 namespace { 60 61 void getKeyframeValuesForProperty(const KeyframeAnimationEffect* effect, CSSPropertyID id, double scale, bool reverse, KeyframeVector& values) 62 { 63 ASSERT(values.isEmpty()); 64 const KeyframeVector& group = effect->getPropertySpecificKeyframes(id); 65 66 if (reverse) { 67 for (size_t i = group.size(); i--;) { 68 double offset = (1 - group[i]->offset()) * scale; 69 values.append(group[i]->cloneWithOffset(offset)); 70 } 71 } else { 72 for (size_t i = 0; i < group.size(); ++i) { 73 double offset = group[i]->offset() * scale; 74 values.append(group[i]->cloneWithOffset(offset)); 75 } 76 } 77 } 78 79 } 80 81 // ----------------------------------------------------------------------- 82 // TimingFunctionReverser methods 83 // ----------------------------------------------------------------------- 84 85 PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const LinearTimingFunction* timefunc) 86 { 87 return const_cast<LinearTimingFunction*>(timefunc); 88 } 89 90 PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const CubicBezierTimingFunction* timefunc) 91 { 92 switch (timefunc->subType()) { 93 case CubicBezierTimingFunction::EaseIn: 94 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseOut); 95 case CubicBezierTimingFunction::EaseOut: 96 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::EaseIn); 97 case CubicBezierTimingFunction::EaseInOut: 98 return const_cast<CubicBezierTimingFunction*>(timefunc); 99 case CubicBezierTimingFunction::Ease: // Ease is not symmetrical 100 case CubicBezierTimingFunction::Custom: 101 return CubicBezierTimingFunction::create(1 - timefunc->x2(), 1 - timefunc->y2(), 1 - timefunc->x1(), 1 - timefunc->y1()); 102 default: 103 ASSERT_NOT_REACHED(); 104 return PassRefPtr<TimingFunction>(); 105 } 106 } 107 108 PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const ChainedTimingFunction* timefunc) 109 { 110 RefPtr<ChainedTimingFunction> reversed = ChainedTimingFunction::create(); 111 for (size_t i = 0; i < timefunc->m_segments.size(); i++) { 112 size_t index = timefunc->m_segments.size() - i - 1; 113 114 RefPtr<TimingFunction> rtf = reverse(timefunc->m_segments[index].m_timingFunction.get()); 115 reversed->appendSegment(1 - timefunc->m_segments[index].m_min, rtf.get()); 116 } 117 return reversed; 118 } 119 120 PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(const TimingFunction* timefunc) 121 { 122 switch (timefunc->type()) { 123 case TimingFunction::LinearFunction: { 124 const LinearTimingFunction* linear = toLinearTimingFunction(timefunc); 125 return reverse(linear); 126 } 127 case TimingFunction::CubicBezierFunction: { 128 const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(timefunc); 129 return reverse(cubic); 130 } 131 case TimingFunction::ChainedFunction: { 132 const ChainedTimingFunction* chained = toChainedTimingFunction(timefunc); 133 return reverse(chained); 134 } 135 136 // Steps function can not be reversed. 137 case TimingFunction::StepsFunction: 138 default: 139 ASSERT_NOT_REACHED(); 140 return PassRefPtr<TimingFunction>(); 141 } 142 } 143 144 // ----------------------------------------------------------------------- 145 // CompositorAnimations public API 146 // ----------------------------------------------------------------------- 147 148 bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& timing, const AnimationEffect& effect) 149 { 150 const KeyframeAnimationEffect& keyframeEffect = *toKeyframeAnimationEffect(&effect); 151 152 // Are the keyframes convertible? 153 const KeyframeAnimationEffect::KeyframeVector frames = keyframeEffect.getFrames(); 154 for (size_t i = 0; i < frames.size(); ++i) { 155 // Only replace mode can be accelerated 156 if (frames[i]->composite() != AnimationEffect::CompositeReplace) 157 return false; 158 159 // Check all the properties can be accelerated 160 const PropertySet properties = frames[i]->properties(); // FIXME: properties creates a whole new PropertySet! 161 162 if (properties.isEmpty()) 163 return false; 164 165 for (PropertySet::const_iterator it = properties.begin(); it != properties.end(); ++it) { 166 switch (*it) { 167 case CSSPropertyOpacity: 168 continue; 169 case CSSPropertyWebkitTransform: 170 if (toAnimatableTransform(frames[i]->propertyValue(CSSPropertyWebkitTransform))->transformOperations().dependsOnBoxSize()) 171 return false; 172 continue; 173 case CSSPropertyWebkitFilter: { 174 const FilterOperations& operations = toAnimatableFilterOperations(frames[i]->propertyValue(CSSPropertyWebkitFilter))->operations(); 175 if (operations.hasFilterThatMovesPixels()) 176 return false; 177 for (size_t i = 0; i < operations.size(); i++) { 178 const FilterOperation& op = *operations.at(i); 179 if (op.type() == FilterOperation::VALIDATED_CUSTOM || op.type() == FilterOperation::CUSTOM) 180 return false; 181 } 182 continue; 183 } 184 default: 185 return false; 186 } 187 } 188 } 189 190 // Is the timing object convertible? 191 CompositorAnimationsImpl::CompositorTiming out; 192 if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, out)) 193 return false; 194 195 // Is the timing function convertible? 196 switch (timing.timingFunction->type()) { 197 case TimingFunction::LinearFunction: 198 break; 199 200 case TimingFunction::CubicBezierFunction: 201 // Can have a cubic if we don't have to split it (IE only have two frames). 202 if (frames.size() != 2) 203 return false; 204 205 ASSERT(frames[0]->offset() == 0.0 && frames[1]->offset() == 1.0); 206 break; 207 208 case TimingFunction::StepsFunction: 209 return false; 210 211 case TimingFunction::ChainedFunction: { 212 // Currently we only support chained segments in the form the CSS code 213 // generates. These chained segments are only one level deep and have 214 // one timing function per frame. 215 const ChainedTimingFunction* chained = static_cast<const ChainedTimingFunction*>(timing.timingFunction.get()); 216 if (!chained->m_segments.size()) 217 return false; 218 219 if (frames.size() != chained->m_segments.size() + 1) 220 return false; 221 222 for (size_t timeIndex = 0; timeIndex < chained->m_segments.size(); timeIndex++) { 223 const ChainedTimingFunction::Segment& segment = chained->m_segments[timeIndex]; 224 225 if (frames[timeIndex]->offset() != segment.m_min || frames[timeIndex + 1]->offset() != segment.m_max) 226 return false; 227 228 switch (segment.m_timingFunction->type()) { 229 case TimingFunction::LinearFunction: 230 case TimingFunction::CubicBezierFunction: 231 continue; 232 233 case TimingFunction::StepsFunction: 234 case TimingFunction::ChainedFunction: 235 default: 236 return false; 237 } 238 } 239 240 break; 241 } 242 default: 243 ASSERT_NOT_REACHED(); 244 return false; 245 } 246 247 return true; 248 } 249 250 bool CompositorAnimations::canStartAnimationOnCompositor(const Element& element) 251 { 252 return element.renderer() && element.renderer()->compositingState() == PaintsIntoOwnBacking; 253 } 254 255 bool CompositorAnimations::startAnimationOnCompositor(const Element& element, const Timing& timing, const AnimationEffect& effect, Vector<int>& startedAnimationIds) 256 { 257 ASSERT(startedAnimationIds.isEmpty()); 258 ASSERT(isCandidateForAnimationOnCompositor(timing, effect)); 259 ASSERT(canStartAnimationOnCompositor(element)); 260 261 const KeyframeAnimationEffect& keyframeEffect = *toKeyframeAnimationEffect(&effect); 262 263 RenderLayer* layer = toRenderBoxModelObject(element.renderer())->layer(); 264 ASSERT(layer); 265 266 Vector<OwnPtr<blink::WebAnimation> > animations; 267 CompositorAnimationsImpl::getAnimationOnCompositor(timing, keyframeEffect, animations); 268 ASSERT(!animations.isEmpty()); 269 for (size_t i = 0; i < animations.size(); ++i) { 270 int id = animations[i]->id(); 271 if (!layer->compositedLayerMapping()->mainGraphicsLayer()->addAnimation(animations[i].release())) { 272 // FIXME: We should know ahead of time whether these animations can be started. 273 for (size_t j = 0; j < startedAnimationIds.size(); ++j) 274 cancelAnimationOnCompositor(element, startedAnimationIds[j]); 275 startedAnimationIds.clear(); 276 return false; 277 } 278 startedAnimationIds.append(id); 279 } 280 ASSERT(!startedAnimationIds.isEmpty()); 281 return true; 282 } 283 284 void CompositorAnimations::cancelAnimationOnCompositor(const Element& element, int id) 285 { 286 if (!canStartAnimationOnCompositor(element)) { 287 ASSERT_NOT_REACHED(); 288 return; 289 } 290 toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->removeAnimation(id); 291 } 292 293 void CompositorAnimations::pauseAnimationForTestingOnCompositor(const Element& element, int id, double pauseTime) 294 { 295 if (!canStartAnimationOnCompositor(element)) { 296 ASSERT_NOT_REACHED(); 297 return; 298 } 299 toRenderBoxModelObject(element.renderer())->layer()->compositedLayerMapping()->mainGraphicsLayer()->pauseAnimation(id, pauseTime); 300 } 301 302 // ----------------------------------------------------------------------- 303 // CompositorAnimationsImpl 304 // ----------------------------------------------------------------------- 305 306 bool CompositorAnimationsImpl::convertTimingForCompositor(const Timing& timing, CompositorTiming& out) 307 { 308 timing.assertValid(); 309 310 // All fill modes are supported (the calling code handles them). 311 312 // FIXME: Support non-zero iteration start. 313 if (timing.iterationStart) 314 return false; 315 316 // FIXME: Compositor only supports positive, integer iteration counts. 317 // Zero iterations could be converted, but silly. 318 if ((std::floor(timing.iterationCount) != timing.iterationCount) || timing.iterationCount <= 0) 319 return false; 320 321 if (!timing.iterationDuration) 322 return false; 323 324 // FIXME: Support other playback rates 325 if (timing.playbackRate != 1) 326 return false; 327 328 // All directions are supported. 329 330 // Now attempt an actual conversion 331 out.scaledDuration = timing.iterationDuration; 332 ASSERT(out.scaledDuration > 0); 333 334 double scaledStartDelay = timing.startDelay; 335 if (scaledStartDelay > 0 && scaledStartDelay > out.scaledDuration * timing.iterationCount) 336 return false; 337 338 out.reverse = (timing.direction == Timing::PlaybackDirectionReverse 339 || timing.direction == Timing::PlaybackDirectionAlternateReverse); 340 out.alternate = (timing.direction == Timing::PlaybackDirectionAlternate 341 || timing.direction == Timing::PlaybackDirectionAlternateReverse); 342 343 if (!std::isfinite(timing.iterationCount)) { 344 out.adjustedIterationCount = -1; 345 } else { 346 out.adjustedIterationCount = std::floor(timing.iterationCount); 347 ASSERT(out.adjustedIterationCount > 0); 348 } 349 350 // Compositor's time offset is positive for seeking into the animation. 351 out.scaledTimeOffset = -scaledStartDelay; 352 return true; 353 } 354 355 namespace { 356 357 template<typename PlatformAnimationCurveType, typename PlatformAnimationKeyframeType> 358 void addKeyframeWithTimingFunction(PlatformAnimationCurveType& curve, const PlatformAnimationKeyframeType& keyframe, const TimingFunction* timingFunction) 359 { 360 if (!timingFunction) { 361 curve.add(keyframe); 362 return; 363 } 364 365 switch (timingFunction->type()) { 366 case TimingFunction::LinearFunction: 367 curve.add(keyframe, blink::WebAnimationCurve::TimingFunctionTypeLinear); 368 return; 369 370 case TimingFunction::CubicBezierFunction: { 371 const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(timingFunction); 372 373 if (cubic->subType() == CubicBezierTimingFunction::Custom) { 374 curve.add(keyframe, cubic->x1(), cubic->y1(), cubic->x2(), cubic->y2()); 375 } else { 376 377 blink::WebAnimationCurve::TimingFunctionType easeType; 378 switch (cubic->subType()) { 379 case CubicBezierTimingFunction::Ease: 380 easeType = blink::WebAnimationCurve::TimingFunctionTypeEase; 381 break; 382 case CubicBezierTimingFunction::EaseIn: 383 easeType = blink::WebAnimationCurve::TimingFunctionTypeEaseIn; 384 break; 385 case CubicBezierTimingFunction::EaseOut: 386 easeType = blink::WebAnimationCurve::TimingFunctionTypeEaseOut; 387 break; 388 case CubicBezierTimingFunction::EaseInOut: 389 easeType = blink::WebAnimationCurve::TimingFunctionTypeEaseInOut; 390 break; 391 392 // Custom Bezier are handled seperately. 393 case CubicBezierTimingFunction::Custom: 394 default: 395 ASSERT_NOT_REACHED(); 396 return; 397 } 398 399 curve.add(keyframe, easeType); 400 } 401 return; 402 } 403 404 case TimingFunction::StepsFunction: 405 case TimingFunction::ChainedFunction: 406 default: 407 ASSERT_NOT_REACHED(); 408 return; 409 } 410 } 411 412 } // namespace anoymous 413 414 void CompositorAnimationsImpl::addKeyframesToCurve(blink::WebAnimationCurve& curve, const KeyframeVector& keyframes, const TimingFunction& timingFunction) 415 { 416 for (size_t i = 0; i < keyframes.size(); i++) { 417 const TimingFunction* keyframeTimingFunction = 0; 418 if (i + 1 < keyframes.size()) { // Last keyframe has no timing function 419 switch (timingFunction.type()) { 420 case TimingFunction::LinearFunction: 421 case TimingFunction::CubicBezierFunction: 422 keyframeTimingFunction = &timingFunction; 423 break; 424 425 case TimingFunction::ChainedFunction: { 426 const ChainedTimingFunction& chained = toChainedTimingFunction(timingFunction); 427 // ChainedTimingFunction criteria was checked in isCandidate, 428 // assert it is valid. 429 ASSERT(keyframes.size() == chained.m_segments.size() + 1); 430 431 keyframeTimingFunction = chained.m_segments[i].m_timingFunction.get(); 432 break; 433 } 434 case TimingFunction::StepsFunction: 435 default: 436 ASSERT_NOT_REACHED(); 437 } 438 } 439 440 ASSERT(!keyframes[i]->value()->dependsOnUnderlyingValue()); 441 RefPtr<AnimatableValue> value = keyframes[i]->value()->compositeOnto(0); 442 443 switch (curve.type()) { 444 case blink::WebAnimationCurve::AnimationCurveTypeFilter: { 445 OwnPtr<blink::WebFilterOperations> ops = adoptPtr(blink::Platform::current()->compositorSupport()->createFilterOperations()); 446 bool converted = toWebFilterOperations(toAnimatableFilterOperations(value.get())->operations(), ops.get()); 447 ASSERT_UNUSED(converted, converted); 448 449 blink::WebFilterKeyframe filterKeyframe(keyframes[i]->offset(), ops.release()); 450 blink::WebFilterAnimationCurve* filterCurve = static_cast<blink::WebFilterAnimationCurve*>(&curve); 451 addKeyframeWithTimingFunction(*filterCurve, filterKeyframe, keyframeTimingFunction); 452 break; 453 } 454 case blink::WebAnimationCurve::AnimationCurveTypeFloat: { 455 blink::WebFloatKeyframe floatKeyframe(keyframes[i]->offset(), toAnimatableDouble(value.get())->toDouble()); 456 blink::WebFloatAnimationCurve* floatCurve = static_cast<blink::WebFloatAnimationCurve*>(&curve); 457 addKeyframeWithTimingFunction(*floatCurve, floatKeyframe, keyframeTimingFunction); 458 break; 459 } 460 case blink::WebAnimationCurve::AnimationCurveTypeTransform: { 461 OwnPtr<blink::WebTransformOperations> ops = adoptPtr(blink::Platform::current()->compositorSupport()->createTransformOperations()); 462 toWebTransformOperations(toAnimatableTransform(value.get())->transformOperations(), FloatSize(), ops.get()); 463 464 blink::WebTransformKeyframe transformKeyframe(keyframes[i]->offset(), ops.release()); 465 blink::WebTransformAnimationCurve* transformCurve = static_cast<blink::WebTransformAnimationCurve*>(&curve); 466 addKeyframeWithTimingFunction(*transformCurve, transformKeyframe, keyframeTimingFunction); 467 break; 468 } 469 default: 470 ASSERT_NOT_REACHED(); 471 } 472 } 473 } 474 475 void CompositorAnimationsImpl::getAnimationOnCompositor( 476 const Timing& timing, const KeyframeAnimationEffect& effect, Vector<OwnPtr<blink::WebAnimation> >& animations) 477 { 478 ASSERT(animations.isEmpty()); 479 CompositorTiming compositorTiming; 480 bool timingValid = convertTimingForCompositor(timing, compositorTiming); 481 ASSERT_UNUSED(timingValid, timingValid); 482 483 RefPtr<TimingFunction> timingFunction = timing.timingFunction; 484 if (compositorTiming.reverse) 485 timingFunction = CompositorAnimationsTimingFunctionReverser::reverse(timingFunction.get()); 486 487 PropertySet properties = effect.properties(); 488 ASSERT(!properties.isEmpty()); 489 for (PropertySet::iterator it = properties.begin(); it != properties.end(); ++it) { 490 491 KeyframeVector values; 492 getKeyframeValuesForProperty(&effect, *it, compositorTiming.scaledDuration, compositorTiming.reverse, values); 493 494 blink::WebAnimation::TargetProperty targetProperty; 495 OwnPtr<blink::WebAnimationCurve> curve; 496 switch (*it) { 497 case CSSPropertyOpacity: { 498 targetProperty = blink::WebAnimation::TargetPropertyOpacity; 499 500 blink::WebFloatAnimationCurve* floatCurve = blink::Platform::current()->compositorSupport()->createFloatAnimationCurve(); 501 addKeyframesToCurve(*floatCurve, values, *timingFunction.get()); 502 curve = adoptPtr(floatCurve); 503 break; 504 } 505 case CSSPropertyWebkitFilter: { 506 targetProperty = blink::WebAnimation::TargetPropertyFilter; 507 blink::WebFilterAnimationCurve* filterCurve = blink::Platform::current()->compositorSupport()->createFilterAnimationCurve(); 508 addKeyframesToCurve(*filterCurve, values, *timingFunction); 509 curve = adoptPtr(filterCurve); 510 break; 511 } 512 case CSSPropertyWebkitTransform: { 513 targetProperty = blink::WebAnimation::TargetPropertyTransform; 514 blink::WebTransformAnimationCurve* transformCurve = blink::Platform::current()->compositorSupport()->createTransformAnimationCurve(); 515 addKeyframesToCurve(*transformCurve, values, *timingFunction.get()); 516 curve = adoptPtr(transformCurve); 517 break; 518 } 519 default: 520 ASSERT_NOT_REACHED(); 521 continue; 522 } 523 ASSERT(curve.get()); 524 525 OwnPtr<blink::WebAnimation> animation = adoptPtr(blink::Platform::current()->compositorSupport()->createAnimation(*curve, targetProperty)); 526 527 animation->setIterations(compositorTiming.adjustedIterationCount); 528 animation->setTimeOffset(compositorTiming.scaledTimeOffset); 529 animation->setAlternatesDirection(compositorTiming.alternate); 530 531 animations.append(animation.release()); 532 } 533 ASSERT(!animations.isEmpty()); 534 } 535 536 } // namespace WebCore 537