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 "TimingFunction.h"
34#include <QuartzCore/CACFAnimation.h>
35#include <QuartzCore/CACFTiming.h>
36#include <QuartzCore/CACFTimingFunction.h>
37#include <QuartzCore/CACFValueFunction.h>
38#include <QuartzCore/CACFVector.h>
39#include <wtf/text/WTFString.h>
40
41using namespace WebCore;
42
43static CFStringRef toCACFFillModeType(PlatformCAAnimation::FillModeType type)
44{
45    switch (type) {
46    case PlatformCAAnimation::NoFillMode:
47    case PlatformCAAnimation::Forwards: return kCACFFillModeForwards;
48    case PlatformCAAnimation::Backwards: return kCACFFillModeBackwards;
49    case PlatformCAAnimation::Both: return kCACFFillModeBoth;
50    }
51    ASSERT_NOT_REACHED();
52    return 0;
53}
54
55static PlatformCAAnimation::FillModeType fromCACFFillModeType(CFStringRef string)
56{
57    if (string == kCACFFillModeBackwards)
58        return PlatformCAAnimation::Backwards;
59
60    if (string == kCACFFillModeBoth)
61        return PlatformCAAnimation::Both;
62
63    return PlatformCAAnimation::Forwards;
64}
65
66static CFStringRef toCACFValueFunctionType(PlatformCAAnimation::ValueFunctionType type)
67{
68    switch (type) {
69    case PlatformCAAnimation::NoValueFunction: return 0;
70    case PlatformCAAnimation::RotateX: return kCACFValueFunctionRotateX;
71    case PlatformCAAnimation::RotateY: return kCACFValueFunctionRotateY;
72    case PlatformCAAnimation::RotateZ: return kCACFValueFunctionRotateZ;
73    case PlatformCAAnimation::ScaleX: return kCACFValueFunctionScaleX;
74    case PlatformCAAnimation::ScaleY: return kCACFValueFunctionScaleY;
75    case PlatformCAAnimation::ScaleZ: return kCACFValueFunctionScaleZ;
76    case PlatformCAAnimation::Scale: return kCACFValueFunctionScale;
77    case PlatformCAAnimation::TranslateX: return kCACFValueFunctionTranslateX;
78    case PlatformCAAnimation::TranslateY: return kCACFValueFunctionTranslateY;
79    case PlatformCAAnimation::TranslateZ: return kCACFValueFunctionTranslateZ;
80    case PlatformCAAnimation::Translate: return kCACFValueFunctionTranslate;
81    }
82    ASSERT_NOT_REACHED();
83    return 0;
84}
85
86static PlatformCAAnimation::ValueFunctionType fromCACFValueFunctionType(CFStringRef string)
87{
88    if (string == kCACFValueFunctionRotateX)
89        return PlatformCAAnimation::RotateX;
90
91    if (string == kCACFValueFunctionRotateY)
92        return PlatformCAAnimation::RotateY;
93
94    if (string == kCACFValueFunctionRotateZ)
95        return PlatformCAAnimation::RotateZ;
96
97    if (string == kCACFValueFunctionScaleX)
98        return PlatformCAAnimation::ScaleX;
99
100    if (string == kCACFValueFunctionScaleY)
101        return PlatformCAAnimation::ScaleY;
102
103    if (string == kCACFValueFunctionScaleZ)
104        return PlatformCAAnimation::ScaleZ;
105
106    if (string == kCACFValueFunctionScale)
107        return PlatformCAAnimation::Scale;
108
109    if (string == kCACFValueFunctionTranslateX)
110        return PlatformCAAnimation::TranslateX;
111
112    if (string == kCACFValueFunctionTranslateY)
113        return PlatformCAAnimation::TranslateY;
114
115    if (string == kCACFValueFunctionTranslateZ)
116        return PlatformCAAnimation::TranslateZ;
117
118    if (string == kCACFValueFunctionTranslate)
119        return PlatformCAAnimation::Translate;
120
121    return PlatformCAAnimation::NoValueFunction;
122}
123
124static RetainPtr<CACFTimingFunctionRef> toCACFTimingFunction(const TimingFunction* timingFunction, bool reverse)
125{
126    ASSERT(timingFunction);
127    if (timingFunction->isCubicBezierTimingFunction()) {
128        RefPtr<CubicBezierTimingFunction> reversed;
129        const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
130
131        if (reverse) {
132            reversed = ctf->createReversed();
133            ctf = reversed.get();
134        }
135
136        float x1 = static_cast<float>(ctf->x1());
137        float y1 = static_cast<float>(ctf->y1());
138        float x2 = static_cast<float>(ctf->x2());
139        float y2 = static_cast<float>(ctf->y2());
140        return adoptCF(CACFTimingFunctionCreate(x1, y1, x2, y2));
141    }
142
143    return CACFTimingFunctionGetFunctionWithName(kCACFTimingFunctionLinear);
144}
145
146PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(AnimationType type, const String& keyPath)
147{
148    return adoptRef(new PlatformCAAnimation(type, keyPath));
149}
150
151PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(PlatformAnimationRef animation)
152{
153    return adoptRef(new PlatformCAAnimation(animation));
154}
155
156PlatformCAAnimation::PlatformCAAnimation(AnimationType type, const String& keyPath)
157    : m_type(type)
158{
159    if (type == Basic)
160        m_animation = adoptCF(CACFAnimationCreate(kCACFBasicAnimation));
161    else
162        m_animation = adoptCF(CACFAnimationCreate(kCACFKeyframeAnimation));
163
164    CACFAnimationSetKeyPath(m_animation.get(), keyPath.createCFString().get());
165}
166
167PlatformCAAnimation::PlatformCAAnimation(PlatformAnimationRef animation)
168{
169    if (CACFAnimationGetClass(animation) == kCACFBasicAnimation)
170        m_type = Basic;
171    else if (CACFAnimationGetClass(animation) == kCACFKeyframeAnimation)
172        m_type = Keyframe;
173    else {
174        ASSERT_NOT_REACHED();
175        return;
176    }
177
178    m_animation = animation;
179}
180
181PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::copy() const
182{
183    RefPtr<PlatformCAAnimation> animation = create(animationType(), keyPath());
184
185    animation->setBeginTime(beginTime());
186    animation->setDuration(duration());
187    animation->setSpeed(speed());
188    animation->setTimeOffset(timeOffset());
189    animation->setRepeatCount(repeatCount());
190    animation->setAutoreverses(autoreverses());
191    animation->setFillMode(fillMode());
192    animation->setRemovedOnCompletion(isRemovedOnCompletion());
193    animation->setAdditive(isAdditive());
194    animation->copyTimingFunctionFrom(this);
195    if (valueFunction())
196        animation->setValueFunction(valueFunction());
197
198    // Copy the specific Basic or Keyframe values
199    if (animationType() == Keyframe) {
200        animation->copyValuesFrom(this);
201        animation->copyKeyTimesFrom(this);
202        animation->copyTimingFunctionsFrom(this);
203    } else {
204        animation->copyFromValueFrom(this);
205        animation->copyToValueFrom(this);
206    }
207
208    return animation;
209}
210
211PlatformCAAnimation::~PlatformCAAnimation()
212{
213}
214
215bool PlatformCAAnimation::supportsValueFunction()
216{
217    return true;
218}
219
220PlatformAnimationRef PlatformCAAnimation::platformAnimation() const
221{
222    return m_animation.get();
223}
224
225String PlatformCAAnimation::keyPath() const
226{
227    return CACFAnimationGetKeyPath(m_animation.get());
228}
229
230CFTimeInterval PlatformCAAnimation::beginTime() const
231{
232    return CACFAnimationGetBeginTime(m_animation.get());
233}
234
235void PlatformCAAnimation::setBeginTime(CFTimeInterval value)
236{
237    CACFAnimationSetBeginTime(m_animation.get(), value);
238}
239
240CFTimeInterval PlatformCAAnimation::duration() const
241{
242    return CACFAnimationGetDuration(m_animation.get());
243}
244
245void PlatformCAAnimation::setDuration(CFTimeInterval value)
246{
247    CACFAnimationSetDuration(m_animation.get(), value);
248}
249
250float PlatformCAAnimation::speed() const
251{
252    return CACFAnimationGetSpeed(m_animation.get());
253}
254
255void PlatformCAAnimation::setSpeed(float value)
256{
257    CACFAnimationSetSpeed(m_animation.get(), value);
258}
259
260CFTimeInterval PlatformCAAnimation::timeOffset() const
261{
262    return CACFAnimationGetTimeOffset(m_animation.get());
263}
264
265void PlatformCAAnimation::setTimeOffset(CFTimeInterval value)
266{
267    CACFAnimationSetTimeOffset(m_animation.get(), value);
268}
269
270float PlatformCAAnimation::repeatCount() const
271{
272    return CACFAnimationGetRepeatCount(m_animation.get());
273}
274
275void PlatformCAAnimation::setRepeatCount(float value)
276{
277    CACFAnimationSetRepeatCount(m_animation.get(), value);
278}
279
280bool PlatformCAAnimation::autoreverses() const
281{
282    return CACFAnimationGetAutoreverses(m_animation.get());
283}
284
285void PlatformCAAnimation::setAutoreverses(bool value)
286{
287    CACFAnimationSetAutoreverses(m_animation.get(), value);
288}
289
290PlatformCAAnimation::FillModeType PlatformCAAnimation::fillMode() const
291{
292    return fromCACFFillModeType(CACFAnimationGetFillMode(m_animation.get()));
293}
294
295void PlatformCAAnimation::setFillMode(FillModeType value)
296{
297    CACFAnimationSetFillMode(m_animation.get(), toCACFFillModeType(value));
298}
299
300void PlatformCAAnimation::setTimingFunction(const TimingFunction* value, bool reverse)
301{
302    UNUSED_PARAM(reverse);
303    CACFAnimationSetTimingFunction(m_animation.get(), toCACFTimingFunction(value, reverse).get());
304}
305
306void PlatformCAAnimation::copyTimingFunctionFrom(const PlatformCAAnimation* value)
307{
308    CACFTimingFunctionRef timingFunc = CACFAnimationGetTimingFunction(value->m_animation.get());
309    if (timingFunc)
310        CACFAnimationSetTimingFunction(m_animation.get(), timingFunc);
311}
312
313bool PlatformCAAnimation::isRemovedOnCompletion() const
314{
315    return CACFAnimationIsRemovedOnCompletion(m_animation.get());
316}
317
318void PlatformCAAnimation::setRemovedOnCompletion(bool value)
319{
320    CACFAnimationSetRemovedOnCompletion(m_animation.get(), value);
321}
322
323bool PlatformCAAnimation::isAdditive() const
324{
325    return CACFAnimationIsAdditive(m_animation.get());
326}
327
328void PlatformCAAnimation::setAdditive(bool value)
329{
330    CACFAnimationSetAdditive(m_animation.get(), value);
331}
332
333PlatformCAAnimation::ValueFunctionType PlatformCAAnimation::valueFunction() const
334{
335    CACFValueFunctionRef func = CACFAnimationGetValueFunction(m_animation.get());
336    return func ? fromCACFValueFunctionType(CACFValueFunctionGetName(func)) : NoValueFunction;
337}
338
339void PlatformCAAnimation::setValueFunction(ValueFunctionType value)
340{
341    CFStringRef valueString = toCACFValueFunctionType(value);
342    CACFAnimationSetValueFunction(m_animation.get(), valueString ? CACFValueFunctionGetFunctionWithName(valueString) : 0);
343}
344
345void PlatformCAAnimation::setFromValue(float value)
346{
347    if (animationType() != Basic)
348        return;
349
350    RetainPtr<CFNumberRef> v = adoptCF(CFNumberCreate(0, kCFNumberFloatType, &value));
351    CACFAnimationSetFromValue(m_animation.get(), v.get());
352}
353
354void PlatformCAAnimation::setFromValue(const WebCore::TransformationMatrix& value)
355{
356    if (animationType() != Basic)
357        return;
358
359    RetainPtr<CACFVectorRef> v = adoptCF(CACFVectorCreateTransform(value));
360    CACFAnimationSetFromValue(m_animation.get(), v.get());
361}
362
363void PlatformCAAnimation::setFromValue(const FloatPoint3D& value)
364{
365    if (animationType() != Basic)
366        return;
367
368    CGFloat a[3] = { value.x(), value.y(), value.z() };
369    RetainPtr<CACFVectorRef> v = adoptCF(CACFVectorCreate(3, a));
370    CACFAnimationSetFromValue(m_animation.get(), v.get());
371}
372
373void PlatformCAAnimation::setFromValue(const WebCore::Color& value)
374{
375    if (animationType() != Basic)
376        return;
377
378    CGFloat a[4] = { value.red(), value.green(), value.blue(), value.alpha() };
379    RetainPtr<CACFVectorRef> v = adoptCF(CACFVectorCreate(4, a));
380    CACFAnimationSetFromValue(m_animation.get(), v.get());
381}
382
383#if ENABLE(CSS_FILTERS)
384void PlatformCAAnimation::setFromValue(const FilterOperation*, int)
385{
386    // FIXME: Hardware filter animation not implemented on Windows
387}
388#endif
389
390void PlatformCAAnimation::copyFromValueFrom(const PlatformCAAnimation* value)
391{
392    if (animationType() != Basic || value->animationType() != Basic)
393        return;
394
395    CACFAnimationSetFromValue(m_animation.get(), CACFAnimationGetFromValue(value->platformAnimation()));
396}
397
398void PlatformCAAnimation::setToValue(float value)
399{
400    if (animationType() != Basic)
401        return;
402
403    RetainPtr<CFNumberRef> v = adoptCF(CFNumberCreate(0, kCFNumberFloatType, &value));
404    CACFAnimationSetToValue(m_animation.get(), v.get());
405}
406
407void PlatformCAAnimation::setToValue(const WebCore::TransformationMatrix& value)
408{
409    if (animationType() != Basic)
410        return;
411
412    RetainPtr<CACFVectorRef> v = adoptCF(CACFVectorCreateTransform(value));
413    CACFAnimationSetToValue(m_animation.get(), v.get());
414}
415
416void PlatformCAAnimation::setToValue(const FloatPoint3D& value)
417{
418    if (animationType() != Basic)
419        return;
420
421    CGFloat a[3] = { value.x(), value.y(), value.z() };
422    RetainPtr<CACFVectorRef> v = adoptCF(CACFVectorCreate(3, a));
423    CACFAnimationSetToValue(m_animation.get(), v.get());
424}
425
426void PlatformCAAnimation::setToValue(const WebCore::Color& value)
427{
428    if (animationType() != Basic)
429        return;
430
431    CGFloat a[4] = { value.red(), value.green(), value.blue(), value.alpha() };
432    RetainPtr<CACFVectorRef> v = adoptCF(CACFVectorCreate(4, a));
433    CACFAnimationSetToValue(m_animation.get(), v.get());
434}
435
436#if ENABLE(CSS_FILTERS)
437void PlatformCAAnimation::setToValue(const FilterOperation*, int)
438{
439    // FIXME: Hardware filter animation not implemented on Windows
440}
441#endif
442
443void PlatformCAAnimation::copyToValueFrom(const PlatformCAAnimation* value)
444{
445    if (animationType() != Basic || value->animationType() != Basic)
446        return;
447
448    CACFAnimationSetToValue(m_animation.get(), CACFAnimationGetToValue(value->platformAnimation()));
449}
450
451
452// Keyframe-animation properties.
453void PlatformCAAnimation::setValues(const Vector<float>& value)
454{
455    if (animationType() != Keyframe)
456        return;
457
458    RetainPtr<CFMutableArrayRef> array = adoptCF(CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
459    for (size_t i = 0; i < value.size(); ++i) {
460        RetainPtr<CFNumberRef> v = adoptCF(CFNumberCreate(0, kCFNumberFloatType, &value[i]));
461        CFArrayAppendValue(array.get(), v.get());
462    }
463
464    CACFAnimationSetValues(m_animation.get(), array.get());
465}
466
467void PlatformCAAnimation::setValues(const Vector<WebCore::TransformationMatrix>& 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        RetainPtr<CACFVectorRef> v = adoptCF(CACFVectorCreateTransform(value[i]));
475        CFArrayAppendValue(array.get(), v.get());
476    }
477
478    CACFAnimationSetValues(m_animation.get(), array.get());
479}
480
481void PlatformCAAnimation::setValues(const Vector<FloatPoint3D>& value)
482{
483    if (animationType() != Keyframe)
484        return;
485
486    RetainPtr<CFMutableArrayRef> array = adoptCF(CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
487    for (size_t i = 0; i < value.size(); ++i) {
488        CGFloat a[3] = { value[i].x(), value[i].y(), value[i].z() };
489        RetainPtr<CACFVectorRef> v = adoptCF(CACFVectorCreate(3, a));
490        CFArrayAppendValue(array.get(), v.get());
491    }
492
493    CACFAnimationSetValues(m_animation.get(), array.get());
494}
495
496void PlatformCAAnimation::setValues(const Vector<WebCore::Color>& value)
497{
498    if (animationType() != Keyframe)
499        return;
500
501    RetainPtr<CFMutableArrayRef> array = adoptCF(CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
502    for (size_t i = 0; i < value.size(); ++i) {
503        CGFloat a[4] = { value[i].red(), value[i].green(), value[i].blue(), value[i].alpha() };
504        RetainPtr<CACFVectorRef> v = adoptCF(CACFVectorCreate(4, a));
505        CFArrayAppendValue(array.get(), v.get());
506    }
507
508    CACFAnimationSetValues(m_animation.get(), array.get());
509}
510
511#if ENABLE(CSS_FILTERS)
512void PlatformCAAnimation::setValues(const Vector<RefPtr<FilterOperation> >&, int)
513{
514    // FIXME: Hardware filter animation not implemented on Windows
515}
516#endif
517
518void PlatformCAAnimation::copyValuesFrom(const PlatformCAAnimation* value)
519{
520    if (animationType() != Keyframe || value->animationType() != Keyframe)
521        return;
522
523    CACFAnimationSetValues(m_animation.get(), CACFAnimationGetValues(value->platformAnimation()));
524}
525
526void PlatformCAAnimation::setKeyTimes(const Vector<float>& value)
527{
528    if (animationType() != Keyframe)
529        return;
530
531    RetainPtr<CFMutableArrayRef> array = adoptCF(CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
532    for (size_t i = 0; i < value.size(); ++i) {
533        RetainPtr<CFNumberRef> v = adoptCF(CFNumberCreate(0, kCFNumberFloatType, &value[i]));
534        CFArrayAppendValue(array.get(), v.get());
535    }
536
537    CACFAnimationSetKeyTimes(m_animation.get(), array.get());
538}
539
540void PlatformCAAnimation::copyKeyTimesFrom(const PlatformCAAnimation* value)
541{
542    if (animationType() != Keyframe)
543        return;
544
545    CACFAnimationSetKeyTimes(m_animation.get(), CACFAnimationGetKeyTimes(value->platformAnimation()));
546}
547
548void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value, bool reverse)
549{
550    UNUSED_PARAM(reverse);
551    if (animationType() != Keyframe)
552        return;
553
554    RetainPtr<CFMutableArrayRef> array = adoptCF(CFArrayCreateMutable(0, value.size(), &kCFTypeArrayCallBacks));
555    for (size_t i = 0; i < value.size(); ++i) {
556        RetainPtr<CFNumberRef> v = adoptCF(CFNumberCreate(0, kCFNumberFloatType, &value[i]));
557        CFArrayAppendValue(array.get(), toCACFTimingFunction(value[i], reverse).get());
558    }
559
560    CACFAnimationSetTimingFunctions(m_animation.get(), array.get());
561}
562
563void PlatformCAAnimation::copyTimingFunctionsFrom(const PlatformCAAnimation* value)
564{
565    CACFAnimationSetTimingFunctions(m_animation.get(), CACFAnimationGetTimingFunctions(value->platformAnimation()));
566}
567
568#endif // USE(ACCELERATED_COMPOSITING)
569