/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Animator.h"
#include <inttypes.h>
#include <set>
#include "AnimationContext.h"
#include "Interpolator.h"
#include "RenderNode.h"
#include "RenderProperties.h"
namespace android {
namespace uirenderer {
/************************************************************
* BaseRenderNodeAnimator
************************************************************/
BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue)
: mTarget(nullptr)
, mFinalValue(finalValue)
, mDeltaValue(0)
, mFromValue(0)
, mStagingPlayState(NOT_STARTED)
, mPlayState(NOT_STARTED)
, mHasStartValue(false)
, mStartTime(0)
, mDuration(300)
, mStartDelay(0)
, mMayRunAsync(true) {
}
BaseRenderNodeAnimator::~BaseRenderNodeAnimator() {
}
void BaseRenderNodeAnimator::checkMutable() {
// Should be impossible to hit as the Java-side also has guards for this
LOG_ALWAYS_FATAL_IF(mStagingPlayState != NOT_STARTED,
"Animator has already been started!");
}
void BaseRenderNodeAnimator::setInterpolator(Interpolator* interpolator) {
checkMutable();
mInterpolator.reset(interpolator);
}
void BaseRenderNodeAnimator::setStartValue(float value) {
checkMutable();
doSetStartValue(value);
}
void BaseRenderNodeAnimator::doSetStartValue(float value) {
mFromValue = value;
mDeltaValue = (mFinalValue - mFromValue);
mHasStartValue = true;
}
void BaseRenderNodeAnimator::setDuration(nsecs_t duration) {
checkMutable();
mDuration = duration;
}
void BaseRenderNodeAnimator::setStartDelay(nsecs_t startDelay) {
checkMutable();
mStartDelay = startDelay;
}
void BaseRenderNodeAnimator::attach(RenderNode* target) {
mTarget = target;
onAttached();
}
void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) {
if (!mHasStartValue) {
doSetStartValue(getValue(mTarget));
}
if (mStagingPlayState > mPlayState) {
mPlayState = mStagingPlayState;
// Oh boy, we're starting! Man the battle stations!
if (mPlayState == RUNNING) {
transitionToRunning(context);
} else if (mPlayState == FINISHED) {
callOnFinishedListener(context);
}
}
}
void BaseRenderNodeAnimator::transitionToRunning(AnimationContext& context) {
nsecs_t frameTimeMs = context.frameTimeMs();
LOG_ALWAYS_FATAL_IF(frameTimeMs <= 0, "%" PRId64 " isn't a real frame time!", frameTimeMs);
if (mStartDelay < 0 || mStartDelay > 50000) {
ALOGW("Your start delay is strange and confusing: %" PRId64, mStartDelay);
}
mStartTime = frameTimeMs + mStartDelay;
if (mStartTime < 0) {
ALOGW("Ended up with a really weird start time of %" PRId64
" with frame time %" PRId64 " and start delay %" PRId64,
mStartTime, frameTimeMs, mStartDelay);
// Set to 0 so that the animate() basically instantly finishes
mStartTime = 0;
}
// No interpolator was set, use the default
if (!mInterpolator) {
mInterpolator.reset(Interpolator::createDefaultInterpolator());
}
if (mDuration < 0 || mDuration > 50000) {
ALOGW("Your duration is strange and confusing: %" PRId64, mDuration);
}
}
bool BaseRenderNodeAnimator::animate(AnimationContext& context) {
if (mPlayState < RUNNING) {
return false;
}
if (mPlayState == FINISHED) {
return true;
}
// If BaseRenderNodeAnimator is handling the delay (not typical), then
// because the staging properties reflect the final value, we always need
// to call setValue even if the animation isn't yet running or is still
// being delayed as we need to override the staging value
if (mStartTime > context.frameTimeMs()) {
setValue(mTarget, mFromValue);
return false;
}
float fraction = 1.0f;
if (mPlayState == RUNNING && mDuration > 0) {
fraction = (float)(context.frameTimeMs() - mStartTime) / mDuration;
}
if (fraction >= 1.0f) {
fraction = 1.0f;
mPlayState = FINISHED;
}
fraction = mInterpolator->interpolate(fraction);
setValue(mTarget, mFromValue + (mDeltaValue * fraction));
if (mPlayState == FINISHED) {
callOnFinishedListener(context);
return true;
}
return false;
}
void BaseRenderNodeAnimator::forceEndNow(AnimationContext& context) {
if (mPlayState < FINISHED) {
mPlayState = FINISHED;
callOnFinishedListener(context);
}
}
void BaseRenderNodeAnimator::callOnFinishedListener(AnimationContext& context) {
if (mListener.get()) {
context.callOnFinished(this, mListener.get());
}
}
/************************************************************
* RenderPropertyAnimator
************************************************************/
struct RenderPropertyAnimator::PropertyAccessors {
RenderNode::DirtyPropertyMask dirtyMask;
GetFloatProperty getter;
SetFloatProperty setter;
};
// Maps RenderProperty enum to accessors
const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::PROPERTY_ACCESSOR_LUT[] = {
{RenderNode::TRANSLATION_X, &RenderProperties::getTranslationX, &RenderProperties::setTranslationX },
{RenderNode::TRANSLATION_Y, &RenderProperties::getTranslationY, &RenderProperties::setTranslationY },
{RenderNode::TRANSLATION_X, &RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ },
{RenderNode::SCALE_X, &RenderProperties::getScaleX, &RenderProperties::setScaleX },
{RenderNode::SCALE_Y, &RenderProperties::getScaleY, &RenderProperties::setScaleY },
{RenderNode::ROTATION, &RenderProperties::getRotation, &RenderProperties::setRotation },
{RenderNode::ROTATION_X, &RenderProperties::getRotationX, &RenderProperties::setRotationX },
{RenderNode::ROTATION_Y, &RenderProperties::getRotationY, &RenderProperties::setRotationY },
{RenderNode::X, &RenderProperties::getX, &RenderProperties::setX },
{RenderNode::Y, &RenderProperties::getY, &RenderProperties::setY },
{RenderNode::Z, &RenderProperties::getZ, &RenderProperties::setZ },
{RenderNode::ALPHA, &RenderProperties::getAlpha, &RenderProperties::setAlpha },
};
RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float finalValue)
: BaseRenderNodeAnimator(finalValue)
, mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) {
}
void RenderPropertyAnimator::onAttached() {
if (!mHasStartValue
&& mTarget->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) {
setStartValue((mTarget->stagingProperties().*mPropertyAccess->getter)());
}
}
void RenderPropertyAnimator::onStagingPlayStateChanged() {
if (mStagingPlayState == RUNNING) {
(mTarget->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
} else if (mStagingPlayState == FINISHED) {
// We're being canceled, so make sure that whatever values the UI thread
// is observing for us is pushed over
mTarget->setPropertyFieldsDirty(dirtyMask());
}
}
uint32_t RenderPropertyAnimator::dirtyMask() {
return mPropertyAccess->dirtyMask;
}
float RenderPropertyAnimator::getValue(RenderNode* target) const {
return (target->properties().*mPropertyAccess->getter)();
}
void RenderPropertyAnimator::setValue(RenderNode* target, float value) {
(target->animatorProperties().*mPropertyAccess->setter)(value);
}
/************************************************************
* CanvasPropertyPrimitiveAnimator
************************************************************/
CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator(
CanvasPropertyPrimitive* property, float finalValue)
: BaseRenderNodeAnimator(finalValue)
, mProperty(property) {
}
float CanvasPropertyPrimitiveAnimator::getValue(RenderNode* target) const {
return mProperty->value;
}
void CanvasPropertyPrimitiveAnimator::setValue(RenderNode* target, float value) {
mProperty->value = value;
}
uint32_t CanvasPropertyPrimitiveAnimator::dirtyMask() {
return RenderNode::DISPLAY_LIST;
}
/************************************************************
* CanvasPropertySkPaintAnimator
************************************************************/
CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator(
CanvasPropertyPaint* property, PaintField field, float finalValue)
: BaseRenderNodeAnimator(finalValue)
, mProperty(property)
, mField(field) {
}
float CanvasPropertyPaintAnimator::getValue(RenderNode* target) const {
switch (mField) {
case STROKE_WIDTH:
return mProperty->value.getStrokeWidth();
case ALPHA:
return mProperty->value.getAlpha();
}
LOG_ALWAYS_FATAL("Unknown field %d", (int) mField);
return -1;
}
static uint8_t to_uint8(float value) {
int c = (int) (value + .5f);
return static_cast<uint8_t>( c < 0 ? 0 : c > 255 ? 255 : c );
}
void CanvasPropertyPaintAnimator::setValue(RenderNode* target, float value) {
switch (mField) {
case STROKE_WIDTH:
mProperty->value.setStrokeWidth(value);
return;
case ALPHA:
mProperty->value.setAlpha(to_uint8(value));
return;
}
LOG_ALWAYS_FATAL("Unknown field %d", (int) mField);
}
uint32_t CanvasPropertyPaintAnimator::dirtyMask() {
return RenderNode::DISPLAY_LIST;
}
RevealAnimator::RevealAnimator(int centerX, int centerY,
float startValue, float finalValue)
: BaseRenderNodeAnimator(finalValue)
, mCenterX(centerX)
, mCenterY(centerY) {
setStartValue(startValue);
}
float RevealAnimator::getValue(RenderNode* target) const {
return target->properties().getRevealClip().getRadius();
}
void RevealAnimator::setValue(RenderNode* target, float value) {
target->animatorProperties().mutableRevealClip().set(true,
mCenterX, mCenterY, value);
}
uint32_t RevealAnimator::dirtyMask() {
return RenderNode::GENERIC;
}
} /* namespace uirenderer */
} /* namespace android */