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