Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7da4903b authored by Doris Liu's avatar Doris Liu Committed by android-build-merger
Browse files

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

am: ea06280b

Change-Id: I8d5a26b50f6737964047d06e5e27abbdc4ba6381
parents 124a7c63 ea06280b
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -1095,8 +1095,12 @@ public class PropertyValuesHolder implements Cloneable {
        }
        // TODO: We need a better way to get data out of keyframes.
        if (mKeyframes instanceof PathKeyframes.FloatKeyframesBase
                || mKeyframes instanceof PathKeyframes.IntKeyframesBase) {
            // property values will animate based on external data source (e.g. Path)
                || mKeyframes instanceof PathKeyframes.IntKeyframesBase
                || (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() {
                @Override
                public Object getValueAtFraction(float fraction) {
@@ -1108,6 +1112,13 @@ public class PropertyValuesHolder implements Cloneable {
        }
    }

    /**
     * @hide
     */
    public Class getValueType() {
        return mValueType;
    }

    @Override
    public String toString() {
        return mPropertyName + ": " + mKeyframes.toString();
+3 −0
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ import android.view.Choreographer;
@HasNativeInterpolator
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 final float mLut[];

@@ -47,6 +49,7 @@ public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeI
        int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
        // We need 2 frame values as the minimal.
        int numAnimFrames = Math.max(2, (int) Math.ceil(((double) duration) / animIntervalMs));
        numAnimFrames = Math.min(numAnimFrames, MAX_SAMPLE_POINTS);
        float values[] = new float[numAnimFrames];
        float lastFrame = numAnimFrames - 1;
        for (int i = 0; i < numAnimFrames; i++) {
+15 −4
Original line number Diff line number Diff line
@@ -142,14 +142,24 @@ static jlong createRootAlphaPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jf
            startValue, endValue);
    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) {

    jfloat* propertyData = env->GetFloatArrayElements(srcData, nullptr);
    PropertyValuesHolder* holder = reinterpret_cast<PropertyValuesHolder*>(propertyHolderPtr);
    PropertyValuesHolderImpl<float>* holder =
            reinterpret_cast<PropertyValuesHolderImpl<float>*>(propertyHolderPtr);
    holder->setPropertyDataSource(propertyData, length);
    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) {
    PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
    AnimationListener* listener = createAnimationListener(env, finishListener, id);
@@ -181,7 +191,8 @@ static const JNINativeMethod gMethods[] = {
    {"nCreatePathColorPropertyHolder", "!(JIII)J", (void*)createPathColorPropertyHolder},
    {"nCreatePathPropertyHolder", "!(JIFF)J", (void*)createPathPropertyHolder},
    {"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},
    {"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimatorRT;I)V", (void*)reverse},
    {"nEnd", "!(J)V", (void*)end},
+113 −48
Original line number Diff line number Diff line
@@ -395,7 +395,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
                            // The animator here could be ObjectAnimator or AnimatorSet.
                            final Animator animator = AnimatorInflater.loadAnimator(
                                    res, theme, animResId, pathErrorScale);
                            updateAnimatorProperty(animator, target, state.mVectorDrawable);
                            updateAnimatorProperty(animator, target, state.mVectorDrawable,
                                    state.mShouldIgnoreInvalidAnim);
                            state.addTargetAnimator(target, animator);
                        } else {
                            // 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,
            VectorDrawable vectorDrawable) {
            VectorDrawable vectorDrawable, boolean ignoreInvalidAnim) {
        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
@@ -438,14 +439,33 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
                            .getProperty(propertyName);
                }
                if (property != null) {
                    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) {
            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;
        }
    }

    /**
@@ -496,6 +516,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
        @Config int mChangingConfigurations;
        VectorDrawable mVectorDrawable;

        private final boolean mShouldIgnoreInvalidAnim;

        /** Animators that require a theme before inflation. */
        ArrayList<PendingAnimator> mPendingAnims;

@@ -507,6 +529,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {

        public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy,
                Callback owner, Resources res) {
            mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
            if (copy != null) {
                mChangingConfigurations = copy.mChangingConfigurations;

@@ -651,7 +674,8 @@ 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 animator = pendingAnimator.newInstance(res, t);
                    updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable);
                    updateAnimatorProperty(animator, pendingAnimator.target, mVectorDrawable,
                            mShouldIgnoreInvalidAnim);
                    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 RESET_ANIMATION = 3;
        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 final LongArray mStartDelays = new LongArray();
        private PropertyValuesHolder.PropertyValues mTmpValues =
@@ -1027,8 +1054,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
        private boolean mInitialized = false;
        private boolean mIsReversible = false;
        private boolean mIsInfinite = false;
        // This needs to be set before parsing starts.
        private boolean mShouldIgnoreInvalidAnim;
        // TODO: Consider using NativeAllocationRegistery to track native allocation
        private final VirtualRefBasePtr mSetRefBasePtr;
        private WeakReference<RenderNode> mLastSeenTarget = null;
@@ -1051,7 +1076,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
                throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
                        "re-initialized");
            }
            mShouldIgnoreInvalidAnim = shouldIgnoreInvalidAnimation();
            parseAnimatorSet(set, 0);
            long vectorDrawableTreePtr = mDrawable.mAnimatedVectorState.mVectorDrawable
                    .getNativeTree();
@@ -1115,7 +1139,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
                    }  else if (target instanceof VectorDrawable.VFullPath) {
                        createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
                                startTime);
                    } else if (!mShouldIgnoreInvalidAnim) {
                    } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
                        throw new IllegalArgumentException("ClipPath only supports PathData " +
                                "property");
                    }
@@ -1124,7 +1148,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
            } else if (target instanceof VectorDrawable.VectorDrawableState) {
                createRTAnimatorForRootGroup(values, animator,
                        (VectorDrawable.VectorDrawableState) target, startTime);
            } else if (!mShouldIgnoreInvalidAnim) {
            } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
                // Should never get here
                throw new UnsupportedOperationException("Target should be either VGroup, VPath, " +
                        "or ConstantState, " + target == null ? "Null target" : target.getClass() +
@@ -1159,8 +1183,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
                long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId,
                        (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
                if (mTmpValues.dataSource != null) {
                    float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
                            .getDuration());
                    float[] dataPoints = createFloatDataPoints(mTmpValues.dataSource,
                            animator.getDuration());
                    nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
                }
                createNativeChildAnimator(propertyPtr, startTime, animator);
@@ -1187,7 +1211,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
            long nativePtr = target.getNativePtr();
            if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
                if (propertyId < 0) {
                    if (mShouldIgnoreInvalidAnim) {
                    if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
                        return;
                    } else {
                        throw new IllegalArgumentException("Property: " + mTmpValues.propertyName
@@ -1196,12 +1220,24 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
                }
                propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
                        (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) {
                propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
                        (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 {
                if (mShouldIgnoreInvalidAnim) {
                if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
                    return;
                } else {
                    throw new UnsupportedOperationException("Unsupported type: " +
@@ -1209,11 +1245,6 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
                            "supported for Paths.");
                }
            }
            if (mTmpValues.dataSource != null) {
                float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
                        .getDuration());
                nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
            }
            createNativeChildAnimator(propertyPtr, startTime, animator);
        }

@@ -1222,7 +1253,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
                long startTime) {
            long nativePtr = target.getNativeRenderer();
            if (!animator.getPropertyName().equals("alpha")) {
                    if (mShouldIgnoreInvalidAnim) {
                if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
                    return;
                } else {
                    throw new UnsupportedOperationException("Only alpha is supported for root "
@@ -1240,26 +1271,47 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
                }
            }
            if (startValue == null && endValue == null) {
                    if (mShouldIgnoreInvalidAnim) {
                if (mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) {
                    return;
                } else {
                    throw new UnsupportedOperationException("No alpha values are specified");
                }
            }
            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.
        // e.g. translateX and translateY can animate along a Path, at any fraction in [0, 1]
        // 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
        // in native.
        private static float[] createDataPoints(
        private static float[] createFloatDataPoints(
                PropertyValuesHolder.PropertyValues.DataSource dataSource, long duration) {
            long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
            int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
            int numAnimFrames = (int) Math.ceil(((double) duration) / animIntervalMs);
            int numAnimFrames = getFrameCount(duration);
            float values[] = new float[numAnimFrames];
            float lastFrame = numAnimFrames - 1;
            for (int i = 0; i < numAnimFrames; i++) {
@@ -1269,6 +1321,18 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
            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,
                                               ObjectAnimator animator) {
            long duration = animator.getDuration();
@@ -1545,6 +1609,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
    private static native long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
            float endValue);
    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 nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
    private static native void nEnd(long animatorSetPtr);
+36 −29
Original line number Diff line number Diff line
@@ -25,7 +25,27 @@ namespace uirenderer {

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) {
        LOG_ALWAYS_FATAL("No data source is defined");
        return 0;
@@ -41,57 +61,44 @@ float PropertyValuesHolder::getValueFromData(float fraction) {
    int lowIndex = floor(fraction);
    fraction -= lowIndex;

    float value = mDataSource[lowIndex] * (1.0f - fraction)
            + mDataSource[lowIndex + 1] * fraction;
    T value;
    mEvaluator->evaluate(&value, mDataSource[lowIndex], mDataSource[lowIndex + 1], fraction);
    return value;
}

void GroupPropertyValuesHolder::setFraction(float fraction) {
    float animatedValue;
template<typename T>
const T PropertyValuesHolderImpl<T>::calculateAnimatedValue(float fraction) const {
    if (mDataSource.size() > 0) {
        animatedValue = getValueFromData(fraction);
        return getValueFromData(fraction);
    } else {
        animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
    }
    mGroup->mutateProperties()->setPropertyValue(mPropertyId, animatedValue);
        T value;
        mEvaluator->evaluate(&value, mStartValue, mEndValue, fraction);
        return value;
    }

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 GroupPropertyValuesHolder::setFraction(float fraction) {
    float animatedValue = calculateAnimatedValue(fraction);
    mGroup->mutateProperties()->setPropertyValue(mPropertyId, animatedValue);
}

void FullPathColorPropertyValuesHolder::setFraction(float fraction) {
    SkColor animatedValue = interpolateColors(mStartValue, mEndValue, fraction);
    SkColor animatedValue = calculateAnimatedValue(fraction);
    mFullPath->mutateProperties()->setColorPropertyValue(mPropertyId, animatedValue);
}

void FullPathPropertyValuesHolder::setFraction(float fraction) {
    float animatedValue;
    if (mDataSource.size() > 0) {
        animatedValue = getValueFromData(fraction);
    } else {
        animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
    }
    float animatedValue = calculateAnimatedValue(fraction);
    mFullPath->mutateProperties()->setPropertyValue(mPropertyId, animatedValue);
}

void PathDataPropertyValuesHolder::setFraction(float fraction) {
    VectorDrawableUtils::interpolatePaths(&mPathData, mStartValue, mEndValue, fraction);
    mEvaluator->evaluate(&mPathData, mStartValue, mEndValue, fraction);
    mPath->mutateProperties()->setData(mPathData);
}

void RootAlphaPropertyValuesHolder::setFraction(float fraction) {
    float animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
    float animatedValue = calculateAnimatedValue(fraction);
    mTree->mutateProperties()->setRootAlpha(animatedValue);
}

Loading