xref: /aosp_15_r20/frameworks/base/libs/hwui/Animator.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2014 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #include "Animator.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include <inttypes.h>
20*d57664e9SAndroid Build Coastguard Worker #include <set>
21*d57664e9SAndroid Build Coastguard Worker 
22*d57664e9SAndroid Build Coastguard Worker #include "AnimationContext.h"
23*d57664e9SAndroid Build Coastguard Worker #include "Interpolator.h"
24*d57664e9SAndroid Build Coastguard Worker #include "RenderNode.h"
25*d57664e9SAndroid Build Coastguard Worker #include "RenderProperties.h"
26*d57664e9SAndroid Build Coastguard Worker 
27*d57664e9SAndroid Build Coastguard Worker namespace android {
28*d57664e9SAndroid Build Coastguard Worker namespace uirenderer {
29*d57664e9SAndroid Build Coastguard Worker 
30*d57664e9SAndroid Build Coastguard Worker /************************************************************
31*d57664e9SAndroid Build Coastguard Worker  *  BaseRenderNodeAnimator
32*d57664e9SAndroid Build Coastguard Worker  ************************************************************/
33*d57664e9SAndroid Build Coastguard Worker 
BaseRenderNodeAnimator(float finalValue)34*d57664e9SAndroid Build Coastguard Worker BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
35*d57664e9SAndroid Build Coastguard Worker         : mTarget(nullptr)
36*d57664e9SAndroid Build Coastguard Worker         , mStagingTarget(nullptr)
37*d57664e9SAndroid Build Coastguard Worker         , mFinalValue(finalValue)
38*d57664e9SAndroid Build Coastguard Worker         , mDeltaValue(0)
39*d57664e9SAndroid Build Coastguard Worker         , mFromValue(0)
40*d57664e9SAndroid Build Coastguard Worker         , mStagingPlayState(PlayState::NotStarted)
41*d57664e9SAndroid Build Coastguard Worker         , mPlayState(PlayState::NotStarted)
42*d57664e9SAndroid Build Coastguard Worker         , mHasStartValue(false)
43*d57664e9SAndroid Build Coastguard Worker         , mStartTime(0)
44*d57664e9SAndroid Build Coastguard Worker         , mDuration(300)
45*d57664e9SAndroid Build Coastguard Worker         , mStartDelay(0)
46*d57664e9SAndroid Build Coastguard Worker         , mMayRunAsync(true)
47*d57664e9SAndroid Build Coastguard Worker         , mPlayTime(0) {}
48*d57664e9SAndroid Build Coastguard Worker 
~BaseRenderNodeAnimator()49*d57664e9SAndroid Build Coastguard Worker BaseRenderNodeAnimator::~BaseRenderNodeAnimator() {}
50*d57664e9SAndroid Build Coastguard Worker 
checkMutable()51*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::checkMutable() {
52*d57664e9SAndroid Build Coastguard Worker     // Should be impossible to hit as the Java-side also has guards for this
53*d57664e9SAndroid Build Coastguard Worker     LOG_ALWAYS_FATAL_IF(mStagingPlayState != PlayState::NotStarted,
54*d57664e9SAndroid Build Coastguard Worker                         "Animator has already been started!");
55*d57664e9SAndroid Build Coastguard Worker }
56*d57664e9SAndroid Build Coastguard Worker 
setInterpolator(Interpolator * interpolator)57*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::setInterpolator(Interpolator* interpolator) {
58*d57664e9SAndroid Build Coastguard Worker     checkMutable();
59*d57664e9SAndroid Build Coastguard Worker     mInterpolator.reset(interpolator);
60*d57664e9SAndroid Build Coastguard Worker }
61*d57664e9SAndroid Build Coastguard Worker 
setStartValue(float value)62*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::setStartValue(float value) {
63*d57664e9SAndroid Build Coastguard Worker     checkMutable();
64*d57664e9SAndroid Build Coastguard Worker     doSetStartValue(value);
65*d57664e9SAndroid Build Coastguard Worker }
66*d57664e9SAndroid Build Coastguard Worker 
doSetStartValue(float value)67*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::doSetStartValue(float value) {
68*d57664e9SAndroid Build Coastguard Worker     mFromValue = value;
69*d57664e9SAndroid Build Coastguard Worker     mDeltaValue = (mFinalValue - mFromValue);
70*d57664e9SAndroid Build Coastguard Worker     mHasStartValue = true;
71*d57664e9SAndroid Build Coastguard Worker }
72*d57664e9SAndroid Build Coastguard Worker 
setDuration(nsecs_t duration)73*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::setDuration(nsecs_t duration) {
74*d57664e9SAndroid Build Coastguard Worker     checkMutable();
75*d57664e9SAndroid Build Coastguard Worker     mDuration = duration;
76*d57664e9SAndroid Build Coastguard Worker }
77*d57664e9SAndroid Build Coastguard Worker 
setStartDelay(nsecs_t startDelay)78*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::setStartDelay(nsecs_t startDelay) {
79*d57664e9SAndroid Build Coastguard Worker     checkMutable();
80*d57664e9SAndroid Build Coastguard Worker     mStartDelay = startDelay;
81*d57664e9SAndroid Build Coastguard Worker }
82*d57664e9SAndroid Build Coastguard Worker 
attach(RenderNode * target)83*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::attach(RenderNode* target) {
84*d57664e9SAndroid Build Coastguard Worker     mStagingTarget = target;
85*d57664e9SAndroid Build Coastguard Worker     onAttached();
86*d57664e9SAndroid Build Coastguard Worker }
87*d57664e9SAndroid Build Coastguard Worker 
start()88*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::start() {
89*d57664e9SAndroid Build Coastguard Worker     mStagingPlayState = PlayState::Running;
90*d57664e9SAndroid Build Coastguard Worker     mStagingRequests.push_back(Request::Start);
91*d57664e9SAndroid Build Coastguard Worker     onStagingPlayStateChanged();
92*d57664e9SAndroid Build Coastguard Worker }
93*d57664e9SAndroid Build Coastguard Worker 
cancel()94*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::cancel() {
95*d57664e9SAndroid Build Coastguard Worker     mStagingPlayState = PlayState::Finished;
96*d57664e9SAndroid Build Coastguard Worker     mStagingRequests.push_back(Request::Cancel);
97*d57664e9SAndroid Build Coastguard Worker     onStagingPlayStateChanged();
98*d57664e9SAndroid Build Coastguard Worker }
99*d57664e9SAndroid Build Coastguard Worker 
reset()100*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::reset() {
101*d57664e9SAndroid Build Coastguard Worker     mStagingPlayState = PlayState::Finished;
102*d57664e9SAndroid Build Coastguard Worker     mStagingRequests.push_back(Request::Reset);
103*d57664e9SAndroid Build Coastguard Worker     onStagingPlayStateChanged();
104*d57664e9SAndroid Build Coastguard Worker }
105*d57664e9SAndroid Build Coastguard Worker 
reverse()106*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::reverse() {
107*d57664e9SAndroid Build Coastguard Worker     mStagingPlayState = PlayState::Reversing;
108*d57664e9SAndroid Build Coastguard Worker     mStagingRequests.push_back(Request::Reverse);
109*d57664e9SAndroid Build Coastguard Worker     onStagingPlayStateChanged();
110*d57664e9SAndroid Build Coastguard Worker }
111*d57664e9SAndroid Build Coastguard Worker 
end()112*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::end() {
113*d57664e9SAndroid Build Coastguard Worker     mStagingPlayState = PlayState::Finished;
114*d57664e9SAndroid Build Coastguard Worker     mStagingRequests.push_back(Request::End);
115*d57664e9SAndroid Build Coastguard Worker     onStagingPlayStateChanged();
116*d57664e9SAndroid Build Coastguard Worker }
117*d57664e9SAndroid Build Coastguard Worker 
resolveStagingRequest(Request request)118*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::resolveStagingRequest(Request request) {
119*d57664e9SAndroid Build Coastguard Worker     switch (request) {
120*d57664e9SAndroid Build Coastguard Worker         case Request::Start:
121*d57664e9SAndroid Build Coastguard Worker             mPlayTime = (mPlayState == PlayState::Running || mPlayState == PlayState::Reversing)
122*d57664e9SAndroid Build Coastguard Worker                                 ? mPlayTime
123*d57664e9SAndroid Build Coastguard Worker                                 : 0;
124*d57664e9SAndroid Build Coastguard Worker             mPlayState = PlayState::Running;
125*d57664e9SAndroid Build Coastguard Worker             mPendingActionUponFinish = Action::None;
126*d57664e9SAndroid Build Coastguard Worker             break;
127*d57664e9SAndroid Build Coastguard Worker         case Request::Reverse:
128*d57664e9SAndroid Build Coastguard Worker             mPlayTime = (mPlayState == PlayState::Running || mPlayState == PlayState::Reversing)
129*d57664e9SAndroid Build Coastguard Worker                                 ? mPlayTime
130*d57664e9SAndroid Build Coastguard Worker                                 : mDuration;
131*d57664e9SAndroid Build Coastguard Worker             mPlayState = PlayState::Reversing;
132*d57664e9SAndroid Build Coastguard Worker             mPendingActionUponFinish = Action::None;
133*d57664e9SAndroid Build Coastguard Worker             break;
134*d57664e9SAndroid Build Coastguard Worker         case Request::Reset:
135*d57664e9SAndroid Build Coastguard Worker             mPlayTime = 0;
136*d57664e9SAndroid Build Coastguard Worker             mPlayState = PlayState::Finished;
137*d57664e9SAndroid Build Coastguard Worker             mPendingActionUponFinish = Action::Reset;
138*d57664e9SAndroid Build Coastguard Worker             break;
139*d57664e9SAndroid Build Coastguard Worker         case Request::Cancel:
140*d57664e9SAndroid Build Coastguard Worker             mPlayState = PlayState::Finished;
141*d57664e9SAndroid Build Coastguard Worker             mPendingActionUponFinish = Action::None;
142*d57664e9SAndroid Build Coastguard Worker             break;
143*d57664e9SAndroid Build Coastguard Worker         case Request::End:
144*d57664e9SAndroid Build Coastguard Worker             mPlayTime = mPlayState == PlayState::Reversing ? 0 : mDuration;
145*d57664e9SAndroid Build Coastguard Worker             mPlayState = PlayState::Finished;
146*d57664e9SAndroid Build Coastguard Worker             mPendingActionUponFinish = Action::End;
147*d57664e9SAndroid Build Coastguard Worker             break;
148*d57664e9SAndroid Build Coastguard Worker         default:
149*d57664e9SAndroid Build Coastguard Worker             LOG_ALWAYS_FATAL("Invalid staging request: %d", static_cast<int>(request));
150*d57664e9SAndroid Build Coastguard Worker     };
151*d57664e9SAndroid Build Coastguard Worker }
152*d57664e9SAndroid Build Coastguard Worker 
pushStaging(AnimationContext & context)153*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) {
154*d57664e9SAndroid Build Coastguard Worker     if (mStagingTarget) {
155*d57664e9SAndroid Build Coastguard Worker         RenderNode* oldTarget = mTarget;
156*d57664e9SAndroid Build Coastguard Worker         mTarget = mStagingTarget;
157*d57664e9SAndroid Build Coastguard Worker         mStagingTarget = nullptr;
158*d57664e9SAndroid Build Coastguard Worker         if (oldTarget && oldTarget != mTarget) {
159*d57664e9SAndroid Build Coastguard Worker             oldTarget->onAnimatorTargetChanged(this);
160*d57664e9SAndroid Build Coastguard Worker         }
161*d57664e9SAndroid Build Coastguard Worker     }
162*d57664e9SAndroid Build Coastguard Worker 
163*d57664e9SAndroid Build Coastguard Worker     if (!mStagingRequests.empty()) {
164*d57664e9SAndroid Build Coastguard Worker         // No interpolator was set, use the default
165*d57664e9SAndroid Build Coastguard Worker         if (mPlayState == PlayState::NotStarted && !mInterpolator) {
166*d57664e9SAndroid Build Coastguard Worker             mInterpolator.reset(Interpolator::createDefaultInterpolator());
167*d57664e9SAndroid Build Coastguard Worker         }
168*d57664e9SAndroid Build Coastguard Worker         // Keep track of the play state and play time before they are changed when
169*d57664e9SAndroid Build Coastguard Worker         // staging requests are resolved.
170*d57664e9SAndroid Build Coastguard Worker         nsecs_t currentPlayTime = mPlayTime;
171*d57664e9SAndroid Build Coastguard Worker         PlayState prevFramePlayState = mPlayState;
172*d57664e9SAndroid Build Coastguard Worker 
173*d57664e9SAndroid Build Coastguard Worker         // Resolve staging requests one by one.
174*d57664e9SAndroid Build Coastguard Worker         for (Request request : mStagingRequests) {
175*d57664e9SAndroid Build Coastguard Worker             resolveStagingRequest(request);
176*d57664e9SAndroid Build Coastguard Worker         }
177*d57664e9SAndroid Build Coastguard Worker         mStagingRequests.clear();
178*d57664e9SAndroid Build Coastguard Worker 
179*d57664e9SAndroid Build Coastguard Worker         if (mStagingPlayState == PlayState::Finished) {
180*d57664e9SAndroid Build Coastguard Worker             callOnFinishedListener(context);
181*d57664e9SAndroid Build Coastguard Worker         } else if (mStagingPlayState == PlayState::Running ||
182*d57664e9SAndroid Build Coastguard Worker                    mStagingPlayState == PlayState::Reversing) {
183*d57664e9SAndroid Build Coastguard Worker             bool changed = currentPlayTime != mPlayTime || prevFramePlayState != mStagingPlayState;
184*d57664e9SAndroid Build Coastguard Worker             if (prevFramePlayState != mStagingPlayState) {
185*d57664e9SAndroid Build Coastguard Worker                 transitionToRunning(context);
186*d57664e9SAndroid Build Coastguard Worker             }
187*d57664e9SAndroid Build Coastguard Worker             if (changed) {
188*d57664e9SAndroid Build Coastguard Worker                 // Now we need to seek to the stagingPlayTime (i.e. the animation progress that was
189*d57664e9SAndroid Build Coastguard Worker                 // requested from UI thread). It is achieved by modifying mStartTime, such that
190*d57664e9SAndroid Build Coastguard Worker                 // current time - mStartTime = stagingPlayTime (or mDuration -stagingPlayTime in the
191*d57664e9SAndroid Build Coastguard Worker                 // case of reversing)
192*d57664e9SAndroid Build Coastguard Worker                 nsecs_t currentFrameTime = context.frameTimeMs();
193*d57664e9SAndroid Build Coastguard Worker                 if (mPlayState == PlayState::Reversing) {
194*d57664e9SAndroid Build Coastguard Worker                     // Reverse is not supported for animations with a start delay, so here we
195*d57664e9SAndroid Build Coastguard Worker                     // assume no start delay.
196*d57664e9SAndroid Build Coastguard Worker                     mStartTime = currentFrameTime - (mDuration - mPlayTime);
197*d57664e9SAndroid Build Coastguard Worker                 } else {
198*d57664e9SAndroid Build Coastguard Worker                     // Animation should play forward
199*d57664e9SAndroid Build Coastguard Worker                     if (mPlayTime == 0) {
200*d57664e9SAndroid Build Coastguard Worker                         // If the request is to start from the beginning, include start delay.
201*d57664e9SAndroid Build Coastguard Worker                         mStartTime = currentFrameTime + mStartDelay;
202*d57664e9SAndroid Build Coastguard Worker                     } else {
203*d57664e9SAndroid Build Coastguard Worker                         // If the request is to seek to a non-zero play time, then we skip start
204*d57664e9SAndroid Build Coastguard Worker                         // delay.
205*d57664e9SAndroid Build Coastguard Worker                         mStartTime = currentFrameTime - mPlayTime;
206*d57664e9SAndroid Build Coastguard Worker                     }
207*d57664e9SAndroid Build Coastguard Worker                 }
208*d57664e9SAndroid Build Coastguard Worker             }
209*d57664e9SAndroid Build Coastguard Worker         }
210*d57664e9SAndroid Build Coastguard Worker     }
211*d57664e9SAndroid Build Coastguard Worker     onPushStaging();
212*d57664e9SAndroid Build Coastguard Worker }
213*d57664e9SAndroid Build Coastguard Worker 
transitionToRunning(AnimationContext & context)214*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) {
215*d57664e9SAndroid Build Coastguard Worker     nsecs_t frameTimeMs = context.frameTimeMs();
216*d57664e9SAndroid Build Coastguard Worker     LOG_ALWAYS_FATAL_IF(frameTimeMs <= 0, "%" PRId64 " isn't a real frame time!", frameTimeMs);
217*d57664e9SAndroid Build Coastguard Worker     if (mStartDelay < 0 || mStartDelay > 50000) {
218*d57664e9SAndroid Build Coastguard Worker         ALOGW("Your start delay is strange and confusing: %" PRId64, mStartDelay);
219*d57664e9SAndroid Build Coastguard Worker     }
220*d57664e9SAndroid Build Coastguard Worker     mStartTime = frameTimeMs + mStartDelay;
221*d57664e9SAndroid Build Coastguard Worker     if (mStartTime < 0) {
222*d57664e9SAndroid Build Coastguard Worker         ALOGW("Ended up with a really weird start time of %" PRId64 " with frame time %" PRId64
223*d57664e9SAndroid Build Coastguard Worker               " and start delay %" PRId64,
224*d57664e9SAndroid Build Coastguard Worker               mStartTime, frameTimeMs, mStartDelay);
225*d57664e9SAndroid Build Coastguard Worker         // Set to 0 so that the animate() basically instantly finishes
226*d57664e9SAndroid Build Coastguard Worker         mStartTime = 0;
227*d57664e9SAndroid Build Coastguard Worker     }
228*d57664e9SAndroid Build Coastguard Worker     if (mDuration < 0) {
229*d57664e9SAndroid Build Coastguard Worker         ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
230*d57664e9SAndroid Build Coastguard Worker     }
231*d57664e9SAndroid Build Coastguard Worker }
232*d57664e9SAndroid Build Coastguard Worker 
animate(AnimationContext & context)233*d57664e9SAndroid Build Coastguard Worker bool BaseRenderNodeAnimator::animate(AnimationContext& context) {
234*d57664e9SAndroid Build Coastguard Worker     if (mPlayState < PlayState::Running) {
235*d57664e9SAndroid Build Coastguard Worker         return false;
236*d57664e9SAndroid Build Coastguard Worker     }
237*d57664e9SAndroid Build Coastguard Worker     if (mPlayState == PlayState::Finished) {
238*d57664e9SAndroid Build Coastguard Worker         if (mPendingActionUponFinish == Action::Reset) {
239*d57664e9SAndroid Build Coastguard Worker             // Skip to start.
240*d57664e9SAndroid Build Coastguard Worker             updatePlayTime(0);
241*d57664e9SAndroid Build Coastguard Worker         } else if (mPendingActionUponFinish == Action::End) {
242*d57664e9SAndroid Build Coastguard Worker             // Skip to end.
243*d57664e9SAndroid Build Coastguard Worker             updatePlayTime(mDuration);
244*d57664e9SAndroid Build Coastguard Worker         }
245*d57664e9SAndroid Build Coastguard Worker         // Reset pending action.
246*d57664e9SAndroid Build Coastguard Worker         mPendingActionUponFinish = Action::None;
247*d57664e9SAndroid Build Coastguard Worker         return true;
248*d57664e9SAndroid Build Coastguard Worker     }
249*d57664e9SAndroid Build Coastguard Worker 
250*d57664e9SAndroid Build Coastguard Worker     // This should be set before setValue() so animators can query this time when setValue
251*d57664e9SAndroid Build Coastguard Worker     // is called.
252*d57664e9SAndroid Build Coastguard Worker     nsecs_t currentPlayTime = context.frameTimeMs() - mStartTime;
253*d57664e9SAndroid Build Coastguard Worker     bool finished = updatePlayTime(currentPlayTime);
254*d57664e9SAndroid Build Coastguard Worker     if (finished && mPlayState != PlayState::Finished) {
255*d57664e9SAndroid Build Coastguard Worker         mPlayState = PlayState::Finished;
256*d57664e9SAndroid Build Coastguard Worker         callOnFinishedListener(context);
257*d57664e9SAndroid Build Coastguard Worker     }
258*d57664e9SAndroid Build Coastguard Worker     return finished;
259*d57664e9SAndroid Build Coastguard Worker }
260*d57664e9SAndroid Build Coastguard Worker 
updatePlayTime(nsecs_t playTime)261*d57664e9SAndroid Build Coastguard Worker bool BaseRenderNodeAnimator::updatePlayTime(nsecs_t playTime) {
262*d57664e9SAndroid Build Coastguard Worker     mPlayTime = mPlayState == PlayState::Reversing ? mDuration - playTime : playTime;
263*d57664e9SAndroid Build Coastguard Worker     onPlayTimeChanged(mPlayTime);
264*d57664e9SAndroid Build Coastguard Worker     // If BaseRenderNodeAnimator is handling the delay (not typical), then
265*d57664e9SAndroid Build Coastguard Worker     // because the staging properties reflect the final value, we always need
266*d57664e9SAndroid Build Coastguard Worker     // to call setValue even if the animation isn't yet running or is still
267*d57664e9SAndroid Build Coastguard Worker     // being delayed as we need to override the staging value
268*d57664e9SAndroid Build Coastguard Worker     if (playTime < 0) {
269*d57664e9SAndroid Build Coastguard Worker         return false;
270*d57664e9SAndroid Build Coastguard Worker     }
271*d57664e9SAndroid Build Coastguard Worker     if (!this->mHasStartValue) {
272*d57664e9SAndroid Build Coastguard Worker         doSetStartValue(getValue(mTarget));
273*d57664e9SAndroid Build Coastguard Worker     }
274*d57664e9SAndroid Build Coastguard Worker 
275*d57664e9SAndroid Build Coastguard Worker     float fraction = 1.0f;
276*d57664e9SAndroid Build Coastguard Worker     if ((mPlayState == PlayState::Running || mPlayState == PlayState::Reversing) && mDuration > 0) {
277*d57664e9SAndroid Build Coastguard Worker         fraction = mPlayTime / (float)mDuration;
278*d57664e9SAndroid Build Coastguard Worker     }
279*d57664e9SAndroid Build Coastguard Worker     fraction = MathUtils::clamp(fraction, 0.0f, 1.0f);
280*d57664e9SAndroid Build Coastguard Worker 
281*d57664e9SAndroid Build Coastguard Worker     fraction = mInterpolator->interpolate(fraction);
282*d57664e9SAndroid Build Coastguard Worker     setValue(mTarget, mFromValue + (mDeltaValue * fraction));
283*d57664e9SAndroid Build Coastguard Worker 
284*d57664e9SAndroid Build Coastguard Worker     return playTime >= mDuration;
285*d57664e9SAndroid Build Coastguard Worker }
286*d57664e9SAndroid Build Coastguard Worker 
getRemainingPlayTime()287*d57664e9SAndroid Build Coastguard Worker nsecs_t BaseRenderNodeAnimator::getRemainingPlayTime() {
288*d57664e9SAndroid Build Coastguard Worker     return mPlayState == PlayState::Reversing ? mPlayTime : mDuration - mPlayTime;
289*d57664e9SAndroid Build Coastguard Worker }
290*d57664e9SAndroid Build Coastguard Worker 
forceEndNow(AnimationContext & context)291*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::forceEndNow(AnimationContext& context) {
292*d57664e9SAndroid Build Coastguard Worker     if (mPlayState < PlayState::Finished) {
293*d57664e9SAndroid Build Coastguard Worker         mPlayState = PlayState::Finished;
294*d57664e9SAndroid Build Coastguard Worker         callOnFinishedListener(context);
295*d57664e9SAndroid Build Coastguard Worker     }
296*d57664e9SAndroid Build Coastguard Worker }
297*d57664e9SAndroid Build Coastguard Worker 
callOnFinishedListener(AnimationContext & context)298*d57664e9SAndroid Build Coastguard Worker void BaseRenderNodeAnimator::callOnFinishedListener(AnimationContext& context) {
299*d57664e9SAndroid Build Coastguard Worker     if (mListener.get()) {
300*d57664e9SAndroid Build Coastguard Worker         context.callOnFinished(this, mListener.get());
301*d57664e9SAndroid Build Coastguard Worker     }
302*d57664e9SAndroid Build Coastguard Worker }
303*d57664e9SAndroid Build Coastguard Worker 
304*d57664e9SAndroid Build Coastguard Worker /************************************************************
305*d57664e9SAndroid Build Coastguard Worker  *  RenderPropertyAnimator
306*d57664e9SAndroid Build Coastguard Worker  ************************************************************/
307*d57664e9SAndroid Build Coastguard Worker 
308*d57664e9SAndroid Build Coastguard Worker struct RenderPropertyAnimator::PropertyAccessors {
309*d57664e9SAndroid Build Coastguard Worker     RenderNode::DirtyPropertyMask dirtyMask;
310*d57664e9SAndroid Build Coastguard Worker     GetFloatProperty getter;
311*d57664e9SAndroid Build Coastguard Worker     SetFloatProperty setter;
312*d57664e9SAndroid Build Coastguard Worker };
313*d57664e9SAndroid Build Coastguard Worker 
314*d57664e9SAndroid Build Coastguard Worker // Maps RenderProperty enum to accessors
315*d57664e9SAndroid Build Coastguard Worker const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = {
316*d57664e9SAndroid Build Coastguard Worker         {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationX,
317*d57664e9SAndroid Build Coastguard Worker          &RenderProperties::setTranslationX},
318*d57664e9SAndroid Build Coastguard Worker         {RenderNode::TRANSLATION_Y, &RenderProperties::getTranslationY,
319*d57664e9SAndroid Build Coastguard Worker          &RenderProperties::setTranslationY},
320*d57664e9SAndroid Build Coastguard Worker         {RenderNode::TRANSLATION_Z, &RenderProperties::getTranslationZ,
321*d57664e9SAndroid Build Coastguard Worker          &RenderProperties::setTranslationZ},
322*d57664e9SAndroid Build Coastguard Worker         {RenderNode::SCALE_X, &RenderProperties::getScaleX, &RenderProperties::setScaleX},
323*d57664e9SAndroid Build Coastguard Worker         {RenderNode::SCALE_Y, &RenderProperties::getScaleY, &RenderProperties::setScaleY},
324*d57664e9SAndroid Build Coastguard Worker         {RenderNode::ROTATION, &RenderProperties::getRotation, &RenderProperties::setRotation},
325*d57664e9SAndroid Build Coastguard Worker         {RenderNode::ROTATION_X, &RenderProperties::getRotationX, &RenderProperties::setRotationX},
326*d57664e9SAndroid Build Coastguard Worker         {RenderNode::ROTATION_Y, &RenderProperties::getRotationY, &RenderProperties::setRotationY},
327*d57664e9SAndroid Build Coastguard Worker         {RenderNode::X, &RenderProperties::getX, &RenderProperties::setX},
328*d57664e9SAndroid Build Coastguard Worker         {RenderNode::Y, &RenderProperties::getY, &RenderProperties::setY},
329*d57664e9SAndroid Build Coastguard Worker         {RenderNode::Z, &RenderProperties::getZ, &RenderProperties::setZ},
330*d57664e9SAndroid Build Coastguard Worker         {RenderNode::ALPHA, &RenderProperties::getAlpha, &RenderProperties::setAlpha},
331*d57664e9SAndroid Build Coastguard Worker };
332*d57664e9SAndroid Build Coastguard Worker 
RenderPropertyAnimator(RenderProperty property,float finalValue)333*d57664e9SAndroid Build Coastguard Worker RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float finalValue)
334*d57664e9SAndroid Build Coastguard Worker         : BaseRenderNodeAnimator(finalValue), mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) {}
335*d57664e9SAndroid Build Coastguard Worker 
onAttached()336*d57664e9SAndroid Build Coastguard Worker void RenderPropertyAnimator::onAttached() {
337*d57664e9SAndroid Build Coastguard Worker     if (!mHasStartValue && mStagingTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
338*d57664e9SAndroid Build Coastguard Worker         setStartValue((mStagingTarget->stagingProperties().*mPropertyAccess->getter)());
339*d57664e9SAndroid Build Coastguard Worker     }
340*d57664e9SAndroid Build Coastguard Worker }
341*d57664e9SAndroid Build Coastguard Worker 
onStagingPlayStateChanged()342*d57664e9SAndroid Build Coastguard Worker void RenderPropertyAnimator::onStagingPlayStateChanged() {
343*d57664e9SAndroid Build Coastguard Worker     if (mStagingPlayState == PlayState::Running) {
344*d57664e9SAndroid Build Coastguard Worker         if (mStagingTarget) {
345*d57664e9SAndroid Build Coastguard Worker             (mStagingTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
346*d57664e9SAndroid Build Coastguard Worker         } else {
347*d57664e9SAndroid Build Coastguard Worker             // In the case of start delay where stagingTarget has been sync'ed over and null'ed
348*d57664e9SAndroid Build Coastguard Worker             // we delay the properties update to push staging.
349*d57664e9SAndroid Build Coastguard Worker             mShouldUpdateStagingProperties = true;
350*d57664e9SAndroid Build Coastguard Worker         }
351*d57664e9SAndroid Build Coastguard Worker     } else if (mStagingPlayState == PlayState::Finished) {
352*d57664e9SAndroid Build Coastguard Worker         // We're being canceled, so make sure that whatever values the UI thread
353*d57664e9SAndroid Build Coastguard Worker         // is observing for us is pushed over
354*d57664e9SAndroid Build Coastguard Worker         mShouldSyncPropertyFields = true;
355*d57664e9SAndroid Build Coastguard Worker     }
356*d57664e9SAndroid Build Coastguard Worker }
357*d57664e9SAndroid Build Coastguard Worker 
onPushStaging()358*d57664e9SAndroid Build Coastguard Worker void RenderPropertyAnimator::onPushStaging() {
359*d57664e9SAndroid Build Coastguard Worker     if (mShouldUpdateStagingProperties) {
360*d57664e9SAndroid Build Coastguard Worker         (mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
361*d57664e9SAndroid Build Coastguard Worker         mShouldUpdateStagingProperties = false;
362*d57664e9SAndroid Build Coastguard Worker     }
363*d57664e9SAndroid Build Coastguard Worker 
364*d57664e9SAndroid Build Coastguard Worker     if (mShouldSyncPropertyFields) {
365*d57664e9SAndroid Build Coastguard Worker         mTarget->setPropertyFieldsDirty(dirtyMask());
366*d57664e9SAndroid Build Coastguard Worker         mShouldSyncPropertyFields = false;
367*d57664e9SAndroid Build Coastguard Worker     }
368*d57664e9SAndroid Build Coastguard Worker }
369*d57664e9SAndroid Build Coastguard Worker 
dirtyMask()370*d57664e9SAndroid Build Coastguard Worker uint32_t RenderPropertyAnimator::dirtyMask() {
371*d57664e9SAndroid Build Coastguard Worker     return mPropertyAccess->dirtyMask;
372*d57664e9SAndroid Build Coastguard Worker }
373*d57664e9SAndroid Build Coastguard Worker 
getValue(RenderNode * target) const374*d57664e9SAndroid Build Coastguard Worker float RenderPropertyAnimator::getValue(RenderNode* target) const {
375*d57664e9SAndroid Build Coastguard Worker     return (target->properties().*mPropertyAccess->getter)();
376*d57664e9SAndroid Build Coastguard Worker }
377*d57664e9SAndroid Build Coastguard Worker 
setValue(RenderNode * target,float value)378*d57664e9SAndroid Build Coastguard Worker void RenderPropertyAnimator::setValue(RenderNode* target, float value) {
379*d57664e9SAndroid Build Coastguard Worker     (target->animatorProperties().*mPropertyAccess->setter)(value);
380*d57664e9SAndroid Build Coastguard Worker }
381*d57664e9SAndroid Build Coastguard Worker 
382*d57664e9SAndroid Build Coastguard Worker /************************************************************
383*d57664e9SAndroid Build Coastguard Worker  *  CanvasPropertyPrimitiveAnimator
384*d57664e9SAndroid Build Coastguard Worker  ************************************************************/
385*d57664e9SAndroid Build Coastguard Worker 
CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive * property,float finalValue)386*d57664e9SAndroid Build Coastguard Worker CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property,
387*d57664e9SAndroid Build Coastguard Worker                                                                  float finalValue)
388*d57664e9SAndroid Build Coastguard Worker         : BaseRenderNodeAnimator(finalValue), mProperty(property) {}
389*d57664e9SAndroid Build Coastguard Worker 
getValue(RenderNode * target) const390*d57664e9SAndroid Build Coastguard Worker float CanvasPropertyPrimitiveAnimator::getValue(RenderNode* target) const {
391*d57664e9SAndroid Build Coastguard Worker     return mProperty->value;
392*d57664e9SAndroid Build Coastguard Worker }
393*d57664e9SAndroid Build Coastguard Worker 
setValue(RenderNode * target,float value)394*d57664e9SAndroid Build Coastguard Worker void CanvasPropertyPrimitiveAnimator::setValue(RenderNode* target, float value) {
395*d57664e9SAndroid Build Coastguard Worker     mProperty->value = value;
396*d57664e9SAndroid Build Coastguard Worker }
397*d57664e9SAndroid Build Coastguard Worker 
dirtyMask()398*d57664e9SAndroid Build Coastguard Worker uint32_t CanvasPropertyPrimitiveAnimator::dirtyMask() {
399*d57664e9SAndroid Build Coastguard Worker     return RenderNode::DISPLAY_LIST;
400*d57664e9SAndroid Build Coastguard Worker }
401*d57664e9SAndroid Build Coastguard Worker 
402*d57664e9SAndroid Build Coastguard Worker /************************************************************
403*d57664e9SAndroid Build Coastguard Worker  *  CanvasPropertySkPaintAnimator
404*d57664e9SAndroid Build Coastguard Worker  ************************************************************/
405*d57664e9SAndroid Build Coastguard Worker 
CanvasPropertyPaintAnimator(CanvasPropertyPaint * property,PaintField field,float finalValue)406*d57664e9SAndroid Build Coastguard Worker CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator(CanvasPropertyPaint* property,
407*d57664e9SAndroid Build Coastguard Worker                                                          PaintField field, float finalValue)
408*d57664e9SAndroid Build Coastguard Worker         : BaseRenderNodeAnimator(finalValue), mProperty(property), mField(field) {}
409*d57664e9SAndroid Build Coastguard Worker 
getValue(RenderNode * target) const410*d57664e9SAndroid Build Coastguard Worker float CanvasPropertyPaintAnimator::getValue(RenderNode* target) const {
411*d57664e9SAndroid Build Coastguard Worker     switch (mField) {
412*d57664e9SAndroid Build Coastguard Worker         case STROKE_WIDTH:
413*d57664e9SAndroid Build Coastguard Worker             return mProperty->value.getStrokeWidth();
414*d57664e9SAndroid Build Coastguard Worker         case ALPHA:
415*d57664e9SAndroid Build Coastguard Worker             return mProperty->value.getAlpha();
416*d57664e9SAndroid Build Coastguard Worker     }
417*d57664e9SAndroid Build Coastguard Worker     LOG_ALWAYS_FATAL("Unknown field %d", (int)mField);
418*d57664e9SAndroid Build Coastguard Worker     return -1;
419*d57664e9SAndroid Build Coastguard Worker }
420*d57664e9SAndroid Build Coastguard Worker 
to_uint8(float value)421*d57664e9SAndroid Build Coastguard Worker static uint8_t to_uint8(float value) {
422*d57664e9SAndroid Build Coastguard Worker     int c = (int)(value + .5f);
423*d57664e9SAndroid Build Coastguard Worker     return static_cast<uint8_t>(c < 0 ? 0 : c > 255 ? 255 : c);
424*d57664e9SAndroid Build Coastguard Worker }
425*d57664e9SAndroid Build Coastguard Worker 
setValue(RenderNode * target,float value)426*d57664e9SAndroid Build Coastguard Worker void CanvasPropertyPaintAnimator::setValue(RenderNode* target, float value) {
427*d57664e9SAndroid Build Coastguard Worker     switch (mField) {
428*d57664e9SAndroid Build Coastguard Worker         case STROKE_WIDTH:
429*d57664e9SAndroid Build Coastguard Worker             mProperty->value.setStrokeWidth(value);
430*d57664e9SAndroid Build Coastguard Worker             return;
431*d57664e9SAndroid Build Coastguard Worker         case ALPHA:
432*d57664e9SAndroid Build Coastguard Worker             mProperty->value.setAlpha(to_uint8(value));
433*d57664e9SAndroid Build Coastguard Worker             return;
434*d57664e9SAndroid Build Coastguard Worker     }
435*d57664e9SAndroid Build Coastguard Worker     LOG_ALWAYS_FATAL("Unknown field %d", (int)mField);
436*d57664e9SAndroid Build Coastguard Worker }
437*d57664e9SAndroid Build Coastguard Worker 
dirtyMask()438*d57664e9SAndroid Build Coastguard Worker uint32_t CanvasPropertyPaintAnimator::dirtyMask() {
439*d57664e9SAndroid Build Coastguard Worker     return RenderNode::DISPLAY_LIST;
440*d57664e9SAndroid Build Coastguard Worker }
441*d57664e9SAndroid Build Coastguard Worker 
RevealAnimator(int centerX,int centerY,float startValue,float finalValue)442*d57664e9SAndroid Build Coastguard Worker RevealAnimator::RevealAnimator(int centerX, int centerY, float startValue, float finalValue)
443*d57664e9SAndroid Build Coastguard Worker         : BaseRenderNodeAnimator(finalValue), mCenterX(centerX), mCenterY(centerY) {
444*d57664e9SAndroid Build Coastguard Worker     setStartValue(startValue);
445*d57664e9SAndroid Build Coastguard Worker }
446*d57664e9SAndroid Build Coastguard Worker 
getValue(RenderNode * target) const447*d57664e9SAndroid Build Coastguard Worker float RevealAnimator::getValue(RenderNode* target) const {
448*d57664e9SAndroid Build Coastguard Worker     return target->properties().getRevealClip().getRadius();
449*d57664e9SAndroid Build Coastguard Worker }
450*d57664e9SAndroid Build Coastguard Worker 
setValue(RenderNode * target,float value)451*d57664e9SAndroid Build Coastguard Worker void RevealAnimator::setValue(RenderNode* target, float value) {
452*d57664e9SAndroid Build Coastguard Worker     target->animatorProperties().mutableRevealClip().set(true, mCenterX, mCenterY, value);
453*d57664e9SAndroid Build Coastguard Worker }
454*d57664e9SAndroid Build Coastguard Worker 
dirtyMask()455*d57664e9SAndroid Build Coastguard Worker uint32_t RevealAnimator::dirtyMask() {
456*d57664e9SAndroid Build Coastguard Worker     return RenderNode::GENERIC;
457*d57664e9SAndroid Build Coastguard Worker }
458*d57664e9SAndroid Build Coastguard Worker 
459*d57664e9SAndroid Build Coastguard Worker } /* namespace uirenderer */
460*d57664e9SAndroid Build Coastguard Worker } /* namespace android */
461