Revert "Revert "VectorDrawable native rendering - Step 4 of MANY""

This reverts commit 5a11e8d0ba21624025b89ac63bbd18befa55be0e.

Change-Id: I7a48b59c4f930dad65ddc8590c25a12636244ea2
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1fb9ac5..c232bd1 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -78,6 +78,8 @@
     Program.cpp \
     ProgramCache.cpp \
     Properties.cpp \
+    PropertyValuesHolder.cpp \
+    PropertyValuesAnimatorSet.cpp \
     RenderBufferCache.cpp \
     RenderNode.cpp \
     RenderProperties.cpp \
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 5ca2a2f..7bd2b24 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -90,6 +90,9 @@
         doSetStartValue(getValue(mTarget));
     }
     if (mStagingPlayState > mPlayState) {
+        if (mStagingPlayState == PlayState::Restarted) {
+            mStagingPlayState = PlayState::Running;
+        }
         mPlayState = mStagingPlayState;
         // Oh boy, we're starting! Man the battle stations!
         if (mPlayState == PlayState::Running) {
@@ -131,6 +134,11 @@
         return true;
     }
 
+    // This should be set before setValue() so animators can query this time when setValue
+    // is called.
+    nsecs_t currentFrameTime = context.frameTimeMs();
+    onPlayTimeChanged(currentFrameTime - mStartTime);
+
     // If BaseRenderNodeAnimator is handling the delay (not typical), then
     // because the staging properties reflect the final value, we always need
     // to call setValue even if the animation isn't yet running or is still
@@ -141,8 +149,9 @@
     }
 
     float fraction = 1.0f;
+
     if (mPlayState == PlayState::Running && mDuration > 0) {
-        fraction = (float)(context.frameTimeMs() - mStartTime) / mDuration;
+        fraction = (float)(currentFrameTime - mStartTime) / mDuration;
     }
     if (fraction >= 1.0f) {
         fraction = 1.0f;
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index aea95bf..2c9c9c3 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -59,7 +59,13 @@
         mMayRunAsync = mayRunAsync;
     }
     bool mayRunAsync() { return mMayRunAsync; }
-    ANDROID_API void start() { mStagingPlayState = PlayState::Running; onStagingPlayStateChanged(); }
+    ANDROID_API void start() {
+        if (mStagingPlayState == PlayState::NotStarted) {
+            mStagingPlayState = PlayState::Running;
+        } else {
+            mStagingPlayState = PlayState::Restarted;
+        }
+        onStagingPlayStateChanged(); }
     ANDROID_API void end() { mStagingPlayState = PlayState::Finished; onStagingPlayStateChanged(); }
 
     void attach(RenderNode* target);
@@ -77,10 +83,27 @@
     void forceEndNow(AnimationContext& context);
 
 protected:
+    // PlayState is used by mStagingPlayState and mPlayState to track the state initiated from UI
+    // thread and Render Thread animation state, respectively.
+    // From the UI thread, mStagingPlayState transition looks like
+    // NotStarted -> Running -> Finished
+    //                ^            |
+    //                |            |
+    //            Restarted <------
+    // Note: For mStagingState, the Finished state (optional) is only set when the animation is
+    // terminated by user.
+    //
+    // On Render Thread, mPlayState transition:
+    // NotStart -> Running -> Finished
+    //                ^            |
+    //                |            |
+    //                -------------
+
     enum class PlayState {
         NotStarted,
         Running,
         Finished,
+        Restarted,
     };
 
     BaseRenderNodeAnimator(float finalValue);
@@ -93,6 +116,7 @@
     void callOnFinishedListener(AnimationContext& context);
 
     virtual void onStagingPlayStateChanged() {}
+    virtual void onPlayTimeChanged(nsecs_t playTime) {}
 
     RenderNode* mTarget;
 
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index dbd502d..27facdf 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -52,6 +52,13 @@
 
 } // namespace SaveFlags
 
+namespace uirenderer {
+namespace VectorDrawable {
+class Tree;
+};
+};
+typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
+
 class ANDROID_API Canvas {
 public:
     virtual ~Canvas() {};
@@ -217,6 +224,11 @@
      */
     virtual bool drawTextAbsolutePos() const = 0;
 
+    /**
+     * Draws a VectorDrawable onto the canvas.
+     */
+    virtual void drawVectorDrawable(VectorDrawableRoot* tree);
+
 protected:
     void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
 };
diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp
index 7eaa785..3db14b5 100644
--- a/libs/hwui/DisplayListCanvas.cpp
+++ b/libs/hwui/DisplayListCanvas.cpp
@@ -16,11 +16,12 @@
 
 #include "DisplayListCanvas.h"
 
-#include "ResourceCache.h"
 #include "DeferredDisplayList.h"
 #include "DeferredLayerUpdater.h"
 #include "DisplayListOp.h"
+#include "ResourceCache.h"
 #include "RenderNode.h"
+#include "VectorDrawable.h"
 #include "utils/PaintUtils.h"
 
 #include <SkCamera.h>
@@ -412,6 +413,16 @@
     addDrawOp(new (alloc()) DrawPointsOp(points, count, refPaint(&paint)));
 }
 
+void DisplayListCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+    mDisplayList->ref(tree);
+    const SkBitmap& bitmap = tree->getBitmapUpdateIfDirty();
+    SkPaint* paint = tree->getPaint();
+    const SkRect bounds = tree->getBounds();
+    addDrawOp(new (alloc()) DrawBitmapRectOp(refBitmap(bitmap),
+            0, 0, bitmap.width(), bitmap.height(),
+            bounds.left(), bounds.top(), bounds.right(), bounds.bottom(), refPaint(paint)));
+}
+
 void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count,
         const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) {
     if (!glyphs || count <= 0) return;
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index ad93960..06e72a0 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -206,6 +206,8 @@
             float dstLeft, float dstTop, float dstRight, float dstBottom,
             const SkPaint* paint) override;
 
+    virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
+
     // Text
     virtual void drawText(const uint16_t* glyphs, const float* positions, int count,
             const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
@@ -214,7 +216,6 @@
             float hOffset, float vOffset, const SkPaint& paint) override;
     virtual bool drawTextAbsolutePos() const override { return false; }
 
-
 private:
 
     CanvasState mState;
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index c8910cb..185acce 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -19,6 +19,7 @@
 #include "Canvas.h"
 #include "LayerUpdateQueue.h"
 #include "RenderNode.h"
+#include "VectorDrawable.h"
 #include "renderstate/OffscreenBufferPool.h"
 #include "utils/FatVector.h"
 #include "utils/PaintUtils.h"
@@ -545,6 +546,18 @@
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
 }
 
+void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
+    const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
+    SkPaint* paint = op.vectorDrawable->getPaint();
+    const BitmapRectOp* resolvedOp = new (mAllocator) BitmapRectOp(op.unmappedBounds,
+            op.localMatrix,
+            op.localClip,
+            paint,
+            &bitmap,
+            Rect(bitmap.width(), bitmap.height()));
+    deferBitmapRectOp(*resolvedOp);
+}
+
 void FrameBuilder::deferCirclePropsOp(const CirclePropsOp& op) {
     // allocate a temporary oval op (with mAllocator, so it persists until render), so the
     // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp
new file mode 100644
index 0000000..eca1afcc
--- /dev/null
+++ b/libs/hwui/PropertyValuesAnimatorSet.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PropertyValuesAnimatorSet.h"
+#include "RenderNode.h"
+
+namespace android {
+namespace uirenderer {
+
+void PropertyValuesAnimatorSet::addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder,
+            Interpolator* interpolator, nsecs_t startDelay,
+            nsecs_t duration, int repeatCount) {
+
+    PropertyAnimator* animator = new PropertyAnimator(propertyValuesHolder,
+            interpolator, startDelay, duration, repeatCount);
+    mAnimators.emplace_back(animator);
+    setListener(new PropertyAnimatorSetListener(this));
+}
+
+PropertyValuesAnimatorSet::PropertyValuesAnimatorSet()
+        : BaseRenderNodeAnimator(1.0f) {
+    setStartValue(0);
+    mLastFraction = 0.0f;
+    setInterpolator(new LinearInterpolator());
+}
+
+void PropertyValuesAnimatorSet::onFinished(BaseRenderNodeAnimator* animator) {
+    if (mOneShotListener.get()) {
+        mOneShotListener->onAnimationFinished(animator);
+        mOneShotListener = nullptr;
+    }
+}
+
+float PropertyValuesAnimatorSet::getValue(RenderNode* target) const {
+    return mLastFraction;
+}
+
+void PropertyValuesAnimatorSet::setValue(RenderNode* target, float value) {
+    mLastFraction = value;
+}
+
+void PropertyValuesAnimatorSet::onPlayTimeChanged(nsecs_t playTime) {
+    for (size_t i = 0; i < mAnimators.size(); i++) {
+        mAnimators[i]->setCurrentPlayTime(playTime);
+    }
+}
+
+void PropertyValuesAnimatorSet::reset() {
+    // TODO: implement reset through adding a play state because we need to support reset() even
+    // during an animation run.
+}
+
+void PropertyValuesAnimatorSet::start(AnimationListener* listener) {
+    init();
+    mOneShotListener = listener;
+    BaseRenderNodeAnimator::start();
+}
+
+void PropertyValuesAnimatorSet::reverse(AnimationListener* listener) {
+// TODO: implement reverse
+}
+
+void PropertyValuesAnimatorSet::init() {
+    if (mInitialized) {
+        return;
+    }
+    nsecs_t maxDuration = 0;
+    for (size_t i = 0; i < mAnimators.size(); i++) {
+        if (maxDuration < mAnimators[i]->getTotalDuration()) {
+            maxDuration = mAnimators[i]->getTotalDuration();
+        }
+    }
+    mDuration = maxDuration;
+    mInitialized = true;
+}
+
+uint32_t PropertyValuesAnimatorSet::dirtyMask() {
+    return RenderNode::DISPLAY_LIST;
+}
+
+PropertyAnimator::PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator,
+        nsecs_t startDelay, nsecs_t duration, int repeatCount)
+        : mPropertyValuesHolder(holder), mInterpolator(interpolator), mStartDelay(startDelay),
+          mDuration(duration) {
+    if (repeatCount < 0) {
+        mRepeatCount = UINT32_MAX;
+    } else {
+        mRepeatCount = repeatCount;
+    }
+    mTotalDuration = ((nsecs_t) mRepeatCount + 1) * mDuration + mStartDelay;
+}
+
+void PropertyAnimator::setCurrentPlayTime(nsecs_t playTime) {
+    if (playTime >= mStartDelay && playTime < mTotalDuration) {
+         nsecs_t currentIterationPlayTime = (playTime - mStartDelay) % mDuration;
+         mLatestFraction = currentIterationPlayTime / (float) mDuration;
+    } else if (mLatestFraction < 1.0f && playTime >= mTotalDuration) {
+        mLatestFraction = 1.0f;
+    } else {
+        return;
+    }
+
+    setFraction(mLatestFraction);
+}
+
+void PropertyAnimator::setFraction(float fraction) {
+    float interpolatedFraction = mInterpolator->interpolate(mLatestFraction);
+    mPropertyValuesHolder->setFraction(interpolatedFraction);
+}
+
+void PropertyAnimatorSetListener::onAnimationFinished(BaseRenderNodeAnimator* animator) {
+    mSet->onFinished(animator);
+}
+
+}
+}
diff --git a/libs/hwui/PropertyValuesAnimatorSet.h b/libs/hwui/PropertyValuesAnimatorSet.h
new file mode 100644
index 0000000..4c7ce52
--- /dev/null
+++ b/libs/hwui/PropertyValuesAnimatorSet.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "Animator.h"
+#include "PropertyValuesHolder.h"
+#include "Interpolator.h"
+
+namespace android {
+namespace uirenderer {
+
+class PropertyAnimator {
+public:
+    PropertyAnimator(PropertyValuesHolder* holder, Interpolator* interpolator, nsecs_t startDelay,
+            nsecs_t duration, int repeatCount);
+    void setCurrentPlayTime(nsecs_t playTime);
+    nsecs_t getTotalDuration() {
+        return mTotalDuration;
+    }
+    void setFraction(float fraction);
+
+private:
+    std::unique_ptr<PropertyValuesHolder> mPropertyValuesHolder;
+    std::unique_ptr<Interpolator> mInterpolator;
+    nsecs_t mStartDelay;
+    nsecs_t mDuration;
+    uint32_t mRepeatCount;
+    nsecs_t mTotalDuration;
+    float mLatestFraction = 0.0f;
+};
+
+class ANDROID_API PropertyValuesAnimatorSet : public BaseRenderNodeAnimator {
+public:
+    friend class PropertyAnimatorSetListener;
+    PropertyValuesAnimatorSet();
+
+    void start(AnimationListener* listener);
+    void reverse(AnimationListener* listener);
+    void reset();
+
+    void addPropertyAnimator(PropertyValuesHolder* propertyValuesHolder,
+            Interpolator* interpolators, int64_t startDelays,
+            nsecs_t durations, int repeatCount);
+    virtual uint32_t dirtyMask();
+
+protected:
+    virtual float getValue(RenderNode* target) const override;
+    virtual void setValue(RenderNode* target, float value) override;
+    virtual void onPlayTimeChanged(nsecs_t playTime) override;
+
+private:
+    void init();
+    void onFinished(BaseRenderNodeAnimator* animator);
+    // Listener set from outside
+    sp<AnimationListener> mOneShotListener;
+    std::vector< std::unique_ptr<PropertyAnimator> > mAnimators;
+    float mLastFraction = 0.0f;
+    bool mInitialized = false;
+};
+
+class PropertyAnimatorSetListener : public AnimationListener {
+public:
+    PropertyAnimatorSetListener(PropertyValuesAnimatorSet* set) : mSet(set) {}
+    virtual void onAnimationFinished(BaseRenderNodeAnimator* animator) override;
+
+private:
+    PropertyValuesAnimatorSet* mSet;
+};
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/PropertyValuesHolder.cpp b/libs/hwui/PropertyValuesHolder.cpp
new file mode 100644
index 0000000..8f837f6
--- /dev/null
+++ b/libs/hwui/PropertyValuesHolder.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "PropertyValuesHolder.h"
+
+#include "utils/VectorDrawableUtils.h"
+
+#include <utils/Log.h>
+
+namespace android {
+namespace uirenderer {
+
+using namespace VectorDrawable;
+
+float PropertyValuesHolder::getValueFromData(float fraction) {
+    if (mDataSource.size() == 0) {
+        LOG_ALWAYS_FATAL("No data source is defined");
+        return 0;
+    }
+    if (fraction <= 0.0f) {
+        return mDataSource.front();
+    }
+    if (fraction >= 1.0f) {
+        return mDataSource.back();
+    }
+
+    fraction *= mDataSource.size() - 1;
+    int lowIndex = floor(fraction);
+    fraction -= lowIndex;
+
+    float value = mDataSource[lowIndex] * (1.0f - fraction)
+            + mDataSource[lowIndex + 1] * fraction;
+    return value;
+}
+
+void GroupPropertyValuesHolder::setFraction(float fraction) {
+    float animatedValue;
+    if (mDataSource.size() > 0) {
+        animatedValue = getValueFromData(fraction);
+    } else {
+        animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+    }
+    mGroup->setPropertyValue(mPropertyId, animatedValue);
+}
+
+inline U8CPU lerp(U8CPU fromValue, U8CPU toValue, float fraction) {
+    return (U8CPU) (fromValue * (1 - fraction) + toValue * fraction);
+}
+
+// TODO: Add a test for this
+SkColor FullPathColorPropertyValuesHolder::interpolateColors(SkColor fromColor, SkColor toColor,
+        float fraction) {
+    U8CPU alpha = lerp(SkColorGetA(fromColor), SkColorGetA(toColor), fraction);
+    U8CPU red = lerp(SkColorGetR(fromColor), SkColorGetR(toColor), fraction);
+    U8CPU green = lerp(SkColorGetG(fromColor), SkColorGetG(toColor), fraction);
+    U8CPU blue = lerp(SkColorGetB(fromColor), SkColorGetB(toColor), fraction);
+    return SkColorSetARGB(alpha, red, green, blue);
+}
+
+void FullPathColorPropertyValuesHolder::setFraction(float fraction) {
+    SkColor animatedValue = interpolateColors(mStartValue, mEndValue, fraction);
+    mFullPath->setColorPropertyValue(mPropertyId, animatedValue);
+}
+
+void FullPathPropertyValuesHolder::setFraction(float fraction) {
+    float animatedValue;
+    if (mDataSource.size() > 0) {
+        animatedValue = getValueFromData(fraction);
+    } else {
+        animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+    }
+    mFullPath->setPropertyValue(mPropertyId, animatedValue);
+}
+
+void PathDataPropertyValuesHolder::setFraction(float fraction) {
+    VectorDrawableUtils::interpolatePaths(&mPathData, mStartValue, mEndValue, fraction);
+    mPath->setPathData(mPathData);
+}
+
+void RootAlphaPropertyValuesHolder::setFraction(float fraction) {
+    float animatedValue = mStartValue * (1 - fraction) + mEndValue * fraction;
+    mTree->setRootAlpha(animatedValue);
+}
+
+} // namepace uirenderer
+} // namespace android
diff --git a/libs/hwui/PropertyValuesHolder.h b/libs/hwui/PropertyValuesHolder.h
new file mode 100644
index 0000000..b905fae
--- /dev/null
+++ b/libs/hwui/PropertyValuesHolder.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "VectorDrawable.h"
+
+#include <SkColor.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * PropertyValues holder contains data needed to change a property of a Vector Drawable object.
+ * When a fraction in [0f, 1f] is provided, the holder will calculate an interpolated value based
+ * on its start and end value, and set the new value on the VectorDrawble's corresponding property.
+ */
+class ANDROID_API PropertyValuesHolder {
+public:
+    virtual void setFraction(float fraction) = 0;
+    void setPropertyDataSource(float* dataSource, int length) {
+        mDataSource.insert(mDataSource.begin(), dataSource, dataSource + length);
+    }
+   float getValueFromData(float fraction);
+   virtual ~PropertyValuesHolder() {}
+protected:
+   std::vector<float> mDataSource;
+};
+
+class ANDROID_API GroupPropertyValuesHolder : public PropertyValuesHolder {
+public:
+    GroupPropertyValuesHolder(VectorDrawable::Group* ptr, int propertyId, float startValue,
+            float endValue)
+            : mGroup(ptr)
+            , mPropertyId(propertyId)
+            , mStartValue(startValue)
+            , mEndValue(endValue){
+    }
+    void setFraction(float fraction) override;
+private:
+    VectorDrawable::Group* mGroup;
+    int mPropertyId;
+    float mStartValue;
+    float mEndValue;
+};
+
+class ANDROID_API FullPathColorPropertyValuesHolder : public PropertyValuesHolder {
+public:
+    FullPathColorPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, int32_t startValue,
+            int32_t endValue)
+            : mFullPath(ptr)
+            , mPropertyId(propertyId)
+            , mStartValue(startValue)
+            , mEndValue(endValue) {};
+    void setFraction(float fraction) override;
+    static SkColor interpolateColors(SkColor fromColor, SkColor toColor, float fraction);
+private:
+    VectorDrawable::FullPath* mFullPath;
+    int mPropertyId;
+    int32_t mStartValue;
+    int32_t mEndValue;
+};
+
+class ANDROID_API FullPathPropertyValuesHolder : public PropertyValuesHolder {
+public:
+    FullPathPropertyValuesHolder(VectorDrawable::FullPath* ptr, int propertyId, float startValue,
+            float endValue)
+            : mFullPath(ptr)
+            , mPropertyId(propertyId)
+            , mStartValue(startValue)
+            , mEndValue(endValue) {};
+    void setFraction(float fraction) override;
+private:
+    VectorDrawable::FullPath* mFullPath;
+    int mPropertyId;
+    float mStartValue;
+    float mEndValue;
+};
+
+class ANDROID_API PathDataPropertyValuesHolder : public PropertyValuesHolder {
+public:
+    PathDataPropertyValuesHolder(VectorDrawable::Path* ptr, PathData* startValue,
+            PathData* endValue)
+            : mPath(ptr)
+            , mStartValue(*startValue)
+            , mEndValue(*endValue) {};
+    void setFraction(float fraction) override;
+private:
+    VectorDrawable::Path* mPath;
+    PathData mPathData;
+    PathData mStartValue;
+    PathData mEndValue;
+};
+
+class ANDROID_API RootAlphaPropertyValuesHolder : public PropertyValuesHolder {
+public:
+    RootAlphaPropertyValuesHolder(VectorDrawable::Tree* tree, float startValue, float endValue)
+            : mTree(tree)
+            , mStartValue(startValue)
+            , mEndValue(endValue) {}
+    void setFraction(float fraction) override;
+private:
+    VectorDrawable::Tree* mTree;
+    float mStartValue;
+    float mEndValue;
+};
+}
+}
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 593d690..bb26e2e 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_HWUI_RECORDED_OP_H
 #define ANDROID_HWUI_RECORDED_OP_H
 
+#include "RecordedOp.h"
 #include "font/FontUtil.h"
 #include "Matrix.h"
 #include "Rect.h"
@@ -39,6 +40,10 @@
 class RenderNode;
 struct Vertex;
 
+namespace VectorDrawable {
+class Tree;
+}
+
 /**
  * Authoritative op list, used for generating the op ID enum, ID based LUTS, and
  * the functions to which they dispatch. Parameter macros are executed for each op,
@@ -75,6 +80,7 @@
         PRE_RENDER_OP_FN(EndLayerOp) \
         PRE_RENDER_OP_FN(BeginUnclippedLayerOp) \
         PRE_RENDER_OP_FN(EndUnclippedLayerOp) \
+        PRE_RENDER_OP_FN(VectorDrawableOp) \
         \
         RENDER_ONLY_OP_FN(ShadowOp) \
         RENDER_ONLY_OP_FN(LayerOp) \
@@ -325,6 +331,13 @@
     const float* ry;
 };
 
+struct VectorDrawableOp : RecordedOp {
+    VectorDrawableOp(VectorDrawable::Tree* tree, BASE_PARAMS_PAINTLESS)
+            : SUPER_PAINTLESS(VectorDrawableOp)
+            , vectorDrawable(tree) {}
+    VectorDrawable::Tree* vectorDrawable;
+};
+
 /**
  * Real-time, dynamic-lit shadow.
  *
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 2387962..16929b8 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -19,6 +19,7 @@
 #include "DeferredLayerUpdater.h"
 #include "RecordedOp.h"
 #include "RenderNode.h"
+#include "VectorDrawable.h"
 
 namespace android {
 namespace uirenderer {
@@ -395,7 +396,6 @@
             &x->value, &y->value, &radius->value));
 }
 
-
 void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
     addOp(new (alloc()) OvalOp(
             Rect(left, top, right, bottom),
@@ -422,6 +422,15 @@
             refPaint(&paint), refPath(&path)));
 }
 
+void RecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
+    mDisplayList->ref(tree);
+    addOp(new (alloc()) VectorDrawableOp(
+            tree,
+            Rect(tree->getBounds()),
+            *(mState.currentSnapshot()->transform),
+            getRecordedClip()));
+}
+
 // Bitmap-based
 void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) {
     save(SaveFlags::Matrix);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 375760f..cc14e61 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -175,6 +175,8 @@
             const uint16_t* indices, int indexCount, const SkPaint& paint) override
         { /* RecordingCanvas does not support drawVertices(); ignore */ }
 
+    virtual void drawVectorDrawable(VectorDrawableRoot* tree) override;
+
     // Bitmap-based
     virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) override;
     virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d320a41..bd4442d 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -32,6 +32,8 @@
 #include <SkTLazy.h>
 #include <SkTemplates.h>
 
+#include "VectorDrawable.h"
+
 #include <memory>
 
 namespace android {
@@ -153,6 +155,7 @@
             float hOffset, float vOffset, const SkPaint& paint) override;
 
     virtual bool drawTextAbsolutePos() const  override { return true; }
+    virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
 
     virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left,
             uirenderer::CanvasPropertyPrimitive* top, uirenderer::CanvasPropertyPrimitive* right,
@@ -742,6 +745,14 @@
     NinePatch::Draw(mCanvas, bounds, bitmap, chunk, paint, nullptr);
 }
 
+void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) {
+    const SkBitmap& bitmap = vectorDrawable->getBitmapUpdateIfDirty();
+    SkRect bounds = vectorDrawable->getBounds();
+    drawBitmap(bitmap, 0, 0, bitmap.width(), bitmap.height(),
+            bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
+            vectorDrawable->getPaint());
+}
+
 // ----------------------------------------------------------------------------
 // Canvas draw operations: Text
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 1cf15ac..541c799 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -138,18 +138,7 @@
 }
 
 FullPath::FullPath(const FullPath& path) : Path(path) {
-    mStrokeWidth = path.mStrokeWidth;
-    mStrokeColor = path.mStrokeColor;
-    mStrokeAlpha = path.mStrokeAlpha;
-    mFillColor = path.mFillColor;
-    mFillAlpha = path.mFillAlpha;
-    mTrimPathStart = path.mTrimPathStart;
-    mTrimPathEnd = path.mTrimPathEnd;
-    mTrimPathOffset = path.mTrimPathOffset;
-    mStrokeMiterLimit = path.mStrokeMiterLimit;
-    mStrokeLineCap = path.mStrokeLineCap;
-    mStrokeLineJoin = path.mStrokeLineJoin;
-
+    mProperties = path.mProperties;
     SkRefCnt_SafeAssign(mStrokeGradient, path.mStrokeGradient);
     SkRefCnt_SafeAssign(mFillGradient, path.mFillGradient);
 }
@@ -159,7 +148,7 @@
         return mTrimmedSkPath;
     }
     Path::getUpdatedPath();
-    if (mTrimPathStart != 0.0f || mTrimPathEnd != 1.0f) {
+    if (mProperties.trimPathStart != 0.0f || mProperties.trimPathEnd != 1.0f) {
         applyTrim();
         return mTrimmedSkPath;
     } else {
@@ -170,14 +159,14 @@
 void FullPath::updateProperties(float strokeWidth, SkColor strokeColor, float strokeAlpha,
         SkColor fillColor, float fillAlpha, float trimPathStart, float trimPathEnd,
         float trimPathOffset, float strokeMiterLimit, int strokeLineCap, int strokeLineJoin) {
-    mStrokeWidth = strokeWidth;
-    mStrokeColor = strokeColor;
-    mStrokeAlpha = strokeAlpha;
-    mFillColor = fillColor;
-    mFillAlpha = fillAlpha;
-    mStrokeMiterLimit = strokeMiterLimit;
-    mStrokeLineCap = SkPaint::Cap(strokeLineCap);
-    mStrokeLineJoin = SkPaint::Join(strokeLineJoin);
+    mProperties.strokeWidth = strokeWidth;
+    mProperties.strokeColor = strokeColor;
+    mProperties.strokeAlpha = strokeAlpha;
+    mProperties.fillColor = fillColor;
+    mProperties.fillAlpha = fillAlpha;
+    mProperties.strokeMiterLimit = strokeMiterLimit;
+    mProperties.strokeLineCap = strokeLineCap;
+    mProperties.strokeLineJoin = strokeLineJoin;
 
     // If any trim property changes, mark trim dirty and update the trim path
     setTrimPathStart(trimPathStart);
@@ -195,12 +184,12 @@
     // Draw path's fill, if fill color or gradient is valid
     bool needsFill = false;
     if (mFillGradient != nullptr) {
-        mPaint.setColor(applyAlpha(SK_ColorBLACK, mFillAlpha));
+        mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.fillAlpha));
         SkShader* newShader = mFillGradient->newWithLocalMatrix(matrix);
         mPaint.setShader(newShader);
         needsFill = true;
-    } else if (mFillColor != SK_ColorTRANSPARENT) {
-        mPaint.setColor(applyAlpha(mFillColor, mFillAlpha));
+    } else if (mProperties.fillColor != SK_ColorTRANSPARENT) {
+        mPaint.setColor(applyAlpha(mProperties.fillColor, mProperties.fillAlpha));
         needsFill = true;
     }
 
@@ -213,21 +202,21 @@
     // Draw path's stroke, if stroke color or gradient is valid
     bool needsStroke = false;
     if (mStrokeGradient != nullptr) {
-        mPaint.setColor(applyAlpha(SK_ColorBLACK, mStrokeAlpha));
+        mPaint.setColor(applyAlpha(SK_ColorBLACK, mProperties.strokeAlpha));
         SkShader* newShader = mStrokeGradient->newWithLocalMatrix(matrix);
         mPaint.setShader(newShader);
         needsStroke = true;
-    } else if (mStrokeColor != SK_ColorTRANSPARENT) {
-        mPaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
+    } else if (mProperties.strokeColor != SK_ColorTRANSPARENT) {
+        mPaint.setColor(applyAlpha(mProperties.strokeColor, mProperties.strokeAlpha));
         needsStroke = true;
     }
     if (needsStroke) {
         mPaint.setStyle(SkPaint::Style::kStroke_Style);
         mPaint.setAntiAlias(true);
-        mPaint.setStrokeJoin(mStrokeLineJoin);
-        mPaint.setStrokeCap(mStrokeLineCap);
-        mPaint.setStrokeMiter(mStrokeMiterLimit);
-        mPaint.setStrokeWidth(mStrokeWidth * strokeScale);
+        mPaint.setStrokeJoin(SkPaint::Join(mProperties.strokeLineJoin));
+        mPaint.setStrokeCap(SkPaint::Cap(mProperties.strokeLineCap));
+        mPaint.setStrokeMiter(mProperties.strokeMiterLimit);
+        mPaint.setStrokeWidth(mProperties.strokeWidth * strokeScale);
         outCanvas->drawPath(renderPath, mPaint);
     }
 }
@@ -236,14 +225,14 @@
  * Applies trimming to the specified path.
  */
 void FullPath::applyTrim() {
-    if (mTrimPathStart == 0.0f && mTrimPathEnd == 1.0f) {
+    if (mProperties.trimPathStart == 0.0f && mProperties.trimPathEnd == 1.0f) {
         // No trimming necessary.
         return;
     }
     SkPathMeasure measure(mSkPath, false);
     float len = SkScalarToFloat(measure.getLength());
-    float start = len * fmod((mTrimPathStart + mTrimPathOffset), 1.0f);
-    float end = len * fmod((mTrimPathEnd + mTrimPathOffset), 1.0f);
+    float start = len * fmod((mProperties.trimPathStart + mProperties.trimPathOffset), 1.0f);
+    float end = len * fmod((mProperties.trimPathEnd + mProperties.trimPathOffset), 1.0f);
 
     mTrimmedSkPath.reset();
     if (start > end) {
@@ -255,76 +244,69 @@
     mTrimDirty = false;
 }
 
-inline int putData(int8_t* outBytes, int startIndex, float value) {
-    int size = sizeof(float);
-    memcpy(&outBytes[startIndex], &value, size);
-    return size;
-}
-
-inline int putData(int8_t* outBytes, int startIndex, int value) {
-    int size = sizeof(int);
-    memcpy(&outBytes[startIndex], &value, size);
-    return size;
-}
-
-struct FullPathProperties {
-    // TODO: Consider storing full path properties in this struct instead of the fields.
-    float strokeWidth;
-    SkColor strokeColor;
-    float strokeAlpha;
-    SkColor fillColor;
-    float fillAlpha;
-    float trimPathStart;
-    float trimPathEnd;
-    float trimPathOffset;
-    int32_t strokeLineCap;
-    int32_t strokeLineJoin;
-    float strokeMiterLimit;
-};
-
-REQUIRE_COMPATIBLE_LAYOUT(FullPathProperties);
+REQUIRE_COMPATIBLE_LAYOUT(FullPath::Properties);
 
 static_assert(sizeof(float) == sizeof(int32_t), "float is not the same size as int32_t");
 static_assert(sizeof(SkColor) == sizeof(int32_t), "SkColor is not the same size as int32_t");
 
 bool FullPath::getProperties(int8_t* outProperties, int length) {
-    int propertyDataSize = sizeof(FullPathProperties);
+    int propertyDataSize = sizeof(Properties);
     if (length != propertyDataSize) {
         LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
                 propertyDataSize, length);
         return false;
     }
-    // TODO: consider replacing the property fields with a FullPathProperties struct.
-    FullPathProperties properties;
-    properties.strokeWidth = mStrokeWidth;
-    properties.strokeColor = mStrokeColor;
-    properties.strokeAlpha = mStrokeAlpha;
-    properties.fillColor = mFillColor;
-    properties.fillAlpha = mFillAlpha;
-    properties.trimPathStart = mTrimPathStart;
-    properties.trimPathEnd = mTrimPathEnd;
-    properties.trimPathOffset = mTrimPathOffset;
-    properties.strokeLineCap = mStrokeLineCap;
-    properties.strokeLineJoin = mStrokeLineJoin;
-    properties.strokeMiterLimit = mStrokeMiterLimit;
-
-    memcpy(outProperties, &properties, length);
+    Properties* out = reinterpret_cast<Properties*>(outProperties);
+    *out = mProperties;
     return true;
 }
 
+void FullPath::setColorPropertyValue(int propertyId, int32_t value) {
+    Property currentProperty = static_cast<Property>(propertyId);
+    if (currentProperty == Property::StrokeColor) {
+        mProperties.strokeColor = value;
+    } else if (currentProperty == Property::FillColor) {
+        mProperties.fillColor = value;
+    } else {
+        LOG_ALWAYS_FATAL("Error setting color property on FullPath: No valid property with id: %d",
+                propertyId);
+    }
+}
+
+void FullPath::setPropertyValue(int propertyId, float value) {
+    Property property = static_cast<Property>(propertyId);
+    switch (property) {
+    case Property::StrokeWidth:
+        setStrokeWidth(value);
+        break;
+    case Property::StrokeAlpha:
+        setStrokeAlpha(value);
+        break;
+    case Property::FillAlpha:
+        setFillAlpha(value);
+        break;
+    case Property::TrimPathStart:
+        setTrimPathStart(value);
+        break;
+    case Property::TrimPathEnd:
+        setTrimPathEnd(value);
+        break;
+    case Property::TrimPathOffset:
+        setTrimPathOffset(value);
+        break;
+    default:
+        LOG_ALWAYS_FATAL("Invalid property id: %d for animation", propertyId);
+        break;
+    }
+}
+
 void ClipPath::drawPath(SkCanvas* outCanvas, const SkPath& renderPath,
         float strokeScale, const SkMatrix& matrix){
     outCanvas->clipPath(renderPath, SkRegion::kIntersect_Op);
 }
 
 Group::Group(const Group& group) : Node(group) {
-    mRotate = group.mRotate;
-    mPivotX = group.mPivotX;
-    mPivotY = group.mPivotY;
-    mScaleX = group.mScaleX;
-    mScaleY = group.mScaleY;
-    mTranslateX = group.mTranslateX;
-    mTranslateY = group.mTranslateY;
+    mProperties = group.mProperties;
 }
 
 void Group::draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, float scaleX,
@@ -371,10 +353,11 @@
     outMatrix->reset();
     // TODO: use rotate(mRotate, mPivotX, mPivotY) and scale with pivot point, instead of
     // translating to pivot for rotating and scaling, then translating back.
-    outMatrix->postTranslate(-mPivotX, -mPivotY);
-    outMatrix->postScale(mScaleX, mScaleY);
-    outMatrix->postRotate(mRotate, 0, 0);
-    outMatrix->postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
+    outMatrix->postTranslate(-mProperties.pivotX, -mProperties.pivotY);
+    outMatrix->postScale(mProperties.scaleX, mProperties.scaleY);
+    outMatrix->postRotate(mProperties.rotate, 0, 0);
+    outMatrix->postTranslate(mProperties.translateX + mProperties.pivotX,
+            mProperties.translateY + mProperties.pivotY);
 }
 
 void Group::addChild(Node* child) {
@@ -388,38 +371,68 @@
                 propertyCount, length);
         return false;
     }
-    for (int i = 0; i < propertyCount; i++) {
-        Property currentProperty = static_cast<Property>(i);
-        switch (currentProperty) {
-        case Property::Rotate_Property:
-            outProperties[i] = mRotate;
-            break;
-        case Property::PivotX_Property:
-            outProperties[i] = mPivotX;
-            break;
-        case Property::PivotY_Property:
-            outProperties[i] = mPivotY;
-            break;
-        case Property::ScaleX_Property:
-            outProperties[i] = mScaleX;
-            break;
-        case Property::ScaleY_Property:
-            outProperties[i] = mScaleY;
-            break;
-        case Property::TranslateX_Property:
-            outProperties[i] = mTranslateX;
-            break;
-        case Property::TranslateY_Property:
-            outProperties[i] = mTranslateY;
-            break;
-        default:
-            LOG_ALWAYS_FATAL("Invalid input index: %d", i);
-            return false;
-        }
-    }
+    Properties* out = reinterpret_cast<Properties*>(outProperties);
+    *out = mProperties;
     return true;
 }
 
+// TODO: Consider animating the properties as float pointers
+float Group::getPropertyValue(int propertyId) const {
+    Property currentProperty = static_cast<Property>(propertyId);
+    switch (currentProperty) {
+    case Property::Rotate:
+        return mProperties.rotate;
+    case Property::PivotX:
+        return mProperties.pivotX;
+    case Property::PivotY:
+        return mProperties.pivotY;
+    case Property::ScaleX:
+        return mProperties.scaleX;
+    case Property::ScaleY:
+        return mProperties.scaleY;
+    case Property::TranslateX:
+        return mProperties.translateX;
+    case Property::TranslateY:
+        return mProperties.translateY;
+    default:
+        LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
+        return 0;
+    }
+}
+
+void Group::setPropertyValue(int propertyId, float value) {
+    Property currentProperty = static_cast<Property>(propertyId);
+    switch (currentProperty) {
+    case Property::Rotate:
+        mProperties.rotate = value;
+        break;
+    case Property::PivotX:
+        mProperties.pivotX = value;
+        break;
+    case Property::PivotY:
+        mProperties.pivotY = value;
+        break;
+    case Property::ScaleX:
+        mProperties.scaleX = value;
+        break;
+    case Property::ScaleY:
+        mProperties.scaleY = value;
+        break;
+    case Property::TranslateX:
+        mProperties.translateX = value;
+        break;
+    case Property::TranslateY:
+        mProperties.translateY = value;
+        break;
+    default:
+        LOG_ALWAYS_FATAL("Invalid property index: %d", propertyId);
+    }
+}
+
+bool Group::isValidProperty(int propertyId) {
+    return propertyId >= 0 && propertyId < static_cast<int>(Property::Count);
+}
+
 void Tree::draw(Canvas* outCanvas, SkColorFilter* colorFilter,
         const SkRect& bounds, bool needsMirroring, bool canReuseCache) {
     // The imageView can scale the canvas in different ways, in order to
@@ -445,6 +458,8 @@
         return;
     }
 
+    mPaint.setColorFilter(colorFilter);
+
     int saveCount = outCanvas->save(SaveFlags::MatrixClip);
     outCanvas->translate(mBounds.fLeft, mBounds.fTop);
 
@@ -458,43 +473,33 @@
     // And we use this bound for the destination rect for the drawBitmap, so
     // we offset to (0, 0);
     mBounds.offsetTo(0, 0);
-
     createCachedBitmapIfNeeded(scaledWidth, scaledHeight);
-    if (!mAllowCaching) {
-        updateCachedBitmap(scaledWidth, scaledHeight);
-    } else {
-        if (!canReuseCache || mCacheDirty) {
-            updateCachedBitmap(scaledWidth, scaledHeight);
-        }
-    }
-    drawCachedBitmapWithRootAlpha(outCanvas, colorFilter, mBounds);
+
+    outCanvas->drawVectorDrawable(this);
 
     outCanvas->restoreToCount(saveCount);
 }
 
-void Tree::drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter,
-        const SkRect& originalBounds) {
+SkPaint* Tree::getPaint() {
     SkPaint* paint;
-    if (mRootAlpha == 1.0f && filter == NULL) {
+    if (mRootAlpha == 1.0f && mPaint.getColorFilter() == NULL) {
         paint = NULL;
     } else {
         mPaint.setFilterQuality(kLow_SkFilterQuality);
         mPaint.setAlpha(mRootAlpha * 255);
-        mPaint.setColorFilter(filter);
         paint = &mPaint;
     }
-    outCanvas->drawBitmap(mCachedBitmap, 0, 0, mCachedBitmap.width(), mCachedBitmap.height(),
-            originalBounds.fLeft, originalBounds.fTop, originalBounds.fRight,
-            originalBounds.fBottom, paint);
+    return paint;
 }
 
-void Tree::updateCachedBitmap(int width, int height) {
+const SkBitmap& Tree::getBitmapUpdateIfDirty() {
     mCachedBitmap.eraseColor(SK_ColorTRANSPARENT);
     SkCanvas outCanvas(mCachedBitmap);
-    float scaleX = width / mViewportWidth;
-    float scaleY = height / mViewportHeight;
+    float scaleX = (float) mCachedBitmap.width() / mViewportWidth;
+    float scaleY = (float) mCachedBitmap.height() / mViewportHeight;
     mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY);
     mCacheDirty = false;
+    return mCachedBitmap;
 }
 
 void Tree::createCachedBitmapIfNeeded(int width, int height) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 09bdce5..f8f1ea6 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -18,6 +18,7 @@
 #define ANDROID_HWUI_VPATH_H
 
 #include "Canvas.h"
+
 #include <SkBitmap.h>
 #include <SkColor.h>
 #include <SkCanvas.h>
@@ -104,6 +105,21 @@
 
 class ANDROID_API FullPath: public Path {
 public:
+
+struct Properties {
+    float strokeWidth = 0;
+    SkColor strokeColor = SK_ColorTRANSPARENT;
+    float strokeAlpha = 1;
+    SkColor fillColor = SK_ColorTRANSPARENT;
+    float fillAlpha = 1;
+    float trimPathStart = 0;
+    float trimPathEnd = 1;
+    float trimPathOffset = 0;
+    int32_t strokeLineCap = SkPaint::Cap::kButt_Cap;
+    int32_t strokeLineJoin = SkPaint::Join::kMiter_Join;
+    float strokeMiterLimit = 4;
+};
+
     FullPath(const FullPath& path); // for cloning
     FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
     FullPath() : Path() {}
@@ -118,55 +134,58 @@
             float strokeAlpha, SkColor fillColor, float fillAlpha,
             float trimPathStart, float trimPathEnd, float trimPathOffset,
             float strokeMiterLimit, int strokeLineCap, int strokeLineJoin);
+    // TODO: Cleanup: Remove the setter and getters below, and their counterparts in java and JNI
     float getStrokeWidth() {
-        return mStrokeWidth;
+        return mProperties.strokeWidth;
     }
     void setStrokeWidth(float strokeWidth) {
-        mStrokeWidth = strokeWidth;
+        mProperties.strokeWidth = strokeWidth;
     }
     SkColor getStrokeColor() {
-        return mStrokeColor;
+        return mProperties.strokeColor;
     }
     void setStrokeColor(SkColor strokeColor) {
-        mStrokeColor = strokeColor;
+        mProperties.strokeColor = strokeColor;
     }
     float getStrokeAlpha() {
-        return mStrokeAlpha;
+        return mProperties.strokeAlpha;
     }
     void setStrokeAlpha(float strokeAlpha) {
-        mStrokeAlpha = strokeAlpha;
+        mProperties.strokeAlpha = strokeAlpha;
     }
     SkColor getFillColor() {
-        return mFillColor;
+        return mProperties.fillColor;
     }
     void setFillColor(SkColor fillColor) {
-        mFillColor = fillColor;
+        mProperties.fillColor = fillColor;
     }
     float getFillAlpha() {
-        return mFillAlpha;
+        return mProperties.fillAlpha;
     }
     void setFillAlpha(float fillAlpha) {
-        mFillAlpha = fillAlpha;
+        mProperties.fillAlpha = fillAlpha;
     }
     float getTrimPathStart() {
-        return mTrimPathStart;
+        return mProperties.trimPathStart;
     }
     void setTrimPathStart(float trimPathStart) {
-        VD_SET_PROP_WITH_FLAG(mTrimPathStart, trimPathStart, mTrimDirty);
+        VD_SET_PROP_WITH_FLAG(mProperties.trimPathStart, trimPathStart, mTrimDirty);
     }
     float getTrimPathEnd() {
-        return mTrimPathEnd;
+        return mProperties.trimPathEnd;
     }
     void setTrimPathEnd(float trimPathEnd) {
-        VD_SET_PROP_WITH_FLAG(mTrimPathEnd, trimPathEnd, mTrimDirty);
+        VD_SET_PROP_WITH_FLAG(mProperties.trimPathEnd, trimPathEnd, mTrimDirty);
     }
     float getTrimPathOffset() {
-        return mTrimPathOffset;
+        return mProperties.trimPathOffset;
     }
     void setTrimPathOffset(float trimPathOffset) {
-        VD_SET_PROP_WITH_FLAG(mTrimPathOffset, trimPathOffset, mTrimDirty);
+        VD_SET_PROP_WITH_FLAG(mProperties.trimPathOffset, trimPathOffset, mTrimDirty);
     }
     bool getProperties(int8_t* outProperties, int length);
+    void setColorPropertyValue(int propertyId, int32_t value);
+    void setPropertyValue(int propertyId, float value);
 
     void setFillGradient(SkShader* fillGradient) {
         SkRefCnt_SafeAssign(mFillGradient, fillGradient);
@@ -182,24 +201,28 @@
             float strokeScale, const SkMatrix& matrix) override;
 
 private:
+    enum class Property {
+        StrokeWidth = 0,
+        StrokeColor,
+        StrokeAlpha,
+        FillColor,
+        FillAlpha,
+        TrimPathStart,
+        TrimPathEnd,
+        TrimPathOffset,
+        StrokeLineCap,
+        StrokeLineJoin,
+        StrokeMiterLimit,
+        Count,
+    };
     // Applies trimming to the specified path.
     void applyTrim();
-    float mStrokeWidth = 0;
-    SkColor mStrokeColor = SK_ColorTRANSPARENT;
-    float mStrokeAlpha = 1;
-    SkColor mFillColor = SK_ColorTRANSPARENT;
-    SkShader* mStrokeGradient = nullptr;
-    SkShader* mFillGradient = nullptr;
-    float mFillAlpha = 1;
-    float mTrimPathStart = 0;
-    float mTrimPathEnd = 1;
-    float mTrimPathOffset = 0;
+    Properties mProperties;
     bool mTrimDirty = true;
-    SkPaint::Cap mStrokeLineCap = SkPaint::Cap::kButt_Cap;
-    SkPaint::Join mStrokeLineJoin = SkPaint::Join::kMiter_Join;
-    float mStrokeMiterLimit = 4;
     SkPath mTrimmedSkPath;
     SkPaint mPaint;
+    SkShader* mStrokeGradient = nullptr;
+    SkShader* mFillGradient = nullptr;
 };
 
 class ANDROID_API ClipPath: public Path {
@@ -216,49 +239,58 @@
 
 class ANDROID_API Group: public Node {
 public:
+    struct Properties {
+        float rotate = 0;
+        float pivotX = 0;
+        float pivotY = 0;
+        float scaleX = 1;
+        float scaleY = 1;
+        float translateX = 0;
+        float translateY = 0;
+    };
     Group(const Group& group);
     Group() {}
     float getRotation() {
-        return mRotate;
+        return mProperties.rotate;
     }
     void setRotation(float rotation) {
-        mRotate = rotation;
+        mProperties.rotate = rotation;
     }
     float getPivotX() {
-        return mPivotX;
+        return mProperties.pivotX;
     }
     void setPivotX(float pivotX) {
-        mPivotX = pivotX;
+        mProperties.pivotX = pivotX;
     }
     float getPivotY() {
-        return mPivotY;
+        return mProperties.pivotY;
     }
     void setPivotY(float pivotY) {
-        mPivotY = pivotY;
+        mProperties.pivotY = pivotY;
     }
     float getScaleX() {
-        return mScaleX;
+        return mProperties.scaleX;
     }
     void setScaleX(float scaleX) {
-        mScaleX = scaleX;
+        mProperties.scaleX = scaleX;
     }
     float getScaleY() {
-        return mScaleY;
+        return mProperties.scaleY;
     }
     void setScaleY(float scaleY) {
-        mScaleY = scaleY;
+        mProperties.scaleY = scaleY;
     }
     float getTranslateX() {
-        return mTranslateX;
+        return mProperties.translateX;
     }
     void setTranslateX(float translateX) {
-        mTranslateX = translateX;
+        mProperties.translateX = translateX;
     }
     float getTranslateY() {
-        return mTranslateY;
+        return mProperties.translateY;
     }
     void setTranslateY(float translateY) {
-        mTranslateY = translateY;
+        mProperties.translateY = translateY;
     }
     virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix,
             float scaleX, float scaleY) override;
@@ -268,38 +300,33 @@
     void addChild(Node* child);
     void dump() override;
     bool getProperties(float* outProperties, int length);
+    float getPropertyValue(int propertyId) const;
+    void setPropertyValue(int propertyId, float value);
+    static bool isValidProperty(int propertyId);
 
 private:
     enum class Property {
-        Rotate_Property = 0,
-        PivotX_Property,
-        PivotY_Property,
-        ScaleX_Property,
-        ScaleY_Property,
-        TranslateX_Property,
-        TranslateY_Property,
+        Rotate = 0,
+        PivotX,
+        PivotY,
+        ScaleX,
+        ScaleY,
+        TranslateX,
+        TranslateY,
         // Count of the properties, must be at the end.
         Count,
     };
-    float mRotate = 0;
-    float mPivotX = 0;
-    float mPivotY = 0;
-    float mScaleX = 1;
-    float mScaleY = 1;
-    float mTranslateX = 0;
-    float mTranslateY = 0;
     std::vector<Node*> mChildren;
+    Properties mProperties;
 };
 
-class ANDROID_API Tree {
+class ANDROID_API Tree : public VirtualLightRefBase {
 public:
     Tree(Group* rootNode) : mRootNode(rootNode) {}
     void draw(Canvas* outCanvas, SkColorFilter* colorFilter,
             const SkRect& bounds, bool needsMirroring, bool canReuseCache);
-    void drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter,
-            const SkRect& originalBounds);
 
-    void updateCachedBitmap(int width, int height);
+    const SkBitmap& getBitmapUpdateIfDirty();
     void createCachedBitmapIfNeeded(int width, int height);
     bool canReuseBitmap(int width, int height);
     void setAllowCaching(bool allowCaching) {
@@ -316,6 +343,10 @@
         mViewportWidth = viewportWidth;
         mViewportHeight = viewportHeight;
     }
+    SkPaint* getPaint();
+    const SkRect& getBounds() const {
+        return mBounds;
+    }
 
 private:
     // Cap the bitmap size, such that it won't hurt the performance too much