1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2013 Collabora Ltd.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28
29#if USE(ACCELERATED_COMPOSITING)
30
31#include "PlatformClutterAnimation.h"
32
33#include "FloatConversion.h"
34#include "GraphicsLayerActor.h"
35#include "Logging.h"
36#include "NotImplemented.h"
37#include "TimingFunction.h"
38#include "UnitBezier.h"
39#include <limits.h>
40#include <wtf/CurrentTime.h>
41#include <wtf/OwnArrayPtr.h>
42#include <wtf/gobject/GOwnPtr.h>
43#include <wtf/text/CString.h>
44
45using namespace std;
46
47namespace WebCore {
48
49static void timelineStartedCallback(ClutterTimeline*, PlatformClutterAnimation* animation)
50{
51    animation->animationDidStart();
52}
53
54static String toClutterActorPropertyString(const PlatformClutterAnimation::ValueFunctionType valueFunctionType)
55{
56    // ClutterActor doesn't have 'scale' and 'translate' properties. So we should support
57    // 'scale' and 'translate' ValueFunctionType by combination of existing property animations.
58    const char* clutterActorProperty[] = { "NoProperty", "rotation-angle-x", "rotation-angle-y", "rotation-angle-z", "scale-x", "scale-y", "scale-z", "scale", "translation-x", "translation-y", "translation-z", "translate", "transform" };
59    return clutterActorProperty[valueFunctionType];
60}
61
62static ClutterAnimationMode toClutterAnimationMode(const TimingFunction* timingFunction)
63{
64    ASSERT(timingFunction);
65
66    if (timingFunction->isLinearTimingFunction())
67        return CLUTTER_LINEAR;
68    if (timingFunction->isCubicBezierTimingFunction()) {
69        CubicBezierTimingFunction::TimingFunctionPreset timingFunctionPreset = static_cast<const CubicBezierTimingFunction*>(timingFunction)->timingFunctionPreset();
70        switch (timingFunctionPreset) {
71        case CubicBezierTimingFunction::Ease:
72            return CLUTTER_EASE;
73        case CubicBezierTimingFunction::EaseIn:
74            return CLUTTER_EASE_IN;
75        case CubicBezierTimingFunction::EaseOut:
76            return CLUTTER_EASE_OUT;
77        case CubicBezierTimingFunction::EaseInOut:
78            return CLUTTER_EASE_IN_OUT;
79        default:
80            ASSERT_NOT_REACHED();
81        }
82    }
83
84    return CLUTTER_EASE;
85}
86
87static gboolean clutterMatrixProgress(const GValue* fromValue, const GValue* toValue, gdouble progress, GValue* returnValue)
88{
89    const CoglMatrix* fromCoglMatrix = static_cast<CoglMatrix*>(g_value_get_boxed(fromValue));
90    const CoglMatrix* toCoglMatrix = static_cast<CoglMatrix*>(g_value_get_boxed(toValue));
91
92    ASSERT(fromCoglMatrix && toCoglMatrix);
93
94    TransformationMatrix fromMatrix(fromCoglMatrix);
95    TransformationMatrix toMatrix(toCoglMatrix);
96    toMatrix.blend(fromMatrix, progress);
97
98    CoglMatrix resultCoglMatrix = toMatrix;
99    g_value_set_boxed(returnValue, &resultCoglMatrix);
100
101    return true;
102}
103
104PlatformClutterAnimation::AnimatedPropertyType PlatformClutterAnimation::stringToAnimatedPropertyType(const String& keyPath) const
105{
106    if (keyPath == "transform")
107        return Transform;
108    if (keyPath == "opacity")
109        return Opacity;
110    if (keyPath == "backgroundColor")
111        return BackgroundColor;
112    return NoAnimatedPropertyType;
113}
114
115PassRefPtr<PlatformClutterAnimation> PlatformClutterAnimation::create(AnimationType type, const String& keyPath)
116{
117    return adoptRef(new PlatformClutterAnimation(type, keyPath));
118}
119
120PassRefPtr<PlatformClutterAnimation> PlatformClutterAnimation::create(PlatformClutterAnimation* animation)
121{
122    return adoptRef(new PlatformClutterAnimation(animation));
123}
124
125PlatformClutterAnimation::PlatformClutterAnimation(AnimationType type, const String& keyPath)
126    : m_type(type)
127    , m_animatedPropertyType(stringToAnimatedPropertyType(keyPath))
128    , m_additive(false)
129    , m_fromValue(0)
130    , m_toValue(0)
131    , m_repeatCount(0)
132    , m_timingFunction(0)
133    , m_valueFunctionType(NoValueFunction)
134{
135    m_animation = adoptGRef(G_OBJECT(clutter_transition_group_new()));
136}
137
138PlatformClutterAnimation::PlatformClutterAnimation(const PlatformClutterAnimation* animation)
139{
140    notImplemented();
141}
142
143PlatformClutterAnimation::~PlatformClutterAnimation()
144{
145    m_animation.clear();
146    m_layer.clear();
147}
148
149bool PlatformClutterAnimation::supportsValueFunction()
150{
151    return true;
152}
153
154bool PlatformClutterAnimation::supportsAdditiveValueFunction()
155{
156    // FIXME: Clutter 1.12 doesn't support additive valueFunction type animations.
157    // So, we use matrix animation instead until clutter supports it.
158    return false;
159}
160
161double PlatformClutterAnimation::beginTime() const
162{
163    notImplemented();
164    return 0;
165}
166
167void PlatformClutterAnimation::setBeginTime(double value)
168{
169    notImplemented();
170}
171
172double PlatformClutterAnimation::duration() const
173{
174    double duration = clutter_timeline_get_duration(CLUTTER_TIMELINE(m_animation.get()));
175    return duration / 1000;
176}
177
178void PlatformClutterAnimation::setDuration(double value)
179{
180    // Clutter Animation sets the duration time in milliseconds.
181    gint duration = value * 1000;
182    clutter_timeline_set_duration(CLUTTER_TIMELINE(m_animation.get()), duration);
183}
184
185float PlatformClutterAnimation::speed() const
186{
187    notImplemented();
188    return 0;
189}
190
191void PlatformClutterAnimation::setSpeed(float value)
192{
193    notImplemented();
194}
195
196double PlatformClutterAnimation::timeOffset() const
197{
198    notImplemented();
199    return 0;
200}
201
202void PlatformClutterAnimation::setTimeOffset(double value)
203{
204    notImplemented();
205}
206
207float PlatformClutterAnimation::repeatCount() const
208{
209    return m_repeatCount;
210}
211
212void PlatformClutterAnimation::setRepeatCount(float value)
213{
214    if (m_repeatCount == value)
215        return;
216
217    m_repeatCount = value;
218    clutter_timeline_set_repeat_count(timeline(), static_cast<gint>(value == numeric_limits<float>::max() ? -1 : value));
219}
220
221bool PlatformClutterAnimation::autoreverses() const
222{
223    notImplemented();
224    return false;
225}
226
227void PlatformClutterAnimation::setAutoreverses(bool value)
228{
229    notImplemented();
230}
231
232PlatformClutterAnimation::FillModeType PlatformClutterAnimation::fillMode() const
233{
234    notImplemented();
235    return PlatformClutterAnimation::NoFillMode;
236}
237
238void PlatformClutterAnimation::setFillMode(FillModeType value)
239{
240    notImplemented();
241}
242
243void PlatformClutterAnimation::setTimingFunction(const TimingFunction* timingFunction, bool reverse)
244{
245    if (!timingFunction)
246        return;
247
248    m_timingFunction = timingFunction;
249}
250
251void PlatformClutterAnimation::copyTimingFunctionFrom(const PlatformClutterAnimation* value)
252{
253    notImplemented();
254}
255
256bool PlatformClutterAnimation::isRemovedOnCompletion() const
257{
258    notImplemented();
259    return false;
260}
261
262void PlatformClutterAnimation::setRemovedOnCompletion(bool value)
263{
264    notImplemented();
265}
266
267bool PlatformClutterAnimation::isAdditive() const
268{
269    return m_additive;
270}
271
272void PlatformClutterAnimation::setAdditive(bool value)
273{
274    if (m_additive == value)
275        return;
276
277    m_additive = value;
278}
279
280PlatformClutterAnimation::ValueFunctionType PlatformClutterAnimation::valueFunction() const
281{
282    return m_valueFunctionType;
283}
284
285void PlatformClutterAnimation::setValueFunction(ValueFunctionType value)
286{
287    if (m_valueFunctionType == value)
288        return;
289
290    m_valueFunctionType = value;
291}
292
293void PlatformClutterAnimation::setFromValue(float value)
294{
295    if (animationType() != Basic || m_fromValue == value)
296        return;
297
298    m_fromValue = value;
299}
300
301void PlatformClutterAnimation::setFromValue(const WebCore::TransformationMatrix& value)
302{
303    if (animationType() != Basic || m_fromValueMatrix == value)
304        return;
305
306    m_fromValueMatrix = value;
307}
308
309void PlatformClutterAnimation::setFromValue(const FloatPoint3D& value)
310{
311    if (animationType() != Basic || m_fromValue3D == value)
312        return;
313
314    m_fromValue3D = value;
315}
316
317void PlatformClutterAnimation::setFromValue(const WebCore::Color& value)
318{
319    notImplemented();
320}
321
322void PlatformClutterAnimation::copyFromValueFrom(const PlatformClutterAnimation* value)
323{
324    notImplemented();
325}
326
327void PlatformClutterAnimation::setToValue(float value)
328{
329    if (animationType() != Basic || m_toValue == value)
330        return;
331
332    m_toValue = value;
333}
334
335void PlatformClutterAnimation::setToValue(const WebCore::TransformationMatrix& value)
336{
337    if (animationType() != Basic || m_toValueMatrix == value)
338        return;
339
340    m_toValueMatrix = value;
341}
342
343void PlatformClutterAnimation::setToValue(const FloatPoint3D& value)
344{
345    if (animationType() != Basic || m_toValue3D == value)
346        return;
347
348    m_toValue3D = value;
349}
350
351void PlatformClutterAnimation::setToValue(const WebCore::Color& value)
352{
353    notImplemented();
354}
355
356void PlatformClutterAnimation::copyToValueFrom(const PlatformClutterAnimation* value)
357{
358    notImplemented();
359}
360
361void PlatformClutterAnimation::setValues(const Vector<float>& value)
362{
363    ASSERT(animationType() == Keyframe);
364
365    m_values = value;
366}
367
368void PlatformClutterAnimation::setValues(const Vector<WebCore::TransformationMatrix>& value)
369{
370    ASSERT(animationType() == Keyframe);
371
372    m_valuesMatrix = value;
373}
374
375void PlatformClutterAnimation::setValues(const Vector<FloatPoint3D>& value)
376{
377    ASSERT(animationType() == Keyframe);
378
379    m_values3D = value;
380}
381
382void PlatformClutterAnimation::setValues(const Vector<WebCore::Color>& value)
383{
384    notImplemented();
385}
386
387void PlatformClutterAnimation::copyValuesFrom(const PlatformClutterAnimation* value)
388{
389    notImplemented();
390}
391
392void PlatformClutterAnimation::setKeyTimes(const Vector<float>& value)
393{
394    ASSERT(animationType() == Keyframe);
395
396    m_keyTimes = value;
397}
398
399void PlatformClutterAnimation::copyKeyTimesFrom(const PlatformClutterAnimation* value)
400{
401    notImplemented();
402}
403
404void PlatformClutterAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value, bool reverse)
405{
406    ASSERT(animationType() == Keyframe);
407
408    m_timingFunctions = value;
409}
410
411void PlatformClutterAnimation::copyTimingFunctionsFrom(const PlatformClutterAnimation* value)
412{
413    notImplemented();
414}
415
416void PlatformClutterAnimation::animationDidStart()
417{
418    ASSERT(CLUTTER_IS_ACTOR(m_layer.get()));
419
420    PlatformClutterLayerClient* client = graphicsLayerActorGetClient(GRAPHICS_LAYER_ACTOR(m_layer.get()));
421    if (!client)
422        return;
423
424    client->platformClutterLayerAnimationStarted(WTF::currentTime());
425}
426
427ClutterTimeline* PlatformClutterAnimation::timeline() const
428{
429    ASSERT(m_animation);
430    return CLUTTER_TIMELINE(m_animation.get());
431}
432
433void PlatformClutterAnimation::addClutterTransitionForProperty(const String& property, const float fromValue, const float toValue)
434{
435    ASSERT(property != "NoProperty");
436
437    GRefPtr<ClutterTransition> transition = adoptGRef(clutter_property_transition_new(property.utf8().data()));
438    if (property == "opacity") {
439        clutter_transition_set_from(transition.get(), G_TYPE_UINT, static_cast<unsigned>(fromValue));
440        clutter_transition_set_to(transition.get(), G_TYPE_UINT, static_cast<unsigned>(toValue));
441    } else {
442        clutter_transition_set_from(transition.get(), G_TYPE_FLOAT, fromValue);
443        clutter_transition_set_to(transition.get(), G_TYPE_FLOAT, toValue);
444    }
445
446    clutter_timeline_set_progress_mode(timeline(), toClutterAnimationMode(m_timingFunction));
447
448    clutter_transition_group_add_transition(CLUTTER_TRANSITION_GROUP(m_animation.get()), transition.get());
449}
450
451void PlatformClutterAnimation::addClutterTransitionForProperty(const String& property, const WebCore::TransformationMatrix& fromValue, const WebCore::TransformationMatrix& toValue)
452{
453    ASSERT(property != "NoProperty");
454
455    const CoglMatrix fromCoglMatrix = fromValue;
456    const CoglMatrix toCoglMatrix = toValue;
457
458    GRefPtr<ClutterTransition> transition = adoptGRef(clutter_property_transition_new(property.utf8().data()));
459    clutter_transition_set_from(transition.get(), CLUTTER_TYPE_MATRIX, &fromCoglMatrix);
460    clutter_transition_set_to(transition.get(), CLUTTER_TYPE_MATRIX, &toCoglMatrix);
461
462    clutter_timeline_set_progress_mode(timeline(), toClutterAnimationMode(m_timingFunction));
463
464    clutter_transition_group_add_transition(CLUTTER_TRANSITION_GROUP(m_animation.get()), transition.get());
465
466    // FIXME: The matrix interpolation api, clutter_matrix_progress of Clutter 1.12 works unexpectedly.
467    // So we overwrite it and handle the interpolation of two matrices with TransformationMatrix.
468    // See https://bugzilla.gnome.org/show_bug.cgi?id=694197
469    clutter_interval_register_progress_func(CLUTTER_TYPE_MATRIX, clutterMatrixProgress);
470}
471
472void PlatformClutterAnimation::addClutterTransitionForProperty(const String& property, const FloatPoint3D& fromValue, const FloatPoint3D& toValue)
473{
474    ASSERT(property != "NoProperty");
475
476    if (property == "scale") {
477        addClutterTransitionForProperty(String("scale-x"), fromValue.x(), toValue.x());
478        addClutterTransitionForProperty(String("scale-y"), fromValue.y(), toValue.y());
479        return;
480    }
481    if (property == "translate") {
482        addClutterTransitionForProperty(String("translation-x"), fromValue.x(), toValue.x());
483        addClutterTransitionForProperty(String("translation-y"), fromValue.x(), toValue.y());
484        return;
485    }
486
487    ASSERT_NOT_REACHED();
488}
489
490void PlatformClutterAnimation::addClutterKeyframeTransitionForProperty(const String& property, const Vector<float>& values)
491{
492    ASSERT(property != "NoProperty");
493
494    GType gType = (property == "opacity" ? G_TYPE_UINT : G_TYPE_FLOAT);
495
496    GRefPtr<ClutterTransition> transition = adoptGRef(clutter_keyframe_transition_new(property.utf8().data()));
497    if (gType == G_TYPE_UINT) {
498        clutter_transition_set_from(transition.get(), gType, static_cast<unsigned>(values.first()));
499        clutter_transition_set_to(transition.get(), gType, static_cast<unsigned>(values.last()));
500    } else {
501        clutter_transition_set_from(transition.get(), gType, values.first());
502        clutter_transition_set_to(transition.get(), gType, values.last());
503    }
504
505    // Ignore the first keyframe, since it's a '0' frame, meaningless.
506    const unsigned nKeyframes = values.size() - 1;
507    OwnArrayPtr<ClutterAnimationMode> animationModes = adoptArrayPtr(new ClutterAnimationMode[nKeyframes]);
508    OwnArrayPtr<double> keyTimes = adoptArrayPtr(new double[nKeyframes]);
509    GOwnPtr<GValue> keyValues(g_new0(GValue, nKeyframes));
510
511    for (unsigned i = 0; i < nKeyframes; ++i) {
512        keyTimes[i] = static_cast<double>(m_keyTimes[i + 1]);
513        animationModes[i] = toClutterAnimationMode(m_timingFunctions[i]);
514        g_value_init(&keyValues.get()[i], gType);
515        if (gType == G_TYPE_UINT)
516            g_value_set_uint(&keyValues.get()[i], static_cast<unsigned>(values[i + 1]));
517        else
518            g_value_set_float(&keyValues.get()[i], values[i + 1]);
519    }
520
521    clutter_keyframe_transition_set_key_frames(CLUTTER_KEYFRAME_TRANSITION(transition.get()), nKeyframes, keyTimes.get());
522    clutter_keyframe_transition_set_values(CLUTTER_KEYFRAME_TRANSITION(transition.get()), nKeyframes, keyValues.get());
523    clutter_keyframe_transition_set_modes(CLUTTER_KEYFRAME_TRANSITION(transition.get()), nKeyframes, animationModes.get());
524
525    clutter_transition_group_add_transition(CLUTTER_TRANSITION_GROUP(m_animation.get()), transition.get());
526
527    for (unsigned i = 0; i < nKeyframes; ++i)
528        g_value_unset(&keyValues.get()[i]);
529}
530
531void PlatformClutterAnimation::addClutterKeyframeTransitionForProperty(const String& property, const Vector<WebCore::TransformationMatrix>& values)
532{
533    ASSERT(property != "NoProperty");
534
535    Vector<CoglMatrix> coglMatrices;
536    for (unsigned i = 0; i < values.size(); ++i)
537        coglMatrices.append(values[i]);
538
539    GRefPtr<ClutterTransition> transition = adoptGRef(clutter_keyframe_transition_new(property.utf8().data()));
540    clutter_transition_set_from(transition.get(), CLUTTER_TYPE_MATRIX, coglMatrices.first());
541    clutter_transition_set_to(transition.get(), CLUTTER_TYPE_MATRIX, coglMatrices.last());
542
543    // Ignore the first keyframe, since it's a '0' frame, meaningless.
544    const unsigned nKeyframes = values.size() - 1;
545    OwnArrayPtr<ClutterAnimationMode> animationModes = adoptArrayPtr(new ClutterAnimationMode[nKeyframes]);
546    OwnArrayPtr<double> keyTimes = adoptArrayPtr(new double[nKeyframes]);
547    GOwnPtr<GValue> keyValues(g_new0(GValue, nKeyframes));
548
549    for (unsigned i = 0; i < nKeyframes; ++i) {
550        keyTimes[i] = static_cast<double>(m_keyTimes[i + 1]);
551        animationModes[i] = toClutterAnimationMode(m_timingFunctions[i]);
552        g_value_init(&keyValues.get()[i], CLUTTER_TYPE_MATRIX);
553        g_value_set_boxed(&keyValues.get()[i], &coglMatrices[i + 1]);
554    }
555
556    clutter_keyframe_transition_set_key_frames(CLUTTER_KEYFRAME_TRANSITION(transition.get()), nKeyframes, keyTimes.get());
557    clutter_keyframe_transition_set_values(CLUTTER_KEYFRAME_TRANSITION(transition.get()), nKeyframes, keyValues.get());
558    clutter_keyframe_transition_set_modes(CLUTTER_KEYFRAME_TRANSITION(transition.get()), nKeyframes, animationModes.get());
559
560    clutter_transition_group_add_transition(CLUTTER_TRANSITION_GROUP(m_animation.get()), transition.get());
561
562    clutter_interval_register_progress_func(CLUTTER_TYPE_MATRIX, clutterMatrixProgress);
563
564    for (unsigned i = 0; i < nKeyframes; ++i)
565        g_value_unset(&keyValues.get()[i]);
566}
567
568void PlatformClutterAnimation::addClutterKeyframeTransitionForProperty(const String& property, const Vector<FloatPoint3D>& values)
569{
570    ASSERT(property != "NoProperty");
571
572    Vector<float> valuesX, valuesY;
573    for (unsigned i = 0; i < values.size(); ++i) {
574        valuesX.append(values[i].x());
575        valuesY.append(values[i].y());
576    }
577
578    if (property == "scale") {
579        addClutterKeyframeTransitionForProperty(String("scale-x"), valuesX);
580        addClutterKeyframeTransitionForProperty(String("scale-y"), valuesY);
581        return;
582    }
583    if (property == "translate") {
584        addClutterKeyframeTransitionForProperty(String("translation-x"), valuesX);
585        addClutterKeyframeTransitionForProperty(String("translation-y"), valuesY);
586        return;
587    }
588
589    ASSERT_NOT_REACHED();
590}
591
592void PlatformClutterAnimation::addOpacityTransition()
593{
594    if (animationType() == Keyframe) {
595        for (unsigned i = 0; i < m_values.size(); ++i)
596            m_values[i] *= 255;
597
598        addClutterKeyframeTransitionForProperty(String("opacity"), m_values);
599    } else {
600        m_fromValue *= 255;
601        m_toValue *= 255;
602
603        addClutterTransitionForProperty(String("opacity"), m_fromValue, m_toValue);
604    }
605}
606
607void PlatformClutterAnimation::addTransformTransition()
608{
609    const bool isKeyframe = (animationType() == Keyframe);
610
611    switch (m_valueFunctionType) {
612    case RotateX:
613    case RotateY:
614    case RotateZ:
615        if (isKeyframe) {
616            for (unsigned i = 0; i < m_values.size(); ++i)
617                m_values[i] = rad2deg(m_values[i]);
618
619            addClutterKeyframeTransitionForProperty(toClutterActorPropertyString(m_valueFunctionType), m_values);
620        } else {
621            m_fromValue = rad2deg(m_fromValue);
622            m_toValue = rad2deg(m_toValue);
623
624            addClutterTransitionForProperty(toClutterActorPropertyString(m_valueFunctionType), m_fromValue, m_toValue);
625        }
626        break;
627    case ScaleX:
628    case ScaleY:
629    case ScaleZ:
630    case TranslateX:
631    case TranslateY:
632    case TranslateZ:
633        if (isKeyframe)
634            addClutterKeyframeTransitionForProperty(toClutterActorPropertyString(m_valueFunctionType), m_values);
635        else
636            addClutterTransitionForProperty(toClutterActorPropertyString(m_valueFunctionType), m_fromValue, m_toValue);
637        break;
638    case Scale:
639    case Translate:
640        if (isKeyframe)
641            addClutterKeyframeTransitionForProperty(toClutterActorPropertyString(m_valueFunctionType), m_values3D);
642        else
643            addClutterTransitionForProperty(toClutterActorPropertyString(m_valueFunctionType), m_fromValue3D, m_toValue3D);
644        break;
645    case Matrix:
646        if (isKeyframe)
647            addClutterKeyframeTransitionForProperty(toClutterActorPropertyString(m_valueFunctionType), m_valuesMatrix);
648        else
649            addClutterTransitionForProperty(toClutterActorPropertyString(m_valueFunctionType), m_fromValueMatrix, m_toValueMatrix);
650        break;
651    default:
652        ASSERT_NOT_REACHED();
653    }
654
655    // FIXME: Work-around the fact that if there is a transform already set the animation fails to start.
656    // https://bugzilla.gnome.org/show_bug.cgi?id=696804
657    clutter_actor_set_transform(m_layer.get(), 0);
658}
659
660void PlatformClutterAnimation::addAnimationForKey(GraphicsLayerActor* platformLayer, const String& key)
661{
662    ASSERT(!g_object_get_data(G_OBJECT(platformLayer), key.utf8().data()));
663
664    m_layer = CLUTTER_ACTOR(platformLayer);
665
666    if (m_animatedPropertyType == Opacity)
667        addOpacityTransition();
668    else if (m_animatedPropertyType == Transform)
669        addTransformTransition();
670    else if (m_animatedPropertyType == BackgroundColor)
671        ASSERT_NOT_REACHED();
672    else
673        ASSERT_NOT_REACHED();
674
675    g_signal_connect(timeline(), "started", G_CALLBACK(timelineStartedCallback), this);
676    g_object_set_data(G_OBJECT(platformLayer), key.utf8().data(), this);
677
678    clutter_actor_add_transition(m_layer.get(), key.utf8().data(), CLUTTER_TRANSITION(m_animation.get()));
679}
680
681void PlatformClutterAnimation::removeAnimationForKey(GraphicsLayerActor* layer, const String& key)
682{
683    clutter_actor_remove_transition(CLUTTER_ACTOR(layer), key.utf8().data());
684    g_object_set_data(G_OBJECT(layer), key.utf8().data(), 0);
685}
686
687} // namespace WebCore
688
689#endif // USE(ACCELERATED_COMPOSITING)
690