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
59 namespace blink {
60
61 namespace {
62
getKeyframeValuesForProperty(const KeyframeEffectModelBase * effect,CSSPropertyID id,double scale,PropertySpecificKeyframeVector & values)63 void 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
getAnimatedBoundingBox(FloatBox & box,const AnimationEffect & effect,double minValue,double maxValue) const76 bool 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
isCandidateForAnimationOnCompositor(const Timing & timing,const AnimationEffect & effect,double playerPlaybackRate)137 bool 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
canStartAnimationOnCompositor(const Element & element)196 bool CompositorAnimations::canStartAnimationOnCompositor(const Element& element)
197 {
198 return element.renderer() && element.renderer()->compositingState() == PaintsIntoOwnBacking;
199 }
200
startAnimationOnCompositor(const Element & element,double startTime,double timeOffset,const Timing & timing,const AnimationEffect & effect,Vector<int> & startedAnimationIds,double playerPlaybackRate)201 bool 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
cancelAnimationOnCompositor(const Element & element,int id)230 void 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
pauseAnimationForTestingOnCompositor(const Element & element,int id,double pauseTime)243 void 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
convertTimingForCompositor(const Timing & timing,double timeOffset,CompositorTiming & out,double playerPlaybackRate)260 bool 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
300 namespace {
301
302 template<typename PlatformAnimationCurveType, typename PlatformAnimationKeyframeType>
addKeyframeWithTimingFunction(PlatformAnimationCurveType & curve,const PlatformAnimationKeyframeType & keyframe,const TimingFunction * timingFunction)303 void 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
addKeyframesToCurve(WebCompositorAnimationCurve & curve,const PropertySpecificKeyframeVector & keyframes,const Timing & timing)358 void 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
getAnimationOnCompositor(const Timing & timing,double startTime,double timeOffset,const KeyframeEffectModelBase & effect,Vector<OwnPtr<WebCompositorAnimation>> & animations,double playerPlaybackRate)406 void 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