From 792926a58c94563cf35d532cd3db888cc1cbeb7d Mon Sep 17 00:00:00 2001 From: Doris Liu Date: Thu, 19 May 2016 19:04:47 -0700 Subject: [PATCH] Improve the property modification effiency of VectorDrawable animators Previously, the VectorDrawable animators modify the VD properties by doing reflection based on the property name. This CL avoids the overhead of calling reflection by wrapping the setters of VD objects in Float/IntProperty. Bug: 28124049 Change-Id: I562c8d749fdfe508564c82a700a92e835cdd9ff4 (cherry picked from commit 9f3b31b9530a2121b4557fc312972c4faa133d5e) --- .../animation/PropertyValuesHolder.java | 20 +- .../drawable/AnimatedVectorDrawable.java | 42 ++- .../graphics/drawable/VectorDrawable.java | 308 +++++++++++++++++- 3 files changed, 356 insertions(+), 14 deletions(-) diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java index 224823ee2ff11..1dee925f2bdd4 100644 --- a/core/java/android/animation/PropertyValuesHolder.java +++ b/core/java/android/animation/PropertyValuesHolder.java @@ -1177,6 +1177,15 @@ public class PropertyValuesHolder implements Cloneable { } } + @Override + public void setProperty(Property property) { + if (property instanceof IntProperty) { + mIntProperty = (IntProperty) property; + } else { + super.setProperty(property); + } + } + @Override public void setIntValues(int... values) { super.setIntValues(values); @@ -1315,6 +1324,15 @@ public class PropertyValuesHolder implements Cloneable { } } + @Override + public void setProperty(Property property) { + if (property instanceof FloatProperty) { + mFloatProperty = (FloatProperty) property; + } else { + super.setProperty(property); + } + } + @Override public void setFloatValues(float... values) { super.setFloatValues(values); @@ -1516,7 +1534,7 @@ public class PropertyValuesHolder implements Cloneable { } propertyMap.put(mPropertyName, mJniSetter); } - } + } } } diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 6762bea1ca350..7fa3947d80f95 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -46,6 +46,7 @@ import android.util.IntArray; import android.util.Log; import android.util.LongArray; import android.util.PathParser; +import android.util.Property; import android.util.TimeUtils; import android.view.Choreographer; import android.view.DisplayListCanvas; @@ -390,9 +391,11 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { R.styleable.AnimatedVectorDrawableTarget_animation, 0); if (animResId != 0) { if (theme != null) { - final Animator objectAnimator = AnimatorInflater.loadAnimator( + // The animator here could be ObjectAnimator or AnimatorSet. + final Animator animator = AnimatorInflater.loadAnimator( res, theme, animResId, pathErrorScale); - state.addTargetAnimator(target, objectAnimator); + updateAnimatorProperty(animator, target, state.mVectorDrawable); + state.addTargetAnimator(target, animator); } else { // The animation may be theme-dependent. As a // workaround until Animator has full support for @@ -414,6 +417,36 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { mRes = state.mPendingAnims == null ? null : res; } + private static void updateAnimatorProperty(Animator animator, String targetName, + VectorDrawable vectorDrawable) { + if (animator instanceof ObjectAnimator) { + // 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 + // specific property for a given object. By replacing the reflection with a direct call, + // we can largely reduce the time it takes for a animator to modify a VD property. + PropertyValuesHolder[] holders = ((ObjectAnimator) animator).getValues(); + for (int i = 0; i < holders.length; i++) { + PropertyValuesHolder pvh = holders[i]; + String propertyName = pvh.getPropertyName(); + Object targetNameObj = vectorDrawable.getTargetByName(targetName); + Property property = null; + if (targetNameObj instanceof VectorDrawable.VObject) { + property = ((VectorDrawable.VObject) targetNameObj).getProperty(propertyName); + } else if (targetNameObj instanceof VectorDrawable.VectorDrawableState) { + property = ((VectorDrawable.VectorDrawableState) targetNameObj) + .getProperty(propertyName); + } + if (property != null) { + pvh.setProperty(property); + } + } + } else if (animator instanceof AnimatorSet) { + for (Animator anim : ((AnimatorSet) animator).getChildAnimations()) { + updateAnimatorProperty(anim, targetName, vectorDrawable); + } + } + } + /** * Force to animate on UI thread. * @hide @@ -616,8 +649,9 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { for (int i = 0, count = pendingAnims.size(); i < count; i++) { final PendingAnimator pendingAnimator = pendingAnims.get(i); - final Animator objectAnimator = pendingAnimator.newInstance(res, t); - addTargetAnimator(pendingAnimator.target, objectAnimator); + final Animator animator = pendingAnimator.newInstance(res, t); + updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable); + addTargetAnimator(pendingAnimator.target, animator); } } } diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index f5592fa7e4079..237c931771149 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -34,9 +34,12 @@ import android.graphics.Shader; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.FloatProperty; +import android.util.IntProperty; import android.util.LayoutDirection; import android.util.Log; import android.util.PathParser; +import android.util.Property; import android.util.Xml; import com.android.internal.R; @@ -790,6 +793,26 @@ public class VectorDrawable extends Drawable { int mLastSWCachePixelCount = 0; int mLastHWCachePixelCount = 0; + final static Property ALPHA = + new FloatProperty("alpha") { + @Override + public void setValue(VectorDrawableState state, float value) { + state.setAlpha(value); + } + + @Override + public Float get(VectorDrawableState state) { + return state.getAlpha(); + } + }; + + Property getProperty(String propertyName) { + if (ALPHA.getName().equals(propertyName)) { + return ALPHA; + } + return null; + } + // This tracks the total native allocation for all the nodes. private int mAllocationOfAllNodes = 0; @@ -967,7 +990,7 @@ public class VectorDrawable extends Drawable { } static class VGroup extends VObject { - private static final int ROTATE_INDEX = 0; + private static final int ROTATION_INDEX = 0; private static final int PIVOT_X_INDEX = 1; private static final int PIVOT_Y_INDEX = 2; private static final int SCALE_X_INDEX = 3; @@ -978,7 +1001,7 @@ public class VectorDrawable extends Drawable { private static final int NATIVE_ALLOCATION_SIZE = 100; - private static final HashMap sPropertyMap = + private static final HashMap sPropertyIndexMap = new HashMap() { { put("translateX", TRANSLATE_X_INDEX); @@ -987,19 +1010,123 @@ public class VectorDrawable extends Drawable { put("scaleY", SCALE_Y_INDEX); put("pivotX", PIVOT_X_INDEX); put("pivotY", PIVOT_Y_INDEX); - put("rotation", ROTATE_INDEX); + put("rotation", ROTATION_INDEX); } }; static int getPropertyIndex(String propertyName) { - if (sPropertyMap.containsKey(propertyName)) { - return sPropertyMap.get(propertyName); + if (sPropertyIndexMap.containsKey(propertyName)) { + return sPropertyIndexMap.get(propertyName); } else { // property not found return -1; } } + // Below are the Properties that wrap the setters to avoid reflection overhead in animations + private static final Property TRANSLATE_X = + new FloatProperty ("translateX") { + @Override + public void setValue(VGroup object, float value) { + object.setTranslateX(value); + } + + @Override + public Float get(VGroup object) { + return object.getTranslateX(); + } + }; + + private static final Property TRANSLATE_Y = + new FloatProperty ("translateY") { + @Override + public void setValue(VGroup object, float value) { + object.setTranslateY(value); + } + + @Override + public Float get(VGroup object) { + return object.getTranslateY(); + } + }; + + private static final Property SCALE_X = + new FloatProperty ("scaleX") { + @Override + public void setValue(VGroup object, float value) { + object.setScaleX(value); + } + + @Override + public Float get(VGroup object) { + return object.getScaleX(); + } + }; + + private static final Property SCALE_Y = + new FloatProperty ("scaleY") { + @Override + public void setValue(VGroup object, float value) { + object.setScaleY(value); + } + + @Override + public Float get(VGroup object) { + return object.getScaleY(); + } + }; + + private static final Property PIVOT_X = + new FloatProperty ("pivotX") { + @Override + public void setValue(VGroup object, float value) { + object.setPivotX(value); + } + + @Override + public Float get(VGroup object) { + return object.getPivotX(); + } + }; + + private static final Property PIVOT_Y = + new FloatProperty ("pivotY") { + @Override + public void setValue(VGroup object, float value) { + object.setPivotY(value); + } + + @Override + public Float get(VGroup object) { + return object.getPivotY(); + } + }; + + private static final Property ROTATION = + new FloatProperty ("rotation") { + @Override + public void setValue(VGroup object, float value) { + object.setRotation(value); + } + + @Override + public Float get(VGroup object) { + return object.getRotation(); + } + }; + + private static final HashMap sPropertyMap = + new HashMap() { + { + put("translateX", TRANSLATE_X); + put("translateY", TRANSLATE_Y); + put("scaleX", SCALE_X); + put("scaleY", SCALE_Y); + put("pivotX", PIVOT_X); + put("pivotY", PIVOT_Y); + put("rotation", ROTATION); + } + }; // Temp array to store transform values obtained from native. private float[] mTransform; ///////////////////////////////////////////////////// @@ -1055,6 +1182,15 @@ public class VectorDrawable extends Drawable { mNativePtr = nCreateGroup(); } + Property getProperty(String propertyName) { + if (sPropertyMap.containsKey(propertyName)) { + return sPropertyMap.get(propertyName); + } else { + // property not found + return null; + } + } + public String getGroupName() { return mGroupName; } @@ -1102,7 +1238,7 @@ public class VectorDrawable extends Drawable { throw new RuntimeException("Error: inconsistent property count"); } float rotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, - mTransform[ROTATE_INDEX]); + mTransform[ROTATION_INDEX]); float pivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mTransform[PIVOT_X_INDEX]); float pivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, @@ -1288,6 +1424,27 @@ public class VectorDrawable extends Drawable { String mPathName; @Config int mChangingConfigurations; + private static final Property PATH_DATA = + new Property(PathParser.PathData.class, "pathData") { + @Override + public void set(VPath object, PathParser.PathData data) { + object.setPathData(data); + } + + @Override + public PathParser.PathData get(VPath object) { + return object.getPathData(); + } + }; + + Property getProperty(String propertyName) { + if (PATH_DATA.getName().equals(propertyName)) { + return PATH_DATA; + } + // property not found + return null; + } + public VPath() { // Empty constructor. } @@ -1410,7 +1567,7 @@ public class VectorDrawable extends Drawable { private static final int NATIVE_ALLOCATION_SIZE = 264; // Property map for animatable attributes. - private final static HashMap sPropertyMap + private final static HashMap sPropertyIndexMap = new HashMap () { { put("strokeWidth", STROKE_WIDTH_INDEX); @@ -1424,6 +1581,125 @@ public class VectorDrawable extends Drawable { } }; + // Below are the Properties that wrap the setters to avoid reflection overhead in animations + private static final Property STROKE_WIDTH = + new FloatProperty ("strokeWidth") { + @Override + public void setValue(VFullPath object, float value) { + object.setStrokeWidth(value); + } + + @Override + public Float get(VFullPath object) { + return object.getStrokeWidth(); + } + }; + + private static final Property STROKE_COLOR = + new IntProperty ("strokeColor") { + @Override + public void setValue(VFullPath object, int value) { + object.setStrokeColor(value); + } + + @Override + public Integer get(VFullPath object) { + return object.getStrokeColor(); + } + }; + + private static final Property STROKE_ALPHA = + new FloatProperty ("strokeAlpha") { + @Override + public void setValue(VFullPath object, float value) { + object.setStrokeAlpha(value); + } + + @Override + public Float get(VFullPath object) { + return object.getStrokeAlpha(); + } + }; + + private static final Property FILL_COLOR = + new IntProperty("fillColor") { + @Override + public void setValue(VFullPath object, int value) { + object.setFillColor(value); + } + + @Override + public Integer get(VFullPath object) { + return object.getFillColor(); + } + }; + + private static final Property FILL_ALPHA = + new FloatProperty ("fillAlpha") { + @Override + public void setValue(VFullPath object, float value) { + object.setFillAlpha(value); + } + + @Override + public Float get(VFullPath object) { + return object.getFillAlpha(); + } + }; + + private static final Property TRIM_PATH_START = + new FloatProperty ("trimPathStart") { + @Override + public void setValue(VFullPath object, float value) { + object.setTrimPathStart(value); + } + + @Override + public Float get(VFullPath object) { + return object.getTrimPathStart(); + } + }; + + private static final Property TRIM_PATH_END = + new FloatProperty ("trimPathEnd") { + @Override + public void setValue(VFullPath object, float value) { + object.setTrimPathEnd(value); + } + + @Override + public Float get(VFullPath object) { + return object.getTrimPathEnd(); + } + }; + + private static final Property TRIM_PATH_OFFSET = + new FloatProperty ("trimPathOffset") { + @Override + public void setValue(VFullPath object, float value) { + object.setTrimPathOffset(value); + } + + @Override + public Float get(VFullPath object) { + return object.getTrimPathOffset(); + } + }; + + private final static HashMap sPropertyMap + = new HashMap () { + { + put("strokeWidth", STROKE_WIDTH); + put("strokeColor", STROKE_COLOR); + put("strokeAlpha", STROKE_ALPHA); + put("fillColor", FILL_COLOR); + put("fillAlpha", FILL_ALPHA); + put("trimPathStart", TRIM_PATH_START); + put("trimPathEnd", TRIM_PATH_END); + put("trimPathOffset", TRIM_PATH_OFFSET); + } + }; + // Temp array to store property data obtained from native getter. private byte[] mPropertyData; ///////////////////////////////////////////////////// @@ -1446,11 +1722,24 @@ public class VectorDrawable extends Drawable { mFillColors = copy.mFillColors; } + Property getProperty(String propertyName) { + Property p = super.getProperty(propertyName); + if (p != null) { + return p; + } + if (sPropertyMap.containsKey(propertyName)) { + return sPropertyMap.get(propertyName); + } else { + // property not found + return null; + } + } + int getPropertyIndex(String propertyName) { - if (!sPropertyMap.containsKey(propertyName)) { + if (!sPropertyIndexMap.containsKey(propertyName)) { return -1; } else { - return sPropertyMap.get(propertyName); + return sPropertyIndexMap.get(propertyName); } } @@ -1784,6 +2073,7 @@ public class VectorDrawable extends Drawable { abstract boolean onStateChange(int[] state); abstract boolean isStateful(); abstract int getNativeSize(); + abstract Property getProperty(String propertyName); } private static native long nCreateTree(long rootGroupPtr);