OLD | NEW |
| (Empty) |
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 "sky/engine/config.h" | |
32 #include "sky/engine/core/animation/CompositorAnimations.h" | |
33 | |
34 #include "sky/engine/core/animation/AnimationTranslationUtil.h" | |
35 #include "sky/engine/core/animation/CompositorAnimationsImpl.h" | |
36 #include "sky/engine/core/animation/animatable/AnimatableDouble.h" | |
37 #include "sky/engine/core/animation/animatable/AnimatableFilterOperations.h" | |
38 #include "sky/engine/core/animation/animatable/AnimatableTransform.h" | |
39 #include "sky/engine/core/animation/animatable/AnimatableValue.h" | |
40 #include "sky/engine/core/rendering/RenderBoxModelObject.h" | |
41 #include "sky/engine/core/rendering/RenderLayer.h" | |
42 #include "sky/engine/core/rendering/RenderObject.h" | |
43 #include "sky/engine/platform/geometry/FloatBox.h" | |
44 #include "sky/engine/public/platform/Platform.h" | |
45 #include "sky/engine/public/platform/WebCompositorAnimation.h" | |
46 #include "sky/engine/public/platform/WebCompositorSupport.h" | |
47 #include "sky/engine/public/platform/WebFilterAnimationCurve.h" | |
48 #include "sky/engine/public/platform/WebFilterKeyframe.h" | |
49 #include "sky/engine/public/platform/WebFloatAnimationCurve.h" | |
50 #include "sky/engine/public/platform/WebFloatKeyframe.h" | |
51 #include "sky/engine/public/platform/WebTransformAnimationCurve.h" | |
52 #include "sky/engine/public/platform/WebTransformKeyframe.h" | |
53 | |
54 #include <algorithm> | |
55 #include <cmath> | |
56 | |
57 namespace blink { | |
58 | |
59 namespace { | |
60 | |
61 void getKeyframeValuesForProperty(const KeyframeEffectModelBase* effect, CSSProp
ertyID id, double scale, bool reverse, PropertySpecificKeyframeVector& values) | |
62 { | |
63 ASSERT(values.isEmpty()); | |
64 const PropertySpecificKeyframeVector& group = effect->getPropertySpecificKey
frames(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(c
onst LinearTimingFunction& timefunc) | |
86 { | |
87 return const_cast<LinearTimingFunction*>(&timefunc); | |
88 } | |
89 | |
90 PassRefPtr<TimingFunction> CompositorAnimationsTimingFunctionReverser::reverse(c
onst CubicBezierTimingFunction& timefunc) | |
91 { | |
92 switch (timefunc.subType()) { | |
93 case CubicBezierTimingFunction::EaseIn: | |
94 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease
Out); | |
95 case CubicBezierTimingFunction::EaseOut: | |
96 return CubicBezierTimingFunction::preset(CubicBezierTimingFunction::Ease
In); | |
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(c
onst TimingFunction& timefunc) | |
109 { | |
110 switch (timefunc.type()) { | |
111 case TimingFunction::LinearFunction: { | |
112 const LinearTimingFunction& linear = toLinearTimingFunction(timefunc); | |
113 return reverse(linear); | |
114 } | |
115 case TimingFunction::CubicBezierFunction: { | |
116 const CubicBezierTimingFunction& cubic = toCubicBezierTimingFunction(tim
efunc); | |
117 return reverse(cubic); | |
118 } | |
119 | |
120 // Steps function can not be reversed. | |
121 case TimingFunction::StepsFunction: | |
122 default: | |
123 ASSERT_NOT_REACHED(); | |
124 return PassRefPtr<TimingFunction>(); | |
125 } | |
126 } | |
127 | |
128 bool CompositorAnimations::getAnimatedBoundingBox(FloatBox& box, const Animation
Effect& effect, double minValue, double maxValue) const | |
129 { | |
130 const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(ef
fect); | |
131 | |
132 PropertySet properties = keyframeEffect.properties(); | |
133 | |
134 if (properties.isEmpty()) | |
135 return true; | |
136 | |
137 minValue = std::min(minValue, 0.0); | |
138 maxValue = std::max(maxValue, 1.0); | |
139 | |
140 for (PropertySet::const_iterator it = properties.begin(); it != properties.e
nd(); ++it) { | |
141 // TODO: Add the ability to get expanded bounds for filters as well. | |
142 if (*it != CSSPropertyTransform && *it != CSSPropertyWebkitTransform) | |
143 continue; | |
144 | |
145 const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropert
ySpecificKeyframes(*it); | |
146 if (frames.isEmpty() || frames.size() < 2) | |
147 continue; | |
148 | |
149 FloatBox originalBox(box); | |
150 | |
151 for (size_t j = 0; j < frames.size() - 1; ++j) { | |
152 const AnimatableTransform* startTransform = toAnimatableTransform(fr
ames[j]->getAnimatableValue().get()); | |
153 const AnimatableTransform* endTransform = toAnimatableTransform(fram
es[j+1]->getAnimatableValue().get()); | |
154 // TODO: Add support for inflating modes other than Replace. | |
155 if (frames[j]->composite() != AnimationEffect::CompositeReplace) | |
156 return false; | |
157 | |
158 const TimingFunction& timing = frames[j]->easing(); | |
159 double min = 0; | |
160 double max = 1; | |
161 if (j == 0) { | |
162 float frameLength = frames[j+1]->offset(); | |
163 if (frameLength > 0) { | |
164 min = minValue / frameLength; | |
165 } | |
166 } | |
167 | |
168 if (j == frames.size() - 2) { | |
169 float frameLength = frames[j+1]->offset() - frames[j]->offset(); | |
170 if (frameLength > 0) { | |
171 max = 1 + (maxValue - 1) / frameLength; | |
172 } | |
173 } | |
174 | |
175 FloatBox bounds; | |
176 timing.range(&min, &max); | |
177 if (!endTransform->transformOperations().blendedBoundsForBox(origina
lBox, startTransform->transformOperations(), min, max, &bounds)) | |
178 return false; | |
179 box.expandTo(bounds); | |
180 } | |
181 } | |
182 return true; | |
183 } | |
184 | |
185 // ----------------------------------------------------------------------- | |
186 // CompositorAnimations public API | |
187 // ----------------------------------------------------------------------- | |
188 | |
189 bool CompositorAnimations::isCandidateForAnimationOnCompositor(const Timing& tim
ing, const AnimationEffect& effect) | |
190 { | |
191 const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(ef
fect); | |
192 | |
193 PropertySet properties = keyframeEffect.properties(); | |
194 | |
195 if (properties.isEmpty()) | |
196 return false; | |
197 | |
198 for (PropertySet::const_iterator it = properties.begin(); it != properties.e
nd(); ++it) { | |
199 const PropertySpecificKeyframeVector& frames = keyframeEffect.getPropert
ySpecificKeyframes(*it); | |
200 ASSERT(frames.size() >= 2); | |
201 for (size_t i = 0; i < frames.size(); ++i) { | |
202 const Keyframe::PropertySpecificKeyframe *frame = frames[i].get(); | |
203 // FIXME: Determine candidacy based on the CSSValue instead of a sna
pshot AnimatableValue. | |
204 if (frame->composite() != AnimationEffect::CompositeReplace || !fram
e->getAnimatableValue()) | |
205 return false; | |
206 | |
207 switch (*it) { | |
208 case CSSPropertyOpacity: | |
209 break; | |
210 case CSSPropertyTransform: | |
211 if (toAnimatableTransform(frame->getAnimatableValue().get())->tr
ansformOperations().dependsOnBoxSize()) | |
212 return false; | |
213 break; | |
214 case CSSPropertyWebkitFilter: { | |
215 const FilterOperations& operations = toAnimatableFilterOperation
s(frame->getAnimatableValue().get())->operations(); | |
216 if (operations.hasFilterThatMovesPixels()) | |
217 return false; | |
218 break; | |
219 } | |
220 default: | |
221 return false; | |
222 } | |
223 | |
224 // FIXME: Remove this check when crbug.com/229405 is resolved | |
225 if (i < frames.size() - 1 && frame->easing().type() == TimingFunctio
n::StepsFunction) | |
226 return false; | |
227 } | |
228 } | |
229 | |
230 CompositorAnimationsImpl::CompositorTiming out; | |
231 if (!CompositorAnimationsImpl::convertTimingForCompositor(timing, 0, out)) | |
232 return false; | |
233 | |
234 if (timing.timingFunction->type() != TimingFunction::LinearFunction) { | |
235 // Checks the of size of KeyframeVector instead of PropertySpecificKeyfr
ameVector. | |
236 const KeyframeVector& keyframes = keyframeEffect.getFrames(); | |
237 if (keyframes.size() == 2 && keyframes[0]->easing().type() == TimingFunc
tion::LinearFunction && timing.timingFunction->type() != TimingFunction::StepsFu
nction) | |
238 return true; | |
239 | |
240 // FIXME: Support non-linear timing functions in the compositor for | |
241 // more than two keyframes and step timing functions in the compositor. | |
242 return false; | |
243 } | |
244 | |
245 return true; | |
246 } | |
247 | |
248 bool CompositorAnimations::canStartAnimationOnCompositor(const Element& element) | |
249 { | |
250 return false; | |
251 } | |
252 | |
253 bool CompositorAnimations::startAnimationOnCompositor(const Element& element, do
uble startTime, double timeOffset, const Timing& timing, const AnimationEffect&
effect, Vector<int>& startedAnimationIds) | |
254 { | |
255 // FIXME(sky): Remove CompositorAnimations entirely. | |
256 ASSERT_NOT_REACHED(); | |
257 return true; | |
258 } | |
259 | |
260 void CompositorAnimations::cancelAnimationOnCompositor(const Element& element, i
nt id) | |
261 { | |
262 // FIXME(sky): Remove CompositorAnimations entirely. | |
263 ASSERT_NOT_REACHED(); | |
264 } | |
265 | |
266 void CompositorAnimations::pauseAnimationForTestingOnCompositor(const Element& e
lement, int id, double pauseTime) | |
267 { | |
268 // FIXME(sky): Remove CompositorAnimations entirely. | |
269 ASSERT_NOT_REACHED(); | |
270 } | |
271 | |
272 // ----------------------------------------------------------------------- | |
273 // CompositorAnimationsImpl | |
274 // ----------------------------------------------------------------------- | |
275 | |
276 bool CompositorAnimationsImpl::convertTimingForCompositor(const Timing& timing,
double timeOffset, CompositorTiming& out) | |
277 { | |
278 timing.assertValid(); | |
279 | |
280 // All fill modes are supported (the calling code handles them). | |
281 | |
282 // FIXME: Support non-zero iteration start. | |
283 if (timing.iterationStart) | |
284 return false; | |
285 | |
286 if (timing.iterationCount <= 0) | |
287 return false; | |
288 | |
289 if (std::isnan(timing.iterationDuration) || !timing.iterationDuration) | |
290 return false; | |
291 | |
292 // FIXME: Support other playback rates | |
293 if (timing.playbackRate != 1) | |
294 return false; | |
295 | |
296 // All directions are supported. | |
297 | |
298 // Now attempt an actual conversion | |
299 out.scaledDuration = timing.iterationDuration; | |
300 ASSERT(out.scaledDuration > 0); | |
301 | |
302 double scaledStartDelay = timing.startDelay; | |
303 if (scaledStartDelay > 0 && scaledStartDelay > out.scaledDuration * timing.i
terationCount) | |
304 return false; | |
305 | |
306 out.reverse = (timing.direction == Timing::PlaybackDirectionReverse | |
307 || timing.direction == Timing::PlaybackDirectionAlternateReverse); | |
308 out.alternate = (timing.direction == Timing::PlaybackDirectionAlternate | |
309 || timing.direction == Timing::PlaybackDirectionAlternateReverse); | |
310 | |
311 if (!std::isfinite(timing.iterationCount)) { | |
312 out.adjustedIterationCount = -1; | |
313 } else { | |
314 out.adjustedIterationCount = timing.iterationCount; | |
315 ASSERT(out.adjustedIterationCount > 0); | |
316 } | |
317 | |
318 // Compositor's time offset is positive for seeking into the animation. | |
319 out.scaledTimeOffset = -scaledStartDelay + timeOffset; | |
320 return true; | |
321 } | |
322 | |
323 namespace { | |
324 | |
325 template<typename PlatformAnimationCurveType, typename PlatformAnimationKeyframe
Type> | |
326 void addKeyframeWithTimingFunction(PlatformAnimationCurveType& curve, const Plat
formAnimationKeyframeType& keyframe, const TimingFunction* timingFunction) | |
327 { | |
328 if (!timingFunction) { | |
329 curve.add(keyframe); | |
330 return; | |
331 } | |
332 | |
333 switch (timingFunction->type()) { | |
334 case TimingFunction::LinearFunction: | |
335 curve.add(keyframe, WebCompositorAnimationCurve::TimingFunctionTypeLinea
r); | |
336 return; | |
337 | |
338 case TimingFunction::CubicBezierFunction: { | |
339 const CubicBezierTimingFunction* cubic = toCubicBezierTimingFunction(tim
ingFunction); | |
340 | |
341 if (cubic->subType() == CubicBezierTimingFunction::Custom) { | |
342 curve.add(keyframe, cubic->x1(), cubic->y1(), cubic->x2(), cubic->y2
()); | |
343 } else { | |
344 | |
345 WebCompositorAnimationCurve::TimingFunctionType easeType; | |
346 switch (cubic->subType()) { | |
347 case CubicBezierTimingFunction::Ease: | |
348 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEase; | |
349 break; | |
350 case CubicBezierTimingFunction::EaseIn: | |
351 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseIn
; | |
352 break; | |
353 case CubicBezierTimingFunction::EaseOut: | |
354 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseOu
t; | |
355 break; | |
356 case CubicBezierTimingFunction::EaseInOut: | |
357 easeType = WebCompositorAnimationCurve::TimingFunctionTypeEaseIn
Out; | |
358 break; | |
359 | |
360 // Custom Bezier are handled seperately. | |
361 case CubicBezierTimingFunction::Custom: | |
362 default: | |
363 ASSERT_NOT_REACHED(); | |
364 return; | |
365 } | |
366 | |
367 curve.add(keyframe, easeType); | |
368 } | |
369 return; | |
370 } | |
371 | |
372 case TimingFunction::StepsFunction: | |
373 default: | |
374 ASSERT_NOT_REACHED(); | |
375 return; | |
376 } | |
377 } | |
378 | |
379 } // namespace anoymous | |
380 | |
381 void CompositorAnimationsImpl::addKeyframesToCurve(WebCompositorAnimationCurve&
curve, const PropertySpecificKeyframeVector& keyframes, const Timing& timing, bo
ol reverse) | |
382 { | |
383 for (size_t i = 0; i < keyframes.size(); i++) { | |
384 RefPtr<TimingFunction> reversedTimingFunction; | |
385 const TimingFunction* keyframeTimingFunction = 0; | |
386 if (i < keyframes.size() - 1) { // Ignore timing function of last frame. | |
387 if (keyframes.size() == 2 && keyframes[0]->easing().type() == Timing
Function::LinearFunction) { | |
388 if (reverse) { | |
389 reversedTimingFunction = CompositorAnimationsTimingFunctionR
everser::reverse(*timing.timingFunction.get()); | |
390 keyframeTimingFunction = reversedTimingFunction.get(); | |
391 } else { | |
392 keyframeTimingFunction = timing.timingFunction.get(); | |
393 } | |
394 } else { | |
395 if (reverse) { | |
396 reversedTimingFunction = CompositorAnimationsTimingFunctionR
everser::reverse(keyframes[i + 1]->easing()); | |
397 keyframeTimingFunction = reversedTimingFunction.get(); | |
398 } else { | |
399 keyframeTimingFunction = &keyframes[i]->easing(); | |
400 } | |
401 } | |
402 } | |
403 | |
404 // FIXME: This relies on StringKeyframes being eagerly evaluated, which
will | |
405 // not happen eventually. Instead we should extract the CSSValue here | |
406 // and convert using another set of toAnimatableXXXOperations functions. | |
407 const AnimatableValue* value = keyframes[i]->getAnimatableValue().get(); | |
408 | |
409 switch (curve.type()) { | |
410 case WebCompositorAnimationCurve::AnimationCurveTypeFilter: { | |
411 OwnPtr<WebFilterOperations> ops = adoptPtr(Platform::current()->comp
ositorSupport()->createFilterOperations()); | |
412 toWebFilterOperations(toAnimatableFilterOperations(value)->operation
s(), ops.get()); | |
413 | |
414 WebFilterKeyframe filterKeyframe(keyframes[i]->offset(), ops.release
()); | |
415 WebFilterAnimationCurve* filterCurve = static_cast<WebFilterAnimatio
nCurve*>(&curve); | |
416 addKeyframeWithTimingFunction(*filterCurve, filterKeyframe, keyframe
TimingFunction); | |
417 break; | |
418 } | |
419 case WebCompositorAnimationCurve::AnimationCurveTypeFloat: { | |
420 WebFloatKeyframe floatKeyframe(keyframes[i]->offset(), toAnimatableD
ouble(value)->toDouble()); | |
421 WebFloatAnimationCurve* floatCurve = static_cast<WebFloatAnimationCu
rve*>(&curve); | |
422 addKeyframeWithTimingFunction(*floatCurve, floatKeyframe, keyframeTi
mingFunction); | |
423 break; | |
424 } | |
425 case WebCompositorAnimationCurve::AnimationCurveTypeTransform: { | |
426 OwnPtr<WebTransformOperations> ops = adoptPtr(Platform::current()->c
ompositorSupport()->createTransformOperations()); | |
427 toWebTransformOperations(toAnimatableTransform(value)->transformOper
ations(), ops.get()); | |
428 | |
429 WebTransformKeyframe transformKeyframe(keyframes[i]->offset(), ops.r
elease()); | |
430 WebTransformAnimationCurve* transformCurve = static_cast<WebTransfor
mAnimationCurve*>(&curve); | |
431 addKeyframeWithTimingFunction(*transformCurve, transformKeyframe, ke
yframeTimingFunction); | |
432 break; | |
433 } | |
434 default: | |
435 ASSERT_NOT_REACHED(); | |
436 } | |
437 } | |
438 } | |
439 | |
440 void CompositorAnimationsImpl::getAnimationOnCompositor(const Timing& timing, do
uble startTime, double timeOffset, const KeyframeEffectModelBase& effect, Vector
<OwnPtr<WebCompositorAnimation> >& animations) | |
441 { | |
442 ASSERT(animations.isEmpty()); | |
443 CompositorTiming compositorTiming; | |
444 bool timingValid = convertTimingForCompositor(timing, timeOffset, compositor
Timing); | |
445 ASSERT_UNUSED(timingValid, timingValid); | |
446 | |
447 PropertySet properties = effect.properties(); | |
448 ASSERT(!properties.isEmpty()); | |
449 for (PropertySet::iterator it = properties.begin(); it != properties.end();
++it) { | |
450 | |
451 PropertySpecificKeyframeVector values; | |
452 getKeyframeValuesForProperty(&effect, *it, compositorTiming.scaledDurati
on, compositorTiming.reverse, values); | |
453 | |
454 WebCompositorAnimation::TargetProperty targetProperty; | |
455 OwnPtr<WebCompositorAnimationCurve> curve; | |
456 switch (*it) { | |
457 case CSSPropertyOpacity: { | |
458 targetProperty = WebCompositorAnimation::TargetPropertyOpacity; | |
459 | |
460 WebFloatAnimationCurve* floatCurve = Platform::current()->compositor
Support()->createFloatAnimationCurve(); | |
461 addKeyframesToCurve(*floatCurve, values, timing, compositorTiming.re
verse); | |
462 curve = adoptPtr(floatCurve); | |
463 break; | |
464 } | |
465 case CSSPropertyWebkitFilter: { | |
466 targetProperty = WebCompositorAnimation::TargetPropertyFilter; | |
467 WebFilterAnimationCurve* filterCurve = Platform::current()->composit
orSupport()->createFilterAnimationCurve(); | |
468 addKeyframesToCurve(*filterCurve, values, timing, compositorTiming.r
everse); | |
469 curve = adoptPtr(filterCurve); | |
470 break; | |
471 } | |
472 case CSSPropertyTransform: { | |
473 targetProperty = WebCompositorAnimation::TargetPropertyTransform; | |
474 WebTransformAnimationCurve* transformCurve = Platform::current()->co
mpositorSupport()->createTransformAnimationCurve(); | |
475 addKeyframesToCurve(*transformCurve, values, timing, compositorTimin
g.reverse); | |
476 curve = adoptPtr(transformCurve); | |
477 break; | |
478 } | |
479 default: | |
480 ASSERT_NOT_REACHED(); | |
481 continue; | |
482 } | |
483 ASSERT(curve.get()); | |
484 | |
485 OwnPtr<WebCompositorAnimation> animation = adoptPtr(Platform::current()-
>compositorSupport()->createAnimation(*curve, targetProperty)); | |
486 | |
487 if (!std::isnan(startTime)) | |
488 animation->setStartTime(startTime); | |
489 | |
490 animation->setIterations(compositorTiming.adjustedIterationCount); | |
491 animation->setTimeOffset(compositorTiming.scaledTimeOffset); | |
492 animation->setAlternatesDirection(compositorTiming.alternate); | |
493 | |
494 animations.append(animation.release()); | |
495 } | |
496 ASSERT(!animations.isEmpty()); | |
497 } | |
498 | |
499 } // namespace blink | |
OLD | NEW |