VectorDrawable native rendering - Step 4 of MANY
This CL runs VectorDrawable animation on RenderThread. The changes in this CL include: - Convert all the animators in AnimatorSet for AVD into a set of RenderNodeAnimators. - Hook up the new animators with RenderThread - Add drawOp in RecordingCanvas for drawing VD so that during the animation on RenderThread, all the property changes on VD can be reflected on the screen. TODO: - Implement reverse and reset for AVD. Change-Id: I2df1d754f2db0ad098d9c15dde4bb2bdfafc2315
This commit is contained in:
@@ -1030,6 +1030,20 @@ public final class AnimatorSet extends Animator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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();
|
||||
|
||||
@@ -231,7 +231,7 @@ class PathKeyframes implements Keyframes {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ class PathKeyframes implements Keyframes {
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class FloatKeyframesBase extends SimpleKeyframes
|
||||
abstract static class FloatKeyframesBase extends SimpleKeyframes
|
||||
implements FloatKeyframes {
|
||||
@Override
|
||||
public Class getType() {
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.graphics.PointF;
|
||||
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 @@ public class PropertyValuesHolder implements Cloneable {
|
||||
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 @@ public class PropertyValuesHolder implements Cloneable {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @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,
|
||||
|
||||
@@ -104,6 +104,7 @@ public class PathParser {
|
||||
}
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.graphics.Matrix;
|
||||
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
|
||||
@@ -771,6 +772,14 @@ public class RenderNode {
|
||||
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);
|
||||
}
|
||||
|
||||
47
core/java/android/view/RenderNodeAnimatorSetHelper.java
Normal file
47
core/java/android/view/RenderNodeAnimatorSetHelper.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -51,6 +51,7 @@ LOCAL_SRC_FILES:= \
|
||||
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 \
|
||||
|
||||
@@ -131,6 +131,7 @@ extern int register_android_graphics_Rasterizer(JNIEnv* env);
|
||||
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 @@ static const RegJNIRec gRegJNI[] = {
|
||||
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),
|
||||
|
||||
194
core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
Normal file
194
core/jni/android_graphics_drawable_AnimatedVectorDrawable.cpp
Normal file
@@ -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
|
||||
@@ -32,11 +32,6 @@ static jlong createTree(JNIEnv*, jobject, jlong groupPtr) {
|
||||
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 void setTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPa
|
||||
|
||||
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},
|
||||
|
||||
@@ -19,6 +19,10 @@ import android.animation.AnimatorInflater;
|
||||
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.graphics.Rect;
|
||||
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 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
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 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (canvas.isHardwareAccelerated()) {
|
||||
mAnimatorSet.recordLastSeenTarget((DisplayListCanvas) canvas);
|
||||
}
|
||||
mAnimatedVectorState.mVectorDrawable.draw(canvas);
|
||||
if (isStarted()) {
|
||||
invalidateSelf();
|
||||
@@ -582,9 +598,8 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
* 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 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
@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 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
}
|
||||
};
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -730,4 +749,406 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.util.PathParser;
|
||||
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.io.IOException;
|
||||
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 class VectorDrawable extends Drawable {
|
||||
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 @@ public class VectorDrawable extends Drawable {
|
||||
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 @@ public class VectorDrawable extends Drawable {
|
||||
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 @@ public class VectorDrawable extends Drawable {
|
||||
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 @@ public class VectorDrawable extends Drawable {
|
||||
}
|
||||
}
|
||||
|
||||
@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 class VectorDrawable extends Drawable {
|
||||
|
||||
public VectorDrawableState() {
|
||||
mRootGroup = new VGroup();
|
||||
mNativeRendererPtr = nCreateRenderer(mRootGroup.mNativePtr);
|
||||
createNativeRenderer(mRootGroup.mNativePtr);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -872,16 +871,16 @@ public class VectorDrawable extends Drawable {
|
||||
* 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 @@ public class VectorDrawable extends Drawable {
|
||||
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 @@ public class VectorDrawable extends Drawable {
|
||||
/**
|
||||
* 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 @@ public class VectorDrawable extends Drawable {
|
||||
/**
|
||||
* 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 @@ public class VectorDrawable extends Drawable {
|
||||
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 @@ public class VectorDrawable extends Drawable {
|
||||
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 @@ public class VectorDrawable extends Drawable {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -78,6 +78,8 @@ hwui_src_files := \
|
||||
Program.cpp \
|
||||
ProgramCache.cpp \
|
||||
Properties.cpp \
|
||||
PropertyValuesHolder.cpp \
|
||||
PropertyValuesAnimatorSet.cpp \
|
||||
RenderBufferCache.cpp \
|
||||
RenderNode.cpp \
|
||||
RenderProperties.cpp \
|
||||
|
||||
@@ -90,6 +90,9 @@ void BaseRenderNodeAnimator::pushStaging(AnimationContext& context) {
|
||||
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 @@ bool BaseRenderNodeAnimator::animate(AnimationContext& context) {
|
||||
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 @@ bool BaseRenderNodeAnimator::animate(AnimationContext& context) {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -59,7 +59,13 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ protected:
|
||||
void callOnFinishedListener(AnimationContext& context);
|
||||
|
||||
virtual void onStagingPlayStateChanged() {}
|
||||
virtual void onPlayTimeChanged(nsecs_t playTime) {}
|
||||
|
||||
RenderNode* mTarget;
|
||||
|
||||
|
||||
@@ -43,6 +43,13 @@ typedef uint32_t Flags;
|
||||
|
||||
} // namespace SaveFlags
|
||||
|
||||
namespace uirenderer {
|
||||
namespace VectorDrawable {
|
||||
class Tree;
|
||||
};
|
||||
};
|
||||
typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
|
||||
|
||||
class ANDROID_API Canvas {
|
||||
public:
|
||||
virtual ~Canvas() {};
|
||||
@@ -185,6 +192,11 @@ public:
|
||||
*/
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -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 @@ void DisplayListCanvas::drawPoints(const float* points, int count, const SkPaint
|
||||
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;
|
||||
|
||||
@@ -209,6 +209,8 @@ public:
|
||||
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,
|
||||
@@ -217,7 +219,6 @@ public:
|
||||
float hOffset, float vOffset, const SkPaint& paint) override;
|
||||
virtual bool drawTextAbsolutePos() const override { return false; }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
CanvasState mState;
|
||||
|
||||
@@ -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"
|
||||
@@ -534,6 +535,18 @@ void FrameBuilder::deferBitmapRectOp(const BitmapRectOp& op) {
|
||||
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.
|
||||
|
||||
129
libs/hwui/PropertyValuesAnimatorSet.cpp
Normal file
129
libs/hwui/PropertyValuesAnimatorSet.cpp
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
85
libs/hwui/PropertyValuesAnimatorSet.h
Normal file
85
libs/hwui/PropertyValuesAnimatorSet.h
Normal file
@@ -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
|
||||
99
libs/hwui/PropertyValuesHolder.cpp
Normal file
99
libs/hwui/PropertyValuesHolder.cpp
Normal file
@@ -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
|
||||
121
libs/hwui/PropertyValuesHolder.h
Normal file
121
libs/hwui/PropertyValuesHolder.h
Normal file
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
@@ -38,6 +39,10 @@ class OffscreenBuffer;
|
||||
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,
|
||||
@@ -74,6 +79,7 @@ struct Vertex;
|
||||
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) \
|
||||
@@ -324,6 +330,13 @@ struct RoundRectPropsOp : RecordedOp {
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "DeferredLayerUpdater.h"
|
||||
#include "RecordedOp.h"
|
||||
#include "RenderNode.h"
|
||||
#include "VectorDrawable.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -395,7 +396,6 @@ void RecordingCanvas::drawCircle(
|
||||
&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 @@ void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
|
||||
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);
|
||||
|
||||
@@ -178,6 +178,8 @@ public:
|
||||
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,
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <SkTLazy.h>
|
||||
#include <SkTemplates.h>
|
||||
|
||||
#include "VectorDrawable.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace android {
|
||||
@@ -136,6 +138,7 @@ public:
|
||||
float hOffset, float vOffset, const SkPaint& paint) override;
|
||||
|
||||
virtual bool drawTextAbsolutePos() const override { return true; }
|
||||
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
|
||||
|
||||
private:
|
||||
struct SaveRec {
|
||||
@@ -713,6 +716,14 @@ void SkiaCanvas::drawNinePatch(const SkBitmap& bitmap, const Res_png_9patch& chu
|
||||
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
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -138,18 +138,7 @@ void Path::setPath(const char* pathStr, size_t strLength) {
|
||||
}
|
||||
|
||||
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 @@ const SkPath& FullPath::getUpdatedPath() {
|
||||
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 @@ const SkPath& FullPath::getUpdatedPath() {
|
||||
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 @@ void FullPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath, float str
|
||||
// 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 @@ void FullPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath, float str
|
||||
// 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 @@ void FullPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath, float str
|
||||
* 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 @@ void FullPath::applyTrim() {
|
||||
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 @@ void Group::getLocalMatrix(SkMatrix* outMatrix) {
|
||||
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 @@ bool Group::getProperties(float* outProperties, int length) {
|
||||
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,7 +458,9 @@ void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter,
|
||||
return;
|
||||
}
|
||||
|
||||
int saveCount = outCanvas->save(SaveFlags::MatrixClip);
|
||||
mPaint.setColorFilter(colorFilter);
|
||||
|
||||
int saveCount = outCanvas->save(SkCanvas::SaveFlags::kMatrixClip_SaveFlag);
|
||||
outCanvas->translate(mBounds.fLeft, mBounds.fTop);
|
||||
|
||||
// Handle RTL mirroring.
|
||||
@@ -458,43 +473,33 @@ void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter,
|
||||
// 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) {
|
||||
|
||||
@@ -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 @@ protected:
|
||||
|
||||
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 @@ public:
|
||||
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 @@ protected:
|
||||
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 @@ protected:
|
||||
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user