VectorDrawable native rendering - Step 3 of MANY

- Refactored VPathRenderer & VectorDrawableState
- Moved all the VD rendering into native
- Set up hooks for VD's property changes in JNI for animated VD

TODO: JNI calls can be further reduced when we convert the animation
in AVD to use RenderNodeAnimator, in which case animation will be
driven from native and therefore most of the JNI hooks for changing
VD's properties during animation will no longer be needed.

Change-Id: I52021f4d7bea057b83ace54085d870dd45acae0f
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 0d1ee46..f48c509 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -86,7 +86,7 @@
     TextDropShadowCache.cpp \
     Texture.cpp \
     TextureCache.cpp \
-    VectorDrawablePath.cpp \
+    VectorDrawable.cpp \
     protos/hwui.proto
 
 hwui_test_common_src_files := \
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index 4c87b18..c4bbb74 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_HWUI_PATHPARSER_H
 #define ANDROID_HWUI_PATHPARSER_H
 
-#include "VectorDrawablePath.h"
+#include "VectorDrawable.h"
 #include "utils/VectorDrawableUtils.h"
 
 #include <jni.h>
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
new file mode 100644
index 0000000..e13a2bb
--- /dev/null
+++ b/libs/hwui/VectorDrawable.cpp
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2015 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 "VectorDrawable.h"
+
+#include "PathParser.h"
+#include "SkImageInfo.h"
+#include <utils/Log.h>
+#include "utils/Macros.h"
+#include "utils/VectorDrawableUtils.h"
+
+#include <math.h>
+#include <string.h>
+
+namespace android {
+namespace uirenderer {
+namespace VectorDrawable {
+
+const int Tree::MAX_CACHED_BITMAP_SIZE = 2048;
+
+void Path::draw(Canvas* outCanvas, const SkMatrix& groupStackedMatrix, float scaleX, float scaleY) {
+    float matrixScale = getMatrixScale(groupStackedMatrix);
+    if (matrixScale == 0) {
+        // When either x or y is scaled to 0, we don't need to draw anything.
+        return;
+    }
+
+    const SkPath updatedPath = getUpdatedPath();
+    SkMatrix pathMatrix(groupStackedMatrix);
+    pathMatrix.postScale(scaleX, scaleY);
+
+    //TODO: try apply the path matrix to the canvas instead of creating a new path.
+    SkPath renderPath;
+    renderPath.reset();
+    renderPath.addPath(updatedPath, pathMatrix);
+
+    float minScale = fmin(scaleX, scaleY);
+    float strokeScale = minScale * matrixScale;
+    drawPath(outCanvas, renderPath, strokeScale);
+}
+
+void Path::setPathData(const Data& data) {
+    if (mData == data) {
+        return;
+    }
+    // Updates the path data. Note that we don't generate a new Skia path right away
+    // because there are cases where the animation is changing the path data, but the view
+    // that hosts the VD has gone off screen, in which case we won't even draw. So we
+    // postpone the Skia path generation to the draw time.
+    mData = data;
+    mSkPathDirty = true;
+}
+
+void Path::dump() {
+    ALOGD("Path: %s has %zu points", mName.c_str(), mData.points.size());
+}
+
+float Path::getMatrixScale(const SkMatrix& groupStackedMatrix) {
+    // Given unit vectors A = (0, 1) and B = (1, 0).
+    // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'.
+    // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)),
+    // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|);
+    // If  max (|A'|, |B'|) = 0, that means either x or y has a scale of 0.
+    //
+    // For non-skew case, which is most of the cases, matrix scale is computing exactly the
+    // scale on x and y axis, and take the minimal of these two.
+    // For skew case, an unit square will mapped to a parallelogram. And this function will
+    // return the minimal height of the 2 bases.
+    SkVector skVectors[2];
+    skVectors[0].set(0, 1);
+    skVectors[1].set(1, 0);
+    groupStackedMatrix.mapVectors(skVectors, 2);
+    float scaleX = hypotf(skVectors[0].fX, skVectors[0].fY);
+    float scaleY = hypotf(skVectors[1].fX, skVectors[1].fY);
+    float crossProduct = skVectors[0].cross(skVectors[1]);
+    float maxScale = fmax(scaleX, scaleY);
+
+    float matrixScale = 0;
+    if (maxScale > 0) {
+        matrixScale = fabs(crossProduct) / maxScale;
+    }
+    return matrixScale;
+}
+Path::Path(const char* pathStr, size_t strLength) {
+    PathParser::ParseResult result;
+    PathParser::getPathDataFromString(&mData, &result, pathStr, strLength);
+    if (!result.failureOccurred) {
+        VectorDrawableUtils::verbsToPath(&mSkPath, mData);
+    }
+}
+
+Path::Path(const Data& data) {
+    mData = data;
+    // Now we need to construct a path
+    VectorDrawableUtils::verbsToPath(&mSkPath, data);
+}
+
+Path::Path(const Path& path) : Node(path) {
+    mData = path.mData;
+    VectorDrawableUtils::verbsToPath(&mSkPath, mData);
+}
+
+bool Path::canMorph(const Data& morphTo) {
+    return VectorDrawableUtils::canMorph(mData, morphTo);
+}
+
+bool Path::canMorph(const Path& path) {
+    return canMorph(path.mData);
+}
+
+const SkPath& Path::getUpdatedPath() {
+    if (mSkPathDirty) {
+        mSkPath.reset();
+        VectorDrawableUtils::verbsToPath(&mSkPath, mData);
+        mSkPathDirty = false;
+    }
+    return mSkPath;
+}
+
+void Path::setPath(const char* pathStr, size_t strLength) {
+    PathParser::ParseResult result;
+    mSkPathDirty = true;
+    PathParser::getPathDataFromString(&mData, &result, pathStr, 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;
+}
+
+const SkPath& FullPath::getUpdatedPath() {
+    if (!mSkPathDirty && !mTrimDirty) {
+        return mTrimmedSkPath;
+    }
+    Path::getUpdatedPath();
+    if (mTrimPathStart != 0.0f || mTrimPathEnd != 1.0f) {
+        applyTrim();
+        return mTrimmedSkPath;
+    } else {
+        return mSkPath;
+    }
+}
+
+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);
+
+    // If any trim property changes, mark trim dirty and update the trim path
+    setTrimPathStart(trimPathStart);
+    setTrimPathEnd(trimPathEnd);
+    setTrimPathOffset(trimPathOffset);
+}
+
+inline SkColor applyAlpha(SkColor color, float alpha) {
+    int alphaBytes = SkColorGetA(color);
+    return SkColorSetA(color, alphaBytes * alpha);
+}
+
+void FullPath::drawPath(Canvas* outCanvas, const SkPath& renderPath, float strokeScale){
+    // Draw path's fill, if fill color isn't transparent.
+    if (mFillColor != SK_ColorTRANSPARENT) {
+        mPaint.setStyle(SkPaint::Style::kFill_Style);
+        mPaint.setAntiAlias(true);
+        mPaint.setColor(applyAlpha(mFillColor, mFillAlpha));
+        outCanvas->drawPath(renderPath, mPaint);
+    }
+    // Draw path's stroke, if stroke color isn't transparent
+    if (mStrokeColor != SK_ColorTRANSPARENT) {
+        mPaint.setStyle(SkPaint::Style::kStroke_Style);
+        mPaint.setAntiAlias(true);
+        mPaint.setStrokeJoin(mStrokeLineJoin);
+        mPaint.setStrokeCap(mStrokeLineCap);
+        mPaint.setStrokeMiter(mStrokeMiterLimit);
+        mPaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
+        mPaint.setStrokeWidth(mStrokeWidth * strokeScale);
+        outCanvas->drawPath(renderPath, mPaint);
+    }
+}
+
+/**
+ * Applies trimming to the specified path.
+ */
+void FullPath::applyTrim() {
+    if (mTrimPathStart == 0.0f && mTrimPathEnd == 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);
+
+    mTrimmedSkPath.reset();
+    if (start > end) {
+        measure.getSegment(start, len, &mTrimmedSkPath, true);
+        measure.getSegment(0, end, &mTrimmedSkPath, true);
+    } else {
+        measure.getSegment(start, end, &mTrimmedSkPath, true);
+    }
+    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);
+
+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);
+    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);
+    return true;
+}
+
+void ClipPath::drawPath(Canvas* outCanvas, const SkPath& renderPath,
+        float strokeScale){
+    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;
+}
+
+void Group::draw(Canvas* outCanvas, const SkMatrix& currentMatrix, float scaleX,
+        float scaleY) {
+    // TODO: Try apply the matrix to the canvas instead of passing it down the tree
+
+    // Calculate current group's matrix by preConcat the parent's and
+    // and the current one on the top of the stack.
+    // Basically the Mfinal = Mviewport * M0 * M1 * M2;
+    // Mi the local matrix at level i of the group tree.
+    SkMatrix stackedMatrix;
+    getLocalMatrix(&stackedMatrix);
+    stackedMatrix.postConcat(currentMatrix);
+
+    // Save the current clip information, which is local to this group.
+    outCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+    // Draw the group tree in the same order as the XML file.
+    for (Node* child : mChildren) {
+        child->draw(outCanvas, stackedMatrix, scaleX, scaleY);
+    }
+    // Restore the previous clip information.
+    outCanvas->restore();
+}
+
+void Group::dump() {
+    ALOGD("Group %s has %zu children: ", mName.c_str(), mChildren.size());
+    for (size_t i = 0; i < mChildren.size(); i++) {
+        mChildren[i]->dump();
+    }
+}
+
+void Group::updateLocalMatrix(float rotate, float pivotX, float pivotY,
+        float scaleX, float scaleY, float translateX, float translateY) {
+    setRotation(rotate);
+    setPivotX(pivotX);
+    setPivotY(pivotY);
+    setScaleX(scaleX);
+    setScaleY(scaleY);
+    setTranslateX(translateX);
+    setTranslateY(translateY);
+}
+
+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);
+}
+
+void Group::addChild(Node* child) {
+    mChildren.push_back(child);
+}
+
+bool Group::getProperties(float* outProperties, int length) {
+    int propertyCount = static_cast<int>(Property::Count);
+    if (length != propertyCount) {
+        LOG_ALWAYS_FATAL("Properties needs exactly %d bytes, a byte array of size %d is provided",
+                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;
+        }
+    }
+    return true;
+}
+
+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
+    // avoid blurry scaling, we have to draw into a bitmap with exact pixel
+    // size first. This bitmap size is determined by the bounds and the
+    // canvas scale.
+    outCanvas->getMatrix(&mCanvasMatrix);
+    mBounds = bounds;
+    int scaledWidth = (int) (mBounds.width() * mCanvasMatrix.getScaleX());
+    int scaledHeight = (int) (mBounds.height() * mCanvasMatrix.getScaleY());
+    scaledWidth = std::min(Tree::MAX_CACHED_BITMAP_SIZE, scaledWidth);
+    scaledHeight = std::min(Tree::MAX_CACHED_BITMAP_SIZE, scaledHeight);
+
+    if (scaledWidth <= 0 || scaledHeight <= 0) {
+        return;
+    }
+
+    int saveCount = outCanvas->save(SkCanvas::SaveFlags::kMatrixClip_SaveFlag);
+    outCanvas->translate(mBounds.fLeft, mBounds.fTop);
+
+    // Handle RTL mirroring.
+    if (needsMirroring) {
+        outCanvas->translate(mBounds.width(), 0);
+        outCanvas->scale(-1.0f, 1.0f);
+    }
+
+    // At this point, canvas has been translated to the right position.
+    // 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->restoreToCount(saveCount);
+}
+
+void Tree::drawCachedBitmapWithRootAlpha(Canvas* outCanvas, SkColorFilter* filter,
+        const SkRect& originalBounds) {
+    SkPaint* paint;
+    if (mRootAlpha == 1.0f && filter == 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);
+}
+
+void Tree::updateCachedBitmap(int width, int height) {
+    mCachedBitmap.eraseColor(SK_ColorTRANSPARENT);
+    Canvas* outCanvas = Canvas::create_canvas(mCachedBitmap);
+    float scaleX = width / mViewportWidth;
+    float scaleY = height / mViewportHeight;
+    mRootNode->draw(outCanvas, SkMatrix::I(), scaleX, scaleY);
+    mCacheDirty = false;
+}
+
+void Tree::createCachedBitmapIfNeeded(int width, int height) {
+    if (!canReuseBitmap(width, height)) {
+        SkImageInfo info = SkImageInfo::Make(width, height,
+                kN32_SkColorType, kPremul_SkAlphaType);
+        mCachedBitmap.setInfo(info);
+        // TODO: Count the bitmap cache against app's java heap
+        mCachedBitmap.allocPixels(info);
+        mCacheDirty = true;
+    }
+}
+
+bool Tree::canReuseBitmap(int width, int height) {
+    return width == mCachedBitmap.width() && height == mCachedBitmap.height();
+}
+
+}; // namespace VectorDrawable
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
new file mode 100644
index 0000000..6c84b05
--- /dev/null
+++ b/libs/hwui/VectorDrawable.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef ANDROID_HWUI_VPATH_H
+#define ANDROID_HWUI_VPATH_H
+
+#include "Canvas.h"
+#include <SkBitmap.h>
+#include <SkColor.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkPath.h>
+#include <SkPathMeasure.h>
+#include <SkRect.h>
+
+#include <cutils/compiler.h>
+#include <stddef.h>
+#include <vector>
+#include <string>
+
+namespace android {
+namespace uirenderer {
+
+namespace VectorDrawable {
+#define VD_SET_PROP_WITH_FLAG(field, value, flag) (VD_SET_PROP(field, value) ? (flag = true, true): false);
+#define VD_SET_PROP(field, value) (value != field ? (field = value, true) : false)
+
+/* A VectorDrawable is composed of a tree of nodes.
+ * Each node can be a group node, or a path.
+ * A group node can have groups or paths as children, but a path node has
+ * no children.
+ * One example can be:
+ *                 Root Group
+ *                /    |     \
+ *           Group    Path    Group
+ *          /     \             |
+ *         Path   Path         Path
+ *
+ */
+class ANDROID_API Node {
+public:
+    Node(const Node& node) {
+        mName = node.mName;
+    }
+    Node() {}
+    virtual void draw(Canvas* outCanvas, const SkMatrix& currentMatrix,
+            float scaleX, float scaleY) = 0;
+    virtual void dump() = 0;
+    void setName(const char* name) {
+        mName = name;
+    }
+    virtual ~Node(){}
+protected:
+    std::string mName;
+};
+
+class ANDROID_API Path : public Node {
+public:
+    struct ANDROID_API Data {
+        std::vector<char> verbs;
+        std::vector<size_t> verbSizes;
+        std::vector<float> points;
+        bool operator==(const Data& data) const {
+            return verbs == data.verbs && verbSizes == data.verbSizes
+                    && points == data.points;
+        }
+    };
+    Path(const Data& nodes);
+    Path(const Path& path);
+    Path(const char* path, size_t strLength);
+    Path() {}
+    void dump() override;
+    bool canMorph(const Data& path);
+    bool canMorph(const Path& path);
+    void draw(Canvas* outCanvas, const SkMatrix& groupStackedMatrix,
+            float scaleX, float scaleY) override;
+    void setPath(const char* path, size_t strLength);
+    void setPathData(const Data& data);
+    static float getMatrixScale(const SkMatrix& groupStackedMatrix);
+
+protected:
+    virtual const SkPath& getUpdatedPath();
+    virtual void drawPath(Canvas *outCanvas, const SkPath& renderPath,
+            float strokeScale) = 0;
+    Data mData;
+    SkPath mSkPath;
+    bool mSkPathDirty = true;
+};
+
+class ANDROID_API FullPath: public Path {
+public:
+    FullPath(const FullPath& path); // for cloning
+    FullPath(const char* path, size_t strLength) : Path(path, strLength) {}
+    FullPath() : Path() {}
+    FullPath(const Data& nodes) : Path(nodes) {}
+
+    void updateProperties(float strokeWidth, SkColor strokeColor,
+            float strokeAlpha, SkColor fillColor, float fillAlpha,
+            float trimPathStart, float trimPathEnd, float trimPathOffset,
+            float strokeMiterLimit, int strokeLineCap, int strokeLineJoin);
+    float getStrokeWidth() {
+        return mStrokeWidth;
+    }
+    void setStrokeWidth(float strokeWidth) {
+        mStrokeWidth = strokeWidth;
+    }
+    SkColor getStrokeColor() {
+        return mStrokeColor;
+    }
+    void setStrokeColor(SkColor strokeColor) {
+        mStrokeColor = strokeColor;
+    }
+    float getStrokeAlpha() {
+        return mStrokeAlpha;
+    }
+    void setStrokeAlpha(float strokeAlpha) {
+        mStrokeAlpha = strokeAlpha;
+    }
+    SkColor getFillColor() {
+        return mFillColor;
+    }
+    void setFillColor(SkColor fillColor) {
+        mFillColor = fillColor;
+    }
+    float getFillAlpha() {
+        return mFillAlpha;
+    }
+    void setFillAlpha(float fillAlpha) {
+        mFillAlpha = fillAlpha;
+    }
+    float getTrimPathStart() {
+        return mTrimPathStart;
+    }
+    void setTrimPathStart(float trimPathStart) {
+        VD_SET_PROP_WITH_FLAG(mTrimPathStart, trimPathStart, mTrimDirty);
+    }
+    float getTrimPathEnd() {
+        return mTrimPathEnd;
+    }
+    void setTrimPathEnd(float trimPathEnd) {
+        VD_SET_PROP_WITH_FLAG(mTrimPathEnd, trimPathEnd, mTrimDirty);
+    }
+    float getTrimPathOffset() {
+        return mTrimPathOffset;
+    }
+    void setTrimPathOffset(float trimPathOffset) {
+        VD_SET_PROP_WITH_FLAG(mTrimPathOffset, trimPathOffset, mTrimDirty);
+    }
+    bool getProperties(int8_t* outProperties, int length);
+
+protected:
+    const SkPath& getUpdatedPath() override;
+    void drawPath(Canvas* outCanvas, const SkPath& renderPath,
+            float strokeScale) override;
+
+private:
+    // Applies trimming to the specified path.
+    void applyTrim();
+    float mStrokeWidth = 0;
+    SkColor mStrokeColor = SK_ColorTRANSPARENT;
+    float mStrokeAlpha = 1;
+    SkColor mFillColor = SK_ColorTRANSPARENT;
+    float mFillAlpha = 1;
+    float mTrimPathStart = 0;
+    float mTrimPathEnd = 1;
+    float mTrimPathOffset = 0;
+    bool mTrimDirty = true;
+    SkPaint::Cap mStrokeLineCap = SkPaint::Cap::kButt_Cap;
+    SkPaint::Join mStrokeLineJoin = SkPaint::Join::kMiter_Join;
+    float mStrokeMiterLimit = 4;
+    SkPath mTrimmedSkPath;
+    SkPaint mPaint;
+};
+
+class ANDROID_API ClipPath: public Path {
+public:
+    ClipPath(const ClipPath& path) : Path(path) {}
+    ClipPath(const char* path, size_t strLength) : Path(path, strLength) {}
+    ClipPath() : Path() {}
+    ClipPath(const Data& nodes) : Path(nodes) {}
+
+protected:
+    void drawPath(Canvas* outCanvas, const SkPath& renderPath,
+            float strokeScale) override;
+};
+
+class ANDROID_API Group: public Node {
+public:
+    Group(const Group& group);
+    Group() {}
+    float getRotation() {
+        return mRotate;
+    }
+    void setRotation(float rotation) {
+        mRotate = rotation;
+    }
+    float getPivotX() {
+        return mPivotX;
+    }
+    void setPivotX(float pivotX) {
+        mPivotX = pivotX;
+    }
+    float getPivotY() {
+        return mPivotY;
+    }
+    void setPivotY(float pivotY) {
+        mPivotY = pivotY;
+    }
+    float getScaleX() {
+        return mScaleX;
+    }
+    void setScaleX(float scaleX) {
+        mScaleX = scaleX;
+    }
+    float getScaleY() {
+        return mScaleY;
+    }
+    void setScaleY(float scaleY) {
+        mScaleY = scaleY;
+    }
+    float getTranslateX() {
+        return mTranslateX;
+    }
+    void setTranslateX(float translateX) {
+        mTranslateX = translateX;
+    }
+    float getTranslateY() {
+        return mTranslateY;
+    }
+    void setTranslateY(float translateY) {
+        mTranslateY = translateY;
+    }
+    virtual void draw(Canvas* outCanvas, const SkMatrix& currentMatrix,
+            float scaleX, float scaleY) override;
+    void updateLocalMatrix(float rotate, float pivotX, float pivotY,
+            float scaleX, float scaleY, float translateX, float translateY);
+    void getLocalMatrix(SkMatrix* outMatrix);
+    void addChild(Node* child);
+    void dump() override;
+    bool getProperties(float* outProperties, int length);
+
+private:
+    enum class Property {
+        Rotate_Property = 0,
+        PivotX_Property,
+        PivotY_Property,
+        ScaleX_Property,
+        ScaleY_Property,
+        TranslateX_Property,
+        TranslateY_Property,
+        // 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;
+};
+
+class ANDROID_API Tree {
+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);
+    void createCachedBitmapIfNeeded(int width, int height);
+    bool canReuseBitmap(int width, int height);
+    void setAllowCaching(bool allowCaching) {
+        mAllowCaching = allowCaching;
+    }
+    bool setRootAlpha(float rootAlpha) {
+        return VD_SET_PROP(mRootAlpha, rootAlpha);
+    }
+
+    float getRootAlpha() {
+        return mRootAlpha;
+    }
+    void setViewportSize(float viewportWidth, float viewportHeight) {
+        mViewportWidth = viewportWidth;
+        mViewportHeight = viewportHeight;
+    }
+
+private:
+    // Cap the bitmap size, such that it won't hurt the performance too much
+    // and it won't crash due to a very large scale.
+    // The drawable will look blurry above this size.
+    const static int MAX_CACHED_BITMAP_SIZE;
+
+    bool mCacheDirty = true;
+    bool mAllowCaching = true;
+    float mViewportWidth = 0;
+    float mViewportHeight = 0;
+    float mRootAlpha = 1.0f;
+
+    Group* mRootNode;
+    SkRect mBounds;
+    SkMatrix mCanvasMatrix;
+    SkPaint mPaint;
+    SkPathMeasure mPathMeasure;
+    SkBitmap mCachedBitmap;
+
+};
+
+} // namespace VectorDrawable
+
+typedef VectorDrawable::Path::Data PathData;
+} // namespace uirenderer
+} // namespace android
+
+#endif // ANDROID_HWUI_VPATH_H
diff --git a/libs/hwui/VectorDrawablePath.cpp b/libs/hwui/VectorDrawablePath.cpp
deleted file mode 100644
index c9a54ca..0000000
--- a/libs/hwui/VectorDrawablePath.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2015 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 "VectorDrawablePath.h"
-
-#include "PathParser.h"
-#include "utils/VectorDrawableUtils.h"
-
-#include <math.h>
-#include <utils/Log.h>
-
-namespace android {
-namespace uirenderer {
-
-
-VectorDrawablePath::VectorDrawablePath(const char* pathStr, size_t strLength) {
-    PathParser::ParseResult result;
-    PathParser::getPathDataFromString(&mData, &result, pathStr, strLength);
-    if (!result.failureOccurred) {
-        VectorDrawableUtils::verbsToPath(&mSkPath, mData);
-    }
-}
-
-VectorDrawablePath::VectorDrawablePath(const PathData& data) {
-    mData = data;
-    // Now we need to construct a path
-    VectorDrawableUtils::verbsToPath(&mSkPath, data);
-}
-
-VectorDrawablePath::VectorDrawablePath(const VectorDrawablePath& path) {
-    mData = path.mData;
-    VectorDrawableUtils::verbsToPath(&mSkPath, mData);
-}
-
-
-bool VectorDrawablePath::canMorph(const PathData& morphTo) {
-    return VectorDrawableUtils::canMorph(mData, morphTo);
-}
-
-bool VectorDrawablePath::canMorph(const VectorDrawablePath& path) {
-    return canMorph(path.mData);
-}
-
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/VectorDrawablePath.h b/libs/hwui/VectorDrawablePath.h
deleted file mode 100644
index 2e56349..0000000
--- a/libs/hwui/VectorDrawablePath.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef ANDROID_HWUI_VPATH_H
-#define ANDROID_HWUI_VPATH_H
-
-#include <cutils/compiler.h>
-#include "SkPath.h"
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-struct ANDROID_API PathData {
-    // TODO: Try using FatVector instead of std::vector and do a micro benchmark on the performance
-    // difference.
-    std::vector<char> verbs;
-    std::vector<size_t> verbSizes;
-    std::vector<float> points;
-    bool operator== (const PathData& data) const {
-        return verbs == data.verbs && verbSizes == data.verbSizes && points == data.points;
-    }
-
-};
-
-class VectorDrawablePath {
-public:
-    VectorDrawablePath(const PathData& nodes);
-    VectorDrawablePath(const VectorDrawablePath& path);
-    VectorDrawablePath(const char* path, size_t strLength);
-    bool canMorph(const PathData& path);
-    bool canMorph(const VectorDrawablePath& path);
-
-private:
-    PathData mData;
-    SkPath mSkPath;
-};
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // ANDROID_HWUI_VPATH_H
diff --git a/libs/hwui/tests/microbench/PathParserBench.cpp b/libs/hwui/tests/microbench/PathParserBench.cpp
index 3d9fafa..bd742c6 100644
--- a/libs/hwui/tests/microbench/PathParserBench.cpp
+++ b/libs/hwui/tests/microbench/PathParserBench.cpp
@@ -17,7 +17,7 @@
 #include <benchmark/Benchmark.h>
 
 #include "PathParser.h"
-#include "VectorDrawablePath.h"
+#include "VectorDrawable.h"
 
 #include <SkPath.h>
 
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 77dd73a..7208547 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -17,6 +17,7 @@
 #include <gtest/gtest.h>
 
 #include "PathParser.h"
+#include "VectorDrawable.h"
 #include "utils/MathUtils.h"
 #include "utils/VectorDrawableUtils.h"
 
@@ -98,6 +99,65 @@
         }
     },
 
+    // Check box VectorDrawable path data
+    {
+        // Path
+        "M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z",
+        {
+            {'M', 'l', 'c', 'l', 'c', 'l', 'c', 'l', 'c', 'Z', 'M', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'c', 'Z'},
+            {2, 2, 6, 2, 6, 2, 6, 2, 6, 0, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0},
+            {0.0, -1.0, 0.0, 0.0, 0.5522848, 0.0, 1.0, 0.44771525, 1.0, 1.0, 0.0, 0.0, 0.0, 0.5522848, -0.44771525, 1.0, -1.0, 1.0, 0.0, 0.0, -0.5522848, 0.0, -1.0, -0.44771525, -1.0, -1.0, 0.0, 0.0, 0.0, -0.5522848, 0.44771525, -1.0, 1.0, -1.0, 7.0, -9.0, 0.0, 0.0, -14.0, 0.0, -14.0, 0.0, -1.1044922, 0.0, -2.0, 0.8955078, -2.0, 2.0, 0.0, 0.0, 0.0, 14.0, 0.0, 14.0, 0.0, 1.1044922, 0.8955078, 2.0, 2.0, 2.0, 0.0, 0.0, 14.0, 0.0, 14.0, 0.0, 1.1044922, 0.0, 2.0, -0.8955078, 2.0, -2.0, 0.0, 0.0, 0.0, -14.0, 0.0, -14.0, 0.0, -1.1044922, -0.8955078, -2.0, -2.0, -2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+        },
+        [](SkPath* outPath) {
+            outPath->moveTo(0.0, -1.0);
+            outPath->rLineTo(0.0, 0.0);
+            outPath->rCubicTo(0.5522848, 0.0, 1.0, 0.44771525, 1.0, 1.0);
+            outPath->rLineTo(0.0, 0.0);
+            outPath->rCubicTo(0.0, 0.5522848, -0.44771525, 1.0, -1.0, 1.0);
+            outPath->rLineTo(0.0, 0.0);
+            outPath->rCubicTo(-0.5522848, 0.0, -1.0, -0.44771525, -1.0, -1.0);
+            outPath->rLineTo(0.0, 0.0);
+            outPath->rCubicTo(0.0, -0.5522848, 0.44771525, -1.0, 1.0, -1.0);
+            outPath->close();
+            outPath->moveTo(0.0, -1.0);
+            outPath->moveTo(7.0, -9.0);
+            outPath->rCubicTo(0.0, 0.0, -14.0, 0.0, -14.0, 0.0);
+            outPath->rCubicTo(-1.1044922, 0.0, -2.0, 0.8955078, -2.0, 2.0);
+            outPath->rCubicTo(0.0, 0.0, 0.0, 14.0, 0.0, 14.0);
+            outPath->rCubicTo(0.0, 1.1044922, 0.8955078, 2.0, 2.0, 2.0);
+            outPath->rCubicTo(0.0, 0.0, 14.0, 0.0, 14.0, 0.0);
+            outPath->rCubicTo(1.1044922, 0.0, 2.0, -0.8955078, 2.0, -2.0);
+            outPath->rCubicTo(0.0, 0.0, 0.0, -14.0, 0.0, -14.0);
+            outPath->rCubicTo(0.0, -1.1044922, -0.8955078, -2.0, -2.0, -2.0);
+            outPath->rCubicTo(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+            outPath->close();
+            outPath->moveTo(7.0, -9.0);
+        }
+    },
+
+    // pie1 in progress bar
+    {
+        "M300,70 a230,230 0 1,0 1,0 z",
+        {
+             {'M', 'a', 'z', },
+             {2, 7, 0, },
+             {300.0, 70.0, 230.0, 230.0, 0.0, 1.0, 0.0, 1.0, 0.0, },
+        },
+        [](SkPath* outPath) {
+            outPath->moveTo(300.0, 70.0);
+            outPath->cubicTo(239.06697794203706, 70.13246340443499, 180.6164396449267, 94.47383115953485, 137.6004913602211, 137.6302781499585);
+            outPath->cubicTo(94.58454307551551, 180.78672514038215, 70.43390412842275, 239.3163266242308, 70.50013586976587, 300.2494566687817);
+            outPath->cubicTo(70.56636761110899, 361.1825867133326, 94.84418775550249, 419.65954850554147, 137.9538527586204, 462.72238058830936);
+            outPath->cubicTo(181.06351776173827, 505.7852126710772, 239.5668339599056, 529.999456521097, 300.49999999999994, 529.999456521097);
+            outPath->cubicTo(361.43316604009436, 529.999456521097, 419.93648223826176, 505.78521267107726, 463.0461472413797, 462.7223805883093);
+            outPath->cubicTo(506.1558122444976, 419.65954850554135, 530.433632388891, 361.1825867133324, 530.4998641302341, 300.2494566687815);
+            outPath->cubicTo(530.5660958715771, 239.31632662423056, 506.4154569244844, 180.7867251403819, 463.3995086397787, 137.6302781499583);
+            outPath->cubicTo(420.383560355073, 94.47383115953468, 361.93302205796255, 70.13246340443492, 300.9999999999996, 70.00000000000003);
+            outPath->close();
+            outPath->moveTo(300.0, 70.0);
+        }
+    },
+
     // Random long data
     {
         // Path
@@ -178,6 +238,7 @@
     {"1-2e34567", false}
 };
 
+
 static bool hasSameVerbs(const PathData& from, const PathData& to) {
     return from.verbs == to.verbs && from.verbSizes == to.verbSizes;
 }
@@ -284,6 +345,49 @@
     }
 }
 
+TEST(VectorDrawable, matrixScale) {
+    struct MatrixAndScale {
+        float buffer[9];
+        float matrixScale;
+    };
 
+    const MatrixAndScale sMatrixAndScales[] {
+        {
+            {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f},
+            1.0
+        },
+        {
+            {1.0f, 0.0f, 240.0f, 0.0f, 1.0f, 240.0f, 0.0f, 0.0f, 1.0f},
+            1.0f,
+        },
+        {
+            {1.5f, 0.0f, 24.0f, 0.0f, 1.5f, 24.0f, 0.0f, 0.0f, 1.0f},
+            1.5f,
+        },
+        {
+            {0.99999994f, 0.0f, 300.0f, 0.0f, 0.99999994f, 158.57864f, 0.0f, 0.0f, 1.0f},
+            0.99999994f,
+        },
+        {
+            {0.7071067f, 0.7071067f, 402.5305f, -0.7071067f, 0.7071067f, 169.18524f, 0.0f, 0.0f, 1.0f},
+            0.99999994f,
+        },
+        {
+            {0.0f, 0.9999999f, 482.5305f, -0.9999999f, 0.0f, 104.18525f, 0.0f, 0.0f, 1.0f},
+            0.9999999f,
+        },
+        {
+            {-0.35810637f, -0.93368083f, 76.55821f, 0.93368083f, -0.35810637f, 89.538506f, 0.0f, 0.0f, 1.0f},
+            1.0000001f,
+        },
+    };
+
+    for (MatrixAndScale matrixAndScale : sMatrixAndScales) {
+        SkMatrix matrix;
+        matrix.set9(matrixAndScale.buffer);
+        float actualMatrixScale = VectorDrawable::Path::getMatrixScale(matrix);
+        EXPECT_EQ(matrixAndScale.matrixScale, actualMatrixScale);
+    }
+}
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/utils/VectorDrawableUtils.h b/libs/hwui/utils/VectorDrawableUtils.h
index 21c1cdc..b5ef510 100644
--- a/libs/hwui/utils/VectorDrawableUtils.h
+++ b/libs/hwui/utils/VectorDrawableUtils.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_HWUI_VECTORDRAWABLE_UTILS_H
 #define ANDROID_HWUI_VECTORDRAWABLE_UTILS_H
 
-#include "VectorDrawablePath.h"
+#include "VectorDrawable.h"
 
 #include <cutils/compiler.h>
 #include "SkPath.h"