summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Doris Liu <tianliu@google.com> 2015-12-01 17:59:40 -0800
committer Doris Liu <tianliu@google.com> 2015-12-28 16:08:38 -0800
commit4bbc2931263b232fba61807fca00e127573eff42 (patch)
tree04ea8fe74ecd7c92c28d797f73c87a9cdde0bbf1
parent701662547341269d610ac5f093e449a4c7078e43 (diff)
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
-rw-r--r--core/java/android/util/PathParser.java4
-rw-r--r--core/jni/Android.mk1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_graphics_drawable_VectorDrawable.cpp386
-rw-r--r--graphics/java/android/graphics/drawable/VectorDrawable.java1101
-rw-r--r--libs/hwui/Android.mk2
-rw-r--r--libs/hwui/PathParser.h2
-rw-r--r--libs/hwui/VectorDrawable.cpp485
-rw-r--r--libs/hwui/VectorDrawable.h330
-rw-r--r--libs/hwui/VectorDrawablePath.cpp59
-rw-r--r--libs/hwui/VectorDrawablePath.h55
-rw-r--r--libs/hwui/tests/microbench/PathParserBench.cpp2
-rw-r--r--libs/hwui/tests/unit/VectorDrawableTests.cpp104
-rw-r--r--libs/hwui/utils/VectorDrawableUtils.h2
14 files changed, 1718 insertions, 817 deletions
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index f17a16c066f0..78d5bcd90624 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -82,6 +82,10 @@ public class PathParser {
}
}
+ public long getNativePtr() {
+ return mNativePathData;
+ }
+
/**
* Update the path data to match the source.
* Before calling this, make sure canMorph(target, source) is true.
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index bc3ac5ff426b..30593f2eb67e 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -49,6 +49,7 @@ LOCAL_SRC_FILES:= \
android_database_SQLiteConnection.cpp \
android_database_SQLiteGlobal.cpp \
android_database_SQLiteDebug.cpp \
+ android_graphics_drawable_VectorDrawable.cpp \
android_view_DisplayEventReceiver.cpp \
android_view_DisplayListCanvas.cpp \
android_view_GraphicBuffer.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c44a62c08020..63f193d57419 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -130,6 +130,7 @@ extern int register_android_graphics_Rasterizer(JNIEnv* env);
extern int register_android_graphics_Region(JNIEnv* env);
extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
extern int register_android_graphics_Xfermode(JNIEnv* env);
+extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
@@ -1312,6 +1313,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_graphics_Typeface),
REG_JNI(register_android_graphics_Xfermode),
REG_JNI(register_android_graphics_YuvImage),
+ REG_JNI(register_android_graphics_drawable_VectorDrawable),
REG_JNI(register_android_graphics_pdf_PdfDocument),
REG_JNI(register_android_graphics_pdf_PdfEditor),
REG_JNI(register_android_graphics_pdf_PdfRenderer),
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
new file mode 100644
index 000000000000..53d4c6a1cb78
--- /dev/null
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -0,0 +1,386 @@
+/*
+ * 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 "jni.h"
+#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
+#include "log/log.h"
+
+#include "Paint.h"
+#include "VectorDrawable.h"
+
+namespace android {
+using namespace uirenderer;
+using namespace uirenderer::VectorDrawable;
+
+static jlong createTree(JNIEnv*, jobject, jlong groupPtr) {
+ VectorDrawable::Group* rootGroup = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ VectorDrawable::Tree* tree = new VectorDrawable::Tree(rootGroup);
+ return reinterpret_cast<jlong>(tree);
+}
+
+static void deleteTree(JNIEnv*, jobject, jlong treePtr) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ delete tree;
+}
+
+static void setTreeViewportSize(JNIEnv*, jobject, jlong treePtr,
+ jfloat viewportWidth, jfloat viewportHeight) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ tree->setViewportSize(viewportWidth, viewportHeight);
+}
+
+static jboolean setRootAlpha(JNIEnv*, jobject, jlong treePtr, jfloat alpha) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ return tree->setRootAlpha(alpha);
+}
+
+static jfloat getRootAlpha(JNIEnv*, jobject, jlong treePtr) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ return tree->getRootAlpha();
+}
+
+static void setAllowCaching(JNIEnv*, jobject, jlong treePtr, jboolean allowCaching) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ tree->setAllowCaching(allowCaching);
+}
+
+static void draw(JNIEnv* env, jobject, jlong treePtr, jlong canvasPtr,
+ jlong colorFilterPtr, jobject jrect, jboolean needsMirroring, jboolean canReuseCache) {
+ VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+ Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+ SkRect rect;
+ GraphicsJNI::jrect_to_rect(env, jrect, &rect);
+ SkColorFilter* colorFilter = reinterpret_cast<SkColorFilter*>(colorFilterPtr);
+ tree->draw(canvas, colorFilter, rect, needsMirroring, canReuseCache);
+}
+
+static jlong createEmptyFullPath(JNIEnv*, jobject) {
+ VectorDrawable::FullPath* newPath = new VectorDrawable::FullPath();
+ return reinterpret_cast<jlong>(newPath);
+}
+
+static jlong createFullPath(JNIEnv*, jobject, jlong srcFullPathPtr) {
+ VectorDrawable::FullPath* srcFullPath =
+ reinterpret_cast<VectorDrawable::FullPath*>(srcFullPathPtr);
+ VectorDrawable::FullPath* newPath = new VectorDrawable::FullPath(*srcFullPath);
+ return reinterpret_cast<jlong>(newPath);
+}
+
+static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong fullPathPtr,
+ jfloat strokeWidth, jint strokeColor, jfloat strokeAlpha, jint fillColor, jfloat fillAlpha,
+ jfloat trimPathStart, jfloat trimPathEnd, jfloat trimPathOffset, jfloat strokeMiterLimit,
+ jint strokeLineCap, jint strokeLineJoin) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ fullPath->updateProperties(strokeWidth, strokeColor, strokeAlpha, fillColor, fillAlpha,
+ trimPathStart, trimPathEnd, trimPathOffset, strokeMiterLimit, strokeLineCap,
+ strokeLineJoin);
+}
+
+static jboolean getFullPathProperties(JNIEnv* env, jobject, jlong fullPathPtr,
+ jbyteArray outProperties, jint length) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ int8_t pathProperties[length];
+ bool success = fullPath->getProperties(pathProperties, length);
+ env->SetByteArrayRegion(outProperties, 0, length, reinterpret_cast<int8_t*>(&pathProperties));
+ return success;
+}
+
+static jboolean getGroupProperties(JNIEnv* env, jobject, jlong groupPtr,
+ jfloatArray outProperties, jint length) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ float groupProperties[length];
+ bool success = group->getProperties(groupProperties, length);
+ env->SetFloatArrayRegion(outProperties, 0, length, reinterpret_cast<float*>(&groupProperties));
+ return success;
+}
+
+static jlong createEmptyClipPath(JNIEnv*, jobject) {
+ VectorDrawable::ClipPath* newPath = new VectorDrawable::ClipPath();
+ return reinterpret_cast<jlong>(newPath);
+}
+
+static jlong createClipPath(JNIEnv*, jobject, jlong srcClipPathPtr) {
+ VectorDrawable::ClipPath* srcClipPath =
+ reinterpret_cast<VectorDrawable::ClipPath*>(srcClipPathPtr);
+ VectorDrawable::ClipPath* newPath = new VectorDrawable::ClipPath(*srcClipPath);
+ return reinterpret_cast<jlong>(newPath);
+}
+
+static jlong createEmptyGroup(JNIEnv*, jobject) {
+ VectorDrawable::Group* newGroup = new VectorDrawable::Group();
+ return reinterpret_cast<jlong>(newGroup);
+}
+
+static jlong createGroup(JNIEnv*, jobject, jlong srcGroupPtr) {
+ VectorDrawable::Group* srcGroup = reinterpret_cast<VectorDrawable::Group*>(srcGroupPtr);
+ VectorDrawable::Group* newGroup = new VectorDrawable::Group(*srcGroup);
+ return reinterpret_cast<jlong>(newGroup);
+}
+
+static void deleteNode(JNIEnv*, jobject, jlong nodePtr) {
+ VectorDrawable::Node* node = reinterpret_cast<VectorDrawable::Node*>(nodePtr);
+ delete node;
+}
+
+static void setNodeName(JNIEnv* env, jobject, jlong nodePtr, jstring nameStr) {
+ VectorDrawable::Node* node = reinterpret_cast<VectorDrawable::Node*>(nodePtr);
+ const char* nodeName = env->GetStringUTFChars(nameStr, NULL);
+ node->setName(nodeName);
+ env->ReleaseStringUTFChars(nameStr, nodeName);
+}
+
+static void updateGroupProperties(JNIEnv*, jobject, jlong groupPtr, jfloat rotate, jfloat pivotX,
+ jfloat pivotY, jfloat scaleX, jfloat scaleY, jfloat translateX, jfloat translateY) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ group->updateLocalMatrix(rotate, pivotX, pivotY, scaleX, scaleY, translateX, translateY);
+}
+
+static void addChild(JNIEnv*, jobject, jlong groupPtr, jlong childPtr) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ VectorDrawable::Node* child = reinterpret_cast<VectorDrawable::Node*>(childPtr);
+ group->addChild(child);
+}
+
+static void setPathString(JNIEnv* env, jobject, jlong pathPtr, jstring inputStr,
+ jint stringLength) {
+ VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(pathPtr);
+ const char* pathString = env->GetStringUTFChars(inputStr, NULL);
+ path->setPath(pathString, stringLength);
+ env->ReleaseStringUTFChars(inputStr, pathString);
+}
+
+static jfloat getRotation(JNIEnv*, jobject, jlong groupPtr) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ return group->getRotation();
+}
+
+static void setRotation(JNIEnv*, jobject, jlong groupPtr, jfloat rotation) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ group->setRotation(rotation);
+}
+
+static jfloat getPivotX(JNIEnv*, jobject, jlong groupPtr) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ return group->getPivotX();
+}
+
+static void setPivotX(JNIEnv*, jobject, jlong groupPtr, jfloat pivotX) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ group->setPivotX(pivotX);
+}
+
+static jfloat getPivotY(JNIEnv*, jobject, jlong groupPtr) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ return group->getPivotY();
+}
+
+static void setPivotY(JNIEnv*, jobject, jlong groupPtr, jfloat pivotY) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ group->setPivotY(pivotY);
+}
+
+static jfloat getScaleX(JNIEnv*, jobject, jlong groupPtr) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ return group->getScaleX();
+}
+
+static void setScaleX(JNIEnv*, jobject, jlong groupPtr, jfloat scaleX) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ group->setScaleX(scaleX);
+}
+
+static jfloat getScaleY(JNIEnv*, jobject, jlong groupPtr) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ return group->getScaleY();
+}
+
+static void setScaleY(JNIEnv*, jobject, jlong groupPtr, jfloat scaleY) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ group->setScaleY(scaleY);
+}
+
+static jfloat getTranslateX(JNIEnv*, jobject, jlong groupPtr) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ return group->getTranslateX();
+}
+
+static void setTranslateX(JNIEnv*, jobject, jlong groupPtr, jfloat translateX) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ group->setTranslateX(translateX);
+}
+
+static jfloat getTranslateY(JNIEnv*, jobject, jlong groupPtr) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ return group->getTranslateY();
+}
+
+static void setTranslateY(JNIEnv*, jobject, jlong groupPtr, jfloat translateY) {
+ VectorDrawable::Group* group = reinterpret_cast<VectorDrawable::Group*>(groupPtr);
+ group->setTranslateY(translateY);
+}
+
+static void setPathData(JNIEnv*, jobject, jlong pathPtr, jlong pathDataPtr) {
+ VectorDrawable::Path* path = reinterpret_cast<VectorDrawable::Path*>(pathPtr);
+ PathData* pathData = reinterpret_cast<PathData*>(pathDataPtr);
+ path->setPathData(*pathData);
+}
+
+static jfloat getStrokeWidth(JNIEnv*, jobject, jlong fullPathPtr) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ return fullPath->getStrokeWidth();
+}
+
+static void setStrokeWidth(JNIEnv*, jobject, jlong fullPathPtr, jfloat strokeWidth) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ fullPath->setStrokeWidth(strokeWidth);
+}
+
+static jint getStrokeColor(JNIEnv*, jobject, jlong fullPathPtr) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ return fullPath->getStrokeColor();
+}
+
+static void setStrokeColor(JNIEnv*, jobject, jlong fullPathPtr, jint strokeColor) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ fullPath->setStrokeColor(strokeColor);
+}
+
+static jfloat getStrokeAlpha(JNIEnv*, jobject, jlong fullPathPtr) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ return fullPath->getStrokeAlpha();
+}
+
+static void setStrokeAlpha(JNIEnv*, jobject, jlong fullPathPtr, jfloat strokeAlpha) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ fullPath->setStrokeAlpha(strokeAlpha);
+}
+
+static jint getFillColor(JNIEnv*, jobject, jlong fullPathPtr) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ return fullPath->getFillColor();
+}
+
+static void setFillColor(JNIEnv*, jobject, jlong fullPathPtr, jint fillColor) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ fullPath->setFillColor(fillColor);
+}
+
+static jfloat getFillAlpha(JNIEnv*, jobject, jlong fullPathPtr) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ return fullPath->getFillAlpha();
+}
+
+static void setFillAlpha(JNIEnv*, jobject, jlong fullPathPtr, jfloat fillAlpha) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ fullPath->setFillAlpha(fillAlpha);
+}
+
+static jfloat getTrimPathStart(JNIEnv*, jobject, jlong fullPathPtr) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ return fullPath->getTrimPathStart();
+}
+
+static void setTrimPathStart(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathStart) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ fullPath->setTrimPathStart(trimPathStart);
+}
+
+static jfloat getTrimPathEnd(JNIEnv*, jobject, jlong fullPathPtr) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ return fullPath->getTrimPathEnd();
+}
+
+static void setTrimPathEnd(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathEnd) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ fullPath->setTrimPathEnd(trimPathEnd);
+}
+
+static jfloat getTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ return fullPath->getTrimPathOffset();
+}
+
+static void setTrimPathOffset(JNIEnv*, jobject, jlong fullPathPtr, jfloat trimPathOffset) {
+ VectorDrawable::FullPath* fullPath = reinterpret_cast<VectorDrawable::FullPath*>(fullPathPtr);
+ fullPath->setTrimPathOffset(trimPathOffset);
+}
+
+static const JNINativeMethod gMethods[] = {
+ {"nCreateRenderer", "!(J)J", (void*)createTree},
+ {"nDestroyRenderer", "!(J)V", (void*)deleteTree},
+ {"nSetRendererViewportSize", "!(JFF)V", (void*)setTreeViewportSize},
+ {"nSetRootAlpha", "!(JF)Z", (void*)setRootAlpha},
+ {"nGetRootAlpha", "!(J)F", (void*)getRootAlpha},
+ {"nSetAllowCaching", "!(JZ)V", (void*)setAllowCaching},
+
+ {"nDraw", "(JJJLandroid/graphics/Rect;ZZ)V", (void*)draw},
+ {"nCreateFullPath", "!()J", (void*)createEmptyFullPath},
+ {"nCreateFullPath", "!(J)J", (void*)createFullPath},
+ {"nUpdateFullPathProperties", "!(JFIFIFFFFFII)V", (void*)updateFullPathPropertiesAndStrokeStyles},
+ {"nGetFullPathProperties", "(J[BI)Z", (void*)getFullPathProperties},
+ {"nGetGroupProperties", "(J[FI)Z", (void*)getGroupProperties},
+
+ {"nCreateClipPath", "!()J", (void*)createEmptyClipPath},
+ {"nCreateClipPath", "!(J)J", (void*)createClipPath},
+ {"nCreateGroup", "!()J", (void*)createEmptyGroup},
+ {"nCreateGroup", "!(J)J", (void*)createGroup},
+ {"nDestroy", "!(J)V", (void*)deleteNode},
+ {"nSetName", "(JLjava/lang/String;)V", (void*)setNodeName},
+ {"nUpdateGroupProperties", "!(JFFFFFFF)V", (void*)updateGroupProperties},
+
+ {"nAddChild", "!(JJ)V", (void*)addChild},
+ {"nSetPathString", "(JLjava/lang/String;I)V", (void*)setPathString},
+
+ {"nGetRotation", "!(J)F", (void*)getRotation},
+ {"nSetRotation", "!(JF)V", (void*)setRotation},
+ {"nGetPivotX", "!(J)F", (void*)getPivotX},
+ {"nSetPivotX", "!(JF)V", (void*)setPivotX},
+ {"nGetPivotY", "!(J)F", (void*)getPivotY},
+ {"nSetPivotY", "!(JF)V", (void*)setPivotY},
+ {"nGetScaleX", "!(J)F", (void*)getScaleX},
+ {"nSetScaleX", "!(JF)V", (void*)setScaleX},
+ {"nGetScaleY", "!(J)F", (void*)getScaleY},
+ {"nSetScaleY", "!(JF)V", (void*)setScaleY},
+ {"nGetTranslateX", "!(J)F", (void*)getTranslateX},
+ {"nSetTranslateX", "!(JF)V", (void*)setTranslateX},
+ {"nGetTranslateY", "!(J)F", (void*)getTranslateY},
+ {"nSetTranslateY", "!(JF)V", (void*)setTranslateY},
+
+ {"nSetPathData", "!(JJ)V", (void*)setPathData},
+ {"nGetStrokeWidth", "!(J)F", (void*)getStrokeWidth},
+ {"nSetStrokeWidth", "!(JF)V", (void*)setStrokeWidth},
+ {"nGetStrokeColor", "!(J)I", (void*)getStrokeColor},
+ {"nSetStrokeColor", "!(JI)V", (void*)setStrokeColor},
+ {"nGetStrokeAlpha", "!(J)F", (void*)getStrokeAlpha},
+ {"nSetStrokeAlpha", "!(JF)V", (void*)setStrokeAlpha},
+ {"nGetFillColor", "!(J)I", (void*)getFillColor},
+ {"nSetFillColor", "!(JI)V", (void*)setFillColor},
+ {"nGetFillAlpha", "!(J)F", (void*)getFillAlpha},
+ {"nSetFillAlpha", "!(JF)V", (void*)setFillAlpha},
+ {"nGetTrimPathStart", "!(J)F", (void*)getTrimPathStart},
+ {"nSetTrimPathStart", "!(JF)V", (void*)setTrimPathStart},
+ {"nGetTrimPathEnd", "!(J)F", (void*)getTrimPathEnd},
+ {"nSetTrimPathEnd", "!(JF)V", (void*)setTrimPathEnd},
+ {"nGetTrimPathOffset", "!(J)F", (void*)getTrimPathOffset},
+ {"nSetTrimPathOffset", "!(JF)V", (void*)setTrimPathOffset},
+};
+
+int register_android_graphics_drawable_VectorDrawable(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/drawable/VectorDrawable", gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 3761a99759c1..65260210042c 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -20,15 +20,9 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Insets;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PathMeasure;
import android.graphics.PixelFormat;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
@@ -38,7 +32,6 @@ import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.LayoutDirection;
import android.util.Log;
-import android.util.MathUtils;
import android.util.PathParser;
import android.util.Xml;
@@ -48,6 +41,8 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Stack;
@@ -196,21 +191,6 @@ public class VectorDrawable extends Drawable {
private static final String SHAPE_PATH = "path";
private static final String SHAPE_VECTOR = "vector";
- private static final int LINECAP_BUTT = 0;
- private static final int LINECAP_ROUND = 1;
- private static final int LINECAP_SQUARE = 2;
-
- private static final int LINEJOIN_MITER = 0;
- private static final int LINEJOIN_ROUND = 1;
- private static final int LINEJOIN_BEVEL = 2;
-
- // 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.
- private static final int MAX_CACHED_BITMAP_SIZE = 2048;
-
- private static final boolean DBG_VECTOR_DRAWABLE = false;
-
private VectorDrawableState mVectorState;
private PorterDuffColorFilter mTintFilter;
@@ -218,10 +198,6 @@ public class VectorDrawable extends Drawable {
private boolean mMutated;
- // AnimatedVectorDrawable needs to turn off the cache all the time, otherwise,
- // caching the bitmap by default is allowed.
- private boolean mAllowCaching = true;
-
/** The density of the display on which this drawable will be rendered. */
private int mTargetDensity;
@@ -235,8 +211,6 @@ public class VectorDrawable extends Drawable {
private boolean mDpiScaledDirty = true;
// Temp variable, only for saving "new" operation at the draw() time.
- private final float[] mTmpFloats = new float[9];
- private final Matrix mTmpMatrix = new Matrix();
private final Rect mTmpBounds = new Rect();
public VectorDrawable() {
@@ -249,7 +223,6 @@ public class VectorDrawable extends Drawable {
*/
private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) {
mVectorState = state;
-
updateLocalState(res);
}
@@ -262,7 +235,7 @@ public class VectorDrawable extends Drawable {
* displayed, or {@code null} to use the constant state defaults
*/
private void updateLocalState(Resources res) {
- final int density = Drawable.resolveDensity(res, mVectorState.mVPathRenderer.mDensity);
+ final int density = Drawable.resolveDensity(res, mVectorState.mDensity);
if (mTargetDensity != density) {
mTargetDensity = density;
mDpiScaledDirty = true;
@@ -289,7 +262,7 @@ public class VectorDrawable extends Drawable {
}
Object getTargetByName(String name) {
- return mVectorState.mVPathRenderer.mVGTargetsMap.get(name);
+ return mVectorState.mVGTargetsMap.get(name);
}
@Override
@@ -310,61 +283,23 @@ public class VectorDrawable extends Drawable {
// Color filters always override tint filters.
final ColorFilter colorFilter = (mColorFilter == null ? mTintFilter : mColorFilter);
-
- // 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.
- canvas.getMatrix(mTmpMatrix);
- mTmpMatrix.getValues(mTmpFloats);
- float canvasScaleX = Math.abs(mTmpFloats[Matrix.MSCALE_X]);
- float canvasScaleY = Math.abs(mTmpFloats[Matrix.MSCALE_Y]);
- int scaledWidth = (int) (mTmpBounds.width() * canvasScaleX);
- int scaledHeight = (int) (mTmpBounds.height() * canvasScaleY);
- scaledWidth = Math.min(MAX_CACHED_BITMAP_SIZE, scaledWidth);
- scaledHeight = Math.min(MAX_CACHED_BITMAP_SIZE, scaledHeight);
-
- if (scaledWidth <= 0 || scaledHeight <= 0) {
- return;
- }
-
- final int saveCount = canvas.save();
- canvas.translate(mTmpBounds.left, mTmpBounds.top);
-
- // Handle RTL mirroring.
- final boolean needMirroring = needMirroring();
- if (needMirroring) {
- canvas.translate(mTmpBounds.width(), 0);
- canvas.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);
- mTmpBounds.offsetTo(0, 0);
-
- mVectorState.createCachedBitmapIfNeeded(scaledWidth, scaledHeight);
- if (!mAllowCaching) {
- mVectorState.updateCachedBitmap(scaledWidth, scaledHeight);
- } else {
- if (!mVectorState.canReuseCache()) {
- mVectorState.updateCachedBitmap(scaledWidth, scaledHeight);
- mVectorState.updateCacheStates();
- }
- }
- mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter, mTmpBounds);
- canvas.restoreToCount(saveCount);
+ final long colorFilterNativeInstance = colorFilter == null ? 0 :
+ colorFilter.native_instance;
+ boolean canReuseCache = mVectorState.canReuseCache();
+ nDraw(mVectorState.getNativeRenderer(), canvas.getNativeCanvasWrapper(),
+ colorFilterNativeInstance, mTmpBounds, needMirroring(),
+ canReuseCache);
}
+
@Override
public int getAlpha() {
- return mVectorState.mVPathRenderer.getRootAlpha();
+ return (int) (mVectorState.getAlpha() * 255);
}
@Override
public void setAlpha(int alpha) {
- if (mVectorState.mVPathRenderer.getRootAlpha() != alpha) {
- mVectorState.mVPathRenderer.setRootAlpha(alpha);
+ if (mVectorState.setAlpha(alpha / 255f)) {
invalidateSelf();
}
}
@@ -410,7 +345,7 @@ public class VectorDrawable extends Drawable {
boolean changed = false;
final VectorDrawableState state = mVectorState;
- if (state.mVPathRenderer != null && state.mVPathRenderer.onStateChange(stateSet)) {
+ if (state.onStateChange(stateSet)) {
changed = true;
state.mCacheDirty = true;
}
@@ -457,16 +392,15 @@ public class VectorDrawable extends Drawable {
* from the source density against which the constant state was loaded.
*/
void computeVectorSize() {
- final VPathRenderer pathRenderer = mVectorState.mVPathRenderer;
- final Insets opticalInsets = pathRenderer.mOpticalInsets;
+ final Insets opticalInsets = mVectorState.mOpticalInsets;
- final int sourceDensity = pathRenderer.mDensity;
+ final int sourceDensity = mVectorState.mDensity;
final int targetDensity = mTargetDensity;
if (targetDensity != sourceDensity) {
mDpiScaledWidth = Drawable.scaleFromDensity(
- (int) pathRenderer.mBaseWidth, sourceDensity, targetDensity, true);
+ (int) mVectorState.mBaseWidth, sourceDensity, targetDensity, true);
mDpiScaledHeight = Drawable.scaleFromDensity(
- (int) pathRenderer.mBaseHeight,sourceDensity, targetDensity, true);
+ (int) mVectorState.mBaseHeight,sourceDensity, targetDensity, true);
final int left = Drawable.scaleFromDensity(
opticalInsets.left, sourceDensity, targetDensity, false);
final int right = Drawable.scaleFromDensity(
@@ -477,8 +411,8 @@ public class VectorDrawable extends Drawable {
opticalInsets.bottom, sourceDensity, targetDensity, false);
mDpiScaledInsets = Insets.of(left, top, right, bottom);
} else {
- mDpiScaledWidth = (int) pathRenderer.mBaseWidth;
- mDpiScaledHeight = (int) pathRenderer.mBaseHeight;
+ mDpiScaledWidth = (int) mVectorState.mBaseWidth;
+ mDpiScaledHeight = (int) mVectorState.mBaseHeight;
mDpiScaledInsets = opticalInsets;
}
@@ -499,8 +433,7 @@ public class VectorDrawable extends Drawable {
return;
}
- final VPathRenderer path = state.mVPathRenderer;
- final boolean changedDensity = path.setDensity(
+ final boolean changedDensity = mVectorState.setDensity(
Drawable.resolveDensity(t.getResources(), 0));
mDpiScaledDirty |= changedDensity;
@@ -511,7 +444,7 @@ public class VectorDrawable extends Drawable {
state.mCacheDirty = true;
updateStateFromTypedArray(a);
} catch (XmlPullParserException e) {
- rethrowAsRuntimeException(e);
+ throw new RuntimeException(e);
} finally {
a.recycle();
}
@@ -525,8 +458,8 @@ public class VectorDrawable extends Drawable {
state.mTint = state.mTint.obtainForTheme(t);
}
- if (path != null && path.canApplyTheme()) {
- path.applyTheme(t);
+ if (mVectorState != null && mVectorState.canApplyTheme()) {
+ mVectorState.applyTheme(t);
}
// Update local properties.
@@ -540,17 +473,17 @@ public class VectorDrawable extends Drawable {
* @hide
*/
public float getPixelSize() {
- if (mVectorState == null || mVectorState.mVPathRenderer == null ||
- mVectorState.mVPathRenderer.mBaseWidth == 0 ||
- mVectorState.mVPathRenderer.mBaseHeight == 0 ||
- mVectorState.mVPathRenderer.mViewportHeight == 0 ||
- mVectorState.mVPathRenderer.mViewportWidth == 0) {
+ if (mVectorState == null ||
+ mVectorState.mBaseWidth == 0 ||
+ mVectorState.mBaseHeight == 0 ||
+ mVectorState.mViewportHeight == 0 ||
+ mVectorState.mViewportWidth == 0) {
return 1; // fall back to 1:1 pixel mapping.
}
- float intrinsicWidth = mVectorState.mVPathRenderer.mBaseWidth;
- float intrinsicHeight = mVectorState.mVPathRenderer.mBaseHeight;
- float viewportWidth = mVectorState.mVPathRenderer.mViewportWidth;
- float viewportHeight = mVectorState.mVPathRenderer.mViewportHeight;
+ float intrinsicWidth = mVectorState.mBaseWidth;
+ float intrinsicHeight = mVectorState.mBaseHeight;
+ float viewportWidth = mVectorState.mViewportWidth;
+ float viewportHeight = mVectorState.mViewportHeight;
float scaleX = viewportWidth / intrinsicWidth;
float scaleY = viewportHeight / intrinsicHeight;
return Math.min(scaleX, scaleY);
@@ -582,20 +515,20 @@ public class VectorDrawable extends Drawable {
return null;
}
- private static int applyAlpha(int color, float alpha) {
- int alphaBytes = Color.alpha(color);
- color &= 0x00FFFFFF;
- color |= ((int) (alphaBytes * alpha)) << 24;
- return color;
- }
-
@Override
public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
+ if (mVectorState.mRootGroup != null || mVectorState.mNativeRendererPtr != 0) {
+ // This VD has been used to display other VD resource content, clean up.
+ mVectorState.mRootGroup = new VGroup();
+ if (mVectorState.mNativeRendererPtr != 0) {
+ nDestroyRenderer(mVectorState.mNativeRendererPtr);
+ }
+ mVectorState.mNativeRendererPtr = nCreateRenderer(mVectorState.mRootGroup.mNativePtr);
+ }
final VectorDrawableState state = mVectorState;
- state.mVPathRenderer = new VPathRenderer();
- state.mVPathRenderer.setDensity(Drawable.resolveDensity(r, 0));
+ state.setDensity(Drawable.resolveDensity(r, 0));
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawable);
updateStateFromTypedArray(a);
@@ -612,7 +545,6 @@ public class VectorDrawable extends Drawable {
private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
final VectorDrawableState state = mVectorState;
- final VPathRenderer pathRenderer = state.mVPathRenderer;
// Account for any configuration changes.
state.mChangingConfigurations |= a.getChangingConfigurations();
@@ -633,63 +565,63 @@ public class VectorDrawable extends Drawable {
state.mAutoMirrored = a.getBoolean(
R.styleable.VectorDrawable_autoMirrored, state.mAutoMirrored);
- pathRenderer.mViewportWidth = a.getFloat(
- R.styleable.VectorDrawable_viewportWidth, pathRenderer.mViewportWidth);
- pathRenderer.mViewportHeight = a.getFloat(
- R.styleable.VectorDrawable_viewportHeight, pathRenderer.mViewportHeight);
+ float viewportWidth = a.getFloat(
+ R.styleable.VectorDrawable_viewportWidth, state.mViewportWidth);
+ float viewportHeight = a.getFloat(
+ R.styleable.VectorDrawable_viewportHeight, state.mViewportHeight);
+ state.setViewportSize(viewportWidth, viewportHeight);
- if (pathRenderer.mViewportWidth <= 0) {
+ if (state.mViewportWidth <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
"<vector> tag requires viewportWidth > 0");
- } else if (pathRenderer.mViewportHeight <= 0) {
+ } else if (state.mViewportHeight <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
"<vector> tag requires viewportHeight > 0");
}
- pathRenderer.mBaseWidth = a.getDimension(
- R.styleable.VectorDrawable_width, pathRenderer.mBaseWidth);
- pathRenderer.mBaseHeight = a.getDimension(
- R.styleable.VectorDrawable_height, pathRenderer.mBaseHeight);
+ state.mBaseWidth = a.getDimension(
+ R.styleable.VectorDrawable_width, state.mBaseWidth);
+ state.mBaseHeight = a.getDimension(
+ R.styleable.VectorDrawable_height, state.mBaseHeight);
- if (pathRenderer.mBaseWidth <= 0) {
+ if (state.mBaseWidth <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
"<vector> tag requires width > 0");
- } else if (pathRenderer.mBaseHeight <= 0) {
+ } else if (state.mBaseHeight <= 0) {
throw new XmlPullParserException(a.getPositionDescription() +
"<vector> tag requires height > 0");
}
final int insetLeft = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetLeft, pathRenderer.mOpticalInsets.left);
+ R.styleable.VectorDrawable_opticalInsetLeft, state.mOpticalInsets.left);
final int insetTop = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetTop, pathRenderer.mOpticalInsets.top);
+ R.styleable.VectorDrawable_opticalInsetTop, state.mOpticalInsets.top);
final int insetRight = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetRight, pathRenderer.mOpticalInsets.right);
+ R.styleable.VectorDrawable_opticalInsetRight, state.mOpticalInsets.right);
final int insetBottom = a.getDimensionPixelOffset(
- R.styleable.VectorDrawable_opticalInsetBottom, pathRenderer.mOpticalInsets.bottom);
- pathRenderer.mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
+ R.styleable.VectorDrawable_opticalInsetBottom, state.mOpticalInsets.bottom);
+ state.mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
final float alphaInFloat = a.getFloat(
- R.styleable.VectorDrawable_alpha, pathRenderer.getAlpha());
- pathRenderer.setAlpha(alphaInFloat);
+ R.styleable.VectorDrawable_alpha, state.getAlpha());
+ state.setAlpha(alphaInFloat);
final String name = a.getString(R.styleable.VectorDrawable_name);
if (name != null) {
- pathRenderer.mRootName = name;
- pathRenderer.mVGTargetsMap.put(name, pathRenderer);
+ state.mRootName = name;
+ state.mVGTargetsMap.put(name, state);
}
}
private void inflateChildElements(Resources res, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
final VectorDrawableState state = mVectorState;
- final VPathRenderer pathRenderer = state.mVPathRenderer;
boolean noPathTag = true;
// Use a stack to help to build the group tree.
// The top of the stack is always the current group.
final Stack<VGroup> groupStack = new Stack<VGroup>();
- groupStack.push(pathRenderer.mRootGroup);
+ groupStack.push(state.mRootGroup);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
@@ -702,7 +634,7 @@ public class VectorDrawable extends Drawable {
path.inflate(res, attrs, theme);
currentGroup.addChild(path);
if (path.getPathName() != null) {
- pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
+ state.mVGTargetsMap.put(path.getPathName(), path);
}
noPathTag = false;
state.mChangingConfigurations |= path.mChangingConfigurations;
@@ -711,7 +643,7 @@ public class VectorDrawable extends Drawable {
path.inflate(res, attrs, theme);
currentGroup.addChild(path);
if (path.getPathName() != null) {
- pathRenderer.mVGTargetsMap.put(path.getPathName(), path);
+ state.mVGTargetsMap.put(path.getPathName(), path);
}
state.mChangingConfigurations |= path.mChangingConfigurations;
} else if (SHAPE_GROUP.equals(tagName)) {
@@ -720,7 +652,7 @@ public class VectorDrawable extends Drawable {
currentGroup.addChild(newChildGroup);
groupStack.push(newChildGroup);
if (newChildGroup.getGroupName() != null) {
- pathRenderer.mVGTargetsMap.put(newChildGroup.getGroupName(),
+ state.mVGTargetsMap.put(newChildGroup.getGroupName(),
newChildGroup);
}
state.mChangingConfigurations |= newChildGroup.mChangingConfigurations;
@@ -734,11 +666,6 @@ public class VectorDrawable extends Drawable {
eventType = parser.next();
}
- // Print the tree out for debug.
- if (DBG_VECTOR_DRAWABLE) {
- pathRenderer.printGroupTree();
- }
-
if (noPathTag) {
final StringBuffer tag = new StringBuffer();
@@ -757,7 +684,7 @@ public class VectorDrawable extends Drawable {
}
void setAllowCaching(boolean allowCaching) {
- mAllowCaching = allowCaching;
+ nSetAllowCaching(mVectorState.getNativeRenderer(), allowCaching);
}
private boolean needMirroring() {
@@ -778,84 +705,68 @@ public class VectorDrawable extends Drawable {
}
private static class VectorDrawableState extends ConstantState {
+ // Variables below need to be copied (deep copy if applicable) for mutation.
int[] mThemeAttrs;
int mChangingConfigurations;
- VPathRenderer mVPathRenderer;
ColorStateList mTint = null;
Mode mTintMode = DEFAULT_TINT_MODE;
boolean mAutoMirrored;
- Bitmap mCachedBitmap;
+ float mBaseWidth = 0;
+ float mBaseHeight = 0;
+ float mViewportWidth = 0;
+ float mViewportHeight = 0;
+ Insets mOpticalInsets = Insets.NONE;
+ String mRootName = null;
+ VGroup mRootGroup;
+ long mNativeRendererPtr;
+
+ int mDensity = DisplayMetrics.DENSITY_DEFAULT;
+ final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
+
+ // Fields for cache
int[] mCachedThemeAttrs;
ColorStateList mCachedTint;
Mode mCachedTintMode;
- int mCachedRootAlpha;
boolean mCachedAutoMirrored;
boolean mCacheDirty;
- /** Temporary paint object used to draw cached bitmaps. */
- Paint mTempPaint;
// Deep copy for mutate() or implicitly mutate.
public VectorDrawableState(VectorDrawableState copy) {
if (copy != null) {
mThemeAttrs = copy.mThemeAttrs;
mChangingConfigurations = copy.mChangingConfigurations;
- mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
mTint = copy.mTint;
mTintMode = copy.mTintMode;
mAutoMirrored = copy.mAutoMirrored;
+ mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
+ mNativeRendererPtr = nCreateRenderer(mRootGroup.mNativePtr);
+
+ mBaseWidth = copy.mBaseWidth;
+ mBaseHeight = copy.mBaseHeight;
+ setViewportSize(copy.mViewportWidth, copy.mViewportHeight);
+ mOpticalInsets = copy.mOpticalInsets;
+
+ mRootName = copy.mRootName;
+ mDensity = copy.mDensity;
+ if (copy.mRootName != null) {
+ mVGTargetsMap.put(copy.mRootName, this);
+ }
}
}
- public void drawCachedBitmapWithRootAlpha(Canvas canvas, ColorFilter filter,
- Rect originalBounds) {
- // The bitmap's size is the same as the bounds.
- final Paint p = getPaint(filter);
- canvas.drawBitmap(mCachedBitmap, null, originalBounds, p);
- }
-
- public boolean hasTranslucentRoot() {
- return mVPathRenderer.getRootAlpha() < 255;
- }
-
- /**
- * @return null when there is no need for alpha paint.
- */
- public Paint getPaint(ColorFilter filter) {
- if (!hasTranslucentRoot() && filter == null) {
- return null;
- }
-
- if (mTempPaint == null) {
- mTempPaint = new Paint();
- mTempPaint.setFilterBitmap(true);
+ @Override
+ public void finalize() throws Throwable {
+ if (mNativeRendererPtr != 0) {
+ nDestroyRenderer(mNativeRendererPtr);
+ mNativeRendererPtr = 0;
}
- mTempPaint.setAlpha(mVPathRenderer.getRootAlpha());
- mTempPaint.setColorFilter(filter);
- return mTempPaint;
- }
-
- public void updateCachedBitmap(int width, int height) {
- mCachedBitmap.eraseColor(Color.TRANSPARENT);
- Canvas tmpCanvas = new Canvas(mCachedBitmap);
- mVPathRenderer.draw(tmpCanvas, width, height, null);
+ super.finalize();
}
- public void createCachedBitmapIfNeeded(int width, int height) {
- if (mCachedBitmap == null || !canReuseBitmap(width, height)) {
- mCachedBitmap = Bitmap.createBitmap(width, height,
- Bitmap.Config.ARGB_8888);
- mCacheDirty = true;
- }
- }
-
- public boolean canReuseBitmap(int width, int height) {
- if (width == mCachedBitmap.getWidth()
- && height == mCachedBitmap.getHeight()) {
- return true;
- }
- return false;
+ long getNativeRenderer() {
+ return mNativeRendererPtr;
}
public boolean canReuseCache() {
@@ -863,10 +774,10 @@ public class VectorDrawable extends Drawable {
&& mCachedThemeAttrs == mThemeAttrs
&& mCachedTint == mTint
&& mCachedTintMode == mTintMode
- && mCachedAutoMirrored == mAutoMirrored
- && mCachedRootAlpha == mVPathRenderer.getRootAlpha()) {
+ && mCachedAutoMirrored == mAutoMirrored) {
return true;
}
+ updateCacheStates();
return false;
}
@@ -876,21 +787,25 @@ public class VectorDrawable extends Drawable {
mCachedThemeAttrs = mThemeAttrs;
mCachedTint = mTint;
mCachedTintMode = mTintMode;
- mCachedRootAlpha = mVPathRenderer.getRootAlpha();
mCachedAutoMirrored = mAutoMirrored;
mCacheDirty = false;
}
+ public void applyTheme(Theme t) {
+ mRootGroup.applyTheme(t);
+ }
+
@Override
public boolean canApplyTheme() {
return mThemeAttrs != null
- || (mVPathRenderer != null && mVPathRenderer.canApplyTheme())
+ || (mRootGroup != null && mRootGroup.canApplyTheme())
|| (mTint != null && mTint.canApplyTheme())
|| super.canApplyTheme();
}
public VectorDrawableState() {
- mVPathRenderer = new VPathRenderer();
+ mRootGroup = new VGroup();
+ mNativeRendererPtr = nCreateRenderer(mRootGroup.mNativePtr);
}
@Override
@@ -911,80 +826,13 @@ public class VectorDrawable extends Drawable {
public boolean isStateful() {
return (mTint != null && mTint.isStateful())
- || (mVPathRenderer != null && mVPathRenderer.isStateful());
- }
- }
-
- private static class VPathRenderer {
- /* Right now the internal data structure is organized as a tree.
- * 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
- *
- */
- // Variables that only used temporarily inside the draw() call, so there
- // is no need for deep copying.
- private final TempState mTempState = new TempState();
-
- /////////////////////////////////////////////////////
- // Variables below need to be copied (deep copy if applicable) for mutation.
- private int mChangingConfigurations;
- private final VGroup mRootGroup;
- float mBaseWidth = 0;
- float mBaseHeight = 0;
- float mViewportWidth = 0;
- float mViewportHeight = 0;
- Insets mOpticalInsets = Insets.NONE;
- int mRootAlpha = 0xFF;
- String mRootName = null;
-
- int mDensity = DisplayMetrics.DENSITY_DEFAULT;
-
- final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<>();
-
- public VPathRenderer() {
- mRootGroup = new VGroup();
- }
-
- public void setRootAlpha(int alpha) {
- mRootAlpha = alpha;
- }
-
- public int getRootAlpha() {
- return mRootAlpha;
- }
-
- // setAlpha() and getAlpha() are used mostly for animation purpose, since
- // Animator like to use alpha from 0 to 1.
- public void setAlpha(float alpha) {
- setRootAlpha((int) (alpha * 255));
- }
-
- @SuppressWarnings("unused")
- public float getAlpha() {
- return getRootAlpha() / 255.0f;
+ || (mRootGroup != null && mRootGroup.isStateful());
}
- public VPathRenderer(VPathRenderer copy) {
- mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap);
- mBaseWidth = copy.mBaseWidth;
- mBaseHeight = copy.mBaseHeight;
- mViewportWidth = copy.mViewportWidth;
- mViewportHeight = copy.mViewportHeight;
- mOpticalInsets = copy.mOpticalInsets;
- mChangingConfigurations = copy.mChangingConfigurations;
- mRootAlpha = copy.mRootAlpha;
- mRootName = copy.mRootName;
- mDensity = copy.mDensity;
- if (copy.mRootName != null) {
- mVGTargetsMap.put(copy.mRootName, this);
- }
+ void setViewportSize(float viewportWidth, float viewportHeight) {
+ mViewportWidth = viewportWidth;
+ mViewportHeight = viewportHeight;
+ nSetRendererViewportSize(getNativeRenderer(), viewportWidth, viewportHeight);
}
public final boolean setDensity(int targetDensity) {
@@ -1012,68 +860,50 @@ public class VectorDrawable extends Drawable {
mOpticalInsets = Insets.of(insetLeft, insetTop, insetRight, insetBottom);
}
- public boolean canApplyTheme() {
- return mRootGroup.canApplyTheme();
- }
-
- public void applyTheme(Theme t) {
- mRootGroup.applyTheme(t);
- }
-
public boolean onStateChange(int[] stateSet) {
return mRootGroup.onStateChange(stateSet);
}
- public boolean isStateful() {
- return mRootGroup.isStateful();
- }
-
- public void draw(Canvas canvas, int w, int h, ColorFilter filter) {
- final float scaleX = w / mViewportWidth;
- final float scaleY = h / mViewportHeight;
- mRootGroup.draw(canvas, mTempState, Matrix.IDENTITY_MATRIX, filter, scaleX, scaleY);
+ /**
+ * setAlpha() and getAlpha() are used mostly for animation purpose. Return true if alpha
+ * has changed.
+ */
+ public boolean setAlpha(float alpha) {
+ return nSetRootAlpha(mNativeRendererPtr, alpha);
}
- public void printGroupTree() {
- mRootGroup.printGroupTree("");
+ @SuppressWarnings("unused")
+ public float getAlpha() {
+ return nGetRootAlpha(mNativeRendererPtr);
}
}
private static class VGroup implements VObject {
- private static final String GROUP_INDENT = " ";
-
- // mStackedMatrix is only used temporarily when drawing, it combines all
- // the parents' local matrices with the current one.
- private final Matrix mStackedMatrix = new Matrix();
-
+ private static final int ROTATE_INDEX = 0;
+ private static final int PIVOT_X_INDEX = 1;
+ private static final int PIVOT_Y_INDEX = 2;
+ private static final int SCALE_X_INDEX = 3;
+ private static final int SCALE_Y_INDEX = 4;
+ private static final int TRANSLATE_X_INDEX = 5;
+ private static final int TRANSLATE_Y_INDEX = 6;
+ private static final int TRANSFORM_PROPERTY_COUNT = 7;
+
+ // Temp array to store transform values obtained from native.
+ private float[] mTransform;
/////////////////////////////////////////////////////
// Variables below need to be copied (deep copy if applicable) for mutation.
private final ArrayList<VObject> mChildren = new ArrayList<>();
-
- private float mRotate = 0;
- private float mPivotX = 0;
- private float mPivotY = 0;
- private float mScaleX = 1;
- private float mScaleY = 1;
- private float mTranslateX = 0;
- private float mTranslateY = 0;
private boolean mIsStateful;
// mLocalMatrix is updated based on the update of transformation information,
// either parsed from the XML or by animation.
- private final Matrix mLocalMatrix = new Matrix();
private int mChangingConfigurations;
private int[] mThemeAttrs;
private String mGroupName = null;
+ private long mNativePtr = 0;
public VGroup(VGroup copy, ArrayMap<String, Object> targetsMap) {
- mRotate = copy.mRotate;
- mPivotX = copy.mPivotX;
- mPivotY = copy.mPivotY;
- mScaleX = copy.mScaleX;
- mScaleY = copy.mScaleY;
- mTranslateX = copy.mTranslateX;
- mTranslateY = copy.mTranslateY;
+
mIsStateful = copy.mIsStateful;
mThemeAttrs = copy.mThemeAttrs;
mGroupName = copy.mGroupName;
@@ -1081,15 +911,14 @@ public class VectorDrawable extends Drawable {
if (mGroupName != null) {
targetsMap.put(mGroupName, this);
}
-
- mLocalMatrix.set(copy.mLocalMatrix);
+ mNativePtr = nCreateGroup(copy.mNativePtr);
final ArrayList<VObject> children = copy.mChildren;
for (int i = 0; i < children.size(); i++) {
final VObject copyChild = children.get(i);
if (copyChild instanceof VGroup) {
final VGroup copyGroup = (VGroup) copyChild;
- mChildren.add(new VGroup(copyGroup, targetsMap));
+ addChild(new VGroup(copyGroup, targetsMap));
} else {
final VPath newPath;
if (copyChild instanceof VFullPath) {
@@ -1099,7 +928,7 @@ public class VectorDrawable extends Drawable {
} else {
throw new IllegalStateException("Unknown object in the tree!");
}
- mChildren.add(newPath);
+ addChild(newPath);
if (newPath.mPathName != null) {
targetsMap.put(newPath.mPathName, newPath);
}
@@ -1108,43 +937,23 @@ public class VectorDrawable extends Drawable {
}
public VGroup() {
+ mNativePtr = nCreateGroup();
}
public String getGroupName() {
return mGroupName;
}
- public Matrix getLocalMatrix() {
- return mLocalMatrix;
- }
-
public void addChild(VObject child) {
+ nAddChild(mNativePtr, child.getNativePtr());
mChildren.add(child);
mIsStateful |= child.isStateful();
}
@Override
- public void draw(Canvas canvas, TempState temp, Matrix currentMatrix,
- ColorFilter filter, float scaleX, float scaleY) {
- // 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.
- mStackedMatrix.set(currentMatrix);
- mStackedMatrix.preConcat(mLocalMatrix);
-
- // Save the current clip information, which is local to this group.
- canvas.save();
-
- // Draw the group tree in the same order as the XML file.
- for (int i = 0, count = mChildren.size(); i < count; i++) {
- final VObject child = mChildren.get(i);
- child.draw(canvas, temp, mStackedMatrix, filter, scaleX, scaleY);
- }
-
- // Restore the previous clip information.
- canvas.restore();
+ public long getNativePtr() {
+ return mNativePtr;
}
@Override
@@ -1155,27 +964,43 @@ public class VectorDrawable extends Drawable {
a.recycle();
}
- private void updateStateFromTypedArray(TypedArray a) {
+ void updateStateFromTypedArray(TypedArray a) {
// Account for any configuration changes.
mChangingConfigurations |= a.getChangingConfigurations();
// Extract the theme attributes, if any.
mThemeAttrs = a.extractThemeAttrs();
-
- mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate);
- mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX);
- mPivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, mPivotY);
- mScaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX, mScaleX);
- mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY);
- mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX);
- mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
+ if (mTransform == null) {
+ // Lazy initialization: If the group is created through copy constructor, this may
+ // never get called.
+ mTransform = new float[TRANSFORM_PROPERTY_COUNT];
+ }
+ boolean success = nGetGroupProperties(mNativePtr, mTransform, TRANSFORM_PROPERTY_COUNT);
+ if (!success) {
+ throw new RuntimeException("Error: inconsistent property count");
+ }
+ float rotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation,
+ mTransform[ROTATE_INDEX]);
+ float pivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX,
+ mTransform[PIVOT_X_INDEX]);
+ float pivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY,
+ mTransform[PIVOT_Y_INDEX]);
+ float scaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX,
+ mTransform[SCALE_X_INDEX]);
+ float scaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY,
+ mTransform[SCALE_Y_INDEX]);
+ float translateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX,
+ mTransform[TRANSLATE_X_INDEX]);
+ float translateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY,
+ mTransform[TRANSLATE_Y_INDEX]);
final String groupName = a.getString(R.styleable.VectorDrawableGroup_name);
if (groupName != null) {
mGroupName = groupName;
+ nSetName(mNativePtr, mGroupName);
}
-
- updateLocalMatrix();
+ nUpdateGroupProperties(mNativePtr, rotate, pivotX, pivotY, scaleX, scaleY,
+ translateX, translateY);
}
@Override
@@ -1216,6 +1041,16 @@ public class VectorDrawable extends Drawable {
}
@Override
+ protected void finalize() throws Throwable {
+ if (mNativePtr != 0) {
+ nDestroy(mNativePtr);
+ mNativePtr = 0;
+ }
+ super.finalize();
+ }
+
+
+ @Override
public void applyTheme(Theme t) {
if (mThemeAttrs != null) {
final TypedArray a = t.resolveAttributes(mThemeAttrs,
@@ -1236,124 +1071,75 @@ public class VectorDrawable extends Drawable {
}
}
- private void updateLocalMatrix() {
- // The order we apply is the same as the
- // RenderNode.cpp::applyViewPropertyTransforms().
- mLocalMatrix.reset();
- mLocalMatrix.postTranslate(-mPivotX, -mPivotY);
- mLocalMatrix.postScale(mScaleX, mScaleY);
- mLocalMatrix.postRotate(mRotate, 0, 0);
- mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
- }
-
- public void printGroupTree(String indent) {
- Log.v(LOGTAG, indent + "group:" + getGroupName() + " rotation is " + mRotate);
- Log.v(LOGTAG, indent + "matrix:" + getLocalMatrix().toString());
-
- final int count = mChildren.size();
- if (count > 0) {
- indent += GROUP_INDENT;
- }
-
- // Then print all the children groups.
- for (int i = 0; i < count; i++) {
- final VObject child = mChildren.get(i);
- if (child instanceof VGroup) {
- ((VGroup) child).printGroupTree(indent);
- }
- }
- }
-
/* Setters and Getters, used by animator from AnimatedVectorDrawable. */
@SuppressWarnings("unused")
public float getRotation() {
- return mRotate;
+ return nGetRotation(mNativePtr);
}
@SuppressWarnings("unused")
public void setRotation(float rotation) {
- if (rotation != mRotate) {
- mRotate = rotation;
- updateLocalMatrix();
- }
+ nSetRotation(mNativePtr, rotation);
}
@SuppressWarnings("unused")
public float getPivotX() {
- return mPivotX;
+ return nGetPivotX(mNativePtr);
}
@SuppressWarnings("unused")
public void setPivotX(float pivotX) {
- if (pivotX != mPivotX) {
- mPivotX = pivotX;
- updateLocalMatrix();
- }
+ nSetPivotX(mNativePtr, pivotX);
}
@SuppressWarnings("unused")
public float getPivotY() {
- return mPivotY;
+ return nGetPivotY(mNativePtr);
}
@SuppressWarnings("unused")
public void setPivotY(float pivotY) {
- if (pivotY != mPivotY) {
- mPivotY = pivotY;
- updateLocalMatrix();
- }
+ nSetPivotY(mNativePtr, pivotY);
}
@SuppressWarnings("unused")
public float getScaleX() {
- return mScaleX;
+ return nGetScaleX(mNativePtr);
}
@SuppressWarnings("unused")
public void setScaleX(float scaleX) {
- if (scaleX != mScaleX) {
- mScaleX = scaleX;
- updateLocalMatrix();
- }
+ nSetScaleX(mNativePtr, scaleX);
}
@SuppressWarnings("unused")
public float getScaleY() {
- return mScaleY;
+ return nGetScaleY(mNativePtr);
}
@SuppressWarnings("unused")
public void setScaleY(float scaleY) {
- if (scaleY != mScaleY) {
- mScaleY = scaleY;
- updateLocalMatrix();
- }
+ nSetScaleY(mNativePtr, scaleY);
}
@SuppressWarnings("unused")
public float getTranslateX() {
- return mTranslateX;
+ return nGetTranslateX(mNativePtr);
}
@SuppressWarnings("unused")
public void setTranslateX(float translateX) {
- if (translateX != mTranslateX) {
- mTranslateX = translateX;
- updateLocalMatrix();
- }
+ nSetTranslateX(mNativePtr, translateX);
}
@SuppressWarnings("unused")
public float getTranslateY() {
- return mTranslateY;
+ return nGetTranslateY(mNativePtr);
}
@SuppressWarnings("unused")
public void setTranslateY(float translateY) {
- if (translateY != mTranslateY) {
- mTranslateY = translateY;
- updateLocalMatrix();
- }
+ nSetTranslateY(mNativePtr, translateY);
}
}
@@ -1362,6 +1148,7 @@ public class VectorDrawable extends Drawable {
*/
private static abstract class VPath implements VObject {
protected PathParser.PathData mPathData = null;
+
String mPathName;
int mChangingConfigurations;
@@ -1379,10 +1166,6 @@ public class VectorDrawable extends Drawable {
return mPathName;
}
- public boolean isClipPath() {
- return false;
- }
-
/* Setters and Getters, used by animator from AnimatedVectorDrawable. */
@SuppressWarnings("unused")
public PathParser.PathData getPathData() {
@@ -1393,79 +1176,7 @@ public class VectorDrawable extends Drawable {
@SuppressWarnings("unused")
public void setPathData(PathParser.PathData pathData) {
mPathData.setPathData(pathData);
- }
-
- @Override
- public final void draw(Canvas canvas, TempState temp, Matrix groupStackedMatrix,
- ColorFilter filter, float scaleX, float scaleY) {
- final float matrixScale = VPath.getMatrixScale(groupStackedMatrix);
- if (matrixScale == 0) {
- // When either x or y is scaled to 0, we don't need to draw anything.
- return;
- }
-
- final Path path = temp.path;
- path.reset();
- toPath(temp, path);
-
- final Matrix pathMatrix = temp.pathMatrix;
- pathMatrix.set(groupStackedMatrix);
- pathMatrix.postScale(scaleX, scaleY);
-
- final Path renderPath = temp.renderPath;
- renderPath.reset();
- renderPath.addPath(path, pathMatrix);
-
- final float minScale = Math.min(scaleX, scaleY);
- final float strokeScale = minScale * matrixScale;
- drawPath(temp, renderPath, canvas, filter, strokeScale);
- }
-
- /**
- * Writes the path's nodes to an output Path for rendering.
- *
- * @param temp temporary state variables
- * @param outPath the output path
- */
- protected void toPath(TempState temp, Path outPath) {
- if (mPathData != null) {
- PathParser.createPathFromPathData(outPath, mPathData);
- }
- }
-
- /**
- * Draws the specified path into the supplied canvas.
- */
- protected abstract void drawPath(TempState temp, Path path, Canvas canvas,
- ColorFilter filter, float strokeScale);
-
- private static float getMatrixScale(Matrix 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.
- float[] unitVectors = new float[] {0, 1, 1, 0};
- groupStackedMatrix.mapVectors(unitVectors);
- float scaleX = MathUtils.mag(unitVectors[0], unitVectors[1]);
- float scaleY = MathUtils.mag(unitVectors[2], unitVectors[3]);
- float crossProduct = MathUtils.cross(unitVectors[0], unitVectors[1],
- unitVectors[2], unitVectors[3]);
- float maxScale = MathUtils.max(scaleX, scaleY);
-
- float matrixScale = 0;
- if (maxScale > 0) {
- matrixScale = MathUtils.abs(crossProduct) / maxScale;
- }
- if (DBG_VECTOR_DRAWABLE) {
- Log.d(LOGTAG, "Scale x " + scaleX + " y " + scaleY + " final " + matrixScale);
- }
- return matrixScale;
+ nSetPathData(getNativePtr(), mPathData.getNativePtr());
}
}
@@ -1473,21 +1184,31 @@ public class VectorDrawable extends Drawable {
* Clip path, which only has name and pathData.
*/
private static class VClipPath extends VPath {
+ long mNativePtr = 0;
public VClipPath() {
+ mNativePtr = nCreateClipPath();
// Empty constructor.
}
public VClipPath(VClipPath copy) {
super(copy);
+ mNativePtr = nCreateClipPath(copy.mNativePtr);
}
@Override
- protected void drawPath(TempState temp, Path renderPath, Canvas canvas, ColorFilter filter,
- float strokeScale) {
- canvas.clipPath(renderPath);
+ public long getNativePtr() {
+ return mNativePtr;
}
@Override
+ protected void finalize() throws Throwable {
+ if (mNativePtr != 0) {
+ nDestroy(mNativePtr);
+ mNativePtr = 0;
+ }
+ super.finalize();
+ }
+ @Override
public void inflate(Resources r, AttributeSet attrs, Theme theme) {
final TypedArray a = obtainAttributes(r, theme, attrs,
R.styleable.VectorDrawableClipPath);
@@ -1522,95 +1243,54 @@ public class VectorDrawable extends Drawable {
final String pathName = a.getString(R.styleable.VectorDrawableClipPath_name);
if (pathName != null) {
mPathName = pathName;
+ nSetName(mNativePtr, mPathName);
}
final String pathDataString = a.getString(R.styleable.VectorDrawableClipPath_pathData);
if (pathDataString != null) {
mPathData = new PathParser.PathData(pathDataString);
+ nSetPathString(mNativePtr, pathDataString, pathDataString.length());
}
}
-
- @Override
- public boolean isClipPath() {
- return true;
- }
}
/**
* Normal path, which contains all the fill / paint information.
*/
private static class VFullPath extends VPath {
+ private static final int STROKE_WIDTH_INDEX = 0;
+ private static final int STROKE_COLOR_INDEX = 1;
+ private static final int STROKE_ALPHA_INDEX = 2;
+ private static final int FILL_COLOR_INDEX = 3;
+ private static final int FILL_ALPHA_INDEX = 4;
+ private static final int TRIM_PATH_START_INDEX = 5;
+ private static final int TRIM_PATH_END_INDEX = 6;
+ private static final int TRIM_PATH_OFFSET_INDEX = 7;
+ private static final int STROKE_LINE_CAP_INDEX = 8;
+ private static final int STROKE_LINE_JOIN_INDEX = 9;
+ private static final int STROKE_MITER_LIMIT_INDEX = 10;
+ private static final int TOTAL_PROPERTY_COUNT = 11;
+
+ // Temp array to store property data obtained from native getter.
+ private byte[] mPropertyData;
/////////////////////////////////////////////////////
// Variables below need to be copied (deep copy if applicable) for mutation.
private int[] mThemeAttrs;
-
ColorStateList mStrokeColors = null;
- int mStrokeColor = Color.TRANSPARENT;
- float mStrokeWidth = 0;
-
ColorStateList mFillColors = null;
- int mFillColor = Color.TRANSPARENT;
- float mStrokeAlpha = 1.0f;
- int mFillRule;
- float mFillAlpha = 1.0f;
- float mTrimPathStart = 0;
- float mTrimPathEnd = 1;
- float mTrimPathOffset = 0;
-
- Paint.Cap mStrokeLineCap = Paint.Cap.BUTT;
- Paint.Join mStrokeLineJoin = Paint.Join.MITER;
- float mStrokeMiterlimit = 4;
+ private long mNativePtr = 0;
public VFullPath() {
// Empty constructor.
+ mNativePtr = nCreateFullPath();
}
public VFullPath(VFullPath copy) {
super(copy);
-
+ mNativePtr = nCreateFullPath(copy.mNativePtr);
mThemeAttrs = copy.mThemeAttrs;
-
mStrokeColors = copy.mStrokeColors;
- mStrokeColor = copy.mStrokeColor;
- mStrokeWidth = copy.mStrokeWidth;
- mStrokeAlpha = copy.mStrokeAlpha;
mFillColors = copy.mFillColors;
- mFillColor = copy.mFillColor;
- mFillRule = copy.mFillRule;
- mFillAlpha = copy.mFillAlpha;
- mTrimPathStart = copy.mTrimPathStart;
- mTrimPathEnd = copy.mTrimPathEnd;
- mTrimPathOffset = copy.mTrimPathOffset;
-
- mStrokeLineCap = copy.mStrokeLineCap;
- mStrokeLineJoin = copy.mStrokeLineJoin;
- mStrokeMiterlimit = copy.mStrokeMiterlimit;
- }
-
- private Paint.Cap getStrokeLineCap(int id, Paint.Cap defValue) {
- switch (id) {
- case LINECAP_BUTT:
- return Paint.Cap.BUTT;
- case LINECAP_ROUND:
- return Paint.Cap.ROUND;
- case LINECAP_SQUARE:
- return Paint.Cap.SQUARE;
- default:
- return defValue;
- }
- }
-
- private Paint.Join getStrokeLineJoin(int id, Paint.Join defValue) {
- switch (id) {
- case LINEJOIN_MITER:
- return Paint.Join.MITER;
- case LINEJOIN_ROUND:
- return Paint.Join.ROUND;
- case LINEJOIN_BEVEL:
- return Paint.Join.BEVEL;
- default:
- return defValue;
- }
}
@Override
@@ -1618,17 +1298,22 @@ public class VectorDrawable extends Drawable {
boolean changed = false;
if (mStrokeColors != null) {
- final int oldStrokeColor = mStrokeColor;
- mStrokeColor = mStrokeColors.getColorForState(stateSet, oldStrokeColor);
- changed |= oldStrokeColor != mStrokeColor;
+ final int oldStrokeColor = getStrokeColor();
+ final int newStrokeColor = mStrokeColors.getColorForState(stateSet, oldStrokeColor);
+ changed |= oldStrokeColor != newStrokeColor;
+ if (oldStrokeColor != newStrokeColor) {
+ nSetStrokeColor(mNativePtr, newStrokeColor);
+ }
}
if (mFillColors != null) {
- final int oldFillColor = mFillColor;
- mFillColor = mFillColors.getColorForState(stateSet, oldFillColor);
- changed |= oldFillColor != mFillColor;
+ final int oldFillColor = getFillColor();
+ final int newFillColor = mFillColors.getColorForState(stateSet, oldFillColor);
+ changed |= oldFillColor != newFillColor;
+ if (oldFillColor != newFillColor) {
+ nSetFillColor(mNativePtr, newFillColor);
+ }
}
-
return changed;
}
@@ -1638,112 +1323,56 @@ public class VectorDrawable extends Drawable {
}
@Override
- public void toPath(TempState temp, Path path) {
- super.toPath(temp, path);
-
- if (mTrimPathStart != 0.0f || mTrimPathEnd != 1.0f) {
- VFullPath.applyTrim(temp, path, mTrimPathStart, mTrimPathEnd, mTrimPathOffset);
- }
+ public long getNativePtr() {
+ return mNativePtr;
}
@Override
- protected void drawPath(TempState temp, Path path, Canvas canvas, ColorFilter filter,
- float strokeScale) {
- drawPathFill(temp, path, canvas, filter);
- drawPathStroke(temp, path, canvas, filter, strokeScale);
- }
-
- /**
- * Draws this path's fill, if necessary.
- */
- private void drawPathFill(TempState temp, Path path, Canvas canvas, ColorFilter filter) {
- if (mFillColor == Color.TRANSPARENT) {
- return;
- }
-
- if (temp.mFillPaint == null) {
- temp.mFillPaint = new Paint();
- temp.mFillPaint.setStyle(Paint.Style.FILL);
- temp.mFillPaint.setAntiAlias(true);
- }
-
- final Paint fillPaint = temp.mFillPaint;
- fillPaint.setColor(applyAlpha(mFillColor, mFillAlpha));
- fillPaint.setColorFilter(filter);
- canvas.drawPath(path, fillPaint);
+ public void inflate(Resources r, AttributeSet attrs, Theme theme) {
+ final TypedArray a = obtainAttributes(r, theme, attrs,
+ R.styleable.VectorDrawablePath);
+ updateStateFromTypedArray(a);
+ a.recycle();
}
- /**
- * Draws this path's stroke, if necessary.
- */
- private void drawPathStroke(TempState temp, Path path, Canvas canvas, ColorFilter filter,
- float strokeScale) {
- if (mStrokeColor == Color.TRANSPARENT) {
- return;
- }
-
- if (temp.mStrokePaint == null) {
- temp.mStrokePaint = new Paint();
- temp.mStrokePaint.setStyle(Paint.Style.STROKE);
- temp.mStrokePaint.setAntiAlias(true);
- }
-
- final Paint strokePaint = temp.mStrokePaint;
- if (mStrokeLineJoin != null) {
- strokePaint.setStrokeJoin(mStrokeLineJoin);
- }
-
- if (mStrokeLineCap != null) {
- strokePaint.setStrokeCap(mStrokeLineCap);
+ @Override
+ protected void finalize() throws Throwable {
+ if (mNativePtr != 0) {
+ nDestroy(mNativePtr);
+ mNativePtr = 0;
}
-
- strokePaint.setStrokeMiter(mStrokeMiterlimit);
- strokePaint.setColor(applyAlpha(mStrokeColor, mStrokeAlpha));
- strokePaint.setColorFilter(filter);
- strokePaint.setStrokeWidth(mStrokeWidth * strokeScale);
- canvas.drawPath(path, strokePaint);
+ super.finalize();
}
- /**
- * Applies trimming to the specified path.
- */
- private static void applyTrim(TempState temp, Path path, float mTrimPathStart,
- float mTrimPathEnd, float mTrimPathOffset) {
- if (mTrimPathStart == 0.0f && mTrimPathEnd == 1.0f) {
- // No trimming necessary.
- return;
- }
-
- if (temp.mPathMeasure == null) {
- temp.mPathMeasure = new PathMeasure();
+ private void updateStateFromTypedArray(TypedArray a) {
+ int byteCount = TOTAL_PROPERTY_COUNT * 4;
+ if (mPropertyData == null) {
+ // Lazy initialization: If the path is created through copy constructor, this may
+ // never get called.
+ mPropertyData = new byte[byteCount];
}
- final PathMeasure pathMeasure = temp.mPathMeasure;
- pathMeasure.setPath(path, false);
-
- final float len = pathMeasure.getLength();
- final float start = len * ((mTrimPathStart + mTrimPathOffset) % 1.0f);
- final float end = len * ((mTrimPathEnd + mTrimPathOffset) % 1.0f);
- path.reset();
- if (start > end) {
- pathMeasure.getSegment(start, len, path, true);
- pathMeasure.getSegment(0, end, path, true);
- } else {
- pathMeasure.getSegment(start, end, path, true);
+ // The bulk getters/setters of property data (e.g. stroke width, color, etc) allows us
+ // to pull current values from native and store modifications with only two methods,
+ // minimizing JNI overhead.
+ boolean success = nGetFullPathProperties(mNativePtr, mPropertyData, byteCount);
+ if (!success) {
+ throw new RuntimeException("Error: inconsistent property count");
}
- // Fix bug in measure.
- path.rLineTo(0, 0);
- }
-
- @Override
- public void inflate(Resources r, AttributeSet attrs, Theme theme) {
- final TypedArray a = obtainAttributes(r, theme, attrs,
- R.styleable.VectorDrawablePath);
- updateStateFromTypedArray(a);
- a.recycle();
- }
+ ByteBuffer properties = ByteBuffer.wrap(mPropertyData);
+ properties.order(ByteOrder.nativeOrder());
+ float strokeWidth = properties.getFloat(STROKE_WIDTH_INDEX * 4);
+ int strokeColor = properties.getInt(STROKE_COLOR_INDEX * 4);
+ float strokeAlpha = properties.getFloat(STROKE_ALPHA_INDEX * 4);
+ int fillColor = properties.getInt(FILL_COLOR_INDEX * 4);
+ float fillAlpha = properties.getFloat(FILL_ALPHA_INDEX * 4);
+ float trimPathStart = properties.getFloat(TRIM_PATH_START_INDEX * 4);
+ float trimPathEnd = properties.getFloat(TRIM_PATH_END_INDEX * 4);
+ float trimPathOffset = properties.getFloat(TRIM_PATH_OFFSET_INDEX * 4);
+ int strokeLineCap = properties.getInt(STROKE_LINE_CAP_INDEX * 4);
+ int strokeLineJoin = properties.getInt(STROKE_LINE_JOIN_INDEX * 4);
+ float strokeMiterLimit = properties.getFloat(STROKE_MITER_LIMIT_INDEX * 4);
- private void updateStateFromTypedArray(TypedArray a) {
// Account for any configuration changes.
mChangingConfigurations |= a.getChangingConfigurations();
@@ -1753,11 +1382,13 @@ public class VectorDrawable extends Drawable {
final String pathName = a.getString(R.styleable.VectorDrawablePath_name);
if (pathName != null) {
mPathName = pathName;
+ nSetName(mNativePtr, mPathName);
}
final String pathString = a.getString(R.styleable.VectorDrawablePath_pathData);
if (pathString != null) {
mPathData = new PathParser.PathData(pathString);
+ nSetPathString(mNativePtr, pathString, pathString.length());
}
final ColorStateList fillColors = a.getColorStateList(
@@ -1766,7 +1397,7 @@ public class VectorDrawable extends Drawable {
// If the color state list isn't stateful, discard the state
// list and keep the default (e.g. the only) color.
mFillColors = fillColors.isStateful() ? fillColors : null;
- mFillColor = fillColors.getDefaultColor();
+ fillColor = fillColors.getDefaultColor();
}
final ColorStateList strokeColors = a.getColorStateList(
@@ -1775,23 +1406,30 @@ public class VectorDrawable extends Drawable {
// If the color state list isn't stateful, discard the state
// list and keep the default (e.g. the only) color.
mStrokeColors = strokeColors.isStateful() ? strokeColors : null;
- mStrokeColor = strokeColors.getDefaultColor();
+ strokeColor = strokeColors.getDefaultColor();
}
-
- mFillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha, mFillAlpha);
- mStrokeLineCap = getStrokeLineCap(a.getInt(
- R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap);
- mStrokeLineJoin = getStrokeLineJoin(a.getInt(
- R.styleable.VectorDrawablePath_strokeLineJoin, -1), mStrokeLineJoin);
- mStrokeMiterlimit = a.getFloat(
- R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit);
- mStrokeAlpha = a.getFloat(R.styleable.VectorDrawablePath_strokeAlpha, mStrokeAlpha);
- mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, mStrokeWidth);
- mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, mTrimPathEnd);
- mTrimPathOffset = a.getFloat(
- R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset);
- mTrimPathStart = a.getFloat(
- R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart);
+ fillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha, fillAlpha);
+
+ strokeLineCap = a.getInt(
+ R.styleable.VectorDrawablePath_strokeLineCap, strokeLineCap);
+ strokeLineJoin = a.getInt(
+ R.styleable.VectorDrawablePath_strokeLineJoin, strokeLineJoin);
+ strokeMiterLimit = a.getFloat(
+ R.styleable.VectorDrawablePath_strokeMiterLimit, strokeMiterLimit);
+ strokeAlpha = a.getFloat(R.styleable.VectorDrawablePath_strokeAlpha,
+ strokeAlpha);
+ strokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth,
+ strokeWidth);
+ trimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd,
+ trimPathEnd);
+ trimPathOffset = a.getFloat(
+ R.styleable.VectorDrawablePath_trimPathOffset, trimPathOffset);
+ trimPathStart = a.getFloat(
+ R.styleable.VectorDrawablePath_trimPathStart, trimPathStart);
+
+ nUpdateFullPathProperties(mNativePtr, strokeWidth, strokeColor, strokeAlpha,
+ fillColor, fillAlpha, trimPathStart, trimPathEnd, trimPathOffset,
+ strokeMiterLimit, strokeLineCap, strokeLineJoin);
}
@Override
@@ -1813,104 +1451,169 @@ public class VectorDrawable extends Drawable {
/* Setters and Getters, used by animator from AnimatedVectorDrawable. */
@SuppressWarnings("unused")
int getStrokeColor() {
- return mStrokeColor;
+ return nGetStrokeColor(mNativePtr);
}
@SuppressWarnings("unused")
void setStrokeColor(int strokeColor) {
mStrokeColors = null;
- mStrokeColor = strokeColor;
+ nSetStrokeColor(mNativePtr, strokeColor);
}
@SuppressWarnings("unused")
float getStrokeWidth() {
- return mStrokeWidth;
+ return nGetStrokeWidth(mNativePtr);
}
@SuppressWarnings("unused")
void setStrokeWidth(float strokeWidth) {
- mStrokeWidth = strokeWidth;
+ nSetStrokeWidth(mNativePtr, strokeWidth);
}
@SuppressWarnings("unused")
float getStrokeAlpha() {
- return mStrokeAlpha;
+ return nGetStrokeAlpha(mNativePtr);
}
@SuppressWarnings("unused")
void setStrokeAlpha(float strokeAlpha) {
- mStrokeAlpha = strokeAlpha;
+ nSetStrokeAlpha(mNativePtr, strokeAlpha);
}
@SuppressWarnings("unused")
int getFillColor() {
- return mFillColor;
+ return nGetFillColor(mNativePtr);
}
@SuppressWarnings("unused")
void setFillColor(int fillColor) {
mFillColors = null;
- mFillColor = fillColor;
+ nSetFillColor(mNativePtr, fillColor);
}
@SuppressWarnings("unused")
float getFillAlpha() {
- return mFillAlpha;
+ return nGetFillAlpha(mNativePtr);
}
@SuppressWarnings("unused")
void setFillAlpha(float fillAlpha) {
- mFillAlpha = fillAlpha;
+ nSetFillAlpha(mNativePtr, fillAlpha);
}
@SuppressWarnings("unused")
float getTrimPathStart() {
- return mTrimPathStart;
+ return nGetTrimPathStart(mNativePtr);
}
@SuppressWarnings("unused")
void setTrimPathStart(float trimPathStart) {
- mTrimPathStart = trimPathStart;
+ nSetTrimPathStart(mNativePtr, trimPathStart);
}
@SuppressWarnings("unused")
float getTrimPathEnd() {
- return mTrimPathEnd;
+ return nGetTrimPathEnd(mNativePtr);
}
@SuppressWarnings("unused")
void setTrimPathEnd(float trimPathEnd) {
- mTrimPathEnd = trimPathEnd;
+ nSetTrimPathEnd(mNativePtr, trimPathEnd);
}
@SuppressWarnings("unused")
float getTrimPathOffset() {
- return mTrimPathOffset;
+ return nGetTrimPathOffset(mNativePtr);
}
@SuppressWarnings("unused")
void setTrimPathOffset(float trimPathOffset) {
- mTrimPathOffset = trimPathOffset;
+ nSetTrimPathOffset(mNativePtr, trimPathOffset);
}
}
- static class TempState {
- final Matrix pathMatrix = new Matrix();
- final Path path = new Path();
- final Path renderPath = new Path();
-
- PathMeasure mPathMeasure;
- Paint mFillPaint;
- Paint mStrokePaint;
- }
-
interface VObject {
- void draw(Canvas canvas, TempState temp, Matrix currentMatrix,
- ColorFilter filter, float scaleX, float scaleY);
+ long getNativePtr();
void inflate(Resources r, AttributeSet attrs, Theme theme);
boolean canApplyTheme();
void applyTheme(Theme t);
boolean onStateChange(int[] state);
boolean isStateful();
}
+
+ private static native long nCreateRenderer(long rootGroupPtr);
+ private static native void nDestroyRenderer(long rendererPtr);
+ private static native void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
+ float viewportHeight);
+ private static native boolean nSetRootAlpha(long rendererPtr, float alpha);
+ private static native float nGetRootAlpha(long rendererPtr);
+ private static native void nSetAllowCaching(long rendererPtr, boolean allowCaching);
+
+ private static native void nDraw(long rendererPtr, long canvasWrapperPtr,
+ long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache);
+ private static native long nCreateFullPath();
+ private static native long nCreateFullPath(long mNativeFullPathPtr);
+ private static native boolean nGetFullPathProperties(long pathPtr, byte[] properties,
+ int length);
+
+ private static native void nUpdateFullPathProperties(long pathPtr, float strokeWidth,
+ int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart,
+ float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap,
+ int strokeLineJoin);
+
+ private static native long nCreateClipPath();
+ private static native long nCreateClipPath(long clipPathPtr);
+
+ private static native long nCreateGroup();
+ private static native long nCreateGroup(long groupPtr);
+ private static native void nDestroy(long nodePtr);
+ private static native void nSetName(long nodePtr, String name);
+ private static native boolean nGetGroupProperties(long groupPtr, float[] properties,
+ int length);
+ private static native void nUpdateGroupProperties(long groupPtr, float rotate, float pivotX,
+ float pivotY, float scaleX, float scaleY, float translateX, float translateY);
+
+ private static native void nAddChild(long groupPtr, long nodePtr);
+ private static native void nSetPathString(long pathPtr, String pathString, int length);
+
+ /**
+ * The setters and getters below for paths and groups are here temporarily, and will be
+ * removed once the animation in AVD is replaced with RenderNodeAnimator, in which case the
+ * animation will modify these properties in native. By then no JNI hopping would be necessary
+ * for VD during animation, and these setters and getters will be obsolete.
+ */
+ // Setters and getters during animation.
+ private static native float nGetRotation(long groupPtr);
+ private static native void nSetRotation(long groupPtr, float rotation);
+ private static native float nGetPivotX(long groupPtr);
+ private static native void nSetPivotX(long groupPtr, float pivotX);
+ private static native float nGetPivotY(long groupPtr);
+ private static native void nSetPivotY(long groupPtr, float pivotY);
+ private static native float nGetScaleX(long groupPtr);
+ private static native void nSetScaleX(long groupPtr, float scaleX);
+ private static native float nGetScaleY(long groupPtr);
+ private static native void nSetScaleY(long groupPtr, float scaleY);
+ private static native float nGetTranslateX(long groupPtr);
+ private static native void nSetTranslateX(long groupPtr, float translateX);
+ private static native float nGetTranslateY(long groupPtr);
+ private static native void nSetTranslateY(long groupPtr, float translateY);
+
+ // Setters and getters for VPath during animation.
+ private static native void nSetPathData(long pathPtr, long pathDataPtr);
+ private static native float nGetStrokeWidth(long pathPtr);
+ private static native void nSetStrokeWidth(long pathPtr, float width);
+ private static native int nGetStrokeColor(long pathPtr);
+ private static native void nSetStrokeColor(long pathPtr, int strokeColor);
+ private static native float nGetStrokeAlpha(long pathPtr);
+ private static native void nSetStrokeAlpha(long pathPtr, float alpha);
+ private static native int nGetFillColor(long pathPtr);
+ private static native void nSetFillColor(long pathPtr, int fillColor);
+ private static native float nGetFillAlpha(long pathPtr);
+ private static native void nSetFillAlpha(long pathPtr, float fillAlpha);
+ private static native float nGetTrimPathStart(long pathPtr);
+ private static native void nSetTrimPathStart(long pathPtr, float trimPathStart);
+ private static native float nGetTrimPathEnd(long pathPtr);
+ private static native void nSetTrimPathEnd(long pathPtr, float trimPathEnd);
+ private static native float nGetTrimPathOffset(long pathPtr);
+ private static native void nSetTrimPathOffset(long pathPtr, float trimPathOffset);
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 0d1ee46712ee..f48c509755e3 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -86,7 +86,7 @@ hwui_src_files := \
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 4c87b1898ae5..c4bbb7495a4c 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 000000000000..e13a2bb2a38b
--- /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 000000000000..6c84b052faf4
--- /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 c9a54ca870fa..000000000000
--- 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 2e56349b3aa4..000000000000
--- 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 3d9fafac6c93..bd742c6ededf 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 77dd73acff10..720854779c98 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 @@ const static TestData sTestDataSet[] = {
}
},
+ // 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 @@ const StringPath sStringPaths[] = {
{"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(VectorDrawableUtils, interpolatePathData) {
}
}
+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 21c1cdcc37f3..b5ef5102d219 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"