Revert "Revert "VectorDrawable native rendering - Step 4 of MANY""
This reverts commit 5a11e8d0ba21624025b89ac63bbd18befa55be0e.
Change-Id: I7a48b59c4f930dad65ddc8590c25a12636244ea2
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 1ab55dd..980329f 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -1030,6 +1030,20 @@
}
}
+ /**
+ * @hide
+ * TODO: For animatorSet defined in XML, we can use a flag to indicate what the play order
+ * if defined (i.e. sequential or together), then we can use the flag instead of calculate
+ * dynamically.
+ * @return whether all the animators in the set are supposed to play together
+ */
+ public boolean shouldPlayTogether() {
+ updateAnimatorsDuration();
+ createDependencyGraph();
+ // All the child nodes are set out to play right after the delay animation
+ return mRootNode.mChildNodes.size() == mNodes.size() - 1;
+ }
+
@Override
public long getTotalDuration() {
updateAnimatorsDuration();
diff --git a/core/java/android/animation/PathKeyframes.java b/core/java/android/animation/PathKeyframes.java
index 2a47b68..8230ac5 100644
--- a/core/java/android/animation/PathKeyframes.java
+++ b/core/java/android/animation/PathKeyframes.java
@@ -231,7 +231,7 @@
}
}
- private abstract static class IntKeyframesBase extends SimpleKeyframes implements IntKeyframes {
+ abstract static class IntKeyframesBase extends SimpleKeyframes implements IntKeyframes {
@Override
public Class getType() {
return Integer.class;
@@ -243,7 +243,7 @@
}
}
- private abstract static class FloatKeyframesBase extends SimpleKeyframes
+ abstract static class FloatKeyframesBase extends SimpleKeyframes
implements FloatKeyframes {
@Override
public Class getType() {
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index e993cca..6ba5b96 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -21,6 +21,7 @@
import android.util.FloatProperty;
import android.util.IntProperty;
import android.util.Log;
+import android.util.PathParser;
import android.util.Property;
import java.lang.reflect.InvocationTargetException;
@@ -1046,6 +1047,43 @@
return mAnimatedValue;
}
+ /**
+ * PropertyValuesHolder is Animators use to hold internal animation related data.
+ * Therefore, in order to replicate the animation behavior, we need to get data out of
+ * PropertyValuesHolder.
+ * @hide
+ */
+ public void getPropertyValues(PropertyValues values) {
+ init();
+ values.propertyName = mPropertyName;
+ values.type = mValueType;
+ values.startValue = mKeyframes.getValue(0);
+ if (values.startValue instanceof PathParser.PathData) {
+ // PathData evaluator returns the same mutable PathData object when query fraction,
+ // so we have to make a copy here.
+ values.startValue = new PathParser.PathData((PathParser.PathData) values.startValue);
+ }
+ values.endValue = mKeyframes.getValue(1);
+ if (values.endValue instanceof PathParser.PathData) {
+ // PathData evaluator returns the same mutable PathData object when query fraction,
+ // so we have to make a copy here.
+ values.endValue = new PathParser.PathData((PathParser.PathData) values.endValue);
+ }
+ // 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)
+ values.dataSource = new PropertyValues.DataSource() {
+ @Override
+ public Object getValueAtFraction(float fraction) {
+ return mKeyframes.getValue(fraction);
+ }
+ };
+ } else {
+ values.dataSource = null;
+ }
+ }
+
@Override
public String toString() {
return mPropertyName + ": " + mKeyframes.toString();
@@ -1601,6 +1639,24 @@
}
};
+ /**
+ * @hide
+ */
+ public static class PropertyValues {
+ public String propertyName;
+ public Class type;
+ public Object startValue;
+ public Object endValue;
+ public DataSource dataSource = null;
+ public interface DataSource {
+ Object getValueAtFraction(float fraction);
+ }
+ public String toString() {
+ return ("property name: " + propertyName + ", type: " + type + ", startValue: "
+ + startValue.toString() + ", endValue: " + endValue.toString());
+ }
+ }
+
native static private long nGetIntMethod(Class targetClass, String methodName);
native static private long nGetFloatMethod(Class targetClass, String methodName);
native static private long nGetMultipleIntMethod(Class targetClass, String methodName,
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 78d5bcd..29a72fd 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -104,6 +104,7 @@
}
super.finalize();
}
+
}
/**
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 3122c0b..7017ff5 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -22,6 +22,7 @@
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
/**
* <p>A display list records a series of graphics related operations and can replay
@@ -774,6 +775,14 @@
mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this);
}
+ public void addAnimator(AnimatedVectorDrawable.VectorDrawableAnimator animatorSet) {
+ if (mOwningView == null || mOwningView.mAttachInfo == null) {
+ throw new IllegalStateException("Cannot start this animator on a detached view!");
+ }
+ nAddAnimator(mNativeRenderNode, animatorSet.getAnimatorNativePtr());
+ mOwningView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(this);
+ }
+
public void endAllAnimators() {
nEndAllAnimators(mNativeRenderNode);
}
diff --git a/core/java/android/view/RenderNodeAnimatorSetHelper.java b/core/java/android/view/RenderNodeAnimatorSetHelper.java
new file mode 100644
index 0000000..ba592d29
--- /dev/null
+++ b/core/java/android/view/RenderNodeAnimatorSetHelper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+package android.view;
+
+import android.animation.TimeInterpolator;
+import com.android.internal.view.animation.FallbackLUTInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
+/**
+ * This is a helper class to get access to methods and fields needed for RenderNodeAnimatorSet
+ * that are internal or package private to android.view package.
+ *
+ * @hide
+ */
+public class RenderNodeAnimatorSetHelper {
+
+ public static RenderNode getTarget(DisplayListCanvas recordingCanvas) {
+ return recordingCanvas.mNode;
+ }
+
+ public static long createNativeInterpolator(TimeInterpolator interpolator, long
+ duration) {
+ if (interpolator == null) {
+ // create LinearInterpolator
+ return NativeInterpolatorFactoryHelper.createLinearInterpolator();
+ } else if (RenderNodeAnimator.isNativeInterpolator(interpolator)) {
+ return ((NativeInterpolatorFactory)interpolator).createNativeInterpolator();
+ } else {
+ return FallbackLUTInterpolator.createNativeInterpolator(interpolator, duration);
+ }
+ }
+
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index ffa3fa6..1b6b53a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -51,6 +51,7 @@
android_database_SQLiteConnection.cpp \
android_database_SQLiteGlobal.cpp \
android_database_SQLiteDebug.cpp \
+ android_graphics_drawable_AnimatedVectorDrawable.cpp \
android_graphics_drawable_VectorDrawable.cpp \
android_view_DisplayEventReceiver.cpp \
android_view_DisplayListCanvas.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2e45f8c..223fc1a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -131,6 +131,7 @@
extern int register_android_graphics_Region(JNIEnv* env);
extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
extern int register_android_graphics_Xfermode(JNIEnv* env);
+extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
@@ -1321,6 +1322,7 @@
REG_JNI(register_android_graphics_Typeface),
REG_JNI(register_android_graphics_Xfermode),
REG_JNI(register_android_graphics_YuvImage),
+ REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
REG_JNI(register_android_graphics_drawable_VectorDrawable),
REG_JNI(register_android_graphics_pdf_PdfDocument),
REG_JNI(register_android_graphics_pdf_PdfEditor),
diff --git a/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
new file mode 100644
index 0000000..7a3c598
--- /dev/null
+++ b/core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#define LOG_TAG "OpenGLRenderer"
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+#include "log/log.h"
+
+#include "Animator.h"
+#include "Interpolator.h"
+#include "PropertyValuesAnimatorSet.h"
+#include "PropertyValuesHolder.h"
+#include "VectorDrawable.h"
+
+namespace android {
+using namespace uirenderer;
+using namespace VectorDrawable;
+
+static struct {
+ jclass clazz;
+ jmethodID callOnFinished;
+} gVectorDrawableAnimatorClassInfo;
+
+static JNIEnv* getEnv(JavaVM* vm) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ return 0;
+ }
+ return env;
+}
+
+static AnimationListener* createAnimationListener(JNIEnv* env, jobject finishListener) {
+ class AnimationListenerBridge : public AnimationListener {
+ public:
+ AnimationListenerBridge(JNIEnv* env, jobject finishListener) {
+ mFinishListener = env->NewGlobalRef(finishListener);
+ env->GetJavaVM(&mJvm);
+ }
+
+ virtual ~AnimationListenerBridge() {
+ if (mFinishListener) {
+ onAnimationFinished(NULL);
+ }
+ }
+
+ virtual void onAnimationFinished(BaseRenderNodeAnimator*) {
+ LOG_ALWAYS_FATAL_IF(!mFinishListener, "Finished listener twice?");
+ JNIEnv* env = getEnv(mJvm);
+ env->CallStaticVoidMethod(
+ gVectorDrawableAnimatorClassInfo.clazz,
+ gVectorDrawableAnimatorClassInfo.callOnFinished,
+ mFinishListener);
+ releaseJavaObject();
+ }
+
+ private:
+ void releaseJavaObject() {
+ JNIEnv* env = getEnv(mJvm);
+ env->DeleteGlobalRef(mFinishListener);
+ mFinishListener = NULL;
+ }
+
+ JavaVM* mJvm;
+ jobject mFinishListener;
+ };
+ return new AnimationListenerBridge(env, finishListener);
+}
+
+static void addAnimator(JNIEnv*, jobject, jlong animatorSetPtr, jlong propertyHolderPtr,
+ jlong interpolatorPtr, jlong startDelay, jlong duration, jint repeatCount) {
+ PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+ PropertyValuesHolder* holder = reinterpret_cast<PropertyValuesHolder*>(propertyHolderPtr);
+ Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr);
+ set->addPropertyAnimator(holder, interpolator, startDelay, duration, repeatCount);
+}
+
+static jlong createAnimatorSet(JNIEnv*, jobject) {
+ PropertyValuesAnimatorSet* animatorSet = new PropertyValuesAnimatorSet();
+ return reinterpret_cast<jlong>(animatorSet);
+}
+
+static jlong createGroupPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
+ jfloat startValue, jfloat endValue) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(nativePtr);
+ GroupPropertyValuesHolder* newHolder = new GroupPropertyValuesHolder(group, propertyId,
+ startValue, endValue);
+ return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createPathDataPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jlong startValuePtr,
+ jlong endValuePtr) {
+ VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(nativePtr);
+ PathData* startData = reinterpret_cast<PathData*>(startValuePtr);
+ PathData* endData = reinterpret_cast<PathData*>(endValuePtr);
+ PathDataPropertyValuesHolder* newHolder = new PathDataPropertyValuesHolder(path,
+ startData, endData);
+ return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createPathColorPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
+ int startValue, jint endValue) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(nativePtr);
+ FullPathColorPropertyValuesHolder* newHolder = new FullPathColorPropertyValuesHolder(fullPath,
+ propertyId, startValue, endValue);
+ return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createPathPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jint propertyId,
+ float startValue, jfloat endValue) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(nativePtr);
+ FullPathPropertyValuesHolder* newHolder = new FullPathPropertyValuesHolder(fullPath,
+ propertyId, startValue, endValue);
+ return reinterpret_cast<jlong>(newHolder);
+}
+
+static jlong createRootAlphaPropertyHolder(JNIEnv*, jobject, jlong nativePtr, jfloat startValue,
+ float endValue) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(nativePtr);
+ RootAlphaPropertyValuesHolder* newHolder = new RootAlphaPropertyValuesHolder(tree,
+ startValue, endValue);
+ return reinterpret_cast<jlong>(newHolder);
+}
+static void setPropertyHolderData(JNIEnv* env, jobject, jlong propertyHolderPtr,
+ jfloatArray srcData, jint length) {
+
+ jfloat* propertyData = env->GetFloatArrayElements(srcData, nullptr);
+ PropertyValuesHolder* holder = reinterpret_cast<PropertyValuesHolder*>(propertyHolderPtr);
+ holder->setPropertyDataSource(propertyData, length);
+ env->ReleaseFloatArrayElements(srcData, propertyData, JNI_ABORT);
+}
+static void start(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener) {
+ PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+ // TODO: keep a ref count in finish listener
+ AnimationListener* listener = createAnimationListener(env, finishListener);
+ set->start(listener);
+}
+
+static void reverse(JNIEnv* env, jobject, jlong animatorSetPtr, jobject finishListener) {
+ // TODO: implement reverse
+}
+
+static void end(JNIEnv*, jobject, jlong animatorSetPtr) {
+ PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+ set->end();
+}
+
+static void reset(JNIEnv*, jobject, jlong animatorSetPtr) {
+ PropertyValuesAnimatorSet* set = reinterpret_cast<PropertyValuesAnimatorSet*>(animatorSetPtr);
+ set->reset();
+}
+
+static const JNINativeMethod gMethods[] = {
+ {"nCreateAnimatorSet", "()J", (void*)createAnimatorSet},
+ {"nAddAnimator", "(JJJJJI)V", (void*)addAnimator},
+ {"nCreateGroupPropertyHolder", "!(JIFF)J", (void*)createGroupPropertyHolder},
+ {"nCreatePathDataPropertyHolder", "!(JJJ)J", (void*)createPathDataPropertyHolder},
+ {"nCreatePathColorPropertyHolder", "!(JIII)J", (void*)createPathColorPropertyHolder},
+ {"nCreatePathPropertyHolder", "!(JIFF)J", (void*)createPathPropertyHolder},
+ {"nCreateRootAlphaPropertyHolder", "!(JFF)J", (void*)createRootAlphaPropertyHolder},
+ {"nSetPropertyHolderData", "(J[FI)V", (void*)setPropertyHolderData},
+ {"nStart", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;)V", (void*)start},
+ {"nReverse", "(JLandroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;)V", (void*)reverse},
+ {"nEnd", "!(J)V", (void*)end},
+ {"nReset", "!(J)V", (void*)reset},
+};
+
+const char* const kClassPathName = "android/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator";
+int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env) {
+ gVectorDrawableAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName);
+ gVectorDrawableAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env,
+ gVectorDrawableAnimatorClassInfo.clazz);
+
+ gVectorDrawableAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie(
+ env, gVectorDrawableAnimatorClassInfo.clazz, "callOnFinished",
+ "(Landroid/graphics/drawable/AnimatedVectorDrawable$VectorDrawableAnimator;)V");
+ return RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedVectorDrawable",
+ gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index 563ec8b..e882876 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -32,11 +32,6 @@
return reinterpret_cast<jlong>(tree);
}
-static void deleteTree(JNIEnv*, jobject, jlong treePtr) {
- VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
- delete tree;
-}
-
static void setTreeViewportSize(JNIEnv*, jobject, jlong treePtr,
jfloat viewportWidth, jfloat viewportHeight) {
VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
@@ -333,7 +328,6 @@
static const JNINativeMethod gMethods[] = {
{"nCreateRenderer", "!(J)J", (void*)createTree},
- {"nDestroyRenderer", "!(J)V", (void*)deleteTree},
{"nSetRendererViewportSize", "!(JFF)V", (void*)setTreeViewportSize},
{"nSetRootAlpha", "!(JF)Z", (void*)setRootAlpha},
{"nGetRootAlpha", "!(J)F", (void*)getRootAlpha},
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index c48c371..0a3e27e 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -19,6 +19,10 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.Animator.AnimatorListener;
+import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.animation.ObjectAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
@@ -34,14 +38,23 @@
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.LongArray;
+import android.util.PathParser;
+import android.util.TimeUtils;
+import android.view.Choreographer;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.view.RenderNodeAnimatorSetHelper;
import android.view.View;
import com.android.internal.R;
+import com.android.internal.util.VirtualRefBasePtr;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -138,7 +151,7 @@
private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
/** Local, mutable animator set. */
- private final AnimatorSet mAnimatorSet = new AnimatorSet();
+ private final VectorDrawableAnimator mAnimatorSet = new VectorDrawableAnimator();
/**
* The resources against which this drawable was created. Used to attempt
@@ -200,6 +213,9 @@
@Override
public void draw(Canvas canvas) {
+ if (canvas.isHardwareAccelerated()) {
+ mAnimatorSet.recordLastSeenTarget((DisplayListCanvas) canvas);
+ }
mAnimatedVectorState.mVectorDrawable.draw(canvas);
if (isStarted()) {
invalidateSelf();
@@ -582,9 +598,8 @@
* Resets the AnimatedVectorDrawable to the start state as specified in the animators.
*/
public void reset() {
- // TODO: Use reverse or seek to implement reset, when AnimatorSet supports them.
- start();
- mAnimatorSet.cancel();
+ mAnimatorSet.reset();
+ invalidateSelf();
}
@Override
@@ -603,8 +618,12 @@
@NonNull
private void ensureAnimatorSet() {
if (!mHasAnimatorSet) {
- mAnimatedVectorState.prepareLocalAnimators(mAnimatorSet, mRes);
+ // TODO: Skip the AnimatorSet creation and init the VectorDrawableAnimator directly
+ // with a list of LocalAnimators.
+ AnimatorSet set = new AnimatorSet();
+ mAnimatedVectorState.prepareLocalAnimators(set, mRes);
mHasAnimatorSet = true;
+ mAnimatorSet.initWithAnimatorSet(set);
mRes = null;
}
}
@@ -694,13 +713,13 @@
}
};
}
- mAnimatorSet.addListener(mAnimatorListener);
+ mAnimatorSet.setListener(mAnimatorListener);
}
// A helper function to clean up the animator listener in the mAnimatorSet.
private void removeAnimatorSetListener() {
if (mAnimatorListener != null) {
- mAnimatorSet.removeListener(mAnimatorListener);
+ mAnimatorSet.removeListener();
mAnimatorListener = null;
}
}
@@ -729,4 +748,407 @@
mAnimationCallbacks.clear();
}
+
+ /**
+ * @hide
+ */
+ public static class VectorDrawableAnimator {
+ private AnimatorListener mListener = null;
+ private final LongArray mStartDelays = new LongArray();
+ private PropertyValuesHolder.PropertyValues mTmpValues =
+ new PropertyValuesHolder.PropertyValues();
+ private long mSetPtr = 0;
+ private boolean mContainsSequentialAnimators = false;
+ private boolean mStarted = false;
+ private boolean mInitialized = false;
+ private boolean mAnimationPending = false;
+ private boolean mIsReversible = false;
+ // TODO: Consider using NativeAllocationRegistery to track native allocation
+ private final VirtualRefBasePtr mSetRefBasePtr;
+ private WeakReference<RenderNode> mTarget = null;
+ private WeakReference<RenderNode> mLastSeenTarget = null;
+
+
+ VectorDrawableAnimator() {
+ mSetPtr = nCreateAnimatorSet();
+ // Increment ref count on native AnimatorSet, so it doesn't get released before Java
+ // side is done using it.
+ mSetRefBasePtr = new VirtualRefBasePtr(mSetPtr);
+ }
+
+ private void initWithAnimatorSet(AnimatorSet set) {
+ if (mInitialized) {
+ // Already initialized
+ throw new UnsupportedOperationException("VectorDrawableAnimator cannot be " +
+ "re-initialized");
+ }
+ parseAnimatorSet(set, 0);
+ mInitialized = true;
+
+ // Check reversible.
+ if (mContainsSequentialAnimators) {
+ mIsReversible = false;
+ } else {
+ // Check if there's any start delay set on child
+ for (int i = 0; i < mStartDelays.size(); i++) {
+ if (mStartDelays.get(i) > 0) {
+ mIsReversible = false;
+ return;
+ }
+ }
+ }
+ mIsReversible = true;
+ }
+
+ private void parseAnimatorSet(AnimatorSet set, long startTime) {
+ ArrayList<Animator> animators = set.getChildAnimations();
+
+ boolean playTogether = set.shouldPlayTogether();
+ // Convert AnimatorSet to VectorDrawableAnimator
+ for (int i = 0; i < animators.size(); i++) {
+ Animator animator = animators.get(i);
+ // Here we only support ObjectAnimator
+ if (animator instanceof AnimatorSet) {
+ parseAnimatorSet((AnimatorSet) animator, startTime);
+ } else if (animator instanceof ObjectAnimator) {
+ createRTAnimator((ObjectAnimator) animator, startTime);
+ } // ignore ValueAnimators and others because they don't directly modify VD
+ // therefore will be useless to AVD.
+
+ if (!playTogether) {
+ // Assume not play together means play sequentially
+ startTime += animator.getTotalDuration();
+ mContainsSequentialAnimators = true;
+ }
+ }
+ }
+
+ // TODO: This method reads animation data from already parsed Animators. We need to move
+ // this step further up the chain in the parser to avoid the detour.
+ private void createRTAnimator(ObjectAnimator animator, long startTime) {
+ PropertyValuesHolder[] values = animator.getValues();
+ Object target = animator.getTarget();
+ if (target instanceof VectorDrawable.VGroup) {
+ createRTAnimatorForGroup(values, animator, (VectorDrawable.VGroup) target,
+ startTime);
+ } else if (target instanceof VectorDrawable.VPath) {
+ for (int i = 0; i < values.length; i++) {
+ values[i].getPropertyValues(mTmpValues);
+ if (mTmpValues.endValue instanceof PathParser.PathData &&
+ mTmpValues.propertyName.equals("pathData")) {
+ createRTAnimatorForPath(animator, (VectorDrawable.VPath) target,
+ startTime);
+ } else if (target instanceof VectorDrawable.VFullPath) {
+ createRTAnimatorForFullPath(animator, (VectorDrawable.VFullPath) target,
+ startTime);
+ } else {
+ throw new IllegalArgumentException("ClipPath only supports PathData " +
+ "property");
+ }
+
+ }
+ } else if (target instanceof VectorDrawable.VectorDrawableState) {
+ createRTAnimatorForRootGroup(values, animator,
+ (VectorDrawable.VectorDrawableState) target, startTime);
+ } else {
+ // Should never get here
+ throw new UnsupportedOperationException("Target should be either VGroup, VPath, " +
+ "or ConstantState, " + target.getClass() + " is not supported");
+ }
+ }
+
+ private void createRTAnimatorForGroup(PropertyValuesHolder[] values,
+ ObjectAnimator animator, VectorDrawable.VGroup target,
+ long startTime) {
+
+ long nativePtr = target.getNativePtr();
+ int propertyId;
+ for (int i = 0; i < values.length; i++) {
+ // TODO: We need to support the rare case in AVD where no start value is provided
+ values[i].getPropertyValues(mTmpValues);
+ propertyId = VectorDrawable.VGroup.getPropertyIndex(mTmpValues.propertyName);
+ if (mTmpValues.type != Float.class && mTmpValues.type != float.class) {
+ if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+ Log.e(LOGTAG, "Unsupported type: " +
+ mTmpValues.type + ". Only float value is supported for Groups.");
+ }
+ continue;
+ }
+ if (propertyId < 0) {
+ if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+ Log.e(LOGTAG, "Unsupported property: " +
+ mTmpValues.propertyName + " for Vector Drawable Group");
+ }
+ continue;
+ }
+ long propertyPtr = nCreateGroupPropertyHolder(nativePtr, propertyId,
+ (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
+ if (mTmpValues.dataSource != null) {
+ float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
+ .getDuration());
+ nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
+ }
+ createNativeChildAnimator(propertyPtr, startTime, animator);
+ }
+ }
+ private void createRTAnimatorForPath( ObjectAnimator animator, VectorDrawable.VPath target,
+ long startTime) {
+
+ long nativePtr = target.getNativePtr();
+ long startPathDataPtr = ((PathParser.PathData) mTmpValues.startValue)
+ .getNativePtr();
+ long endPathDataPtr = ((PathParser.PathData) mTmpValues.endValue)
+ .getNativePtr();
+ long propertyPtr = nCreatePathDataPropertyHolder(nativePtr, startPathDataPtr,
+ endPathDataPtr);
+ createNativeChildAnimator(propertyPtr, startTime, animator);
+ }
+
+ private void createRTAnimatorForFullPath(ObjectAnimator animator,
+ VectorDrawable.VFullPath target, long startTime) {
+
+ int propertyId = target.getPropertyIndex(mTmpValues.propertyName);
+ long propertyPtr;
+ long nativePtr = target.getNativePtr();
+ if (mTmpValues.type == Float.class || mTmpValues.type == float.class) {
+ if (propertyId < 0) {
+ throw new IllegalArgumentException("Property: " + mTmpValues
+ .propertyName + " is not supported for FullPath");
+ }
+ propertyPtr = nCreatePathPropertyHolder(nativePtr, propertyId,
+ (Float) mTmpValues.startValue, (Float) mTmpValues.endValue);
+
+ } else if (mTmpValues.type == Integer.class || mTmpValues.type == int.class) {
+ propertyPtr = nCreatePathColorPropertyHolder(nativePtr, propertyId,
+ (Integer) mTmpValues.startValue, (Integer) mTmpValues.endValue);
+ } else {
+ throw new UnsupportedOperationException("Unsupported type: " +
+ mTmpValues.type + ". Only float, int or PathData value is " +
+ "supported for Paths.");
+ }
+ if (mTmpValues.dataSource != null) {
+ float[] dataPoints = createDataPoints(mTmpValues.dataSource, animator
+ .getDuration());
+ nSetPropertyHolderData(propertyPtr, dataPoints, dataPoints.length);
+ }
+ createNativeChildAnimator(propertyPtr, startTime, animator);
+ }
+
+ private void createRTAnimatorForRootGroup(PropertyValuesHolder[] values,
+ ObjectAnimator animator, VectorDrawable.VectorDrawableState target,
+ long startTime) {
+ long nativePtr = target.getNativeRenderer();
+ if (!animator.getPropertyName().equals("alpha")) {
+ throw new UnsupportedOperationException("Only alpha is supported for root " +
+ "group");
+ }
+ Float startValue = null;
+ Float endValue = null;
+ for (int i = 0; i < values.length; i++) {
+ values[i].getPropertyValues(mTmpValues);
+ if (mTmpValues.propertyName.equals("alpha")) {
+ startValue = (Float) mTmpValues.startValue;
+ endValue = (Float) mTmpValues.endValue;
+ break;
+ }
+ }
+ if (startValue == null && endValue == null) {
+ throw new UnsupportedOperationException("No alpha values are specified");
+ }
+ long propertyPtr = nCreateRootAlphaPropertyHolder(nativePtr, startValue, endValue);
+ createNativeChildAnimator(propertyPtr, startTime, animator);
+ }
+
+ // 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(
+ 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);
+ float values[] = new float[numAnimFrames];
+ float lastFrame = numAnimFrames - 1;
+ for (int i = 0; i < numAnimFrames; i++) {
+ float fraction = i / lastFrame;
+ values[i] = (Float) dataSource.getValueAtFraction(fraction);
+ }
+ return values;
+ }
+
+ private void createNativeChildAnimator(long propertyPtr, long extraDelay,
+ ObjectAnimator animator) {
+ long duration = animator.getDuration();
+ int repeatCount = animator.getRepeatCount();
+ long startDelay = extraDelay + animator.getStartDelay();
+ TimeInterpolator interpolator = animator.getInterpolator();
+ long nativeInterpolator =
+ RenderNodeAnimatorSetHelper.createNativeInterpolator(interpolator, duration);
+
+ startDelay *= ValueAnimator.getDurationScale();
+ duration *= ValueAnimator.getDurationScale();
+
+ mStartDelays.add(startDelay);
+ nAddAnimator(mSetPtr, propertyPtr, nativeInterpolator, startDelay, duration,
+ repeatCount);
+ }
+
+ /**
+ * Holds a weak reference to the target that was last seen (through the DisplayListCanvas
+ * in the last draw call), so that when animator set needs to start, we can add the animator
+ * to the last seen RenderNode target and start right away.
+ */
+ protected void recordLastSeenTarget(DisplayListCanvas canvas) {
+ if (mAnimationPending) {
+ mLastSeenTarget = new WeakReference<RenderNode>(
+ RenderNodeAnimatorSetHelper.getTarget(canvas));
+ if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+ Log.d(LOGTAG, "Target is set in the next frame");
+ }
+ mAnimationPending = false;
+ start();
+ } else {
+ mLastSeenTarget = new WeakReference<RenderNode>(
+ RenderNodeAnimatorSetHelper.getTarget(canvas));
+ }
+
+ }
+
+ private boolean setTarget(RenderNode node) {
+ if (mTarget != null && mTarget.get() != null) {
+ // TODO: Maybe we want to support target change.
+ throw new IllegalStateException("Target already set!");
+ }
+
+ node.addAnimator(this);
+ mTarget = new WeakReference<RenderNode>(node);
+ return true;
+ }
+
+ private boolean useLastSeenTarget() {
+ if (mLastSeenTarget != null && mLastSeenTarget.get() != null) {
+ setTarget(mLastSeenTarget.get());
+ return true;
+ }
+ return false;
+ }
+
+ public void start() {
+ if (!mInitialized) {
+ return;
+ }
+
+ if (mStarted) {
+ return;
+ }
+
+ if (!useLastSeenTarget()) {
+ mAnimationPending = true;
+ return;
+ }
+
+ if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+ Log.d(LOGTAG, "Target is set. Starting VDAnimatorSet from java");
+ }
+
+ nStart(mSetPtr, this);
+ if (mListener != null) {
+ mListener.onAnimationStart(null);
+ }
+ mStarted = true;
+ }
+
+ public void end() {
+ if (mInitialized && mStarted) {
+ nEnd(mSetPtr);
+ onAnimationEnd();
+ }
+ }
+
+ void reset() {
+ if (!mInitialized) {
+ return;
+ }
+ // TODO: Need to implement reset.
+ Log.w(LOGTAG, "Reset is yet to be implemented");
+ nReset(mSetPtr);
+ }
+
+ // Current (imperfect) Java AnimatorSet cannot be reversed when the set contains sequential
+ // animators or when the animator set has a start delay
+ void reverse() {
+ if (!mIsReversible) {
+ return;
+ }
+ // TODO: Need to support reverse (non-public API)
+ Log.w(LOGTAG, "Reverse is yet to be implemented");
+ nReverse(mSetPtr, this);
+ }
+
+ public long getAnimatorNativePtr() {
+ return mSetPtr;
+ }
+
+ boolean canReverse() {
+ return mIsReversible;
+ }
+
+ boolean isStarted() {
+ return mStarted;
+ }
+
+ boolean isRunning() {
+ if (!mInitialized) {
+ return false;
+ }
+ return mStarted;
+ }
+
+ void setListener(AnimatorListener listener) {
+ mListener = listener;
+ }
+
+ void removeListener() {
+ mListener = null;
+ }
+
+ private void onAnimationEnd() {
+ mStarted = false;
+ if (mListener != null) {
+ mListener.onAnimationEnd(null);
+ }
+ mTarget = null;
+ }
+
+ // onFinished: should be called from native
+ private static void callOnFinished(VectorDrawableAnimator set) {
+ if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+ Log.d(LOGTAG, "on finished called from native");
+ }
+ set.onAnimationEnd();
+ }
+ }
+
+ private static native long nCreateAnimatorSet();
+ private static native void nAddAnimator(long setPtr, long propertyValuesHolder,
+ long nativeInterpolator, long startDelay, long duration, int repeatCount);
+
+ private static native long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
+ float startValue, float endValue);
+
+ private static native long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
+ long endValuePtr);
+ private static native long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
+ int startValue, int endValue);
+ private static native long nCreatePathPropertyHolder(long nativePtr, int propertyId,
+ float startValue, float endValue);
+ 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 nStart(long animatorSetPtr, VectorDrawableAnimator set);
+ private static native void nReverse(long animatorSetPtr, VectorDrawableAnimator set);
+ private static native void nEnd(long animatorSetPtr);
+ private static native void nReset(long animatorSetPtr);
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 1fc1b83..f4bbc8c 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -39,6 +39,7 @@
import android.util.Xml;
import com.android.internal.R;
+import com.android.internal.util.VirtualRefBasePtr;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -47,6 +48,7 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Stack;
/**
@@ -522,13 +524,13 @@
public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
- if (mVectorState.mRootGroup != null || mVectorState.mNativeRendererPtr != 0) {
+ if (mVectorState.mRootGroup != null || mVectorState.mNativeRendererRefBase != null) {
// This VD has been used to display other VD resource content, clean up.
mVectorState.mRootGroup = new VGroup();
- if (mVectorState.mNativeRendererPtr != 0) {
- nDestroyRenderer(mVectorState.mNativeRendererPtr);
+ if (mVectorState.mNativeRendererRefBase != null) {
+ mVectorState.mNativeRendererRefBase.release();
}
- mVectorState.mNativeRendererPtr = nCreateRenderer(mVectorState.mRootGroup.mNativePtr);
+ mVectorState.createNativeRenderer(mVectorState.mRootGroup.mNativePtr);
}
final VectorDrawableState state = mVectorState;
state.setDensity(Drawable.resolveDensity(r, 0));
@@ -707,7 +709,7 @@
return mVectorState.mAutoMirrored;
}
- private static class VectorDrawableState extends ConstantState {
+ static class VectorDrawableState extends ConstantState {
// Variables below need to be copied (deep copy if applicable) for mutation.
int[] mThemeAttrs;
int mChangingConfigurations;
@@ -722,7 +724,7 @@
Insets mOpticalInsets = Insets.NONE;
String mRootName = null;
VGroup mRootGroup;
- long mNativeRendererPtr;
+ VirtualRefBasePtr mNativeRendererRefBase = null;
int mDensity = DisplayMetrics.DENSITY_DEFAULT;
final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
@@ -743,7 +745,7 @@
mTintMode = copy.mTintMode;
mAutoMirrored = copy.mAutoMirrored;
mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
- mNativeRendererPtr = nCreateRenderer(mRootGroup.mNativePtr);
+ createNativeRenderer(mRootGroup.mNativePtr);
mBaseWidth = copy.mBaseWidth;
mBaseHeight = copy.mBaseHeight;
@@ -758,18 +760,15 @@
}
}
- @Override
- public void finalize() throws Throwable {
- if (mNativeRendererPtr != 0) {
- nDestroyRenderer(mNativeRendererPtr);
- mNativeRendererPtr = 0;
- }
- super.finalize();
+ private void createNativeRenderer(long rootGroupPtr) {
+ mNativeRendererRefBase = new VirtualRefBasePtr(nCreateRenderer(rootGroupPtr));
}
-
long getNativeRenderer() {
- return mNativeRendererPtr;
+ if (mNativeRendererRefBase == null) {
+ return 0;
+ }
+ return mNativeRendererRefBase.get();
}
public boolean canReuseCache() {
@@ -808,7 +807,7 @@
public VectorDrawableState() {
mRootGroup = new VGroup();
- mNativeRendererPtr = nCreateRenderer(mRootGroup.mNativePtr);
+ createNativeRenderer(mRootGroup.mNativePtr);
}
@Override
@@ -872,16 +871,16 @@
* has changed.
*/
public boolean setAlpha(float alpha) {
- return nSetRootAlpha(mNativeRendererPtr, alpha);
+ return nSetRootAlpha(mNativeRendererRefBase.get(), alpha);
}
@SuppressWarnings("unused")
public float getAlpha() {
- return nGetRootAlpha(mNativeRendererPtr);
+ return nGetRootAlpha(mNativeRendererRefBase.get());
}
}
- private static class VGroup implements VObject {
+ static class VGroup implements VObject {
private static final int ROTATE_INDEX = 0;
private static final int PIVOT_X_INDEX = 1;
private static final int PIVOT_Y_INDEX = 2;
@@ -891,6 +890,28 @@
private static final int TRANSLATE_Y_INDEX = 6;
private static final int TRANSFORM_PROPERTY_COUNT = 7;
+ private static final HashMap<String, Integer> sPropertyMap =
+ new HashMap<String, Integer>() {
+ {
+ put("translateX", TRANSLATE_X_INDEX);
+ put("translateY", TRANSLATE_Y_INDEX);
+ put("scaleX", SCALE_X_INDEX);
+ put("scaleY", SCALE_Y_INDEX);
+ put("pivotX", PIVOT_X_INDEX);
+ put("pivotY", PIVOT_Y_INDEX);
+ put("rotation", ROTATE_INDEX);
+ }
+ };
+
+ static int getPropertyIndex(String propertyName) {
+ if (sPropertyMap.containsKey(propertyName)) {
+ return sPropertyMap.get(propertyName);
+ } else {
+ // property not found
+ return -1;
+ }
+ }
+
// Temp array to store transform values obtained from native.
private float[] mTransform;
/////////////////////////////////////////////////////
@@ -1149,7 +1170,7 @@
/**
* Common Path information for clip path and normal path.
*/
- private static abstract class VPath implements VObject {
+ static abstract class VPath implements VObject {
protected PathParser.PathData mPathData = null;
String mPathName;
@@ -1260,7 +1281,7 @@
/**
* Normal path, which contains all the fill / paint information.
*/
- private static class VFullPath extends VPath {
+ static class VFullPath extends VPath {
private static final int STROKE_WIDTH_INDEX = 0;
private static final int STROKE_COLOR_INDEX = 1;
private static final int STROKE_ALPHA_INDEX = 2;
@@ -1274,6 +1295,20 @@
private static final int STROKE_MITER_LIMIT_INDEX = 10;
private static final int TOTAL_PROPERTY_COUNT = 11;
+ private final static HashMap<String, Integer> sPropertyMap
+ = new HashMap<String, Integer> () {
+ {
+ put("strokeWidth", STROKE_WIDTH_INDEX);
+ put("strokeColor", STROKE_COLOR_INDEX);
+ put("strokeAlpha", STROKE_ALPHA_INDEX);
+ put("fillColor", FILL_COLOR_INDEX);
+ put("fillAlpha", FILL_ALPHA_INDEX);
+ put("trimPathStart", TRIM_PATH_START_INDEX);
+ put("trimPathEnd", TRIM_PATH_END_INDEX);
+ put("trimPathOffset", TRIM_PATH_OFFSET_INDEX);
+ }
+ };
+
// Temp array to store property data obtained from native getter.
private byte[] mPropertyData;
/////////////////////////////////////////////////////
@@ -1297,6 +1332,14 @@
mFillColors = copy.mFillColors;
}
+ int getPropertyIndex(String propertyName) {
+ if (!sPropertyMap.containsKey(propertyName)) {
+ return -1;
+ } else {
+ return sPropertyMap.get(propertyName);
+ }
+ }
+
@Override
public boolean onStateChange(int[] stateSet) {
boolean changed = false;
@@ -1595,7 +1638,6 @@
}
private static native long nCreateRenderer(long rootGroupPtr);
- private static native void nDestroyRenderer(long rendererPtr);
private static native void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
float viewportHeight);
private static native boolean nSetRootAlpha(long rendererPtr, float alpha);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1fb9ac5..c232bd1 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -78,6 +78,8 @@
Program.cpp \
ProgramCache.cpp \
Properties.cpp \
+ PropertyValuesHolder.cpp \
+ PropertyValuesAnimatorSet.cpp \
RenderBufferCache.cpp \
RenderNode.cpp \
RenderProperties.cpp \
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 5ca2a2f..7bd2b24 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -90,6 +90,9 @@
doSetStartValue(getValue(mTarget));
}
if (mStagingPlayState > mPlayState) {
+ if (mStagingPlayState == PlayState::Restarted) {
+ mStagingPlayState = PlayState::Running;
+ }
mPlayState = mStagingPlayState;
// Oh boy, we're starting! Man the battle stations!
if (mPlayState == PlayState::Running) {
@@ -131,6 +134,11 @@
return true;
}
+ // This should be set before setValue() so animators can query this time when setValue
+ // is called.
+ nsecs_t currentFrameTime = context.frameTimeMs();
+ onPlayTimeChanged(currentFrameTime - mStartTime);
+
// 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
@@ -141,8 +149,9 @@
}
float fraction = 1.0f;
+
if (mPlayState == PlayState::Running && mDuration > 0) {
- fraction = (float)(context.frameTimeMs() - mStartTime) / mDuration;
+ fraction = (float)(currentFrameTime - mStartTime) / mDuration;
}
if (fraction >= 1.0f) {
fraction = 1.0f;
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index aea95bf..2c9c9c3 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -59,7 +59,13 @@
mMayRunAsync = mayRunAsync;
}
bool mayRunAsync() { return mMayRunAsync; }
- ANDROID_API void start() { mStagingPlayState = PlayState::Running; onStagingPlayStateChanged(); }
+ ANDROID_API void start() {
+ if (mStagingPlayState == PlayState::NotStarted) {
+ mStagingPlayState = PlayState::Running;
+ } else {
+ mStagingPlayState = PlayState::Restarted;
+ }
+ onStagingPlayStateChanged(); }
ANDROID_API void end() { mStagingPlayState = PlayState::Finished; onStagingPlayStateChanged(); }
void attach(RenderNode* target);
@@ -77,10 +83,27 @@
void forceEndNow(AnimationContext& context);
protected:
+ // PlayState is used by mStagingPlayState and mPlayState to track the state initiated from UI
+ // thread and Render Thread animation state, respectively.
+ // From the UI thread, mStagingPlayState transition looks like
+ // NotStarted -> Running -> Finished
+ // ^ |
+ // | |
+ // Restarted <------
+ // Note: For mStagingState, the Finished state (optional) is only set when the animation is
+ // terminated by user.
+ //
+ // On Render Thread, mPlayState transition:
+ // NotStart -> Running -> Finished
+ // ^ |
+ // | |
+ // -------------
+
enum class PlayState {
NotStarted,
Running,
Finished,
+ Restarted,
};
BaseRenderNodeAnimator(float finalValue);
@@ -93,6 +116,7 @@
void callOnFinishedListener(AnimationContext& context);
virtual void onStagingPlayStateChanged() {}
+ virtual void onPlayTimeChanged(nsecs_t playTime) {}
RenderNode* mTarget;
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index dbd502d..27facdf 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -52,6 +52,13 @@
} // namespace SaveFlags
+namespace uirenderer {
+namespace VectorDrawable {
+class Tree;
+};
+};
+typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+
class ANDROID_API Canvas {
public:
virtual ~Canvas() {};
@@ -217,6 +224,11 @@
*/
virtual bool drawTextAbsolutePos() const = 0;
+ /**
+ * Draws a VectorDrawable onto the canvas.
+ */
+ virtual void drawVectorDrawable(VectorDrawableRoot* tree);
+
protected:
void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
};
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 7eaa785..3db14b5 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -16,11 +16,12 @@
#include "DisplayListCanvas.h"
-#include "ResourceCache.h"
#include "DeferredDisplayList.h"
#include "DeferredLayerUpdater.h"
#include "DisplayListOp.h"
+#include "ResourceCache.h"
#include "RenderNode.h"
+#include "VectorDrawable.h"
#include "utils/PaintUtils.h"
#include <SkCamera.h>
@@ -412,6 +413,16 @@
addDrawOp(new (alloc()) DrawPointsOp(points, count, refPaint(&paint)));
}
+void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+ mDisplayList->ref(tree);
+ const SkBitmap& bitmap = tree->getBitmapUpdateIfDirty();
+ SkPaint* paint = tree->getPaint();
+ const SkRect bounds = tree->getBounds();
+ addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap),
+ 0, 0, bitmap.width(), bitmap.height(),
+ bounds.left(), bounds.top(), bounds.right(), bounds.bottom(), refPaint(paint)));
+}
+
void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count,
const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) {
if (!glyphs || count <= 0) return;
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index ad93960..06e72a0 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -206,6 +206,8 @@
float dstLeft, float dstTop, float dstRight, float dstBottom,
const SkPaint* paint) override;
+ virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
+
// Text
virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
@@ -214,7 +216,6 @@
float hOffset, float vOffset, const SkPaint& paint) override;
virtual bool drawTextAbsolutePos() const override { return false; }
-
private:
CanvasState mState;
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index c8910cb..185acce 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -19,6 +19,7 @@
#include "Canvas.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
+#include "VectorDrawable.h"
#include "renderstate/OffscreenBufferPool.h"
#include "utils/FatVector.h"
#include "utils/PaintUtils.h"
@@ -545,6 +546,18 @@
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
+void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
+ const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
+ SkPaint* paint = op.vectorDrawable->getPaint();
+ const BitmapRectOp* resolvedOp = new (mAllocator) BitmapRectOp(op.unmappedBounds,
+ op.localMatrix,
+ op.localClip,
+ paint,
+ &bitmap,
+ Rect(bitmap.width(), bitmap.height()));
+ deferBitmapRectOp(*resolvedOp);
+}
+
void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) {
// allocate a temporary oval op (with mAllocator, so it persists until render), so the
// renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp
new file mode 100644
index 0000000..eca1afcc
--- /dev/null
+++ b/libs/hwui/PropertyValuesAnimatorSet.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 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 "PropertyValuesAnimatorSet.h"
+#include "RenderNode.h"
+
+namespace android {
+namespace uirenderer {
+
+void PropertyValuesAnimatorSet::addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder,
+ Interpolator* interpolator, nsecs_t startDelay,
+ nsecs_t duration, int repeatCount) {
+
+ PropertyAnimator* animator = new PropertyAnimator(propertyValuesHolder,
+ interpolator, startDelay, duration, repeatCount);
+ mAnimators.emplace_back(animator);
+ setListener(new PropertyAnimatorSetListener(this));
+}
+
+PropertyValuesAnimatorSet::PropertyValuesAnimatorSet()
+ : BaseRenderNodeAnimator(1.0f) {
+ setStartValue(0);
+ mLastFraction = 0.0f;
+ setInterpolator(new LinearInterpolator());
+}
+
+void PropertyValuesAnimatorSet::onFinished(BaseRenderNodeAnimator* animator) {
+ if (mOneShotListener.get()) {
+ mOneShotListener->onAnimationFinished(animator);
+ mOneShotListener = nullptr;
+ }
+}
+
+float PropertyValuesAnimatorSet::getValue(RenderNode* target) const {
+ return mLastFraction;
+}
+
+void PropertyValuesAnimatorSet::setValue(RenderNode* target, float value) {
+ mLastFraction = value;
+}
+
+void PropertyValuesAnimatorSet::onPlayTimeChanged(nsecs_t playTime) {
+ for (size_t i = 0; i < mAnimators.size(); i++) {
+ mAnimators[i]->setCurrentPlayTime(playTime);
+ }
+}
+
+void PropertyValuesAnimatorSet::reset() {
+ // TODO: implement reset through adding a play state because we need to support reset() even
+ // during an animation run.
+}
+
+void PropertyValuesAnimatorSet::start(AnimationListener* listener) {
+ init();
+ mOneShotListener = listener;
+ BaseRenderNodeAnimator::start();
+}
+
+void PropertyValuesAnimatorSet::reverse(AnimationListener* listener) {
+// TODO: implement reverse
+}
+
+void PropertyValuesAnimatorSet::init() {
+ if (mInitialized) {
+ return;
+ }
+ nsecs_t maxDuration = 0;
+ for (size_t i = 0; i < mAnimators.size(); i++) {
+ if (maxDuration < mAnimators[i]->getTotalDuration()) {
+ maxDuration = mAnimators[i]->getTotalDuration();
+ }
+ }
+ mDuration = maxDuration;
+ mInitialized = true;
+}
+
+uint32_t PropertyValuesAnimatorSet::dirtyMask() {
+ return RenderNode::DISPLAY_LIST;
+}
+
+PropertyAnimator::PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator,
+ nsecs_t startDelay, nsecs_t duration, int repeatCount)
+ : mPropertyValuesHolder(holder), mInterpolator(interpolator), mStartDelay(startDelay),
+ mDuration(duration) {
+ if (repeatCount < 0) {
+ mRepeatCount = UINT32_MAX;
+ } else {
+ mRepeatCount = repeatCount;
+ }
+ mTotalDuration = ((nsecs_t) mRepeatCount + 1) * mDuration + mStartDelay;
+}
+
+void PropertyAnimator::setCurrentPlayTime(nsecs_t playTime) {
+ if (playTime >= mStartDelay && playTime < mTotalDuration) {
+ nsecs_t currentIterationPlayTime = (playTime - mStartDelay) % mDuration;
+ mLatestFraction = currentIterationPlayTime / (float) mDuration;
+ } else if (mLatestFraction < 1.0f && playTime >= mTotalDuration) {
+ mLatestFraction = 1.0f;
+ } else {
+ return;
+ }
+
+ setFraction(mLatestFraction);
+}
+
+void PropertyAnimator::setFraction(float fraction) {
+ float interpolatedFraction = mInterpolator->interpolate(mLatestFraction);
+ mPropertyValuesHolder->setFraction(interpolatedFraction);
+}
+
+void PropertyAnimatorSetListener::onAnimationFinished(BaseRenderNodeAnimator* animator) {
+ mSet->onFinished(animator);
+}
+
+}
+}
diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h
new file mode 100644
index 0000000..4c7ce52
--- /dev/null
+++ b/libs/hwui/PropertyValuesAnimatorSet.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "Animator.h"
+#include "PropertyValuesHolder.h"
+#include "Interpolator.h"
+
+namespace android {
+namespace uirenderer {
+
+class PropertyAnimator {
+public:
+ PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator, nsecs_t startDelay,
+ nsecs_t duration, int repeatCount);
+ void setCurrentPlayTime(nsecs_t playTime);
+ nsecs_t getTotalDuration() {
+ return mTotalDuration;
+ }
+ void setFraction(float fraction);
+
+private:
+ std::unique_ptr<PropertyValuesHolder> mPropertyValuesHolder;
+ std::unique_ptr<Interpolator> mInterpolator;
+ nsecs_t mStartDelay;
+ nsecs_t mDuration;
+ uint32_t mRepeatCount;
+ nsecs_t mTotalDuration;
+ float mLatestFraction = 0.0f;
+};
+
+class ANDROID_API PropertyValuesAnimatorSet : public BaseRenderNodeAnimator {
+public:
+ friend class PropertyAnimatorSetListener;
+ PropertyValuesAnimatorSet();
+
+ void start(AnimationListener* listener);
+ void reverse(AnimationListener* listener);
+ void reset();
+
+ void addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder,
+ Interpolator* interpolators, int64_t startDelays,
+ nsecs_t durations, int repeatCount);
+ virtual uint32_t dirtyMask();
+
+protected:
+ virtual float getValue(RenderNode* target) const override;
+ virtual void setValue(RenderNode* target, float value) override;
+ virtual void onPlayTimeChanged(nsecs_t playTime) override;
+
+private:
+ void init();
+ void onFinished(BaseRenderNodeAnimator* animator);
+ // Listener set from outside
+ sp<AnimationListener> mOneShotListener;
+ std::vector< std::unique_ptr<PropertyAnimator> > mAnimators;
+ float mLastFraction = 0.0f;
+ bool mInitialized = false;
+};
+
+class PropertyAnimatorSetListener : public AnimationListener {
+public:
+ PropertyAnimatorSetListener(PropertyValuesAnimatorSet* set) : mSet(set) {}
+ virtual void onAnimationFinished(BaseRenderNodeAnimator* animator) override;
+
+private:
+ PropertyValuesAnimatorSet* mSet;
+};
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp
new file mode 100644
index 0000000..8f837f6
--- /dev/null
+++ b/libs/hwui/PropertyValuesHolder.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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 "PropertyValuesHolder.h"
+
+#include "utils/VectorDrawableUtils.h"
+
+#include <utils/Log.h>
+
+namespace android {
+namespace uirenderer {
+
+using namespace VectorDrawable;
+
+float PropertyValuesHolder::getValueFromData(float fraction) {
+ if (mDataSource.size() == 0) {
+ LOG_ALWAYS_FATAL("No data source is defined");
+ return 0;
+ }
+ if (fraction <= 0.0f) {
+ return mDataSource.front();
+ }
+ if (fraction >= 1.0f) {
+ return mDataSource.back();
+ }
+
+ fraction *= mDataSource.size() - 1;
+ int lowIndex = floor(fraction);
+ fraction -= lowIndex;
+
+ float value = mDataSource[lowIndex] * (1.0f - fraction)
+ + mDataSource[lowIndex + 1] * fraction;
+ return value;
+}
+
+void GroupPropertyValuesHolder::setFraction(float fraction) {
+ float animatedValue;
+ if (mDataSource.size() > 0) {
+ animatedValue = getValueFromData(fraction);
+ } else {
+ animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+ }
+ mGroup->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) {
+ SkColor animatedValue = interpolateColors(mStartValue, mEndValue, fraction);
+ mFullPath->setColorPropertyValue(mPropertyId, animatedValue);
+}
+
+void FullPathPropertyValuesHolder::setFraction(float fraction) {
+ float animatedValue;
+ if (mDataSource.size() > 0) {
+ animatedValue = getValueFromData(fraction);
+ } else {
+ animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+ }
+ mFullPath->setPropertyValue(mPropertyId, animatedValue);
+}
+
+void PathDataPropertyValuesHolder::setFraction(float fraction) {
+ VectorDrawableUtils::interpolatePaths(&mPathData, mStartValue, mEndValue, fraction);
+ mPath->setPathData(mPathData);
+}
+
+void RootAlphaPropertyValuesHolder::setFraction(float fraction) {
+ float animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+ mTree->setRootAlpha(animatedValue);
+}
+
+} // namepace uirenderer
+} // namespace android
diff --git a/libs/hwui/PropertyValuesHolder.h b/libs/hwui/PropertyValuesHolder.h
new file mode 100644
index 0000000..b905fae
--- /dev/null
+++ b/libs/hwui/PropertyValuesHolder.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "VectorDrawable.h"
+
+#include <SkColor.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * PropertyValues holder contains data needed to change a property of a Vector Drawable object.
+ * When a fraction in [0f, 1f] is provided, the holder will calculate an interpolated value based
+ * on its start and end value, and set the new value on the VectorDrawble's corresponding property.
+ */
+class ANDROID_API PropertyValuesHolder {
+public:
+ virtual void setFraction(float fraction) = 0;
+ void setPropertyDataSource(float* dataSource, int length) {
+ mDataSource.insert(mDataSource.begin(), dataSource, dataSource + length);
+ }
+ float getValueFromData(float fraction);
+ virtual ~PropertyValuesHolder() {}
+protected:
+ std::vector<float> mDataSource;
+};
+
+class ANDROID_API GroupPropertyValuesHolder : public PropertyValuesHolder {
+public:
+ GroupPropertyValuesHolder(VectorDrawable::Group* ptr, int propertyId, float startValue,
+ float endValue)
+ : mGroup(ptr)
+ , mPropertyId(propertyId)
+ , mStartValue(startValue)
+ , mEndValue(endValue){
+ }
+ void setFraction(float fraction) override;
+private:
+ VectorDrawable::Group* mGroup;
+ int mPropertyId;
+ float mStartValue;
+ float mEndValue;
+};
+
+class ANDROID_API FullPathColorPropertyValuesHolder : public PropertyValuesHolder {
+public:
+ FullPathColorPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, int32_t startValue,
+ int32_t endValue)
+ : mFullPath(ptr)
+ , mPropertyId(propertyId)
+ , mStartValue(startValue)
+ , mEndValue(endValue) {};
+ void setFraction(float fraction) override;
+ static SkColor interpolateColors(SkColor fromColor, SkColor toColor, float fraction);
+private:
+ VectorDrawable::FullPath* mFullPath;
+ int mPropertyId;
+ int32_t mStartValue;
+ int32_t mEndValue;
+};
+
+class ANDROID_API FullPathPropertyValuesHolder : public PropertyValuesHolder {
+public:
+ FullPathPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, float startValue,
+ float endValue)
+ : mFullPath(ptr)
+ , mPropertyId(propertyId)
+ , mStartValue(startValue)
+ , mEndValue(endValue) {};
+ void setFraction(float fraction) override;
+private:
+ VectorDrawable::FullPath* mFullPath;
+ int mPropertyId;
+ float mStartValue;
+ float mEndValue;
+};
+
+class ANDROID_API PathDataPropertyValuesHolder : public PropertyValuesHolder {
+public:
+ PathDataPropertyValuesHolder(VectorDrawable::Path* ptr, PathData* startValue,
+ PathData* endValue)
+ : mPath(ptr)
+ , mStartValue(*startValue)
+ , mEndValue(*endValue) {};
+ void setFraction(float fraction) override;
+private:
+ VectorDrawable::Path* mPath;
+ PathData mPathData;
+ PathData mStartValue;
+ PathData mEndValue;
+};
+
+class ANDROID_API RootAlphaPropertyValuesHolder : public PropertyValuesHolder {
+public:
+ RootAlphaPropertyValuesHolder(VectorDrawable::Tree* tree, float startValue, float endValue)
+ : mTree(tree)
+ , mStartValue(startValue)
+ , mEndValue(endValue) {}
+ void setFraction(float fraction) override;
+private:
+ VectorDrawable::Tree* mTree;
+ float mStartValue;
+ float mEndValue;
+};
+}
+}
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 593d690..bb26e2e 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_HWUI_RECORDED_OP_H
#define ANDROID_HWUI_RECORDED_OP_H
+#include "RecordedOp.h"
#include "font/FontUtil.h"
#include "Matrix.h"
#include "Rect.h"
@@ -39,6 +40,10 @@
class RenderNode;
struct Vertex;
+namespace VectorDrawable {
+class Tree;
+}
+
/**
* Authoritative op list, used for generating the op ID enum, ID based LUTS, and
* the functions to which they dispatch. Parameter macros are executed for each op,
@@ -75,6 +80,7 @@
PRE_RENDER_OP_FN(EndLayerOp) \
PRE_RENDER_OP_FN(BeginUnclippedLayerOp) \
PRE_RENDER_OP_FN(EndUnclippedLayerOp) \
+ PRE_RENDER_OP_FN(VectorDrawableOp) \
\
RENDER_ONLY_OP_FN(ShadowOp) \
RENDER_ONLY_OP_FN(LayerOp) \
@@ -325,6 +331,13 @@
const float* ry;
};
+struct VectorDrawableOp : RecordedOp {
+ VectorDrawableOp(VectorDrawable::Tree* tree, BASE_PARAMS_PAINTLESS)
+ : SUPER_PAINTLESS(VectorDrawableOp)
+ , vectorDrawable(tree) {}
+ VectorDrawable::Tree* vectorDrawable;
+};
+
/**
* Real-time, dynamic-lit shadow.
*
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 2387962..16929b8 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -19,6 +19,7 @@
#include "DeferredLayerUpdater.h"
#include "RecordedOp.h"
#include "RenderNode.h"
+#include "VectorDrawable.h"
namespace android {
namespace uirenderer {
@@ -395,7 +396,6 @@
&x->value, &y->value, &radius->value));
}
-
void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
addOp(new (alloc()) OvalOp(
Rect(left, top, right, bottom),
@@ -422,6 +422,15 @@
refPaint(&paint), refPath(&path)));
}
+void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+ mDisplayList->ref(tree);
+ addOp(new (alloc()) VectorDrawableOp(
+ tree,
+ Rect(tree->getBounds()),
+ *(mState.currentSnapshot()->transform),
+ getRecordedClip()));
+}
+
// Bitmap-based
void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
save(SaveFlags::Matrix);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 375760f..cc14e61 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -175,6 +175,8 @@
const uint16_t* indices, int indexCount, const SkPaint& paint) override
{ /* RecordingCanvas does not support drawVertices(); ignore */ }
+ virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
+
// Bitmap-based
virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override;
virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d320a41..bd4442d 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -32,6 +32,8 @@
#include <SkTLazy.h>
#include <SkTemplates.h>
+#include "VectorDrawable.h"
+
#include <memory>
namespace android {
@@ -153,6 +155,7 @@
float hOffset, float vOffset, const SkPaint& paint) override;
virtual bool drawTextAbsolutePos() const override { return true; }
+ virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
@@ -742,6 +745,14 @@
NinePatch::Draw(mCanvas, bounds, bitmap, chunk, paint, nullptr);
}
+void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
+ const SkBitmap& bitmap = vectorDrawable->getBitmapUpdateIfDirty();
+ SkRect bounds = vectorDrawable->getBounds();
+ drawBitmap(bitmap, 0, 0, bitmap.width(), bitmap.height(),
+ bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
+ vectorDrawable->getPaint());
+}
+
// ----------------------------------------------------------------------------
// Canvas draw operations: Text
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 1cf15ac..541c799 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -138,18 +138,7 @@
}
FullPath::FullPath(const FullPath& path) : Path(path) {
- mStrokeWidth = path.mStrokeWidth;
- mStrokeColor = path.mStrokeColor;
- mStrokeAlpha = path.mStrokeAlpha;
- mFillColor = path.mFillColor;
- mFillAlpha = path.mFillAlpha;
- mTrimPathStart = path.mTrimPathStart;
- mTrimPathEnd = path.mTrimPathEnd;
- mTrimPathOffset = path.mTrimPathOffset;
- mStrokeMiterLimit = path.mStrokeMiterLimit;
- mStrokeLineCap = path.mStrokeLineCap;
- mStrokeLineJoin = path.mStrokeLineJoin;
-
+ mProperties = path.mProperties;
SkRefCnt_SafeAssign(mStrokeGradient, path.mStrokeGradient);
SkRefCnt_SafeAssign(mFillGradient, path.mFillGradient);
}
@@ -159,7 +148,7 @@
return mTrimmedSkPath;
}
Path::getUpdatedPath();
- if (mTrimPathStart != 0.0f || mTrimPathEnd != 1.0f) {
+ if (mProperties.trimPathStart != 0.0f || mProperties.trimPathEnd != 1.0f) {
applyTrim();
return mTrimmedSkPath;
} else {
@@ -170,14 +159,14 @@
void FullPath::updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha,
SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd,
float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin) {
- mStrokeWidth = strokeWidth;
- mStrokeColor = strokeColor;
- mStrokeAlpha = strokeAlpha;
- mFillColor = fillColor;
- mFillAlpha = fillAlpha;
- mStrokeMiterLimit = strokeMiterLimit;
- mStrokeLineCap = SkPaint::Cap(strokeLineCap);
- mStrokeLineJoin = SkPaint::Join(strokeLineJoin);
+ mProperties.strokeWidth = strokeWidth;
+ mProperties.strokeColor = strokeColor;
+ mProperties.strokeAlpha = strokeAlpha;
+ mProperties.fillColor = fillColor;
+ mProperties.fillAlpha = fillAlpha;
+ mProperties.strokeMiterLimit = strokeMiterLimit;
+ mProperties.strokeLineCap = strokeLineCap;
+ mProperties.strokeLineJoin = strokeLineJoin;
// If any trim property changes, mark trim dirty and update the trim path
setTrimPathStart(trimPathStart);
@@ -195,12 +184,12 @@
// Draw path's fill, if fill color or gradient is valid
bool needsFill = false;
if (mFillGradient != nullptr) {
- mPaint.setColor(applyAlpha(SK_ColorBLACK, mFillAlpha));
+ mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.fillAlpha));
SkShader* newShader = mFillGradient->newWithLocalMatrix(matrix);
mPaint.setShader(newShader);
needsFill = true;
- } else if (mFillColor != SK_ColorTRANSPARENT) {
- mPaint.setColor(applyAlpha(mFillColor, mFillAlpha));
+ } else if (mProperties.fillColor != SK_ColorTRANSPARENT) {
+ mPaint.setColor(applyAlpha(mProperties.fillColor, mProperties.fillAlpha));
needsFill = true;
}
@@ -213,21 +202,21 @@
// Draw path's stroke, if stroke color or gradient is valid
bool needsStroke = false;
if (mStrokeGradient != nullptr) {
- mPaint.setColor(applyAlpha(SK_ColorBLACK, mStrokeAlpha));
+ mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.strokeAlpha));
SkShader* newShader = mStrokeGradient->newWithLocalMatrix(matrix);
mPaint.setShader(newShader);
needsStroke = true;
- } else if (mStrokeColor != SK_ColorTRANSPARENT) {
- mPaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
+ } else if (mProperties.strokeColor != SK_ColorTRANSPARENT) {
+ mPaint.setColor(applyAlpha(mProperties.strokeColor, mProperties.strokeAlpha));
needsStroke = true;
}
if (needsStroke) {
mPaint.setStyle(SkPaint::Style::kStroke_Style);
mPaint.setAntiAlias(true);
- mPaint.setStrokeJoin(mStrokeLineJoin);
- mPaint.setStrokeCap(mStrokeLineCap);
- mPaint.setStrokeMiter(mStrokeMiterLimit);
- mPaint.setStrokeWidth(mStrokeWidth * strokeScale);
+ mPaint.setStrokeJoin(SkPaint::Join(mProperties.strokeLineJoin));
+ mPaint.setStrokeCap(SkPaint::Cap(mProperties.strokeLineCap));
+ mPaint.setStrokeMiter(mProperties.strokeMiterLimit);
+ mPaint.setStrokeWidth(mProperties.strokeWidth * strokeScale);
outCanvas->drawPath(renderPath, mPaint);
}
}
@@ -236,14 +225,14 @@
* Applies trimming to the specified path.
*/
void FullPath::applyTrim() {
- if (mTrimPathStart == 0.0f && mTrimPathEnd == 1.0f) {
+ if (mProperties.trimPathStart == 0.0f && mProperties.trimPathEnd == 1.0f) {
// No trimming necessary.
return;
}
SkPathMeasure measure(mSkPath, false);
float len = SkScalarToFloat(measure.getLength());
- float start = len * fmod((mTrimPathStart + mTrimPathOffset), 1.0f);
- float end = len * fmod((mTrimPathEnd + mTrimPathOffset), 1.0f);
+ float start = len * fmod((mProperties.trimPathStart + mProperties.trimPathOffset), 1.0f);
+ float end = len * fmod((mProperties.trimPathEnd + mProperties.trimPathOffset), 1.0f);
mTrimmedSkPath.reset();
if (start > end) {
@@ -255,76 +244,69 @@
mTrimDirty = false;
}
-inline int putData(int8_t* outBytes, int startIndex, float value) {
- int size = sizeof(float);
- memcpy(&outBytes[startIndex], &value, size);
- return size;
-}
-
-inline int putData(int8_t* outBytes, int startIndex, int value) {
- int size = sizeof(int);
- memcpy(&outBytes[startIndex], &value, size);
- return size;
-}
-
-struct FullPathProperties {
- // TODO: Consider storing full path properties in this struct instead of the fields.
- float strokeWidth;
- SkColor strokeColor;
- float strokeAlpha;
- SkColor fillColor;
- float fillAlpha;
- float trimPathStart;
- float trimPathEnd;
- float trimPathOffset;
- int32_t strokeLineCap;
- int32_t strokeLineJoin;
- float strokeMiterLimit;
-};
-
-REQUIRE_COMPATIBLE_LAYOUT(FullPathProperties);
+REQUIRE_COMPATIBLE_LAYOUT(FullPath::Properties);
static_assert(sizeof(float) == sizeof(int32_t), "float is not the same size as int32_t");
static_assert(sizeof(SkColor) == sizeof(int32_t), "SkColor is not the same size as int32_t");
bool FullPath::getProperties(int8_t* outProperties, int length) {
- int propertyDataSize = sizeof(FullPathProperties);
+ int propertyDataSize = sizeof(Properties);
if (length != propertyDataSize) {
LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
propertyDataSize, length);
return false;
}
- // TODO: consider replacing the property fields with a FullPathProperties struct.
- FullPathProperties properties;
- properties.strokeWidth = mStrokeWidth;
- properties.strokeColor = mStrokeColor;
- properties.strokeAlpha = mStrokeAlpha;
- properties.fillColor = mFillColor;
- properties.fillAlpha = mFillAlpha;
- properties.trimPathStart = mTrimPathStart;
- properties.trimPathEnd = mTrimPathEnd;
- properties.trimPathOffset = mTrimPathOffset;
- properties.strokeLineCap = mStrokeLineCap;
- properties.strokeLineJoin = mStrokeLineJoin;
- properties.strokeMiterLimit = mStrokeMiterLimit;
-
- memcpy(outProperties, &properties, length);
+ Properties* out = reinterpret_cast<Properties*>(outProperties);
+ *out = mProperties;
return true;
}
+void FullPath::setColorPropertyValue(int propertyId, int32_t value) {
+ Property currentProperty = static_cast<Property>(propertyId);
+ if (currentProperty == Property::StrokeColor) {
+ mProperties.strokeColor = value;
+ } else if (currentProperty == Property::FillColor) {
+ mProperties.fillColor = value;
+ } else {
+ LOG_ALWAYS_FATAL("Error setting color property on FullPath: No valid property with id: %d",
+ propertyId);
+ }
+}
+
+void FullPath::setPropertyValue(int propertyId, float value) {
+ Property property = static_cast<Property>(propertyId);
+ switch (property) {
+ case Property::StrokeWidth:
+ setStrokeWidth(value);
+ break;
+ case Property::StrokeAlpha:
+ setStrokeAlpha(value);
+ break;
+ case Property::FillAlpha:
+ setFillAlpha(value);
+ break;
+ case Property::TrimPathStart:
+ setTrimPathStart(value);
+ break;
+ case Property::TrimPathEnd:
+ setTrimPathEnd(value);
+ break;
+ case Property::TrimPathOffset:
+ setTrimPathOffset(value);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Invalid property id: %d for animation", propertyId);
+ break;
+ }
+}
+
void ClipPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath,
float strokeScale, const SkMatrix& matrix){
outCanvas->clipPath(renderPath, SkRegion::kIntersect_Op);
}
Group::Group(const Group& group) : Node(group) {
- mRotate = group.mRotate;
- mPivotX = group.mPivotX;
- mPivotY = group.mPivotY;
- mScaleX = group.mScaleX;
- mScaleY = group.mScaleY;
- mTranslateX = group.mTranslateX;
- mTranslateY = group.mTranslateY;
+ mProperties = group.mProperties;
}
void Group::draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, float scaleX,
@@ -371,10 +353,11 @@
outMatrix->reset();
// TODO: use rotate(mRotate, mPivotX, mPivotY) and scale with pivot point, instead of
// translating to pivot for rotating and scaling, then translating back.
- outMatrix->postTranslate(-mPivotX, -mPivotY);
- outMatrix->postScale(mScaleX, mScaleY);
- outMatrix->postRotate(mRotate, 0, 0);
- outMatrix->postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
+ outMatrix->postTranslate(-mProperties.pivotX, -mProperties.pivotY);
+ outMatrix->postScale(mProperties.scaleX, mProperties.scaleY);
+ outMatrix->postRotate(mProperties.rotate, 0, 0);
+ outMatrix->postTranslate(mProperties.translateX + mProperties.pivotX,
+ mProperties.translateY + mProperties.pivotY);
}
void Group::addChild(Node* child) {
@@ -388,38 +371,68 @@
propertyCount, length);
return false;
}
- for (int i = 0; i < propertyCount; i++) {
- Property currentProperty = static_cast<Property>(i);
- switch (currentProperty) {
- case Property::Rotate_Property:
- outProperties[i] = mRotate;
- break;
- case Property::PivotX_Property:
- outProperties[i] = mPivotX;
- break;
- case Property::PivotY_Property:
- outProperties[i] = mPivotY;
- break;
- case Property::ScaleX_Property:
- outProperties[i] = mScaleX;
- break;
- case Property::ScaleY_Property:
- outProperties[i] = mScaleY;
- break;
- case Property::TranslateX_Property:
- outProperties[i] = mTranslateX;
- break;
- case Property::TranslateY_Property:
- outProperties[i] = mTranslateY;
- break;
- default:
- LOG_ALWAYS_FATAL("Invalid input index: %d", i);
- return false;
- }
- }
+ Properties* out = reinterpret_cast<Properties*>(outProperties);
+ *out = mProperties;
return true;
}
+// TODO: Consider animating the properties as float pointers
+float Group::getPropertyValue(int propertyId) const {
+ Property currentProperty = static_cast<Property>(propertyId);
+ switch (currentProperty) {
+ case Property::Rotate:
+ return mProperties.rotate;
+ case Property::PivotX:
+ return mProperties.pivotX;
+ case Property::PivotY:
+ return mProperties.pivotY;
+ case Property::ScaleX:
+ return mProperties.scaleX;
+ case Property::ScaleY:
+ return mProperties.scaleY;
+ case Property::TranslateX:
+ return mProperties.translateX;
+ case Property::TranslateY:
+ return mProperties.translateY;
+ default:
+ LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
+ return 0;
+ }
+}
+
+void Group::setPropertyValue(int propertyId, float value) {
+ Property currentProperty = static_cast<Property>(propertyId);
+ switch (currentProperty) {
+ case Property::Rotate:
+ mProperties.rotate = value;
+ break;
+ case Property::PivotX:
+ mProperties.pivotX = value;
+ break;
+ case Property::PivotY:
+ mProperties.pivotY = value;
+ break;
+ case Property::ScaleX:
+ mProperties.scaleX = value;
+ break;
+ case Property::ScaleY:
+ mProperties.scaleY = value;
+ break;
+ case Property::TranslateX:
+ mProperties.translateX = value;
+ break;
+ case Property::TranslateY:
+ mProperties.translateY = value;
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
+ }
+}
+
+bool Group::isValidProperty(int propertyId) {
+ return propertyId >= 0 && propertyId < static_cast<int>(Property::Count);
+}
+
void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter,
const SkRect& bounds, bool needsMirroring, bool canReuseCache) {
// The imageView can scale the canvas in different ways, in order to
@@ -445,6 +458,8 @@
return;
}
+ mPaint.setColorFilter(colorFilter);
+
int saveCount = outCanvas->save(SaveFlags::MatrixClip);
outCanvas->translate(mBounds.fLeft, mBounds.fTop);
@@ -458,43 +473,33 @@
// And we use this bound for the destination rect for the drawBitmap, so
// we offset to (0, 0);
mBounds.offsetTo(0, 0);
-
createCachedBitmapIfNeeded(scaledWidth, scaledHeight);
- if (!mAllowCaching) {
- updateCachedBitmap(scaledWidth, scaledHeight);
- } else {
- if (!canReuseCache || mCacheDirty) {
- updateCachedBitmap(scaledWidth, scaledHeight);
- }
- }
- drawCachedBitmapWithRootAlpha(outCanvas, colorFilter, mBounds);
+
+ outCanvas->drawVectorDrawable(this);
outCanvas->restoreToCount(saveCount);
}
-void Tree::drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter,
- const SkRect& originalBounds) {
+SkPaint* Tree::getPaint() {
SkPaint* paint;
- if (mRootAlpha == 1.0f && filter == NULL) {
+ if (mRootAlpha == 1.0f && mPaint.getColorFilter() == NULL) {
paint = NULL;
} else {
mPaint.setFilterQuality(kLow_SkFilterQuality);
mPaint.setAlpha(mRootAlpha * 255);
- mPaint.setColorFilter(filter);
paint = &mPaint;
}
- outCanvas->drawBitmap(mCachedBitmap, 0, 0, mCachedBitmap.width(), mCachedBitmap.height(),
- originalBounds.fLeft, originalBounds.fTop, originalBounds.fRight,
- originalBounds.fBottom, paint);
+ return paint;
}
-void Tree::updateCachedBitmap(int width, int height) {
+const SkBitmap& Tree::getBitmapUpdateIfDirty() {
mCachedBitmap.eraseColor(SK_ColorTRANSPARENT);
SkCanvas outCanvas(mCachedBitmap);
- float scaleX = width / mViewportWidth;
- float scaleY = height / mViewportHeight;
+ float scaleX = (float) mCachedBitmap.width() / mViewportWidth;
+ float scaleY = (float) mCachedBitmap.height() / mViewportHeight;
mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY);
mCacheDirty = false;
+ return mCachedBitmap;
}
void Tree::createCachedBitmapIfNeeded(int width, int height) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 09bdce5..f8f1ea6 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -18,6 +18,7 @@
#define ANDROID_HWUI_VPATH_H
#include "Canvas.h"
+
#include <SkBitmap.h>
#include <SkColor.h>
#include <SkCanvas.h>
@@ -104,6 +105,21 @@
class ANDROID_API FullPath: public Path {
public:
+
+struct Properties {
+ float strokeWidth = 0;
+ SkColor strokeColor = SK_ColorTRANSPARENT;
+ float strokeAlpha = 1;
+ SkColor fillColor = SK_ColorTRANSPARENT;
+ float fillAlpha = 1;
+ float trimPathStart = 0;
+ float trimPathEnd = 1;
+ float trimPathOffset = 0;
+ int32_t strokeLineCap = SkPaint::Cap::kButt_Cap;
+ int32_t strokeLineJoin = SkPaint::Join::kMiter_Join;
+ float strokeMiterLimit = 4;
+};
+
FullPath(const FullPath& path); // for cloning
FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
FullPath() : Path() {}
@@ -118,55 +134,58 @@
float strokeAlpha, SkColor fillColor, float fillAlpha,
float trimPathStart, float trimPathEnd, float trimPathOffset,
float strokeMiterLimit, int strokeLineCap, int strokeLineJoin);
+ // TODO: Cleanup: Remove the setter and getters below, and their counterparts in java and JNI
float getStrokeWidth() {
- return mStrokeWidth;
+ return mProperties.strokeWidth;
}
void setStrokeWidth(float strokeWidth) {
- mStrokeWidth = strokeWidth;
+ mProperties.strokeWidth = strokeWidth;
}
SkColor getStrokeColor() {
- return mStrokeColor;
+ return mProperties.strokeColor;
}
void setStrokeColor(SkColor strokeColor) {
- mStrokeColor = strokeColor;
+ mProperties.strokeColor = strokeColor;
}
float getStrokeAlpha() {
- return mStrokeAlpha;
+ return mProperties.strokeAlpha;
}
void setStrokeAlpha(float strokeAlpha) {
- mStrokeAlpha = strokeAlpha;
+ mProperties.strokeAlpha = strokeAlpha;
}
SkColor getFillColor() {
- return mFillColor;
+ return mProperties.fillColor;
}
void setFillColor(SkColor fillColor) {
- mFillColor = fillColor;
+ mProperties.fillColor = fillColor;
}
float getFillAlpha() {
- return mFillAlpha;
+ return mProperties.fillAlpha;
}
void setFillAlpha(float fillAlpha) {
- mFillAlpha = fillAlpha;
+ mProperties.fillAlpha = fillAlpha;
}
float getTrimPathStart() {
- return mTrimPathStart;
+ return mProperties.trimPathStart;
}
void setTrimPathStart(float trimPathStart) {
- VD_SET_PROP_WITH_FLAG(mTrimPathStart, trimPathStart, mTrimDirty);
+ VD_SET_PROP_WITH_FLAG(mProperties.trimPathStart, trimPathStart, mTrimDirty);
}
float getTrimPathEnd() {
- return mTrimPathEnd;
+ return mProperties.trimPathEnd;
}
void setTrimPathEnd(float trimPathEnd) {
- VD_SET_PROP_WITH_FLAG(mTrimPathEnd, trimPathEnd, mTrimDirty);
+ VD_SET_PROP_WITH_FLAG(mProperties.trimPathEnd, trimPathEnd, mTrimDirty);
}
float getTrimPathOffset() {
- return mTrimPathOffset;
+ return mProperties.trimPathOffset;
}
void setTrimPathOffset(float trimPathOffset) {
- VD_SET_PROP_WITH_FLAG(mTrimPathOffset, trimPathOffset, mTrimDirty);
+ VD_SET_PROP_WITH_FLAG(mProperties.trimPathOffset, trimPathOffset, mTrimDirty);
}
bool getProperties(int8_t* outProperties, int length);
+ void setColorPropertyValue(int propertyId, int32_t value);
+ void setPropertyValue(int propertyId, float value);
void setFillGradient(SkShader* fillGradient) {
SkRefCnt_SafeAssign(mFillGradient, fillGradient);
@@ -182,24 +201,28 @@
float strokeScale, const SkMatrix& matrix) override;
private:
+ enum class Property {
+ StrokeWidth = 0,
+ StrokeColor,
+ StrokeAlpha,
+ FillColor,
+ FillAlpha,
+ TrimPathStart,
+ TrimPathEnd,
+ TrimPathOffset,
+ StrokeLineCap,
+ StrokeLineJoin,
+ StrokeMiterLimit,
+ Count,
+ };
// Applies trimming to the specified path.
void applyTrim();
- float mStrokeWidth = 0;
- SkColor mStrokeColor = SK_ColorTRANSPARENT;
- float mStrokeAlpha = 1;
- SkColor mFillColor = SK_ColorTRANSPARENT;
- SkShader* mStrokeGradient = nullptr;
- SkShader* mFillGradient = nullptr;
- float mFillAlpha = 1;
- float mTrimPathStart = 0;
- float mTrimPathEnd = 1;
- float mTrimPathOffset = 0;
+ Properties mProperties;
bool mTrimDirty = true;
- SkPaint::Cap mStrokeLineCap = SkPaint::Cap::kButt_Cap;
- SkPaint::Join mStrokeLineJoin = SkPaint::Join::kMiter_Join;
- float mStrokeMiterLimit = 4;
SkPath mTrimmedSkPath;
SkPaint mPaint;
+ SkShader* mStrokeGradient = nullptr;
+ SkShader* mFillGradient = nullptr;
};
class ANDROID_API ClipPath: public Path {
@@ -216,49 +239,58 @@
class ANDROID_API Group: public Node {
public:
+ struct Properties {
+ float rotate = 0;
+ float pivotX = 0;
+ float pivotY = 0;
+ float scaleX = 1;
+ float scaleY = 1;
+ float translateX = 0;
+ float translateY = 0;
+ };
Group(const Group& group);
Group() {}
float getRotation() {
- return mRotate;
+ return mProperties.rotate;
}
void setRotation(float rotation) {
- mRotate = rotation;
+ mProperties.rotate = rotation;
}
float getPivotX() {
- return mPivotX;
+ return mProperties.pivotX;
}
void setPivotX(float pivotX) {
- mPivotX = pivotX;
+ mProperties.pivotX = pivotX;
}
float getPivotY() {
- return mPivotY;
+ return mProperties.pivotY;
}
void setPivotY(float pivotY) {
- mPivotY = pivotY;
+ mProperties.pivotY = pivotY;
}
float getScaleX() {
- return mScaleX;
+ return mProperties.scaleX;
}
void setScaleX(float scaleX) {
- mScaleX = scaleX;
+ mProperties.scaleX = scaleX;
}
float getScaleY() {
- return mScaleY;
+ return mProperties.scaleY;
}
void setScaleY(float scaleY) {
- mScaleY = scaleY;
+ mProperties.scaleY = scaleY;
}
float getTranslateX() {
- return mTranslateX;
+ return mProperties.translateX;
}
void setTranslateX(float translateX) {
- mTranslateX = translateX;
+ mProperties.translateX = translateX;
}
float getTranslateY() {
- return mTranslateY;
+ return mProperties.translateY;
}
void setTranslateY(float translateY) {
- mTranslateY = translateY;
+ mProperties.translateY = translateY;
}
virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
float scaleX, float scaleY) override;
@@ -268,38 +300,33 @@
void addChild(Node* child);
void dump() override;
bool getProperties(float* outProperties, int length);
+ float getPropertyValue(int propertyId) const;
+ void setPropertyValue(int propertyId, float value);
+ static bool isValidProperty(int propertyId);
private:
enum class Property {
- Rotate_Property = 0,
- PivotX_Property,
- PivotY_Property,
- ScaleX_Property,
- ScaleY_Property,
- TranslateX_Property,
- TranslateY_Property,
+ Rotate = 0,
+ PivotX,
+ PivotY,
+ ScaleX,
+ ScaleY,
+ TranslateX,
+ TranslateY,
// Count of the properties, must be at the end.
Count,
};
- float mRotate = 0;
- float mPivotX = 0;
- float mPivotY = 0;
- float mScaleX = 1;
- float mScaleY = 1;
- float mTranslateX = 0;
- float mTranslateY = 0;
std::vector<Node*> mChildren;
+ Properties mProperties;
};
-class ANDROID_API Tree {
+class ANDROID_API Tree : public VirtualLightRefBase {
public:
Tree(Group* rootNode) : mRootNode(rootNode) {}
void draw(Canvas* outCanvas, SkColorFilter* colorFilter,
const SkRect& bounds, bool needsMirroring, bool canReuseCache);
- void drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter,
- const SkRect& originalBounds);
- void updateCachedBitmap(int width, int height);
+ const SkBitmap& getBitmapUpdateIfDirty();
void createCachedBitmapIfNeeded(int width, int height);
bool canReuseBitmap(int width, int height);
void setAllowCaching(bool allowCaching) {
@@ -316,6 +343,10 @@
mViewportWidth = viewportWidth;
mViewportHeight = viewportHeight;
}
+ SkPaint* getPaint();
+ const SkRect& getBounds() const {
+ return mBounds;
+ }
private:
// Cap the bitmap size, such that it won't hurt the performance too much