1/*
2 * Copyright (C) 2010 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#import "PlatformCAAnimation.h"
31
32#import "FloatConversion.h"
33#import "PlatformString.h"
34#import "TimingFunction.h"
35#import <QuartzCore/QuartzCore.h>
36#import <wtf/UnusedParam.h>
37
38#define HAVE_MODERN_QUARTZCORE (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD))
39
40using namespace WebCore;
41
42// This value must be the same as in PlatformCALayerMac.mm
43static NSString * const WKNonZeroBeginTimeFlag = @"WKPlatformCAAnimationNonZeroBeginTimeFlag";
44
45static bool hasNonZeroBeginTimeFlag(const PlatformCAAnimation* animation)
46{
47    return [[animation->platformAnimation() valueForKey:WKNonZeroBeginTimeFlag] boolValue];
48}
49
50static void setNonZeroBeginTimeFlag(PlatformCAAnimation* animation, bool value)
51{
52    [animation->platformAnimation() setValue:[NSNumber numberWithBool:value] forKey:WKNonZeroBeginTimeFlag];
53}
54
55static NSString* toCAFillModeType(PlatformCAAnimation::FillModeType type)
56{
57    switch (type) {
58    case PlatformCAAnimation::NoFillMode:
59    case PlatformCAAnimation::Forwards: return kCAFillModeForwards;
60    case PlatformCAAnimation::Backwards: return kCAFillModeBackwards;
61    case PlatformCAAnimation::Both: return kCAFillModeBoth;
62    }
63    return @"";
64}
65
66static PlatformCAAnimation::FillModeType fromCAFillModeType(NSString* string)
67{
68    if ([string isEqualToString:kCAFillModeBackwards])
69        return PlatformCAAnimation::Backwards;
70
71    if ([string isEqualToString:kCAFillModeBoth])
72        return PlatformCAAnimation::Both;
73
74    return PlatformCAAnimation::Forwards;
75}
76
77#if HAVE_MODERN_QUARTZCORE
78static NSString* toCAValueFunctionType(PlatformCAAnimation::ValueFunctionType type)
79{
80    switch (type) {
81    case PlatformCAAnimation::NoValueFunction: return @"";
82    case PlatformCAAnimation::RotateX: return kCAValueFunctionRotateX;
83    case PlatformCAAnimation::RotateY: return kCAValueFunctionRotateY;
84    case PlatformCAAnimation::RotateZ: return kCAValueFunctionRotateZ;
85    case PlatformCAAnimation::ScaleX: return kCAValueFunctionScaleX;
86    case PlatformCAAnimation::ScaleY: return kCAValueFunctionScaleY;
87    case PlatformCAAnimation::ScaleZ: return kCAValueFunctionScaleZ;
88    case PlatformCAAnimation::Scale: return kCAValueFunctionScale;
89    case PlatformCAAnimation::TranslateX: return kCAValueFunctionTranslateX;
90    case PlatformCAAnimation::TranslateY: return kCAValueFunctionTranslateY;
91    case PlatformCAAnimation::TranslateZ: return kCAValueFunctionTranslateZ;
92    case PlatformCAAnimation::Translate: return kCAValueFunctionTranslate;
93    }
94    return @"";
95}
96
97static PlatformCAAnimation::ValueFunctionType fromCAValueFunctionType(NSString* string)
98{
99    if ([string isEqualToString:kCAValueFunctionRotateX])
100        return PlatformCAAnimation::RotateX;
101
102    if ([string isEqualToString:kCAValueFunctionRotateY])
103        return PlatformCAAnimation::RotateY;
104
105    if ([string isEqualToString:kCAValueFunctionRotateZ])
106        return PlatformCAAnimation::RotateZ;
107
108    if ([string isEqualToString:kCAValueFunctionScaleX])
109        return PlatformCAAnimation::ScaleX;
110
111    if ([string isEqualToString:kCAValueFunctionScaleY])
112        return PlatformCAAnimation::ScaleY;
113
114    if ([string isEqualToString:kCAValueFunctionScaleZ])
115        return PlatformCAAnimation::ScaleZ;
116
117    if ([string isEqualToString:kCAValueFunctionScale])
118        return PlatformCAAnimation::Scale;
119
120    if ([string isEqualToString:kCAValueFunctionTranslateX])
121        return PlatformCAAnimation::TranslateX;
122
123    if ([string isEqualToString:kCAValueFunctionTranslateY])
124        return PlatformCAAnimation::TranslateY;
125
126    if ([string isEqualToString:kCAValueFunctionTranslateZ])
127        return PlatformCAAnimation::TranslateZ;
128
129    if ([string isEqualToString:kCAValueFunctionTranslate])
130        return PlatformCAAnimation::Translate;
131
132    return PlatformCAAnimation::NoValueFunction;
133}
134#endif
135
136static CAMediaTimingFunction* toCAMediaTimingFunction(const TimingFunction* timingFunction)
137{
138    ASSERT(timingFunction);
139    if (timingFunction->isCubicBezierTimingFunction()) {
140        const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
141        return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(ctf->x1()) :static_cast<float>(ctf->y1())
142                                                               :static_cast<float>(ctf->x2()) :static_cast<float>(ctf->y2())];
143    }
144
145    return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
146}
147
148PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(AnimationType type, const String& keyPath)
149{
150    return adoptRef(new PlatformCAAnimation(type, keyPath));
151}
152
153PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(PlatformAnimationRef animation)
154{
155    return adoptRef(new PlatformCAAnimation(animation));
156}
157
158PlatformCAAnimation::PlatformCAAnimation(AnimationType type, const String& keyPath)
159    : m_type(type)
160{
161    if (type == Basic)
162        m_animation.adoptNS([[CABasicAnimation animationWithKeyPath:keyPath] retain]);
163    else
164        m_animation.adoptNS([[CAKeyframeAnimation animationWithKeyPath:keyPath] retain]);
165}
166
167PlatformCAAnimation::PlatformCAAnimation(PlatformAnimationRef animation)
168{
169    if ([static_cast<CAAnimation*>(animation) isKindOfClass:[CABasicAnimation class]])
170        m_type = Basic;
171    else if ([static_cast<CAAnimation*>(animation) isKindOfClass:[CAKeyframeAnimation class]])
172        m_type = Keyframe;
173    else {
174        ASSERT(0);
175        return;
176    }
177
178    m_animation = static_cast<CAPropertyAnimation*>(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    animation->setValueFunction(valueFunction());
196
197    setNonZeroBeginTimeFlag(animation.get(), hasNonZeroBeginTimeFlag(this));
198
199    // Copy the specific Basic or Keyframe values
200    if (animationType() == Keyframe) {
201        animation->copyValuesFrom(this);
202        animation->copyKeyTimesFrom(this);
203        animation->copyTimingFunctionsFrom(this);
204    } else {
205        animation->copyFromValueFrom(this);
206        animation->copyToValueFrom(this);
207    }
208
209    return animation;
210}
211PlatformCAAnimation::~PlatformCAAnimation()
212{
213}
214
215bool PlatformCAAnimation::supportsValueFunction()
216{
217    static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)];
218    return sHaveValueFunction;
219}
220
221PlatformAnimationRef PlatformCAAnimation::platformAnimation() const
222{
223    return m_animation.get();
224}
225
226String PlatformCAAnimation::keyPath() const
227{
228    return [m_animation.get() keyPath];
229}
230
231CFTimeInterval PlatformCAAnimation::beginTime() const
232{
233    return [m_animation.get() beginTime];
234}
235
236void PlatformCAAnimation::setBeginTime(CFTimeInterval value)
237{
238    [m_animation.get() setBeginTime:value];
239
240    // Also set a flag to tell us if we've passed in a 0 value.
241    // The flag is needed because later beginTime will get changed
242    // to the time at which it fired and we need to know whether
243    // or not it was 0 to begin with.
244    if (value)
245        setNonZeroBeginTimeFlag(this, true);
246}
247
248CFTimeInterval PlatformCAAnimation::duration() const
249{
250    return [m_animation.get() duration];
251}
252
253void PlatformCAAnimation::setDuration(CFTimeInterval value)
254{
255    [m_animation.get() setDuration:value];
256}
257
258float PlatformCAAnimation::speed() const
259{
260    return [m_animation.get() speed];
261}
262
263void PlatformCAAnimation::setSpeed(float value)
264{
265    [m_animation.get() setSpeed:value];
266}
267
268CFTimeInterval PlatformCAAnimation::timeOffset() const
269{
270    return [m_animation.get() timeOffset];
271}
272
273void PlatformCAAnimation::setTimeOffset(CFTimeInterval value)
274{
275    [m_animation.get() setTimeOffset:value];
276}
277
278float PlatformCAAnimation::repeatCount() const
279{
280    return [m_animation.get() repeatCount];
281}
282
283void PlatformCAAnimation::setRepeatCount(float value)
284{
285    [m_animation.get() setRepeatCount:value];
286}
287
288bool PlatformCAAnimation::autoreverses() const
289{
290    return [m_animation.get() autoreverses];
291}
292
293void PlatformCAAnimation::setAutoreverses(bool value)
294{
295    [m_animation.get() setAutoreverses:value];
296}
297
298PlatformCAAnimation::FillModeType PlatformCAAnimation::fillMode() const
299{
300    return fromCAFillModeType([m_animation.get() fillMode]);
301}
302
303void PlatformCAAnimation::setFillMode(FillModeType value)
304{
305    [m_animation.get() setFillMode:toCAFillModeType(value)];
306}
307
308void PlatformCAAnimation::setTimingFunction(const TimingFunction* value)
309{
310    [m_animation.get() setTimingFunction:toCAMediaTimingFunction(value)];
311}
312
313void PlatformCAAnimation::copyTimingFunctionFrom(const PlatformCAAnimation* value)
314{
315    [m_animation.get() setTimingFunction:[value->m_animation.get() timingFunction]];
316}
317
318bool PlatformCAAnimation::isRemovedOnCompletion() const
319{
320    return [m_animation.get() isRemovedOnCompletion];
321}
322
323void PlatformCAAnimation::setRemovedOnCompletion(bool value)
324{
325    [m_animation.get() setRemovedOnCompletion:value];
326}
327
328bool PlatformCAAnimation::isAdditive() const
329{
330    return [m_animation.get() isAdditive];
331}
332
333void PlatformCAAnimation::setAdditive(bool value)
334{
335    [m_animation.get() setAdditive:value];
336}
337
338PlatformCAAnimation::ValueFunctionType PlatformCAAnimation::valueFunction() const
339{
340#if HAVE_MODERN_QUARTZCORE
341    CAValueFunction* vf = [m_animation.get() valueFunction];
342    return fromCAValueFunctionType([vf name]);
343#else
344    return NoValueFunction;
345#endif
346}
347
348void PlatformCAAnimation::setValueFunction(ValueFunctionType value)
349{
350#if HAVE_MODERN_QUARTZCORE
351    [m_animation.get() setValueFunction:[CAValueFunction functionWithName:toCAValueFunctionType(value)]];
352#else
353    UNUSED_PARAM(value);
354#endif
355}
356
357void PlatformCAAnimation::setFromValue(float value)
358{
359    if (animationType() != Basic)
360        return;
361    [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:[NSNumber numberWithDouble:value]];
362}
363
364void PlatformCAAnimation::setFromValue(const WebCore::TransformationMatrix& value)
365{
366    if (animationType() != Basic)
367        return;
368
369    [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:[NSValue valueWithCATransform3D:value]];
370}
371
372void PlatformCAAnimation::setFromValue(const FloatPoint3D& value)
373{
374    if (animationType() != Basic)
375        return;
376
377    NSArray* array = [NSArray arrayWithObjects:
378                        [NSNumber numberWithDouble:value.x()],
379                        [NSNumber numberWithDouble:value.y()],
380                        [NSNumber numberWithDouble:value.z()],
381                        nil];
382    [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:array];
383}
384
385void PlatformCAAnimation::setFromValue(const WebCore::Color& value)
386{
387    if (animationType() != Basic)
388        return;
389
390    NSArray* array = [NSArray arrayWithObjects:
391                        [NSNumber numberWithDouble:value.red()],
392                        [NSNumber numberWithDouble:value.green()],
393                        [NSNumber numberWithDouble:value.blue()],
394                        [NSNumber numberWithDouble:value.alpha()],
395                        nil];
396    [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:array];
397}
398
399void PlatformCAAnimation::copyFromValueFrom(const PlatformCAAnimation* value)
400{
401    if (animationType() != Basic || value->animationType() != Basic)
402        return;
403
404    CABasicAnimation* otherAnimation = static_cast<CABasicAnimation*>(value->m_animation.get());
405    [static_cast<CABasicAnimation*>(m_animation.get()) setFromValue:[otherAnimation fromValue]];
406}
407
408void PlatformCAAnimation::setToValue(float value)
409{
410    if (animationType() != Basic)
411        return;
412    [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:[NSNumber numberWithDouble:value]];
413}
414
415void PlatformCAAnimation::setToValue(const WebCore::TransformationMatrix& value)
416{
417    if (animationType() != Basic)
418        return;
419
420    [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:[NSValue valueWithCATransform3D:value]];
421}
422
423void PlatformCAAnimation::setToValue(const FloatPoint3D& value)
424{
425    if (animationType() != Basic)
426        return;
427
428    NSArray* array = [NSArray arrayWithObjects:
429                        [NSNumber numberWithDouble:value.x()],
430                        [NSNumber numberWithDouble:value.y()],
431                        [NSNumber numberWithDouble:value.z()],
432                        nil];
433    [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:array];
434}
435
436void PlatformCAAnimation::setToValue(const WebCore::Color& value)
437{
438    if (animationType() != Basic)
439        return;
440
441    NSArray* array = [NSArray arrayWithObjects:
442                        [NSNumber numberWithDouble:value.red()],
443                        [NSNumber numberWithDouble:value.green()],
444                        [NSNumber numberWithDouble:value.blue()],
445                        [NSNumber numberWithDouble:value.alpha()],
446                        nil];
447    [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:array];
448}
449
450void PlatformCAAnimation::copyToValueFrom(const PlatformCAAnimation* value)
451{
452    if (animationType() != Basic || value->animationType() != Basic)
453        return;
454
455    CABasicAnimation* otherAnimation = static_cast<CABasicAnimation*>(value->m_animation.get());
456    [static_cast<CABasicAnimation*>(m_animation.get()) setToValue:[otherAnimation toValue]];
457}
458
459
460// Keyframe-animation properties.
461void PlatformCAAnimation::setValues(const Vector<float>& value)
462{
463    if (animationType() != Keyframe)
464        return;
465
466    NSMutableArray* array = [NSMutableArray array];
467    for (size_t i = 0; i < value.size(); ++i)
468        [array addObject:[NSNumber numberWithDouble:value[i]]];
469    [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array];
470}
471
472void PlatformCAAnimation::setValues(const Vector<WebCore::TransformationMatrix>& value)
473{
474    if (animationType() != Keyframe)
475        return;
476
477    NSMutableArray* array = [NSMutableArray array];
478
479    for (size_t i = 0; i < value.size(); ++i)
480        [array addObject:[NSValue valueWithCATransform3D:value[i]]];
481
482    [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array];
483}
484
485void PlatformCAAnimation::setValues(const Vector<FloatPoint3D>& value)
486{
487    if (animationType() != Keyframe)
488        return;
489
490    NSMutableArray* array = [NSMutableArray array];
491
492    for (size_t i = 0; i < value.size(); ++i) {
493        NSArray* object = [NSArray arrayWithObjects:
494                        [NSNumber numberWithDouble:value[i].x()],
495                        [NSNumber numberWithDouble:value[i].y()],
496                        [NSNumber numberWithDouble:value[i].z()],
497                        nil];
498        [array addObject:object];
499    }
500    [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array];
501}
502
503void PlatformCAAnimation::setValues(const Vector<WebCore::Color>& value)
504{
505    if (animationType() != Keyframe)
506        return;
507
508    NSMutableArray* array = [NSMutableArray array];
509
510    for (size_t i = 0; i < value.size(); ++i) {
511        NSArray* object = [NSArray arrayWithObjects:
512                        [NSNumber numberWithDouble:value[i].red()],
513                        [NSNumber numberWithDouble:value[i].green()],
514                        [NSNumber numberWithDouble:value[i].blue()],
515                        [NSNumber numberWithDouble:value[i].alpha()],
516                        nil];
517        [array addObject:object];
518    }
519    [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:array];
520}
521
522void PlatformCAAnimation::copyValuesFrom(const PlatformCAAnimation* value)
523{
524    if (animationType() != Keyframe || value->animationType() != Keyframe)
525        return;
526
527    CAKeyframeAnimation* otherAnimation = static_cast<CAKeyframeAnimation*>(value->m_animation.get());
528    [static_cast<CAKeyframeAnimation*>(m_animation.get()) setValues:[otherAnimation values]];
529}
530
531void PlatformCAAnimation::setKeyTimes(const Vector<float>& value)
532{
533    NSMutableArray* array = [NSMutableArray array];
534
535    for (size_t i = 0; i < value.size(); ++i)
536        [array addObject:[NSNumber numberWithFloat:value[i]]];
537
538    [static_cast<CAKeyframeAnimation*>(m_animation.get()) setKeyTimes:array];
539}
540
541void PlatformCAAnimation::copyKeyTimesFrom(const PlatformCAAnimation* value)
542{
543    CAKeyframeAnimation* other = static_cast<CAKeyframeAnimation*>(value->m_animation.get());
544    [static_cast<CAKeyframeAnimation*>(m_animation.get()) setKeyTimes:[other keyTimes]];
545}
546
547void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value)
548{
549    NSMutableArray* array = [NSMutableArray array];
550
551    for (size_t i = 0; i < value.size(); ++i)
552        [array addObject:toCAMediaTimingFunction(value[i])];
553
554    [static_cast<CAKeyframeAnimation*>(m_animation.get()) setTimingFunctions:array];
555}
556
557void PlatformCAAnimation::copyTimingFunctionsFrom(const PlatformCAAnimation* value)
558{
559    CAKeyframeAnimation* other = static_cast<CAKeyframeAnimation*>(value->m_animation.get());
560    [static_cast<CAKeyframeAnimation*>(m_animation.get()) setTimingFunctions:[other timingFunctions]];
561}
562
563#endif // USE(ACCELERATED_COMPOSITING)
564