1/*
2 * Copyright (C) 2011 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if USE(ACCELERATED_COMPOSITING)
29
30#include "PlatformCAAnimation.h"
31
32#include "FloatConversion.h"
33#include "PlatformString.h"
34#include "TimingFunction.h"
35#include <QuartzCore/CACFAnimation.h>
36#include <QuartzCore/CACFTiming.h>
37#include <QuartzCore/CACFTimingFunction.h>
38#include <QuartzCore/CACFValueFunction.h>
39#include <QuartzCore/CACFVector.h>
40#include <wtf/UnusedParam.h>
41
42using namespace WebCore;
43
44static CFStringRef toCACFFillModeType(PlatformCAAnimation::FillModeType type)
45{
46    switch (type) {
47    case PlatformCAAnimation::NoFillMode:
48    case PlatformCAAnimation::Forwards: return kCACFFillModeForwards;
49    case PlatformCAAnimation::Backwards: return kCACFFillModeBackwards;
50    case PlatformCAAnimation::Both: return kCACFFillModeBoth;
51    }
52    ASSERT_NOT_REACHED();
53    return 0;
54}
55
56static PlatformCAAnimation::FillModeType fromCACFFillModeType(CFStringRef string)
57{
58    if (string == kCACFFillModeBackwards)
59        return PlatformCAAnimation::Backwards;
60
61    if (string == kCACFFillModeBoth)
62        return PlatformCAAnimation::Both;
63
64    return PlatformCAAnimation::Forwards;
65}
66
67static CFStringRef toCACFValueFunctionType(PlatformCAAnimation::ValueFunctionType type)
68{
69    switch (type) {
70    case PlatformCAAnimation::NoValueFunction: return 0;
71    case PlatformCAAnimation::RotateX: return kCACFValueFunctionRotateX;
72    case PlatformCAAnimation::RotateY: return kCACFValueFunctionRotateY;
73    case PlatformCAAnimation::RotateZ: return kCACFValueFunctionRotateZ;
74    case PlatformCAAnimation::ScaleX: return kCACFValueFunctionScaleX;
75    case PlatformCAAnimation::ScaleY: return kCACFValueFunctionScaleY;
76    case PlatformCAAnimation::ScaleZ: return kCACFValueFunctionScaleZ;
77    case PlatformCAAnimation::Scale: return kCACFValueFunctionScale;
78    case PlatformCAAnimation::TranslateX: return kCACFValueFunctionTranslateX;
79    case PlatformCAAnimation::TranslateY: return kCACFValueFunctionTranslateY;
80    case PlatformCAAnimation::TranslateZ: return kCACFValueFunctionTranslateZ;
81    case PlatformCAAnimation::Translate: return kCACFValueFunctionTranslate;
82    }
83    ASSERT_NOT_REACHED();
84    return 0;
85}
86
87static PlatformCAAnimation::ValueFunctionType fromCACFValueFunctionType(CFStringRef string)
88{
89    if (string == kCACFValueFunctionRotateX)
90        return PlatformCAAnimation::RotateX;
91
92    if (string == kCACFValueFunctionRotateY)
93        return PlatformCAAnimation::RotateY;
94
95    if (string == kCACFValueFunctionRotateZ)
96        return PlatformCAAnimation::RotateZ;
97
98    if (string == kCACFValueFunctionScaleX)
99        return PlatformCAAnimation::ScaleX;
100
101    if (string == kCACFValueFunctionScaleY)
102        return PlatformCAAnimation::ScaleY;
103
104    if (string == kCACFValueFunctionScaleZ)
105        return PlatformCAAnimation::ScaleZ;
106
107    if (string == kCACFValueFunctionScale)
108        return PlatformCAAnimation::Scale;
109
110    if (string == kCACFValueFunctionTranslateX)
111        return PlatformCAAnimation::TranslateX;
112
113    if (string == kCACFValueFunctionTranslateY)
114        return PlatformCAAnimation::TranslateY;
115
116    if (string == kCACFValueFunctionTranslateZ)
117        return PlatformCAAnimation::TranslateZ;
118
119    if (string == kCACFValueFunctionTranslate)
120        return PlatformCAAnimation::Translate;
121
122    return PlatformCAAnimation::NoValueFunction;
123}
124
125static RetainPtr<CACFTimingFunctionRef> toCACFTimingFunction(const TimingFunction* timingFunction)
126{
127    ASSERT(timingFunction);
128    if (timingFunction->isCubicBezierTimingFunction()) {
129        const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
130        return RetainPtr<CACFTimingFunctionRef>(AdoptCF, CACFTimingFunctionCreate(static_cast<float>(ctf->x1()), static_cast<float>(ctf->y1()), static_cast<float>(ctf->x2()), static_cast<float>(ctf->y2())));
131    }
132
133    return CACFTimingFunctionGetFunctionWithName(kCACFTimingFunctionLinear);
134}
135
136PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(AnimationType type, const String& keyPath)
137{
138    return adoptRef(new PlatformCAAnimation(type, keyPath));
139}
140
141PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(PlatformAnimationRef animation)
142{
143    return adoptRef(new PlatformCAAnimation(animation));
144}
145
146PlatformCAAnimation::PlatformCAAnimation(AnimationType type, const String& keyPath)
147    : m_type(type)
148{
149    if (type == Basic)
150        m_animation.adoptCF(CACFAnimationCreate(kCACFBasicAnimation));
151    else
152        m_animation.adoptCF(CACFAnimationCreate(kCACFKeyframeAnimation));
153
154    RetainPtr<CFStringRef> s(AdoptCF, keyPath.createCFString());
155    CACFAnimationSetKeyPath(m_animation.get(), s.get());
156}
157
158PlatformCAAnimation::PlatformCAAnimation(PlatformAnimationRef animation)
159{
160    if (CACFAnimationGetClass(animation) == kCACFBasicAnimation)
161        m_type = Basic;
162    else if (CACFAnimationGetClass(animation) == kCACFKeyframeAnimation)
163        m_type = Keyframe;
164    else {
165        ASSERT_NOT_REACHED();
166        return;
167    }
168
169    m_animation = animation;
170}
171
172PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::copy() const
173{
174    RefPtr<PlatformCAAnimation> animation = create(animationType(), keyPath());
175
176    animation->setBeginTime(beginTime());
177    animation->setDuration(duration());
178    animation->setSpeed(speed());
179    animation->setTimeOffset(timeOffset());
180    animation->setRepeatCount(repeatCount());
181    animation->setAutoreverses(autoreverses());
182    animation->setFillMode(fillMode());
183    animation->setRemovedOnCompletion(isRemovedOnCompletion());
184    animation->setAdditive(isAdditive());
185    animation->copyTimingFunctionFrom(this);
186    animation->setValueFunction(valueFunction());
187
188    // Copy the specific Basic or Keyframe values
189    if (animationType() == Keyframe) {
190        animation->copyValuesFrom(this);
191        animation->copyKeyTimesFrom(this);
192        animation->copyTimingFunctionsFrom(this);
193    } else {
194        animation->copyFromValueFrom(this);
195        animation->copyToValueFrom(this);
196    }
197
198    return animation;
199}
200
201PlatformCAAnimation::~PlatformCAAnimation()
202{
203}
204
205bool PlatformCAAnimation::supportsValueFunction()
206{
207    return true;
208}
209
210PlatformAnimationRef PlatformCAAnimation::platformAnimation() const
211{
212    return m_animation.get();
213}
214
215String PlatformCAAnimation::keyPath() const
216{
217    return CACFAnimationGetKeyPath(m_animation.get());
218}
219
220CFTimeInterval PlatformCAAnimation::beginTime() const
221{
222    return CACFAnimationGetBeginTime(m_animation.get());
223}
224
225void PlatformCAAnimation::setBeginTime(CFTimeInterval value)
226{
227    CACFAnimationSetBeginTime(m_animation.get(), value);
228}
229
230CFTimeInterval PlatformCAAnimation::duration() const
231{
232    return CACFAnimationGetDuration(m_animation.get());
233}
234
235void PlatformCAAnimation::setDuration(CFTimeInterval value)
236{
237    CACFAnimationSetDuration(m_animation.get(), value);
238}
239
240float PlatformCAAnimation::speed() const
241{
242    return CACFAnimationGetSpeed(m_animation.get());
243}
244
245void PlatformCAAnimation::setSpeed(float value)
246{
247    CACFAnimationSetSpeed(m_animation.get(), value);
248}
249
250CFTimeInterval PlatformCAAnimation::timeOffset() const
251{
252    return CACFAnimationGetTimeOffset(m_animation.get());
253}
254
255void PlatformCAAnimation::setTimeOffset(CFTimeInterval value)
256{
257    CACFAnimationSetTimeOffset(m_animation.get(), value);
258}
259
260float PlatformCAAnimation::repeatCount() const
261{
262    return CACFAnimationGetRepeatCount(m_animation.get());
263}
264
265void PlatformCAAnimation::setRepeatCount(float value)
266{
267    CACFAnimationSetRepeatCount(m_animation.get(), value);
268}
269
270bool PlatformCAAnimation::autoreverses() const
271{
272    return CACFAnimationGetAutoreverses(m_animation.get());
273}
274
275void PlatformCAAnimation::setAutoreverses(bool value)
276{
277    CACFAnimationSetAutoreverses(m_animation.get(), value);
278}
279
280PlatformCAAnimation::FillModeType PlatformCAAnimation::fillMode() const
281{
282    return fromCACFFillModeType(CACFAnimationGetFillMode(m_animation.get()));
283}
284
285void PlatformCAAnimation::setFillMode(FillModeType value)
286{
287    CACFAnimationSetFillMode(m_animation.get(), toCACFFillModeType(value));
288}
289
290void PlatformCAAnimation::setTimingFunction(const TimingFunction* value)
291{
292    CACFAnimationSetTimingFunction(m_animation.get(), toCACFTimingFunction(value).get());
293}
294
295void PlatformCAAnimation::copyTimingFunctionFrom(const PlatformCAAnimation* value)
296{
297    CACFAnimationSetTimingFunction(m_animation.get(), CACFAnimationGetTimingFunction(value->m_animation.get()));
298}
299
300bool PlatformCAAnimation::isRemovedOnCompletion() const
301{
302    return CACFAnimationIsRemovedOnCompletion(m_animation.get());
303}
304
305void PlatformCAAnimation::setRemovedOnCompletion(bool value)
306{
307    CACFAnimationSetRemovedOnCompletion(m_animation.get(), value);
308}
309
310bool PlatformCAAnimation::isAdditive() const
311{
312    return CACFAnimationIsAdditive(m_animation.get());
313}
314
315void PlatformCAAnimation::setAdditive(bool value)
316{
317    CACFAnimationSetAdditive(m_animation.get(), value);
318}
319
320PlatformCAAnimation::ValueFunctionType PlatformCAAnimation::valueFunction() const
321{
322    return fromCACFValueFunctionType(CACFValueFunctionGetName(CACFAnimationGetValueFunction(m_animation.get())));
323}
324
325void PlatformCAAnimation::setValueFunction(ValueFunctionType value)
326{
327    CACFAnimationSetValueFunction(m_animation.get(), CACFValueFunctionGetFunctionWithName(toCACFValueFunctionType(value)));
328}
329
330void PlatformCAAnimation::setFromValue(float value)
331{
332    if (animationType() != Basic)
333        return;
334
335    RetainPtr<CFNumberRef> v(AdoptCF, CFNumberCreate(0, kCFNumberFloatType, &value));
336    CACFAnimationSetFromValue(m_animation.get(), v.get());
337}
338
339void PlatformCAAnimation::setFromValue(const WebCore::TransformationMatrix& value)
340{
341    if (animationType() != Basic)
342        return;
343
344    RetainPtr<CACFVectorRef> v(AdoptCF, CACFVectorCreateTransform(value));
345    CACFAnimationSetFromValue(m_animation.get(), v.get());
346}
347
348void PlatformCAAnimation::setFromValue(const FloatPoint3D& value)
349{
350    if (animationType() != Basic)
351        return;
352
353    float a[3] = { value.x(), value.y(), value.z() };
354    RetainPtr<CACFVectorRef> v(AdoptCF, CACFVectorCreate(3, a));
355    CACFAnimationSetFromValue(m_animation.get(), v.get());
356}
357
358void PlatformCAAnimation::setFromValue(const WebCore::Color& value)
359{
360    if (animationType() != Basic)
361        return;
362
363    float a[4] = { value.red(), value.green(), value.blue(), value.alpha() };
364    RetainPtr<CACFVectorRef> v(AdoptCF, CACFVectorCreate(4, a));
365    CACFAnimationSetFromValue(m_animation.get(), v.get());
366}
367
368void PlatformCAAnimation::copyFromValueFrom(const PlatformCAAnimation* value)
369{
370    if (animationType() != Basic || value->animationType() != Basic)
371        return;
372
373    CACFAnimationSetFromValue(m_animation.get(), CACFAnimationGetFromValue(value->platformAnimation()));
374}
375
376void PlatformCAAnimation::setToValue(float value)
377{
378    if (animationType() != Basic)
379        return;
380
381    RetainPtr<CFNumberRef> v(AdoptCF, CFNumberCreate(0, kCFNumberFloatType, &value));
382    CACFAnimationSetToValue(m_animation.get(), v.get());
383}
384
385void PlatformCAAnimation::setToValue(const WebCore::TransformationMatrix& value)
386{
387    if (animationType() != Basic)
388        return;
389
390    RetainPtr<CACFVectorRef> v(AdoptCF, CACFVectorCreateTransform(value));
391    CACFAnimationSetToValue(m_animation.get(), v.get());
392}
393
394void PlatformCAAnimation::setToValue(const FloatPoint3D& value)
395{
396    if (animationType() != Basic)
397        return;
398
399    float a[3] = { value.x(), value.y(), value.z() };
400    RetainPtr<CACFVectorRef> v(AdoptCF, CACFVectorCreate(3, a));
401    CACFAnimationSetToValue(m_animation.get(), v.get());
402}
403
404void PlatformCAAnimation::setToValue(const WebCore::Color& value)
405{
406    if (animationType() != Basic)
407        return;
408
409    float a[4] = { value.red(), value.green(), value.blue(), value.alpha() };
410    RetainPtr<CACFVectorRef> v(AdoptCF, CACFVectorCreate(4, a));
411    CACFAnimationSetToValue(m_animation.get(), v.get());
412}
413
414void PlatformCAAnimation::copyToValueFrom(const PlatformCAAnimation* value)
415{
416    if (animationType() != Basic || value->animationType() != Basic)
417        return;
418
419    CACFAnimationSetToValue(m_animation.get(), CACFAnimationGetToValue(value->platformAnimation()));
420}
421
422
423// Keyframe-animation properties.
424void PlatformCAAnimation::setValues(const Vector<float>& value)
425{
426    if (animationType() != Keyframe)
427        return;
428
429    RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
430    for (size_t i = 0; i < value.size(); ++i) {
431        RetainPtr<CFNumberRef> v(AdoptCF, CFNumberCreate(0, kCFNumberFloatType, &value[i]));
432        CFArrayAppendValue(array.get(), v.get());
433    }
434
435    CACFAnimationSetValues(m_animation.get(), array.get());
436}
437
438void PlatformCAAnimation::setValues(const Vector<WebCore::TransformationMatrix>& value)
439{
440    if (animationType() != Keyframe)
441        return;
442
443    RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
444    for (size_t i = 0; i < value.size(); ++i) {
445        RetainPtr<CACFVectorRef> v(AdoptCF, CACFVectorCreateTransform(value[i]));
446        CFArrayAppendValue(array.get(), v.get());
447    }
448
449    CACFAnimationSetValues(m_animation.get(), array.get());
450}
451
452void PlatformCAAnimation::setValues(const Vector<FloatPoint3D>& value)
453{
454    if (animationType() != Keyframe)
455        return;
456
457    RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
458    for (size_t i = 0; i < value.size(); ++i) {
459        float a[3] = { value[i].x(), value[i].y(), value[i].z() };
460        RetainPtr<CACFVectorRef> v(AdoptCF, CACFVectorCreate(3, a));
461        CFArrayAppendValue(array.get(), v.get());
462    }
463
464    CACFAnimationSetValues(m_animation.get(), array.get());
465}
466
467void PlatformCAAnimation::setValues(const Vector<WebCore::Color>& value)
468{
469    if (animationType() != Keyframe)
470        return;
471
472    RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
473    for (size_t i = 0; i < value.size(); ++i) {
474        float a[4] = { value[i].red(), value[i].green(), value[i].blue(), value[i].alpha() };
475        RetainPtr<CACFVectorRef> v(AdoptCF, CACFVectorCreate(4, a));
476        CFArrayAppendValue(array.get(), v.get());
477    }
478
479    CACFAnimationSetValues(m_animation.get(), array.get());
480}
481
482void PlatformCAAnimation::copyValuesFrom(const PlatformCAAnimation* value)
483{
484    if (animationType() != Keyframe || value->animationType() != Keyframe)
485        return;
486
487    CACFAnimationSetValues(m_animation.get(), CACFAnimationGetValues(value->platformAnimation()));
488}
489
490void PlatformCAAnimation::setKeyTimes(const Vector<float>& value)
491{
492    if (animationType() != Keyframe)
493        return;
494
495    RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
496    for (size_t i = 0; i < value.size(); ++i) {
497        RetainPtr<CFNumberRef> v(AdoptCF, CFNumberCreate(0, kCFNumberFloatType, &value[i]));
498        CFArrayAppendValue(array.get(), v.get());
499    }
500
501    CACFAnimationSetKeyTimes(m_animation.get(), array.get());
502}
503
504void PlatformCAAnimation::copyKeyTimesFrom(const PlatformCAAnimation* value)
505{
506    if (animationType() != Keyframe)
507        return;
508
509    CACFAnimationSetKeyTimes(m_animation.get(), CACFAnimationGetKeyTimes(value->platformAnimation()));
510}
511
512void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value)
513{
514    if (animationType() != Keyframe)
515        return;
516
517    RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
518    for (size_t i = 0; i < value.size(); ++i) {
519        RetainPtr<CFNumberRef> v(AdoptCF, CFNumberCreate(0, kCFNumberFloatType, &value[i]));
520        CFArrayAppendValue(array.get(), toCACFTimingFunction(value[i]).get());
521    }
522
523    CACFAnimationSetTimingFunctions(m_animation.get(), array.get());
524}
525
526void PlatformCAAnimation::copyTimingFunctionsFrom(const PlatformCAAnimation* value)
527{
528    CACFAnimationSetTimingFunctions(m_animation.get(), CACFAnimationGetTimingFunctions(value->platformAnimation()));
529}
530
531#endif // USE(ACCELERATED_COMPOSITING)
532