Merge changes from topic 'VectorDrawable polishing cherrypicks from master' into nyc-mr1-dev

* changes:
  Support Keyframe definition for AVD on RT
  Throw Exception for wrong valueType with API guard
This commit is contained in:
Doris Liu
2016-06-09 23:33:50 +00:00
committed by Android (Google) Code Review
6 changed files with 262 additions and 126 deletions

View File

@@ -1095,8 +1095,12 @@ public class PropertyValuesHolder implements Cloneable {
} }
// TODO: We need a better way to get data out of keyframes. // TODO: We need a better way to get data out of keyframes.
if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase
|| mKeyframes instanceof PathKeyframes.IntKeyframesBase) { || mKeyframes instanceof PathKeyframes.IntKeyframesBase
// property values will animate based on external data source (e.g. Path) || (mKeyframes.getKeyframes() != null && mKeyframes.getKeyframes().size() > 2)) {
// When a pvh has more than 2 keyframes, that means there are intermediate values in
// addition to start/end values defined for animators. Another case where such
// intermediate values are defined is when animator has a path to animate along. In
// these cases, a data source is needed to capture these intermediate values.
values.dataSource = new PropertyValues.DataSource() { values.dataSource = new PropertyValues.DataSource() {
@Override @Override
public Object getValueAtFraction(float fraction) { public Object getValueAtFraction(float fraction) {
@@ -1108,6 +1112,13 @@ public class PropertyValuesHolder implements Cloneable {
} }
} }
/**
* @hide
*/
public Class getValueType() {
return mValueType;
}
@Override @Override
public String toString() { public String toString() {
return mPropertyName + ": " + mKeyframes.toString(); return mPropertyName + ": " + mKeyframes.toString();

View File

@@ -30,6 +30,8 @@ import android.view.Choreographer;
@HasNativeInterpolator @HasNativeInterpolator
public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeInterpolator { public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeInterpolator {
// If the duration of an animation is more than 300 frames, we cap the sample size to 300.
private static final int MAX_SAMPLE_POINTS = 300;
private TimeInterpolator mSourceInterpolator; private TimeInterpolator mSourceInterpolator;
private final float mLut[]; private final float mLut[];
@@ -47,6 +49,7 @@ public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeI
int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS); int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
// We need 2 frame values as the minimal. // We need 2 frame values as the minimal.
int numAnimFrames = Math.max(2, (int) Math.ceil(((double) duration) / animIntervalMs)); int numAnimFrames = Math.max(2, (int) Math.ceil(((double) duration) / animIntervalMs));
numAnimFrames = Math.min(numAnimFrames, MAX_SAMPLE_POINTS);
float values[] = new float[numAnimFrames]; float values[] = new float[numAnimFrames];
float lastFrame = numAnimFrames - 1; float lastFrame = numAnimFrames - 1;
for (int i = 0; i < numAnimFrames; i++) { for (int i = 0; i < numAnimFrames; i++) {

View File

@@ -142,14 +142,24 @@ static jlong createRootAlphaPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jf
startValue, endValue); startValue, endValue);
return reinterpret_cast<jlong>(newHolder); return reinterpret_cast<jlong>(newHolder);
} }
static void setPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr, static void setFloatPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr,
jfloatArray srcData, jint length) { jfloatArray srcData, jint length) {
jfloat* propertyData = env->GetFloatArrayElements(srcData, nullptr); jfloat* propertyData = env->GetFloatArrayElements(srcData, nullptr);
PropertyValuesHolder* holder = reinterpret_cast<PropertyValuesHolder*>(propertyHolderPtr); PropertyValuesHolderImpl<float>* holder =
reinterpret_cast<PropertyValuesHolderImpl<float>*>(propertyHolderPtr);
holder->setPropertyDataSource(propertyData, length); holder->setPropertyDataSource(propertyData, length);
env->ReleaseFloatArrayElements(srcData, propertyData, JNI_ABORT); env->ReleaseFloatArrayElements(srcData, propertyData, JNI_ABORT);
} }
static void setIntPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr,
jintArray srcData, jint length) {
jint* propertyData = env->GetIntArrayElements(srcData, nullptr);
PropertyValuesHolderImpl<int>* holder =
reinterpret_cast<PropertyValuesHolderImpl<int>*>(propertyHolderPtr);
holder->setPropertyDataSource(propertyData, length);
env->ReleaseIntArrayElements(srcData, propertyData, JNI_ABORT);
}
static void start(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener, jint id) { static void start(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener, jint id) {
PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr); PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
AnimationListener* listener = createAnimationListener(env, finishListener, id); AnimationListener* listener = createAnimationListener(env, finishListener, id);
@@ -181,7 +191,8 @@ static const JNINativeMethod gMethods[] = {
{"nCreatePathColorPropertyHolder", "!(JIII)J", (void*)createPathColorPropertyHolder}, {"nCreatePathColorPropertyHolder", "!(JIII)J", (void*)createPathColorPropertyHolder},
{"nCreatePathPropertyHolder", "!(JIFF)J", (void*)createPathPropertyHolder}, {"nCreatePathPropertyHolder", "!(JIFF)J", (void*)createPathPropertyHolder},
{"nCreateRootAlphaPropertyHolder", "!(JFF)J", (void*)createRootAlphaPropertyHolder}, {"nCreateRootAlphaPropertyHolder", "!(JFF)J", (void*)createRootAlphaPropertyHolder},
{"nSetPropertyHolderData", "(J[FI)V", (void*)setPropertyHolderData}, {"nSetPropertyHolderData", "(J[FI)V", (void*)setFloatPropertyHolderData},
{"nSetPropertyHolderData", "(J[II)V", (void*)setIntPropertyHolderData},
{"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)start}, {"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)start},
{"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)reverse}, {"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)reverse},
{"nEnd", "!(J)V", (void*)end}, {"nEnd", "!(J)V", (void*)end},

View File

@@ -395,7 +395,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
// The animator here could be ObjectAnimator or AnimatorSet. // The animator here could be ObjectAnimator or AnimatorSet.
final Animator animator = AnimatorInflater.loadAnimator( final Animator animator = AnimatorInflater.loadAnimator(
res, theme, animResId, pathErrorScale); res, theme, animResId, pathErrorScale);
updateAnimatorProperty(animator, target, state.mVectorDrawable); updateAnimatorProperty(animator, target, state.mVectorDrawable,
state.mShouldIgnoreInvalidAnim);
state.addTargetAnimator(target, animator); state.addTargetAnimator(target, animator);
} else { } else {
// The animation may be theme-dependent. As a // The animation may be theme-dependent. As a
@@ -419,7 +420,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
} }
private static void updateAnimatorProperty(Animator animator, String targetName, private static void updateAnimatorProperty(Animator animator, String targetName,
VectorDrawable vectorDrawable) { VectorDrawable vectorDrawable, boolean ignoreInvalidAnim) {
if (animator instanceof ObjectAnimator) { if (animator instanceof ObjectAnimator) {
// Change the property of the Animator from using reflection based on the property // Change the property of the Animator from using reflection based on the property
// name to a Property object that wraps the setter and getter for modifying that // name to a Property object that wraps the setter and getter for modifying that
@@ -438,16 +439,35 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
.getProperty(propertyName); .getProperty(propertyName);
} }
if (property != null) { if (property != null) {
pvh.setProperty(property); if (containsSameValueType(pvh, property)) {
pvh.setProperty(property);
} else if (!ignoreInvalidAnim) {
throw new RuntimeException("Wrong valueType for Property: " + propertyName
+ ". Expected type: " + property.getType().toString() + ". Actual "
+ "type defined in resources: " + pvh.getValueType().toString());
}
} }
} }
} else if (animator instanceof AnimatorSet) { } else if (animator instanceof AnimatorSet) {
for (Animator anim : ((AnimatorSet) animator).getChildAnimations()) { for (Animator anim : ((AnimatorSet) animator).getChildAnimations()) {
updateAnimatorProperty(anim, targetName, vectorDrawable); updateAnimatorProperty(anim, targetName, vectorDrawable, ignoreInvalidAnim);
} }
} }
} }
private static boolean containsSameValueType(PropertyValuesHolder holder, Property property) {
Class type1 = holder.getValueType();
Class type2 = property.getType();
if (type1 == float.class || type1 == Float.class) {
return type2 == float.class || type2 == Float.class;
} else if (type1 == int.class || type1 == Integer.class) {
return type2 == int.class || type2 == Integer.class;
} else {
return type1 == type2;
}
}
/** /**
* Force to animate on UI thread. * Force to animate on UI thread.
* @hide * @hide
@@ -496,6 +516,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
@Config int mChangingConfigurations; @Config int mChangingConfigurations;
VectorDrawable mVectorDrawable; VectorDrawable mVectorDrawable;
private final boolean mShouldIgnoreInvalidAnim;
/** Animators that require a theme before inflation. */ /** Animators that require a theme before inflation. */
ArrayList<PendingAnimator> mPendingAnims; ArrayList<PendingAnimator> mPendingAnims;
@@ -507,6 +529,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy, public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy,
Callback owner, Resources res) { Callback owner, Resources res) {
mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
if (copy != null) { if (copy != null) {
mChangingConfigurations = copy.mChangingConfigurations; mChangingConfigurations = copy.mChangingConfigurations;
@@ -651,7 +674,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
for (int i = 0, count = pendingAnims.size(); i < count; i++) { for (int i = 0, count = pendingAnims.size(); i < count; i++) {
final PendingAnimator pendingAnimator = pendingAnims.get(i); final PendingAnimator pendingAnimator = pendingAnims.get(i);
final Animator animator = pendingAnimator.newInstance(res, t); final Animator animator = pendingAnimator.newInstance(res, t);
updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable); updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable,
mShouldIgnoreInvalidAnim);
addTargetAnimator(pendingAnimator.target, animator); addTargetAnimator(pendingAnimator.target, animator);
} }
} }
@@ -1017,6 +1041,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private static final int REVERSE_ANIMATION = 2; private static final int REVERSE_ANIMATION = 2;
private static final int RESET_ANIMATION = 3; private static final int RESET_ANIMATION = 3;
private static final int END_ANIMATION = 4; private static final int END_ANIMATION = 4;
// If the duration of an animation is more than 300 frames, we cap the sample size to 300.
private static final int MAX_SAMPLE_POINTS = 300;
private AnimatorListener mListener = null; private AnimatorListener mListener = null;
private final LongArray mStartDelays = new LongArray(); private final LongArray mStartDelays = new LongArray();
private PropertyValuesHolder.PropertyValues mTmpValues = private PropertyValuesHolder.PropertyValues mTmpValues =
@@ -1027,8 +1054,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private boolean mInitialized = false; private boolean mInitialized = false;
private boolean mIsReversible = false; private boolean mIsReversible = false;
private boolean mIsInfinite = false; private boolean mIsInfinite = false;
// This needs to be set before parsing starts.
private boolean mShouldIgnoreInvalidAnim;
// TODO: Consider using NativeAllocationRegistery to track native allocation // TODO: Consider using NativeAllocationRegistery to track native allocation
private final VirtualRefBasePtr mSetRefBasePtr; private final VirtualRefBasePtr mSetRefBasePtr;
private WeakReference<RenderNode> mLastSeenTarget = null; private WeakReference<RenderNode> mLastSeenTarget = null;
@@ -1051,7 +1076,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " + throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
"re-initialized"); "re-initialized");
} }
mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
parseAnimatorSet(set, 0); parseAnimatorSet(set, 0);
long vectorDrawableTreePtr = mDrawable.mAnimatedVectorState.mVectorDrawable long vectorDrawableTreePtr = mDrawable.mAnimatedVectorState.mVectorDrawable
.getNativeTree(); .getNativeTree();
@@ -1115,7 +1139,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
} else if (target instanceof VectorDrawable.VFullPath) { } else if (target instanceof VectorDrawable.VFullPath) {
createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target, createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
startTime); startTime);
} else if (!mShouldIgnoreInvalidAnim) { } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
throw new IllegalArgumentException("ClipPath only supports PathData " + throw new IllegalArgumentException("ClipPath only supports PathData " +
"property"); "property");
} }
@@ -1124,7 +1148,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
} else if (target instanceof VectorDrawable.VectorDrawableState) { } else if (target instanceof VectorDrawable.VectorDrawableState) {
createRTAnimatorForRootGroup(values, animator, createRTAnimatorForRootGroup(values, animator,
(VectorDrawable.VectorDrawableState) target, startTime); (VectorDrawable.VectorDrawableState) target, startTime);
} else if (!mShouldIgnoreInvalidAnim) { } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
// Should never get here // Should never get here
throw new UnsupportedOperationException("Target should be either VGroup, VPath, " + throw new UnsupportedOperationException("Target should be either VGroup, VPath, " +
"or ConstantState, " + target == null ? "Null target" : target.getClass() + "or ConstantState, " + target == null ? "Null target" : target.getClass() +
@@ -1159,8 +1183,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId, long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId,
(Float) mTmpValues.startValue, (Float) mTmpValues.endValue); (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
if (mTmpValues.dataSource != null) { if (mTmpValues.dataSource != null) {
float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
.getDuration()); animator.getDuration());
nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length); nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
} }
createNativeChildAnimator(propertyPtr, startTime, animator); createNativeChildAnimator(propertyPtr, startTime, animator);
@@ -1187,7 +1211,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
long nativePtr = target.getNativePtr(); long nativePtr = target.getNativePtr();
if (mTmpValues.type == Float.class || mTmpValues.type == float.class) { if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
if (propertyId < 0) { if (propertyId < 0) {
if (mShouldIgnoreInvalidAnim) { if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
return; return;
} else { } else {
throw new IllegalArgumentException("Property: " + mTmpValues.propertyName throw new IllegalArgumentException("Property: " + mTmpValues.propertyName
@@ -1196,12 +1220,24 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
} }
propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId, propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
(Float) mTmpValues.startValue, (Float) mTmpValues.endValue); (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
if (mTmpValues.dataSource != null) {
// Pass keyframe data to native, if any.
float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
animator.getDuration());
nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
}
} else if (mTmpValues.type == Integer.class || mTmpValues.type == int.class) { } else if (mTmpValues.type == Integer.class || mTmpValues.type == int.class) {
propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId, propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
(Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue); (Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue);
if (mTmpValues.dataSource != null) {
// Pass keyframe data to native, if any.
int[] dataPoints = createIntDataPoints(mTmpValues.dataSource,
animator.getDuration());
nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
}
} else { } else {
if (mShouldIgnoreInvalidAnim) { if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
return; return;
} else { } else {
throw new UnsupportedOperationException("Unsupported type: " + throw new UnsupportedOperationException("Unsupported type: " +
@@ -1209,45 +1245,63 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
"supported for Paths."); "supported for Paths.");
} }
} }
if (mTmpValues.dataSource != null) {
float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
.getDuration());
nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
}
createNativeChildAnimator(propertyPtr, startTime, animator); createNativeChildAnimator(propertyPtr, startTime, animator);
} }
private void createRTAnimatorForRootGroup(PropertyValuesHolder[] values, private void createRTAnimatorForRootGroup(PropertyValuesHolder[] values,
ObjectAnimator animator, VectorDrawable.VectorDrawableState target, ObjectAnimator animator, VectorDrawable.VectorDrawableState target,
long startTime) { long startTime) {
long nativePtr = target.getNativeRenderer(); long nativePtr = target.getNativeRenderer();
if (!animator.getPropertyName().equals("alpha")) { if (!animator.getPropertyName().equals("alpha")) {
if (mShouldIgnoreInvalidAnim) { if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
return; return;
} else { } else {
throw new UnsupportedOperationException("Only alpha is supported for root " throw new UnsupportedOperationException("Only alpha is supported for root "
+ "group"); + "group");
}
} }
Float startValue = null; }
Float endValue = null; Float startValue = null;
for (int i = 0; i < values.length; i++) { Float endValue = null;
values[i].getPropertyValues(mTmpValues); for (int i = 0; i < values.length; i++) {
if (mTmpValues.propertyName.equals("alpha")) { values[i].getPropertyValues(mTmpValues);
startValue = (Float) mTmpValues.startValue; if (mTmpValues.propertyName.equals("alpha")) {
endValue = (Float) mTmpValues.endValue; startValue = (Float) mTmpValues.startValue;
break; endValue = (Float) mTmpValues.endValue;
} break;
} }
if (startValue == null && endValue == null) { }
if (mShouldIgnoreInvalidAnim) { if (startValue == null && endValue == null) {
return; if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
} else { return;
throw new UnsupportedOperationException("No alpha values are specified"); } else {
} throw new UnsupportedOperationException("No alpha values are specified");
} }
long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue); }
createNativeChildAnimator(propertyPtr, startTime, animator); long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
if (mTmpValues.dataSource != null) {
// Pass keyframe data to native, if any.
float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
animator.getDuration());
nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
}
createNativeChildAnimator(propertyPtr, startTime, animator);
}
/**
* Calculate the amount of frames an animation will run based on duration.
*/
private static int getFrameCount(long duration) {
long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
// We need 2 frames of data minimum.
numAnimFrames = Math.max(2, numAnimFrames);
if (numAnimFrames > MAX_SAMPLE_POINTS) {
Log.w("AnimatedVectorDrawable", "Duration for the animation is too long :" +
duration + ", the animation will subsample the keyframe or path data.");
numAnimFrames = MAX_SAMPLE_POINTS;
}
return numAnimFrames;
} }
// These are the data points that define the value of the animating properties. // These are the data points that define the value of the animating properties.
@@ -1255,11 +1309,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
// a point on the path corresponds to the values of translateX and translateY. // a point on the path corresponds to the values of translateX and translateY.
// TODO: (Optimization) We should pass the path down in native and chop it into segments // TODO: (Optimization) We should pass the path down in native and chop it into segments
// in native. // in native.
private static float[] createDataPoints( private static float[] createFloatDataPoints(
PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) { PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos(); int numAnimFrames = getFrameCount(duration);
int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
float values[] = new float[numAnimFrames]; float values[] = new float[numAnimFrames];
float lastFrame = numAnimFrames - 1; float lastFrame = numAnimFrames - 1;
for (int i = 0; i < numAnimFrames; i++) { for (int i = 0; i < numAnimFrames; i++) {
@@ -1269,6 +1321,18 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
return values; return values;
} }
private static int[] createIntDataPoints(
PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
int numAnimFrames = getFrameCount(duration);
int values[] = new int[numAnimFrames];
float lastFrame = numAnimFrames - 1;
for (int i = 0; i < numAnimFrames; i++) {
float fraction = i / lastFrame;
values[i] = (Integer) dataSource.getValueAtFraction(fraction);
}
return values;
}
private void createNativeChildAnimator(long propertyPtr, long extraDelay, private void createNativeChildAnimator(long propertyPtr, long extraDelay,
ObjectAnimator animator) { ObjectAnimator animator) {
long duration = animator.getDuration(); long duration = animator.getDuration();
@@ -1545,6 +1609,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue, private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
float endValue); float endValue);
private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length); private static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
private static native void nSetPropertyHolderData(long nativePtr, int[] data, int length);
private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id); private static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id); private static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
private static native void nEnd(long animatorSetPtr); private static native void nEnd(long animatorSetPtr);

View File

@@ -25,7 +25,27 @@ namespace uirenderer {
using namespace VectorDrawable; using namespace VectorDrawable;
float PropertyValuesHolder::getValueFromData(float fraction) { inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) {
return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction);
}
// TODO: Add a test for this
void ColorEvaluator::evaluate(SkColor* outColor,
const SkColor& fromColor, const SkColor& toColor, float fraction) const {
U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction);
U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction);
U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction);
U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction);
*outColor = SkColorSetARGB(alpha, red, green, blue);
}
void PathEvaluator::evaluate(PathData* out,
const PathData& from, const PathData& to, float fraction) const {
VectorDrawableUtils::interpolatePaths(out, from, to, fraction);
}
template<typename T>
const T PropertyValuesHolderImpl<T>::getValueFromData(float fraction) const {
if (mDataSource.size() == 0) { if (mDataSource.size() == 0) {
LOG_ALWAYS_FATAL("No data source is defined"); LOG_ALWAYS_FATAL("No data source is defined");
return 0; return 0;
@@ -41,57 +61,44 @@ float PropertyValuesHolder::getValueFromData(float fraction) {
int lowIndex = floor(fraction); int lowIndex = floor(fraction);
fraction -= lowIndex; fraction -= lowIndex;
float value = mDataSource[lowIndex] * (1.0f - fraction) T value;
+ mDataSource[lowIndex + 1] * fraction; mEvaluator->evaluate(&value, mDataSource[lowIndex], mDataSource[lowIndex + 1], fraction);
return value; return value;
} }
void GroupPropertyValuesHolder::setFraction(float fraction) { template<typename T>
float animatedValue; const T PropertyValuesHolderImpl<T>::calculateAnimatedValue(float fraction) const {
if (mDataSource.size() > 0) { if (mDataSource.size() > 0) {
animatedValue = getValueFromData(fraction); return getValueFromData(fraction);
} else { } else {
animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction; T value;
mEvaluator->evaluate(&value, mStartValue, mEndValue, fraction);
return value;
} }
}
void GroupPropertyValuesHolder::setFraction(float fraction) {
float animatedValue = calculateAnimatedValue(fraction);
mGroup->mutateProperties()->setPropertyValue(mPropertyId, animatedValue); mGroup->mutateProperties()->setPropertyValue(mPropertyId, animatedValue);
} }
inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) {
return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction);
}
// TODO: Add a test for this
SkColor FullPathColorPropertyValuesHolder::interpolateColors(SkColor fromColor, SkColor toColor,
float fraction) {
U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction);
U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction);
U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction);
U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction);
return SkColorSetARGB(alpha, red, green, blue);
}
void FullPathColorPropertyValuesHolder::setFraction(float fraction) { void FullPathColorPropertyValuesHolder::setFraction(float fraction) {
SkColor animatedValue = interpolateColors(mStartValue, mEndValue, fraction); SkColor animatedValue = calculateAnimatedValue(fraction);
mFullPath->mutateProperties()->setColorPropertyValue(mPropertyId, animatedValue); mFullPath->mutateProperties()->setColorPropertyValue(mPropertyId, animatedValue);
} }
void FullPathPropertyValuesHolder::setFraction(float fraction) { void FullPathPropertyValuesHolder::setFraction(float fraction) {
float animatedValue; float animatedValue = calculateAnimatedValue(fraction);
if (mDataSource.size() > 0) {
animatedValue = getValueFromData(fraction);
} else {
animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
}
mFullPath->mutateProperties()->setPropertyValue(mPropertyId, animatedValue); mFullPath->mutateProperties()->setPropertyValue(mPropertyId, animatedValue);
} }
void PathDataPropertyValuesHolder::setFraction(float fraction) { void PathDataPropertyValuesHolder::setFraction(float fraction) {
VectorDrawableUtils::interpolatePaths(&mPathData, mStartValue, mEndValue, fraction); mEvaluator->evaluate(&mPathData, mStartValue, mEndValue, fraction);
mPath->mutateProperties()->setData(mPathData); mPath->mutateProperties()->setData(mPathData);
} }
void RootAlphaPropertyValuesHolder::setFraction(float fraction) { void RootAlphaPropertyValuesHolder::setFraction(float fraction) {
float animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction; float animatedValue = calculateAnimatedValue(fraction);
mTree->mutateProperties()->setRootAlpha(animatedValue); mTree->mutateProperties()->setRootAlpha(animatedValue);
} }

View File

@@ -31,91 +31,130 @@ namespace uirenderer {
class ANDROID_API PropertyValuesHolder { class ANDROID_API PropertyValuesHolder {
public: public:
virtual void setFraction(float fraction) = 0; virtual void setFraction(float fraction) = 0;
void setPropertyDataSource(float* dataSource, int length) { virtual ~PropertyValuesHolder() {}
mDataSource.insert(mDataSource.begin(), dataSource, dataSource + length);
}
float getValueFromData(float fraction);
virtual ~PropertyValuesHolder() {}
protected:
std::vector<float> mDataSource;
}; };
class ANDROID_API GroupPropertyValuesHolder : public PropertyValuesHolder { template <typename T>
class Evaluator {
public:
virtual void evaluate(T* out, const T& from, const T& to, float fraction) const {};
virtual ~Evaluator() {}
};
class FloatEvaluator : public Evaluator<float> {
public:
virtual void evaluate(float* out, const float& from, const float& to, float fraction)
const override {
*out = from * (1 - fraction) + to * fraction;
}
};
class ANDROID_API ColorEvaluator : public Evaluator<SkColor> {
public:
virtual void evaluate(SkColor* outColor, const SkColor& from, const SkColor& to,
float fraction) const override;
};
class ANDROID_API PathEvaluator : public Evaluator<PathData> {
virtual void evaluate(PathData* out, const PathData& from, const PathData& to, float fraction)
const override;
};
template <typename T>
class ANDROID_API PropertyValuesHolderImpl : public PropertyValuesHolder {
public:
PropertyValuesHolderImpl(const T& startValue, const T& endValue)
: mStartValue(startValue)
, mEndValue(endValue) {}
void setPropertyDataSource(T* dataSource, int length) {
mDataSource.insert(mDataSource.begin(), dataSource, dataSource + length);
}
// Calculate the animated value from the data source.
const T getValueFromData(float fraction) const;
// Convenient method to favor getting animated value from data source. If no data source is set
// fall back to linear interpolation.
const T calculateAnimatedValue(float fraction) const;
protected:
std::unique_ptr<Evaluator<T>> mEvaluator = nullptr;
// This contains uniformly sampled data throughout the animation duration. The first element
// should be the start value and the last should be the end value of the animation. When the
// data source is set, we'll favor data source over the linear interpolation of start/end value
// for calculation of animated value.
std::vector<T> mDataSource;
T mStartValue;
T mEndValue;
};
class ANDROID_API GroupPropertyValuesHolder : public PropertyValuesHolderImpl<float> {
public: public:
GroupPropertyValuesHolder(VectorDrawable::Group* ptr, int propertyId, float startValue, GroupPropertyValuesHolder(VectorDrawable::Group* ptr, int propertyId, float startValue,
float endValue) float endValue)
: mGroup(ptr) : PropertyValuesHolderImpl(startValue, endValue)
, mPropertyId(propertyId) , mGroup(ptr)
, mStartValue(startValue) , mPropertyId(propertyId) {
, mEndValue(endValue){ mEvaluator.reset(new FloatEvaluator());
} }
void setFraction(float fraction) override; void setFraction(float fraction) override;
private: private:
VectorDrawable::Group* mGroup; VectorDrawable::Group* mGroup;
int mPropertyId; int mPropertyId;
float mStartValue;
float mEndValue;
}; };
class ANDROID_API FullPathColorPropertyValuesHolder : public PropertyValuesHolder { class ANDROID_API FullPathColorPropertyValuesHolder : public PropertyValuesHolderImpl<SkColor> {
public: public:
FullPathColorPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, int32_t startValue, FullPathColorPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId,
int32_t endValue) SkColor startValue, SkColor endValue)
: mFullPath(ptr) : PropertyValuesHolderImpl(startValue, endValue)
, mPropertyId(propertyId) , mFullPath(ptr)
, mStartValue(startValue) , mPropertyId(propertyId) {
, mEndValue(endValue) {}; mEvaluator.reset(new ColorEvaluator());
}
void setFraction(float fraction) override; void setFraction(float fraction) override;
static SkColor interpolateColors(SkColor fromColor, SkColor toColor, float fraction); static SkColor interpolateColors(SkColor fromColor, SkColor toColor, float fraction);
private: private:
VectorDrawable::FullPath* mFullPath; VectorDrawable::FullPath* mFullPath;
int mPropertyId; int mPropertyId;
int32_t mStartValue;
int32_t mEndValue;
}; };
class ANDROID_API FullPathPropertyValuesHolder : public PropertyValuesHolder { class ANDROID_API FullPathPropertyValuesHolder : public PropertyValuesHolderImpl<float> {
public: public:
FullPathPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, float startValue, FullPathPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, float startValue,
float endValue) float endValue)
: mFullPath(ptr) : PropertyValuesHolderImpl(startValue, endValue)
, mPropertyId(propertyId) , mFullPath(ptr)
, mStartValue(startValue) , mPropertyId(propertyId) {
, mEndValue(endValue) {}; mEvaluator.reset(new FloatEvaluator());
};
void setFraction(float fraction) override; void setFraction(float fraction) override;
private: private:
VectorDrawable::FullPath* mFullPath; VectorDrawable::FullPath* mFullPath;
int mPropertyId; int mPropertyId;
float mStartValue;
float mEndValue;
}; };
class ANDROID_API PathDataPropertyValuesHolder : public PropertyValuesHolder { class ANDROID_API PathDataPropertyValuesHolder : public PropertyValuesHolderImpl<PathData> {
public: public:
PathDataPropertyValuesHolder(VectorDrawable::Path* ptr, PathData* startValue, PathDataPropertyValuesHolder(VectorDrawable::Path* ptr, PathData* startValue,
PathData* endValue) PathData* endValue)
: mPath(ptr) : PropertyValuesHolderImpl(*startValue, *endValue)
, mStartValue(*startValue) , mPath(ptr) {
, mEndValue(*endValue) {}; mEvaluator.reset(new PathEvaluator());
};
void setFraction(float fraction) override; void setFraction(float fraction) override;
private: private:
VectorDrawable::Path* mPath; VectorDrawable::Path* mPath;
PathData mPathData; PathData mPathData;
PathData mStartValue;
PathData mEndValue;
}; };
class ANDROID_API RootAlphaPropertyValuesHolder : public PropertyValuesHolder { class ANDROID_API RootAlphaPropertyValuesHolder : public PropertyValuesHolderImpl<float> {
public: public:
RootAlphaPropertyValuesHolder(VectorDrawable::Tree* tree, float startValue, float endValue) RootAlphaPropertyValuesHolder(VectorDrawable::Tree* tree, float startValue, float endValue)
: mTree(tree) : PropertyValuesHolderImpl(startValue, endValue)
, mStartValue(startValue) , mTree(tree) {
, mEndValue(endValue) {} mEvaluator.reset(new FloatEvaluator());
}
void setFraction(float fraction) override; void setFraction(float fraction) override;
private: private:
VectorDrawable::Tree* mTree; VectorDrawable::Tree* mTree;
float mStartValue;
float mEndValue;
}; };
} }
} }