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